Skip to content

Commit 90070e2

Browse files
committed
First pass of reverting to the trait encoding of Scala 2.
1 parent 239f4e3 commit 90070e2

File tree

6 files changed

+146
-88
lines changed

6 files changed

+146
-88
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ class Compiler {
8686
new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods
8787
new CollectNullableFields, // Collect fields that can be nulled out after use in lazy initialization
8888
new ElimOuterSelect, // Expand outer selections
89-
new AugmentScala2Traits, // Augments Scala2 traits with additional members needed for mixin composition.
89+
//new AugmentScala2Traits, // Augments Scala2 traits with additional members needed for mixin composition.
9090
new ResolveSuper, // Implement super accessors
9191
new FunctionXXLForwarders, // Add forwarders for FunctionXXL apply method
9292
new ParamForwarding, // Add forwarders for aliases of superclass parameters

compiler/src/dotty/tools/dotc/transform/Constructors.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,16 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
205205
owner = constr.symbol).installAfter(thisPhase)
206206
constrStats += intoConstr(stat, sym)
207207
}
208+
case stat @ DefDef(name, _, _, tpt, _)
209+
if stat.symbol.isGetter && stat.symbol.owner.is(Trait) && !stat.symbol.is(Lazy) =>
210+
val sym = stat.symbol
211+
assert(isRetained(sym), sym)
212+
if (!stat.rhs.isEmpty && !isWildcardArg(stat.rhs))
213+
val setter =
214+
if (sym.setter.exists) sym.setter
215+
else sym.accessorNamed(Mixin.traitSetterName(sym.asTerm))
216+
constrStats += Apply(ref(setter), intoConstr(stat.rhs, sym).withSpan(stat.span) :: Nil)
217+
clsStats += cpy.DefDef(stat)(rhs = EmptyTree)
208218
case DefDef(nme.CONSTRUCTOR, _, ((outerParam @ ValDef(nme.OUTER, _, _)) :: _) :: Nil, _, _) =>
209219
clsStats += mapOuter(outerParam.symbol).transform(stat)
210220
case _: DefTree =>

compiler/src/dotty/tools/dotc/transform/LinkScala2Impls.scala

Lines changed: 78 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,25 @@ import Symbols._
1010
import Types._
1111
import Decorators._
1212
import DenotTransformers._
13+
import Names._
1314
import StdNames._
15+
import SymDenotations._
1416
import ast.Trees._
1517
import NameKinds.ImplMethName
1618

