|
1 |
| -package dotty.tools.dotc |
| 1 | +package dotty.tools |
| 2 | +package dotc |
2 | 3 | package transform
|
3 | 4 |
|
4 | 5 | import core._
|
5 | 6 | import ast.Trees._
|
6 | 7 | import Contexts._, Types._, Symbols._, Flags._, TypeUtils._, DenotTransformers._, StdNames._
|
7 | 8 | import Decorators._
|
| 9 | +import MegaPhase._ |
| 10 | +import NameKinds.ParamAccessorName |
8 | 11 | import config.Printers.typr
|
9 | 12 |
|
10 |
| -/** For all parameter accessors |
| 13 | +/** For all private parameter accessors |
11 | 14 | *
|
12 |
| - * val x: T = ... |
| 15 | + * private val x: T = ... |
13 | 16 | *
|
14 |
| - * if |
15 |
| - * (1) x is forwarded in the supercall to a parameter that's also named `x` |
16 |
| - * (2) the superclass parameter accessor for `x` is accessible from the current class |
17 |
| - * change the accessor to |
| 17 | + * If there is a chain of parameter accessors starting with `x` such that |
| 18 | + * (1) The last parameter accessor in the chain is a field that's accessible |
| 19 | + * from the current class, and |
| 20 | + * (2) each preceding parameter is forwarded in the supercall of its class |
| 21 | + * to a parameter that's also named `x` |
| 22 | + * then change the accessor to |
18 | 23 | *
|
19 |
| - * def x: T = super.x.asInstanceOf[T] |
| 24 | + * private def x$accessor: T = super.x'.asInstanceOf[T] |
| 25 | + * |
| 26 | + * where x' is a reference to the final parameter in the chain. |
| 27 | + * Property (1) is established by the @see forwardParamAccessors method in PostTyper. |
| 28 | + * |
| 29 | + * The reason for renaming `x` to `x$accessor` is that private methods in the JVM |
| 30 | + * cannot override public ones. |
20 | 31 | *
|
21 |
| - * Do the same also if there are intermediate inaccessible parameter accessor forwarders. |
22 | 32 | * The aim of this transformation is to avoid redundant parameter accessor fields.
|
23 | 33 | */
|
24 |
| -class ParamForwarding(thisPhase: DenotTransformer) { |
| 34 | +class ParamForwarding extends MiniPhase with IdentityDenotTransformer: |
25 | 35 | import ast.tpd._
|
26 | 36 |
|
27 |
| - def forwardParamAccessors(impl: Template)(implicit ctx: Context): Template = { |
28 |
| - def fwd(stats: List[Tree])(implicit ctx: Context): List[Tree] = { |
29 |
| - val (superArgs, superParamNames) = impl.parents match { |
30 |
| - case superCall @ Apply(fn, args) :: _ => |
31 |
| - fn.tpe.widen match { |
32 |
| - case MethodType(paramNames) => (args, paramNames) |
33 |
| - case _ => (Nil, Nil) |
34 |
| - } |
35 |
| - case _ => (Nil, Nil) |
36 |
| - } |
37 |
| - def inheritedAccessor(sym: Symbol): Symbol = { |
38 |
| - /** |
39 |
| - * Dmitry: having it have the same name is needed to maintain correctness in presence of subclassing |
40 |
| - * if you would use parent param-name `a` to implement param-field `b` |
41 |
| - * overriding field `b` will actually override field `a`, that is wrong! |
42 |
| - * |
43 |
| - * class A(val s: Int); |
44 |
| - * class B(val b: Int) extends A(b) |
45 |
| - * class C extends A(2) { |
46 |
| - * def s = 3 |
47 |
| - * assert(this.b == 2) |
48 |
| - * } |
49 |
| - */ |
50 |
| - val candidate = sym.owner.asClass.superClass |
51 |
| - .info.decl(sym.name).suchThat(_.is(ParamAccessor, butNot = Mutable)).symbol |
52 |
| - if (candidate.isAccessibleFrom(currentClass.thisType, superAccess = true)) candidate |
53 |
| - else if (candidate.exists) inheritedAccessor(candidate) |
54 |
| - else NoSymbol |
55 |
| - } |
56 |
| - def forwardParamAccessor(stat: Tree): Tree = { |
57 |
| - stat match { |
58 |
| - case stat: ValDef => |
59 |
| - val sym = stat.symbol.asTerm |
60 |
| - if (sym.is(ParamAccessor, butNot = Mutable) && !sym.info.isInstanceOf[ExprType]) { |
61 |
| - // ElimByName gets confused with methods returning an ExprType, |
62 |
| - // so avoid param forwarding if parameter is by name. See i1766.scala |
63 |
| - val idx = superArgs.indexWhere(_.symbol == sym) |
64 |
| - if (idx >= 0 && superParamNames(idx) == stat.name) { // supercall to like-named parameter |
65 |
| - val alias = inheritedAccessor(sym) |
66 |
| - if (alias.exists) { |
67 |
| - def forwarder(implicit ctx: Context) = { |
68 |
| - sym.copySymDenotation(initFlags = sym.flags | Method | StableRealizable, info = sym.info.ensureMethodic) |
69 |
| - .installAfter(thisPhase) |
70 |
| - val superAcc = |
71 |
| - Super(This(currentClass), tpnme.EMPTY, inConstrCall = false).select(alias) |
72 |
| - typr.println(i"adding param forwarder $superAcc") |
73 |
| - DefDef(sym, superAcc.ensureConforms(sym.info.widen)).withSpan(stat.span) |
74 |
| - } |
75 |
| - return forwarder(ctx.withPhase(thisPhase.next)) |
76 |
| - } |
77 |
| - } |
78 |
| - } |
79 |
| - case _ => |
80 |
| - } |
81 |
| - stat |
82 |
| - } |
83 |
| - stats map forwardParamAccessor |
84 |
| - } |
| 37 | + private def thisPhase: ParamForwarding = this |
| 38 | + |
| 39 | + val phaseName: String = "paramForwarding" |
| 40 | + |
| 41 | + def transformIfParamAlias(mdef: ValOrDefDef)(using ctx: Context): Tree = |
| 42 | + |
| 43 | + def inheritedAccessor(sym: Symbol)(using Context): Symbol = |
| 44 | + val candidate = sym.owner.asClass.superClass |
| 45 | + .info.decl(sym.name).suchThat(_.is(ParamAccessor, butNot = Mutable)) |
| 46 | + .symbol |
| 47 | + if !candidate.is(Private) // candidate might be private and accessible if it is in an outer class |
| 48 | + && candidate.isAccessibleFrom(currentClass.thisType, superAccess = true) |
| 49 | + then |
| 50 | + candidate |
| 51 | + else if candidate.is(SuperParamAlias) then |
| 52 | + inheritedAccessor(candidate) |
| 53 | + else |
| 54 | + NoSymbol |
| 55 | + |
| 56 | + val sym = mdef.symbol.asTerm |
| 57 | + if sym.is(SuperParamAlias) then |
| 58 | + assert(sym.is(ParamAccessor, butNot = Mutable)) |
| 59 | + val alias = inheritedAccessor(sym)(using ctx.withPhase(thisPhase)) |
| 60 | + if alias.exists then |
| 61 | + sym.copySymDenotation( |
| 62 | + name = ParamAccessorName(sym.name), |
| 63 | + initFlags = sym.flags | Method | StableRealizable, |
| 64 | + info = sym.info.ensureMethodic |
| 65 | + ).installAfter(thisPhase) |
| 66 | + val superAcc = |
| 67 | + Super(This(currentClass), tpnme.EMPTY, inConstrCall = false) |
| 68 | + .withSpan(mdef.span) |
| 69 | + .select(alias) |
| 70 | + .ensureApplied |
| 71 | + .ensureConforms(sym.info.finalResultType) |
| 72 | + ctx.log(i"adding param forwarder $superAcc") |
| 73 | + DefDef(sym, superAcc) |
| 74 | + else mdef |
| 75 | + else mdef |
| 76 | + end transformIfParamAlias |
| 77 | + |
| 78 | + override def transformValDef(mdef: ValDef)(using Context): Tree = |
| 79 | + transformIfParamAlias(mdef) |
85 | 80 |
|
86 |
| - cpy.Template(impl)(body = fwd(impl.body)(ctx.withPhase(thisPhase))) |
87 |
| - } |
88 |
| -} |
| 81 | + override def transformDefDef(mdef: DefDef)(using Context): Tree = |
| 82 | + transformIfParamAlias(mdef) |
0 commit comments