diff options
author | Dmitry Petrashko <dark@d-d.me> | 2014-11-10 13:27:50 +0100 |
---|---|---|
committer | Dmitry Petrashko <dark@d-d.me> | 2014-11-10 13:27:50 +0100 |
commit | 42e51d56303c3e43d091dd0ed1a32c28d922a519 (patch) | |
tree | 31d852b1db06c48b3129a594030928e6315ea64f /src/dotty | |
parent | a9481bbb42d0439ba98a1bce33051e20c23f74b6 (diff) | |
parent | 83734e1962c11b3e699ece5787caf845cbfa6c0d (diff) | |
download | dotty-42e51d56303c3e43d091dd0ed1a32c28d922a519.tar.gz dotty-42e51d56303c3e43d091dd0ed1a32c28d922a519.tar.bz2 dotty-42e51d56303c3e43d091dd0ed1a32c28d922a519.zip |
Merge pull request #217 from dotty-staging/transform/mixin
Transform/mixin
Diffstat (limited to 'src/dotty')
29 files changed, 677 insertions, 142 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index f4690df08..004a3868c 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -46,14 +46,16 @@ class Compiler { new TailRec), List(new PatternMatcher, new ExplicitOuter, - // new LazyValTranformContext().transformer, // disabled, awaiting fixes new Splitter), List(new ElimByName, new InterceptedMethods, new Literalize, - new GettersSetters), + new Getters, + new ResolveSuper), List(new Erasure), - List(new CapturedVars, + List(new Mixin, + new Memoize, // TODO: Make LazyVals a part of this phase + new CapturedVars, new Constructors), List(new LambdaLift, new Flatten, diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index 2a55d6732..4a492560f 100644 --- a/src/dotty/tools/dotc/TypeErasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -4,6 +4,7 @@ package core import Symbols._, Types._, Contexts._, Flags._, Names._, StdNames._, Decorators._, Flags.JavaDefined import dotc.transform.ExplicitOuter._ +import typer.Mode import util.DotClass /** Erased types are: @@ -89,7 +90,7 @@ object TypeErasure { /** The current context with a phase no later than erasure */ private def erasureCtx(implicit ctx: Context) = - if (ctx.erasedTypes) ctx.withPhase(ctx.erasurePhase) else ctx + if (ctx.erasedTypes) ctx.withPhase(ctx.erasurePhase).addMode(Mode.FutureDefsOK) else ctx def erasure(tp: Type)(implicit ctx: Context): Type = scalaErasureFn(tp)(erasureCtx) def semiErasure(tp: Type)(implicit ctx: Context): Type = semiErasureFn(tp)(erasureCtx) @@ -107,6 +108,8 @@ object TypeErasure { case tp: TermRef => assert(tp.symbol.exists, tp) TermRef(erasedRef(tp.prefix), tp.symbol.asTerm) + case tp: ThisType => + tp case tp => erasure(tp) } @@ -141,7 +144,12 @@ 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 eraseInfo(tp)(erasureCtx) + else eraseInfo(tp)(erasureCtx) match { + case einfo: MethodType if sym.isGetter && einfo.resultType.isRef(defn.UnitClass) => + defn.BoxedUnitClass.typeRef + case einfo => + einfo + } } def isUnboundedGeneric(tp: Type)(implicit ctx: Context) = !( @@ -265,7 +273,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild case tp: TermRef => this(tp.widen) case ThisType(_) => - tp + this(tp.widen) case SuperType(thistpe, supertpe) => SuperType(this(thistpe), this(supertpe)) case ExprType(rt) => @@ -319,7 +327,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild } def eraseInfo(tp: Type)(implicit ctx: Context) = tp match { - case ExprType(rt) => MethodType(Nil, Nil, erasure(rt)) + case ExprType(rt) => MethodType(Nil, Nil, eraseResult(rt)) case tp => erasure(tp) } diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 74ba79176..735b218e3 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -6,7 +6,7 @@ import transform.SymUtils._ import core._ import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Symbols._ -import Denotations._, Decorators._ +import Denotations._, Decorators._, DenotTransformers._ import config.Printers._ import typer.Mode import collection.mutable @@ -39,8 +39,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def This(cls: ClassSymbol)(implicit ctx: Context): This = untpd.This(cls.name).withType(cls.thisType) - def Super(qual: Tree, mix: TypeName, inConstrCall: Boolean)(implicit ctx: Context): Super = - ta.assignType(untpd.Super(qual, mix), qual, inConstrCall) + def Super(qual: Tree, mix: TypeName, inConstrCall: Boolean, mixinClass: Symbol = NoSymbol)(implicit ctx: Context): Super = + ta.assignType(untpd.Super(qual, mix), qual, inConstrCall, mixinClass) def Apply(fn: Tree, args: List[Tree])(implicit ctx: Context): Apply = ta.assignType(untpd.Apply(fn, args), fn, args) @@ -527,7 +527,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { */ def changeOwner(from: Symbol, to: Symbol)(implicit ctx: Context): ThisTree = { def loop(from: Symbol, froms: List[Symbol], tos: List[Symbol]): ThisTree = { - if (from.isWeakOwner) loop(from.owner, from :: froms, to :: tos) + if (from.isWeakOwner && !from.owner.isClass) + loop(from.owner, from :: froms, to :: tos) else { //println(i"change owner ${from :: froms}%, % ==> $tos of $tree") new TreeTypeMap(oldOwners = from :: froms, newOwners = tos).apply(tree) @@ -536,6 +537,26 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { loop(from, Nil, to :: Nil) } + /** After phase `trans`, set the owner of every definition in this tree that was formerly + * owner by `from` to `to`. + */ + def changeOwnerAfter(from: Symbol, to: Symbol, trans: DenotTransformer)(implicit ctx: Context): ThisTree = { + assert(ctx.phase == trans.next) + val traverser = new TreeTraverser { + def traverse(tree: Tree) = tree match { + case tree: DefTree => + val sym = tree.symbol + if (sym.denot(ctx.withPhase(trans)).owner == from) + sym.copySymDenotation(owner = to).installAfter(trans) + if (sym.isWeakOwner) traverseChildren(tree) + case _ => + traverseChildren(tree) + } + } + traverser.traverse(tree) + tree + } + def select(name: Name)(implicit ctx: Context): Select = Select(tree, name) diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index 743925e40..abde6cb53 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -107,8 +107,9 @@ class ScalaSettings extends Settings.SettingGroup { val Xdce = BooleanSetting("-Ydead-code", "Perform dead code elimination.") val debug = BooleanSetting("-Ydebug", "Increase the quantity of debugging output.") val debugNames = BooleanSetting("-YdebugNames", "Show name-space indicators when printing names") - val debugTrace = BooleanSetting("-YdebugTrace", "Trace core operations") - val debugFlags = BooleanSetting("-YdebugFlags", "Print all flags of definitions") + val debugTrace = BooleanSetting("-Ydebug-trace", "Trace core operations") + val debugFlags = BooleanSetting("-Ydebug-flags", "Print all flags of definitions") + val debugOwners = BooleanSetting("-Ydebug-owners", "Print all owners of definitions (requires -Yprint-syms)") //val doc = BooleanSetting ("-Ydoc", "Generate documentation") val termConflict = ChoiceSetting("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error") val inline = BooleanSetting("-Yinline", "Perform inlining when possible.") diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index de6b0cabf..6293d18d2 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -297,16 +297,21 @@ object Contexts { def thisCallArgContext: Context = { assert(owner.isClassConstructor) val constrCtx = outersIterator.dropWhile(_.outer.owner == owner).next - var classCtx = outersIterator.dropWhile(!_.isClassDefContext).next - classCtx.superOrThisCallContext(owner, constrCtx.scope).setTyperState(typerState) + superOrThisCallContext(owner, constrCtx.scope).setTyperState(typerState) } /** The super= or this-call context with given owner and locals. */ private def superOrThisCallContext(owner: Symbol, locals: Scope): FreshContext = { - assert(isClassDefContext) - outer.fresh.setOwner(owner).setScope(locals).setMode(ctx.mode | Mode.InSuperCall) + var classCtx = outersIterator.dropWhile(!_.isClassDefContext).next + classCtx.outer.fresh.setOwner(owner).setScope(locals).setMode(classCtx.mode | Mode.InSuperCall) } + /** The context of expression `expr` seen as a member of a statement sequence */ + def exprContext(stat: Tree[_ >: Untyped], exprOwner: Symbol) = + if (exprOwner == this.owner) this + else if (untpd.isSuperConstrCall(stat) && this.owner.isClass) superCallContext + else ctx.fresh.setOwner(exprOwner) + /** The current source file; will be derived from current * compilation unit. */ diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 82fd60fa0..ce11759ce 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -202,7 +202,7 @@ object Denotations { def requiredClass(name: PreName)(implicit ctx: Context): ClassSymbol = info.member(name.toTypeName).requiredSymbol(_.isClass).asClass - /** The denotation that has a type matching `targetType` when seen + /** The alternative of this denotation that has a type matching `targetType` when seen * as a member of type `site`, `NoDenotation` if none exists. */ def matchingDenotation(site: Type, targetType: Type)(implicit ctx: Context): SingleDenotation = diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index beb3142d3..bc15f6a06 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -47,13 +47,15 @@ object NameOps { } } - object SuperAccessorName { - val pre = nme.SUPER_PREFIX + class PrefixNameExtractor(pre: TermName) { def apply(name: TermName): TermName = pre ++ name def unapply(name: TermName): Option[TermName] = if (name startsWith pre) Some(name.drop(pre.length).asTermName) else None } + object SuperAccessorName extends PrefixNameExtractor(nme.SUPER_PREFIX) + object InitializerName extends PrefixNameExtractor(nme.INITIALIZER_PREFIX) + implicit class NameDecorator[N <: Name](val name: N) extends AnyVal { import nme._ @@ -68,14 +70,13 @@ object NameOps { def isProtectedAccessorName = name startsWith PROTECTED_PREFIX def isReplWrapperName = name containsSlice INTERPRETER_IMPORT_WRAPPER def isSetterName = name endsWith SETTER_SUFFIX - def isTraitSetterName = isSetterName && (name containsSlice TRAIT_SETTER_PREFIX) def isSingletonName = name endsWith SINGLETON_SUFFIX def isModuleClassName = name endsWith MODULE_SUFFIX def isImportName = name startsWith IMPORT def isFieldName = name endsWith LOCAL_SUFFIX def isInheritedName = name.length > 0 && name.head == '(' && name.startsWith(nme.INHERITED) def isDefaultGetterName = name.isTermName && name.asTermName.defaultGetterIndex >= 0 - + def isScala2LocalSuffix = name.endsWith(" ") def isModuleVarName(name: Name): Boolean = name.stripAnonNumberSuffix endsWith MODULE_VAR_SUFFIX @@ -224,9 +225,6 @@ object NameOps { implicit class TermNameDecorator(val name: TermName) extends AnyVal { import nme._ - def traitSetterName: TermName = - nme.TRAIT_SETTER_PREFIX ++ setterName - def setterName: TermName = if (name.isFieldName) name.fieldToGetter.setterName else name ++ SETTER_SUFFIX @@ -240,13 +238,8 @@ object NameOps { else name ++ LOCAL_SUFFIX private def setterToGetter: TermName = { - val p = name.indexOfSlice(TRAIT_SETTER_PREFIX) - if (p >= 0) - (name drop (p + TRAIT_SETTER_PREFIX.length)).asTermName.getterName - else { - assert(name.endsWith(SETTER_SUFFIX), name + " is referenced as a setter but has wrong name format") - name.take(name.length - SETTER_SUFFIX.length).asTermName - } + assert(name.endsWith(SETTER_SUFFIX), name + " is referenced as a setter but has wrong name format") + name.take(name.length - SETTER_SUFFIX.length).asTermName } def fieldToGetter: TermName = { @@ -283,6 +276,9 @@ object NameOps { -1 } + def stripScala2LocalSuffix: TermName = + if (name.isScala2LocalSuffix) name.init.asTermName else name + /** The name of an accessor for protected symbols. */ def protectedAccessorName: TermName = PROTECTED_PREFIX ++ name.unexpandedName() diff --git a/src/dotty/tools/dotc/core/Scopes.scala b/src/dotty/tools/dotc/core/Scopes.scala index 494a26f7e..09bdca196 100644 --- a/src/dotty/tools/dotc/core/Scopes.scala +++ b/src/dotty/tools/dotc/core/Scopes.scala @@ -20,7 +20,7 @@ import printing.Printer import util.common._ import util.DotClass import SymDenotations.NoDenotation -import collection.mutable.ListBuffer +import collection.mutable object Scopes { @@ -172,12 +172,27 @@ object Scopes { */ private var elemsCache: List[Symbol] = null - def cloneScope(implicit ctx: Context): MutableScope = newScopeWith(this.toList: _*) + /** Clone scope, taking care not to force the denotations of any symbols in the scope. + */ + def cloneScope(implicit ctx: Context): MutableScope = { + val entries = new mutable.ArrayBuffer[ScopeEntry] + var e = lastEntry + while ((e ne null) && e.owner == this) { + entries += e + e = e.prev + } + val scope = newScope + for (i <- entries.length - 1 to 0 by -1) { + val e = entries(i) + scope.newScopeEntry(e.name, e.sym) + } + scope + } - /** create and enter a scope entry */ - protected def newScopeEntry(sym: Symbol)(implicit ctx: Context): ScopeEntry = { + /** create and enter a scope entry with given name and symbol */ + protected def newScopeEntry(name: Name, sym: Symbol)(implicit ctx: Context): ScopeEntry = { ensureCapacity(if (hashTable ne null) hashTable.length else MinHash) - val e = new ScopeEntry(sym.name, sym, this) + val e = new ScopeEntry(name, sym, this) e.prev = lastEntry lastEntry = e if (hashTable ne null) enterInHash(e) @@ -186,6 +201,10 @@ object Scopes { e } + /** create and enter a scope entry */ + protected def newScopeEntry(sym: Symbol)(implicit ctx: Context): ScopeEntry = + newScopeEntry(sym.name, sym) + private def enterInHash(e: ScopeEntry)(implicit ctx: Context): Unit = { val idx = e.name.hashCode & (hashTable.length - 1) e.tail = hashTable(idx) @@ -325,7 +344,7 @@ object Scopes { } override def implicitDecls(implicit ctx: Context): List[TermRef] = { - var irefs = new ListBuffer[TermRef] + var irefs = new mutable.ListBuffer[TermRef] var e = lastEntry while (e ne null) { if (e.sym is Implicit) { diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 99290f084..8393eb56f 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -119,9 +119,9 @@ object StdNames { val SINGLETON_SUFFIX: N = ".type" val SPECIALIZED_SUFFIX: N = "$sp" val SUPER_PREFIX: N = "super$" - val TRAIT_SETTER_PREFIX: N = "_setter_$" val WHILE_PREFIX: N = "while$" - val DEFAULT_EXCEPTION_NAME: N = "ex$" + val DEFAULT_EXCEPTION_NAME: N = "ex$" + val INITIALIZER_PREFIX: N = "initial$" // value types (and AnyRef) are all used as terms as well // as (at least) arguments to the @specialize annotation. diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index ae8fceeb7..dfb58f68b 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -425,10 +425,14 @@ object SymDenotations { final def isSourceMethod(implicit ctx: Context) = this is (Method, butNot = Accessor) /** Is this a setter? */ - final def isGetter(implicit ctx: Context) = (this is Accessor) && !originalName.isSetterName + final def isGetter(implicit ctx: Context) = + (this is Accessor) && !originalName.isSetterName && !originalName.isScala2LocalSuffix /** Is this a setter? */ - final def isSetter(implicit ctx: Context) = (this is Accessor) && originalName.isSetterName + final def isSetter(implicit ctx: Context) = + (this is Accessor) && + originalName.isSetterName && + info.firstParamTypes.nonEmpty // to avoid being fooled by var x_= : Unit = ... /** is this the constructor of a class? */ final def isClassConstructor = name == nme.CONSTRUCTOR @@ -560,7 +564,7 @@ object SymDenotations { def membersNeedAsSeenFrom(pre: Type)(implicit ctx: Context) = !( this.isTerm || this.isStaticOwner - || ctx.erasedTypes && symbol != defn.ArrayClass + || ctx.erasedTypes || (pre eq NoPrefix) || (pre eq thisType) ) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index d0ddfdd28..5228c077e 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1173,8 +1173,19 @@ object Types { if (newd.exists) newd else d.staleSymbolError } case d => - if (d.validFor.runId == ctx.period.runId) d.current - else loadDenot + if (d.validFor.runId != ctx.period.runId) + loadDenot + // The following branch was used to avoid an assertErased error. + // It's idea was to void keeping non-sym denotations after erasure + // since they violate the assertErased contract. But the problem is + // that when seen again in an earlier phase the denotation is + // still seen as a SymDenotation, whereas it should be a SingleDenotation. + // That's why the branch is disabled. + // + // else if (ctx.erasedTypes && lastSymbol != null) + // denotOfSym(lastSymbol) + else + d.current } if (ctx.typerState.ephemeral) record("ephemeral cache miss: loadDenot") else if (d.exists) { @@ -1505,7 +1516,7 @@ object Types { * signature, if denotation is not yet completed. */ def apply(prefix: Type, name: TermName, denot: Denotation)(implicit ctx: Context): TermRef = { - if ((prefix eq NoPrefix) || denot.symbol.isFresh) + if ((prefix eq NoPrefix) || denot.symbol.isFresh || ctx.erasedTypes) apply(prefix, denot.symbol.asTerm) else denot match { case denot: SymDenotation if denot.isCompleted => withSig(prefix, name, denot.signature) @@ -1527,7 +1538,7 @@ object Types { * (2) The name in the term ref need not be the same as the name of the Symbol. */ def withSymAndName(prefix: Type, sym: TermSymbol, name: TermName)(implicit ctx: Context): TermRef = - if ((prefix eq NoPrefix) || sym.isFresh) + if ((prefix eq NoPrefix) || sym.isFresh || ctx.erasedTypes) withFixedSym(prefix, name, sym) else if (sym.defRunId != NoRunId && sym.isCompleted) withSig(prefix, name, sym.signature) withSym (sym, sym.signature) @@ -1538,7 +1549,7 @@ object Types { * (which must be completed). */ def withSig(prefix: Type, sym: TermSymbol)(implicit ctx: Context): TermRef = - if ((prefix eq NoPrefix) || sym.isFresh) withFixedSym(prefix, sym.name, sym) + if ((prefix eq NoPrefix) || sym.isFresh || ctx.erasedTypes) withFixedSym(prefix, sym.name, sym) else withSig(prefix, sym.name, sym.signature).withSym(sym, sym.signature) /** Create a term ref with given prefix, name and signature */ @@ -1547,7 +1558,7 @@ object Types { /** Create a term ref with given prefix, name, signature, and initial denotation */ def withSigAndDenot(prefix: Type, name: TermName, sig: Signature, denot: Denotation)(implicit ctx: Context): TermRef = { - if ((prefix eq NoPrefix) || denot.symbol.isFresh) + if ((prefix eq NoPrefix) || denot.symbol.isFresh || ctx.erasedTypes) withFixedSym(prefix, denot.symbol.asTerm.name, denot.symbol.asTerm) else withSig(prefix, name, sig) @@ -1623,8 +1634,10 @@ object Types { final class CachedSuperType(thistpe: Type, supertpe: Type) extends SuperType(thistpe, supertpe) object SuperType { - def apply(thistpe: Type, supertpe: Type)(implicit ctx: Context): Type = + def apply(thistpe: Type, supertpe: Type)(implicit ctx: Context): Type = { + assert(thistpe != NoPrefix) unique(new CachedSuperType(thistpe, supertpe)) + } } /** A constant type with single `value`. */ @@ -1817,7 +1830,7 @@ object Types { } catch { case ex: AssertionError => - println(i"failure while taking result signture of $resultType") + println(i"failure while taking result signture of $this: $resultType") throw ex } @@ -1837,7 +1850,7 @@ object Types { extends CachedGroundType with BindingType with TermType with MethodOrPoly with NarrowCached { thisMethodType => override val resultType = resultTypeExp(this) - assert(resultType != NoType) + assert(resultType.exists) def isJava = false def isImplicit = false diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 9fba7ec09..78ee32b98 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -288,7 +288,7 @@ class PlainPrinter(_ctx: Context) extends Printer { else if (flags is Mutable) "variable" else if (sym.isClassConstructor && sym.isPrimaryConstructor) "primary constructor" else if (sym.isClassConstructor) "constructor" - else if (sym.isSourceMethod) "method" + else if (sym.is(Method)) "method" else if (sym.isTerm) "value" else "" } @@ -303,7 +303,7 @@ class PlainPrinter(_ctx: Context) extends Printer { else if (flags is Mutable) "var" else if (flags is Package) "package" else if (flags is Module) "object" - else if (sym.isSourceMethod) "def" + else if (sym is Method) "def" else if (sym.isTerm && (!(flags is Param))) "val" else "" } diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index e43aaa24f..50b73a357 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -205,7 +205,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } def dclTextOr(treeText: => Text) = - if (useSymbol) annotsText(tree.symbol) ~~ dclText(tree.symbol) + if (useSymbol) + annotsText(tree.symbol) ~~ dclText(tree.symbol) ~ + ( " <in " ~ toText(tree.symbol.owner) ~ ">" provided ctx.settings.debugOwners.value) else treeText def idText(tree: untpd.Tree): Text = { diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 7bde1ba4f..9420ce2c0 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -109,32 +109,6 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor } } - val superCalls = new mutable.ListBuffer[Tree] - - // If parent is a constructor call, pull out the call into a separate - // supercall constructor, which gets appended to `superCalls`, and keep - // only the type. - def normalizeParent(tree: Tree) = tree match { - case superApp @ Apply( - superSel @ Select( - superNew @ New(superType), - nme.CONSTRUCTOR), - superArgs) => - val toClass = !superType.symbol.is(Trait) - val mappedArgs = superArgs.map(intoConstr(_, inSuperCall = toClass)) - val receiver = - if (toClass) Super(This(cls), tpnme.EMPTY, inConstrCall = true) - else This(cls) - superCalls += - cpy.Apply(superApp)( - receiver.withPos(superNew.pos) - .select(superSel.symbol).withPos(superSel.pos), - mappedArgs) - superType - case tree: TypeTree => tree - } - val parentTypeTrees = tree.parents.map(normalizeParent) - // Collect all private parameter accessors and value definitions that need // to be retained. There are several reasons why a parameter accessor or // definition might need to be retained: @@ -172,16 +146,12 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor traverse(stat) } } - usage.collect(superCalls.toList ++ tree.body) + usage.collect(tree.body) def isRetained(acc: Symbol) = !mightBeDropped(acc) || usage.retained(acc) val constrStats, clsStats = new mutable.ListBuffer[Tree] - def assign(vble: Symbol, rhs: Tree): Tree = - if (cls is Trait) ref(vble.setter).appliedTo(rhs) - else Assign(ref(vble), rhs) - // Split class body into statements that go into constructor and // definitions that are kept as members of the class. def splitStats(stats: List[Tree]): Unit = stats match { @@ -191,7 +161,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor val sym = stat.symbol if (isRetained(sym)) { if (!rhs.isEmpty && !isWildcardArg(rhs)) - constrStats += assign(sym, intoConstr(rhs)).withPos(stat.pos) + constrStats += Assign(ref(sym), intoConstr(rhs)).withPos(stat.pos) clsStats += cpy.ValDef(stat)(rhs = EmptyTree) } else if (!rhs.isEmpty) { @@ -215,7 +185,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor // The initializers for the retained accessors */ val copyParams = accessorFields.filter(isRetained).map(acc => - assign(acc, ref(acc.subst(accessors, paramSyms))).withPos(tree.pos)) + Assign(ref(acc), ref(acc.subst(accessors, paramSyms))).withPos(tree.pos)) // Drop accessors that are not retained from class scope val dropped = usage.dropped @@ -226,10 +196,14 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor decls = clsInfo.decls.filteredScope(!dropped.contains(_)))) } + val (superCalls, followConstrStats) = constrStats.toList match { + case (sc: Apply) :: rest if sc.symbol.isConstructor => (sc :: Nil, rest) + case stats => (Nil, stats) + } + cpy.Template(tree)( constr = cpy.DefDef(constr)( - rhs = Block(superCalls.toList ::: copyParams ::: constrStats.toList, unitLiteral)), - parents = parentTypeTrees, + rhs = Block(superCalls ::: copyParams ::: followConstrStats, unitLiteral)), body = clsStats.toList) } }
\ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 9f381bb8e..0a34b9e7c 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -94,7 +94,8 @@ class Erasure extends Phase with DenotTransformer { thisTransformer => if (ctx.mode.isExpr) tree.tpe match { case ref: TermRef => - assert(ref.denot.isInstanceOf[SymDenotation], + assert(ref.denot.isInstanceOf[SymDenotation] || + ref.denot.isInstanceOf[UniqueRefDenotation], i"non-sym type $ref of class ${ref.getClass} with denot of class ${ref.denot.getClass} of $tree") case _ => } @@ -247,6 +248,10 @@ object Erasure extends TypeTestsCasts{ tree.withType(erased) } + override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal = + if (tree.typeOpt.isRef(defn.UnitClass)) tree.withType(tree.typeOpt) + else super.typedLiteral(tree) + /** Type check select nodes, applying the following rewritings exhaustively * on selections `e.m`, where `OT` is the type of the owner of `m` and `ET` * is the erased type of the selection's original qualifier expression. @@ -277,7 +282,7 @@ object Erasure extends TypeTestsCasts{ case _ => sym.name } untpd.cpy.Select(tree)(qual, sym.name) - .withType(NamedType.withSymAndName(qual.tpe, sym, name)) + .withType(NamedType.withFixedSym(qual.tpe, sym)) } def selectArrayMember(qual: Tree, erasedPre: Type): Tree = @@ -369,12 +374,20 @@ object Erasure extends TypeTestsCasts{ } } + override def typedValDef(vdef: untpd.ValDef, sym: Symbol)(implicit ctx: Context): ValDef = + super.typedValDef(untpd.cpy.ValDef(vdef)( + tpt = untpd.TypedSplice(TypeTree(sym.info).withPos(vdef.tpt.pos))), sym) + override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = { + val restpe = sym.info.resultType val ddef1 = untpd.cpy.DefDef(ddef)( tparams = Nil, vparamss = ddef.vparamss.flatten :: Nil, - tpt = // keep UnitTypes intact in result position - untpd.TypedSplice(TypeTree(eraseResult(ddef.tpt.typeOpt)).withPos(ddef.tpt.pos))) + tpt = untpd.TypedSplice(TypeTree(restpe).withPos(ddef.tpt.pos)), + rhs = ddef.rhs match { + case id @ Ident(nme.WILDCARD) => untpd.TypedSplice(id.withType(restpe)) + case _ => ddef.rhs + }) super.typedDefDef(ddef1, sym) } diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala index d056d7e35..28d742b5e 100644 --- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -187,6 +187,10 @@ object ExplicitOuter { private def hasOuter(cls: ClassSymbol)(implicit ctx: Context): Boolean = needsOuterIfReferenced(cls) && outerAccessor(cls).exists + /** Class constructor takes an outer argument. Can be called only after phase ExplicitOuter. */ + private def hasOuterParam(cls: ClassSymbol)(implicit ctx: Context): Boolean = + !cls.is(Trait) && needsOuterIfReferenced(cls) && outerAccessor(cls).exists + /** Tree references a an outer class of `cls` which is not a static owner. */ def referencesOuter(cls: Symbol, tree: Tree)(implicit ctx: Context): Boolean = { @@ -248,7 +252,7 @@ object ExplicitOuter { /** If `cls` has an outer parameter add one to the method type `tp`. */ def addParam(cls: ClassSymbol, tp: Type): Type = - if (hasOuter(cls)) { + if (hasOuterParam(cls)) { val mt @ MethodType(pnames, ptypes) = tp mt.derivedMethodType( nme.OUTER :: pnames, cls.owner.enclosingClass.typeRef :: ptypes, mt.resultType) @@ -261,14 +265,14 @@ object ExplicitOuter { if (fun.symbol.isConstructor) { val cls = fun.symbol.owner.asClass def outerArg(receiver: Tree): Tree = receiver match { - case New(tpt) => - singleton(outerPrefix(tpt.tpe)) + case New(_) | Super(_, _) => + singleton(outerPrefix(receiver.tpe)) case This(_) => - ref(outerParamAccessor(cls)) // will be rewried to outer argument of secondary constructor in phase Constructors + ref(outerParamAccessor(cls)) // will be rewired to outer argument of secondary constructor in phase Constructors case TypeApply(Select(r, nme.asInstanceOf_), args) => outerArg(r) // cast was inserted, skip } - if (hasOuter(cls)) + if (hasOuterParam(cls)) methPart(fun) match { case Select(receiver, _) => outerArg(receiver).withPos(fun.pos) :: Nil } diff --git a/src/dotty/tools/dotc/transform/Getters.scala b/src/dotty/tools/dotc/transform/Getters.scala new file mode 100644 index 000000000..4ea9d2c6b --- /dev/null +++ b/src/dotty/tools/dotc/transform/Getters.scala @@ -0,0 +1,69 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers.SymTransformer +import Contexts.Context +import SymDenotations.SymDenotation +import Types._ +import Symbols._ +import SymUtils._ +import Constants._ +import TreeTransforms._ +import Flags._ +import Decorators._ + +/** Performs the following rewritings for fields of a class: + * + * <mods> val x: T = e + * --> <mods> <stable> def x: T = e + * <mods> var x: T = e + * --> <mods> def x: T = e + * + * <mods> val x: T + * --> <mods> <stable> def x: T + * + * <mods> var x: T + * --> <mods> def x: T + * + * Omitted from the rewritings are + * + * - private[this] fields in non-trait classes + * - fields generated for static modules (TODO: needed?) + * - parameters, static fields, and fields coming from Java + * + * Furthermore, assignements to mutable vars are replaced by setter calls + * + * p.x = e + * --> p.x_=(e) + * + * No fields are generated yet. This is done later in phase Memoize. + */ +class Getters extends MiniPhaseTransform with SymTransformer { thisTransform => + import ast.tpd._ + + override def phaseName = "getters" + override def treeTransformPhase = thisTransform.next + + override def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = { + def noGetterNeeded = + d.is(NoGetterNeeded) || + d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) || + d.is(Module) && d.isStatic || + d.isSelfSym + if (d.isTerm && d.owner.isClass && d.info.isValueType && !noGetterNeeded) { + val maybeStable = if (d.isStable) Stable else EmptyFlags + d.copySymDenotation( + initFlags = d.flags | maybeStable | AccessorCreationFlags, + info = ExprType(d.info)) + } + else d + } + private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic + + override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = + if (tree.symbol is Method) DefDef(tree.symbol.asTerm, tree.rhs) else tree + + override def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree = + if (tree.lhs.symbol is Method) tree.lhs.becomes(tree.rhs) else tree +} diff --git a/src/dotty/tools/dotc/transform/GettersSetters.scala b/src/dotty/tools/dotc/transform/GettersSetters.scala index 772a63e52..b5933cc48 100644 --- a/src/dotty/tools/dotc/transform/GettersSetters.scala +++ b/src/dotty/tools/dotc/transform/GettersSetters.scala @@ -16,27 +16,39 @@ import NameOps._ import Flags._ import Decorators._ -/** Performs the following rewritings: +/** Performs the following rewritings on fields of classes, where `x_L` is the "local name" of `x`: * * val x: T = e * --> private val x_L: T = e - * <stable> def x: T = x_L (if in class) - * --> private notJavaPrivate var x_L: T = e * <stable> def x: T = x_L - * private notJavaPrivate def x_=(x: T): Unit = x_L = x (if in trait) + * * var x: T = e + * def x_=(y: T) = () * --> private var x_L: T = e * def x: T = x_L * def x_=(x: T): Unit = x_L = x (if in class or trait) + * * lazy val x: T = e - * --> lazy def x = e + * --> def x: T = e + * + * val x: T + * --> <stable> def x: T + * + * var x: T + * --> def x: T + * + * Omitted from the rewritings are + * + * - private[this] fields in non-trait classes + * - fields generated for static modules (TODO: needed?) + * - parameters, static fields, and fields coming from Java * * Furthermore, assignements to mutable vars are replaced by setter calls * * p.x = e * --> p.x_=(e) */ -class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransform => + class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransform => import ast.tpd._ override def phaseName = "gettersSetters" @@ -45,38 +57,34 @@ class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransf override def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = { def noGetterNeeded = - d.is(Method | Param | JavaDefined) || + d.is(NoGetterNeeded) || d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) || d.is(Module) && d.isStatic || d.isSelfSym - if (d.isTerm && (d.owner.isClass || d.is(Lazy)) && d.info.isValueType && !noGetterNeeded) { + if (d.isTerm && d.owner.isClass && d.info.isValueType && !noGetterNeeded) { val maybeStable = if (d.isStable) Stable else EmptyFlags - if (d.name.toString == "_") println(i"make accessor $d in ${d.owner} ${d.symbol.id}") + //if (d.name.toString == "_") println(i"make accessor $d in ${d.owner} ${d.symbol.id}") d.copySymDenotation( initFlags = d.flags | maybeStable | AccessorCreationFlags, info = ExprType(d.info)) } else d } + private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic + private val NoFieldNeeded = Lazy | Deferred | ParamAccessor override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = { if (tree.symbol is Method) { val getter = tree.symbol.asTerm assert(getter is Accessor) - if (getter.is(Lazy | Deferred | ParamAccessor)) DefDef(getter, tree.rhs) + if (getter is NoFieldNeeded) + DefDef(getter, tree.rhs) else { val inTrait = getter.owner.is(Trait) - val maybePrivate = - if (inTrait) Private | NotJavaPrivate - else if (getter.owner.isClass) Private - else EmptyFlags - val maybeMutable = - if (inTrait || getter.is(Mutable)) Mutable - else EmptyFlags val field = ctx.newSymbol( owner = ctx.owner, name = getter.name.fieldName, - flags = maybePrivate | maybeMutable, + flags = Private | (getter.flags & Mutable), info = getter.info.resultType).enteredAfter(thisTransform) assert(tree.rhs.tpe.exists, tree.show) val fieldDef = @@ -88,16 +96,7 @@ class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransf val rhs = ref(field) assert(rhs.hasType) val getterDef = DefDef(getter, rhs.ensureConforms(getter.info.widen)) - if (!getter.is(Mutable) && inTrait) { // add a setter anyway, will be needed for mixin - val setter = ctx.newSymbol( - owner = ctx.owner, - name = getter.name.traitSetterName, - flags = (getter.flags & AccessFlags) | Accessor | maybePrivate, - info = MethodType(field.info :: Nil, defn.UnitType)).enteredAfter(thisTransform) - val setterDef = DefDef(setter.asTerm, vrefss => Assign(ref(field), vrefss.head.head)) - Thicket(fieldDef, getterDef, setterDef) - } - else Thicket(fieldDef, getterDef) + Thicket(fieldDef, getterDef) } } else tree @@ -106,6 +105,7 @@ class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransf override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = if (tree.symbol.isSetter && !tree.symbol.is(Deferred | ParamAccessor)) { val Literal(Constant(())) = tree.rhs + assert(tree.symbol.field.exists, i"no field for ${tree.symbol.showLocated}") val initializer = Assign(ref(tree.symbol.field), ref(tree.vparamss.head.head.symbol)) assert(initializer.hasType) cpy.DefDef(tree)(rhs = initializer) diff --git a/src/dotty/tools/dotc/transform/MacroTransform.scala b/src/dotty/tools/dotc/transform/MacroTransform.scala index 47ffaafb3..3a8bcc920 100644 --- a/src/dotty/tools/dotc/transform/MacroTransform.scala +++ b/src/dotty/tools/dotc/transform/MacroTransform.scala @@ -44,11 +44,10 @@ abstract class MacroTransform extends Phase { def currentClass(implicit ctx: Context): ClassSymbol = ctx.owner.enclosingClass.asClass def transformStats(trees: List[Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = { - val exprCtx = ctx.withOwner(exprOwner) def transformStat(stat: Tree): Tree = stat match { case _: Import | _: DefTree => transform(stat) case Thicket(stats) => cpy.Thicket(stat)(stats mapConserve transformStat) - case _ => transform(stat)(exprCtx) + case _ => transform(stat)(ctx.exprContext(stat, exprOwner)) } flatten(trees.mapconserve(transformStat(_))) } diff --git a/src/dotty/tools/dotc/transform/Memoize.scala b/src/dotty/tools/dotc/transform/Memoize.scala new file mode 100644 index 000000000..ef70b9ecf --- /dev/null +++ b/src/dotty/tools/dotc/transform/Memoize.scala @@ -0,0 +1,86 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers._ +import Phases.Phase +import Contexts.Context +import SymDenotations.SymDenotation +import Types._ +import Symbols._ +import SymUtils._ +import Constants._ +import ast.Trees._ +import TreeTransforms._ +import NameOps._ +import Flags._ +import Decorators._ + +/** Provides the implementations of all getters and setters, introducing + * fields to hold the value accessed by them. + * TODO: Make LazyVals a part of this phase? + * + * <accessor> <stable> <mods> def x(): T = e + * --> private val x: T = e + * <accessor> <stable> <mods> def x(): T = x + * + * <accessor> <mods> def x(): T = e + * --> private var x: T = e + * <accessor> <mods> def x(): T = x + * + * <accessor> <mods> def x_=(y: T): Unit = () + * --> <accessor> <mods> def x_=(y: T): Unit = x = y + */ + class Memoize extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => + import ast.tpd._ + + override def phaseName = "memoize" + override def treeTransformPhase = thisTransform.next + + /** Should to run after mixin so that fields get generated in the + * class that contains the concrete getter rather than the trait + * that defines it. + */ + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Mixin]) + + override def prepareForDefDef(tree: DefDef)(implicit ctx: Context) = { + val sym = tree.symbol + if (sym.isGetter && !sym.is(NoFieldNeeded)) { + // allocate field early so that initializer has the right owner for subsequeny phases in + // the group. + val maybeMutable = if (sym is Stable) EmptyFlags else Mutable + val field = ctx.newSymbol( + owner = ctx.owner, + name = sym.name.asTermName.fieldName, + flags = Private | maybeMutable, + info = sym.info.resultType, + coord = tree.pos).enteredAfter(thisTransform) + tree.rhs.changeOwnerAfter(sym, field, thisTransform) + } + this + } + + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + val sym = tree.symbol + def field = { + val field = sym.field.asTerm + assert(field.exists, i"no field for ${sym.showLocated} in ${sym.owner.info.decls.toList.map{_.showDcl}}%; %") + field + } + if (sym.is(Accessor, butNot = NoFieldNeeded)) + if (sym.isGetter) { + val fieldDef = transformFollowing(ValDef(field, tree.rhs)) + val getterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(ref(field))) + Thicket(fieldDef, getterDef) + } + else if (sym.isSetter) { + val Literal(Constant(())) = tree.rhs + val initializer = Assign(ref(field), ref(tree.vparamss.head.head.symbol)) + cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)) + } + else tree // curiously, some accessors from Scala2 have ' ' suffixes. They count as + // neither getters nor setters + else tree + } + private val NoFieldNeeded = Lazy | Deferred | ParamAccessor +}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala new file mode 100644 index 000000000..1d342404a --- /dev/null +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -0,0 +1,176 @@ +package dotty.tools.dotc +package transform + +import core._ +import TreeTransforms._ +import Contexts.Context +import Flags._ +import SymUtils._ +import Symbols._ +import SymDenotations._ +import Types._ +import Decorators._ +import DenotTransformers._ +import StdNames._ +import NameOps._ +import Phases._ +import ast.Trees._ +import collection.mutable + +/** This phase performs the following transformations: + * + * 1. (done in `traitDefs`) Map every concrete trait getter + * + * <mods> def x(): T = expr + * + * to the pair of definitions: + * + * <mods> def x(): T + * protected def initial$x(): T = { stats; expr } + * + * where `stats` comprises all statements between either the start of the trait + * or the previous field definition which are not definitions (i.e. are executed for + * their side effects). + * + * 2. (done in `traitDefs`) Make every concrete trait setter + * + * <mods> def x_=(y: T) = () + * + * deferred by maping it to + * + * <mods> def x_=(y: T) + * + * 3. For a non-trait class C: + * + * For every trait M directly implemented by the class (see SymUtils.mixin), in + * reverse linearization order, add the following definitions to C: + * + * 3.1 (done in `traitInits`) For every concrete trait getter `<mods> def x(): T` in M, + * in order of textual occurrence: + * + * <mods> def x(): T = super[M].initial$x() + * + * 3.2 (done in `superCallOpt`) The call: + * + * super[M].<init> + * + * 3.3 (done in `setters`) For every concrete setter `<mods> def x_=(y: T)` in M: + * + * <mods> def x_=(y: T) = () + * + * Conceptually, this is the second half of the previous mixin phase. It needs to run + * after erasure because it copies references to possibly private inner classes and objects + * into enclosing classes where they are not visible. This can only be done if all references + * are symbolic. + */ +class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => + import ast.tpd._ + + override def phaseName: String = "mixin" + override def treeTransformPhase = thisTransform.next + + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Erasure]) + + override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = + if (sym.is(Accessor, butNot = Deferred) && sym.owner.is(Trait)) + sym.copySymDenotation(initFlags = sym.flags | Deferred) + else + sym + + private def initializer(sym: Symbol)(implicit ctx: Context): TermSymbol = { + val initName = InitializerName(sym.name.asTermName) + sym.owner.info.decl(initName).symbol + .orElse( + ctx.newSymbol( + sym.owner, + initName, + Protected | Synthetic | Method, + sym.info, + coord = sym.symbol.coord).enteredAfter(thisTransform)) + .asTerm + } + + override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) = { + val cls = impl.symbol.owner.asClass + val ops = new MixinOps(cls, thisTransform) + import ops._ + + def traitDefs(stats: List[Tree]): List[Tree] = { + val initBuf = new mutable.ListBuffer[Tree] + stats flatMap { + case stat: DefDef if stat.symbol.isGetter && !stat.rhs.isEmpty => + val vsym = stat.symbol + val isym = initializer(vsym) + val rhs = Block( + initBuf.toList.map(_.changeOwner(impl.symbol, isym)), + stat.rhs.changeOwner(vsym, isym)) + initBuf.clear() + List( + cpy.DefDef(stat)(mods = stat.mods | Deferred, rhs = EmptyTree), + DefDef(isym, rhs)) + case stat: DefDef if stat.symbol.isSetter => + List(cpy.DefDef(stat)( + mods = stat.mods | Deferred, + rhs = EmptyTree)) + case stat: DefTree => + List(stat) + case stat => + initBuf += stat + Nil + } + } + + def transformSuper(tree: Tree): Tree = { + val Apply(sel @ Select(New(_), nme.CONSTRUCTOR), args) = tree + superRef(tree.symbol, tree.pos).appliedToArgs(args) + } + + val superCalls = ( + for (p <- impl.parents if p.symbol.isConstructor) + yield p.symbol.owner -> transformSuper(p) + ).toMap + + def superCallOpt(baseCls: Symbol): List[Tree] = superCalls.get(baseCls) match { + case Some(call) => + if (defn.PhantomClasses.contains(baseCls)) Nil else call :: Nil + case None => + if (baseCls.is(Interface) || defn.PhantomClasses.contains(baseCls)) Nil + else { + //println(i"synth super call ${baseCls.primaryConstructor}: ${baseCls.primaryConstructor.info}") + superRef(baseCls.primaryConstructor).appliedToNone :: Nil +/* constr.tpe.widen match { + case tpe: PolyType => + val targs = cls.thisType.baseTypeWithArgs(baseCls).argTypes + constr = constr.appliedToTypes(targs) + case _ => + } + constr.ensureApplied :: Nil +*/ + } + } + + def wasDeferred(sym: Symbol) = + ctx.atPhase(thisTransform) { implicit ctx => sym is Deferred } + + def traitInits(mixin: ClassSymbol): List[Tree] = + for (getter <- mixin.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) + yield { + DefDef(implementation(getter.asTerm), superRef(initializer(getter)).appliedToNone) + } + + def setters(mixin: ClassSymbol): List[Tree] = + for (setter <- mixin.decls.filter(setr => setr.isSetter && !wasDeferred(setr)).toList) + yield DefDef(implementation(setter.asTerm), unitLiteral.withPos(cls.pos)) + + cpy.Template(impl)( + parents = impl.parents.map(p => TypeTree(p.tpe).withPos(p.pos)), + body = + if (cls is Trait) traitDefs(impl.body) + else { + val mixInits = mixins.flatMap { mixin => + traitInits(mixin) ::: superCallOpt(mixin) ::: setters(mixin) + } + superCallOpt(superCls) ::: mixInits ::: impl.body + }) + } +} diff --git a/src/dotty/tools/dotc/transform/MixinOps.scala b/src/dotty/tools/dotc/transform/MixinOps.scala new file mode 100644 index 000000000..de15b045f --- /dev/null +++ b/src/dotty/tools/dotc/transform/MixinOps.scala @@ -0,0 +1,37 @@ +package dotty.tools.dotc +package transform + +import core._ +import Symbols._, Types._, Contexts._, SymDenotations._, DenotTransformers._, Flags._ +import util.Positions._ +import StdNames._, NameOps._ + +class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx: Context) { + import ast.tpd._ + + val superCls: Symbol = cls.classInfo.parents.head.symbol + val mixins: List[ClassSymbol] = + if (cls is Trait) Nil + else cls.baseClasses.tail.takeWhile(_ ne superCls).reverse + + def implementation(member: TermSymbol): TermSymbol = + member.copy( + owner = cls, + name = member.name.stripScala2LocalSuffix, + flags = member.flags &~ Deferred &~ Module, + info = cls.thisType.memberInfo(member)).enteredAfter(thisTransform).asTerm + + def superRef(target: Symbol, pos: Position = cls.pos): Tree = { + val sup = if (target.isConstructor && !target.owner.is(Trait)) + Super(This(cls), tpnme.EMPTY, true) + else + Super(This(cls), target.owner.name.asTypeName, false, target.owner) + //println(i"super ref $target on $sup") + ast.untpd.Select(sup.withPos(pos), target.name) + .withType(NamedType.withFixedSym(sup.tpe, target)) + //sup.select(target) + } + + def forwarder(target: Symbol) = (targs: List[Type]) => (vrefss: List[List[Tree]]) => + superRef(target).appliedToTypes(targs).appliedToArgss(vrefss) +} diff --git a/src/dotty/tools/dotc/transform/ResolveSuper.scala b/src/dotty/tools/dotc/transform/ResolveSuper.scala new file mode 100644 index 000000000..23ff45a7c --- /dev/null +++ b/src/dotty/tools/dotc/transform/ResolveSuper.scala @@ -0,0 +1,98 @@ +package dotty.tools.dotc +package transform + +import core._ +import TreeTransforms._ +import Contexts.Context +import Flags._ +import SymUtils._ +import Symbols._ +import SymDenotations._ +import Types._ +import Decorators._ +import DenotTransformers._ +import StdNames._ +import NameOps._ +import ast.Trees._ +import util.Positions._ +import Names._ +import collection.mutable + +/** This phase adds super accessors and method overrides where + * linearization differs from Java's rule for default methods in interfaces. + * In particular: + * + * For every trait M directly implemented by the class (see SymUtils.mixin), in + * reverse linearization order, add the following definitions to C: + * + * 3.1 (done in `superAccessors`) For every superAccessor + * `<mods> def super$f[Ts](ps1)...(psN): U` in M: + * + * <mods> def super$f[Ts](ps1)...(psN): U = super[S].f[Ts](ps1)...(psN) + * + * where `S` is the superclass of `M` in the linearization of `C`. + * + * 3.2 (done in `methodOverrides`) For every method + * `<mods> def f[Ts](ps1)...(psN): U` in M` that needs to be disambiguated: + * + * <mods> def f[Ts](ps1)...(psN): U = super[M].f[Ts](ps1)...(psN) + * + * A method in M needs to be disambiguated if it is concrete, not overridden in C, + * and if it overrides another concrete method. + * + * This is the first part of what was the mixin phase. It is complemented by + * Mixin, which runs after erasure. + */ +class ResolveSuper extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => + import ast.tpd._ + + override def phaseName: String = "resolveSuper" + + override def treeTransformPhase = thisTransform.next + + override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) = { + val cls = impl.symbol.owner.asClass + val ops = new MixinOps(cls, thisTransform) + import ops._ + + /** Returns the symbol that is accessed by a super-accessor in a mixin composition. + * + * @param base The class in which everything is mixed together + * @param member The symbol statically referred to by the superaccessor in the trait + */ + def rebindSuper(base: Symbol, acc: Symbol): Symbol = { + var bcs = cls.info.baseClasses.dropWhile(acc.owner != _).tail + var sym: Symbol = NoSymbol + val SuperAccessorName(memberName) = acc.name: Name // dotty deviation: ": Name" needed otherwise pattern type is neither a subtype nor a supertype of selector type + ctx.debuglog(i"starting rebindsuper from $cls of ${acc.showLocated}: ${acc.info} in $bcs, name = $memberName") + while (bcs.nonEmpty && sym == NoSymbol) { + val other = bcs.head.info.nonPrivateDecl(memberName) + if (ctx.settings.debug.value) + ctx.log(i"rebindsuper ${bcs.head} $other deferred = ${other.symbol.is(Deferred)}") + sym = other.matchingDenotation(cls.thisType, cls.thisType.memberInfo(acc)).symbol + bcs = bcs.tail + } + assert(sym.exists) + sym + } + + def superAccessors(mixin: ClassSymbol): List[Tree] = + for (superAcc <- mixin.decls.filter(_ is SuperAccessor).toList) + yield polyDefDef(implementation(superAcc.asTerm), forwarder(rebindSuper(cls, superAcc))) + + def methodOverrides(mixin: ClassSymbol): List[Tree] = { + def isOverridden(meth: Symbol) = meth.overridingSymbol(cls).is(Method, butNot = Deferred) + def needsDisambiguation(meth: Symbol): Boolean = + meth.is(Method, butNot = PrivateOrDeferred) && + !isOverridden(meth) && + !meth.allOverriddenSymbols.forall(_ is Deferred) + for (meth <- mixin.decls.toList if needsDisambiguation(meth)) + yield polyDefDef(implementation(meth.asTerm), forwarder(meth)) + } + + val overrides = mixins.flatMap(mixin => superAccessors(mixin) ::: methodOverrides(mixin)) + + cpy.Template(impl)(body = overrides ::: impl.body) + } + private val PrivateOrDeferred = Private | Deferred +} diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index b11658efb..537c8c0c6 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -90,7 +90,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this val superAcc = clazz.info.decl(supername).suchThat(_.signature == sym.signature).symbol orElse { ctx.debuglog(s"add super acc ${sym.showLocated} to $clazz") val acc = ctx.newSymbol( - clazz, supername, SuperAccessor | Private | Artifact, + clazz, supername, SuperAccessor | Private | Artifact | Method, ensureMethodic(sel.tpe.widenSingleton), coord = sym.coord).enteredAfter(thisTransformer) // Diagnostic for SI-7091 if (!accDefs.contains(clazz)) diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala index ba45d3f04..449affb9e 100644 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/src/dotty/tools/dotc/transform/SymUtils.scala @@ -61,12 +61,17 @@ class SymUtils(val self: Symbol) extends AnyVal { def setter(implicit ctx: Context): Symbol = if (self.isSetter) self - else accessorNamed(self.asTerm.name.setterName) orElse - accessorNamed(self.asTerm.name.traitSetterName) + else accessorNamed(self.asTerm.name.setterName) 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('$') + + def initializer(implicit ctx: Context): TermSymbol = + self.owner.info.decl(InitializerName(self.asTerm.name)).symbol.asTerm + + def isField(implicit ctx: Context): Boolean = + self.isTerm && !self.is(Method) } diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index 588a13fc9..850563a48 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -905,7 +905,7 @@ object TreeTransforms { case tree: UnApply => goUnApply(tree, info.nx.nxTransUnApply(cur)) case tree: Template => goTemplate(tree, info.nx.nxTransTemplate(cur)) case tree: PackageDef => goPackageDef(tree, info.nx.nxTransPackageDef(cur)) - case Thicket(trees) => cpy.Thicket(tree)(transformTrees(trees, info, cur)) + case Thicket(trees) => tree case tree => goOther(tree, info.nx.nxTransOther(cur)) } @@ -1164,7 +1164,8 @@ object TreeTransforms { val stats = transformStats(tree.stats, tree.symbol, mutatedInfo, cur)(nestedCtx) goPackageDef(cpy.PackageDef(tree)(pid, stats), mutatedInfo.nx.nxTransPackageDef(cur)) } - case Thicket(trees) => cpy.Thicket(tree)(transformTrees(trees, info, cur)) + case Thicket(trees) => + cpy.Thicket(tree)(transformTrees(trees, info, cur)) case tree => implicit val originalInfo: TransformerInfo = info goOther(tree, info.nx.nxTransOther(cur)) @@ -1200,11 +1201,10 @@ object TreeTransforms { def transformStats(trees: List[Tree], exprOwner: Symbol, info: TransformerInfo, current: Int)(implicit ctx: Context): List[Tree] = { val newInfo = mutateTransformers(info, prepForStats, info.nx.nxPrepStats, trees, current) - val exprCtx = ctx.withOwner(exprOwner) def transformStat(stat: Tree): Tree = stat match { case _: Import | _: DefTree => transform(stat, newInfo, current) case Thicket(stats) => cpy.Thicket(stat)(stats mapConserve transformStat) - case _ => transform(stat, newInfo, current)(exprCtx) + case _ => transform(stat, newInfo, current)(ctx.exprContext(stat, exprOwner)) } val newTrees = flatten(trees.mapconserve(transformStat)) goStats(newTrees, newInfo.nx.nxTransStats(current))(ctx, newInfo) diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala index e96e04b1a..8e8cf58f9 100644 --- a/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -35,7 +35,7 @@ object ErrorReporting { // See test pending/pos/boundspropagation.scala val treeSym = ctx.symOfContextTree(tree) if (treeSym.exists && treeSym.name == cycleSym.name && treeSym.owner == cycleSym.owner) { - val result = if (cycleSym.isSourceMethod) " result" else "" + val result = if (cycleSym is Method) " result" else "" d"overloaded or recursive $cycleSym needs$result type" } else errorMsg(msg, cx.outer) diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index bb488bdc5..765c6bea7 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -236,9 +236,10 @@ trait TypeAssigner { tree.withType(cls.thisType) } - def assignType(tree: untpd.Super, qual: Tree, inConstrCall: Boolean)(implicit ctx: Context) = { + def assignType(tree: untpd.Super, qual: Tree, inConstrCall: Boolean, mixinClass: Symbol = NoSymbol)(implicit ctx: Context) = { val mix = tree.mix - val cls = qual.tpe.widen.typeSymbol + val qtype @ ThisType(_) = qual.tpe + val cls = qtype.cls def findMixinSuper(site: Type): Type = site.parents filter (_.name == mix) match { case p :: Nil => @@ -249,7 +250,8 @@ trait TypeAssigner { errorType("ambiguous parent class qualifier", tree.pos) } val owntype = - if (!mix.isEmpty) findMixinSuper(cls.info) + if (mixinClass.exists) mixinClass.typeRef + else if (!mix.isEmpty) findMixinSuper(cls.info) else if (inConstrCall || ctx.erasedTypes) cls.info.firstParent else { val ps = cls.info.parents diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 3c36a1f25..a5396d445 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1042,8 +1042,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case Thicket(stats) :: rest => traverse(stats ++ rest) case stat :: rest => - val nestedCtx = if (exprOwner == ctx.owner) ctx else ctx.fresh.setOwner(exprOwner) - buf += typed(stat)(nestedCtx) + buf += typed(stat)(ctx.exprContext(stat, exprOwner)) traverse(rest) case nil => buf.toList @@ -1246,6 +1245,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit typed(etaExpand(tree, wtp, arity), pt) else if (wtp.paramTypes.isEmpty) adaptInterpolated(tpd.Apply(tree, Nil), pt, EmptyTree) + else if (wtp.isImplicit) + err.typeMismatch(tree, pt) else errorTree(tree, d"""missing arguments for $methodStr |