Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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.
  • Loading branch information...
commit b11324a5e41b9ece60f4eac75180ffaa6a903d76 1 parent bf3c44c
@soc soc authored
View
2  src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -163,8 +163,6 @@ trait ScalaSettings extends AbsScalaSettings
val Ystatistics = BooleanSetting ("-Ystatistics", "Print compiler statistics.") andThen (scala.reflect.internal.util.Statistics.enabled = _)
val stopAfter = PhasesSetting ("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat
val stopBefore = PhasesSetting ("-Ystop-before", "Stop before")
- val refinementMethodDispatch
- = ChoiceSetting ("-Ystruct-dispatch", "policy", "structural method dispatch policy", List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache")
val Yrangepos = BooleanSetting ("-Yrangepos", "Use range positions for syntax trees.")
val Ymemberpos = StringSetting ("-Yshow-member-pos", "output style", "Show start and end positions of members", "") withPostSetHook (_ => Yrangepos.value = true)
val Yreifycopypaste = BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.")
View
323 src/compiler/scala/tools/nsc/transform/CleanUp.scala
@@ -47,15 +47,6 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
private var localTyper: analyzer.Typer = null
- private object MethodDispatchType extends scala.Enumeration {
- val NO_CACHE, MONO_CACHE, POLY_CACHE = Value
- }
- import MethodDispatchType.{ NO_CACHE, MONO_CACHE, POLY_CACHE }
- private def dispatchType() = settings.refinementMethodDispatch.value match {
- case "no-cache" => NO_CACHE
- case "mono-cache" => MONO_CACHE
- case "poly-cache" => POLY_CACHE
- }
private def typedWithPos(pos: Position)(tree: Tree) =
localTyper.typedPos(pos)(tree)
@@ -113,133 +104,65 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
def fromTypesToClassArrayLiteral(paramTypes: List[Type]): Tree =
ArrayValue(TypeTree(ClassClass.tpe), paramTypes map LIT)
- /* ... */
- def reflectiveMethodCache(method: String, paramTypes: List[Type]): Symbol = dispatchType() match {
- case NO_CACHE =>
-
- /* Implementation of the cache is as follows for method "def xyz(a: A, b: B)":
-
- var reflParams$Cache: Array[Class[_]] = Array[JClass](classOf[A], classOf[B])
-
- def reflMethod$Method(forReceiver: JClass[_]): JMethod =
- forReceiver.getMethod("xyz", reflParams$Cache)
-
- */
-
- val reflParamsCacheSym: Symbol =
- addStaticVariableToClass(nme.reflParamsCacheName, arrayType(ClassClass.tpe), fromTypesToClassArrayLiteral(paramTypes), true)
-
- addStaticMethodToClass((_, forReceiverSym) =>
- gen.mkMethodCall(REF(forReceiverSym), Class_getMethod, Nil, List(LIT(method), REF(reflParamsCacheSym)))
- )
-
- case MONO_CACHE =>
-
- /* Implementation of the cache is as follows for method "def xyz(a: A, b: B)"
- (but with a SoftReference wrapping reflClass$Cache, similarly in the poly Cache) :
-
- var reflParams$Cache: Array[Class[_]] = Array[JClass](classOf[A], classOf[B])
-
- var reflMethod$Cache: JMethod = null
-
- var reflClass$Cache: JClass[_] = null
-
- def reflMethod$Method(forReceiver: JClass[_]): JMethod = {
- if (reflClass$Cache != forReceiver) {
- reflMethod$Cache = forReceiver.getMethod("xyz", reflParams$Cache)
- reflClass$Cache = forReceiver
- }
- reflMethod$Cache
- }
-
- */
-
- val reflParamsCacheSym: Symbol =
- addStaticVariableToClass(nme.reflParamsCacheName, arrayType(ClassClass.tpe), fromTypesToClassArrayLiteral(paramTypes), true)
-
- val reflMethodCacheSym: Symbol =
- addStaticVariableToClass(nme.reflMethodCacheName, MethodClass.tpe, NULL, false)
-
- val reflClassCacheSym: Symbol =
- addStaticVariableToClass(nme.reflClassCacheName, SoftReferenceClass.tpe, NULL, false)
-
- def isCacheEmpty(receiver: Symbol): Tree =
- reflClassCacheSym.IS_NULL() OR (reflClassCacheSym.GET() OBJ_NE REF(receiver))
-
- addStaticMethodToClass((_, forReceiverSym) =>
- BLOCK(
- IF (isCacheEmpty(forReceiverSym)) THEN BLOCK(
- REF(reflMethodCacheSym) === ((REF(forReceiverSym) DOT Class_getMethod)(LIT(method), REF(reflParamsCacheSym))) ,
- REF(reflClassCacheSym) === gen.mkSoftRef(REF(forReceiverSym)),
- UNIT
- ) ENDIF,
- REF(reflMethodCacheSym)
- )
- )
-
- case POLY_CACHE =>
+ def reflectiveMethodCache(method: String, paramTypes: List[Type]): Symbol = {
+ /* Implementation of the cache is as follows for method "def xyz(a: A, b: B)"
+ (SoftReference so that it does not interfere with classloader garbage collection,
+ see ticket #2365 for details):
- /* Implementation of the cache is as follows for method "def xyz(a: A, b: B)"
- (SoftReference so that it does not interfere with classloader garbage collection, see ticket
- #2365 for details):
+ var reflParams$Cache: Array[Class[_]] = Array[JClass](classOf[A], classOf[B])
- var reflParams$Cache: Array[Class[_]] = Array[JClass](classOf[A], classOf[B])
+ var reflPoly$Cache: SoftReference[scala.runtime.MethodCache] = new SoftReference(new EmptyMethodCache())
- var reflPoly$Cache: SoftReference[scala.runtime.MethodCache] = new SoftReference(new EmptyMethodCache())
+ def reflMethod$Method(forReceiver: JClass[_]): JMethod = {
+ var methodCache: MethodCache = reflPoly$Cache.find(forReceiver)
+ if (methodCache eq null) {
+ methodCache = new EmptyMethodCache
+ reflPoly$Cache = new SoftReference(methodCache)
+ }
+ var method: JMethod = methodCache.find(forReceiver)
+ if (method ne null)
+ return method
+ else {
+ method = ScalaRunTime.ensureAccessible(forReceiver.getMethod("xyz", reflParams$Cache))
+ reflPoly$Cache = new SoftReference(methodCache.add(forReceiver, method))
+ return method
+ }
+ }
+ */
- def reflMethod$Method(forReceiver: JClass[_]): JMethod = {
- var methodCache: MethodCache = reflPoly$Cache.find(forReceiver)
- if (methodCache eq null) {
- methodCache = new EmptyMethodCache
- reflPoly$Cache = new SoftReference(methodCache)
- }
- var method: JMethod = methodCache.find(forReceiver)
- if (method ne null)
- return method
- else {
- method = ScalaRunTime.ensureAccessible(forReceiver.getMethod("xyz", reflParams$Cache))
- reflPoly$Cache = new SoftReference(methodCache.add(forReceiver, method))
- return method
- }
- }
+ val reflParamsCacheSym: Symbol =
+ addStaticVariableToClass(nme.reflParamsCacheName, arrayType(ClassClass.tpe), fromTypesToClassArrayLiteral(paramTypes), true)
- */
+ def mkNewPolyCache = gen.mkSoftRef(NEW(TypeTree(EmptyMethodCacheClass.tpe)))
+ val reflPolyCacheSym: Symbol = addStaticVariableToClass(nme.reflPolyCacheName, SoftReferenceClass.tpe, mkNewPolyCache, false)
- val reflParamsCacheSym: Symbol =
- addStaticVariableToClass(nme.reflParamsCacheName, arrayType(ClassClass.tpe), fromTypesToClassArrayLiteral(paramTypes), true)
+ def getPolyCache = gen.mkCast(fn(REF(reflPolyCacheSym), nme.get), MethodCacheClass.tpe)
- def mkNewPolyCache = gen.mkSoftRef(NEW(TypeTree(EmptyMethodCacheClass.tpe)))
- val reflPolyCacheSym: Symbol = (
- addStaticVariableToClass(nme.reflPolyCacheName, SoftReferenceClass.tpe, mkNewPolyCache, false)
- )
- def getPolyCache = gen.mkCast(fn(REF(reflPolyCacheSym), nme.get), MethodCacheClass.tpe)
+ addStaticMethodToClass((reflMethodSym, forReceiverSym) => {
+ val methodCache = reflMethodSym.newVariable(mkTerm("methodCache"), ad.pos) setInfo MethodCacheClass.tpe
+ val methodSym = reflMethodSym.newVariable(mkTerm("method"), ad.pos) setInfo MethodClass.tpe
- addStaticMethodToClass((reflMethodSym, forReceiverSym) => {
- val methodCache = reflMethodSym.newVariable(mkTerm("methodCache"), ad.pos) setInfo MethodCacheClass.tpe
- val methodSym = reflMethodSym.newVariable(mkTerm("method"), ad.pos) setInfo MethodClass.tpe
+ BLOCK(
+ VAL(methodCache) === getPolyCache,
+ IF (REF(methodCache) OBJ_EQ NULL) THEN BLOCK(
+ REF(methodCache) === NEW(TypeTree(EmptyMethodCacheClass.tpe)),
+ REF(reflPolyCacheSym) === gen.mkSoftRef(REF(methodCache))
+ ) ENDIF,
+ VAL(methodSym) === (REF(methodCache) DOT methodCache_find)(REF(forReceiverSym)),
+ IF (REF(methodSym) OBJ_NE NULL) .
+ THEN (Return(REF(methodSym)))
+ ELSE {
+ def methodSymRHS = ((REF(forReceiverSym) DOT Class_getMethod)(LIT(method), REF(reflParamsCacheSym)))
+ def cacheRHS = ((REF(methodCache) DOT methodCache_add)(REF(forReceiverSym), REF(methodSym)))
BLOCK(
- VAL(methodCache) === getPolyCache,
- IF (REF(methodCache) OBJ_EQ NULL) THEN BLOCK(
- REF(methodCache) === NEW(TypeTree(EmptyMethodCacheClass.tpe)),
- REF(reflPolyCacheSym) === gen.mkSoftRef(REF(methodCache))
- ) ENDIF,
-
- VAL(methodSym) === (REF(methodCache) DOT methodCache_find)(REF(forReceiverSym)),
- IF (REF(methodSym) OBJ_NE NULL) .
- THEN (Return(REF(methodSym)))
- ELSE {
- def methodSymRHS = ((REF(forReceiverSym) DOT Class_getMethod)(LIT(method), REF(reflParamsCacheSym)))
- def cacheRHS = ((REF(methodCache) DOT methodCache_add)(REF(forReceiverSym), REF(methodSym)))
- BLOCK(
- REF(methodSym) === (REF(ensureAccessibleMethod) APPLY (methodSymRHS)),
- REF(reflPolyCacheSym) === gen.mkSoftRef(cacheRHS),
- Return(REF(methodSym))
- )
- }
+ REF(methodSym) === (REF(ensureAccessibleMethod) APPLY (methodSymRHS)),
+ REF(reflPolyCacheSym) === gen.mkSoftRef(cacheRHS),
+ Return(REF(methodSym))
)
- })
-
+ }
+ )
+ })
}
/* ### HANDLING METHODS NORMALLY COMPILED TO OPERATORS ### */
@@ -394,99 +317,75 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
}
}
- if (settings.refinementMethodDispatch.value == "invoke-dynamic") {
-/* val guardCallSite: Tree = {
- val cachedClass = addStaticVariableToClass("cachedClass", definitions.ClassClass.tpe, EmptyTree)
- val tmpVar = currentOwner.newVariable(ad.pos, unit.freshTermName(ad.pos, "x")).setInfo(definitions.AnyRefClass.tpe)
- atPos(ad.pos)(Block(List(
- ValDef(tmpVar, transform(qual))),
- If(Apply(Select(gen.mkAttributedRef(cachedClass), nme.EQ), List(getClass(Ident(tmpVar)))),
- Block(List(Assign(gen.mkAttributedRef(cachedClass), getClass(Ident(tmpVar)))),
- treeCopy.ApplyDynamic(ad, Ident(tmpVar), transformTrees(params))),
- EmptyTree)))
- }
- //println(guardCallSite)
-*/
- localTyper.typed(treeCopy.ApplyDynamic(ad, transform(qual), transformTrees(params)))
- }
- else {
-
- /* ### BODY OF THE TRANSFORMATION -> remember we're in case ad@ApplyDynamic(qual, params) ### */
-
- /* This creates the tree that does the reflective call (see general comment
- * on the apply-dynamic tree for its format). This tree is simply composed
- * of three successive calls, first to getClass on the callee, then to
- * getMethod on the class, then to invoke on the method.
- * - getMethod needs an array of classes for choosing one amongst many
- * overloaded versions of the method. This is provided by paramTypeClasses
- * and must be done on the static type as Scala's dispatching is static on
- * the parameters.
- * - invoke needs an array of AnyRefs that are the method's arguments. The
- * erasure phase guarantees that any parameter passed to a dynamic apply
- * is compatible (through boxing). Boxed ints et al. is what invoke expects
- * when the applied method expects ints, hence no change needed there.
- * - in the end, the result of invoke must be fixed, again to deal with arrays.
- * This is provided by fixResult. fixResult will cast the invocation's result
- * to the method's return type, which is generally ok, except when this type
- * is a value type (int et al.) in which case it must cast to the boxed version
- * because invoke only returns object and erasure made sure the result is
- * expected to be an AnyRef. */
- val t: Tree = {
- val (mparams, resType) = ad.symbol.tpe match {
- case MethodType(mparams, resType) =>
- assert(params.length == mparams.length, ((params, mparams)))
- (mparams, resType)
- case tpe @ OverloadedType(pre, alts) =>
- 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))
- alts filter (_.paramss.flatten.size == params.length) map (_.tpe) match {
- case mt @ MethodType(mparams, resType) :: Nil =>
- unit.warning(NoPosition, "Only one overload has the right arity, proceeding with overload " + mt)
- (mparams, resType)
- case _ =>
- unit.error(ad.pos, "Cannot resolve overload.")
- (Nil, NoType)
- }
- }
- typedPos {
- val sym = currentOwner.newValue(mkTerm("qual"), ad.pos) setInfo qual0.tpe
- qual = REF(sym)
-
- BLOCK(
- VAL(sym) === qual0,
- callAsReflective(mparams map (_.tpe), resType)
- )
- }
+ {
+
+ /* ### BODY OF THE TRANSFORMATION -> remember we're in case ad@ApplyDynamic(qual, params) ### */
+
+ /* This creates the tree that does the reflective call (see general comment
+ * on the apply-dynamic tree for its format). This tree is simply composed
+ * of three successive calls, first to getClass on the callee, then to
+ * getMethod on the class, then to invoke on the method.
+ * - getMethod needs an array of classes for choosing one amongst many
+ * overloaded versions of the method. This is provided by paramTypeClasses
+ * and must be done on the static type as Scala's dispatching is static on
+ * the parameters.
+ * - invoke needs an array of AnyRefs that are the method's arguments. The
+ * erasure phase guarantees that any parameter passed to a dynamic apply
+ * is compatible (through boxing). Boxed ints et al. is what invoke expects
+ * when the applied method expects ints, hence no change needed there.
+ * - in the end, the result of invoke must be fixed, again to deal with arrays.
+ * This is provided by fixResult. fixResult will cast the invocation's result
+ * to the method's return type, which is generally ok, except when this type
+ * is a value type (int et al.) in which case it must cast to the boxed version
+ * because invoke only returns object and erasure made sure the result is
+ * expected to be an AnyRef. */
+ val t: Tree = {
+ val (mparams, resType) = ad.symbol.tpe match {
+ case MethodType(mparams, resType) =>
+ assert(params.length == mparams.length, ((params, mparams)))
+ (mparams, resType)
+ case tpe @ OverloadedType(pre, alts) =>
+ 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))
+ alts filter (_.paramss.flatten.size == params.length) map (_.tpe) match {
+ case mt @ MethodType(mparams, resType) :: Nil =>
+ unit.warning(NoPosition, "Only one overload has the right arity, proceeding with overload " + mt)
+ (mparams, resType)
+ case _ =>
+ unit.error(ad.pos, "Cannot resolve overload.")
+ (Nil, NoType)
+ }
}
+ typedPos {
+ val sym = currentOwner.newValue(mkTerm("qual"), ad.pos) setInfo qual0.tpe
+ qual = REF(sym)
- /* For testing purposes, the dynamic application's condition
- * can be printed-out in great detail. Remove? */
- if (settings.debug) {
- def paramsToString(xs: Any*) = xs map (_.toString) mkString ", "
- val mstr = ad.symbol.tpe match {
- case MethodType(mparams, resType) =>
- """| with
- | - declared parameter types: '%s'
- | - passed argument types: '%s'
- | - result type: '%s'""" .
- stripMargin.format(
- paramsToString(mparams),
- paramsToString(params),
- resType.toString
- )
- case _ => ""
- }
- log(
- """Dynamically application '%s.%s(%s)' %s - resulting code: '%s'""".format(
- qual, ad.symbol.name, paramsToString(params), mstr, t
- )
+ BLOCK(
+ VAL(sym) === qual0,
+ callAsReflective(mparams map (_.tpe), resType)
)
}
+ }
- /* We return the dynamic call tree, after making sure no other
- * clean-up transformation are to be applied on it. */
- transform(t)
+ /* For testing purposes, the dynamic application's condition
+ * can be printed-out in great detail. Remove? */
+ if (settings.debug) {
+ def paramsToString(xs: Any*) = xs map (_.toString) mkString ", "
+ val mstr = ad.symbol.tpe match {
+ case MethodType(mparams, resType) =>
+ sm"""| with
+ | - declared parameter types: '${paramsToString(mparams)}'
+ | - passed argument types: '${paramsToString(params)}'
+ | - result type: '${resType.toString}'"""
+ case _ => ""
+ }
+ log(s"""Dynamically application '$qual.${ad.symbol.name}(${paramsToString(params)})' $mstr - resulting code: '$t'""")
}
- /* ### END OF DYNAMIC APPLY TRANSFORM ### */
+
+ /* We return the dynamic call tree, after making sure no other
+ * clean-up transformation are to be applied on it. */
+ transform(t)
+ /* ### END OF DYNAMIC APPLY TRANSFORM ### */
+ }
}
override def transform(tree: Tree): Tree = tree match {
View
5 src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -5094,10 +5094,9 @@ trait Typers extends Adaptations with Tags {
def typedApplyDynamic(tree: ApplyDynamic) = {
assert(phase.erasedTypes)
- val reflectiveCalls = !(settings.refinementMethodDispatch.value == "invoke-dynamic")
val qual1 = typed(tree.qual, AnyRefClass.tpe)
- val args1 = tree.args mapConserve (arg => if (reflectiveCalls) typed(arg, AnyRefClass.tpe) else typed(arg))
- treeCopy.ApplyDynamic(tree, qual1, args1) setType (if (reflectiveCalls) AnyRefClass.tpe else tree.symbol.info.resultType)
+ val args1 = tree.args mapConserve (arg => typed(arg, AnyRefClass.tpe))
+ treeCopy.ApplyDynamic(tree, qual1, args1) setType AnyRefClass.tpe
}
def typedReferenceToBoxed(tree: ReferenceToBoxed) = {
Please sign in to comment.
Something went wrong with that request. Please try again.