Skip to content

Implement Scala2 traits #622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Jun 6, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,15 @@ class Compiler {
new Getters,
new ClassTags,
new ElimByName,
new AugmentScala2Traits,
new ResolveSuper),
List(new Erasure),
List(new ElimErasedValueType,
new VCInline,
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),
Expand All @@ -74,7 +76,6 @@ class Compiler {
new ExpandPrivate,
new CollectEntryPoints,
new LabelDefs,
new ElimWildcardIdents,
new TraitConstructors),
List(new GenBCode)
)
Expand Down
9 changes: 9 additions & 0 deletions src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down
54 changes: 45 additions & 9 deletions src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
8 changes: 7 additions & 1 deletion src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand Down
14 changes: 10 additions & 4 deletions src/dotty/tools/dotc/core/NameOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/TypeErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
97 changes: 97 additions & 0 deletions src/dotty/tools/dotc/transform/AugmentScala2Traits.scala
Original file line number Diff line number Diff line change
@@ -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 %")
}
}
}
14 changes: 9 additions & 5 deletions src/dotty/tools/dotc/transform/Constructors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 _ =>
}
}
Expand Down
37 changes: 0 additions & 37 deletions src/dotty/tools/dotc/transform/ElimWildcardIdents.scala

This file was deleted.

2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/transform/ExpandSAMs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading