@@ -10,21 +10,25 @@ import Symbols._
1010import Types ._
1111import Decorators ._
1212import DenotTransformers ._
13+ import Names ._
1314import StdNames ._
15+ import SymDenotations ._
1416import ast .Trees ._
1517import 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}
0 commit comments