From 5700ca9d5b4710bcee5aac63c426efd8863e5339 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 15 Apr 2014 08:50:28 +0200 Subject: [PATCH 01/36] Erasure of term refs without symbols Need to allow for possibility that term ref does not have a symbol (e.g. termrefs with union prefixes do not always have symbols). --- src/dotty/tools/dotc/core/transform/Erasure.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/transform/Erasure.scala b/src/dotty/tools/dotc/core/transform/Erasure.scala index 9fb580ae8b95..a7981b70f200 100644 --- a/src/dotty/tools/dotc/core/transform/Erasure.scala +++ b/src/dotty/tools/dotc/core/transform/Erasure.scala @@ -124,7 +124,7 @@ class Erasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcard else this(parent) case tp: TermRef => val sym = tp.symbol - if (sym.owner is Package) sym.termRef + if (sym.exists && (sym.owner is Package)) sym.termRef else tp.derivedSelect(this(tp.prefix)) case _: ThisType | _: ConstantType => tp From 5c11da2148412f36416ad6998909138323ae4894 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 15 Apr 2014 08:51:08 +0200 Subject: [PATCH 02/36] Fix withSymAndName There was a mismatch between symbol and signature before. --- src/dotty/tools/dotc/Compiler.scala | 4 ++-- src/dotty/tools/dotc/core/Types.scala | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 36af6a438035..a4a8fbbc84b1 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -21,7 +21,6 @@ class Compiler { List( List(new FrontEnd), List(new LazyValsCreateCompanionObjects, - /* new Constructors, */ new TailRec), //force separataion between lazyVals and LVCreateCO List(new PatternMatcher, new LazyValTranformContext().transformer, @@ -30,7 +29,8 @@ class Compiler { new TypeTestsCasts, new InterceptedMethods), List(new Erasure), - List(new UncurryTreeTransform, new CollectEntryPoints) + List(new UncurryTreeTransform + /* , new Constructors */) ) var runId = 1 diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 5ce13ad55a98..b17c40eb760f 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1314,11 +1314,12 @@ object Types { unique(new NonMemberTermRef(prefix, name, sym)) def withSymAndName(prefix: Type, sym: TermSymbol, name: TermName)(implicit ctx: Context): TermRef = - if (prefix eq NoPrefix) withNonMemberSym(prefix, name, sym) - else { - if (sym.defRunId != NoRunId && sym.isCompleted) withSig(prefix, name, sym.signature) - else apply(prefix, name) - } withSym (sym, Signature.NotAMethod) + if (prefix eq NoPrefix) + withNonMemberSym(prefix, name, sym) + else if (sym.defRunId != NoRunId && sym.isCompleted) + withSig(prefix, name, sym.signature) withSym (sym, sym.signature) + else + apply(prefix, name) withSym (sym, Signature.NotAMethod) def withSig(prefix: Type, sym: TermSymbol)(implicit ctx: Context): TermRef = unique(withSig(prefix, sym.name, sym.signature).withSym(sym, sym.signature)) From 92c02ee9d514c197221fd4c4a027fe0e70e081f2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 18 Apr 2014 11:31:11 +0200 Subject: [PATCH 03/36] Removing duplication between Any and Object methods We cannot have same named methods defined in Object and Any because after erasure the Any references get remapped to the Object methods which would result in a double binding assertion failure. Instead we do the following: - Have some methods exist only in Any, and remap them with the Erasure denotation transformer to be owned by Object. - Have other methods exist only in Object. To achieve this, we synthesize all Any and Object methods; Objetc methods no longer get loaded from a classfile. There's a complication with getClass. We need to reconsider what the best treatment of getClass is. Right now there's too much magic going on for my taste. It might be better to leave getClass on Object only as it is in Java, forget about the special treatement of its type, and have another getClass like method in an decorator on class Any. That could produce the right types and could also work for primitive types. --- src/dotty/tools/dotc/core/Definitions.scala | 134 +++++++++--------- src/dotty/tools/dotc/core/StdNames.scala | 2 - .../dotc/core/pickling/ClassfileParser.scala | 6 +- .../tools/dotc/core/transform/Erasure.scala | 4 +- src/dotty/tools/dotc/transform/Erasure.scala | 7 +- .../dotc/transform/InterceptedMethods.scala | 20 +-- .../tools/dotc/transform/TypeTestsCasts.scala | 8 +- test/dotc/tests.scala | 2 +- 8 files changed, 93 insertions(+), 90 deletions(-) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 9497438f2f33..211d9e9cd780 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -30,6 +30,12 @@ class Definitions { private def newCompleteClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, parents: List[TypeRef], decls: Scope = newScope) = ctx.newCompleteClassSymbol(owner, name, flags | Permanent, parents, decls).entered + private def newTopClassSymbol(name: TypeName, flags: FlagSet, parents: List[TypeRef]) = { + val cls = newCompleteClassSymbol(ScalaPackageClass, name, flags, parents) + ensureConstructor(cls, EmptyScope) + cls + } + private def newTypeParam(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = scope.enter(newSymbol(cls, name, flags | TypeParamCreationFlags, TypeBounds.empty)) @@ -74,7 +80,7 @@ class Definitions { newPolyMethod(cls, name, 1, resultTypeFn, flags) private def newT1EmptyParamsMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) = - newPolyMethod(cls, name, 1, pt => MethodType(Nil, Nil, resultTypeFn(pt)), flags) + newPolyMethod(cls, name, 1, pt => MethodType(Nil, resultTypeFn(pt)), flags) private def mkArityArray(name: String, arity: Int, countFrom: Int): Array[ClassSymbol] = { val arr = new Array[ClassSymbol](arity + 1) @@ -95,58 +101,69 @@ class Definitions { lazy val JavaPackageVal = ctx.requiredPackage("java") lazy val JavaLangPackageVal = ctx.requiredPackage("java.lang") - lazy val ObjectClass = ctx.requiredClass("java.lang.Object") - lazy val AnyRefAlias: TypeSymbol = newAliasType(tpnme.AnyRef, ObjectType) - - lazy val Object_## = newMethod(ObjectClass, nme.HASHHASH, ExprType(IntType), Final) - lazy val Object_== = newMethod(ObjectClass, nme.EQ, methOfAny(BooleanType), Final) - lazy val Object_!= = newMethod(ObjectClass, nme.NE, methOfAny(BooleanType), Final) - lazy val Object_eq = newMethod(ObjectClass, nme.eq, methOfAnyRef(BooleanType), Final) - lazy val Object_ne = newMethod(ObjectClass, nme.ne, methOfAnyRef(BooleanType), Final) - lazy val Object_isInstanceOf = newT1ParameterlessMethod(ObjectClass, nme.isInstanceOf_Ob, _ => BooleanType, Final | Synthetic) - lazy val Object_asInstanceOf = newT1ParameterlessMethod(ObjectClass, nme.asInstanceOf_Ob, PolyParam(_, 0), Final | Synthetic) - lazy val Object_synchronized = newPolyMethod(ObjectClass, nme.synchronized_, 1, - pt => MethodType(List(PolyParam(pt, 0)), PolyParam(pt, 0)), Final) - - def Object_getClass = objMethod(nme.getClass_) - def Object_clone = objMethod(nme.clone_) - def Object_finalize = objMethod(nme.finalize_) - def Object_notify = objMethod(nme.notify_) - def Object_notifyAll = objMethod(nme.notifyAll_) - def Object_equals = objMethod(nme.equals_) - def Object_hashCode = objMethod(nme.hashCode_) - def Object_toString = objMethod(nme.toString_) - private def objMethod(name: PreName) = ObjectClass.requiredMethod(name) - - lazy val AnyClass: ClassSymbol = { - val cls = newCompleteClassSymbol(ScalaPackageClass, tpnme.Any, Abstract, Nil) - ensureConstructor(cls, EmptyScope) - cls - } - - lazy val AnyValClass: ClassSymbol = ctx.requiredClass("scala.AnyVal") + /** Note: We cannot have same named methods defined in Object and Any (and AnyVal, for that matter) + * because after erasure the Any and AnyVal references get remapped to the Object methods + * which would result in a double binding assertion failure. + * Instead we do the following: + * + * - Have some methods exist only in Any, and remap them with the Erasure denotation + * transformer to be owned by Object. + * - Have other methods exist only in Object. + * To achieve this, we synthesize all Any and Object methods; Object methods no longer get + * loaded from a classfile. + * + * There's a remaining question about `getClass`. In Scala2.x `getClass` was handled by compiler magic. + * This is deemed too cumersome for Dotty and therefore right now `getClass` gets no special treatment; + * it's just a method on `Any` which returns the raw type `java.lang.Class`. An alternative + * way to get better `getClass` typing would be to treat `getClass` as a method of a generic + * decorator which gets remapped in a later phase to Object#getClass. Then we could give it + * the right type without changing the typechecker: + * + * implicit class AnyGetClass[T](val x: T) extends AnyVal { + * def getClass: java.lang.Class[T] = ??? + * } + */ + lazy val AnyClass: ClassSymbol = newTopClassSymbol(tpnme.Any, Abstract, Nil) + lazy val AnyValClass: ClassSymbol = newTopClassSymbol(tpnme.AnyVal, Abstract, AnyClass.typeRef :: Nil) - lazy val AnyVal_getClass = AnyValClass.requiredMethod(nme.getClass_) lazy val Any_== = newMethod(AnyClass, nme.EQ, methOfAny(BooleanType), Final) lazy val Any_!= = newMethod(AnyClass, nme.NE, methOfAny(BooleanType), Final) lazy val Any_equals = newMethod(AnyClass, nme.equals_, methOfAny(BooleanType)) - lazy val Any_hashCode = newMethod(AnyClass, nme.hashCode_, ExprType(IntType)) - lazy val Any_toString = newMethod(AnyClass, nme.toString_, ExprType(StringType)) + lazy val Any_hashCode = newMethod(AnyClass, nme.hashCode_, MethodType(Nil, IntType)) + lazy val Any_toString = newMethod(AnyClass, nme.toString_, MethodType(Nil, StringType)) lazy val Any_## = newMethod(AnyClass, nme.HASHHASH, ExprType(IntType), Final) - - // Any_getClass requires special handling. The return type is determined on - // a per-call-site basis as if the function being called were actually: - // - // // Assuming `target.getClass()` - // def getClass[T](target: T): Class[_ <: T] - // - // Since getClass is not actually a polymorphic method, this requires compiler - // participation. At the "Any" level, the return type is Class[_] as it is in - // java.lang.Object. Java also special cases the return type. - lazy val Any_getClass = newMethod(AnyClass, nme.getClass_, ExprType(Object_getClass.info.resultType), Deferred) + lazy val Any_getClass = newMethod(AnyClass, nme.getClass_, MethodType(Nil, ClassClass.typeRef), Final) lazy val Any_isInstanceOf = newT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final) lazy val Any_asInstanceOf = newT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, PolyParam(_, 0), Final) + def AnyMethods = List(Any_==, Any_!=, Any_equals, Any_hashCode, + Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_asInstanceOf) + + lazy val ObjectClass: ClassSymbol = { + val cls = ctx.requiredClass("java.lang.Object") + assert(!cls.isCompleted, "race for completing java.lang.Object") + cls.info = ClassInfo(cls.owner.thisType, cls, AnyClass.typeRef :: Nil, newScope) + cls.linkedClass.info = NoType + ensureConstructor(cls, cls.decls) + cls + } + lazy val AnyRefAlias: TypeSymbol = newAliasType(tpnme.AnyRef, ObjectType) + + lazy val Object_eq = newMethod(ObjectClass, nme.eq, methOfAnyRef(BooleanType), Final) + lazy val Object_ne = newMethod(ObjectClass, nme.ne, methOfAnyRef(BooleanType), Final) + lazy val Object_synchronized = newPolyMethod(ObjectClass, nme.synchronized_, 1, + pt => MethodType(List(PolyParam(pt, 0)), PolyParam(pt, 0)), Final) + lazy val Object_clone = newMethod(ObjectClass, nme.clone_, MethodType(Nil, ObjectType), Protected) + lazy val Object_finalize = newMethod(ObjectClass, nme.finalize_, MethodType(Nil, UnitType), Protected) + lazy val Object_notify = newMethod(ObjectClass, nme.notify_, MethodType(Nil, UnitType)) + lazy val Object_notifyAll = newMethod(ObjectClass, nme.notifyAll_, MethodType(Nil, UnitType)) + lazy val Object_wait = newMethod(ObjectClass, nme.wait_, MethodType(Nil, UnitType)) + lazy val Object_waitL = newMethod(ObjectClass, nme.wait_, MethodType(LongType :: Nil, UnitType)) + lazy val Object_waitLI = newMethod(ObjectClass, nme.wait_, MethodType(LongType :: IntType :: Nil, UnitType)) + + def ObjectMethods = List(Object_eq, Object_ne, Object_synchronized, Object_clone, + Object_finalize, Object_notify, Object_notifyAll, Object_wait, Object_waitL, Object_waitLI) + lazy val NotNullClass = ctx.requiredClass("scala.NotNull") lazy val NothingClass: ClassSymbol = newCompleteClassSymbol( @@ -380,8 +397,8 @@ class Definitions { lazy val PhantomClasses = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass) - lazy val asInstanceOfMethods = Set[Symbol](Any_asInstanceOf, Object_asInstanceOf) - lazy val isInstanceOfMethods = Set[Symbol](Any_isInstanceOf, Object_isInstanceOf) + lazy val asInstanceOfMethods = Set[Symbol](Any_asInstanceOf) + lazy val isInstanceOfMethods = Set[Symbol](Any_isInstanceOf) lazy val typeTestsOrCasts = asInstanceOfMethods ++ isInstanceOfMethods lazy val RootImports = List[Symbol](JavaLangPackageVal, ScalaPackageVal, ScalaPredefModule, DottyPredefModule) @@ -518,11 +535,11 @@ class Definitions { /** Lists core classes that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */ lazy val syntheticCoreClasses = List( + AnyClass, AnyRefAlias, RepeatedParamClass, JavaRepeatedParamClass, ByNameParamClass2x, - AnyClass, AnyValClass, NullClass, NothingClass, @@ -531,26 +548,7 @@ class Definitions { EmptyPackageVal) /** Lists core methods that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */ - lazy val syntheticCoreMethods = List( - Any_==, - Any_!=, - Any_equals, - Any_hashCode, - Any_toString, - Any_getClass, - Any_isInstanceOf, - Any_asInstanceOf, - Any_##, - Object_eq, - Object_ne, - Object_==, - Object_!=, - Object_##, - Object_synchronized, - Object_isInstanceOf, - Object_asInstanceOf, - String_+ - ) + lazy val syntheticCoreMethods = AnyMethods ++ ObjectMethods ++ List(String_+) private[this] var _isInitialized = false def isInitialized = _isInitialized diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index cb4272f7a267..593feb9092e5 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -331,7 +331,6 @@ object StdNames { val asType: N = "asType" val asClass: N = "asClass" val asInstanceOf_ : N = "asInstanceOf" - val asInstanceOf_Ob : N = "$asInstanceOf" val assert_ : N = "assert" val assume_ : N = "assume" val box: N = "box" @@ -388,7 +387,6 @@ object StdNames { val isDefinedAt: N = "isDefinedAt" val isEmpty: N = "isEmpty" val isInstanceOf_ : N = "isInstanceOf" - val isInstanceOf_Ob : N = "$isInstanceOf" val java: N = "java" val keepUnions: N = "keepUnions" val key: N = "key" diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala index f5942dac24b8..0ed301732e8a 100644 --- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala +++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala @@ -891,8 +891,10 @@ class ClassfileParser( def getType(index: Int)(implicit ctx: Context): Type = sigToType(getExternalName(index)) - def getSuperClass(index: Int)(implicit ctx: Context): Symbol = - if (index == 0) defn.AnyClass else getClassSymbol(index) + def getSuperClass(index: Int)(implicit ctx: Context): Symbol = { + assert(index != 0, "attempt to parse java.lang.Object from classfile") + getClassSymbol(index) + } def getConstant(index: Int)(implicit ctx: Context): Constant = { if (index <= 0 || len <= index) errorBadIndex(index) diff --git a/src/dotty/tools/dotc/core/transform/Erasure.scala b/src/dotty/tools/dotc/core/transform/Erasure.scala index a7981b70f200..f2e3355fb304 100644 --- a/src/dotty/tools/dotc/core/transform/Erasure.scala +++ b/src/dotty/tools/dotc/core/transform/Erasure.scala @@ -61,8 +61,8 @@ object Erasure { */ def transformInfo(sym: Symbol, tp: Type)(implicit ctx: Context): Type = { val erase = erasureFn(sym is JavaDefined, isSemi = true, sym.isConstructor, wildcardOK = false) - if ((sym eq defn.Object_asInstanceOf) || - (sym eq defn.Object_isInstanceOf) || + if ((sym eq defn.Any_asInstanceOf) || + (sym eq defn.Any_isInstanceOf) || (sym.owner eq defn.ArrayClass) && (sym.isType || sym.isConstructor)) sym.info else if (sym.isAbstractType) TypeAlias(WildcardType) else erase(tp) diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 791df341ad09..9473bf10a0ad 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -33,7 +33,10 @@ class Erasure extends Phase with DenotTransformer { def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match { case ref: SymDenotation => assert(ctx.phase == this, s"transforming $ref at ${ctx.phase}") - ref.copySymDenotation(info = transformInfo(ref.symbol, ref.info)) + val owner = ref.owner + ref.copySymDenotation( + owner = if (owner eq defn.AnyClass) defn.ObjectClass else owner, + info = transformInfo(ref.symbol, ref.info)) case ref => ref.derivedSingleDenotation(ref.symbol, erasure(ref.info)) } @@ -136,7 +139,7 @@ object Erasure { cast(runtimeCall(nme.toObjectArray, tree :: Nil), pt) case _ => ctx.log(s"casting from ${tree.showSummary}: ${tree.tpe.show} to ${pt.show}") - TypeApply(Select(tree, defn.Object_asInstanceOf), TypeTree(pt) :: Nil) + TypeApply(Select(tree, defn.Any_asInstanceOf), TypeTree(pt) :: Nil) } /** Adaptation of an expression `e` to an expected type `PT`, applying the following diff --git a/src/dotty/tools/dotc/transform/InterceptedMethods.scala b/src/dotty/tools/dotc/transform/InterceptedMethods.scala index d5a4377d049d..f5fed6fda760 100644 --- a/src/dotty/tools/dotc/transform/InterceptedMethods.scala +++ b/src/dotty/tools/dotc/transform/InterceptedMethods.scala @@ -28,6 +28,10 @@ import dotty.tools.dotc.core.Denotations.SingleDenotation import dotty.tools.dotc.core.SymDenotations.SymDenotation import StdNames._ +// @DarkDimius The getClass scheme changed. We no longer can have +// two different methods in Any and Object. The tests pass but I +// am not sure Intercepted methods treats getClass right now. +// Please check and delete comment when done. /** Replace member references as follows: * * - `x == y` for == in class Any becomes `x equals y` with equals in class Object. @@ -50,12 +54,10 @@ class InterceptedMethods extends TreeTransform { /** perform context-dependant initialization */ override def init(implicit ctx: Context, info: TransformerInfo): Unit = { - getClassMethods = Set(defn.Any_getClass, defn.AnyVal_getClass) - poundPoundMethods = Set(defn.Any_##, defn.Object_##) + poundPoundMethods = Set(defn.Any_##) Any_comparisons = Set(defn.Any_==, defn.Any_!=) - interceptedMethods = getClassMethods ++ poundPoundMethods ++ Any_comparisons - primitiveGetClassMethods = Set[Symbol](defn.Any_getClass, defn.AnyVal_getClass) ++ - defn.ScalaValueClasses.map(x => x.requiredMethod(nme.getClass_)) + interceptedMethods = poundPoundMethods ++ Any_comparisons + primitiveGetClassMethods = Set[Symbol]() ++ defn.ScalaValueClasses.map(x => x.requiredMethod(nme.getClass_)) } // this should be removed if we have guarantee that ## will get Apply node @@ -97,7 +99,7 @@ class InterceptedMethods extends TreeTransform { override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = { def unknown = { - assert(false, s"The symbol '${tree.fun.symbol}' was interecepted but didn't match any cases, " + + assert(false, s"The symbol '${tree.fun.symbol.showLocated}' was intercepted but didn't match any cases, " + s"that means the intercepted methods set doesn't match the code") tree } @@ -109,9 +111,9 @@ class InterceptedMethods extends TreeTransform { PoundPoundValue(qual) } else if (Any_comparisons contains tree.fun.symbol.asTerm) { if (tree.fun.symbol eq defn.Any_==) { - Apply(Select(qual, defn.Object_equals.termRef), tree.args) + Apply(Select(qual, defn.Any_equals), tree.args) } else if (tree.fun.symbol eq defn.Any_!=) { - Select(Apply(Select(qual, defn.Object_equals.termRef), tree.args), defn.Boolean_!.termRef) + Select(Apply(Select(qual, defn.Any_equals), tree.args), defn.Boolean_!) } else unknown } /* else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) { // todo: this is needed to support value classes @@ -128,7 +130,7 @@ class InterceptedMethods extends TreeTransform { // we get a primitive form of _getClass trying to target a boxed value // so we need replace that method name with Object_getClass to get correct behavior. // See SI-5568. - Apply(Select(qual, defn.Object_getClass.termRef), Nil) + Apply(Select(qual, defn.Any_getClass), Nil) } else { unknown } diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index a36bf6500c40..5f65ee414806 100644 --- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -68,10 +68,10 @@ class TypeTestsCasts extends TreeTransform { runtimeCall(nme.isArray, arg :: Literal(Constant(ndims)) :: Nil) if (ndims == 1) isArrayTest(qual) else evalOnce(qual) { qual1 => - mkAnd(derivedTree(qual1, defn.Object_isInstanceOf, qual1.tpe), isArrayTest(qual1)) + mkAnd(derivedTree(qual1, defn.Any_isInstanceOf, qual1.tpe), isArrayTest(qual1)) } case _ => - derivedTree(expr, defn.Object_isInstanceOf, argType) + derivedTree(expr, defn.Any_isInstanceOf, argType) } } @@ -81,10 +81,10 @@ class TypeTestsCasts extends TreeTransform { else if (qualCls.isPrimitiveValueClass) { val argCls = argType.classSymbol if (argCls.isPrimitiveValueClass) primitiveConversion(qual, argCls) - else derivedTree(box(qual), defn.Object_asInstanceOf, argType) + else derivedTree(box(qual), defn.Any_asInstanceOf, argType) } else - derivedTree(qual, defn.Object_asInstanceOf, argType) + derivedTree(qual, defn.Any_asInstanceOf, argType) } if (sym eq defn.Any_isInstanceOf) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index c6fbcabf4eb6..3f99a9d5b386 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -12,7 +12,7 @@ class tests extends CompilerTest { // "-Xprompt", // "-explaintypes", // "-Yshow-suppressed-errors", - "-pagewidth", "160" + "-pagewidth", "160"//, "-Ystop-before:terminal" ) val twice = List("#runs", "2", "-YnoDoubleBindings") val doErase = List("-Ystop-before:terminal") From a8894ed954e16b481379f09ce7e04342bd9b34f2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 18 Apr 2014 17:35:43 +0200 Subject: [PATCH 04/36] Fixing stillValid code We need to take into account that symbols can can have non-class owners yet still be valid. Previously such symbols were treated as invalid. Now they are valid if their owners are valid. The commit supersedes 29976d7922c5c3e99f0469e3db1 which got reverted. --- src/dotty/tools/dotc/core/SymDenotations.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 9cf06ec47f4e..c7e7dc50d13a 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -46,8 +46,9 @@ trait SymDenotations { this: Context => case ClassInfo(_, _, _, _, selfInfo) => selfInfo == denot.symbol case _ => false } - stillValid(owner) && owner.isClass && ( - (owner.decls.lookupAll(denot.name) contains denot.symbol) + stillValid(owner) && ( + !owner.isClass + || (owner.decls.lookupAll(denot.name) contains denot.symbol) || isSelfSym ) } catch { From 861f10b72e2a6332f8919f6880604bdc49cc3bca Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 18 Apr 2014 17:41:14 +0200 Subject: [PATCH 05/36] Added a unique identifier to trees. Used for debugging purposes for now. Might be used for replaying IDE interactions later. --- src/dotty/tools/dotc/ast/Trees.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 2ae494d558dc..8df48696802f 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -180,6 +180,8 @@ object Trees { def tokenPos: Seq[(Token, Position)] = ??? } + private var nextId = 0 + /** Trees take a parameter indicating what the type of their `tpe` field * is. Two choices: `Type` or `Untyped`. * Untyped trees have type `Tree[Untyped]`. @@ -204,6 +206,14 @@ object Trees { if (Stats.enabled) ntrees += 1 + /** A unique identifier for this tree. Used for debugging, and potentially + * tracking presentation compiler interactions + */ + val uniqueId = { + nextId += 1 + nextId + } + /** The type constructor at the root of the tree */ type ThisTree[T >: Untyped] <: Tree[T] From 9203e01ddd141ddbb96e3064be5e894ddb666fd8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 18 Apr 2014 17:43:40 +0200 Subject: [PATCH 06/36] Some more debug info. --- src/dotty/tools/dotc/core/Denotations.scala | 2 +- src/dotty/tools/dotc/transform/Erasure.scala | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index e5f5e6f87529..9eab2bd040f7 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -600,7 +600,7 @@ object Denotations { case denot: SymDenotation => s"in ${denot.owner}" case _ => "" } - def msg = s"stale symbol; $this#${symbol.id} $ownerMsg, defined in run ${myValidFor.runId}, is referred to in run ${ctx.runId}" + def msg = s"stale symbol; $this#${symbol.id} $ownerMsg, defined in ${myValidFor}, is referred to in run ${ctx.period}" throw new StaleSymbol(msg) } diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 9473bf10a0ad..e6d012d68a6a 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -198,7 +198,7 @@ object Erasure { */ override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { val sym = tree.symbol - assert(sym.exists) + assert(sym.exists, tree.show) def select(qual: Tree, sym: Symbol): Tree = untpd.cpy.Select(tree, qual, sym.name) withType qual.tpe.select(sym) @@ -245,6 +245,8 @@ object Erasure { case mt: MethodType => val args1 = args.zipWithConserve(mt.paramTypes)(typedExpr) untpd.cpy.Apply(tree, fun1, args1) withType mt.resultType + case _ => + throw new MatchError(i"tree $tree has uxpected type of function ${fun1.tpe.widen}, was ${fun.typeOpt.widen}") } } From d78885ff06bea4840cdf9bee61dd3698fcff820a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 19 Apr 2014 19:48:57 +0200 Subject: [PATCH 07/36] isAfterTyper test Add test whether we are after typer. Use it to assert that eta expansion and implicit search do not happen after typer. --- src/dotty/tools/dotc/core/Phases.scala | 6 +++++- src/dotty/tools/dotc/typer/EtaExpansion.scala | 1 + src/dotty/tools/dotc/typer/Implicits.scala | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index f76b83db69cd..251cd3876113 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -36,6 +36,8 @@ trait Phases { def atPhaseNotLaterThanTyper[T](op: Context => T): T = atPhaseNotLaterThan(base.typerPhase)(op) + + def isAfterTyper: Boolean = base.isAfterTyper(phase) } object Phases { @@ -177,9 +179,11 @@ object Phases { def refchecksPhase = refChecksCache.phase def erasurePhase = erasureCache.phase def flattenPhase = flattenCache.phase + + def isAfterTyper(phase: Phase): Boolean = phase.id > typerPhase.id } - final val typerName = "typer" + final val typerName = "frontend" final val refChecksName = "refchecks" final val erasureName = "erasure" final val flattenName = "flatten" diff --git a/src/dotty/tools/dotc/typer/EtaExpansion.scala b/src/dotty/tools/dotc/typer/EtaExpansion.scala index 099dd943a608..110dc6152932 100644 --- a/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -116,6 +116,7 @@ object EtaExpansion { */ def etaExpand(tree: Tree, mt: MethodType, xarity: Int)(implicit ctx: Context): untpd.Tree = { import untpd._ + assert(!ctx.isAfterTyper) val defs = new mutable.ListBuffer[tpd.Tree] val lifted: Tree = TypedSplice(liftApp(defs, tree)) val paramTypes: List[Tree] = diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index 93876651ac80..4767e6dd3dda 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -400,6 +400,7 @@ trait Implicits { self: Typer => * !!! todo: catch potential cycles */ def inferImplicit(pt: Type, argument: Tree, pos: Position)(implicit ctx: Context): SearchResult = track("inferImplicit") { + assert(!ctx.isAfterTyper) ctx.traceIndented(s"search implicit ${pt.show}, arg = ${argument.show}: ${argument.tpe.show}", implicits, show = true) { assert(!pt.isInstanceOf[ExprType]) val isearch = @@ -472,7 +473,7 @@ trait Implicits { self: Typer => shadowedImplicit(ref, methPart(shadowing).tpe) } else - SearchSuccess(generated, ref, ctx.typerState) + SearchSuccess(generated1, ref, ctx.typerState) }} /** Given a list of implicit references, produce a list of all implicit search successes, From 0fed5dd63f5d40a2aba012fca46f8901d0d4b70d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 21 Apr 2014 11:54:32 +0200 Subject: [PATCH 08/36] Avoid -Ycheck after errors --- src/dotty/tools/dotc/Run.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala index 247fa43365e1..a639b20cd770 100644 --- a/src/dotty/tools/dotc/Run.scala +++ b/src/dotty/tools/dotc/Run.scala @@ -42,8 +42,10 @@ class Run(comp: Compiler)(implicit ctx: Context) { phase.runOn(units) def foreachUnit(op: Context => Unit)(implicit ctx: Context): Unit = for (unit <- units) op(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit)) - if (ctx.settings.Xprint.value.containsPhase(phase)) foreachUnit(printTree) - if (ctx.settings.Ycheck.value.containsPhase(phase)) foreachUnit(TreeChecker.check) + if (ctx.settings.Xprint.value.containsPhase(phase)) + foreachUnit(printTree) + if (ctx.settings.Ycheck.value.containsPhase(phase) && !ctx.reporter.hasErrors) + foreachUnit(TreeChecker.check) } } } From b4e5d395785576d1b6d84a35ec9ab4e7f6df6031 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 21 Apr 2014 11:56:52 +0200 Subject: [PATCH 09/36] Better printing of type variables Will now print the full chain of instances using ? for uninstantiated and ' for instantiated. --- src/dotty/tools/dotc/printing/PlainPrinter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index f10aa7225fcf..308470885fa9 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -143,7 +143,7 @@ class PlainPrinter(_ctx: Context) extends Printer { toTextLocal(tpe) ~ " " ~ toText(annot) case tp: TypeVar => val suffix = if (tp.isInstantiated) "'" else "?" - toTextLocal(tp.stripTypeVar) ~ suffix // debug for now, so that we can see where the TypeVars are. + toTextLocal(tp.instanceOpt orElse tp.origin) ~ suffix // debug for now, so that we can see where the TypeVars are. case _ => tp.fallbackToText(this) } From 6f7fc527560891b2fc520a53a4c21b3a36ae0fed Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 21 Apr 2014 11:58:21 +0200 Subject: [PATCH 10/36] Avoid checking for tpt non empty in implicit defs after typer. The check assumes that inferred TypeTrees do not exist yet, but after the typer they do exist. --- src/dotty/tools/dotc/typer/ReTyper.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala index c2f627b1e7b9..7c2cd5b1703e 100644 --- a/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/src/dotty/tools/dotc/typer/ReTyper.scala @@ -4,6 +4,7 @@ package typer import core.Contexts._ import core.Types._ import core.Symbols._ +import core.Decorators._ import typer.ProtoTypes._ import ast.{tpd, untpd} import ast.Trees._ @@ -20,7 +21,7 @@ class ReTyper extends Typer { import tpd._ protected def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = { - assert(tree.hasType) + assert(tree.hasType, i"$tree ${tree.getClass} ${tree.uniqueId}") tree.withType(tree.typeOpt) } @@ -55,4 +56,6 @@ class ReTyper extends Typer { override def localTyper(sym: Symbol) = this override def index(trees: List[untpd.Tree])(implicit ctx: Context) = ctx + + override def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = () } \ No newline at end of file From 8298eb20b072d405aad71ae6c390a4939c8f090f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 21 Apr 2014 11:59:43 +0200 Subject: [PATCH 11/36] Addiong an adapt to implicit parameters. Implicit parameters were not adapted to the formal argument before which caused a -Ycheck violation. Todo: we should find out whether we can do without the adapt in typedImplicit whose result is afterwards thrown away. --- src/dotty/tools/dotc/typer/Typer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index e4acff53631d..d46b13818964 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1137,7 +1137,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def where = d"parameter $pname of $methodStr" inferImplicit(formal, EmptyTree, tree.pos.endPos) match { case SearchSuccess(arg, _, _) => - arg + adapt(arg, formal) case ambi: AmbiguousImplicits => implicitArgError(s"ambiguous implicits: ${ambi.explanation} of $where") case failure: SearchFailure => From 60527d656dfed2e5432d7c839c2ed4591709672b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 21 Apr 2014 12:00:45 +0200 Subject: [PATCH 12/36] Making reporters configurable Add method to install a new reporter in a subcontext's typer state. --- src/dotty/tools/dotc/core/TyperState.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala index 6e17767d23e2..a4b365b172a4 100644 --- a/src/dotty/tools/dotc/core/TyperState.scala +++ b/src/dotty/tools/dotc/core/TyperState.scala @@ -35,6 +35,9 @@ class TyperState(val reporter: Reporter) extends DotClass with Showable { */ def fresh(isCommittable: Boolean): TyperState = this + /** A fresh type state with the same constraint as this one and the given reporter */ + def withReporter(reporter: Reporter) = new TyperState(reporter) + /** Commit state so that it gets propagated to enclosing context */ def commit()(implicit ctx: Context): Unit = unsupported("commit") @@ -64,6 +67,9 @@ extends TyperState(reporter) { override def fresh(isCommittable: Boolean): TyperState = new MutableTyperState(this, new StoreReporter, isCommittable) + override def withReporter(reporter: Reporter) = + new MutableTyperState(this, reporter, isCommittable) + override val isGlobalCommittable = isCommittable && (!previous.isInstanceOf[MutableTyperState] || previous.isGlobalCommittable) From b6e3c9d15d1e9569d14cd98a57406683fbb3690d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 21 Apr 2014 12:02:07 +0200 Subject: [PATCH 13/36] Refining TreeChecker to work immediately after typer. Making TreeChecker work immediately after typer. This requires a number of fixes. Also, errors now cause exceptions instead of being reported on the command line. --- .../tools/dotc/transform/TreeChecker.scala | 57 ++++++++++++------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index f52c5bc1c94d..f39dd505a73e 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -14,8 +14,10 @@ import core.Decorators._ import core.transform.Erasure.isUnboundedGeneric import typer._ import typer.ErrorReporting._ +import reporting.ThrowingReporter import ast.Trees._ import ast.{tpd, untpd} +import java.lang.AssertionError /** This transform eliminates patterns. Right now it's a dummy. * Awaiting the real pattern matcher. @@ -25,36 +27,49 @@ class TreeChecker { def check(ctx: Context) = { println(s"checking ${ctx.compilationUnit} after phase ${ctx.phase.prev}") - Checker.typedExpr(ctx.compilationUnit.tpdTree)(ctx) + val checkingCtx = ctx.fresh + .setTyperState(ctx.typerState.withReporter(new ThrowingReporter(ctx.typerState.reporter))) + Checker.typedExpr(ctx.compilationUnit.tpdTree)(checkingCtx) } object Checker extends ReTyper { - override def typed(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = - if (tree.isEmpty) tree.asInstanceOf[Tree] - else { - assert(tree.hasType, tree.show) - val tree1 = super.typed(tree, pt) - def sameType(tp1: Type, tp2: Type) = - (tp1 eq tp2) || // accept NoType / NoType - (tp1 =:= tp2) - def divergenceMsg = - s"""Types differ - |Original type : ${tree.typeOpt.show} - |After checking: ${tree1.tpe.show} - |Original tree : ${tree.show} - |After checking: ${tree1.show} - """.stripMargin - assert(sameType(tree1.tpe, tree.typeOpt), divergenceMsg) - tree1 - } + override def typed(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = try { + tree match { + case _: untpd.UnApply => + // can't recheck patterns + tree.asInstanceOf[tpd.Tree] + case _: untpd.TypedSplice | _: untpd.Thicket | _: EmptyValDef[_] => + super.typed(tree) + case _ if tree.isType => + promote(tree) + case _ => + val tree1 = super.typed(tree, pt) + def sameType(tp1: Type, tp2: Type) = + (tp1 eq tp2) || // accept NoType / NoType + (tp1 =:= tp2) + def divergenceMsg = + s"""Types differ + |Original type : ${tree.typeOpt.show} + |After checking: ${tree1.tpe.show} + |Original tree : ${tree.show} + |After checking: ${tree1.show} + """.stripMargin + assert(sameType(tree1.tpe, tree.typeOpt), divergenceMsg) + tree1 + } + } catch { + case ex: Throwable => + println(i"exception while checking $tree of class ${tree.getClass} # ${tree.uniqueId}") + throw ex + } override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree = { - assert(tree.isTerm, tree.show) + assert(tree.isTerm || ctx.phase.prev.id <= ctx.typerPhase.id, tree.show + " at " + ctx.phase) super.typedIdent(tree, pt) } override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { - assert(tree.isTerm, tree.show) + assert(tree.isTerm || ctx.phase.prev.id <= ctx.typerPhase.id, tree.show + " at " + ctx.phase) super.typedSelect(tree, pt) } } From afb541ade2fc83f47661e1d94cee00f7a4b32178 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 21 Apr 2014 14:49:40 +0200 Subject: [PATCH 14/36] Fix of TyperState#instType If a type variable TV1 is instantiated to a poly param, and the poly param is itself accomanied by a type variable TV2, then the instance of TV1 should be TV2 and not the original poly param. Otherwise we lose instantiations. This is demonstrated by running dotc on the .scala files in the dotc directory itself with option -Ycheck. --- src/dotty/tools/dotc/core/Constraint.scala | 5 ++++- src/dotty/tools/dotc/core/TyperState.scala | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/Constraint.scala b/src/dotty/tools/dotc/core/Constraint.scala index 64fb8764ebb6..d3d19ec305dc 100644 --- a/src/dotty/tools/dotc/core/Constraint.scala +++ b/src/dotty/tools/dotc/core/Constraint.scala @@ -66,7 +66,10 @@ class Constraint(val myMap: SimpleMap[PolyType, Array[Type]]) extends Showable { def typeVarOfParam(param: PolyParam): Type = { val entries = myMap(param.binder) if (entries == null) NoType - else typeVar(entries, param.paramNum) + else { + val tvar = typeVar(entries, param.paramNum) + if (tvar != null) tvar else NoType + } } /** A new constraint which is derived from this constraint by adding or replacing diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala index a4b365b172a4..65cebf967d61 100644 --- a/src/dotty/tools/dotc/core/TyperState.scala +++ b/src/dotty/tools/dotc/core/TyperState.scala @@ -27,6 +27,9 @@ class TyperState(val reporter: Reporter) extends DotClass with Showable { */ def instType(tvar: TypeVar): Type = constraint.at(tvar.origin) match { case _: TypeBounds => NoType + case tp: PolyParam => + var tvar1 = constraint.typeVarOfParam(tp) + if (tvar1.exists) tvar1 else tp case tp => tp } From 35366df3bed1e2a13aa41abfd0f75c7241f22197 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 21 Apr 2014 17:40:03 +0200 Subject: [PATCH 15/36] Make sure AnyVal is loaded by classfile parser. In dotc, AnyVal is a synthetic toplevel class. We need to make sure it is not loaded by the classfile parser from scala.AnyVal. To do this, the companion module class needs to be marked as absent (by setting its info to NoType). Also, added Repeated annotation which will be used in the next commit. --- src/dotty/tools/dotc/core/Definitions.scala | 24 +++++++++++---------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 211d9e9cd780..280e8de76890 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -30,11 +30,8 @@ class Definitions { private def newCompleteClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, parents: List[TypeRef], decls: Scope = newScope) = ctx.newCompleteClassSymbol(owner, name, flags | Permanent, parents, decls).entered - private def newTopClassSymbol(name: TypeName, flags: FlagSet, parents: List[TypeRef]) = { - val cls = newCompleteClassSymbol(ScalaPackageClass, name, flags, parents) - ensureConstructor(cls, EmptyScope) - cls - } + private def newTopClassSymbol(name: TypeName, flags: FlagSet, parents: List[TypeRef]) = + completeClass(newCompleteClassSymbol(ScalaPackageClass, name, flags, parents)) private def newTypeParam(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = scope.enter(newSymbol(cls, name, flags | TypeParamCreationFlags, TypeBounds.empty)) @@ -88,6 +85,12 @@ class Definitions { arr } + private def completeClass(cls: ClassSymbol): ClassSymbol = { + ensureConstructor(cls, EmptyScope) + if (cls.linkedClass.exists) cls.linkedClass.info = NoType + cls + } + lazy val RootClass: ClassSymbol = ctx.newPackageSymbol( NoSymbol, nme.ROOT, (root, rootcls) => ctx.rootLoader(root)).moduleClass.asClass lazy val RootPackage: TermSymbol = ctx.newSymbol( @@ -123,8 +126,8 @@ class Definitions { * def getClass: java.lang.Class[T] = ??? * } */ - lazy val AnyClass: ClassSymbol = newTopClassSymbol(tpnme.Any, Abstract, Nil) - lazy val AnyValClass: ClassSymbol = newTopClassSymbol(tpnme.AnyVal, Abstract, AnyClass.typeRef :: Nil) + lazy val AnyClass: ClassSymbol = completeClass(newCompleteClassSymbol(ScalaPackageClass, tpnme.Any, Abstract, Nil)) + lazy val AnyValClass: ClassSymbol = completeClass(newCompleteClassSymbol(ScalaPackageClass, tpnme.AnyVal, Abstract, List(AnyClass.typeRef))) lazy val Any_== = newMethod(AnyClass, nme.EQ, methOfAny(BooleanType), Final) lazy val Any_!= = newMethod(AnyClass, nme.NE, methOfAny(BooleanType), Final) @@ -143,12 +146,10 @@ class Definitions { val cls = ctx.requiredClass("java.lang.Object") assert(!cls.isCompleted, "race for completing java.lang.Object") cls.info = ClassInfo(cls.owner.thisType, cls, AnyClass.typeRef :: Nil, newScope) - cls.linkedClass.info = NoType - ensureConstructor(cls, cls.decls) - cls + completeClass(cls) } lazy val AnyRefAlias: TypeSymbol = newAliasType(tpnme.AnyRef, ObjectType) - + lazy val Object_eq = newMethod(ObjectClass, nme.eq, methOfAnyRef(BooleanType), Final) lazy val Object_ne = newMethod(ObjectClass, nme.ne, methOfAnyRef(BooleanType), Final) lazy val Object_synchronized = newPolyMethod(ObjectClass, nme.synchronized_, 1, @@ -282,6 +283,7 @@ class Definitions { // Annotation classes lazy val AliasAnnot = ctx.requiredClass("dotty.annotation.internal.Alias") lazy val ChildAnnot = ctx.requiredClass("dotty.annotation.internal.Child") + lazy val RepeatedAnnot = ctx.requiredClass("dotty.annotation.internal.Repeated") lazy val InvariantBetweenClass = ctx.requiredClass("dotty.annotation.internal.InvariantBetween") lazy val CovariantBetweenClass = ctx.requiredClass("dotty.annotation.internal.CovariantBetween") lazy val ContravariantBetweenClass = ctx.requiredClass("dotty.annotation.internal.ContravariantBetween") From 782c24ff51865e43e34a2485dc585a757ada2c3b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 21 Apr 2014 18:10:25 +0200 Subject: [PATCH 16/36] Changed handling of repeated parameters. Previously, repeated parameters were typed as `[T]`. The method `underlyingWithRepeated` converts `[T]` to `Seq[T]`. This method was called in typedIdent, but the call was ineffective because the type of a repeated parameter ident is a TermRef. This led to a retyping error in Decorators.scala under -Ycheck:front. We now distinguish between the type of the internal parameter ValDef and the type of the parameter in the MethodType. The former has the type `Seq[T] @dotty.annotation.internal.repeated`, the latter has the type `[T]`. The translation with `underlyingWithRepeated` thus becomes unneccessary. --- src/dotty/annotation/internal/Repeated.scala | 5 +++++ src/dotty/tools/dotc/Main.scala | 2 -- src/dotty/tools/dotc/ast/Desugar.scala | 4 +++- src/dotty/tools/dotc/ast/Trees.scala | 1 + src/dotty/tools/dotc/core/Types.scala | 12 +++++++++--- src/dotty/tools/dotc/typer/TypeAssigner.scala | 11 ++++------- src/dotty/tools/dotc/typer/Typer.scala | 2 +- 7 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 src/dotty/annotation/internal/Repeated.scala diff --git a/src/dotty/annotation/internal/Repeated.scala b/src/dotty/annotation/internal/Repeated.scala new file mode 100644 index 000000000000..94e9df858d1f --- /dev/null +++ b/src/dotty/annotation/internal/Repeated.scala @@ -0,0 +1,5 @@ +package dotty.annotation.internal + +import scala.annotation.Annotation + +final class Repeated() extends Annotation \ No newline at end of file diff --git a/src/dotty/tools/dotc/Main.scala b/src/dotty/tools/dotc/Main.scala index 72f29fe6e9db..0b136d72f7b9 100644 --- a/src/dotty/tools/dotc/Main.scala +++ b/src/dotty/tools/dotc/Main.scala @@ -9,8 +9,6 @@ import core.Contexts.Context import reporting.Reporter /* To do: -s * - Revise the way classes are inherited - when not followed by [...] or (...), - * assume the unparameterized type and forward type parameters as we do now for the synthetic head class. */ object Main extends Driver { def resident(compiler: Compiler): Reporter = unsupported("resident") /*loop { line => diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 8d891b9d96a8..513dee2ffe2a 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -751,7 +751,9 @@ object desugar { makeBinop(l, op, r) case PostfixOp(t, op) => if ((ctx.mode is Mode.Type) && op == nme.raw.STAR) - AppliedTypeTree(ref(defn.RepeatedParamType), t) + Annotated( + New(ref(defn.RepeatedAnnot.typeRef), Nil :: Nil), + AppliedTypeTree(ref(defn.SeqClass.typeRef), t)) else { assert(ctx.mode.isExpr, ctx.mode) Select(t, op) diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 8df48696802f..e11bdeefc810 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -211,6 +211,7 @@ object Trees { */ val uniqueId = { nextId += 1 + //assert(nextId != 214, this) nextId } diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index b17c40eb760f..1107f184fa8c 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -597,7 +597,7 @@ object Types { final def objToAny(implicit ctx: Context) = if ((this isRef defn.ObjectClass) && !ctx.phase.erasedTypes) defn.AnyType else this - /** If this is repeated parameter type, its underlying type, + /** If this is repeated parameter type, its underlying Seq type, * else the type itself. */ def underlyingIfRepeated(implicit ctx: Context): Type = this match { @@ -1316,7 +1316,7 @@ object Types { def withSymAndName(prefix: Type, sym: TermSymbol, name: TermName)(implicit ctx: Context): TermRef = if (prefix eq NoPrefix) withNonMemberSym(prefix, name, sym) - else if (sym.defRunId != NoRunId && sym.isCompleted) + else if (sym.defRunId != NoRunId && sym.isCompleted) withSig(prefix, name, sym.signature) withSym (sym, sym.signature) else apply(prefix, name) withSym (sym, Signature.NotAMethod) @@ -1664,9 +1664,15 @@ object Types { def apply(paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType = apply(nme.syntheticParamNames(paramTypes.length), paramTypes, resultType) def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context) = { + def paramInfo(param: Symbol): Type = param.info match { + case AnnotatedType(annot, tp) if annot matches defn.RepeatedAnnot => + tp.translateParameterized(defn.SeqClass, defn.RepeatedParamClass) + case tp => + tp + } def transformResult(mt: MethodType) = resultType.subst(params, (0 until params.length).toList map (MethodParam(mt, _))) - apply(params map (_.name.asTermName), params map (_.info))(transformResult _) + apply(params map (_.name.asTermName), params map paramInfo)(transformResult _) } } diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 12f2415d89da..13f65d42491f 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -166,17 +166,14 @@ trait TypeAssigner { * - any further information it needs to access to compute that type. */ - def assignType(tree: untpd.Ident, rawType: Type)(implicit ctx: Context) = { - tree.withType(if (tree.isType) rawType else rawType.underlyingIfRepeated) - } + def assignType(tree: untpd.Ident, tp: Type)(implicit ctx: Context) = + tree.withType(tp) - def assignType(tree: untpd.Select, qual: Tree)(implicit ctx: Context) = { + def assignType(tree: untpd.Select, qual: Tree)(implicit ctx: Context) = tree.withType(accessibleSelectionType(tree, qual)) - } - def assignType(tree: untpd.SelectFromTypeTree, qual: Tree)(implicit ctx: Context) = { + def assignType(tree: untpd.SelectFromTypeTree, qual: Tree)(implicit ctx: Context) = tree.withType(accessibleSelectionType(tree, qual)) - } def assignType(tree: untpd.New, tpt: Tree)(implicit ctx: Context) = tree.withType(tpt.tpe) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index d46b13818964..c79ecdf85f4f 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -260,7 +260,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit error(d"not found: $kind$name", tree.pos) ErrorType } - checkValue(tree.withType(ownType.underlyingIfRepeated), pt) + checkValue(tree.withType(ownType), pt) } def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = track("typedSelect") { From 3ae84523f96d36afa922c9655d88b60417d0d42d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 21 Apr 2014 18:13:38 +0200 Subject: [PATCH 17/36] Moved tests to pending because they fail -Ycheck:front --- test/dotc/tests.scala | 2 +- tests/{ => pending}/pos/t0786.scala | 0 tests/{ => pending}/pos/t1164.scala | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename tests/{ => pending}/pos/t0786.scala (100%) rename tests/{ => pending}/pos/t1164.scala (100%) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 3f99a9d5b386..4234a65419a5 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -12,7 +12,7 @@ class tests extends CompilerTest { // "-Xprompt", // "-explaintypes", // "-Yshow-suppressed-errors", - "-pagewidth", "160"//, "-Ystop-before:terminal" + "-pagewidth", "160" //, "-Ycheck:front" , "-Ystop-before:terminal" ) val twice = List("#runs", "2", "-YnoDoubleBindings") val doErase = List("-Ystop-before:terminal") diff --git a/tests/pos/t0786.scala b/tests/pending/pos/t0786.scala similarity index 100% rename from tests/pos/t0786.scala rename to tests/pending/pos/t0786.scala diff --git a/tests/pos/t1164.scala b/tests/pending/pos/t1164.scala similarity index 100% rename from tests/pos/t1164.scala rename to tests/pending/pos/t1164.scala From 79632fb0eeb6adb82739e41cae671d698278aab9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 22 Apr 2014 08:48:41 +0200 Subject: [PATCH 18/36] Avoid retyping of Literals Literals need to be promoted as is, because constant folding might have changed the type of the literal to the expected type. E.g. in val x: Byte = 2 The literal 2 will have type `Byte(2)` but its underlying constant will still be `Int(2)`. So re-typing would give the wrong type. --- src/dotty/tools/dotc/typer/ReTyper.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala index 7c2cd5b1703e..8d81347819a6 100644 --- a/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/src/dotty/tools/dotc/typer/ReTyper.scala @@ -40,6 +40,9 @@ class ReTyper extends Typer { untpd.cpy.SelectFromTypeTree(tree, qual1, tree.name).withType(tree.typeOpt) } + override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal = + promote(tree) + override def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = promote(tree) From f84a49d4502c9a1ad328c1f5a3a558afade63848 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 Apr 2014 16:05:02 +0200 Subject: [PATCH 19/36] Fixing tailrec test. null is not a subtype of a singleton type in Dotty. This slipped by the type checker before. With the tightened rules in the next commits, it will become illegal. --- tests/neg/tailcall/tailrec-2.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/neg/tailcall/tailrec-2.scala b/tests/neg/tailcall/tailrec-2.scala index d6b8b1355b01..bc594293d7f6 100644 --- a/tests/neg/tailcall/tailrec-2.scala +++ b/tests/neg/tailcall/tailrec-2.scala @@ -13,7 +13,7 @@ class Bop2[+A](val element: A) extends Super[A] { @annotation.tailrec final def f[B >: A](mem: List[B]): List[B] = (null: Bop2[A]).f(mem) } object Bop3 extends Super[Nothing] { - @annotation.tailrec final def f[B](mem: List[B]): List[B] = (null: Bop3.type).f(mem) + @annotation.tailrec final def f[B](mem: List[B]): List[B] = (???: Bop3.type).f(mem) } class Bop4[+A](val element: A) extends Super[A] { @annotation.tailrec final def f[B >: A](mem: List[B]): List[B] = Other.f[A].f(mem) From ef8f24203f4a3ef75d0b8e45a9dd9470bd474e7d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 Apr 2014 16:14:42 +0200 Subject: [PATCH 20/36] Tightened satisfiablity checks. Satisfiability was too loose before. It is noww tightened. We check that the lower bounds of all constrained parameters represent a solution to the constraint. To make the check pass we have to first propagate the constraint by re-verifying all bounds. --- src/dotty/tools/dotc/core/Constraint.scala | 12 +++++ src/dotty/tools/dotc/core/TypeComparer.scala | 48 ++++++++++++++++++-- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/core/Constraint.scala b/src/dotty/tools/dotc/core/Constraint.scala index d3d19ec305dc..a16c85316662 100644 --- a/src/dotty/tools/dotc/core/Constraint.scala +++ b/src/dotty/tools/dotc/core/Constraint.scala @@ -77,6 +77,8 @@ class Constraint(val myMap: SimpleMap[PolyType, Array[Type]]) extends Showable { */ private def updateEntries(pt: PolyType, entries: Array[Type])(implicit ctx: Context) : Constraint = { val res = new Constraint(myMap.updated(pt, entries)) + //assert(res.domainPolys.filter(pt => + // pt.resultType.resultType.widen.classSymbol.name.toString == "Ensuring").length < 2) //DEBUG if (Config.checkConstraintsNonCyclic) checkNonCyclic(pt, entries) ctx.runInfo.recordConstraintSize(res) res @@ -246,6 +248,16 @@ class Constraint(val myMap: SimpleMap[PolyType, Array[Type]]) extends Showable { if isBounds(entries(n)) } yield PolyParam(poly, n) + /** Check whether predicate holds for all parameters in constraint + */ + def forallParams(p: PolyParam => Boolean): Boolean = { + myMap.foreachBinding { (poly, entries) => + for (i <- 0 until paramCount(entries)) + if (isBounds(entries(i)) && !p(PolyParam(poly, i))) return false + } + true + } + /** Perform operation `op` on all typevars, or only on uninstantiated * typevars, depending on whether `uninstOnly` is set or not. */ diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index cab09826b9b8..319b9957e150 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -79,13 +79,50 @@ class TypeComparer(initctx: Context) extends DotClass { myAnyType } + /** Map that approximates each param in constraint by its lower bound */ + val approxParams = new TypeMap { + def apply(tp: Type): Type = tp.stripTypeVar match { + case tp: PolyParam if constraint contains tp => + this(constraint.bounds(tp).lo) + case tp => + mapOver(tp) + } + } + + /** Propagate new constraint by comparing all bounds */ + def propagate: Boolean = + constraint.forallParams { poly => + val TypeBounds(lo, hi) = constraint.bounds(poly) + isSubType(lo, hi) + } + + /** Check whether the lower bounds of all parameters in this + * constraint are a solution to the constraint. + */ + def isSatisfiable: Boolean = + constraint.forallParams { poly => + val TypeBounds(lo, hi) = constraint.bounds(poly) + isSubType(approxParams(lo), approxParams(hi)) || + { ctx.log(i"sub fail $lo <:< $hi") + ctx.log(i"approximated = ${approxParams(lo)} <:< ${approxParams(hi)}") + ctx.log(TypeComparer.explained(implicit ctx => isSubType(approxParams(lo), approxParams(hi)))) + false + } + } + /** Update constraint for `param` to `bounds`, check that * new constraint is still satisfiable. */ private def updateConstraint(param: PolyParam, bounds: TypeBounds): Boolean = { val saved = constraint constraint = constraint.updated(param, bounds) - isSubType(bounds.lo, bounds.hi) || + + propagate && + { isSatisfiable || { + ctx.log(i"SAT $constraint produced by $param $bounds is not satisfiable") + false + } + } || { constraint = saved; false } // don't leave the constraint in unsatisfiable state } @@ -101,7 +138,7 @@ class TypeComparer(initctx: Context) extends DotClass { finally ignoreConstraint = saved val res = (param == bound) || (oldBounds eq newBounds) || updateConstraint(param, newBounds) - constr.println(s"add constraint $param ${if (fromBelow) ">:" else "<:"} $bound = $res") + constr.println(s"added1 constraint $param ${if (fromBelow) ">:" else "<:"} $bound = $res") if (res) constr.println(constraint.show) res } @@ -127,7 +164,8 @@ class TypeComparer(initctx: Context) extends DotClass { def addConstraint(param: PolyParam, bound0: Type, fromBelow: Boolean): Boolean = { assert(!frozenConstraint) val bound = bound0.dealias.stripTypeVar - constr.println(s"adding ${param.show} ${if (fromBelow) ">:>" else "<:<"} ${bound.show} (${bound.getClass}) to ${constraint.show}") + def description = s"${param.show} ${if (fromBelow) ">:>" else "<:<"} ${bound.show} (${bound.getClass}) to ${constraint.show}" + constr.println(s"adding $description") val res = bound match { case bound: PolyParam if constraint contains bound => val TypeBounds(lo, hi) = constraint.bounds(bound) @@ -150,7 +188,7 @@ class TypeComparer(initctx: Context) extends DotClass { case bound => // !!! remove to keep the originals addConstraint1(param, bound, fromBelow) } - constr.println(s"added ${param.show} ${if (fromBelow) ">:>" else "<:<"} ${bound.show} = ${constraint.show}") + constr.println(s"added $description = ${constraint.show}") res } @@ -656,7 +694,7 @@ class TypeComparer(initctx: Context) extends DotClass { v == 0 || tparam.variance == v } hk.println(s"isSubTypeHK: args1 = $args1, hk-bounds = $hkBounds $boundsOK $variancesOK") - boundsOK && variancesOK + boundsOK && variancesOK || fourthTry(tp1, tp2) } def trySetType(tr: NamedType, bounds: TypeBounds): Boolean = From 15345390b10e60d0cffd0c1eb01a61a9ef28b6d1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 Apr 2014 16:15:39 +0200 Subject: [PATCH 21/36] Excluding <:< from eligible candidates for views. This is an opimization to save on unncessessary typed implicits. --- src/dotty/tools/dotc/core/Definitions.scala | 1 + src/dotty/tools/dotc/typer/Implicits.scala | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 280e8de76890..6f34efc8bcc8 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -178,6 +178,7 @@ class Definitions { lazy val BoxesRunTimeClass = BoxesRunTimeModule.moduleClass lazy val DottyPredefModule = ctx.requiredModule("dotty.DottyPredef") lazy val NilModule = ctx.requiredModule("scala.collection.immutable.Nil") + lazy val PredefConformsClass = ctx.requiredClass("scala.Predef." + tpnme.Conforms) // lazy val FunctionClass: ClassSymbol = ctx.requiredClass("scala.Function") lazy val SingletonClass: ClassSymbol = diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index 4767e6dd3dda..d4cfc1b8e31d 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -65,7 +65,7 @@ object Implicits { case tpw => //if (ctx.typer.isApplicable(tp, argType :: Nil, resultType)) // println(i"??? $tp is applicable to $this / typeSymbol = ${tpw.typeSymbol}") - !tpw.derivesFrom(defn.FunctionClass(1)) + !tpw.derivesFrom(defn.FunctionClass(1)) || tpw.isRef(defn.PredefConformsClass) } def discardForValueType(tpw: Type): Boolean = tpw match { From 90aa3ba1df70fea42b4a1cafe6551e7c3204f012 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 Apr 2014 17:09:22 +0200 Subject: [PATCH 22/36] Better wildcard type approximation 1) PolyParams are now approximated with a bounded wildcardtype representing the bounds in the current constraint, rather than the bounds in the parameter's declaration. That makes them consistent with the handling of TypeVars. 2) Wildcard types are taken into consideration when result types are constrained. --- src/dotty/tools/dotc/typer/ProtoTypes.scala | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala index aeba02648da1..4aba4fb59ac4 100644 --- a/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -46,7 +46,7 @@ object ProtoTypes { * fits the given expected result type. */ def constrainResult(mt: Type, pt: Type)(implicit ctx: Context): Boolean = pt match { - case FunProto(_, result, _) => + case _: FunProto => mt match { case mt: MethodType => mt.isDependent || constrainResult(mt.resultType, pt.resultType) @@ -60,6 +60,8 @@ object ProtoTypes { case _ => isCompatible(mt, pt) } + case _: WildcardType => + isCompatible(mt, pt) case _ => true } @@ -217,9 +219,8 @@ object ProtoTypes { */ abstract case class ViewProto(argType: Type, override val resultType: Type)(implicit ctx: Context) extends CachedGroundType with ApplyingProto { - def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean = /*ctx.conditionalTraceIndented(lookingForInfo, i"?.info isMatchedBy $tp ${tp.getClass}")*/ { + def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean = ctx.typer.isApplicable(tp, argType :: Nil, resultType) - } def derivedViewProto(argType: Type, resultType: Type)(implicit ctx: Context) = if ((argType eq this.argType) && (resultType eq this.resultType)) this @@ -352,17 +353,15 @@ object ProtoTypes { tp.derivedRefinedType(wildApprox(tp.parent, theMap), tp.refinedName, wildApprox(tp.refinedInfo, theMap)) case tp: TypeBounds if tp.lo eq tp.hi => // default case, inlined for speed tp.derivedTypeAlias(wildApprox(tp.lo, theMap)) - case PolyParam(pt, pnum) => - WildcardType(wildApprox(pt.paramBounds(pnum)).bounds) + case tp @ PolyParam(poly, pnum) => + ctx.typerState.constraint.at(tp) match { + case bounds: TypeBounds => wildApprox(WildcardType(bounds)) + case _ => WildcardType(wildApprox(poly.paramBounds(pnum)).bounds) + } case MethodParam(mt, pnum) => WildcardType(TypeBounds.upper(wildApprox(mt.paramTypes(pnum)))) case tp: TypeVar => - val inst = tp.instanceOpt - if (inst.exists) wildApprox(inst) - else ctx.typerState.constraint.at(tp.origin) match { - case bounds: TypeBounds => wildApprox(WildcardType(bounds)) - case NoType => WildcardType - } + wildApprox(tp.underlying) case tp: AndType => val tp1a = wildApprox(tp.tp1) val tp2a = wildApprox(tp.tp2) From ba2913bdb52bf1a42bd831e9429e044ee8d3c42f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 Apr 2014 17:14:27 +0200 Subject: [PATCH 23/36] Changes to test framework and tests defaultOptions is now an implicit parameter, which means it can be overridden on a call-by-call basis. Added -Ycheck:front to verify that typed trees typecheck again with same types. The option is disabled for one of the structural tests. --- test/dotc/comptest.scala | 2 +- test/dotc/tests.scala | 16 +++++++++++----- test/test/CompilerTest.scala | 16 +++++++--------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/test/dotc/comptest.scala b/test/dotc/comptest.scala index e7f60488b89d..63c4003047f3 100644 --- a/test/dotc/comptest.scala +++ b/test/dotc/comptest.scala @@ -15,7 +15,7 @@ object comptest extends CompilerTest { dotcDir + "tools/dotc/ast/Trees.scala", "#runs", "2", "-Ylog:frontend", - "-Xprompt")) + "-Xprompt"))(Nil) // compileDir(dotcDir + "tools/dotc/printing", List("-Xprompt", "-Ylog:frontend", "#runs", "2", "-uniqid")) } diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 4234a65419a5..436ca876fa70 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -5,15 +5,18 @@ import test._ class tests extends CompilerTest { - override val defaultOptions = - List( + val noCheckOptions = List( // "-verbose", // "-Ylog:frontend", // "-Xprompt", // "-explaintypes", // "-Yshow-suppressed-errors", - "-pagewidth", "160" //, "-Ycheck:front" , "-Ystop-before:terminal" - ) + "-pagewidth", "160") + + implicit val defaultOptions = noCheckOptions ++ List( + "-Ycheck:front"//, "-Ystop-before:terminal" + ) + val twice = List("#runs", "2", "-YnoDoubleBindings") val doErase = List("-Ystop-before:terminal") @@ -68,7 +71,10 @@ class tests extends CompilerTest { @Test def neg_i50_volatile = compileFile(negDir, "i50-volatile", xerrors = 4) @Test def neg_t0273_doubledefs = compileFile(negDir, "t0273", xerrors = 1) @Test def neg_t0586_structural = compileFile(negDir, "t0586", xerrors = 1) - @Test def neg_t0625_structural = compileFile(negDir, "t0625", xerrors = 1) + @Test def neg_t0625_structural = compileFile(negDir, "t0625", xerrors = 1)( + defaultOptions = noCheckOptions) + // -Ycheck fails because there are structural types involving higher-kinded types. + // these are illegal, but are tested only later. @Test def neg_t0654_polyalias = compileFile(negDir, "t0654", xerrors = 2) @Test def neg_t1192_legalPrefix = compileFile(negDir, "t1192", xerrors = 1) @Test def neg_tailcall_t1672b = compileFile(negDir, "tailcall/t1672b", xerrors = 6) diff --git a/test/test/CompilerTest.scala b/test/test/CompilerTest.scala index d52e74de7b9e..1bf138d54920 100644 --- a/test/test/CompilerTest.scala +++ b/test/test/CompilerTest.scala @@ -8,29 +8,27 @@ import dotty.tools.dotc.reporting.Reporter class CompilerTest extends DottyTest { - def defaultOptions: List[String] = Nil - - def compileArgs(args: Array[String], xerrors: Int = 0): Unit = { + def compileArgs(args: Array[String], xerrors: Int = 0)(implicit defaultOptions: List[String]): Unit = { val allArgs = args ++ defaultOptions val processor = if (allArgs.exists(_.startsWith("#"))) Bench else Main val nerrors = processor.process(allArgs, ctx).count(Reporter.ERROR.level) assert(nerrors == xerrors, s"Wrong # of errors. Expected: $xerrors, found: $nerrors") } - def compileLine(cmdLine: String, xerrors: Int = 0): Unit = compileArgs(cmdLine.split("\n"), xerrors) + def compileLine(cmdLine: String, xerrors: Int = 0)(implicit defaultOptions: List[String]): Unit = compileArgs(cmdLine.split("\n"), xerrors) - def compileFile(prefix: String, fileName: String, args: List[String] = Nil, xerrors: Int = 0): Unit = + def compileFile(prefix: String, fileName: String, args: List[String] = Nil, xerrors: Int = 0)(implicit defaultOptions: List[String]): Unit = compileArgs((s"$prefix$fileName.scala" :: args).toArray, xerrors) - def compileDir(path: String, args: List[String] = Nil, xerrors: Int = 0): Unit = + def compileDir(path: String, args: List[String] = Nil, xerrors: Int = 0)(implicit defaultOptions: List[String]): Unit = compileDir(Directory(path), args, xerrors) - def compileDir(dir: Directory, args: List[String], xerrors: Int): Unit = { + def compileDir(dir: Directory, args: List[String], xerrors: Int)(implicit defaultOptions: List[String]): Unit = { val fileNames = dir.files.toArray.map(_.toString).filter(_ endsWith ".scala") compileArgs(fileNames ++ args, xerrors) } - def compileFiles(path: String, args: List[String] = Nil): Unit = { + def compileFiles(path: String, args: List[String] = Nil)(implicit defaultOptions: List[String]): Unit = { val dir = Directory(path) val fileNames = dir.files.toArray.map(_.toString).filter(_ endsWith ".scala") for (name <- fileNames) { @@ -43,7 +41,7 @@ class CompilerTest extends DottyTest { } } } -object CompilerText extends App { +object CompilerTest extends App { // val dotcDir = "/Users/odersky/workspace/dotty/src/dotty/" From 4d47745e8fbadc26aaa093b3513d1c9b42798bed Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 Apr 2014 17:16:44 +0200 Subject: [PATCH 24/36] Re-enable t0786. t0786 works again after type inference fixes. Re-enabling. --- tests/{pending => }/pos/t0786.scala | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{pending => }/pos/t0786.scala (100%) diff --git a/tests/pending/pos/t0786.scala b/tests/pos/t0786.scala similarity index 100% rename from tests/pending/pos/t0786.scala rename to tests/pos/t0786.scala From cb1f9cb03f9610d0c6f4d8aa08b9144e90dcf2b5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 30 Apr 2014 14:54:18 +0200 Subject: [PATCH 25/36] Optimization: Avoid substituting when checking satisfiability Instead of replacing all constrained poly params by their lower bounds before checking satsfiability, we now do this on the fly in the subtype tests. --- src/dotty/tools/dotc/core/TypeComparer.scala | 95 ++++++++++++-------- 1 file changed, 59 insertions(+), 36 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 319b9957e150..f5f7ac367bb2 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -32,6 +32,11 @@ class TypeComparer(initctx: Context) extends DotClass { */ protected var ignoreConstraint = false + /** Compare a solution of the constraint instead of the constrained parameters. + * The solution maps every parameter to its lower bound. + */ + protected var solvedConstraint = false + private var needsGc = false /** Is a subtype check in course? In that case we may not @@ -82,7 +87,7 @@ class TypeComparer(initctx: Context) extends DotClass { /** Map that approximates each param in constraint by its lower bound */ val approxParams = new TypeMap { def apply(tp: Type): Type = tp.stripTypeVar match { - case tp: PolyParam if constraint contains tp => + case tp: PolyParam if !solvedConstraint && (constraint contains tp) => this(constraint.bounds(tp).lo) case tp => mapOver(tp) @@ -99,16 +104,30 @@ class TypeComparer(initctx: Context) extends DotClass { /** Check whether the lower bounds of all parameters in this * constraint are a solution to the constraint. */ - def isSatisfiable: Boolean = - constraint.forallParams { poly => - val TypeBounds(lo, hi) = constraint.bounds(poly) - isSubType(approxParams(lo), approxParams(hi)) || - { ctx.log(i"sub fail $lo <:< $hi") - ctx.log(i"approximated = ${approxParams(lo)} <:< ${approxParams(hi)}") - ctx.log(TypeComparer.explained(implicit ctx => isSubType(approxParams(lo), approxParams(hi)))) - false - } - } + def isSatisfiable(useSolved: Boolean): Boolean = { + val saved = solvedConstraint + solvedConstraint = true + try + constraint.forallParams { poly => + val TypeBounds(lo, hi) = constraint.bounds(poly) + isSubType(approxParams(lo), approxParams(hi)) || + { + ctx.log(i"sub fail $lo <:< $hi") + solvedConstraint = saved + ctx.log(i"approximated = ${approxParams(lo)} <:< ${approxParams(hi)}") + ctx.log(TypeComparer.explained(implicit ctx => isSubType(approxParams(lo), approxParams(hi)))) + false + } + } + finally solvedConstraint = saved + } + + def isSatisfiable: Boolean = { + val withSolved = isSatisfiable(true) + val withoutSolved = isSatisfiable(false) + assert(withSolved == withoutSolved, i"sat diff for $constraint, with solved: $withSolved, without: $withoutSolved") + withSolved + } /** Update constraint for `param` to `bounds`, check that * new constraint is still satisfiable. @@ -193,7 +212,7 @@ class TypeComparer(initctx: Context) extends DotClass { } def isConstrained(param: PolyParam): Boolean = - !frozenConstraint && (constraint contains param) + !frozenConstraint && !solvedConstraint && (constraint contains param) /** Solve constraint set for given type parameter `param`. * If `fromBelow` is true the parameter is approximated by its lower bound, @@ -343,13 +362,15 @@ class TypeComparer(initctx: Context) extends DotClass { case tp2: ProtoType => isMatchedByProto(tp2, tp1) case tp2: PolyParam => - def comparePolyParam = { - tp2 == tp1 || - isSubTypeWhenFrozen(tp1, bounds(tp2).lo) || { - if (isConstrained(tp2)) addConstraint(tp2, tp1.widen, fromBelow = true) - else (ctx.mode is Mode.TypevarsMissContext) || secondTry(tp1, tp2) - } - } + def comparePolyParam = + tp2 == tp1 || { + if (solvedConstraint && (constraint contains tp2)) isSubType(tp1, bounds(tp2).lo) + else + isSubTypeWhenFrozen(tp1, bounds(tp2).lo) || { + if (isConstrained(tp2)) addConstraint(tp2, tp1.widen, fromBelow = true) + else (ctx.mode is Mode.TypevarsMissContext) || secondTry(tp1, tp2) + } + } comparePolyParam case tp2: BoundType => tp2 == tp1 || secondTry(tp1, tp2) @@ -383,23 +404,25 @@ class TypeComparer(initctx: Context) extends DotClass { case OrType(tp11, tp12) => isSubType(tp11, tp2) && isSubType(tp12, tp2) case tp1: PolyParam => - def comparePolyParam = { - tp1 == tp2 || - isSubTypeWhenFrozen(bounds(tp1).hi, tp2) || { - if (isConstrained(tp1)) - addConstraint(tp1, tp2, fromBelow = false) && { - if ((!frozenConstraint) && - (tp2 isRef defn.NothingClass) && - state.isGlobalCommittable) { - def msg = s"!!! instantiated to Nothing: $tp1, constraint = ${constraint.show}" - if (Config.flagInstantiationToNothing) assert(false, msg) - else ctx.log(msg) - } - true + def comparePolyParam = + tp1 == tp2 || { + if (solvedConstraint && (constraint contains tp1)) isSubType(bounds(tp1).lo, tp2) + else + isSubTypeWhenFrozen(bounds(tp1).hi, tp2) || { + if (isConstrained(tp1)) + addConstraint(tp1, tp2, fromBelow = false) && { + if ((!frozenConstraint) && + (tp2 isRef defn.NothingClass) && + state.isGlobalCommittable) { + def msg = s"!!! instantiated to Nothing: $tp1, constraint = ${constraint.show}" + if (Config.flagInstantiationToNothing) assert(false, msg) + else ctx.log(msg) + } + true + } + else (ctx.mode is Mode.TypevarsMissContext) || thirdTry(tp1, tp2) } - else (ctx.mode is Mode.TypevarsMissContext) || thirdTry(tp1, tp2) } - } comparePolyParam case tp1: BoundType => tp1 == tp2 || thirdTry(tp1, tp2) @@ -628,7 +651,7 @@ class TypeComparer(initctx: Context) extends DotClass { /** Defer constraining type variables when compared against prototypes */ def isMatchedByProto(proto: ProtoType, tp: Type) = tp.stripTypeVar match { - case tp: PolyParam if constraint contains tp => true + case tp: PolyParam if !solvedConstraint && (constraint contains tp) => true case _ => proto.isMatchedBy(tp) } @@ -657,7 +680,7 @@ class TypeComparer(initctx: Context) extends DotClass { * type variable with (the corresponding type in) `tp2` instead. */ def isCappable(tp: Type): Boolean = tp match { - case tp: PolyParam => constraint contains tp + case tp: PolyParam => !solvedConstraint && (constraint contains tp) case tp: TypeProxy => isCappable(tp.underlying) case tp: AndOrType => isCappable(tp.tp1) || isCappable(tp.tp2) case _ => false From 9099df69ceb9a73073c63ba051d7da1230cf25fa Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 1 May 2014 11:16:34 +0200 Subject: [PATCH 26/36] Fix for type applications. Test case in TreeTransformers.scala. We have there type Mutator[T] = (TreeTransform, T, Context) => TreeTransform It turns out that then Mutator[X] for some type X did not typecheck because the typer got confused what were the type parameters of the Function3 type on Mutator's RHS. The fix adds a case to handle RefinedTypes that bind type parameters. --- .../tools/dotc/core/TypeApplications.scala | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 4b251f1839e2..c4845a249de4 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -111,7 +111,7 @@ class TypeApplications(val self: Type) extends AnyVal { /** Encode the type resulting from applying this type to given arguments */ final def appliedTo(args: List[Type])(implicit ctx: Context): Type = /*>|>*/ track("appliedTo") /*<|<*/ { - def recur(tp: Type, tparams: List[TypeSymbol], args: List[Type]): Type = args match { + def matchParams(tp: Type, tparams: List[TypeSymbol], args: List[Type]): Type = args match { case arg :: args1 => if (tparams.isEmpty) { println(s"applied type mismatch: $self $args, typeParams = $typeParams, tsym = ${self.typeSymbol.debugString}") // !!! DEBUG @@ -119,32 +119,46 @@ class TypeApplications(val self: Type) extends AnyVal { } val tparam = tparams.head val tp1 = RefinedType(tp, tparam.name, arg.toBounds(tparam)) - recur(tp1, tparams.tail, args1) + matchParams(tp1, tparams.tail, args1) case nil => tp } - def safeTypeParams(tsym: Symbol) = - if (tsym.isClass || !self.typeSymbol.isCompleting) typeParams - else { - ctx.warning("encountered F-bounded higher-kinded type parameters; assuming they are invariant") - defn.hkTrait(args map alwaysZero).typeParams - } - - if (args.isEmpty || !canHaveTypeParams) self - else self match { + /** Instantiate type `tp` with `args`. + * @param original The original type for which we compute the type parameters + * This makes a difference for refinement types, because + * refinements bind type parameters and thereby remove them + * from `typeParams`. + */ + def instantiate(tp: Type, original: Type): Type = tp match { case tp: TypeRef => val tsym = tp.symbol if (tsym.isAliasType) tp.underlying.appliedTo(args) - else recur(tp, safeTypeParams(tsym), args) + else { + val safeTypeParams = + if (tsym.isClass || !tp.typeSymbol.isCompleting) original.typeParams + else { + ctx.warning("encountered F-bounded higher-kinded type parameters; assuming they are invariant") + defn.hkTrait(args map alwaysZero).typeParams + } + matchParams(tp, safeTypeParams, args) + } + case tp: RefinedType => + tp.derivedRefinedType( + instantiate(tp.parent, original), + tp.refinedName, + tp.refinedInfo) case tp: TypeProxy => - tp.underlying.appliedTo(args) + instantiate(tp.underlying, original) case AndType(l, r) => l.appliedTo(args) & r case tp: PolyType => tp.instantiate(args) case ErrorType => - self + tp } + + if (args.isEmpty || !canHaveTypeParams) self + else instantiate(self, self) } final def appliedTo(arg: Type)(implicit ctx: Context): Type = appliedTo(arg :: Nil) From 615ad1f9eddc3dff9f8f4019ee9608a0b43db45f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 1 May 2014 11:17:47 +0200 Subject: [PATCH 27/36] Making dotc/transform Dotty compliant Fixes to make the files in `transform` compile in Dotty. --- .../tools/dotc/config/PathResolver.scala | 2 +- src/dotty/tools/dotc/core/Types.scala | 2 +- src/dotty/tools/dotc/transform/LazyVals.scala | 2 +- src/dotty/tools/dotc/transform/TailRec.scala | 5 +- .../tools/dotc/transform/TreeTransform.scala | 61 ++++++++++--------- 5 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/dotty/tools/dotc/config/PathResolver.scala b/src/dotty/tools/dotc/config/PathResolver.scala index 34678ae2bf54..f9f698e7238c 100644 --- a/src/dotty/tools/dotc/config/PathResolver.scala +++ b/src/dotty/tools/dotc/config/PathResolver.scala @@ -148,7 +148,7 @@ object PathResolver { println(Defaults) } else { - implicit val ctx = (new ContextBase).initialCtx + implicit val ctx: Context = (new ContextBase).initialCtx // Dotty deviation: implicits need explicit type val ArgsSummary(sstate, rest, errors) = ctx.settings.processArguments(args.toList, true) errors.foreach(println) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 1107f184fa8c..bb882e36cd7e 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2276,7 +2276,7 @@ object Types { /** Map this function over given type */ def mapOver(tp: Type): Type = { - implicit val ctx = this.ctx + implicit val ctx: Context = this.ctx // Dotty deviation: implicits need explicit type tp match { case tp: NamedType => if (stopAtStatic && tp.symbol.isStatic) tp diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index 9e5ddffa4249..527c7e228965 100644 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -54,7 +54,7 @@ class LazyValTranformContext { val infoTransformerNewDefinitions = mutable.Map.empty[ClassSymbol, ListBuffer[Symbol]] - def addSym(owner: ClassSymbol, sym: Symbol) { + def addSym(owner: ClassSymbol, sym: Symbol) = { infoTransformerNewDefinitions.get(owner) match { case Some(x) => x += sym case None => infoTransformerNewDefinitions.put(owner, ListBuffer(sym)) diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala index b0d5c643834d..7d9b37be10c5 100644 --- a/src/dotty/tools/dotc/transform/TailRec.scala +++ b/src/dotty/tools/dotc/transform/TailRec.scala @@ -213,7 +213,8 @@ class TailRec extends TreeTransform with DenotTransformer { val methodWithTargs = if (targs.nonEmpty) TypeApply(method, targs) else method if (methodWithTargs.tpe.widen.isParameterless) methodWithTargs else argumentss.foldLeft(methodWithTargs) { - case (method, args) => Apply(method, args) + // case (method, args) => Apply(method, args) // Dotty deviation no auto-detupling yet. Interesting that one can do it in Scala2! + (method, args) => Apply(method, args) } } def fail(reason: String) = { @@ -229,7 +230,7 @@ class TailRec extends TreeTransform with DenotTransformer { val recv = noTailTransform(reciever) if (recv.tpe.widen.isParameterless) method else argumentss.foldLeft(Apply(method, List(recv))) { - case (method, args) => Apply(method, args) + (method, args) => Apply(method, args) // Dotty deviation no auto-detupling yet. } } diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index c0ce68e95469..1bce0de66b57 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -886,32 +886,33 @@ object TreeTransforms { final private[TreeTransforms] def transformNamed(tree: NameTree, info: TransformerInfo, cur: Int)(implicit ctx: Context): Tree = tree match { case tree: Ident => - implicit val mutatedInfo = mutateTransformers(info, prepForIdent, info.nx.nxPrepIdent, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForIdent, info.nx.nxPrepIdent, tree, cur) + // Dotty deviation: implicits need explicit type if (mutatedInfo eq null) tree else goIdent(tree, mutatedInfo.nx.nxTransIdent(cur)) case tree: Select => - implicit val mutatedInfo = mutateTransformers(info, prepForSelect, info.nx.nxPrepSelect, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForSelect, info.nx.nxPrepSelect, tree, cur) if (mutatedInfo eq null) tree else { val qual = transform(tree.qualifier, mutatedInfo, cur) goSelect(cpy.Select(tree, qual, tree.name), mutatedInfo.nx.nxTransSelect(cur)) } case tree: SelectFromTypeTree => - implicit val mutatedInfo = mutateTransformers(info, prepForSelectFromTypeTree, info.nx.nxPrepSelectFromTypeTree, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForSelectFromTypeTree, info.nx.nxPrepSelectFromTypeTree, tree, cur) if (mutatedInfo eq null) tree else { val qual = transform(tree.qualifier, mutatedInfo, cur) goSelectFromTypeTree(cpy.SelectFromTypeTree(tree, qual, tree.name), mutatedInfo.nx.nxTransSelectFromTypeTree(cur)) } case tree: Bind => - implicit val mutatedInfo = mutateTransformers(info, prepForBind, info.nx.nxPrepBind, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForBind, info.nx.nxPrepBind, tree, cur) if (mutatedInfo eq null) tree else { val body = transform(tree.body, mutatedInfo, mutatedInfo.nx.nxTransBind(cur)) goBind(cpy.Bind(tree, tree.name, body), cur) } case tree: ValDef if !tree.isEmpty => // As a result of discussing with Martin: emptyValDefs shouldn't be copied // NAME - implicit val mutatedInfo = mutateTransformers(info, prepForValDef, info.nx.nxPrepValDef, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForValDef, info.nx.nxPrepValDef, tree, cur) if (mutatedInfo eq null) tree else { val nestedCtx = if (tree.symbol.exists) localContext(tree.symbol) else ctx @@ -920,7 +921,7 @@ object TreeTransforms { goValDef(cpy.ValDef(tree, tree.mods, tree.name, tpt, rhs), mutatedInfo.nx.nxTransValDef(cur)) } case tree: DefDef => - implicit val mutatedInfo = mutateTransformers(info, prepForDefDef, info.nx.nxPrepDefDef, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForDefDef, info.nx.nxPrepDefDef, tree, cur) if (mutatedInfo eq null) tree else { val nestedCtx = localContext(tree.symbol) @@ -931,7 +932,7 @@ object TreeTransforms { goDefDef(cpy.DefDef(tree, tree.mods, tree.name, tparams, vparams, tpt, rhs), mutatedInfo.nx.nxTransDefDef(cur)) } case tree: TypeDef => - implicit val mutatedInfo = mutateTransformers(info, prepForTypeDef, info.nx.nxPrepTypeDef, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeDef, info.nx.nxPrepTypeDef, tree, cur) if (mutatedInfo eq null) tree else { val rhs = transform(tree.rhs, mutatedInfo, cur)(localContext(tree.symbol)) @@ -944,18 +945,18 @@ object TreeTransforms { final private[TreeTransforms] def transformUnnamed(tree: Tree, info: TransformerInfo, cur: Int)(implicit ctx: Context): Tree = tree match { case tree: This => - implicit val mutatedInfo = mutateTransformers(info, prepForThis, info.nx.nxPrepThis, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForThis, info.nx.nxPrepThis, tree, cur) if (mutatedInfo eq null) tree else goThis(tree, mutatedInfo.nx.nxTransThis(cur)) case tree: Super => - implicit val mutatedInfo = mutateTransformers(info, prepForSuper, info.nx.nxPrepSuper, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForSuper, info.nx.nxPrepSuper, tree, cur) if (mutatedInfo eq null) tree else { val qual = transform(tree.qual, mutatedInfo, cur) goSuper(cpy.Super(tree, qual, tree.mix), mutatedInfo.nx.nxTransSuper(cur)) } case tree: Apply => - implicit val mutatedInfo = mutateTransformers(info, prepForApply, info.nx.nxPrepApply, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForApply, info.nx.nxPrepApply, tree, cur) if (mutatedInfo eq null) tree else { val fun = transform(tree.fun, mutatedInfo, cur) @@ -963,7 +964,7 @@ object TreeTransforms { goApply(cpy.Apply(tree, fun, args), mutatedInfo.nx.nxTransApply(cur)) } case tree: TypeApply => - implicit val mutatedInfo = mutateTransformers(info, prepForTypeApply, info.nx.nxPrepTypeApply, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeApply, info.nx.nxPrepTypeApply, tree, cur) if (mutatedInfo eq null) tree else { val fun = transform(tree.fun, mutatedInfo, cur) @@ -971,18 +972,18 @@ object TreeTransforms { goTypeApply(cpy.TypeApply(tree, fun, args), mutatedInfo.nx.nxTransTypeApply(cur)) } case tree: Literal => - implicit val mutatedInfo = mutateTransformers(info, prepForLiteral, info.nx.nxPrepLiteral, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForLiteral, info.nx.nxPrepLiteral, tree, cur) if (mutatedInfo eq null) tree else goLiteral(tree, mutatedInfo.nx.nxTransLiteral(cur)) case tree: New => - implicit val mutatedInfo = mutateTransformers(info, prepForNew, info.nx.nxPrepNew, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForNew, info.nx.nxPrepNew, tree, cur) if (mutatedInfo eq null) tree else { val tpt = transform(tree.tpt, mutatedInfo, cur) goNew(cpy.New(tree, tpt), mutatedInfo.nx.nxTransNew(cur)) } case tree: Pair => - implicit val mutatedInfo = mutateTransformers(info, prepForPair, info.nx.nxPrepPair, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForPair, info.nx.nxPrepPair, tree, cur) if (mutatedInfo eq null) tree else { val left = transform(tree.left, mutatedInfo, cur) @@ -990,7 +991,7 @@ object TreeTransforms { goPair(cpy.Pair(tree, left, right), mutatedInfo.nx.nxTransPair(cur)) } case tree: Typed => - implicit val mutatedInfo = mutateTransformers(info, prepForTyped, info.nx.nxPrepTyped, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTyped, info.nx.nxPrepTyped, tree, cur) if (mutatedInfo eq null) tree else { val expr = transform(tree.expr, mutatedInfo, cur) @@ -998,7 +999,7 @@ object TreeTransforms { goTyped(cpy.Typed(tree, expr, tpt), mutatedInfo.nx.nxTransTyped(cur)) } case tree: Assign => - implicit val mutatedInfo = mutateTransformers(info, prepForAssign, info.nx.nxPrepAssign, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForAssign, info.nx.nxPrepAssign, tree, cur) if (mutatedInfo eq null) tree else { val lhs = transform(tree.lhs, mutatedInfo, cur) @@ -1006,7 +1007,7 @@ object TreeTransforms { goAssign(cpy.Assign(tree, lhs, rhs), mutatedInfo.nx.nxTransAssign(cur)) } case tree: Block => - implicit val mutatedInfo = mutateTransformers(info, prepForBlock, info.nx.nxPrepBlock, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForBlock, info.nx.nxPrepBlock, tree, cur) if (mutatedInfo eq null) tree else { val stats = transformStats(tree.stats, ctx.owner, mutatedInfo, cur) @@ -1014,7 +1015,7 @@ object TreeTransforms { goBlock(cpy.Block(tree, stats, expr), mutatedInfo.nx.nxTransBlock(cur)) } case tree: If => - implicit val mutatedInfo = mutateTransformers(info, prepForIf, info.nx.nxPrepIf, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForIf, info.nx.nxPrepIf, tree, cur) if (mutatedInfo eq null) tree else { val cond = transform(tree.cond, mutatedInfo, cur) @@ -1023,7 +1024,7 @@ object TreeTransforms { goIf(cpy.If(tree, cond, thenp, elsep), mutatedInfo.nx.nxTransIf(cur)) } case tree: Closure => - implicit val mutatedInfo = mutateTransformers(info, prepForClosure, info.nx.nxPrepClosure, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForClosure, info.nx.nxPrepClosure, tree, cur) if (mutatedInfo eq null) tree else { val env = transformTrees(tree.env, mutatedInfo, cur) @@ -1032,7 +1033,7 @@ object TreeTransforms { goClosure(cpy.Closure(tree, env, meth, tpt), mutatedInfo.nx.nxTransClosure(cur)) } case tree: Match => - implicit val mutatedInfo = mutateTransformers(info, prepForMatch, info.nx.nxPrepMatch, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForMatch, info.nx.nxPrepMatch, tree, cur) if (mutatedInfo eq null) tree else { val selector = transform(tree.selector, mutatedInfo, cur) @@ -1040,7 +1041,7 @@ object TreeTransforms { goMatch(cpy.Match(tree, selector, cases), mutatedInfo.nx.nxTransMatch(cur)) } case tree: CaseDef => - implicit val mutatedInfo = mutateTransformers(info, prepForCaseDef, info.nx.nxPrepCaseDef, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForCaseDef, info.nx.nxPrepCaseDef, tree, cur) if (mutatedInfo eq null) tree else { val pat = transform(tree.pat, mutatedInfo, cur) @@ -1049,7 +1050,7 @@ object TreeTransforms { goCaseDef(cpy.CaseDef(tree, pat, guard, body), mutatedInfo.nx.nxTransCaseDef(cur)) } case tree: Return => - implicit val mutatedInfo = mutateTransformers(info, prepForReturn, info.nx.nxPrepReturn, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForReturn, info.nx.nxPrepReturn, tree, cur) if (mutatedInfo eq null) tree else { val expr = transform(tree.expr, mutatedInfo, cur) @@ -1057,7 +1058,7 @@ object TreeTransforms { goReturn(cpy.Return(tree, expr, from), mutatedInfo.nx.nxTransReturn(cur)) } case tree: Try => - implicit val mutatedInfo = mutateTransformers(info, prepForTry, info.nx.nxPrepTry, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTry, info.nx.nxPrepTry, tree, cur) if (mutatedInfo eq null) tree else { val block = transform(tree.expr, mutatedInfo, cur) @@ -1066,35 +1067,35 @@ object TreeTransforms { goTry(cpy.Try(tree, block, handler, finalizer), mutatedInfo.nx.nxTransTry(cur)) } case tree: Throw => - implicit val mutatedInfo = mutateTransformers(info, prepForThrow, info.nx.nxPrepThrow, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForThrow, info.nx.nxPrepThrow, tree, cur) if (mutatedInfo eq null) tree else { val expr = transform(tree.expr, mutatedInfo, cur) goThrow(cpy.Throw(tree, expr), mutatedInfo.nx.nxTransThrow(cur)) } case tree: SeqLiteral => - implicit val mutatedInfo = mutateTransformers(info, prepForSeqLiteral, info.nx.nxPrepSeqLiteral, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForSeqLiteral, info.nx.nxPrepSeqLiteral, tree, cur) if (mutatedInfo eq null) tree else { val elems = transformTrees(tree.elems, mutatedInfo, cur) goSeqLiteral(cpy.SeqLiteral(tree, elems), mutatedInfo.nx.nxTransLiteral(cur)) } case tree: TypeTree => - implicit val mutatedInfo = mutateTransformers(info, prepForTypeTree, info.nx.nxPrepTypeTree, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeTree, info.nx.nxPrepTypeTree, tree, cur) if (mutatedInfo eq null) tree else { val original = transform(tree.original, mutatedInfo, cur) goTypeTree(cpy.TypeTree(tree, original), mutatedInfo.nx.nxTransTypeTree(cur)) } case tree: Alternative => - implicit val mutatedInfo = mutateTransformers(info, prepForAlternative, info.nx.nxPrepAlternative, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForAlternative, info.nx.nxPrepAlternative, tree, cur) if (mutatedInfo eq null) tree else { val trees = transformTrees(tree.trees, mutatedInfo, cur) goAlternative(cpy.Alternative(tree, trees), mutatedInfo.nx.nxTransAlternative(cur)) } case tree: UnApply => - implicit val mutatedInfo = mutateTransformers(info, prepForUnApply, info.nx.nxPrepUnApply, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForUnApply, info.nx.nxPrepUnApply, tree, cur) if (mutatedInfo eq null) tree else { val fun = transform(tree.fun, mutatedInfo, cur) @@ -1103,7 +1104,7 @@ object TreeTransforms { goUnApply(cpy.UnApply(tree, fun, implicits, patterns), mutatedInfo.nx.nxTransUnApply(cur)) } case tree: Template => - implicit val mutatedInfo = mutateTransformers(info, prepForTemplate, info.nx.nxPrepTemplate, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTemplate, info.nx.nxPrepTemplate, tree, cur) if (mutatedInfo eq null) tree else { val constr = transformSub(tree.constr, mutatedInfo, cur) @@ -1113,7 +1114,7 @@ object TreeTransforms { goTemplate(cpy.Template(tree, constr, parents, self, body), mutatedInfo.nx.nxTransTemplate(cur)) } case tree: PackageDef => - implicit val mutatedInfo = mutateTransformers(info, prepForPackageDef, info.nx.nxPrepPackageDef, tree, cur) + implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForPackageDef, info.nx.nxPrepPackageDef, tree, cur) if (mutatedInfo eq null) tree else { val nestedCtx = localContext(tree.symbol) From 620b2f4435249cc651d31dbabcb3c902da3b160c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 1 May 2014 11:21:43 +0200 Subject: [PATCH 28/36] Handling of higher-kinded types in intersections and unions. If we intersect a higher-kinded type C with an instance C[T], we should expect C[T]. Conversely, taking the union of a higher-kinded type C and an instance C[T] should give C. Previously, the higher-kinded place-holder $hkN was merged with &/| with the type T which led to type errors. --- src/dotty/tools/dotc/core/TypeComparer.scala | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index f5f7ac367bb2..9003de3c1eca 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -337,7 +337,7 @@ class TypeComparer(initctx: Context) extends DotClass { tp2 match { case tp2: NamedType => def compareNamed = { - implicit val ctx = this.ctx + implicit val ctx: Context = this.ctx // Dotty deviation: implicits need explicit type tp1 match { case tp1: NamedType => val sym1 = tp1.symbol @@ -673,6 +673,12 @@ class TypeComparer(initctx: Context) extends DotClass { i < tparams.length && tparams(i).name == name2 } + /** Is type `tp` a TypeRef referring to a higher-kinded parameter? */ + private def isHKRef(tp: Type) = tp match { + case TypeRef(_, name) => name.isHkParamName + case _ => false + } + /** Can type `tp` be constrained from above by adding a constraint to * a typevar that it refers to? In that case we have to be careful not * to approximate with the lower bound of a type in `thirdTry`. Instead, @@ -956,7 +962,11 @@ class TypeComparer(initctx: Context) extends DotClass { else { val t2 = distributeAnd(tp2, tp1) if (t2.exists) t2 - else AndType(tp1, tp2) + else { + if (isHKRef(tp1)) tp2 + else if (isHKRef(tp2)) tp1 + else AndType(tp1, tp2) + } } } @@ -976,7 +986,11 @@ class TypeComparer(initctx: Context) extends DotClass { else { val t2 = distributeOr(tp2, tp1) if (t2.exists) t2 - else OrType(tp1, tp2) + else { + if (isHKRef(tp1)) tp1 + else if (isHKRef(tp2)) tp2 + else OrType(tp1, tp2) + } } } From 4ae473eff25335d2b6b0f5900eed55ffa5141d2a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 1 May 2014 11:30:11 +0200 Subject: [PATCH 29/36] Tightening of rules for explicit types for implicit defs 1) We now demand that all implicit defs have an implicit type, not just class members. If we admitted implicit term members without explicit types, the rules and algorithms for dteremining eligible implicits would be greatly complicated (because there's always the danger that inferring the type by typechecking the rhs causes a cyclic reference). 2) We check for violations of this rule earlier, during type completion, in order to avoid cyclic references happening before we do the check. --- src/dotty/tools/dotc/typer/Checking.scala | 10 -- src/dotty/tools/dotc/typer/Namer.scala | 140 ++++++++++++---------- src/dotty/tools/dotc/typer/ReTyper.scala | 2 - src/dotty/tools/dotc/typer/Typer.scala | 6 +- 4 files changed, 77 insertions(+), 81 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index ae325af2a449..5e52c5d7e0cd 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -69,15 +69,6 @@ trait Checking { defn.ObjectClass.typeRef } - /** Check that (return) type of implicit definition is not empty */ - def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = defTree.tpt match { - case tpt: untpd.DerivedTypeTree => - case TypeTree(untpd.EmptyTree) => - val resStr = if (defTree.isInstanceOf[untpd.DefDef]) "result " else "" - ctx.error(d"${resStr}type of implicit definition needs to be given explicitly", defTree.pos) - case _ => - } - /** Check that a non-implicit parameter making up the first parameter section of an * implicit conversion is not a singleton type. */ @@ -150,7 +141,6 @@ trait NoChecking extends Checking { override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = () override def checkLegalPrefix(tp: Type, pos: Position)(implicit ctx: Context): Unit = () override def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = tp - override def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = () override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = () override def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = () diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 2d4bf809951c..c3f1dcc81997 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -519,80 +519,92 @@ class Namer { typer: Typer => * defined symbol, given its final return type */ def valOrDefDefSig(mdef: ValOrDefDef, sym: Symbol, typeParams: List[Symbol], paramFn: Type => Type)(implicit ctx: Context): Type = { - val pt = - if (!mdef.tpt.isEmpty) WildcardType - else { - /** An type for this definition that might be inherited from elsewhere: - * If this is a setter parameter, the corresponding getter type. - * If this is a class member, the conjunction of all result types - * of overridden methods. - * NoType if neither case holds. - */ - val inherited = - if (sym.owner.isTerm) NoType - else { - // TODO: Look only at member of supertype instead? - lazy val schema = paramFn(WildcardType) - val site = sym.owner.thisType - ((NoType: Type) /: sym.owner.info.baseClasses.tail) { (tp, cls) => - val iRawInfo = - cls.info.nonPrivateDecl(sym.name).matchingDenotation(site, schema).info - val iInstInfo = iRawInfo match { - case iRawInfo: PolyType => - if (iRawInfo.paramNames.length == typeParams.length) - iRawInfo.instantiate(typeParams map (_.typeRef)) - else NoType - case _ => - if (typeParams.isEmpty) iRawInfo - else NoType - } - val iResType = iInstInfo.finalResultType.asSeenFrom(site, cls) - if (iResType.exists) - typr.println(s"using inherited type; raw: $iRawInfo, inst: $iInstInfo, inherited: $iResType") - tp & iResType + def inferredType = { + /** A type for this definition that might be inherited from elsewhere: + * If this is a setter parameter, the corresponding getter type. + * If this is a class member, the conjunction of all result types + * of overridden methods. + * NoType if neither case holds. + */ + val inherited = + if (sym.owner.isTerm) NoType + else { + // TODO: Look only at member of supertype instead? + lazy val schema = paramFn(WildcardType) + val site = sym.owner.thisType + ((NoType: Type) /: sym.owner.info.baseClasses.tail) { (tp, cls) => + val iRawInfo = + cls.info.nonPrivateDecl(sym.name).matchingDenotation(site, schema).info + val iInstInfo = iRawInfo match { + case iRawInfo: PolyType => + if (iRawInfo.paramNames.length == typeParams.length) + iRawInfo.instantiate(typeParams map (_.typeRef)) + else NoType + case _ => + if (typeParams.isEmpty) iRawInfo + else NoType } + val iResType = iInstInfo.finalResultType.asSeenFrom(site, cls) + if (iResType.exists) + typr.println(s"using inherited type; raw: $iRawInfo, inst: $iInstInfo, inherited: $iResType") + tp & iResType } + } - /** The proto-type to be used when inferring the result type from - * the right hand side. This is `WildcardType` except if the definition - * is a default getter. In that case, the proto-type is the type of - * the corresponding parameter where bound parameters are replaced by - * Wildcards. - */ - def rhsProto = { - val name = sym.asTerm.name - val idx = name.defaultGetterIndex - if (idx < 0) WildcardType - else { - val original = name.defaultGetterToMethod - val meth: Denotation = - if (original.isConstructorName && (sym.owner is ModuleClass)) - sym.owner.companionClass.info.decl(nme.CONSTRUCTOR) - else - ctx.defContext(sym).denotNamed(original) - def paramProto(paramss: List[List[Type]], idx: Int): Type = paramss match { - case params :: paramss1 => - if (idx < params.length) wildApprox(params(idx)) - else paramProto(paramss1, idx - params.length) - case nil => - WildcardType - } - val defaultAlts = meth.altsWith(_.hasDefaultParams) - if (defaultAlts.length == 1) - paramProto(defaultAlts.head.info.widen.paramTypess, idx) + /** The proto-type to be used when inferring the result type from + * the right hand side. This is `WildcardType` except if the definition + * is a default getter. In that case, the proto-type is the type of + * the corresponding parameter where bound parameters are replaced by + * Wildcards. + */ + def rhsProto = { + val name = sym.asTerm.name + val idx = name.defaultGetterIndex + if (idx < 0) WildcardType + else { + val original = name.defaultGetterToMethod + val meth: Denotation = + if (original.isConstructorName && (sym.owner is ModuleClass)) + sym.owner.companionClass.info.decl(nme.CONSTRUCTOR) else + ctx.defContext(sym).denotNamed(original) + def paramProto(paramss: List[List[Type]], idx: Int): Type = paramss match { + case params :: paramss1 => + if (idx < params.length) wildApprox(params(idx)) + else paramProto(paramss1, idx - params.length) + case nil => WildcardType } + val defaultAlts = meth.altsWith(_.hasDefaultParams) + if (defaultAlts.length == 1) + paramProto(defaultAlts.head.info.widen.paramTypess, idx) + else + WildcardType } + } - // println(s"final inherited for $sym: ${inherited.toString}") !!! - // println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}") - val rhsCtx = ctx.fresh addMode Mode.InferringReturnType - def rhsType = typedAheadExpr(mdef.rhs, rhsProto)(rhsCtx).tpe.widen.approximateUnion - def lhsType = fullyDefinedType(rhsType, "right-hand side", mdef.pos) - inherited orElse lhsType orElse WildcardType + // println(s"final inherited for $sym: ${inherited.toString}") !!! + // println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}") + val rhsCtx = ctx.fresh addMode Mode.InferringReturnType + def rhsType = typedAheadExpr(mdef.rhs, rhsProto)(rhsCtx).tpe.widen.approximateUnion + def lhsType = fullyDefinedType(rhsType, "right-hand side", mdef.pos) + if (inherited.exists) inherited + else { + if (sym is Implicit) { + val resStr = if (mdef.isInstanceOf[DefDef]) "result " else "" + ctx.error(d"${resStr}type of implicit definition needs to be given explicitly", mdef.pos) + sym.resetFlag(Implicit) + } + lhsType orElse WildcardType } + } + + val pt = mdef.tpt match { + case _: untpd.DerivedTypeTree => WildcardType + case TypeTree(untpd.EmptyTree) => inferredType + case _ => WildcardType + } paramFn(typedAheadType(mdef.tpt, pt).tpe) } diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala index 8d81347819a6..76817fd16cb6 100644 --- a/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/src/dotty/tools/dotc/typer/ReTyper.scala @@ -59,6 +59,4 @@ class ReTyper extends Typer { override def localTyper(sym: Symbol) = this override def index(trees: List[untpd.Tree])(implicit ctx: Context) = ctx - - override def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = () } \ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index c79ecdf85f4f..f4b52ce09d16 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -753,7 +753,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val ValDef(mods, name, tpt, rhs) = vdef val mods1 = typedModifiers(mods, sym) val tpt1 = typedType(tpt) - if ((sym is Implicit) && sym.owner.isType) checkImplicitTptNonEmpty(vdef) val rhs1 = rhs match { case Ident(nme.WILDCARD) => rhs withType tpt1.tpe case _ => typedExpr(rhs, tpt1.tpe) @@ -766,10 +765,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val mods1 = typedModifiers(mods, sym) val tparams1 = tparams mapconserve (typed(_).asInstanceOf[TypeDef]) val vparamss1 = vparamss nestedMapconserve (typed(_).asInstanceOf[ValDef]) - if (sym is Implicit) { - if (sym.owner.isType) checkImplicitTptNonEmpty(ddef) - checkImplicitParamsNotSingletons(vparamss1) - } + if (sym is Implicit) checkImplicitParamsNotSingletons(vparamss1) val tpt1 = typedType(tpt) val rhs1 = typedExpr(rhs, tpt1.tpe) assignType(cpy.DefDef(ddef, mods1, name, tparams1, vparamss1, tpt1, rhs1), sym) From 94c13485c7ced4b2fe1fec2936d74291d657ab88 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 1 May 2014 11:31:01 +0200 Subject: [PATCH 30/36] Added compliing dotc/transform to tests With the previous fixes, we can now compile dotc/transform without errors. --- test/dotc/tests.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 436ca876fa70..31ab15b57b91 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -89,7 +89,8 @@ class tests extends CompilerTest { @Test def dotc_config = compileDir(dotcDir + "tools/dotc/config", twice) @Test def dotc_core = compileDir(dotcDir + "tools/dotc/core", twice) @Test def dotc_core_pickling = compileDir(dotcDir + "tools/dotc/core/pickling", twice) - @Test def dotc_transform = compileDir(dotcDir + "tools/dotc/core/transform", twice) + @Test def dotc_core_transform = compileDir(dotcDir + "tools/dotc/core/transform", twice) + @Test def dotc_transform = compileDir(dotcDir + "tools/dotc/transform", twice) @Test def dotc_parsing = compileDir(dotcDir + "tools/dotc/parsing", twice) @Test def dotc_printing = compileDir(dotcDir + "tools/dotc/printing", twice) @Test def dotc_reporting = compileDir(dotcDir + "tools/dotc/reporting", twice) From 640feb1fe9de15dbf846c5a1ddc480c44523daa3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 1 May 2014 16:48:02 +0200 Subject: [PATCH 31/36] Adding dependency tracking to constraint satisfaction The previous scheme checked all constraint bounds twice everytime the bounds for a parameter in a constraint were changed. The new scheme, which can be disabled by unsetting `Config.trackContrDeps`, only checks those cbounds that directly or indirectly mention the changed parameter. --- src/dotty/tools/dotc/config/Config.scala | 6 + src/dotty/tools/dotc/core/Constraint.scala | 150 ++++++++++++++++++- src/dotty/tools/dotc/core/TypeComparer.scala | 81 ++++++---- src/dotty/tools/dotc/core/TyperState.scala | 6 +- src/dotty/tools/dotc/core/Types.scala | 3 +- 5 files changed, 206 insertions(+), 40 deletions(-) diff --git a/src/dotty/tools/dotc/config/Config.scala b/src/dotty/tools/dotc/config/Config.scala index e77b10bfbaec..c247699da351 100644 --- a/src/dotty/tools/dotc/config/Config.scala +++ b/src/dotty/tools/dotc/config/Config.scala @@ -37,4 +37,10 @@ object Config { /** The recursion depth for showing a summarized string */ final val summarizeDepth = 2 + + /** Track dependencies for constraint propagation satisfiability checking + * If turned off, constraint checking is simpler but potentially slower + * for large constraints. + */ + final val trackConstrDeps = true } \ No newline at end of file diff --git a/src/dotty/tools/dotc/core/Constraint.scala b/src/dotty/tools/dotc/core/Constraint.scala index a16c85316662..339dbb64a343 100644 --- a/src/dotty/tools/dotc/core/Constraint.scala +++ b/src/dotty/tools/dotc/core/Constraint.scala @@ -10,6 +10,55 @@ import printing.Texts._ import config.Config import config.Printers._ +object Constraint { + + /** The type of `Constraint#myMap` */ + type ParamInfo = SimpleMap[PolyType, Array[Type]] + + /** The type of `Constraint#dependents */ + type DependentMap = SimpleMap[PolyType, Array[Set[PolyParam]]] + + /** The type of functions that include or exclude a `PolyParam` in or from a set*/ + private type DepDelta = (Set[PolyParam], PolyParam) => Set[PolyParam] + + private val addDep: DepDelta = (_ + _) + private val removeDep: DepDelta = (_ - _) + + private val NoTypeBounds = new TypeBounds(WildcardType, WildcardType){} + + /** An accumulator that changes dependencies on `param`. + * @param param The parameter to which changed dependencies refer. + * @param ofVariance Include `PolyParams` occurring at this variance in the dependencies. + * @param delta The dependency change to perform (add or remove). + */ + private class ChangeDependencies(param: PolyParam, ofVariance: Int, delta: DepDelta)(implicit ctx: Context) + extends TypeAccumulator[DependentMap] { + def apply(deps: DependentMap, tp: Type): DependentMap = tp match { + case tp @ PolyParam(pt, n) if + this.variance == 0 || this.variance == ofVariance => + val oldDeps = deps(pt) + val original = safeSelect(oldDeps, n) + val changed = delta(original, param) + if (original eq changed) deps + else { + val newDeps = + if (oldDeps == null) new Array[Set[PolyParam]](pt.paramBounds.length) + else oldDeps.clone + newDeps(n) = changed + deps.updated(pt, newDeps) + } + case _ => foldOver(deps, tp) + } + } + + /** `deps(n)`, except that `Set()` is returned if `deps` or `deps(n)` are null */ + private def safeSelect(deps: Array[Set[PolyParam]], n: Int) : Set[PolyParam] = + if (deps == null || deps(n) == null) Set() + else deps(n) +} + +import Constraint._ + /** Constraint over undetermined type parameters * @param myMap a map from PolyType to arrays. * Each array contains twice the number of entries as there a type parameters @@ -18,8 +67,20 @@ import config.Printers._ * track the corresponding parameters, or is left empty (filled with nulls). * An instantiated type parameter is represented by having its instance type in * the corresponding array entry. + * @param dependents a map from PolyTypes to arrays of Sets of PolyParams. + * The i'th set in an array corresponding to polytype `pt` contains + * those dependent `PolyParam`s `dp` that have `PolyParam(pt, i)` in their bounds in + * significant position. A position is significant if solving the + * constraint for `(pt, i)` with a type higher than its lower bound + * would lead to a constraint for `dp` that was not looser than + * the existing constraint. Specifically, it means that all poly params + * appearing covariantly in the lower bound and contravariantly in the + * upper bound, as well as all poly params appearing nonvariantly are + * significant. + * The `dependents` map is maintained and queried only of `Config.trackConstrDeps` is set. */ -class Constraint(val myMap: SimpleMap[PolyType, Array[Type]]) extends Showable { +class Constraint(private val myMap: ParamInfo, + private val dependents: DependentMap) extends Showable { /** Does the constraint's domain contain the type parameters of `pt`? */ def contains(pt: PolyType): Boolean = myMap(pt) != null @@ -72,15 +133,82 @@ class Constraint(val myMap: SimpleMap[PolyType, Array[Type]]) extends Showable { } } + /** Change dependencies in map `deps` to reflect new parameter bounds. + * @param deps The map to change + * @param pt the polytype that contains the parameters which might have new bounds + * @param entries the entries for the parameters which might have new bounds + * @param delta the change operation, one of `addDep` or `removeDep`. + * @param cmpEntries the comparison entries or `null` if no such entries exist. + * As an optimization, only bounds that differ between `entries` + * and `cmpEntries` will record their dependencies. + */ + def changeDependencies(deps: DependentMap, pt: PolyType, entries: Array[Type], delta: DepDelta, cmpEntries: Array[Type])(implicit ctx: Context): DependentMap = { + val limit = paramCount(entries) + def loop(deps: DependentMap, n: Int): DependentMap = { + if (n >= limit) deps + else { + val newDeps = entries(n) match { + case bounds @ TypeBounds(lo, hi) => + val cmpBounds = + if (cmpEntries == null) NoTypeBounds + else cmpEntries(n) match { + case bounds: TypeBounds => bounds + case _ => NoTypeBounds + } + if (cmpBounds eq bounds) deps + else { + val param = PolyParam(pt, n) + val deps1 = + if (cmpBounds.lo eq lo) deps + else new ChangeDependencies(param, 1, delta).apply(deps, lo) + val deps2 = + if (cmpBounds.hi eq hi) deps1 + else new ChangeDependencies(param, -1, delta).apply(deps1, hi) + deps2 + } + case _ => + deps + } + loop(newDeps, n + 1) + } + } + if (Config.trackConstrDeps) loop(deps, 0) else deps + } + + /** Change dependencies to reflect all changes between the bounds in `oldMap` and `newMap`. + */ + def diffDependencies(deps: DependentMap, oldMap: ParamInfo, newMap: ParamInfo)(implicit ctx: Context): DependentMap = + if (Config.trackConstrDeps) { + var d = deps + oldMap foreachBinding { (poly, entries) => + val newEntries = newMap(poly) + if (newEntries ne entries) d = changeDependencies(d, poly, entries, removeDep, newEntries) + } + newMap foreachBinding { (poly, entries) => + val oldEntries = oldMap(poly) + if (oldEntries ne entries) d = changeDependencies(d, poly, entries, addDep, oldEntries) + } + d + } else deps + + /** The set of parameters that depend directly on `param` + * according to what's stored in `dependents`. + */ + def dependentParams(param: PolyParam): Set[PolyParam] = + safeSelect(dependents(param.binder), param.paramNum) + /** A new constraint which is derived from this constraint by adding or replacing * the entries corresponding to `pt` with `entries`. */ private def updateEntries(pt: PolyType, entries: Array[Type])(implicit ctx: Context) : Constraint = { - val res = new Constraint(myMap.updated(pt, entries)) + val res = new Constraint( + myMap.updated(pt, entries), + changeDependencies(dependents, pt, entries, addDep, myMap(pt))) + //assert(res.domainPolys.filter(pt => // pt.resultType.resultType.widen.classSymbol.name.toString == "Ensuring").length < 2) //DEBUG if (Config.checkConstraintsNonCyclic) checkNonCyclic(pt, entries) - ctx.runInfo.recordConstraintSize(res) + ctx.runInfo.recordConstraintSize(res, res.myMap.size) res } @@ -119,7 +247,10 @@ class Constraint(val myMap: SimpleMap[PolyType, Array[Type]]) extends Showable { updateEntries(poly, myMap(poly) map op) /** A new constraint with all entries coming from `pt` removed. */ - def remove(pt: PolyType) = new Constraint(myMap remove pt) + def remove(pt: PolyType)(implicit ctx: Context) = + new Constraint( + myMap remove pt, + changeDependencies(dependents, pt, myMap(pt), removeDep, null)) /** Is entry associated with `pt` removable? * @param removedParam The index of a parameter which is still present in the @@ -190,7 +321,10 @@ class Constraint(val myMap: SimpleMap[PolyType, Array[Type]]) extends Showable { val pt = param.binder val constr1 = if (isRemovable(pt, param.paramNum)) remove(pt) else updated(param, tp) - val result = new Constraint(constr1.myMap mapValues subst) + val substMap = constr1.myMap mapValues subst + val result = new Constraint( + substMap, + diffDependencies(constr1.dependents, constr1.myMap, substMap)) if (Config.checkConstraintsNonCyclic) result.checkNonCyclic() result } @@ -314,9 +448,9 @@ class Constraint(val myMap: SimpleMap[PolyType, Array[Type]]) extends Showable { trait ConstraintRunInfo { self: RunInfo => private var maxSize = 0 private var maxConstraint: Constraint = _ - def recordConstraintSize(c: Constraint) = - if (c.myMap.size > maxSize) { - maxSize = c.myMap.size + def recordConstraintSize(c: Constraint, size: Int) = + if (size > maxSize) { + maxSize = size maxConstraint = c } def printMaxConstraint()(implicit ctx: Context) = diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 9003de3c1eca..2d66e7d2ffa4 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -84,51 +84,77 @@ class TypeComparer(initctx: Context) extends DotClass { myAnyType } - /** Map that approximates each param in constraint by its lower bound */ + /** Map that approximates each param in constraint by its lower bound. + * Currently only used for diagnostics. + */ val approxParams = new TypeMap { def apply(tp: Type): Type = tp.stripTypeVar match { - case tp: PolyParam if !solvedConstraint && (constraint contains tp) => + case tp: PolyParam if constraint contains tp => this(constraint.bounds(tp).lo) case tp => mapOver(tp) } } - /** Propagate new constraint by comparing all bounds */ - def propagate: Boolean = - constraint.forallParams { poly => - val TypeBounds(lo, hi) = constraint.bounds(poly) + /** If `param` is contained in constraint, test whether its + * bounds are non-empty. Otherwise return `true`. + */ + private def checkBounds(param: PolyParam): Boolean = constraint.at(param) match { + case TypeBounds(lo, hi) => + if (Stats.monitored) Stats.record("checkBounds") isSubType(lo, hi) + case _ => true + } + + /** Test validity of constraint for parameter `changed` and of all + * parameters that depend on it. + */ + private def propagate(changed: PolyParam): Boolean = + if (Config.trackConstrDeps) + checkBounds(changed) && + propagate(constraint.dependentParams(changed) - changed, Set(changed)) + else + constraint forallParams checkBounds + + /** Ensure validity of constraints for parameters `params` and of all + * parameters that depend on them and that have not been tested + * now or before. If `trackConstrDeps` is not set, do this for all + * parameters in the constraint. + * @param seen the set of parameters that have been tested before. + */ + private def propagate(params: Set[PolyParam], seen: Set[PolyParam]): Boolean = + params.isEmpty || + (params forall checkBounds) && { + val seen1 = seen ++ params + val nextParams = (Set[PolyParam]() /: params) { (ps, p) => + ps ++ (constraint.dependentParams(p) -- seen1) + } + propagate(nextParams, seen1) } /** Check whether the lower bounds of all parameters in this * constraint are a solution to the constraint. + * As an optimization, when `trackConstrDeps` is set, we + * only test that the solutions satisfy the constraints `changed` + * and all parameters that depend on it. */ - def isSatisfiable(useSolved: Boolean): Boolean = { + def isSatisfiable(changed: PolyParam): Boolean = { val saved = solvedConstraint solvedConstraint = true try - constraint.forallParams { poly => - val TypeBounds(lo, hi) = constraint.bounds(poly) - isSubType(approxParams(lo), approxParams(hi)) || - { + if (Config.trackConstrDeps) propagate(changed) + else + constraint.forallParams { param => + checkBounds(param) || { + val TypeBounds(lo, hi) = constraint.bounds(param) ctx.log(i"sub fail $lo <:< $hi") - solvedConstraint = saved ctx.log(i"approximated = ${approxParams(lo)} <:< ${approxParams(hi)}") - ctx.log(TypeComparer.explained(implicit ctx => isSubType(approxParams(lo), approxParams(hi)))) false } - } + } finally solvedConstraint = saved } - def isSatisfiable: Boolean = { - val withSolved = isSatisfiable(true) - val withoutSolved = isSatisfiable(false) - assert(withSolved == withoutSolved, i"sat diff for $constraint, with solved: $withSolved, without: $withoutSolved") - withSolved - } - /** Update constraint for `param` to `bounds`, check that * new constraint is still satisfiable. */ @@ -136,13 +162,12 @@ class TypeComparer(initctx: Context) extends DotClass { val saved = constraint constraint = constraint.updated(param, bounds) - propagate && - { isSatisfiable || { - ctx.log(i"SAT $constraint produced by $param $bounds is not satisfiable") - false - } - } || - { constraint = saved; false } // don't leave the constraint in unsatisfiable state + if (propagate(param)) { + if (isSatisfiable(param)) return true + ctx.log(i"SAT $constraint produced by $param $bounds is not satisfiable") + } + constraint = saved // don't leave the constraint in unsatisfiable state + false } private def addConstraint1(param: PolyParam, bound: Type, fromBelow: Boolean): Boolean = { diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala index 65cebf967d61..59c934e0d39d 100644 --- a/src/dotty/tools/dotc/core/TyperState.scala +++ b/src/dotty/tools/dotc/core/TyperState.scala @@ -14,7 +14,7 @@ import collection.mutable class TyperState(val reporter: Reporter) extends DotClass with Showable { /** The current constraint set */ - def constraint: Constraint = new Constraint(SimpleMap.Empty) + def constraint: Constraint = new Constraint(SimpleMap.Empty, SimpleMap.Empty) def constraint_=(c: Constraint): Unit = {} /** The uninstantiated variables */ @@ -48,7 +48,7 @@ class TyperState(val reporter: Reporter) extends DotClass with Showable { * type variable instantiation cannot be retracted anymore. Then, remove * no-longer needed constraint entries. */ - def gc(): Unit = () + def gc()(implicit ctx: Context): Unit = () /** Is it allowed to commit this state? */ def isCommittable: Boolean = false @@ -96,7 +96,7 @@ extends TyperState(reporter) { reporter.flush() } - override def gc(): Unit = { + override def gc()(implicit ctx: Context): Unit = { val toCollect = new mutable.ListBuffer[PolyType] constraint foreachTypeVar { tvar => if (!tvar.inst.exists) { diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index bb882e36cd7e..088a2e3af987 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2119,11 +2119,12 @@ object Types { override def toString = if (lo eq hi) s"TypeAlias($lo)" else s"TypeBounds($lo, $hi)" + + override def computeHash = unsupported("computeHash") } class CachedTypeBounds(lo: Type, hi: Type, hc: Int) extends TypeBounds(lo, hi) { myHash = hc - override def computeHash = unsupported("computeHash") } final class CoTypeBounds(lo: Type, hi: Type, hc: Int) extends CachedTypeBounds(lo, hi, hc) { From 69403ee21743246c3f1d10cfa5f5b0c76d01e0c7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 1 May 2014 19:25:29 +0200 Subject: [PATCH 32/36] Adding a check that owner chains are correct for -Ycheck --- src/dotty/tools/dotc/core/TypeComparer.scala | 1 - .../tools/dotc/transform/TreeChecker.scala | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 2d66e7d2ffa4..6174f0915dff 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -161,7 +161,6 @@ class TypeComparer(initctx: Context) extends DotClass { private def updateConstraint(param: PolyParam, bounds: TypeBounds): Boolean = { val saved = constraint constraint = constraint.updated(param, bounds) - if (propagate(param)) { if (isSatisfiable(param)) return true ctx.log(i"SAT $constraint produced by $param $bounds is not satisfiable") diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index f39dd505a73e..42e4f95babdf 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -8,6 +8,7 @@ import core.SymDenotations._ import core.Contexts._ import core.Symbols._ import core.Types._ +import core.Flags.Method import core.Constants._ import core.StdNames._ import core.Decorators._ @@ -72,6 +73,24 @@ class TreeChecker { assert(tree.isTerm || ctx.phase.prev.id <= ctx.typerPhase.id, tree.show + " at " + ctx.phase) super.typedSelect(tree, pt) } + + /** Check that all defined symbols have legal owners. + * An owner is legal if it is either the same as the context's owner + * or there's an owner chain of valdefs starting at the context's owner and + * reaching up to the symbol's owner. The reason for this relaxed matching + * is that we should be able to pull out an expression as an initializer + * of a helper value without having to do a change owner traversal of the expression. + */ + override def index(trees: List[untpd.Tree])(implicit ctx: Context): Context = { + def ownerMatches(symOwner: Symbol, ctxOwner: Symbol): Boolean = + symOwner == ctxOwner || + ctxOwner.isTerm && !(ctxOwner is Method | Lazy | Mutable) && + ownerMatches(symOwner, ctxOwner.owner) + for (tree <- trees if tree.isDef) + assert(ownerMatches(tree.symbol.owner, ctx.owner), + i"bad owner; $tree has owner ${tree.symbol.owner}, expected was ${ctx.owner}") + super.index(trees) + } } } From 9a251994b59bdcfc3acfb4d8b22aaee359724e6b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 1 May 2014 19:26:04 +0200 Subject: [PATCH 33/36] Adding descriptive message to no-implicits-after-typer assertion. --- src/dotty/tools/dotc/transform/TreeChecker.scala | 2 +- src/dotty/tools/dotc/typer/Implicits.scala | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index 42e4f95babdf..e9970e1f88f3 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -8,7 +8,7 @@ import core.SymDenotations._ import core.Contexts._ import core.Symbols._ import core.Types._ -import core.Flags.Method +import core.Flags._ import core.Constants._ import core.StdNames._ import core.Decorators._ diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index d4cfc1b8e31d..a32f552edf51 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -400,7 +400,9 @@ trait Implicits { self: Typer => * !!! todo: catch potential cycles */ def inferImplicit(pt: Type, argument: Tree, pos: Position)(implicit ctx: Context): SearchResult = track("inferImplicit") { - assert(!ctx.isAfterTyper) + assert(!ctx.isAfterTyper, + if (argument.isEmpty) i"missing implicit parameter of type $pt after typer" + else i"type error: ${argument.tpe} does not conform to $pt") ctx.traceIndented(s"search implicit ${pt.show}, arg = ${argument.show}: ${argument.tpe.show}", implicits, show = true) { assert(!pt.isInstanceOf[ExprType]) val isearch = From 8baa5de7e7ee01b6d47924a293de746379271f08 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 5 May 2014 15:05:02 +0200 Subject: [PATCH 34/36] Fixing tpd.ClassDef. The superclass comnstructor of a ClassDef is supposed to be a constructor call. The fix ensures this is the case when creating classes with tpd.ClassDef. --- src/dotty/tools/dotc/ast/tpd.scala | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index e9775f1dc9c0..3b240ad2ca57 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -210,8 +210,24 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def TypeDef(sym: TypeSymbol)(implicit ctx: Context): TypeDef = ta.assignType(untpd.TypeDef(Modifiers(sym), sym.name, TypeTree(sym.info)), sym) - def ClassDef(cls: ClassSymbol, constr: DefDef, body: List[Tree])(implicit ctx: Context): TypeDef = { - val parents = cls.info.parents map (TypeTree(_)) + def ClassDef(cls: ClassSymbol, constr: DefDef, body: List[Tree], superArgs: List[Tree] = Nil)(implicit ctx: Context): TypeDef = { + val firstParent :: otherParents = cls.info.parents + val superRef = + if (cls is Trait) TypeTree(firstParent) + else { + def isApplicable(ctpe: Type): Boolean = ctpe match { + case ctpe: PolyType => + isApplicable(ctpe.instantiate(firstParent.argTypes)) + case ctpe: MethodType => + (superArgs corresponds ctpe.paramTypes)(_.tpe <:< _) + case _ => + false + } + val constr = firstParent.decl(nme.CONSTRUCTOR).suchThat(constr => isApplicable(constr.info)) + New(firstParent, constr.symbol.asTerm, superArgs) + } + val parents = superRef :: otherParents.map(TypeTree(_)) + val selfType = if (cls.classInfo.selfInfo ne NoType) ValDef(ctx.newSelfSym(cls)) else EmptyValDef @@ -260,10 +276,13 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { // ------ Creating typed equivalents of trees that exist only in untyped form ------- - /** new C(args) */ - def New(tp: Type, args: List[Tree])(implicit ctx: Context): Apply = { + /** new C(args), calling the primary constructor of C */ + def New(tp: Type, args: List[Tree])(implicit ctx: Context): Apply = + New(tp, tp.typeSymbol.primaryConstructor.asTerm, args) + + /** new C(args), calling given constructor `constr` of C */ + def New(tp: Type, constr: TermSymbol, args: List[Tree])(implicit ctx: Context): Apply = { val targs = tp.argTypes - val constr = tp.typeSymbol.primaryConstructor.asTerm Apply( Select( New(tp withoutArgs targs), From ed4cba06177932e1247f0bf3ee38b674517fac72 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 7 May 2014 12:06:26 +0200 Subject: [PATCH 35/36] Fix lazy vals test broken by fixing fixing tpd.ClassDef. --- test/test/transform/LazyValsTest.scala | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/test/test/transform/LazyValsTest.scala b/test/test/transform/LazyValsTest.scala index 98853ad60078..30c093f70eb1 100644 --- a/test/test/transform/LazyValsTest.scala +++ b/test/test/transform/LazyValsTest.scala @@ -183,8 +183,9 @@ class LazyValsTest extends DottyTest { (tree, ctx) => val accessor = "DefDef(Modifiers(,,List(Apply(Select(New(Ident(volatile)),),List()))),s,List(),List(),TypeTree[TypeRef(ThisType(module class lang),String)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(null))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(a))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class lang),String)],Literal(Constant(null))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" - val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(Apply(Select(New(TypeTree[TypeRef(ThisType(module class lang),Object)]),),List())),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" val treeS = tree.toString + println(treeS) Assert.assertTrue("volatile field lazy ref rewritten to class creation", treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) } @@ -196,7 +197,7 @@ class LazyValsTest extends DottyTest { (tree, ctx) => val accessor = "DefDef(Modifiers(,,List(Apply(Select(New(Ident(volatile)),),List()))),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(0))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(1))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class scala),Int)],Literal(Constant(0))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" - val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(Apply(Select(New(TypeTree[TypeRef(ThisType(module class lang),Object)]),),List())),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" val treeS = tree.toString Assert.assertTrue("volatile field lazy ref rewritten to class creation", treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) @@ -209,7 +210,7 @@ class LazyValsTest extends DottyTest { (tree, ctx) => val accessor = "DefDef(Modifiers(,,List(Apply(Select(New(Ident(volatile)),),List()))),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Long)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(0))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(1))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" - val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(Apply(Select(New(TypeTree[TypeRef(ThisType(module class lang),Object)]),),List())),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" val treeS = tree.toString Assert.assertTrue("volatile field lazy ref rewritten to class creation", treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) @@ -222,7 +223,7 @@ class LazyValsTest extends DottyTest { (tree, ctx) => val accessor = "DefDef(Modifiers(,,List(Apply(Select(New(Ident(volatile)),),List()))),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Float)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(0.0))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(1.0))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class scala),Float)],Literal(Constant(0.0))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" - val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(Apply(Select(New(TypeTree[TypeRef(ThisType(module class lang),Object)]),),List())),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" val treeS = tree.toString Assert.assertTrue("volatile field lazy ref rewritten to class creation", treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) @@ -235,7 +236,7 @@ class LazyValsTest extends DottyTest { (tree, ctx) => val accessor = "DefDef(Modifiers(,,List(Apply(Select(New(Ident(volatile)),),List()))),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Double)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(0.0))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(1.0))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class scala),Double)],Literal(Constant(0.0))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" - val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(Apply(Select(New(TypeTree[TypeRef(ThisType(module class lang),Object)]),),List())),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" val treeS = tree.toString Assert.assertTrue("volatile field lazy ref rewritten to class creation", treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) @@ -248,7 +249,7 @@ class LazyValsTest extends DottyTest { (tree, ctx) => val accessor = "DefDef(Modifiers(,,List(Apply(Select(New(Ident(volatile)),),List()))),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Boolean)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(false))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(true))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(false))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" - val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(Apply(Select(New(TypeTree[TypeRef(ThisType(module class lang),Object)]),),List())),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" val treeS = tree.toString Assert.assertTrue("volatile field lazy ref rewritten to class creation", treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) @@ -261,7 +262,7 @@ class LazyValsTest extends DottyTest { (tree, ctx) => val accessor = "DefDef(Modifiers(,,List(Apply(Select(New(Ident(volatile)),),List()))),s,List(),List(),TypeTree[TypeRef(TermRef(ThisType(module class ),scala),Byte)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(0))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(1))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(TermRef(ThisType(module class ),scala),Byte)],Literal(Constant(0))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" - val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(Apply(Select(New(TypeTree[TypeRef(ThisType(module class lang),Object)]),),List())),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" val treeS = tree.toString Assert.assertTrue("volatile field lazy ref rewritten to class creation", treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) @@ -274,7 +275,7 @@ class LazyValsTest extends DottyTest { (tree, ctx) => val accessor = "DefDef(Modifiers(,,List(Apply(Select(New(Ident(volatile)),),List()))),s,List(),List(),TypeTree[TypeRef(TermRef(ThisType(module class ),scala),Short)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(0))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(1))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(TermRef(ThisType(module class ),scala),Short)],Literal(Constant(0))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" - val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(Apply(Select(New(TypeTree[TypeRef(ThisType(module class lang),Object)]),),List())),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" val treeS = tree.toString Assert.assertTrue("volatile field lazy ref rewritten to class creation", treeS.contains(accessor) && treeS.contains(fields) && treeS.contains(moduleField)) @@ -287,7 +288,7 @@ class LazyValsTest extends DottyTest { (tree, ctx) => val accessor = "DefDef(Modifiers(,,List(Apply(Select(New(Ident(volatile)),),List()))),s,List(),List(),TypeTree[TypeRef(ThisType(module class scala),Char)],Block(List(ValDef(Modifiers(,,List()),result,TypeTree[TermRef(ThisType(class LV),s)],Literal(Constant(\u0000))), ValDef(Modifiers(,,List()),retry,TypeTree[TypeRef(ThisType(module class scala),Boolean)],Literal(Constant(true))), ValDef(Modifiers(,,List()),flag,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0))), WhileDo(Ident(retry),Block(List(Assign(Ident(flag),Apply(Select(Ident(LazyVals),get),List(This(LV), Select(Ident(LV),OFFSET$0))))),Match(Apply(Select(Ident(LazyVals),STATE),List(Ident(flag), Literal(Constant(0)))),List(CaseDef(Literal(Constant(0)),EmptyTree,If(Apply(Select(Ident(LazyVals),CAS),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(1)), Literal(Constant(0)))),Block(List(Try(Assign(Ident(result),Literal(Constant(a))),Block(List(DefDef(Modifiers(,,List()),$anonfun,List(),List(List(ValDef(Modifiers(,,List()),x$1,TypeTree[TypeRef(ThisType(module class lang),Throwable)],EmptyTree))),TypeTree[TypeRef(ThisType(module class scala),Int)],Block(List(Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(0)), Literal(Constant(0))))),Throw(Ident(x$1))))),Closure(List(),Ident($anonfun),EmptyTree)),EmptyTree), Assign(Ident(s$lzy1),Ident(result)), Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(0)))), Assign(Ident(retry),Literal(Constant(false)))),Literal(Constant(()))),Literal(Constant(())))), CaseDef(Literal(Constant(1)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(2)),EmptyTree,Apply(Select(Ident(LazyVals),wait4Notification),List(This(LV), Select(Ident(LV),OFFSET$0), Ident(flag), Literal(Constant(0))))), CaseDef(Literal(Constant(3)),EmptyTree,Block(List(Assign(Ident(retry),Literal(Constant(false))), Assign(Ident(result),Ident(s$lzy1))),Literal(Constant(()))))))))),Ident(result)))" val fields = "ValDef(Modifiers(,,List()),s$lzy1,TypeTree[TypeRef(ThisType(module class scala),Char)],Literal(Constant(\u0000))), ValDef(Modifiers(,,List()),bitmap$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Literal(Constant(0)))" - val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(Apply(Select(New(TypeTree[TypeRef(ThisType(module class lang),Object)]),),List())),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" val treeS = tree.toString Assert.assertTrue("volatile field lazy ref rewritten to class creation", @@ -299,7 +300,7 @@ class LazyValsTest extends DottyTest { def volatilesReuseBitmaps = { checkCompile("LazyVals", "class LV { @volatile lazy val a = 'a'; @volatile lazy val b = 'b'; }") { (tree, ctx) => - val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(Apply(Select(New(TypeTree[TypeRef(ThisType(module class lang),Object)]),),List())),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" val reuseFieldPattern = "Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$0), Literal(Constant(3)), Literal(Constant(1))))" val treeS = tree.toString Assert.assertTrue("volatile field lazy ref rewritten to class creation", @@ -349,7 +350,7 @@ class LazyValsTest extends DottyTest { | } """.stripMargin ){ (tree, ctx) => - val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(TypeTree[TypeRef(ThisType(module class lang),Object)]),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$1,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$1))))), ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" + val moduleField = "TypeDef(Modifiers(final module ,,List()),LV$,Template(DefDef(Modifiers(,,List()),,List(),List(List()),TypeTree[TypeRef(ThisType(module class ),LV$)],EmptyTree),List(Apply(Select(New(TypeTree[TypeRef(ThisType(module class lang),Object)]),),List())),ValDef(Modifiers(,,List()),_,TypeTree[TermRef(ThisType(module class ),LV)],EmptyTree),List(ValDef(Modifiers(,,List()),OFFSET$1,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$1))))), ValDef(Modifiers(,,List()),OFFSET$0,TypeTree[TypeRef(ThisType(module class scala),Long)],Apply(Select(Ident(LazyVals),getOffset),List(This(LV), Literal(Constant(bitmap$0))))))))" val reuseFieldPattern = "Apply(Select(Ident(LazyVals),setFlag),List(This(LV), Select(Ident(LV),OFFSET$1), Literal(Constant(3)), Literal(Constant(1))))" val treeS = tree.toString Assert.assertTrue("volatile field lazy ref rewritten to class creation", From e83df26f7a6bc583599e0b50bdb2f806cbf314f7 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Thu, 8 May 2014 22:20:06 +0200 Subject: [PATCH 36/36] Fix dotty deviation failure in TailRec --- src/dotty/tools/dotc/transform/TailRec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala index 7d9b37be10c5..cd0643a6e7fe 100644 --- a/src/dotty/tools/dotc/transform/TailRec.scala +++ b/src/dotty/tools/dotc/transform/TailRec.scala @@ -129,7 +129,7 @@ class TailRec extends TreeTransform with DenotTransformer { Block( List(res), vparamss0.foldLeft(Apply(call, List(This(owner)))) - {case (call, args) => Apply(call, args.map(x=> Ident(x.symbol.termRef)))} + {(call, args) => Apply(call, args.map(x => Ident(x.symbol.termRef)))} ) } else {