diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 1aa1cce1073d..d4fa7e671104 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -53,8 +53,11 @@ class Compiler { new Literalize, new GettersSetters), List(new Erasure), - List(new CapturedVars, new Constructors)/*, - List(new LambdaLift)*/ + List(new CapturedVars, + new Constructors), + List(new LambdaLift, + new Flatten, + new RestoreScopes) ) var runId = 1 diff --git a/src/dotty/tools/dotc/Flatten.scala b/src/dotty/tools/dotc/Flatten.scala deleted file mode 100644 index d7ccc1ae4e11..000000000000 --- a/src/dotty/tools/dotc/Flatten.scala +++ /dev/null @@ -1,15 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import DenotTransformers.SymTransformer -import Phases.Phase -import Contexts.Context -import SymDenotations.SymDenotation -import TreeTransforms.MiniPhaseTransform - -class Flatten extends MiniPhaseTransform with SymTransformer { thisTransformer => - override def phaseName = "flatten" - - def transformSym(ref: SymDenotation)(implicit ctx: Context) = ??? -} diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index 50aaafc82279..2a55d6732f83 100644 --- a/src/dotty/tools/dotc/TypeErasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -111,6 +111,12 @@ object TypeErasure { erasure(tp) } + /** The erasure of a symbol's info. This is different of `erasure` in the way `ExprType`s are + * treated. `eraseInfo` maps them them to nullary method types, whereas `erasure` maps them + * to `Function0`. + */ + def eraseInfo(tp: Type)(implicit ctx: Context): Type = scalaErasureFn.eraseInfo(tp)(erasureCtx) + /** The erasure of a function result type. Differs from normal erasure in that * Unit is kept instead of being mapped to BoxedUnit. */ @@ -135,7 +141,7 @@ object TypeErasure { if ((sym eq defn.Any_asInstanceOf) || (sym eq defn.Any_isInstanceOf)) eraseParamBounds(sym.info.asInstanceOf[PolyType]) else if (sym.isAbstractType) TypeAlias(WildcardType) else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx)) - else erase(tp)(erasureCtx) + else eraseInfo(tp)(erasureCtx) } def isUnboundedGeneric(tp: Type)(implicit ctx: Context) = !( @@ -258,10 +264,12 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild else this(parent) case tp: TermRef => this(tp.widen) - case ThisType(_) | SuperType(_, _) => + case ThisType(_) => tp + case SuperType(thistpe, supertpe) => + SuperType(this(thistpe), this(supertpe)) case ExprType(rt) => - MethodType(Nil, Nil, this(rt)) + defn.FunctionClass(0).typeRef case tp: TypeProxy => this(tp.underlying) case AndType(tp1, tp2) => @@ -310,6 +318,11 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild else JavaArrayType(this(elemtp)) } + def eraseInfo(tp: Type)(implicit ctx: Context) = tp match { + case ExprType(rt) => MethodType(Nil, Nil, erasure(rt)) + case tp => erasure(tp) + } + private def eraseDerivedValueClassRef(tref: TypeRef)(implicit ctx: Context): Type = unsupported("eraseDerivedValueClass") diff --git a/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/src/dotty/tools/dotc/ast/TreeTypeMap.scala index 33be848c1ed1..32c89c94fb51 100644 --- a/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -79,14 +79,13 @@ final class TreeTypeMap( override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = treeMap(tree) match { case impl @ Template(constr, parents, self, body) => - val tmap = withMappedSyms(impl.symbol :: impl.constr.symbol :: Nil) - val parents1 = parents mapconserve transform - val (_, constr1 :: self1 :: Nil) = transformDefs(constr :: self :: Nil) - val body1 = tmap.transformStats(body) - updateDecls(constr :: body, constr1 :: body1) + val tmap = withMappedSyms(localSyms(impl :: self :: Nil)) cpy.Template(impl)( - constr1.asInstanceOf[DefDef], parents1, self1.asInstanceOf[ValDef], body1) - .withType(tmap.mapType(impl.tpe)) + constr = tmap.transformSub(constr), + parents = parents mapconserve transform, + self = tmap.transformSub(self), + body = body mapconserve tmap.transform + ).withType(tmap.mapType(impl.tpe)) case tree1 => tree1.withType(mapType(tree1.tpe)) match { case id: Ident if tpd.needsSelect(id.tpe) => @@ -160,8 +159,24 @@ final class TreeTypeMap( * and return a treemap that contains the substitution * between original and mapped symbols. */ - def withMappedSyms(syms: List[Symbol]): TreeTypeMap = { - val mapped = ctx.mapSymbols(syms, this) - withSubstitution(syms, mapped) + def withMappedSyms(syms: List[Symbol], mapAlways: Boolean = false): TreeTypeMap = + withMappedSyms(syms, ctx.mapSymbols(syms, this, mapAlways)) + + /** The tree map with the substitution between originals `syms` + * and mapped symbols `mapped`. Also goes into mapped classes + * and substitutes their declarations. + */ + def withMappedSyms(syms: List[Symbol], mapped: List[Symbol]): TreeTypeMap = { + val symsChanged = syms ne mapped + val substMap = withSubstitution(syms, mapped) + val fullMap = (substMap /: mapped.filter(_.isClass)) { (tmap, cls) => + val origDcls = cls.decls.toList + val mappedDcls = ctx.mapSymbols(origDcls, tmap) + val tmap1 = tmap.withMappedSyms(origDcls, mappedDcls) + if (symsChanged) (origDcls, mappedDcls).zipped.foreach(cls.asClass.replace) + tmap1 + } + if (symsChanged || (fullMap eq substMap)) fullMap + else withMappedSyms(syms, mapAlways = true) } } diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index f1ccfdb75e3e..f7d3d134af76 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -347,6 +347,11 @@ object Trees { s } + /** If this is a thicket, gerform `op` on each of its trees + * otherwise, perform `op` ion tree itself. + */ + def foreachInThicket(op: Tree[T] => Unit): Unit = op(this) + override def toText(printer: Printer) = printer.toText(this) override def hashCode(): Int = System.identityHashCode(this) @@ -809,6 +814,8 @@ object Trees { val newTrees = trees.map(_.withPos(pos)) new Thicket[T](newTrees).asInstanceOf[this.type] } + override def foreachInThicket(op: Tree[T] => Unit): Unit = + trees foreach (_.foreachInThicket(op)) } class EmptyValDef[T >: Untyped] extends ValDef[T]( diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 9b7c9cbae810..4c21fcf49229 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -9,6 +9,7 @@ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Symbols. import Denotations._, Decorators._ import config.Printers._ import typer.Mode +import collection.mutable import typer.ErrorReporting._ import scala.annotation.tailrec @@ -620,6 +621,12 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } acc(false, tree) } + + def filterSubTrees(f: Tree => Boolean): List[Tree] = { + val buf = new mutable.ListBuffer[Tree] + foreachSubTree { tree => if (f(tree)) buf += tree } + buf.toList + } } implicit class ListOfTreeDecorator(val xs: List[tpd.Tree]) extends AnyVal { diff --git a/src/dotty/tools/dotc/config/Printers.scala b/src/dotty/tools/dotc/config/Printers.scala index 3926be59b121..f8d7f8de557a 100644 --- a/src/dotty/tools/dotc/config/Printers.scala +++ b/src/dotty/tools/dotc/config/Printers.scala @@ -4,10 +4,12 @@ object Printers { class Printer { def println(msg: => String): Unit = System.out.println(msg) + def echo[T](msg: => String, value: T): T = { println(msg + value); value } } object noPrinter extends Printer { override def println(msg: => String): Unit = () + override def echo[T](msg: => String, value: T): T = value } val default: Printer = new Printer diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala index 99af4d0cb42e..e0d7fae33735 100644 --- a/src/dotty/tools/dotc/core/Decorators.scala +++ b/src/dotty/tools/dotc/core/Decorators.scala @@ -100,6 +100,11 @@ object Decorators { else x1 :: xs1 } + def foldRightBN[U](z: => U)(op: (T, => U) => U): U = xs match { + case Nil => z + case x :: xs1 => op(x, xs1.foldRightBN(z)(op)) + } + final def hasSameLengthAs[U](ys: List[U]): Boolean = { @tailrec def loop(xs: List[T], ys: List[U]): Boolean = if (xs.isEmpty) ys.isEmpty diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index a27dd6614fac..e5bf27eaeb00 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -331,7 +331,9 @@ object Flags { final val JavaDefined = commonFlag(30, "") /** Symbol is implemented as a Java static */ - final val Static = commonFlag(31, "") + final val JavaStatic = commonFlag(31, "") + final val JavaStaticTerm = JavaStatic.toTermFlags + final val JavaStaticType = JavaStatic.toTypeFlags /** Variable is accessed from nested function. */ final val Captured = termFlag(32, "") @@ -421,7 +423,7 @@ object Flags { /** Flags representing source modifiers */ final val SourceModifierFlags = commonFlags(Private, Protected, Abstract, Final, - Sealed, Case, Implicit, Override, AbsOverride, Lazy, Static) + Sealed, Case, Implicit, Override, AbsOverride, Lazy, JavaStatic) /** Flags representing modifiers that can appear in trees */ final val ModifierFlags = @@ -436,7 +438,7 @@ object Flags { /** Flags guaranteed to be set upon symbol creation */ final val FromStartFlags = AccessFlags | Module | Package | Deferred | MethodOrHKCommon | Param | ParamAccessor | Scala2ExistentialCommon | - InSuperCall | Touched | Static | CovariantOrOuter | ContravariantOrLabel | ExpandedName | AccessorOrSealed | + InSuperCall | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | ExpandedName | AccessorOrSealed | CaseAccessorOrTypeArgument | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent | SelfNameOrImplClass @@ -473,7 +475,7 @@ object Flags { */ final val RetainedModuleValAndClassFlags: FlagSet = AccessFlags | Package | Case | - Synthetic | ExpandedName | JavaDefined | Static | Artifact | + Synthetic | ExpandedName | JavaDefined | JavaStatic | Artifact | Erroneous | Lifted | MixedIn | Specialized /** Flags that can apply to a module val */ @@ -487,7 +489,7 @@ object Flags { /** Packages and package classes always have these flags set */ final val PackageCreationFlags = - Module | Package | Final | JavaDefined | Static + Module | Package | Final | JavaDefined /** These flags are pickled */ final val PickledFlags = flagRange(FirstFlag, FirstNotPickledFlag) @@ -562,7 +564,7 @@ object Flags { final val ProtectedLocal = allOf(Protected, Local) /** Java symbol which is `protected` and `static` */ - final val StaticProtected = allOf(JavaDefined, Protected, Static) + final val StaticProtected = allOf(JavaDefined, Protected, JavaStatic) final val AbstractFinal = allOf(Abstract, Final) final val AbstractSealed = allOf(Abstract, Sealed) diff --git a/src/dotty/tools/dotc/core/Scopes.scala b/src/dotty/tools/dotc/core/Scopes.scala index 3f7a4cb2ca20..494a26f7e58d 100644 --- a/src/dotty/tools/dotc/core/Scopes.scala +++ b/src/dotty/tools/dotc/core/Scopes.scala @@ -278,6 +278,7 @@ object Scopes { if (e.sym == prev) e.sym = replacement e = lookupNextEntry(e) } + elemsCache = null } /** Lookup a symbol entry matching given name. diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index ae37ab87cdeb..ae8fceeb743b 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -390,7 +390,7 @@ object SymDenotations { /** Is this denotation static (i.e. with no outer instance)? */ final def isStatic(implicit ctx: Context) = - (this is Static) || this.exists && owner.isStaticOwner + (this is JavaStatic) || this.exists && owner.isStaticOwner /** Is this a package class or module class that defines static symbols? */ final def isStaticOwner(implicit ctx: Context): Boolean = @@ -666,10 +666,16 @@ object SymDenotations { * for these definitions. */ final def enclosingClass(implicit ctx: Context): Symbol = { - def enclClass(d: SymDenotation): Symbol = - if (d.isClass || !d.exists) d.symbol else enclClass(d.owner) - val cls = enclClass(this) - if (this is InSuperCall) cls.owner.enclosingClass else cls + def enclClass(sym: Symbol, skip: Boolean): Symbol = { + def newSkip = sym.is(InSuperCall) || sym.is(JavaStaticTerm) + if (!sym.exists) + NoSymbol + else if (sym.isClass) + if (skip) enclClass(sym.owner, newSkip) else sym + else + enclClass(sym.owner, skip || newSkip) + } + enclClass(symbol, false) } final def isEffectivelyFinal(implicit ctx: Context): Boolean = { @@ -976,7 +982,7 @@ object SymDenotations { /** The type parameters of this class */ override final def typeParams(implicit ctx: Context): List[TypeSymbol] = { def computeTypeParams = { - if (ctx.erasedTypes && (symbol ne defn.ArrayClass)) Nil + if (ctx.erasedTypes || is(Module)) Nil // fast return for modules to avoid scanning package decls else if (this ne initial) initial.asSymDenotation.typeParams else decls.filter(sym => (sym is TypeParam) && sym.owner == symbol).asInstanceOf[List[TypeSymbol]] diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 0174f75cf6f6..83fb2c134e91 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -267,10 +267,10 @@ trait Symbols { this: Context => * Cross symbol references are brought over from originals to copies. * Do not copy any symbols if all attributes of all symbols stay the same. */ - def mapSymbols(originals: List[Symbol], ttmap: TreeTypeMap) = - if (originals forall (sym => + def mapSymbols(originals: List[Symbol], ttmap: TreeTypeMap, mapAlways: Boolean = false): List[Symbol] = + if (originals.forall(sym => (ttmap.mapType(sym.info) eq sym.info) && - !(ttmap.oldOwners contains sym.owner))) + !(ttmap.oldOwners contains sym.owner)) && !mapAlways) originals else { val copies: List[Symbol] = for (original <- originals) yield diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index f36572755eb8..dcd8af3b65e4 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -515,6 +515,14 @@ class TypeComparer(initctx: Context) extends DotClass { case _ => secondTry(tp1, tp2) } + case tp2: SuperType => + tp1 match { + case tp1: SuperType => + isSubType(tp1.thistpe, tp2.thistpe) && + isSameType(tp1.supertpe, tp2.supertpe) + case _ => + secondTry(tp1, tp2) + } case AndType(tp21, tp22) => isSubType(tp1, tp21) && isSubType(tp1, tp22) case ErrorType => diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index d93e4eb09338..d0ddfdd28b50 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -116,6 +116,8 @@ object Types { tp.tp1.derivesFrom(cls) || tp.tp2.derivesFrom(cls) case tp: OrType => tp.tp1.derivesFrom(cls) && tp.tp2.derivesFrom(cls) + case tp: JavaArrayType => + cls == defn.ObjectClass case _ => false } @@ -408,6 +410,8 @@ object Types { goAnd(l, r) case OrType(l, r) => goOr(l, r) + case tp: JavaArrayType => + defn.ObjectType.findMember(name, pre, excluded) case ErrorType => ctx.newErrorSymbol(pre.classSymbol orElse defn.RootClass, name) case _ => diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala b/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala index c3850d0fd76d..c35b9ca474fd 100644 --- a/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala +++ b/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala @@ -343,7 +343,7 @@ object ClassfileConstants { case JAVA_ACC_PROTECTED => Protected case JAVA_ACC_FINAL => Final case JAVA_ACC_SYNTHETIC => Synthetic - case JAVA_ACC_STATIC => Static + case JAVA_ACC_STATIC => JavaStatic case JAVA_ACC_ABSTRACT => if (isAnnotation) EmptyFlags else if (isClass) Abstract else Deferred case JAVA_ACC_INTERFACE => if (isAnnotation) EmptyFlags else JavaInterface case _ => EmptyFlags diff --git a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala index ef2b4acb2700..d2a05bf3a4dc 100644 --- a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala +++ b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala @@ -236,7 +236,7 @@ object PickleBuffer { JAVA -> JavaDefined, SYNTHETIC -> Synthetic, STABLE -> Stable, - STATIC -> Static, + STATIC -> JavaStatic, CASEACCESSOR -> CaseAccessor, DEFAULTPARAM -> (DefaultParameterized, Trait), BRIDGE -> Bridge, diff --git a/src/dotty/tools/dotc/transform/ElimByName.scala b/src/dotty/tools/dotc/transform/ElimByName.scala index 1d0307398fc3..40fd48327305 100644 --- a/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/src/dotty/tools/dotc/transform/ElimByName.scala @@ -2,32 +2,50 @@ package dotty.tools.dotc package transform import TreeTransforms._ -import core.DenotTransformers._ -import core.Symbols._ -import core.SymDenotations._ -import core.Contexts._ -import core.Types._ -import core.Flags._ -import core.Decorators._ +import core._ +import DenotTransformers._ +import Symbols._ +import SymDenotations._ +import Contexts._ +import Types._ +import Flags._ +import Decorators._ import SymUtils._ +import util.Attachment import core.StdNames.nme import ast.Trees._ -/** This phase eliminates ExprTypes `=> T` and replaces them by +object ElimByName { + val ByNameArg = new Attachment.Key[Unit] +} + +/** This phase eliminates ExprTypes `=> T` as types of function parameters, and replaces them by * nullary function types. More precisely: * - * For parameter types: + * For the types of parameter symbols: * * => T ==> () => T * - * For terms: + * Note that `=> T` types are not eliminated in MnethodTypes. This is done later at erasure. + * Terms are rewritten as follows: * * x ==> x.apply() if x is a parameter that had type => T + * + * Arguments to call-by-name parameters are translated as follows. First, the argument is + * rewritten by the rules + * * e.apply() ==> e if e.apply() is an argument to a call-by-name parameter * expr ==> () => expr if other expr is an argument to a call-by-name parameter + * + * This makes the argument compatible with a parameter type of () => T, which will be the + * formal parameter type at erasure. But to be -Ycheckable until then, any argument + * ARG rewritten by the rules above is again wrapped in an application ARG.apply(), + * labelled with a `ByNameParam` attachment. Erasure will later strip wrapped + * `.apply()` calls with ByNameParam attachments. */ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransformer => import ast.tpd._ + import ElimByName._ override def phaseName: String = "elimByName" @@ -44,9 +62,9 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) { - def transformArg(arg: Tree, formal: Type): Tree = formal match { - case _: ExprType => - arg match { + def transformArg(arg: Tree, formal: Type): Tree = formal.dealias match { + case formalExpr: ExprType => + val argFun = arg match { case Apply(Select(qual, nme.apply), Nil) if qual.tpe derivesFrom defn.FunctionClass(0) => qual case _ => @@ -54,24 +72,14 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform ctx.owner, nme.ANON_FUN, Synthetic | Method, MethodType(Nil, Nil, arg.tpe.widen)) Closure(meth, _ => arg.changeOwner(ctx.owner, meth)) } + val argApplied = argFun.select(defn.Function0_apply).appliedToNone + argApplied.putAttachment(ByNameArg, ()) + argApplied case _ => arg } - /** Given that `info` is the possibly curried) method type of the - * tree's symbol, the method type that corresponds to the current application. - */ - def matchingMethType(info: Type, tree: Tree): Type = tree match { - case Apply(fn, _) => matchingMethType(info.resultType, fn) - case _ => info - } - - val origMethType = originalDenotation(tree.fun).info match { - case pt: PolyType => pt.resultType - case mt => mt - } - - val MethodType(_, formals) = matchingMethType(origMethType, tree.fun) + val MethodType(_, formals) = tree.fun.tpe.widen val args1 = tree.args.zipWithConserve(formals)(transformArg) cpy.Apply(tree)(tree.fun, args1) } @@ -99,22 +107,13 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform case _ => tree } - def elimByNameParams(tp: Type)(implicit ctx: Context): Type = tp match { - case tp: PolyType => - tp.derivedPolyType(tp.paramNames, tp.paramBounds, elimByNameParams(tp.resultType)) - case tp: MethodType => - tp.derivedMethodType(tp.paramNames, tp.paramTypes mapConserve transformParamInfo, - elimByNameParams(tp.resultType)) - case _ => - tp - } + override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = + if (exprBecomesFunction(tree.symbol)) + cpy.ValDef(tree)(tpt = tree.tpt.withType(tree.symbol.info)) + else tree - def transformParamInfo(tp: Type)(implicit ctx: Context) = tp match { - case ExprType(rt) => defn.FunctionType(Nil, rt) + def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp match { + case ExprType(rt) if exprBecomesFunction(sym) => defn.FunctionType(Nil, rt) case _ => tp } - - def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = - if (exprBecomesFunction(sym)) transformParamInfo(tp) - else elimByNameParams(tp) } diff --git a/src/dotty/tools/dotc/ElimLocals.scala b/src/dotty/tools/dotc/transform/ElimLocals.scala similarity index 100% rename from src/dotty/tools/dotc/ElimLocals.scala rename to src/dotty/tools/dotc/transform/ElimLocals.scala diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index ed853f8f1c2c..3de7ae36a57d 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -62,7 +62,7 @@ class Erasure extends Phase with DenotTransformer { thisTransformer => } } case ref => - ref.derivedSingleDenotation(ref.symbol, erasure(ref.info)) + ref.derivedSingleDenotation(ref.symbol, eraseInfo(ref.info)) } val eraser = new Erasure.Typer @@ -101,7 +101,7 @@ class Erasure extends Phase with DenotTransformer { thisTransformer => } def assertErased(tp: Type, tree: tpd.Tree = tpd.EmptyTree)(implicit ctx: Context): Unit = - assert(isErasedType(tp), i"The type $tp - ${tp.toString} of class ${tp.getClass} of tree $tree / ${tree.getClass} is illegal after erasure, phase = ${ctx.phase}") + assert(isErasedType(tp), i"The type $tp - ${tp.toString} of class ${tp.getClass} of tree $tree : ${tree.tpe} / ${tree.getClass} is illegal after erasure, phase = ${ctx.phase}") } object Erasure extends TypeTestsCasts{ @@ -183,6 +183,7 @@ object Erasure extends TypeTestsCasts{ } /** Generate a synthetic cast operation from tree.tpe to pt. + * Does not do any boxing/unboxing (this is handled upstream). */ def cast(tree: Tree, pt: Type)(implicit ctx: Context): Tree = { // TODO: The commented out assertion fails for tailcall/t6574.scala @@ -315,7 +316,7 @@ object Erasure extends TypeTestsCasts{ override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree = if (tree.symbol == ctx.owner.enclosingClass || tree.symbol.isStaticOwner) promote(tree) else { - ctx.log(i"computing outer path from ${ctx.owner.ownersIterator.toList}%, % to ${tree.symbol}") + ctx.log(i"computing outer path from ${ctx.owner.ownersIterator.toList}%, % to ${tree.symbol}, encl class = ${ctx.owner.enclosingClass}") outer.path(tree.symbol) } @@ -349,7 +350,11 @@ object Erasure extends TypeTestsCasts{ override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { val Apply(fun, args) = tree - typedExpr(fun, FunProto(args, pt, this)) match { + if (tree.removeAttachment(ElimByName.ByNameArg).isDefined) { + val Select(qual, nme.apply) = fun + typedUnadapted(qual, pt) + } + else typedExpr(fun, FunProto(args, pt, this)) match { case fun1: Apply => // arguments passed in prototype were already passed fun1 case fun1 => @@ -377,10 +382,8 @@ object Erasure extends TypeTestsCasts{ EmptyTree override def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = { - val statsFlatten = Trees.flatten(stats) - val stats1 = super.typedStats(statsFlatten, exprOwner) - - if (ctx.owner.isClass) stats1:::addBridges(statsFlatten, stats1)(ctx) else stats1 + val stats1 = Trees.flatten(super.typedStats(stats, exprOwner)) + if (ctx.owner.isClass) stats1 ::: addBridges(stats, stats1)(ctx) else stats1 } // this implementation doesn't check for bridge clashes with value types! diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 179f8d7124e6..d056d7e351a2 100644 --- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -179,7 +179,8 @@ object ExplicitOuter { * definitions in the class to find the one with the OuterAccessor flag. */ def outerAccessor(cls: ClassSymbol)(implicit ctx: Context): Symbol = - cls.info.member(outerAccName(cls)).suchThat(_ is OuterAccessor).symbol orElse + if (cls.isStatic) NoSymbol // fast return to avoid scanning package decls + else cls.info.member(outerAccName(cls)).suchThat(_ is OuterAccessor).symbol orElse cls.info.decls.find(_ is OuterAccessor).getOrElse(NoSymbol) /** Class has an outer accessor. Can be called only after phase ExplicitOuter. */ diff --git a/src/dotty/tools/dotc/transform/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala index a8cbb05959b6..fed47b160d9d 100644 --- a/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -10,6 +10,8 @@ import Types._ import Constants.Constant import Contexts.Context import Symbols._ +import SymDenotations._ +import Decorators._ import scala.collection.mutable import DenotTransformers._ import typer.Checking @@ -21,6 +23,7 @@ import NameOps._ * - ensures there are companion objects for all classes except module classes * - eliminates some kinds of trees: Imports, NamedArgs, all TypTrees other than TypeTree * - converts Select/Ident/SelectFromTypeTree nodes that refer to types to TypeTrees. + * - inserts `.package` for selections of package object members * - checks the bounds of AppliedTypeTrees * - stubs out native methods */ @@ -29,6 +32,15 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer { override def phaseName = "companions" + override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = tree match { + case Select(qual, _) if tree.symbol.exists => + assert(qual.tpe derivesFrom tree.symbol.owner, i"non member selection of ${tree.symbol.showLocated} from ${qual.tpe}") + case _: TypeTree => + case _: Import | _: NamedArg | _: TypTree => + assert(false, i"illegal tree: $tree") + case _ => + } + /** Reorder statements so that module classes always come after their companion classes, add missing companion classes */ private def reorderAndComplete(stats: List[Tree])(implicit ctx: Context): List[Tree] = { val moduleClassDefs, singleClassDefs = mutable.Map[Name, Tree]() @@ -96,14 +108,22 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer { } override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = - normalizeType(tree) + normalizeType { + val qual = tree.qualifier + qual.symbol.moduleClass.denot match { + case pkg: PackageClassDenotation if tree.symbol.maybeOwner.isPackageObject => + cpy.Select(tree)(qual select pkg.packageObj.symbol, tree.name) + case _ => + tree + } + } override def transformSelectFromTypeTree(tree: SelectFromTypeTree)(implicit ctx: Context, info: TransformerInfo) = normalizeType(tree) override def transformOther(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = tree match { case tree: Import => EmptyTree - case tree: NamedArg => tree.arg + case tree: NamedArg => transform(tree.arg) case AppliedTypeTree(tycon, args) => val tparams = tycon.tpe.typeSymbol.typeParams Checking.checkBounds( diff --git a/src/dotty/tools/dotc/transform/Flatten.scala b/src/dotty/tools/dotc/transform/Flatten.scala new file mode 100644 index 000000000000..dceefc0bc90b --- /dev/null +++ b/src/dotty/tools/dotc/transform/Flatten.scala @@ -0,0 +1,49 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers.SymTransformer +import Phases.Phase +import Contexts.Context +import Flags._ +import SymUtils._ +import SymDenotations.SymDenotation +import collection.mutable +import TreeTransforms.MiniPhaseTransform +import dotty.tools.dotc.transform.TreeTransforms.TransformerInfo + +class Flatten extends MiniPhaseTransform with SymTransformer { thisTransform => + import ast.tpd._ + override def phaseName = "flatten" + + def transformSym(ref: SymDenotation)(implicit ctx: Context) = { + if (ref.isClass && !ref.is(Package) && !ref.owner.is(Package)) { + ref.copySymDenotation( + name = ref.flatName, + owner = ref.enclosingPackageClass) + } + else ref + } + + override def treeTransformPhase = thisTransform.next + + private val liftedDefs = new mutable.ListBuffer[Tree] + + private def liftIfNested(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = + if (ctx.owner is Package) tree + else { + transformFollowing(tree).foreachInThicket(liftedDefs += _) + EmptyTree + } + + override def transformStats(stats: List[Tree])(implicit ctx: Context, info: TransformerInfo) = + if (ctx.owner is Package) { + val liftedStats = stats ++ liftedDefs + liftedDefs.clear + liftedStats + } + else stats + + override def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo) = + liftIfNested(tree) +} diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala index a08df1c33ef0..f36ff6247a3a 100644 --- a/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -31,7 +31,7 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this import ast.tpd._ /** the following two members override abstract members in Transform */ - val phaseName: String = "lambdalift" + val phaseName: String = "lambdaLift" override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Constructors]) // Constructors has to happen before LambdaLift because the lambda lift logic @@ -88,7 +88,7 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this } def narrowLiftedOwner(sym: Symbol, owner: Symbol)(implicit ctx: Context) = { - println(i"narrow lifted $sym") + ctx.log(i"narrow lifted $sym to $owner") if (sym.owner.skipConstructor.isTerm && owner.isProperlyContainedIn(liftedOwner(sym))) { changedLiftedOwner = true @@ -128,7 +128,7 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this */ private def markFree(sym: Symbol, enclosure: Symbol)(implicit ctx: Context): Boolean = try { if (!enclosure.exists) throw new NoPath - println(i"mark free: ${sym.showLocated} with owner ${sym.maybeOwner} marked free in $enclosure") + ctx.log(i"mark free: ${sym.showLocated} with owner ${sym.maybeOwner} marked free in $enclosure") (enclosure == sym.enclosure) || { ctx.debuglog(i"$enclosure != ${sym.enclosure}") narrowLiftedOwner(enclosure, sym.enclosingClass) @@ -164,9 +164,13 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this val sym = tree.symbol tree match { case tree: Ident => - if (sym.maybeOwner.isTerm) - if (sym is (Method, butNot = Label)) markCalled(sym, enclosure) + if (sym.maybeOwner.isTerm) { + if (sym is Label) + assert(enclosure == sym.enclosure, + i"attempt to refer to label $sym from nested $enclosure") + else if (sym is Method) markCalled(sym, enclosure) else if (sym.isTerm) markFree(sym, enclosure) + } case tree: Select => if (sym.isConstructor && sym.owner.owner.isTerm) markCalled(sym, enclosure) @@ -183,7 +187,7 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this case tree: TypeDef => if (sym.owner.isTerm) liftedOwner(sym) = sym.topLevelClass.owner case tree: Template => - liftedDefs(enclosure) = new mutable.ListBuffer + liftedDefs(tree.symbol.owner) = new mutable.ListBuffer case _ => } foldOver(enclosure, tree) @@ -198,7 +202,6 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this private def computeFreeVars()(implicit ctx: Context): Unit = do { changedFreeVars = false - // println(s"called = ${called.toList map { case (from, to) => from.showLocated + " -> " + to.toList.map(_.showLocated) }}") for { caller <- called.keys callee <- called(caller) @@ -257,7 +260,7 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this private def liftLocals()(implicit ctx: Context): Unit = { for ((local, lOwner) <- liftedOwner) { val (newOwner, maybeStatic) = - if (lOwner is Package) (local.topLevelClass, Static) + if (lOwner is Package) (local.topLevelClass, JavaStatic) else (lOwner, EmptyFlags) val maybeNotJavaPrivate = if (calledFromInner(local)) NotJavaPrivate else EmptyFlags local.copySymDenotation( @@ -275,14 +278,20 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this } } - override def init(implicit ctx: Context, info: TransformerInfo) = { - assert(ctx.phase == thisTransform) - (new CollectDependencies).traverse(NoSymbol, ctx.compilationUnit.tpdTree) - computeFreeVars() - computeLiftedOwners() - generateProxies()(ctx.withPhase(thisTransform.next)) - liftLocals()(ctx.withPhase(thisTransform.next)) - } + override def init(implicit ctx: Context, info: TransformerInfo) = + ctx.atPhase(thisTransform) { implicit ctx => + free.clear() + proxyMap.clear() + called.clear() + calledFromInner.clear() + liftedOwner.clear() + liftedDefs.clear() + (new CollectDependencies).traverse(NoSymbol, ctx.compilationUnit.tpdTree) + computeFreeVars() + computeLiftedOwners() + generateProxies()(ctx.withPhase(thisTransform.next)) + liftLocals()(ctx.withPhase(thisTransform.next)) + } private def currentEnclosure(implicit ctx: Context) = ctx.owner.enclosingMethod.skipConstructor @@ -292,8 +301,11 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this private def proxy(sym: Symbol)(implicit ctx: Context): Symbol = { def searchIn(enclosure: Symbol): Symbol = { - if (!enclosure.exists) - throw new IllegalArgumentException(i"Could not find proxy for ${sym.showDcl} in ${sym.ownersIterator.toList}, currentOwner= $currentEnclosure") + if (!enclosure.exists) { + def enclosures(encl: Symbol): List[Symbol] = + if (encl.exists) encl :: enclosures(encl.enclosure) else Nil + throw new IllegalArgumentException(i"Could not find proxy for ${sym.showDcl} in ${sym.ownersIterator.toList}, encl = $currentEnclosure, owners = ${currentEnclosure.ownersIterator.toList}%, %; enclosures = ${enclosures(currentEnclosure)}%, %") + } ctx.debuglog(i"searching for $sym(${sym.owner}) in $enclosure") proxyMap get enclosure match { case Some(pmap) => @@ -309,13 +321,15 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this } private def memberRef(sym: Symbol)(implicit ctx: Context, info: TransformerInfo): Tree = { - val clazz = sym.owner - val qual = if (clazz.isStaticOwner) singleton(clazz.thisType) else outer.path(clazz) + val clazz = sym.enclosingClass + val qual = + if (clazz.isStaticOwner) singleton(clazz.thisType) + else outer(ctx.withPhase(thisTransform)).path(clazz) transformFollowingDeep(qual.select(sym)) } private def proxyRef(sym: Symbol)(implicit ctx: Context, info: TransformerInfo): Tree = { - val psym = proxy(sym) + val psym = proxy(sym)(ctx.withPhase(thisTransform)) transformFollowingDeep(if (psym.owner.isTerm) ref(psym) else memberRef(psym)) } @@ -341,8 +355,9 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this } } - private def liftDef(tree: MemberDef)(implicit ctx: Context): Tree = { - liftedDefs(tree.symbol.owner) += rename(tree, tree.symbol.name) + private def liftDef(tree: MemberDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + val buf = liftedDefs(tree.symbol.owner) + transformFollowing(rename(tree, tree.symbol.name)).foreachInThicket(buf += _) EmptyTree } @@ -351,8 +366,11 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = { val sym = tree.symbol tree.tpe match { - case TermRef(NoPrefix, _) if sym.enclosure != currentEnclosure && !sym.isStatic => - (if (sym is Method) memberRef(sym) else proxyRef(sym)).withPos(tree.pos) + case tpe @ TermRef(prefix, _) => + if ((prefix eq NoPrefix) && sym.enclosure != currentEnclosure && !sym.isStatic) + (if (sym is Method) memberRef(sym) else proxyRef(sym)).withPos(tree.pos) + else if (!prefixIsElidable(tpe)) ref(tpe) + else tree case _ => tree } @@ -368,7 +386,9 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this val sym = tree.symbol val proxyHolder = sym.skipConstructor if (needsLifting(proxyHolder)) { - val paramsAdded = addFreeParams(tree, proxies(proxyHolder)).asInstanceOf[DefDef] + var paramsAdded = addFreeParams(tree, proxies(proxyHolder)).asInstanceOf[DefDef] + if (sym.is(JavaStatic) && !paramsAdded.mods.is(JavaStatic)) + paramsAdded = cpy.DefDef(paramsAdded)(mods = paramsAdded.mods | JavaStatic) if (sym.isConstructor) paramsAdded else liftDef(paramsAdded) } else tree @@ -389,16 +409,4 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this override def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo) = if (needsLifting(tree.symbol)) liftDef(tree) else tree - } - - -/* done in lazyvals? - case Block(stats, expr0) => - val (lzyVals, rest) = stats partition { - case stat: ValDef => stat.symbol.isLazy || stat.symbol.isModuleVar - case _ => false - } - if (lzyVals.isEmpty) tree - else treeCopy.Block(tree, lzyVals ::: rest, expr0) - -*/ +} diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala index ac92bb80c009..8af1b4a214d0 100644 --- a/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -393,12 +393,14 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans val res: Tree val nextBinder: Symbol - lazy val introducedRebindings = + lazy val introducedRebindings = /* if(nextBinder ne prevBinder) Rebindings(prevBinder, nextBinder) - else NoRebindings + else */ NoRebindings def chainBefore(next: Tree)(casegen: Casegen): Tree = - /*atPos(pos)(*/casegen.flatMapCond(cond, res, nextBinder, next)//) + if(prevBinder ne nextBinder) // happens when typeTest is known to succeed + /*atPos(pos)(*/casegen.flatMapCond(cond, res, nextBinder, next)//) + else casegen.flatMapGuard(cond, next) } // unless we're optimizing, emit local variable bindings for all subpatterns of extractor/case class patterns @@ -426,6 +428,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans private lazy val (stored, substed) = (subPatBinders, subPatRefs).zipped.partition{ case (sym, _) => storedBinders(sym) } + // dd: this didn't yet trigger error. But I believe it would. if this causes double denition of symbol error this can be replaced with NoRebindings protected lazy val introducedRebindings: Rebindings = if (!emitVars) Rebindings(subPatBinders, subPatRefs) else { val (subPatBindersSubstituted, subPatRefsSubstituted) = substed.unzip @@ -946,7 +949,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans case Bind(nme.WILDCARD, _) => true // don't skip when binding an interesting symbol! case Ident(nme.WILDCARD) => true case Alternative(ps) => ps forall unapply - case Typed(PatternBoundToUnderscore(), _) => true + case Typed(PatternBoundToUnderscore(), _) => false // true // Dmitry: change in dotty. Type test will be performed and the field must be stored case _ => false } } diff --git a/src/dotty/tools/dotc/transform/RestoreScopes.scala b/src/dotty/tools/dotc/transform/RestoreScopes.scala new file mode 100644 index 000000000000..4a42523266ec --- /dev/null +++ b/src/dotty/tools/dotc/transform/RestoreScopes.scala @@ -0,0 +1,36 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers.IdentityDenotTransformer +import Contexts.Context +import Symbols._ +import Scopes._ +import collection.mutable +import TreeTransforms.MiniPhaseTransform +import ast.Trees._ +import TreeTransforms.TransformerInfo + +/** The preceding lambda lift and flatten phases move symbols to different scopes + * and rename them. This miniphase cleans up afterwards and makes sure that all + * class scopes contain the symbols defined in them. + */ +class RestoreScopes extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => + import ast.tpd._ + override def phaseName = "restoreScopes" + + override def treeTransformPhase = thisTransform.next + + override def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo) = { + val TypeDef(_, _, Template(constr, _, _, body)) = tree + val restoredDecls = newScope + for (stat <- constr :: body) + if (stat.isInstanceOf[MemberDef] && stat.symbol.exists) + restoredDecls.enter(stat.symbol) + val cinfo = tree.symbol.asClass.classInfo + tree.symbol.copySymDenotation( + info = cinfo.derivedClassInfo( // Dotty deviation: Cannot expand cinfo inline without a type error + decls = restoredDecls: Scope)).installAfter(thisTransform) + tree + } +} diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala index 2875327c4712..ba45d3f04663 100644 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/src/dotty/tools/dotc/transform/SymUtils.scala @@ -5,6 +5,7 @@ import core._ import Types._ import Contexts._ import Symbols._ +import SymDenotations._ import Decorators._ import Names._ import StdNames._ @@ -13,7 +14,8 @@ import Flags._ import language.implicitConversions object SymUtils { - implicit def decorateSymUtils(sym: Symbol): SymUtils = new SymUtils(sym) + implicit def decorateSymbol(sym: Symbol): SymUtils = new SymUtils(sym) + implicit def decorateSymDenot(d: SymDenotation): SymUtils = new SymUtils(d.symbol) } /** A decorator that provides methods on symbols @@ -64,4 +66,7 @@ class SymUtils(val self: Symbol) extends AnyVal { def field(implicit ctx: Context): Symbol = self.owner.info.decl(self.asTerm.name.fieldName).suchThat(!_.is(Method)).symbol + + /** `fullName` where `$' is the separator character */ + def flatName(implicit ctx: Context): Name = self.fullNameSeparated('$') } diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index 1b5cc7c07c1a..f307f423728c 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -20,6 +20,7 @@ import reporting.ThrowingReporter import ast.Trees._ import ast.{tpd, untpd} import util.SourcePosition +import collection.mutable import ProtoTypes._ import java.lang.AssertionError @@ -53,21 +54,53 @@ class TreeChecker { val checkingCtx = ctx.fresh .setTyperState(ctx.typerState.withReporter(new ThrowingReporter(ctx.typerState.reporter))) val checker = new Checker(previousPhases(phasesToRun.toList)(ctx)) - checker.typedExpr(ctx.compilationUnit.tpdTree)(checkingCtx) + try checker.typedExpr(ctx.compilationUnit.tpdTree)(checkingCtx) + catch { + case ex: Throwable => + implicit val ctx: Context = checkingCtx + println(i"*** error while checking after phase ${checkingCtx.phase.prev} ***") + throw ex + } } class Checker(phasesToCheck: Seq[Phase]) extends ReTyper { - override def typed(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = { + + val definedSyms = new mutable.HashSet[Symbol] + + def withDefinedSym[T](tree: untpd.Tree)(op: => T)(implicit ctx: Context): T = { + if (tree.isDef) { + assert(!definedSyms.contains(tree.symbol), i"doubly defined symbol: ${tree.symbol}in $tree") + definedSyms += tree.symbol + //println(i"defined: ${tree.symbol}") + val res = op + definedSyms -= tree.symbol + //println(i"undefined: ${tree.symbol}") + res + } + else op + } + + def withDefinedSyms[T](trees: List[untpd.Tree])(op: => T)(implicit ctx: Context) = + trees.foldRightBN(op)(withDefinedSym(_)(_)) + + def withDefinedSymss[T](vparamss: List[List[untpd.ValDef]])(op: => T)(implicit ctx: Context): T = + vparamss.foldRightBN(op)(withDefinedSyms(_)(_)) + + def assertDefined(tree: untpd.Tree)(implicit ctx: Context) = + if (tree.symbol.maybeOwner.isTerm) + assert(definedSyms contains tree.symbol, i"undefined symbol ${tree.symbol}") + + override def typedUnadapted(tree: untpd.Tree, pt: Type)(implicit ctx: Context): tpd.Tree = { val res = tree match { case _: untpd.UnApply => // can't recheck patterns tree.asInstanceOf[tpd.Tree] case _: untpd.TypedSplice | _: untpd.Thicket | _: EmptyValDef[_] => - super.typed(tree) + super.typedUnadapted(tree) case _ if tree.isType => promote(tree) case _ => - val tree1 = super.typed(tree, pt) + val tree1 = super.typedUnadapted(tree, pt) def isSubType(tp1: Type, tp2: Type) = (tp1 eq tp2) || // accept NoType / NoType (tp1 <:< tp2) @@ -79,16 +112,18 @@ class TreeChecker { |After checking: ${tree1.show} |Why different : """.stripMargin + core.TypeComparer.explained((tp1 <:< tp2)(_)) - assert(isSubType(tree1.tpe, tree.typeOpt), divergenceMsg(tree1.tpe, tree.typeOpt)) + if (tree.hasType) // it might not be typed because Typer sometimes constructs new untyped trees and resubmits them to typedUnadapted + assert(isSubType(tree1.tpe, tree.typeOpt), divergenceMsg(tree1.tpe, tree.typeOpt)) tree1 - } + } phasesToCheck.foreach(_.checkPostCondition(res)) res } override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree = { assert(tree.isTerm || !ctx.isAfterTyper, tree.show + " at " + ctx.phase) - assert(tree.isType || !needsSelect(tree.tpe), i"bad type ${tree.tpe} for $tree") + assert(tree.isType || !needsSelect(tree.tpe), i"bad type ${tree.tpe} for $tree # ${tree.uniqueId}") + assertDefined(tree) super.typedIdent(tree, pt) } @@ -101,20 +136,38 @@ class TreeChecker { def ownerMatches(symOwner: Symbol, ctxOwner: Symbol): Boolean = symOwner == ctxOwner || ctxOwner.isWeakOwner && ownerMatches(symOwner, ctxOwner.owner) - if(!ownerMatches(tree.symbol.owner, ctx.owner)) { - assert(ownerMatches(tree.symbol.owner, ctx.owner), - i"bad owner; ${tree.symbol} has owner ${tree.symbol.owner}, expected was ${ctx.owner}\n" + - i"owner chain = ${tree.symbol.ownersIterator.toList}%, %, ctxOwners = ${ctx.outersIterator.map(_.owner).toList}%, %") - } + assert(ownerMatches(tree.symbol.owner, ctx.owner), + i"bad owner; ${tree.symbol} has owner ${tree.symbol.owner}, expected was ${ctx.owner}\n" + + i"owner chain = ${tree.symbol.ownersIterator.toList}%, %, ctxOwners = ${ctx.outersIterator.map(_.owner).toList}%, %") } override def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context) = { val TypeDef(_, _, impl @ Template(constr, _, _, _)) = cdef + assert(cdef.symbol == cls) + assert(impl.symbol.owner == cls) + assert(constr.symbol.owner == cls) + assert(cls.primaryConstructor == constr.symbol, i"mismatch, primary constructor ${cls.primaryConstructor}, in tree = ${constr.symbol}") checkOwner(impl) checkOwner(impl.constr) super.typedClassDef(cdef, cls) } + override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = + withDefinedSyms(ddef.tparams) { + withDefinedSymss(ddef.vparamss) { + super.typedDefDef(ddef, sym) + } + } + + override def typedCase(tree: untpd.CaseDef, pt: Type, selType: Type, gadtSyms: Set[Symbol])(implicit ctx: Context): CaseDef = { + withDefinedSyms(tree.pat.asInstanceOf[tpd.Tree].filterSubTrees(_.isInstanceOf[ast.Trees.Bind[_]])) { + super.typedCase(tree, pt, selType, gadtSyms) + } + } + + override def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) = + withDefinedSyms(tree.stats) { super.typedBlock(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 @@ -125,7 +178,7 @@ class TreeChecker { override def typedStats(trees: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = { for (tree <- trees) tree match { case tree: untpd.DefTree => checkOwner(tree) - case _: untpd.Thicket => assert(false, "unexpanded thicket in statement sequence") + case _: untpd.Thicket => assert(false, i"unexpanded thicket $tree in statement sequence $trees%\n%") case _ => } super.typedStats(trees, exprOwner) @@ -137,10 +190,13 @@ class TreeChecker { override def adapt(tree: Tree, pt: Type, original: untpd.Tree = untpd.EmptyTree)(implicit ctx: Context) = { def isPrimaryConstructorReturn = ctx.owner.isPrimaryConstructor && pt.isRef(ctx.owner.owner) && tree.tpe.isRef(defn.UnitClass) - if (ctx.mode.isExpr && !isPrimaryConstructorReturn && !pt.isInstanceOf[FunProto]) + if (ctx.mode.isExpr && + !tree.isEmpty && + !isPrimaryConstructorReturn && + !pt.isInstanceOf[FunProto]) assert(tree.tpe <:< pt, s"error at ${sourcePos(tree.pos)}\n" + - err.typeMismatchStr(tree.tpe, pt)) + err.typeMismatchStr(tree.tpe, pt) + "\ntree = " + tree) tree } } diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 035f19028d7c..a237e7781822 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -461,7 +461,7 @@ trait Applications extends Compatibility { self: Typer => val result = { var typedArgs = typedArgBuf.toList - val app0 = cpy.Apply(app)(normalizedFun, typedArgs) + def app0 = cpy.Apply(app)(normalizedFun, typedArgs) // needs to be a `def` because typedArgs can change later val app1 = if (!success) app0.withType(ErrorType) else { diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala index 6895f997874a..713549840a0c 100644 --- a/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/src/dotty/tools/dotc/typer/ReTyper.scala @@ -47,6 +47,9 @@ class ReTyper extends Typer { override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree = promote(tree) + override def typedSuper(tree: untpd.Super, pt: Type)(implicit ctx: Context): Tree = + promote(tree) + override def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = promote(tree) @@ -84,8 +87,8 @@ class ReTyper extends Typer { super.handleUnexpectedFunType(tree, fun) } - override def typed(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = - try super.typed(tree, pt) + override def typedUnadapted(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = + try super.typedUnadapted(tree, pt) catch { case ex: Throwable => println(i"exception while typing $tree of class ${tree.getClass} # ${tree.uniqueId}") diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index ccf67b55b9ae..cb6fefab1472 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -49,7 +49,21 @@ trait TypeAssigner { case TypeAlias(ref) => apply(ref) case info: ClassInfo => - mapOver(info.instantiatedParents.reduceLeft(ctx.typeComparer.andType(_, _))) + val parentType = info.instantiatedParents.reduceLeft(ctx.typeComparer.andType(_, _)) + def addRefinement(parent: Type, decl: Symbol) = { + val inherited = parentType.findMember(decl.name, info.cls.thisType, Private) + val inheritedInfo = inherited.atSignature(decl.info .signature).info + if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info)) + typr.echo( + i"add ref $parent $decl --> ", + RefinedType(parent, decl.name, decl.info)) + else + parent + } + val refinableDecls = info.decls.filterNot( + sym => sym.is(TypeParamAccessor | Private) || sym.isConstructor) + val fullType = (parentType /: refinableDecls)(addRefinement) + mapOver(fullType) case _ => mapOver(tp) } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 80eb5965c63f..1afa5f9f33f9 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -415,17 +415,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def escapingRefs(block: Block)(implicit ctx: Context): collection.Set[NamedType] = { var hoisted: Set[Symbol] = Set() lazy val locals = localSyms(block.stats).toSet - def isLocal(sym: Symbol): Boolean = - (locals contains sym) && !isHoistableClass(sym) - def isHoistableClass(sym: Symbol) = - sym.isClass && { - (hoisted contains sym) || { - hoisted += sym - !classLeaks(sym.asClass) - } - } def leakingTypes(tp: Type): collection.Set[NamedType] = - tp namedPartsWith (tp => isLocal(tp.symbol)) + tp namedPartsWith (tp => locals.contains(tp.symbol)) def typeLeaks(tp: Type): Boolean = leakingTypes(tp).nonEmpty def classLeaks(sym: ClassSymbol): Boolean = (ctx.owner is Method) || // can't hoist classes out of method bodies @@ -619,29 +610,29 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit accu(Set.empty, selType) } - def typedCase(tree: untpd.CaseDef): CaseDef = track("typedCase") { - def caseRest(pat: Tree)(implicit ctx: Context) = { - gadtSyms foreach (_.resetGADTFlexType) - pat foreachSubTree { - case b: Bind => - if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol) - else ctx.error(d"duplicate pattern variable: ${b.name}", b.pos) - case _ => - } - val guard1 = typedExpr(tree.guard, defn.BooleanType) - val body1 = typedExpr(tree.body, pt) - assignType(cpy.CaseDef(tree)(pat, guard1, body1), body1) - } - val doCase: () => CaseDef = - () => caseRest(typedPattern(tree.pat, selType))(ctx.fresh.setNewScope) - (doCase /: gadtSyms)((op, tsym) => tsym.withGADTFlexType(op))() - } - - val cases1 = tree.cases mapconserve typedCase + val cases1 = tree.cases mapconserve (typedCase(_, pt, selType, gadtSyms)) assignType(cpy.Match(tree)(sel1, cases1), cases1) } } + def typedCase(tree: untpd.CaseDef, pt: Type, selType: Type, gadtSyms: Set[Symbol])(implicit ctx: Context): CaseDef = track("typedCase") { + def caseRest(pat: Tree)(implicit ctx: Context) = { + gadtSyms foreach (_.resetGADTFlexType) + pat foreachSubTree { + case b: Bind => + if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol) + else ctx.error(d"duplicate pattern variable: ${b.name}", b.pos) + case _ => + } + val guard1 = typedExpr(tree.guard, defn.BooleanType) + val body1 = typedExpr(tree.body, pt) + assignType(cpy.CaseDef(tree)(pat, guard1, body1), body1) + } + val doCase: () => CaseDef = + () => caseRest(typedPattern(tree.pat, selType))(ctx.fresh.setNewScope) + (doCase /: gadtSyms)((op, tsym) => tsym.withGADTFlexType(op))() + } + def typedReturn(tree: untpd.Return)(implicit ctx: Context): Return = track("typedReturn") { def returnProto(owner: Symbol) = if (owner.isConstructor) defn.UnitType else owner.info.finalResultType diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 614dc95271db..0d30d806d95c 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -15,7 +15,7 @@ class tests extends CompilerTest { implicit val defaultOptions = noCheckOptions ++ List( "-Yno-deep-subtypes", - "-Ycheck:patternMatcher,gettersSetters,constructors" + "-Ycheck:patternMatcher,gettersSetters,restoreScopes" ) val twice = List("#runs", "2", "-YnoDoubleBindings") @@ -99,6 +99,7 @@ class tests extends CompilerTest { @Test def neg_variances = compileFile(negDir, "variances", xerrors = 2) @Test def neg_badAuxConstr = compileFile(negDir, "badAuxConstr", xerrors = 2) @Test def neg_typetest = compileFile(negDir, "typetest", xerrors = 1) + @Test def neg_t1569_failedAvoid = compileFile(negDir, "t1569-failedAvoid", xerrors = 1) @Test def dotc = compileDir(dotcDir + "tools/dotc", twice)(allowDeepSubtypes) @Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", twice) @Test def dotc_config = compileDir(dotcDir + "tools/dotc/config", twice) diff --git a/tests/neg/t1569-failedAvoid.scala b/tests/neg/t1569-failedAvoid.scala new file mode 100644 index 000000000000..9d0fbb37a3b8 --- /dev/null +++ b/tests/neg/t1569-failedAvoid.scala @@ -0,0 +1,9 @@ +// This was t1569.scala. +// It fails in dotty because the expected type of the anonymous function in the last line +// is fully determined (C). So that type is taken as the type of the anonymous function. +// See pos/t1569a.scala for related examples that work. +object Bug { + class C { type T } + def foo(x: Int)(y: C)(z: y.T): Unit = {} + foo(3)(new C { type T = String })("hello") +} diff --git a/tests/pos/tailcall/t1672.scala b/tests/pending/pos/tailcall/t1672.scala similarity index 100% rename from tests/pos/tailcall/t1672.scala rename to tests/pending/pos/tailcall/t1672.scala diff --git a/tests/pos/avoid.scala b/tests/pos/avoid.scala new file mode 100644 index 000000000000..51471feaae65 --- /dev/null +++ b/tests/pos/avoid.scala @@ -0,0 +1,10 @@ +abstract class C { + def y: Any +} + +object test { + val x = new C{ + def y: String = "abc" + } + val z: String = x.y +} diff --git a/tests/pos/bynamefuns.scala b/tests/pos/bynamefuns.scala new file mode 100644 index 000000000000..5aa1df38df2c --- /dev/null +++ b/tests/pos/bynamefuns.scala @@ -0,0 +1,15 @@ +object Test { + + type LF = (=> Int) => Int + + def f(x: => Int) = x * x + + val x: LF = f + + def g = 3 + + f(11) + x(g) + x(11) + +} diff --git a/tests/pos/t1569.scala b/tests/pos/t1569.scala deleted file mode 100644 index a7200a6d1ebc..000000000000 --- a/tests/pos/t1569.scala +++ /dev/null @@ -1,5 +0,0 @@ -object Bug { - class C { type T } - def foo(x: Int)(y: C)(z: y.T): Unit = {} - foo(3)(new C { type T = String })("hello") -} diff --git a/tests/pos/t1569a.scala b/tests/pos/t1569a.scala new file mode 100644 index 000000000000..6cc3619a4697 --- /dev/null +++ b/tests/pos/t1569a.scala @@ -0,0 +1,12 @@ +object Bug { + class C[T] { type TT = T } + def foo[U](x: Int)(y: C[U])(z: y.TT): Unit = {} + foo(3)(new C[String])("hello") +} + +object Bug2 { + class C { type T } + class D extends C { type T = String } + def foo(x: Int)(y: C)(z: y.T): Unit = {} + foo(3)(new D)("hello") +}