Skip to content

Commit

Permalink
Simplify Scala 2 trait support
Browse files Browse the repository at this point in the history
Drop vestigial code related to Scala 2.11 support. In particular, we
created a fake impl class in AugmentScala2Trait only to add its members
to the trait in LinkScala2Impls. We now directly add the members to the
trait in LinkScala2Impls.

We could potentially simplify things even further by getting rid of
LinkScala2Impls since the static `foo$` methods in Scala 2 traits always
forward to instance methods `foo`, but that could have performance
implication as detailed in scala#5928, so we keep things as-is for now, but
eventually we should either switch Dotty trait encoding to also use
static forwarders, or not use them at all.
  • Loading branch information
smarter committed Mar 7, 2019
1 parent 757e2d1 commit e2beab9
Show file tree
Hide file tree
Showing 28 changed files with 101 additions and 197 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes {
}

private def setClassInfo(classSym: Symbol, classBType: ClassBType): ClassBType = {
val superClassSym = if (classSym.isImplClass) ObjectClass else classSym.superClass
val superClassSym = classSym.superClass
assert(
if (classSym == ObjectClass)
superClassSym == NoSymbol
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,6 @@ abstract class BackendInterface extends BackendInterfaceDefinitions {
def isStrictFP: Boolean
def isLabel: Boolean
def hasPackageFlag: Boolean
def isImplClass: Boolean
def isInterface: Boolean
def isGetter: Boolean
def isSetter: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,6 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
def isStrictFP: Boolean = false // todo: implement
def isLabel: Boolean = sym is Flags.Label
def hasPackageFlag: Boolean = sym is Flags.Package
def isImplClass: Boolean = sym is Flags.ImplClass
def isInterface: Boolean = (sym is Flags.PureInterface) || (sym is Flags.Trait)
def isGetter: Boolean = toDenot(sym).isGetter
def isSetter: Boolean = toDenot(sym).isSetter
Expand All @@ -683,7 +682,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma

def isFinal: Boolean = sym is Flags.Final
def isStaticMember: Boolean = (sym ne NoSymbol) &&
((sym is Flags.JavaStatic) || (owner is Flags.ImplClass) || toDenot(sym).hasAnnotation(ctx.definitions.ScalaStaticAnnot))
((sym is Flags.JavaStatic) || toDenot(sym).hasAnnotation(ctx.definitions.ScalaStaticAnnot))
// guard against no sumbol cause this code is executed to select which call type(static\dynamic) to use to call array.clone

def isBottomClass: Boolean = (sym ne defn.NullClass) && (sym ne defn.NothingClass)
Expand All @@ -701,7 +700,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
def isNonBottomSubClass(other: Symbol): Boolean = sym.derivesFrom(other)
def hasAnnotation(ann: Symbol): Boolean = toDenot(sym).hasAnnotation(ann)
def shouldEmitForwarders: Boolean =
(sym is Flags.Module) && !(sym is Flags.ImplClass) && sym.isStatic
(sym is Flags.Module) && sym.isStatic
def isJavaEntryPoint: Boolean = CollectEntryPoints.isJavaEntryPoint(sym)

def isClassConstructor: Boolean = toDenot(sym).isClassConstructor
Expand Down
8 changes: 0 additions & 8 deletions compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1575,8 +1575,6 @@ class JSCodeGen()(implicit ctx: Context) {
genApplyJSMethodGeneric(tree, sym, genExpr(receiver), genActualJSArgs(sym, args), isStat)
/*else
genApplyJSClassMethod(genExpr(receiver), sym, genActualArgs(sym, args))*/
} else if (foreignIsImplClass(sym.owner)) {
genTraitImplApply(sym, args.map(genExpr))
} else if (sym.isClassConstructor) {
// Calls to constructors are always statically linked
genApplyMethodStatically(genExpr(receiver), sym, genActualArgs(sym, args))
Expand Down Expand Up @@ -2022,12 +2020,6 @@ class JSCodeGen()(implicit ctx: Context) {
toIRType(patchedResultType(method)))
}

/** Gen a call to a Scala2 impl class method. */
private def genTraitImplApply(method: Symbol, arguments: List[js.Tree])(
implicit pos: Position): js.Tree = {
genApplyStatic(method, arguments)
}

/** Gen a call to a non-exposed method of a non-native JS class. */
private def genApplyJSClassMethod(receiver: js.Tree, method: Symbol,
arguments: List[js.Tree])(implicit pos: Position): js.Tree = {
Expand Down
3 changes: 0 additions & 3 deletions compiler/src/dotty/tools/backend/sjs/JSEncoding.scala
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,6 @@ object JSEncoding {
js.Ident(localNames.localSymbolName(sym), Some(sym.unexpandedName.decoded))
}

def foreignIsImplClass(sym: Symbol)(implicit ctx: Context): Boolean =
sym.name.endsWith(nme.IMPL_CLASS_SUFFIX.toString)

def encodeClassType(sym: Symbol)(implicit ctx: Context): jstpe.Type = {
if (sym == defn.ObjectClass) jstpe.AnyType
else if (isJSType(sym)) jstpe.AnyType
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class Compiler {
new ElimByName, // Expand by-name parameter references
new CollectNullableFields, // Collect fields that can be nulled out after use in lazy initialization
new ElimOuterSelect, // Expand outer selections
new AugmentScala2Traits, // Expand traits defined in Scala 2.x to simulate old-style rewritings
new AugmentScala2Traits, // Augments Scala2 traits with additional members needed for mixin composition.
new ResolveSuper, // Implement super accessors and add forwarders to trait methods
new FunctionXXLForwarders, // Add forwarders for FunctionXXL apply method
new ArrayConstructors) :: // Intercept creation of (non-generic) arrays and intrinsify.
Expand Down
24 changes: 9 additions & 15 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,8 @@ object Flags {
/** An unpickled Scala 2.x class */
final val Scala2x: FlagSet = typeFlag(26, "<scala-2.x>")

final val Scala2xTrait: FlagSet = Scala2x | Trait

final val SuperAccessorOrScala2x: FlagSet = Scala2x.toCommonFlags

/** A method that has default params */
Expand Down Expand Up @@ -413,11 +415,6 @@ object Flags {
/** Symbol is a self name */
final val SelfName: FlagSet = termFlag(54, "<selfname>")

/** Symbol is an implementation class of a Scala2 trait */
final val ImplClass: FlagSet = typeFlag(54, "<implclass>")

final val SelfNameOrImplClass: FlagSet = SelfName.toCommonFlags

/** An existentially bound symbol (Scala 2.x only) */
final val Scala2ExistentialCommon: FlagSet = commonFlag(55, "<existential>")
final val Scala2Existential: FlagSet = Scala2ExistentialCommon.toTypeFlags
Expand All @@ -428,14 +425,11 @@ object Flags {
/** A module variable (Scala 2.x only) */
final val Scala2ModuleVar: FlagSet = termFlag(57, "<modulevar>")

/** A Scala 2.12 trait that has been augmented with static members */
final val Scala_2_12_Augmented: FlagSet = typeFlag(57, "<scala_2_12_augmented>")

/** A definition that's initialized before the super call (Scala 2.x only) */
final val Scala2PreSuper: FlagSet = termFlag(58, "<presuper>")

/** A Scala 2.12 or higher trait */
final val Scala_2_12_Trait: FlagSet = typeFlag(58, "<scala_2_12_trait>")
/** A Scala 2.x trait that has been partially augmented.
* This is set in `AugmentScala2Trait` and reset in `LinkScala2Impls`
* when the trait is fully augmented.
*/
final val Scala2xPartiallyAugmented: FlagSet = typeFlag(57, "<scala-2.x-partially-augmented>")

/** A macro */
final val Macro: FlagSet = commonFlag(59, "<macro>")
Expand Down Expand Up @@ -497,7 +491,7 @@ object Flags {
* is completed)
*/
final val AfterLoadFlags: FlagSet =
FromStartFlags | AccessFlags | Final | AccessorOrSealed | LazyOrTrait | SelfNameOrImplClass
FromStartFlags | AccessFlags | Final | AccessorOrSealed | LazyOrTrait | SelfName.toCommonFlags

assert(FromStartFlags.isTermFlags && FromStartFlags.isTypeFlags)
// TODO: Should check that FromStartFlags do not change in completion
Expand Down Expand Up @@ -549,7 +543,7 @@ object Flags {

/** Flags that can apply to a module class */
final val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags |
ImplClass | Enum | Opaque
Enum | Opaque

/** Flags that are copied from a synthetic companion to a user-defined one
* when the two are merged. See: Namer.mergeCompanionDefs
Expand Down
3 changes: 0 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Mode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@ object Mode {
/** We are currently unpickling Scala2 info */
val Scala2Unpickling: Mode = newMode(13, "Scala2Unpickling")

/** We are currently unpickling from Java 8 or higher */
val Java8Unpickling: Mode = newMode(14, "Java8Unpickling")

/** Use Scala2 scheme for overloading and implicit resolution */
val OldOverloadingResolution: Mode = newMode(15, "OldOverloadingResolution")

Expand Down
8 changes: 0 additions & 8 deletions compiler/src/dotty/tools/dotc/core/NameOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -141,14 +141,6 @@ object NameOps {
name.replace { case VariantName(invariant, _) => invariant }
}

def implClassName: N = likeSpacedN(name ++ tpnme.IMPL_CLASS_SUFFIX)

def traitOfImplClassName: N = {
val suffix = tpnme.IMPL_CLASS_SUFFIX.toString
assert(name.endsWith(suffix), name)
likeSpacedN(name.mapLast(_.dropRight(suffix.length)))
}

def errorName: N = likeSpacedN(name ++ nme.ERROR)

/** Map variance value -1, +1 to 0, 1 */
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ object StdNames {
val EMPTY_PACKAGE: N = "<empty>"
val EXCEPTION_RESULT_PREFIX: N = "exceptionResult"
val EXPAND_SEPARATOR: N = str.EXPAND_SEPARATOR
val IMPL_CLASS_SUFFIX: N = "$class"
val IMPORT: N = "<import>"
val MODULE_SUFFIX: N = str.MODULE_SUFFIX
val OPS_PACKAGE: N = "<special-ops>"
Expand Down
7 changes: 1 addition & 6 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -650,9 +650,6 @@ object SymDenotations {
/** is this the constructor of a class? */
final def isClassConstructor: Boolean = name == nme.CONSTRUCTOR

/** Is this the constructor of a trait? */
final def isImplClassConstructor: Boolean = name == nme.TRAIT_CONSTRUCTOR

/** Is this the constructor of a trait or a class */
final def isConstructor: Boolean = name.isConstructorName

Expand Down Expand Up @@ -757,7 +754,6 @@ object SymDenotations {
|| boundary.isRoot
|| (accessWithin(boundary) || accessWithinLinked(boundary)) &&
( !(this is Local)
|| (owner is ImplClass) // allow private local accesses to impl class members
|| isCorrectThisType(pre)
)
|| (this is Protected) &&
Expand Down Expand Up @@ -1901,8 +1897,7 @@ object SymDenotations {
override def primaryConstructor(implicit ctx: Context): Symbol = {
def constrNamed(cname: TermName) = info.decls.denotsNamed(cname).last.symbol
// denotsNamed returns Symbols in reverse order of occurrence
if (this.is(ImplClass)) constrNamed(nme.TRAIT_CONSTRUCTOR) // ignore normal constructor
else if (this.is(Package)) NoSymbol
if (this.is(Package)) NoSymbol
else constrNamed(nme.CONSTRUCTOR).orElse(constrNamed(nme.TRAIT_CONSTRUCTOR))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,6 @@ class ClassfileParser(
throw new IOException(s"class file '${in.file}' has wrong magic number 0x${toHexString(magic)}, should be 0x${toHexString(JAVA_MAGIC)}")
val minorVersion = in.nextChar.toInt
val majorVersion = in.nextChar.toInt
if (majorVersion >= JAVA8_MAJOR_VERSION)
Scala2UnpicklingMode |= Mode.Java8Unpickling
if ((majorVersion < JAVA_MAJOR_VERSION) ||
((majorVersion == JAVA_MAJOR_VERSION) &&
(minorVersion < JAVA_MINOR_VERSION)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,6 @@ object PickleBuffer {
LAZY -> Lazy,
MIXEDIN -> (MixedIn, Scala2Existential),
EXPANDEDNAME -> Scala2ExpandedName,
IMPLCLASS -> (Scala2PreSuper, ImplClass),
SPECIALIZED -> Specialized,
VBRIDGE -> EmptyFlags,
VARARGS -> JavaVarargs,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ object Scala2Flags {
final val MIXEDIN = 1L << 35 // term member has been mixed in
final val EXISTENTIAL = 1L << 35 // type is an existential parameter or skolem
final val EXPANDEDNAME = 1L << 36 // name has been expanded with class suffix
final val IMPLCLASS = 1L << 37 // symbol is an implementation class
final val TRANS_FLAG = 1L << 38 // transient flag guaranteed to be reset after each phase.

final val LOCKED = 1L << 39 // temporary flag to catch cyclic dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -474,8 +474,6 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
def finishSym(sym: Symbol): Symbol = {
if (sym.isClass) {
sym.setFlag(Scala2x)
if (flags.is(Trait) && ctx.mode.is(Mode.Java8Unpickling))
sym.setFlag(Scala_2_12_Trait)
}
if (!(isRefinementClass(sym) || isUnpickleRoot(sym) || (sym is Scala2Existential))) {
val owner = sym.owner
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,6 @@ class PlainPrinter(_ctx: Context) extends Printer {
else if (sym.isAnonymousClass) "anonymous class"
else if (flags is ModuleClass) "module class"
else if (flags is ModuleVal) "module"
else if (flags is ImplClass) "implementation class"
else if (flags is Trait) "trait"
else if (sym.isClass) "class"
else if (sym.isType) "type"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
if (flags is Package) "package"
else if (sym.isPackageObject) "package object"
else if (flags is Module) "object"
else if (flags is ImplClass) "class"
else if (sym.isClassConstructor) "constructor"
else super.kindString(sym)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1601,7 +1601,6 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util.
def Flags_ModuleClass: Flags = core.Flags.ModuleClass
def Flags_PrivateLocal: Flags = core.Flags.PrivateLocal
def Flags_Package: Flags = core.Flags.Package
def Flags_ImplClass: Flags = core.Flags.ImplClass

//
// QUOTED SEAL/UNSEAL
Expand Down
100 changes: 33 additions & 67 deletions compiler/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,93 +14,59 @@ import Annotations._
import StdNames._
import NameOps._
import NameKinds.{ExpandedName, TraitSetterName}
import ast.Trees._

object AugmentScala2Traits {
val name: String = "augmentScala2Traits"
}

/** 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.
* Specifically, it adds
/** This phase augments Scala2 traits with additional members needed for mixin composition.
*
* - an implementation class which defines a trait constructor and trait method implementations
* - trait setters for vals defined in traits
* These symbols would have been added between Unpickling and Mixin in the Scala2 pipeline.
*
* Furthermore, it expands the names of all private getters and setters as well as super accessors in the trait and makes
* them not-private.
* Specifically, we:
* - Mark all lazy val fields as @volatile to get the proper Scala 2 semantics.
* - Add trait setters for vals defined in traits.
* - Expand the names of all private getters and setters as well as super accessors in the trait and make
* not-private.
*/
class AugmentScala2Traits extends MiniPhase with IdentityDenotTransformer with FullParameterization { thisPhase =>
class AugmentScala2Traits extends MiniPhase with IdentityDenotTransformer { thisPhase =>
import ast.tpd._

override def changesMembers: Boolean = true

override def phaseName: String = AugmentScala2Traits.name

override def rewiredTarget(referenced: Symbol, derived: Symbol)(implicit ctx: Context): Symbol = NoSymbol

override def transformTemplate(impl: Template)(implicit ctx: Context): Template = {
val cls = impl.symbol.owner.asClass
for (mixin <- cls.mixins)
if (mixin.is(Scala2x))
augmentScala2Trait(mixin, cls)
for (mixin <- cls.mixins if mixin.is(Scala2x) && !mixin.is(Scala2xPartiallyAugmented))
augmentScala2Trait(mixin)
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, thisPhase)
import ops._

val implClass = ctx.newCompleteClassSymbol(
owner = mixin.owner,
name = mixin.name.implClassName,
flags = Abstract | Scala2x | ImplClass,
parents = defn.ObjectType :: Nil,
assocFile = mixin.assocFile).enteredAfter(thisPhase)

def implMethod(meth: TermSymbol): Symbol = {
val mold =
if (meth.isConstructor)
meth.copySymDenotation(
name = nme.TRAIT_CONSTRUCTOR,
info = MethodType(Nil, defn.UnitType))
else meth.ensureNotPrivate
meth.copy(
owner = implClass,
name = mold.name.asTermName,
flags = Method | JavaStatic,
info = fullyParameterizedType(mold.info, mixin))
}

def traitSetter(getter: TermSymbol) =
getter.copy(
name = getter.ensureNotPrivate.name
.expandedName(getter.owner, TraitSetterName)
.asTermName.setterName,
flags = Method | Accessor,
info = MethodType(getter.info.resultType :: Nil, defn.UnitType))
private def augmentScala2Trait(mixin: ClassSymbol)(implicit ctx: Context): Unit = {
def traitSetter(getter: TermSymbol) =
getter.copy(
name = getter.ensureNotPrivate.name
.expandedName(getter.owner, TraitSetterName)
.asTermName.setterName,
flags = Method | Accessor,
info = MethodType(getter.info.resultType :: Nil, defn.UnitType))

for (sym <- mixin.info.decls) {
if (needsForwarder(sym) || sym.isConstructor || sym.isGetter && sym.is(Lazy) || sym.is(Method, butNot = Deferred))
implClass.enter(implMethod(sym.asTerm))
if (sym.isGetter)
if (sym.is(Lazy)) {
if (!sym.hasAnnotation(defn.VolatileAnnot))
sym.addAnnotation(Annotation(defn.VolatileAnnot, Nil))
}
else if (!sym.is(Deferred) && !sym.setter.exists &&
!sym.info.resultType.isInstanceOf[ConstantType])
traitSetter(sym.asTerm).enteredAfter(thisPhase)
if ((sym.is(PrivateAccessor) && !sym.name.is(ExpandedName) &&
(sym.isGetter || sym.isSetter)) // strangely, Scala 2 fields are also methods that have Accessor set.
|| sym.isSuperAccessor) // scala2 superaccessors are pickled as private, but are compiled as public expanded
sym.ensureNotPrivate.installAfter(thisPhase)
}
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 %")
for (sym <- mixin.info.decls) {
if (sym.isGetter)
if (sym.is(Lazy)) {
if (!sym.hasAnnotation(defn.VolatileAnnot))
sym.addAnnotation(Annotation(defn.VolatileAnnot, Nil))
}
else if (!sym.is(Deferred) && !sym.setter.exists &&
!sym.info.resultType.isInstanceOf[ConstantType])
traitSetter(sym.asTerm).enteredAfter(thisPhase)
if ((sym.is(PrivateAccessor) && !sym.name.is(ExpandedName) &&
(sym.isGetter || sym.isSetter)) // strangely, Scala 2 fields are also methods that have Accessor set.
|| sym.isSuperAccessor) // scala2 superaccessors are pickled as private, but are compiled as public expanded
sym.ensureNotPrivate.installAfter(thisPhase)
}
mixin.setFlag(Scala2xPartiallyAugmented)
}
}
Loading

0 comments on commit e2beab9

Please sign in to comment.