Skip to content

Commit b11324a

Browse files
committed
SI-7492 Remove -Ystruct-dispatch and associated code
This means that the private option is gone as well as the untested code for no-cache and mono-cache and the non-working code for invoke-dynamic. poly-cache is now always used. For the future it probably makes more sense to let the backend decide how it wants to treat structural dispatch instead of allowing the user to “mix and match” backends with structural dispatch implementations.
1 parent bf3c44c commit b11324a

File tree

3 files changed

+113
-217
lines changed

3 files changed

+113
-217
lines changed

src/compiler/scala/tools/nsc/settings/ScalaSettings.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,6 @@ trait ScalaSettings extends AbsScalaSettings
163163
val Ystatistics = BooleanSetting ("-Ystatistics", "Print compiler statistics.") andThen (scala.reflect.internal.util.Statistics.enabled = _)
164164
val stopAfter = PhasesSetting ("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat
165165
val stopBefore = PhasesSetting ("-Ystop-before", "Stop before")
166-
val refinementMethodDispatch
167-
= ChoiceSetting ("-Ystruct-dispatch", "policy", "structural method dispatch policy", List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache")
168166
val Yrangepos = BooleanSetting ("-Yrangepos", "Use range positions for syntax trees.")
169167
val Ymemberpos = StringSetting ("-Yshow-member-pos", "output style", "Show start and end positions of members", "") withPostSetHook (_ => Yrangepos.value = true)
170168
val Yreifycopypaste = BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.")

src/compiler/scala/tools/nsc/transform/CleanUp.scala

Lines changed: 111 additions & 212 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,6 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
4747

4848
private var localTyper: analyzer.Typer = null
4949

50-
private object MethodDispatchType extends scala.Enumeration {
51-
val NO_CACHE, MONO_CACHE, POLY_CACHE = Value
52-
}
53-
import MethodDispatchType.{ NO_CACHE, MONO_CACHE, POLY_CACHE }
54-
private def dispatchType() = settings.refinementMethodDispatch.value match {
55-
case "no-cache" => NO_CACHE
56-
case "mono-cache" => MONO_CACHE
57-
case "poly-cache" => POLY_CACHE
58-
}
5950
private def typedWithPos(pos: Position)(tree: Tree) =
6051
localTyper.typedPos(pos)(tree)
6152

@@ -113,133 +104,65 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
113104
def fromTypesToClassArrayLiteral(paramTypes: List[Type]): Tree =
114105
ArrayValue(TypeTree(ClassClass.tpe), paramTypes map LIT)
115106

116-
/* ... */
117-
def reflectiveMethodCache(method: String, paramTypes: List[Type]): Symbol = dispatchType() match {
118-
case NO_CACHE =>
119-
120-
/* Implementation of the cache is as follows for method "def xyz(a: A, b: B)":
121-
122-
var reflParams$Cache: Array[Class[_]] = Array[JClass](classOf[A], classOf[B])
123-
124-
def reflMethod$Method(forReceiver: JClass[_]): JMethod =
125-
forReceiver.getMethod("xyz", reflParams$Cache)
126-
127-
*/
128-
129-
val reflParamsCacheSym: Symbol =
130-
addStaticVariableToClass(nme.reflParamsCacheName, arrayType(ClassClass.tpe), fromTypesToClassArrayLiteral(paramTypes), true)
131-
132-
addStaticMethodToClass((_, forReceiverSym) =>
133-
gen.mkMethodCall(REF(forReceiverSym), Class_getMethod, Nil, List(LIT(method), REF(reflParamsCacheSym)))
134-
)
135-
136-
case MONO_CACHE =>
137-
138-
/* Implementation of the cache is as follows for method "def xyz(a: A, b: B)"
139-
(but with a SoftReference wrapping reflClass$Cache, similarly in the poly Cache) :
140-
141-
var reflParams$Cache: Array[Class[_]] = Array[JClass](classOf[A], classOf[B])
142-
143-
var reflMethod$Cache: JMethod = null
144-
145-
var reflClass$Cache: JClass[_] = null
146-
147-
def reflMethod$Method(forReceiver: JClass[_]): JMethod = {
148-
if (reflClass$Cache != forReceiver) {
149-
reflMethod$Cache = forReceiver.getMethod("xyz", reflParams$Cache)
150-
reflClass$Cache = forReceiver
151-
}
152-
reflMethod$Cache
153-
}
154-
155-
*/
156-
157-
val reflParamsCacheSym: Symbol =
158-
addStaticVariableToClass(nme.reflParamsCacheName, arrayType(ClassClass.tpe), fromTypesToClassArrayLiteral(paramTypes), true)
159-
160-
val reflMethodCacheSym: Symbol =
161-
addStaticVariableToClass(nme.reflMethodCacheName, MethodClass.tpe, NULL, false)
162-
163-
val reflClassCacheSym: Symbol =
164-
addStaticVariableToClass(nme.reflClassCacheName, SoftReferenceClass.tpe, NULL, false)
165-
166-
def isCacheEmpty(receiver: Symbol): Tree =
167-
reflClassCacheSym.IS_NULL() OR (reflClassCacheSym.GET() OBJ_NE REF(receiver))
168-
169-
addStaticMethodToClass((_, forReceiverSym) =>
170-
BLOCK(
171-
IF (isCacheEmpty(forReceiverSym)) THEN BLOCK(
172-
REF(reflMethodCacheSym) === ((REF(forReceiverSym) DOT Class_getMethod)(LIT(method), REF(reflParamsCacheSym))) ,
173-
REF(reflClassCacheSym) === gen.mkSoftRef(REF(forReceiverSym)),
174-
UNIT
175-
) ENDIF,
176-
REF(reflMethodCacheSym)
177-
)
178-
)
179-
180-
case POLY_CACHE =>
107+
def reflectiveMethodCache(method: String, paramTypes: List[Type]): Symbol = {
108+
/* Implementation of the cache is as follows for method "def xyz(a: A, b: B)"
109+
(SoftReference so that it does not interfere with classloader garbage collection,
110+
see ticket #2365 for details):
181111
182-
/* Implementation of the cache is as follows for method "def xyz(a: A, b: B)"
183-
(SoftReference so that it does not interfere with classloader garbage collection, see ticket
184-
#2365 for details):
112+
var reflParams$Cache: Array[Class[_]] = Array[JClass](classOf[A], classOf[B])
185113
186-
var reflParams$Cache: Array[Class[_]] = Array[JClass](classOf[A], classOf[B])
114+
var reflPoly$Cache: SoftReference[scala.runtime.MethodCache] = new SoftReference(new EmptyMethodCache())
187115
188-
var reflPoly$Cache: SoftReference[scala.runtime.MethodCache] = new SoftReference(new EmptyMethodCache())
116+
def reflMethod$Method(forReceiver: JClass[_]): JMethod = {
117+
var methodCache: MethodCache = reflPoly$Cache.find(forReceiver)
118+
if (methodCache eq null) {
119+
methodCache = new EmptyMethodCache
120+
reflPoly$Cache = new SoftReference(methodCache)
121+
}
122+
var method: JMethod = methodCache.find(forReceiver)
123+
if (method ne null)
124+
return method
125+
else {
126+
method = ScalaRunTime.ensureAccessible(forReceiver.getMethod("xyz", reflParams$Cache))
127+
reflPoly$Cache = new SoftReference(methodCache.add(forReceiver, method))
128+
return method
129+
}
130+
}
131+
*/
189132

190-
def reflMethod$Method(forReceiver: JClass[_]): JMethod = {
191-
var methodCache: MethodCache = reflPoly$Cache.find(forReceiver)
192-
if (methodCache eq null) {
193-
methodCache = new EmptyMethodCache
194-
reflPoly$Cache = new SoftReference(methodCache)
195-
}
196-
var method: JMethod = methodCache.find(forReceiver)
197-
if (method ne null)
198-
return method
199-
else {
200-
method = ScalaRunTime.ensureAccessible(forReceiver.getMethod("xyz", reflParams$Cache))
201-
reflPoly$Cache = new SoftReference(methodCache.add(forReceiver, method))
202-
return method
203-
}
204-
}
133+
val reflParamsCacheSym: Symbol =
134+
addStaticVariableToClass(nme.reflParamsCacheName, arrayType(ClassClass.tpe), fromTypesToClassArrayLiteral(paramTypes), true)
205135

206-
*/
136+
def mkNewPolyCache = gen.mkSoftRef(NEW(TypeTree(EmptyMethodCacheClass.tpe)))
137+
val reflPolyCacheSym: Symbol = addStaticVariableToClass(nme.reflPolyCacheName, SoftReferenceClass.tpe, mkNewPolyCache, false)
207138

208-
val reflParamsCacheSym: Symbol =
209-
addStaticVariableToClass(nme.reflParamsCacheName, arrayType(ClassClass.tpe), fromTypesToClassArrayLiteral(paramTypes), true)
139+
def getPolyCache = gen.mkCast(fn(REF(reflPolyCacheSym), nme.get), MethodCacheClass.tpe)
210140

211-
def mkNewPolyCache = gen.mkSoftRef(NEW(TypeTree(EmptyMethodCacheClass.tpe)))
212-
val reflPolyCacheSym: Symbol = (
213-
addStaticVariableToClass(nme.reflPolyCacheName, SoftReferenceClass.tpe, mkNewPolyCache, false)
214-
)
215-
def getPolyCache = gen.mkCast(fn(REF(reflPolyCacheSym), nme.get), MethodCacheClass.tpe)
141+
addStaticMethodToClass((reflMethodSym, forReceiverSym) => {
142+
val methodCache = reflMethodSym.newVariable(mkTerm("methodCache"), ad.pos) setInfo MethodCacheClass.tpe
143+
val methodSym = reflMethodSym.newVariable(mkTerm("method"), ad.pos) setInfo MethodClass.tpe
216144

217-
addStaticMethodToClass((reflMethodSym, forReceiverSym) => {
218-
val methodCache = reflMethodSym.newVariable(mkTerm("methodCache"), ad.pos) setInfo MethodCacheClass.tpe
219-
val methodSym = reflMethodSym.newVariable(mkTerm("method"), ad.pos) setInfo MethodClass.tpe
145+
BLOCK(
146+
VAL(methodCache) === getPolyCache,
147+
IF (REF(methodCache) OBJ_EQ NULL) THEN BLOCK(
148+
REF(methodCache) === NEW(TypeTree(EmptyMethodCacheClass.tpe)),
149+
REF(reflPolyCacheSym) === gen.mkSoftRef(REF(methodCache))
150+
) ENDIF,
220151

152+
VAL(methodSym) === (REF(methodCache) DOT methodCache_find)(REF(forReceiverSym)),
153+
IF (REF(methodSym) OBJ_NE NULL) .
154+
THEN (Return(REF(methodSym)))
155+
ELSE {
156+
def methodSymRHS = ((REF(forReceiverSym) DOT Class_getMethod)(LIT(method), REF(reflParamsCacheSym)))
157+
def cacheRHS = ((REF(methodCache) DOT methodCache_add)(REF(forReceiverSym), REF(methodSym)))
221158
BLOCK(
222-
VAL(methodCache) === getPolyCache,
223-
IF (REF(methodCache) OBJ_EQ NULL) THEN BLOCK(
224-
REF(methodCache) === NEW(TypeTree(EmptyMethodCacheClass.tpe)),
225-
REF(reflPolyCacheSym) === gen.mkSoftRef(REF(methodCache))
226-
) ENDIF,
227-
228-
VAL(methodSym) === (REF(methodCache) DOT methodCache_find)(REF(forReceiverSym)),
229-
IF (REF(methodSym) OBJ_NE NULL) .
230-
THEN (Return(REF(methodSym)))
231-
ELSE {
232-
def methodSymRHS = ((REF(forReceiverSym) DOT Class_getMethod)(LIT(method), REF(reflParamsCacheSym)))
233-
def cacheRHS = ((REF(methodCache) DOT methodCache_add)(REF(forReceiverSym), REF(methodSym)))
234-
BLOCK(
235-
REF(methodSym) === (REF(ensureAccessibleMethod) APPLY (methodSymRHS)),
236-
REF(reflPolyCacheSym) === gen.mkSoftRef(cacheRHS),
237-
Return(REF(methodSym))
238-
)
239-
}
159+
REF(methodSym) === (REF(ensureAccessibleMethod) APPLY (methodSymRHS)),
160+
REF(reflPolyCacheSym) === gen.mkSoftRef(cacheRHS),
161+
Return(REF(methodSym))
240162
)
241-
})
242-
163+
}
164+
)
165+
})
243166
}
244167

245168
/* ### HANDLING METHODS NORMALLY COMPILED TO OPERATORS ### */
@@ -394,99 +317,75 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
394317
}
395318
}
396319

397-
if (settings.refinementMethodDispatch.value == "invoke-dynamic") {
398-
/* val guardCallSite: Tree = {
399-
val cachedClass = addStaticVariableToClass("cachedClass", definitions.ClassClass.tpe, EmptyTree)
400-
val tmpVar = currentOwner.newVariable(ad.pos, unit.freshTermName(ad.pos, "x")).setInfo(definitions.AnyRefClass.tpe)
401-
atPos(ad.pos)(Block(List(
402-
ValDef(tmpVar, transform(qual))),
403-
If(Apply(Select(gen.mkAttributedRef(cachedClass), nme.EQ), List(getClass(Ident(tmpVar)))),
404-
Block(List(Assign(gen.mkAttributedRef(cachedClass), getClass(Ident(tmpVar)))),
405-
treeCopy.ApplyDynamic(ad, Ident(tmpVar), transformTrees(params))),
406-
EmptyTree)))
407-
}
408-
//println(guardCallSite)
409-
*/
410-
localTyper.typed(treeCopy.ApplyDynamic(ad, transform(qual), transformTrees(params)))
411-
}
412-
else {
413-
414-
/* ### BODY OF THE TRANSFORMATION -> remember we're in case ad@ApplyDynamic(qual, params) ### */
415-
416-
/* This creates the tree that does the reflective call (see general comment
417-
* on the apply-dynamic tree for its format). This tree is simply composed
418-
* of three successive calls, first to getClass on the callee, then to
419-
* getMethod on the class, then to invoke on the method.
420-
* - getMethod needs an array of classes for choosing one amongst many
421-
* overloaded versions of the method. This is provided by paramTypeClasses
422-
* and must be done on the static type as Scala's dispatching is static on
423-
* the parameters.
424-
* - invoke needs an array of AnyRefs that are the method's arguments. The
425-
* erasure phase guarantees that any parameter passed to a dynamic apply
426-
* is compatible (through boxing). Boxed ints et al. is what invoke expects
427-
* when the applied method expects ints, hence no change needed there.
428-
* - in the end, the result of invoke must be fixed, again to deal with arrays.
429-
* This is provided by fixResult. fixResult will cast the invocation's result
430-
* to the method's return type, which is generally ok, except when this type
431-
* is a value type (int et al.) in which case it must cast to the boxed version
432-
* because invoke only returns object and erasure made sure the result is
433-
* expected to be an AnyRef. */
434-
val t: Tree = {
435-
val (mparams, resType) = ad.symbol.tpe match {
436-
case MethodType(mparams, resType) =>
437-
assert(params.length == mparams.length, ((params, mparams)))
438-
(mparams, resType)
439-
case tpe @ OverloadedType(pre, alts) =>
440-
unit.warning(ad.pos, s"Overloaded type reached the backend! This is a bug in scalac.\n Symbol: ${ad.symbol}\n Overloads: $tpe\n Arguments: " + ad.args.map(_.tpe))
441-
alts filter (_.paramss.flatten.size == params.length) map (_.tpe) match {
442-
case mt @ MethodType(mparams, resType) :: Nil =>
443-
unit.warning(NoPosition, "Only one overload has the right arity, proceeding with overload " + mt)
444-
(mparams, resType)
445-
case _ =>
446-
unit.error(ad.pos, "Cannot resolve overload.")
447-
(Nil, NoType)
448-
}
449-
}
450-
typedPos {
451-
val sym = currentOwner.newValue(mkTerm("qual"), ad.pos) setInfo qual0.tpe
452-
qual = REF(sym)
453-
454-
BLOCK(
455-
VAL(sym) === qual0,
456-
callAsReflective(mparams map (_.tpe), resType)
457-
)
458-
}
320+
{
321+
322+
/* ### BODY OF THE TRANSFORMATION -> remember we're in case ad@ApplyDynamic(qual, params) ### */
323+
324+
/* This creates the tree that does the reflective call (see general comment
325+
* on the apply-dynamic tree for its format). This tree is simply composed
326+
* of three successive calls, first to getClass on the callee, then to
327+
* getMethod on the class, then to invoke on the method.
328+
* - getMethod needs an array of classes for choosing one amongst many
329+
* overloaded versions of the method. This is provided by paramTypeClasses
330+
* and must be done on the static type as Scala's dispatching is static on
331+
* the parameters.
332+
* - invoke needs an array of AnyRefs that are the method's arguments. The
333+
* erasure phase guarantees that any parameter passed to a dynamic apply
334+
* is compatible (through boxing). Boxed ints et al. is what invoke expects
335+
* when the applied method expects ints, hence no change needed there.
336+
* - in the end, the result of invoke must be fixed, again to deal with arrays.
337+
* This is provided by fixResult. fixResult will cast the invocation's result
338+
* to the method's return type, which is generally ok, except when this type
339+
* is a value type (int et al.) in which case it must cast to the boxed version
340+
* because invoke only returns object and erasure made sure the result is
341+
* expected to be an AnyRef. */
342+
val t: Tree = {
343+
val (mparams, resType) = ad.symbol.tpe match {
344+
case MethodType(mparams, resType) =>
345+
assert(params.length == mparams.length, ((params, mparams)))
346+
(mparams, resType)
347+
case tpe @ OverloadedType(pre, alts) =>
348+
unit.warning(ad.pos, s"Overloaded type reached the backend! This is a bug in scalac.\n Symbol: ${ad.symbol}\n Overloads: $tpe\n Arguments: " + ad.args.map(_.tpe))
349+
alts filter (_.paramss.flatten.size == params.length) map (_.tpe) match {
350+
case mt @ MethodType(mparams, resType) :: Nil =>
351+
unit.warning(NoPosition, "Only one overload has the right arity, proceeding with overload " + mt)
352+
(mparams, resType)
353+
case _ =>
354+
unit.error(ad.pos, "Cannot resolve overload.")
355+
(Nil, NoType)
356+
}
459357
}
358+
typedPos {
359+
val sym = currentOwner.newValue(mkTerm("qual"), ad.pos) setInfo qual0.tpe
360+
qual = REF(sym)
460361

461-
/* For testing purposes, the dynamic application's condition
462-
* can be printed-out in great detail. Remove? */
463-
if (settings.debug) {
464-
def paramsToString(xs: Any*) = xs map (_.toString) mkString ", "
465-
val mstr = ad.symbol.tpe match {
466-
case MethodType(mparams, resType) =>
467-
"""| with
468-
| - declared parameter types: '%s'
469-
| - passed argument types: '%s'
470-
| - result type: '%s'""" .
471-
stripMargin.format(
472-
paramsToString(mparams),
473-
paramsToString(params),
474-
resType.toString
475-
)
476-
case _ => ""
477-
}
478-
log(
479-
"""Dynamically application '%s.%s(%s)' %s - resulting code: '%s'""".format(
480-
qual, ad.symbol.name, paramsToString(params), mstr, t
481-
)
362+
BLOCK(
363+
VAL(sym) === qual0,
364+
callAsReflective(mparams map (_.tpe), resType)
482365
)
483366
}
367+
}
484368

485-
/* We return the dynamic call tree, after making sure no other
486-
* clean-up transformation are to be applied on it. */
487-
transform(t)
369+
/* For testing purposes, the dynamic application's condition
370+
* can be printed-out in great detail. Remove? */
371+
if (settings.debug) {
372+
def paramsToString(xs: Any*) = xs map (_.toString) mkString ", "
373+
val mstr = ad.symbol.tpe match {
374+
case MethodType(mparams, resType) =>
375+
sm"""| with
376+
| - declared parameter types: '${paramsToString(mparams)}'
377+
| - passed argument types: '${paramsToString(params)}'
378+
| - result type: '${resType.toString}'"""
379+
case _ => ""
380+
}
381+
log(s"""Dynamically application '$qual.${ad.symbol.name}(${paramsToString(params)})' $mstr - resulting code: '$t'""")
488382
}
489-
/* ### END OF DYNAMIC APPLY TRANSFORM ### */
383+
384+
/* We return the dynamic call tree, after making sure no other
385+
* clean-up transformation are to be applied on it. */
386+
transform(t)
387+
/* ### END OF DYNAMIC APPLY TRANSFORM ### */
388+
}
490389
}
491390

492391
override def transform(tree: Tree): Tree = tree match {

0 commit comments

Comments
 (0)