@@ -2,7 +2,7 @@ package dotty.tools.dotc
2
2
package transform
3
3
4
4
import MegaPhase ._
5
- import core .DenotTransformers .IdentityDenotTransformer
5
+ import core .DenotTransformers .{ DenotTransformer , IdentityDenotTransformer }
6
6
import core .Symbols ._
7
7
import core .Contexts ._
8
8
import core .Types ._
@@ -44,8 +44,21 @@ import collection.mutable
44
44
* (2) A reference `qual.apply` where `qual` has implicit function type and
45
45
* `qual` refers to a method `m` is rewritten to a reference to `m$direct`,
46
46
* keeping the same type and value arguments as they are found in `qual`.
47
+ *
48
+ * Note: The phase adds direct methods for all methods with IFT results that
49
+ * are defined in the transformed compilation unit, as well as for all
50
+ * methods that are referenced from inside the unit. It does NOT do an
51
+ * info transformer that adds these methods everywhere where an IFT returning
52
+ * method exists (including in separately compiled classes).
53
+ * Adding such an info transformer is impractical because it would mean
54
+ * that we have to force the types of all members of classes that are referenced.
55
+ * But not adding an info transformer can lead to inconsistencies in RefChecks.
56
+ * We solve that by ignoring direct methods in Refchecks.
57
+ * Another, related issue is bridge generation, where we also generate
58
+ * shortcut methods on the fly.
47
59
*/
48
60
class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisPhase =>
61
+ import ShortcutImplicits ._
49
62
import tpd ._
50
63
51
64
override def phaseName : String = ShortcutImplicits .name
@@ -61,82 +74,38 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisPh
61
74
override def initContext (ctx : FreshContext ) =
62
75
DirectMeth = ctx.addLocation[MutableSymbolMap [Symbol ]]()
63
76
64
- /** If this option is true, we don't specialize symbols that are known to be only
65
- * targets of monomorphic calls.
66
- * The reason for this option is that benchmarks show that on the JVM for monomorphic dispatch
67
- * scenarios inlining and escape analysis can often remove all calling overhead, so we might as
68
- * well not duplicate the code. We need more experience to decide on the best setting of this option.
69
- */
70
- final val specializeMonoTargets = true
71
77
72
78
override def prepareForUnit (tree : Tree )(implicit ctx : Context ) =
73
79
ctx.fresh.updateStore(DirectMeth , newMutableSymbolMap[Symbol ])
74
80
75
- /** Should `sym` get a ..$direct companion?
76
- * This is the case if `sym` is a method with a non-nullary implicit function type as final result type.
77
- * However if `specializeMonoTargets` is false, we exclude symbols that are known
78
- * to be only targets of monomorphic calls because they are effectively
79
- * final and don't override anything.
80
- */
81
- private def shouldBeSpecialized (sym : Symbol )(implicit ctx : Context ) =
82
- sym.is(Method , butNot = Accessor ) &&
83
- defn.isImplicitFunctionType(sym.info.finalResultType) &&
84
- defn.functionArity(sym.info.finalResultType) > 0 &&
85
- ! sym.isAnonymousFunction &&
86
- (specializeMonoTargets || ! sym.isEffectivelyFinal || sym.allOverriddenSymbols.nonEmpty)
87
-
88
- /** @pre The type's final result type is an implicit function type `implicit Ts => R`.
89
- * @return The type of the `apply` member of `implicit Ts => R`.
90
- */
91
- private def directInfo (info : Type )(implicit ctx : Context ): Type = info match {
92
- case info : PolyType => info.derivedLambdaType(resType = directInfo(info.resultType))
93
- case info : MethodType => info.derivedLambdaType(resType = directInfo(info.resultType))
94
- case info : ExprType => directInfo(info.resultType)
95
- case info => info.member(nme.apply).info
96
- }
97
-
98
- /** A new `m$direct` method to accompany the given method `m` */
99
- private def newDirectMethod (sym : Symbol )(implicit ctx : Context ): Symbol = {
100
- val direct = sym.copy(
101
- name = DirectMethodName (sym.name.asTermName).asInstanceOf [sym.ThisName ],
102
- flags = sym.flags | Synthetic ,
103
- info = directInfo(sym.info))
104
- if (direct.allOverriddenSymbols.isEmpty) direct.resetFlag(Override )
105
- direct
106
- }
107
81
108
82
/** The direct method `m$direct` that accompanies the given method `m`.
109
83
* Create one if it does not exist already.
110
84
*/
111
85
private def directMethod (sym : Symbol )(implicit ctx : Context ): Symbol =
112
- if (sym.owner.isClass) {
113
- val direct = sym.owner.info.member(DirectMethodName (sym.name.asTermName))
114
- .suchThat(_.info matches directInfo(sym.info)).symbol
115
- if (direct.maybeOwner == sym.owner) direct
116
- else newDirectMethod(sym).enteredAfter(thisPhase)
117
- }
118
- else directMeth.getOrElseUpdate(sym, newDirectMethod(sym))
86
+ if (sym.owner.isClass) shortcutMethod(sym, thisPhase)
87
+ else directMeth.getOrElseUpdate(sym, newShortcutMethod(sym))
119
88
120
89
/** Transform `qual.apply` occurrences according to rewrite rule (2) above */
121
90
override def transformSelect (tree : Select )(implicit ctx : Context ) =
122
91
if (tree.name == nme.apply &&
123
92
defn.isImplicitFunctionType(tree.qualifier.tpe.widen) &&
124
- shouldBeSpecialized (tree.qualifier.symbol)) {
93
+ needsImplicitShortcut (tree.qualifier.symbol)) {
125
94
def directQual (tree : Tree ): Tree = tree match {
126
95
case Apply (fn, args) => cpy.Apply (tree)(directQual(fn), args)
127
96
case TypeApply (fn, args) => cpy.TypeApply (tree)(directQual(fn), args)
128
97
case Block (stats, expr) => cpy.Block (tree)(stats, directQual(expr))
129
98
case tree : RefTree =>
130
99
cpy.Ref (tree)(DirectMethodName (tree.name.asTermName))
131
- .withType(directMethod(tree.symbol).termRef )
100
+ .withType(tree.tpe. asInstanceOf [ NamedType ].prefix.select( directMethod(tree.symbol)) )
132
101
}
133
102
directQual(tree.qualifier)
134
103
} else tree
135
104
136
105
/** Transform methods with implicit function type result according to rewrite rule (1) above */
137
106
override def transformDefDef (mdef : DefDef )(implicit ctx : Context ): Tree = {
138
107
val original = mdef.symbol
139
- if (shouldBeSpecialized (original)) {
108
+ if (needsImplicitShortcut (original)) {
140
109
val direct = directMethod(original)
141
110
142
111
// Move @tailrec to the direct method
@@ -179,4 +148,51 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisPh
179
148
180
149
object ShortcutImplicits {
181
150
val name = " shortcutImplicits"
151
+
152
+ /** If this option is true, we don't specialize symbols that are known to be only
153
+ * targets of monomorphic calls.
154
+ * The reason for this option is that benchmarks show that on the JVM for monomorphic dispatch
155
+ * scenarios inlining and escape analysis can often remove all calling overhead, so we might as
156
+ * well not duplicate the code. We need more experience to decide on the best setting of this option.
157
+ */
158
+ final val specializeMonoTargets = true
159
+
160
+ /** Should `sym` get a ..$direct companion?
161
+ * This is the case if `sym` is a method with a non-nullary implicit function type as final result type.
162
+ * However if `specializeMonoTargets` is false, we exclude symbols that are known
163
+ * to be only targets of monomorphic calls because they are effectively
164
+ * final and don't override anything.
165
+ */
166
+ def needsImplicitShortcut (sym : Symbol )(implicit ctx : Context ) =
167
+ sym.is(Method , butNot = Accessor ) &&
168
+ defn.isImplicitFunctionType(sym.info.finalResultType) &&
169
+ defn.functionArity(sym.info.finalResultType) > 0 &&
170
+ ! sym.isAnonymousFunction &&
171
+ (specializeMonoTargets || ! sym.isEffectivelyFinal || sym.allOverriddenSymbols.nonEmpty)
172
+
173
+ /** @pre The type's final result type is an implicit function type `implicit Ts => R`.
174
+ * @return The type of the `apply` member of `implicit Ts => R`.
175
+ */
176
+ private def directInfo (info : Type )(implicit ctx : Context ): Type = info match {
177
+ case info : PolyType => info.derivedLambdaType(resType = directInfo(info.resultType))
178
+ case info : MethodType => info.derivedLambdaType(resType = directInfo(info.resultType))
179
+ case info : ExprType => directInfo(info.resultType)
180
+ case info => info.member(nme.apply).info
181
+ }
182
+
183
+ /** A new `m$direct` method to accompany the given method `m` */
184
+ private def newShortcutMethod (sym : Symbol )(implicit ctx : Context ): Symbol =
185
+ sym.copy(
186
+ name = DirectMethodName (sym.name.asTermName).asInstanceOf [sym.ThisName ],
187
+ flags = sym.flags &~ Override | Synthetic ,
188
+ info = directInfo(sym.info))
189
+
190
+ def shortcutMethod (sym : Symbol , phase : DenotTransformer )(implicit ctx : Context ) =
191
+ sym.owner.info.decl(DirectMethodName (sym.name.asTermName))
192
+ .suchThat(_.info matches directInfo(sym.info)).symbol
193
+ .orElse(newShortcutMethod(sym).enteredAfter(phase))
194
+
195
+ def isImplicitShortcut (sym : Symbol )(implicit ctx : Context ) = sym.name.is(DirectMethodName )
182
196
}
197
+
198
+
0 commit comments