diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 41e77c61f95a..2a3bdc1caf47 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -56,6 +56,7 @@ class Compiler { new Getters, new ClassTags, new ElimByName, + new AugmentScala2Traits, new ResolveSuper), List(new Erasure), List(new ElimErasedValueType, @@ -63,6 +64,7 @@ class Compiler { new Mixin, new LazyVals, new Memoize, + new LinkScala2ImplClasses, new CapturedVars, // capturedVars has a transformUnit: no phases should introduce local mutable vars here new Constructors, new FunctionalInterfaces), @@ -74,7 +76,6 @@ class Compiler { new ExpandPrivate, new CollectEntryPoints, new LabelDefs, - new ElimWildcardIdents, new TraitConstructors), List(new GenBCode) ) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index a38a238c80e8..e38de458a556 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -418,6 +418,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Thicket(valdef, clsdef) } + /** A `_' with given type */ + def Underscore(tp: Type)(implicit ctx: Context) = untpd.Ident(nme.WILDCARD).withType(tp) + def defaultValue(tpe: Types.Type)(implicit ctx: Context) = { val tpw = tpe.widen @@ -720,6 +723,12 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def ensureConforms(tp: Type)(implicit ctx: Context): Tree = if (tree.tpe <:< tp) tree else asInstance(tp) + /** If inititializer tree is `_', the default value of its type, + * otherwise the tree itself. + */ + def wildcardToDefault(implicit ctx: Context) = + if (isWildcardArg(tree)) defaultValue(tree.tpe) else tree + /** `this && that`, for boolean trees `this`, `that` */ def and(that: Tree)(implicit ctx: Context): Tree = tree.select(defn.Boolean_&&).appliedTo(that) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 6ac15dbca925..4d475fe2baf7 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -227,16 +227,36 @@ object Denotations { else asSingleDenotation - /** Form a denotation by conjoining with denotation `that` */ + /** Form a denotation by conjoining with denotation `that`. + * + * NoDenotations are dropped. MultiDenotations are handled by merging + * parts with same signatures. SingleDenotations with equal signatures + * are joined as follows: + * + * In a first step, consider only those denotations which have symbols + * that are accessible from prefix `pre`. + * + * If there are several such denotations, try to pick one by applying the following + * three precedence rules in decreasing order of priority: + * + * 1. Prefer denotations with more specific infos. + * 2. If infos are equally specific, prefer denotations with concrete symbols over denotations + * with abstract symbols. + * 3. If infos are equally specific and symbols are equally concrete, + * prefer denotations with symbols defined in subclasses + * over denotations with symbols defined in proper superclasses. + * + * If there is exactly one (preferred) accessible denotation, return it. + * + * If there is no preferred accessible denotation, return a JointRefDenotation + * with one of the operand symbols (unspecified which one), and an info which + * is intersection (&) of the infos of the operand denotations. + * + * If SingleDenotations with different signatures are joined, return NoDenotation. + */ def & (that: Denotation, pre: Type)(implicit ctx: Context): Denotation = { - /** Try to merge denot1 and denot2 without adding a new signature. - * Prefer denotations with more specific types, provided the symbol stays accessible - * Prefer denotations with accessible symbols over denotations with - * existing, but inaccessible symbols. - * If there's no preference, produce a JointRefDenotation with the intersection of both infos. - * If unsuccessful, return NoDenotation. - */ + /** Try to merge denot1 and denot2 without adding a new signature. */ def mergeDenot(denot1: Denotation, denot2: SingleDenotation): Denotation = denot1 match { case denot1 @ MultiDenotation(denot11, denot12) => val d1 = mergeDenot(denot11, denot2) @@ -254,8 +274,24 @@ object Denotations { val sym1 = denot1.symbol val sym2 = denot2.symbol val sym2Accessible = sym2.isAccessibleFrom(pre) + def shadows(sym1: Symbol, sym2: Symbol) = { + val owner1 = sym1.owner + val owner2 = sym2.owner + owner1.derivesFrom(owner2) && owner1.ne(owner2) + } + /** Preference according to order (overrides, isAsConcrete, shadows)*/ def prefer(info1: Type, sym1: Symbol, info2: Type, sym2: Symbol) = - info1.overrides(info2) && (sym1.isAsConcrete(sym2) || !info2.overrides(info1)) + info1.overrides(info2) && ( + // non-standard ordering of tests for efficiency - + // overrides is costlier to compute than the others, so its 2nd test comes last. + sym1.isAsConcrete(sym2) && ( + !sym2.isAsConcrete(sym1) + || + shadows(sym1, sym2) + ) + || + !info2.overrides(info1) + ) if (sym2Accessible && prefer(info2, sym2, info1, sym1)) denot2 else { val sym1Accessible = sym1.isAccessibleFrom(pre) diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index cfa0faef9bee..3efadcb00406 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -427,7 +427,7 @@ object Flags { /** Flags representing modifiers that can appear in trees */ final val ModifierFlags = - SourceModifierFlags | Module | Param | Synthetic | Package | Local + SourceModifierFlags | Module | Param | Synthetic | Package | Local | commonFlags(Mutable) // | Trait is subsumed by commonFlags(Lazy) from SourceModifierFlags assert(ModifierFlags.isTermFlags && ModifierFlags.isTypeFlags) @@ -520,12 +520,18 @@ object Flags { /** A private method */ final val PrivateMethod = allOf(Private, Method) + /** A private accessor */ + final val PrivateAccessor = allOf(Private, Accessor) + /** A type parameter with synthesized name */ final val ExpandedTypeParam = allOf(ExpandedName, TypeParam) /** A parameter or parameter accessor */ final val ParamOrAccessor = Param | ParamAccessor + /** A lazy or deferred value */ + final val LazyOrDeferred = Lazy | Deferred + /** A type parameter or type parameter accessor */ final val TypeParamOrAccessor = TypeParam | TypeParamAccessor diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index bf5e219cfd7c..dc94f6db12da 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -155,13 +155,17 @@ object NameOps { /** The expanded name of `name` relative to given class `base`. */ - def expandedName(base: Symbol)(implicit ctx: Context): N = - expandedName(if (base is Flags.ExpandedName) base.name else base.fullNameSeparated("$")) + def expandedName(base: Symbol, separator: Name)(implicit ctx: Context): N = + expandedName(if (base is Flags.ExpandedName) base.name else base.fullNameSeparated("$"), separator) + + def expandedName(base: Symbol)(implicit ctx: Context): N = expandedName(base, nme.EXPAND_SEPARATOR) /** The expanded name of `name` relative to `basename` with given `separator` */ - def expandedName(prefix: Name): N = - name.fromName(prefix ++ nme.EXPAND_SEPARATOR ++ name).asInstanceOf[N] + def expandedName(prefix: Name, separator: Name = nme.EXPAND_SEPARATOR): N = + name.fromName(prefix ++ separator ++ name).asInstanceOf[N] + + def expandedName(prefix: Name): N = expandedName(prefix, nme.EXPAND_SEPARATOR) def unexpandedName: N = { val idx = name.lastIndexOfSlice(nme.EXPAND_SEPARATOR) @@ -178,6 +182,8 @@ object NameOps { def revertShadowed: N = likeTyped(name.drop(nme.SHADOWED.length)) + def implClassName: N = likeTyped(name ++ tpnme.IMPL_CLASS_SUFFIX) + /** Translate a name into a list of simple TypeNames and TermNames. * In all segments before the last, type/term is determined by whether * the following separator char is '.' or '#'. The last segment diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 6273612c779a..eaf4ce1e2080 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -126,6 +126,7 @@ object StdNames { val INITIALIZER_PREFIX: N = "initial$" val COMPANION_MODULE_METHOD: N = "companion$module" val COMPANION_CLASS_METHOD: N = "companion$class" + val TRAIT_SETTER_SEPARATOR: N = "$_setter_$" // 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/TypeErasure.scala b/src/dotty/tools/dotc/core/TypeErasure.scala index fac795ef837d..92e32d4b1c12 100644 --- a/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/src/dotty/tools/dotc/core/TypeErasure.scala @@ -164,7 +164,7 @@ object TypeErasure { else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx)) else erase.eraseInfo(tp, sym)(erasureCtx) match { case einfo: MethodType if sym.isGetter && einfo.resultType.isRef(defn.UnitClass) => - MethodType(Nil, Nil, defn.BoxedUnitClass.typeRef) + MethodType(Nil, defn.BoxedUnitClass.typeRef) case einfo => einfo } diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index cc8f0bef8193..fa238f32cadd 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -172,7 +172,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { * parent { type Apply = body; argBindings? } * * split it into - + * * - the `parent` * - the simplified `body` * - the bindings HK$ members, if there are any diff --git a/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala b/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala new file mode 100644 index 000000000000..c3e205f83306 --- /dev/null +++ b/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala @@ -0,0 +1,97 @@ +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._ + +/** This phase augments Scala2 traits with implementation classes and with additional members + * needed for mixin composition. + * These symbols would have been added between Unpickling and Mixin in the Scala2 pipeline. + * Specifcally, it adds + * + * - an implementation class which defines a trait constructor and trait method implementations + * - trait setters for vals defined in traits + * + * Furthermore, it expands the names of all private getters and setters in the trait and makes + * them not-private. + */ +class AugmentScala2Traits extends MiniPhaseTransform with IdentityDenotTransformer with FullParameterization { thisTransform => + import ast.tpd._ + + override def phaseName: String = "augmentScala2Traits" + + override def rewiredTarget(referenced: Symbol, derived: Symbol)(implicit ctx: Context) = NoSymbol + + override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) = { + val cls = impl.symbol.owner.asClass + for (mixin <- cls.mixins) + if (mixin.is(Scala2x)) + augmentScala2Trait(mixin, cls) + impl + } + + private def augmentScala2Trait(mixin: ClassSymbol, cls: ClassSymbol)(implicit ctx: Context): Unit = { + if (mixin.implClass.is(Scala2x)) () // nothing to do, mixin was already augmented + else { + //println(i"creating new implclass for $mixin ${mixin.implClass}") + val ops = new MixinOps(cls, thisTransform) + import ops._ + + val implClass = ctx.newCompleteClassSymbol( + owner = mixin.owner, + name = mixin.name.implClassName, + flags = Abstract | Scala2x, + parents = defn.ObjectClass.typeRef :: Nil, + assocFile = mixin.assocFile).enteredAfter(thisTransform) + + def implMethod(meth: TermSymbol): Symbol = { + val mold = + if (meth.isConstructor) + meth.copySymDenotation( + name = nme.IMPLCLASS_CONSTRUCTOR, + info = MethodType(Nil, defn.UnitType)) + else meth.ensureNotPrivate + meth.copy( + owner = implClass, + name = mold.name.asTermName, + flags = Method | JavaStatic | mold.flags & ExpandedName, + info = fullyParameterizedType(mold.info, mixin)) + } + + def traitSetter(getter: TermSymbol) = { + val separator = if (getter.is(Private)) nme.EXPAND_SEPARATOR else nme.TRAIT_SETTER_SEPARATOR + val expandedGetterName = + if (getter.is(ExpandedName)) getter.name + else getter.name.expandedName(getter.owner, separator) + getter.copy( + name = expandedGetterName.setterName, + flags = Method | Accessor | ExpandedName, + info = MethodType(getter.info.resultType :: Nil, defn.UnitType)) + } + + for (sym <- mixin.info.decls) { + if (needsForwarder(sym) || sym.isConstructor || sym.isGetter && sym.is(Lazy)) + implClass.enter(implMethod(sym.asTerm)) + if (sym.isGetter && !sym.is(LazyOrDeferred) && + !sym.setter.exists && !sym.info.resultType.isInstanceOf[ConstantType]) + traitSetter(sym.asTerm).enteredAfter(thisTransform) + if (sym.is(PrivateAccessor, butNot = ExpandedName) && + (sym.isGetter || sym.isSetter)) // strangely, Scala 2 fields are also methods that have Accessor set. + sym.ensureNotPrivate.installAfter(thisTransform) + } + ctx.log(i"Scala2x trait decls of $mixin = ${mixin.info.decls.toList.map(_.showDcl)}%\n %") + ctx.log(i"Scala2x impl decls of $mixin = ${implClass.info.decls.toList.map(_.showDcl)}%\n %") + } + } +} diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index fa60ad277c5c..100e9ff211c8 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -30,15 +30,19 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor import tpd._ override def phaseName: String = "constructors" - override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Erasure]) + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Memoize]) - /** All initializers should be moved into constructor - */ + /** All initializers for non-lazy fields should be moved into constructor. + * All non-abstract methods should be implemented (this is assured for constructors + * in this phase and for other methods in memoize). + */ override def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = { tree match { - case t: ValDef if ((t.rhs ne EmptyTree) && !(t.symbol is Flags.Lazy) && t.symbol.owner.isClass) => - assert(false, i"$t initializers should be moved to constructors") + case tree: ValDef if tree.symbol.exists && tree.symbol.owner.isClass && !tree.symbol.is(Lazy) => + assert(tree.rhs.isEmpty, i"$tree: initializer should be moved to constructors") + case tree: DefDef if !tree.symbol.is(LazyOrDeferred) => + assert(!tree.rhs.isEmpty, i"unimplemented: $tree") case _ => } } diff --git a/src/dotty/tools/dotc/transform/ElimWildcardIdents.scala b/src/dotty/tools/dotc/transform/ElimWildcardIdents.scala deleted file mode 100644 index 29194d2353c0..000000000000 --- a/src/dotty/tools/dotc/transform/ElimWildcardIdents.scala +++ /dev/null @@ -1,37 +0,0 @@ -package dotty.tools.dotc -package transform - -import TreeTransforms.{MiniPhaseTransform, TransformerInfo} -import ast.tpd -import ast.Trees._ -import core._ -import Contexts.Context -import Symbols._ -import Types._ -import StdNames._ - -/** - * Replace Ident("_") in tree with default values of corresponding type: - * numerics: `0` - * booleans: `false` - * classes: `null` - */ -class ElimWildcardIdents extends MiniPhaseTransform { - import ast.tpd._ - def phaseName: String = "elimWildcardIdents" - - def wildcardToDefaultValue(tree: Tree)(implicit ctx: Context) = { - def recur(x: Tree): Tree = x match { - case x: Ident if x.name == nme.WILDCARD && x.symbol.isClass => defaultValue(tree.tpe) - case Block(Nil, y) => recur(y) - case _ => tree - } - recur(tree) - } - - override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = - cpy.ValDef(tree)(rhs = wildcardToDefaultValue(tree.rhs)) - - override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = - cpy.DefDef(tree)(rhs = wildcardToDefaultValue(tree.rhs)) -} diff --git a/src/dotty/tools/dotc/transform/ExpandSAMs.scala b/src/dotty/tools/dotc/transform/ExpandSAMs.scala index bba42f403de3..2416e46246fb 100644 --- a/src/dotty/tools/dotc/transform/ExpandSAMs.scala +++ b/src/dotty/tools/dotc/transform/ExpandSAMs.scala @@ -74,7 +74,7 @@ class ExpandSAMs extends MiniPhaseTransform { thisTransformer => val defaultSym = ctx.newSymbol(isDefinedAtFn, nme.WILDCARD, Synthetic, selector.tpe.widen) val defaultCase = CaseDef( - Bind(defaultSym, untpd.Ident(nme.WILDCARD).withType(selector.tpe.widen)), + Bind(defaultSym, Underscore(selector.tpe.widen)), EmptyTree, Literal(Constant(false))) cpy.Match(applyRhs)(paramRef, cases.map(translateCase) :+ defaultCase) diff --git a/src/dotty/tools/dotc/transform/FullParameterization.scala b/src/dotty/tools/dotc/transform/FullParameterization.scala index 87d49206217d..e9057e885c47 100644 --- a/src/dotty/tools/dotc/transform/FullParameterization.scala +++ b/src/dotty/tools/dotc/transform/FullParameterization.scala @@ -86,6 +86,7 @@ trait FullParameterization { * } * * If a self type is present, $this has this self type as its type. + * @param abstractOverClass if true, include the type parameters of the class in the method's list of type parameters. */ def fullyParameterizedType(info: Type, clazz: ClassSymbol, abstractOverClass: Boolean = true)(implicit ctx: Context): Type = { val (mtparamCount, origResult) = info match { @@ -225,12 +226,18 @@ trait FullParameterization { } object FullParameterization { + /** Assuming `info` is a result of a `fullyParameterizedType` call, the signature of the * original method type `X` such that `info = fullyParameterizedType(X, ...)`. */ def memberSignature(info: Type)(implicit ctx: Context): Signature = info match { - case info: PolyType => memberSignature(info.resultType) - case info @ MethodType(nme.SELF :: Nil, _) => info.resultType.ensureMethodic.signature - case _ => Signature.NotAMethod + case info: PolyType => + memberSignature(info.resultType) + case info @ MethodType(nme.SELF :: Nil, _) => + info.resultType.ensureMethodic.signature + case info @ MethodType(nme.SELF :: otherNames, thisType :: otherTypes) => + info.derivedMethodType(otherNames, otherTypes, info.resultType).signature + case _ => + Signature.NotAMethod } } diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index 5b146a7856d5..e52e2537c8dd 100644 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -146,8 +146,8 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { def mkNonThreadSafeDef(target: Tree, flag: Tree, rhs: Tree)(implicit ctx: Context) = { val setFlag = flag.becomes(Literal(Constants.Constant(true))) - val setTarget = target.becomes(rhs) - val init = Block(List(setFlag, setTarget), target.ensureApplied) + val setTargets = if (isWildcardArg(rhs)) Nil else target.becomes(rhs) :: Nil + val init = Block(setFlag :: setTargets, target.ensureApplied) If(flag.ensureApplied, target.ensureApplied, init) } @@ -175,7 +175,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { val containerSymbol = ctx.newSymbol(claz, containerName, x.symbol.flags &~ containerFlagsMask | containerFlags | Flags.Private, tpe, coord = x.symbol.coord - ).entered + ).enteredAfter(this) val containerTree = ValDef(containerSymbol, defaultValue(tpe)) if (x.tpe.isNotNull && tpe <:< defn.ObjectType) { // can use 'null' value instead of flag @@ -184,7 +184,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { } else { val flagName = ctx.freshName(x.name ++ StdNames.nme.BITMAP_PREFIX).toTermName - val flagSymbol = ctx.newSymbol(x.symbol.owner, flagName, containerFlags | Flags.Private, defn.BooleanType).entered + val flagSymbol = ctx.newSymbol(x.symbol.owner, flagName, containerFlags | Flags.Private, defn.BooleanType).enteredAfter(this) val flag = ValDef(flagSymbol, Literal(Constants.Constant(false))) val slowPath = DefDef(x.symbol.asTerm, mkNonThreadSafeDef(ref(containerSymbol), ref(flagSymbol), x.rhs)) Thicket(List(containerTree, flag, slowPath)) @@ -278,7 +278,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { CaseDef(computedState, EmptyTree, body) } - val default = CaseDef(untpd.Ident(nme.WILDCARD).withType(defn.LongType), EmptyTree, Literal(Constant(()))) + val default = CaseDef(Underscore(defn.LongType), EmptyTree, Literal(Constant(()))) val cases = Match(stateMask.appliedTo(ref(flagSymbol), Literal(Constant(ord))), List(compute, waitFirst, waitSecond, computed, default)) //todo: annotate with @switch @@ -331,7 +331,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { } val containerName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL).toTermName - val containerSymbol = ctx.newSymbol(claz, containerName, (x.mods &~ containerFlagsMask | containerFlags).flags, tpe, coord = x.symbol.coord).entered + val containerSymbol = ctx.newSymbol(claz, containerName, (x.mods &~ containerFlagsMask | containerFlags).flags, tpe, coord = x.symbol.coord).enteredAfter(this) val containerTree = ValDef(containerSymbol, defaultValue(tpe)) val offset = ref(companion).ensureApplied.select(offsetSymbol) diff --git a/src/dotty/tools/dotc/transform/LinkScala2ImplClasses.scala b/src/dotty/tools/dotc/transform/LinkScala2ImplClasses.scala new file mode 100644 index 000000000000..8c247130a50f --- /dev/null +++ b/src/dotty/tools/dotc/transform/LinkScala2ImplClasses.scala @@ -0,0 +1,58 @@ +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.untpd +import ast.Trees._ +import collection.mutable + +/** Rewrite calls + * + * super[M].f(args) + * + * where M is a Scala2 trait implemented by the current class to + * + * M$class.f(this, args) + * + * provided the implementation class M$class defines a corresponding function `f`. + */ +class LinkScala2ImplClasses extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => + import ast.tpd._ + + override def phaseName: String = "linkScala2ImplClasses" + + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Mixin]) + + override def transformApply(app: Apply)(implicit ctx: Context, info: TransformerInfo) = { + def currentClass = ctx.owner.enclosingClass.asClass + app match { + case Apply(sel @ Select(Super(_, _), _), args) + if sel.symbol.owner.is(Scala2xTrait) && currentClass.mixins.contains(sel.symbol.owner) => + val impl = implMethod(sel.symbol) + if (impl.exists) Apply(ref(impl), This(currentClass) :: args).withPos(app.pos) + else app // could have been an abstract method in a trait linked to from a super constructor + case _ => + app + } + } + + private def implMethod(meth: Symbol)(implicit ctx: Context): Symbol = + meth.owner.implClass.info + .decl(if (meth.isConstructor) nme.IMPLCLASS_CONSTRUCTOR else meth.name) + .suchThat(c => FullParameterization.memberSignature(c.info) == meth.signature) + .symbol + + private val Scala2xTrait = allOf(Scala2x, Trait) +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/Memoize.scala b/src/dotty/tools/dotc/transform/Memoize.scala index cbde1ef8ae33..6b19b6d13722 100644 --- a/src/dotty/tools/dotc/transform/Memoize.scala +++ b/src/dotty/tools/dotc/transform/Memoize.scala @@ -42,12 +42,6 @@ import Decorators._ */ override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Mixin]) - override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = tree match { - case tree: DefDef if !tree.symbol.is(Lazy | Deferred) => - assert(!tree.rhs.isEmpty, i"unimplemented: $tree") - case _ => - } - override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { val sym = tree.symbol @@ -61,8 +55,9 @@ import Decorators._ lazy val field = sym.field.orElse(newField).asTerm if (sym.is(Accessor, butNot = NoFieldNeeded)) if (sym.isGetter) { - tree.rhs.changeOwnerAfter(sym, field, thisTransform) - val fieldDef = transformFollowing(ValDef(field, tree.rhs)) + var rhs = tree.rhs.changeOwnerAfter(sym, field, thisTransform) + if (isWildcardArg(rhs)) rhs = EmptyTree + val fieldDef = transformFollowing(ValDef(field, rhs)) val getterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(ref(field))) Thicket(fieldDef, getterDef) } diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index d2e7943f82b4..63e680414e94 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -14,6 +14,7 @@ import DenotTransformers._ import StdNames._ import NameOps._ import Phases._ +import ast.untpd import ast.Trees._ import collection.mutable @@ -46,10 +47,23 @@ import collection.mutable * reverse linearization order, add the following definitions to C: * * 3.1 (done in `traitInits`) For every concrete trait getter ` def x(): T` in M, - * in order of textual occurrence: + * in order of textual occurrence, produce the following: + * + * 3.1.1 If `x` is also a member of `C`, and M is a Dotty trait: * * def x(): T = super[M].initial$x() * + * 3.1.2 If `x` is also a member of `C`, and M is a Scala 2.x trait: + * + * def x(): T = _ + * + * 3.1.3 If `x` is not a member of `C`, and M is a Dotty trait: + * + * super[M].initial$x() + * + * 3.1.4 If `x` is not a member of `C`, and M is a Scala2.x trait, nothing gets added. + * + * * 3.2 (done in `superCallOpt`) The call: * * super[M]. @@ -104,7 +118,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => val isym = initializer(vsym) val rhs = Block( initBuf.toList.map(_.changeOwner(impl.symbol, isym)), - stat.rhs.changeOwner(vsym, isym)) + stat.rhs.changeOwner(vsym, isym).wildcardToDefault) initBuf.clear() cpy.DefDef(stat)(rhs = EmptyTree) :: DefDef(isym, rhs) :: Nil case stat: DefDef if stat.symbol.isSetter => @@ -134,15 +148,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => if (baseCls.is(NoInitsTrait) || 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 -*/ + transformFollowingDeep(superRef(baseCls.primaryConstructor).appliedToNone) :: Nil } } @@ -150,16 +156,16 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => ctx.atPhase(thisTransform) { implicit ctx => sym is Deferred } def traitInits(mixin: ClassSymbol): List[Tree] = - for (getter <- mixin.info.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) - yield { - // transformFollowing call is needed to make memoize & lazy vals run - val rhs = transformFollowing(superRef(initializer(getter)).appliedToNone) - // isCurrent: getter is a member of implementing class - val isCurrent = getter.is(ExpandedName) || ctx.atPhase(thisTransform) { implicit ctx => - cls.info.member(getter.name).suchThat(_.isGetter).symbol == getter - } - if (isCurrent) transformFollowing(DefDef(implementation(getter.asTerm), rhs)) - else rhs + for (getter <- mixin.info.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) yield { + val isScala2x = mixin.is(Scala2x) + def default = Underscore(getter.info.resultType) + def initial = transformFollowing(superRef(initializer(getter)).appliedToNone) + if (isCurrent(getter) || getter.is(ExpandedName)) + // transformFollowing call is needed to make memoize & lazy vals run + transformFollowing( + DefDef(implementation(getter.asTerm), if (isScala2x) default else initial)) + else if (isScala2x) EmptyTree + else initial } def setters(mixin: ClassSymbol): List[Tree] = diff --git a/src/dotty/tools/dotc/transform/MixinOps.scala b/src/dotty/tools/dotc/transform/MixinOps.scala index 1dce85eaa9e7..64f0edfb8822 100644 --- a/src/dotty/tools/dotc/transform/MixinOps.scala +++ b/src/dotty/tools/dotc/transform/MixinOps.scala @@ -31,6 +31,18 @@ class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx: //sup.select(target) } + /** Is `sym` a member of implementing class `cls`? */ + def isCurrent(sym: Symbol) = cls.info.member(sym.name).hasAltWith(_.symbol == sym) + + def needsForwarder(meth: Symbol): Boolean = { + def needsDisambiguation = !meth.allOverriddenSymbols.forall(_ is Deferred) + meth.is(Method, butNot = PrivateOrAccessorOrDeferred) && + isCurrent(meth) && + (needsDisambiguation || meth.owner.is(Scala2x)) + } + + final val PrivateOrAccessorOrDeferred = Private | Accessor | Deferred + 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/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala index 507dbb0ce38d..5fa17921f53a 100644 --- a/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -60,7 +60,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans val sel = Ident(selector.termRef).withPos(tree.pos) val rethrow = tpd.CaseDef(EmptyTree, EmptyTree, Throw(ref(selector))) val newCases = tpd.CaseDef( - Bind(selector,untpd.Ident(nme.WILDCARD).withPos(tree.pos).withType(selector.info)), + Bind(selector, Underscore(selector.info).withPos(tree.pos)), EmptyTree, transformMatch(tpd.Match(sel, tree.cases ::: rethrow :: Nil))) cpy.Try(tree)(tree.expr, newCases :: Nil, tree.finalizer) diff --git a/src/dotty/tools/dotc/transform/ResolveSuper.scala b/src/dotty/tools/dotc/transform/ResolveSuper.scala index 0f1c448d9956..27387bca48a3 100644 --- a/src/dotty/tools/dotc/transform/ResolveSuper.scala +++ b/src/dotty/tools/dotc/transform/ResolveSuper.scala @@ -48,7 +48,8 @@ class ResolveSuper extends MiniPhaseTransform with IdentityDenotTransformer { th override def phaseName: String = "resolveSuper" - override def runsAfter = Set(classOf[ElimByName]) // verified empirically, need to figure out what the reason is. + override def runsAfter = Set(classOf[ElimByName], // verified empirically, need to figure out what the reason is. + classOf[AugmentScala2Traits]) /** Returns the symbol that is accessed by a super-accessor in a mixin composition. * @@ -80,15 +81,9 @@ class ResolveSuper extends MiniPhaseTransform with IdentityDenotTransformer { th for (superAcc <- mixin.info.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 = PrivateOrAccessorOrDeferred) && - !isOverridden(meth) && - !meth.allOverriddenSymbols.forall(_ is Deferred) - for (meth <- mixin.info.decls.toList if needsDisambiguation(meth)) + def methodOverrides(mixin: ClassSymbol): List[Tree] = + for (meth <- mixin.info.decls.toList if needsForwarder(meth)) yield polyDefDef(implementation(meth.asTerm), forwarder(meth)) - } val overrides = mixins.flatMap(mixin => superAccessors(mixin) ::: methodOverrides(mixin)) diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala index df3b183a956b..d3e029a74767 100644 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/src/dotty/tools/dotc/transform/SymUtils.scala @@ -85,9 +85,9 @@ 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 - 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) + + def implClass(implicit ctx: Context): Symbol = + self.owner.info.decl(self.name.implClassName).symbol } diff --git a/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/src/dotty/tools/dotc/transform/SyntheticMethods.scala index 9d0aebe450d9..b33bbd6a481c 100644 --- a/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -105,8 +105,7 @@ class SyntheticMethods(thisTransformer: DenotTransformer) { */ def equalsBody(that: Tree)(implicit ctx: Context): Tree = { val thatAsClazz = ctx.newSymbol(ctx.owner, nme.x_0, Synthetic, clazzType, coord = ctx.owner.pos) // x$0 - def wildcardAscription(tp: Type) = - Typed(untpd.Ident(nme.WILDCARD).withType(tp), TypeTree(tp)) + def wildcardAscription(tp: Type) = Typed(Underscore(tp), TypeTree(tp)) val pattern = Bind(thatAsClazz, wildcardAscription(clazzType)) // x$0 @ (_: C) val comparisons = accessors map (accessor => This(clazz).select(accessor).select(defn.Any_==).appliedTo(ref(thatAsClazz).select(accessor))) diff --git a/src/dotty/tools/dotc/transform/TraitConstructors.scala b/src/dotty/tools/dotc/transform/TraitConstructors.scala index 32c4b9da4e2f..9fea468dac82 100644 --- a/src/dotty/tools/dotc/transform/TraitConstructors.scala +++ b/src/dotty/tools/dotc/transform/TraitConstructors.scala @@ -22,8 +22,7 @@ class TraitConstructors extends MiniPhaseTransform with SymTransformer { def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = { if (sym.isPrimaryConstructor && (sym.owner is Flags.Trait)) - // TODO: Someone needs to carefully check if name clashes are possible with this mangling scheme - sym.copySymDenotation(name = nme.INITIALIZER_PREFIX ++ sym.owner.fullNameSeparated("$")) + sym.copySymDenotation(name = nme.IMPLCLASS_CONSTRUCTOR) else sym } diff --git a/src/dotty/tools/dotc/transform/TypeUtils.scala b/src/dotty/tools/dotc/transform/TypeUtils.scala index c01b6478cfa6..d474c77b4621 100644 --- a/src/dotty/tools/dotc/transform/TypeUtils.scala +++ b/src/dotty/tools/dotc/transform/TypeUtils.scala @@ -29,6 +29,6 @@ class TypeUtils(val self: Type) extends AnyVal { def ensureMethodic(implicit ctx: Context): Type = self match { case self: MethodicType => self - case _ => ExprType(self) + case _ => if (ctx.erasedTypes) MethodType(Nil, self) else ExprType(self) } } diff --git a/tests/pos/scala2traits/dotty-subclass.scala b/tests/pos/scala2traits/dotty-subclass.scala new file mode 100644 index 000000000000..4e162dd145e2 --- /dev/null +++ b/tests/pos/scala2traits/dotty-subclass.scala @@ -0,0 +1,27 @@ +// This is supposed to be compiled by Dotty +class Sub extends T + +class A extends S2T with S2Tprivate { + val a: Int = 3 + var b = 2 +} + +object Main { + def main(args: Array[String]): Unit = { + val sub = new Sub + println(sub.d) + println(sub.v) + println(sub.O) + println(sub.w) + + val a = new A + a.x += a.y + println(a.x) + println(a.f(a.a + a.b)) + + a.xx += a.yy + println(a.x) + println(a.ff(a.xx)) + } +} + diff --git a/tests/pos/scala2traits/scala-trait.scala b/tests/pos/scala2traits/scala-trait.scala new file mode 100644 index 000000000000..db05bc941d3a --- /dev/null +++ b/tests/pos/scala2traits/scala-trait.scala @@ -0,0 +1,31 @@ +// This is supposed to be compiled by Scala 2.11 +trait T { + def d = 42 + val v = "" + object O + final val w = 33 +} + +trait S2T { + var x: Int = 0 + lazy val y: Int = 1 +// val z: Int = 2 + val a: Int + var b: Int + + def f(x: Int): Int = x + y +} + +trait S2Tprivate { + private var x: Int = 0 + private lazy val y: Int = 1 +// private val z: Int = 2 // @darkdimius uncomment once lazy vals can be inherited. + + private def f(x: Int): Int = x + y + def xx = x + def xx_=(x: Int) = this.x = x + def yy = y +// def zz = z + def ff(x: Int) = f(x) +} + diff --git a/tests/run/t6534.scala b/tests/run/t6534.scala index 33df97e41e44..b5789f52c4fa 100644 --- a/tests/run/t6534.scala +++ b/tests/run/t6534.scala @@ -8,7 +8,13 @@ object Test { def main(args: Array[String]): Unit = { val b1 = new Bippy1(71) val b2 = new Bippy2(71) - assert(b1 == b1 && b1.## == b1.x.##, ((b1, b1.##))) - assert(b2 == b2 && b2.## == b2.x.##, ((b2, b2.##))) + assert(b1 == b1) + assert(b1.## == b1.x.##, "hash differs1 " + ((b1, b1.##))) + assert(b2 == b2) + // assert(b2.## == b2.x.##, "hash differs2 " + ((b2, b2.##, b2.x.##))) + // Disabled, this does not hold. Because the value class inherits + // a different hashCode, no code is generated for it. Replaced by: + assert(b2.## == -1) + assert(!b1.equals(b1)) } }