1719
/** Rewrite calls
1820
*
1921
* super[M].f(args)
2022
*
21-
* where M is a Scala 2.x trait implemented by the current class to
23+
* where M is a trait implemented by the current class to
2224
*
2325
* M.f$(this, args)
2426
*
2527
* where f$ is a static member of M.
28+
*
29+
* Also generates said static members, as forwarders to the normal methods.
2630
*/
27-
class LinkScala2Impls extends MiniPhase with IdentityDenotTransformer { thisPhase =>
31+
class LinkScala2Impls extends MiniPhase with SymTransformer { thisPhase =>
2832
import ast.tpd._
2933

3034
override def phaseName: String = "linkScala2Impls"
@@ -34,47 +38,77 @@ class LinkScala2Impls extends MiniPhase with IdentityDenotTransformer { thisPhas
3438
// Adds as a side effect static members to traits which can confuse Mixin,
3539
// that's why it is runsAfterGroupOf
3640

37-
private def addStaticForwarders(mixin: ClassSymbol)(using Context): Unit = {
38-
val ops = new MixinOps(mixin, thisPhase)
39-
import ops._
41+
override def transformSym(sym: SymDenotation)(using Context): SymDenotation =
42+
if sym.is(Trait) && !ctx.settings.scalajs.value then
43+
val mixin = sym.asClass.classSymbol
44+
val ops = new MixinOps(mixin, thisPhase)
45+
import ops._
4046

41-
def newImpl(meth: TermSymbol): Symbol = {
42-
def staticInfo(tp: Type) = tp match {
43-
case mt: MethodType =>
44-
MethodType(nme.SELF :: mt.paramNames, mixin.typeRef :: mt.paramInfos, mt.resType)
45-
}
46-
val mold =
47-
if (meth.isConstructor)
48-
meth.copySymDenotation(
49-
name = nme.TRAIT_CONSTRUCTOR,
50-
info = MethodType(Nil, defn.UnitType))
51-
else meth.ensureNotPrivate
52-
meth.copy(
53-
owner = mixin,
54-
name = if (meth.isConstructor) mold.name.asTermName else ImplMethName(mold.name.asTermName),
55-
flags = Method | JavaStatic,
56-
info = staticInfo(mold.info)
57-
)
58-
}
59-
for (sym <- mixin.info.decls)
60-
if (needsMixinForwarder(sym) || sym.isConstructor || sym.isGetter && sym.is(Lazy) || sym.is(Method, butNot = Deferred))
61-
newImpl(sym.asTerm).enteredAfter(thisPhase)
62-
// The trait is now fully augmented so the flag isn't needed anymore.
63-
mixin.resetFlag(Scala2xPartiallyAugmented)
64-
}
47+
def newImpl(meth: TermSymbol): Symbol =
48+
def staticInfo(tp: Type) = tp match
49+
case mt: MethodType =>
50+
MethodType(nme.SELF :: mt.paramNames, mixin.typeRef :: mt.paramInfos, mt.resType)
51+
val mold = meth.ensureNotPrivate
52+
meth.copy(
53+
owner = mixin,
54+
name = staticForwarderName(mold.name.asTermName),
55+
flags = Method | JavaStatic,
56+
info = staticInfo(mold.info)
57+
)
6558

66-
override def prepareForTemplate(impl: Template)(using Context): Context = {
67-
val cls = impl.symbol.owner.asClass
68-
for (mixin <- cls.mixins if (mixin.is(Scala2xPartiallyAugmented)))
69-
addStaticForwarders(mixin)
70-
ctx
71-
}
59+
val classInfo = sym.asClass.classInfo
60+
val decls1 = classInfo.decls.cloneScope
61+
var modified: Boolean = false
62+
for (meth <- classInfo.decls)
63+
if needsStaticForwarder(meth) then
64+
decls1.enter(newImpl(meth.asTerm))
65+
modified = true
66+
if modified then
67+
sym.copySymDenotation(
68+
info = classInfo.derivedClassInfo(decls = decls1))
69+
else
70+
sym
71+
else
72+
sym
73+
end transformSym
74+
75+
private def needsStaticForwarder(sym: Symbol)(using Context): Boolean =
76+
sym.is(Method, butNot = Deferred)
77+
78+
private def staticForwarderName(name: TermName)(using Context): TermName =
79+
if name == nme.TRAIT_CONSTRUCTOR then name
80+
else ImplMethName(name)
81+
82+
override def transformTemplate(impl: Template)(using Context): Tree =
83+
val sym = impl.symbol.owner.asClass
84+
if sym.is(Trait) && !ctx.settings.scalajs.value then
85+
val newConstr :: newBody = (impl.constr :: impl.body).flatMap {
86+
case stat: DefDef if needsStaticForwarder(stat.symbol) =>
87+
val meth = stat.symbol
88+
val staticForwarderDef = DefDef(implMethod(meth).asTerm, { paramss =>
89+
val params = paramss.head
90+
/* FIXME This is wrong. We are emitting a virtual call to `meth`,
91+
* but we must instead emit an `invokespecial` so that it is not
92+
* virtual. However, I don't know how to represent an invokevirtual
93+
* call on a non-`this` receiver. My best attempt is the following,
94+
* but that throws an exception when type-assigning the Super node:
95+
*/
96+
//Apply(Super(params.head, sym.name.asTypeName, sym).select(meth), params.tail)
97+
Apply(params.head.select(meth), params.tail)
98+
})
99+
stat :: transformFollowing(staticForwarderDef) :: Nil
100+
case stat =>
101+
stat :: Nil
102+
}
103+
cpy.Template(impl)(constr = newConstr.asInstanceOf[DefDef], body = newBody)
104+
else
105+
impl
72106

73107
override def transformApply(app: Apply)(using Context): Tree = {
74108
def currentClass = ctx.owner.enclosingClass.asClass
75109
app match {
76110
case Apply(sel @ Select(Super(_, _), _), args)
77-
if sel.symbol.owner.is(Scala2x) && currentClass.mixins.contains(sel.symbol.owner) && !ctx.settings.scalajs.value =>
111+
if sel.symbol.owner.is(Trait) && currentClass.mixins.contains(sel.symbol.owner) && !ctx.settings.scalajs.value =>
78112
val impl = implMethod(sel.symbol)
79113
if (impl.exists) Apply(ref(impl), This(currentClass) :: args).withSpan(app.span)
80114
else app // could have been an abstract method in a trait linked to from a super constructor
@@ -83,19 +117,13 @@ class LinkScala2Impls extends MiniPhase with IdentityDenotTransformer { thisPhas
83117
}
84118
}
85119

86-
/** The 2.12 implementation method of a super call or implementation class target */
87-
private def implMethod(meth: Symbol)(using Context): Symbol = {
88-
val implName = ImplMethName(meth.name.asTermName)
120+
/** The implementation method of a super call or implementation class target */
121+
private def implMethod(meth: Symbol)(using Context): Symbol =
89122
val cls = meth.owner
90-
if (cls.isAllOf(Scala2xTrait))
91-
if (meth.isConstructor)
92-
cls.info.decl(nme.TRAIT_CONSTRUCTOR).symbol
93-
else
94-
cls.info.decl(implName)
95-
.suchThat(c => FullParameterization.memberSignature(c.info) == meth.signature)
96-
.symbol
97-
else throw new AssertionError(i"no impl method for $meth")
98-
}
99-
100-
private val Scala2xTrait = Scala2x | Trait
123+
if meth.name == nme.TRAIT_CONSTRUCTOR then
124+
cls.info.decl(nme.TRAIT_CONSTRUCTOR).suchThat(_.is(JavaStatic)).symbol
125+
else
126+
cls.info.decl(staticForwarderName(meth.name.asTermName))
127+
.suchThat(c => FullParameterization.memberSignature(c.info) == meth.signature)
128+
.symbol
101129
}

compiler/src/dotty/tools/dotc/transform/Mixin.scala

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,19 @@ import Types._
1313
import Decorators._
1414
import DenotTransformers._
1515
import StdNames._
16+
import Names._
1617
import NameKinds._
18+
import NameOps._
1719
import ast.Trees._
1820
import collection.mutable
1921

2022
object Mixin {
2123
val name: String = "mixin"
24+
25+
def traitSetterName(getter: TermSymbol)(using Context): TermName =
26+
getter.ensureNotPrivate.name
27+
.expandedName(getter.owner, TraitSetterName)
28+
.asTermName.setterName
2229
}
2330

2431
/** This phase performs the following transformations:
@@ -127,12 +134,27 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
127134
else sym.copySymDenotation(initFlags = sym.flags &~ ParamAccessor | Deferred)
128135
sym1.ensureNotPrivate
129136
}
130-
else if (sym.isConstructor && (sym.owner.is(Trait, butNot = Scala2x) || (sym.owner.isAllOf(Trait | Scala2x) && ctx.settings.scalajs.value)))
137+
else if (sym.isConstructor && sym.owner.is(Trait))
131138
sym.copySymDenotation(
132139
name = nme.TRAIT_CONSTRUCTOR,
133140
info = MethodType(Nil, sym.info.resultType))
141+
else if sym.is(Trait) then
142+
val classInfo = sym.asClass.classInfo
143+
val decls1 = classInfo.decls.cloneScope
144+
var modified: Boolean = false
145+
for (getter <- classInfo.decls)
146+
if needsTraitSetter(getter) then
147+
val setter = makeTraitSetter(getter.asTerm)
148+
decls1.enter(setter)
149+
modified = true
150+
if modified then
151+
sym.copySymDenotation(
152+
info = classInfo.derivedClassInfo(decls = decls1))
153+
else
154+
sym
134155
else
135156
sym
157+
end transformSym
136158

137159
private def initializer(sym: Symbol)(using Context): TermSymbol = {
138160
if (sym.is(Lazy)) sym
@@ -149,32 +171,34 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
149171
}
150172
}.asTerm
151173

174+
private def wasOneOf(sym: Symbol, flags: FlagSet)(using Context): Boolean =
175+
atPhase(thisPhase) { sym.isOneOf(flags) }
176+
177+
private def needsTraitSetter(sym: Symbol)(using Context): Boolean =
178+
sym.isGetter && !wasOneOf(sym, DeferredOrLazy | ParamAccessor) && !sym.setter.exists
179+
&& !sym.info.resultType.isInstanceOf[ConstantType]
180+
181+
private def makeTraitSetter(getter: TermSymbol)(using Context): Symbol =
182+
getter.copy(
183+
name = Mixin.traitSetterName(getter),
184+
flags = Method | Accessor | Deferred,
185+
info = MethodType(getter.info.resultType :: Nil, defn.UnitType))
186+
152187
override def transformTemplate(impl: Template)(using Context): Template = {
153188
val cls = impl.symbol.owner.asClass
154189
val ops = new MixinOps(cls, thisPhase)
155190
import ops._
156191

157192
def traitDefs(stats: List[Tree]): List[Tree] = {
158-
val initBuf = new mutable.ListBuffer[Tree]
159-
stats.flatMap({
160-
case stat: DefDef if stat.symbol.isGetter && !stat.rhs.isEmpty && !stat.symbol.is(Lazy) =>
161-
// make initializer that has all effects of previous getter,
162-
// replace getter rhs with empty tree.
163-
val vsym = stat.symbol
164-
val isym = initializer(vsym)
165-
val rhs = Block(
166-
initBuf.toList.map(_.changeOwnerAfter(impl.symbol, isym, thisPhase)),
167-
stat.rhs.changeOwnerAfter(vsym, isym, thisPhase).wildcardToDefault)
168-
initBuf.clear()
169-
cpy.DefDef(stat)(rhs = EmptyTree) :: DefDef(isym, rhs) :: Nil
193+
stats.flatMap {
194+
case stat: DefDef if needsTraitSetter(stat.symbol) =>
195+
// add a trait setter for this getter
196+
stat :: DefDef(stat.symbol.traitSetter.asTerm, EmptyTree) :: Nil
170197
case stat: DefDef if stat.symbol.isSetter =>
171198
cpy.DefDef(stat)(rhs = EmptyTree) :: Nil
172-
case stat: DefTree =>
173-
stat :: Nil
174199
case stat =>
175-
initBuf += stat
176-
Nil
177-
}) ++ initBuf
200+
stat :: Nil
201+
}
178202
}
179203

180204
/** Map constructor call to a triple of a supercall, and if the target
@@ -221,9 +245,6 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
221245
//println(i"synth super call ${baseCls.primaryConstructor}: ${baseCls.primaryConstructor.info}")
222246
transformFollowingDeep(superRef(baseCls.primaryConstructor).appliedToNone) :: Nil
223247

224-
def wasOneOf(sym: Symbol, flags: FlagSet) =
225-
atPhase(thisPhase) { sym.isOneOf(flags) }
226-
227248
def traitInits(mixin: ClassSymbol): List[Tree] = {
228249
val argsIt = superCallsAndArgs.get(mixin) match
229250
case Some((_, _, args)) => args.iterator
@@ -241,33 +262,28 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
241262
EmptyTree
242263

243264
for (getter <- mixin.info.decls.toList if getter.isGetter && !wasOneOf(getter, Deferred)) yield {
244-
val isScala2x = mixin.is(Scala2x)
245-
def default = Underscore(getter.info.resultType)
246-
def initial = transformFollowing(superRef(initializer(getter)).appliedToNone)
247-
248265
if (isCurrent(getter) || getter.name.is(ExpandedName)) {
249266
val rhs =
250267
if (wasOneOf(getter, ParamAccessor))
251268
nextArgument()
252-
else if (isScala2x)
253-
if (getter.is(Lazy, butNot = Module))
254-
initial
255-
else if (getter.is(Module))
256-
New(getter.info.resultType, List(This(cls)))
257-
else
258-
Underscore(getter.info.resultType)
269+
else if (getter.is(Lazy, butNot = Module))
270+
transformFollowing(superRef(getter).appliedToNone)
271+
else if (getter.is(Module))
272+
New(getter.info.resultType, List(This(cls)))
259273
else
260-
initial
274+
Underscore(getter.info.resultType)
261275
// transformFollowing call is needed to make memoize & lazy vals run
262276
transformFollowing(DefDef(mkForwarderSym(getter.asTerm), rhs))
263277
}
264-
else if (isScala2x || wasOneOf(getter, ParamAccessor | Lazy)) EmptyTree
265-
else initial
278+
else EmptyTree
266279
}
267280
}
268281

269282
def setters(mixin: ClassSymbol): List[Tree] =
270-
for (setter <- mixin.info.decls.filter(setr => setr.isSetter && !wasOneOf(setr, Deferred)))
283+
val mixinSetters = mixin.info.decls.filter { sym =>
284+
sym.isSetter && (!wasOneOf(sym, Deferred) || sym.name.is(TraitSetterName))
285+
}
286+
for (setter <- mixinSetters)
271287
yield transformFollowing(DefDef(mkForwarderSym(setter.asTerm), unitLiteral.withSpan(cls.span)))
272288

273289
def mixinForwarders(mixin: ClassSymbol): List[Tree] =

compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class ResolveSuper extends MiniPhase with IdentityDenotTransformer { thisPhase =
3434
override def phaseName: String = ResolveSuper.name
3535

3636
override def runsAfter: Set[String] = Set(ElimByName.name, // verified empirically, need to figure out what the reason is.
37-
AugmentScala2Traits.name,
37+
//AugmentScala2Traits.name,
3838
PruneErasedDefs.name) // Erased decls make `isCurrent` work incorrectly
3939

4040
override def changesMembers: Boolean = true // the phase adds super accessors

compiler/src/dotty/tools/dotc/transform/SymUtils.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ class SymUtils(val self: Symbol) extends AnyVal {
152152
if (self.isSetter) self
153153
else accessorNamed(self.asTerm.name.setterName)
154154

155+
def traitSetter(using Context): Symbol =
156+
if (self.name.is(TraitSetterName)) self
157+
else accessorNamed(Mixin.traitSetterName(self.asTerm))
158+
155159
def field(using Context): Symbol = {
156160
val thisName = self.name.asTermName
157161
val fieldName =

0 commit comments

Comments
 (0)