Skip to content
This repository
Browse code

accommodates pull request feedback

  • Loading branch information...
commit 57c0e63ba18c5845772d737570342e4054af459f 1 parent ce867c7
Eugene Burmako xeno-by authored
31 src/compiler/scala/reflect/reify/codegen/GenSymbols.scala
... ... @@ -1,6 +1,8 @@
1 1 package scala.reflect.reify
2 2 package codegen
3 3
  4 +import scala.reflect.internal.Flags._
  5 +
4 6 trait GenSymbols {
5 7 self: Reifier =>
6 8
@@ -100,8 +102,33 @@ trait GenSymbols {
100 102 reifyIntoSymtab(binding.symbol) { sym =>
101 103 if (reifyDebug) println("Free term" + (if (sym.isCapturedVariable) " (captured)" else "") + ": " + sym + "(" + sym.accurateKindString + ")")
102 104 val name = newTermName(nme.REIFY_FREE_PREFIX + sym.name + (if (sym.isType) nme.REIFY_FREE_THIS_SUFFIX else ""))
103   - // Flag <stable> is set here to make reified free value pass stability test during reflective compilation
104   - if (!sym.isMutable) sym setFlag reflect.internal.Flags.STABLE
  105 + // We need to note whether the free value being reified is stable or not to guide subsequent reflective compilation.
  106 + // Here's why reflection compilation needs our help.
  107 + //
  108 + // When dealing with a tree, which contain free values, toolboxes extract those and wrap the entire tree in a Function
  109 + // having parameters defined for every free values in the tree. For example, evaluating
  110 + //
  111 + // Ident(setTypeSignature(newFreeTerm("x", 2), <Int>))
  112 + //
  113 + // Will generate something like
  114 + //
  115 + // object wrapper {
  116 + // def wrapper(x: () => Int) = {
  117 + // x()
  118 + // }
  119 + // }
  120 + //
  121 + // Note that free values get transformed into, effectively, by-name parameters. This is done to make sure
  122 + // that evaluation order is kept intact. And indeed, we cannot just evaluate all free values at once in order
  123 + // to obtain arguments for wrapper.wrapper, because if some of the free values end up being unused during evaluation,
  124 + // we might end up doing unnecessary calculations.
  125 + //
  126 + // So far, so good - we didn't need any flags at all. However, if the code being reified contains path-dependent types,
  127 + // we're in trouble, because valid code like `free.T` ends up being transformed into `free.apply().T`, which won't compile.
  128 + //
  129 + // To overcome this glitch, we note whether a given free term is stable or not (because vars can also end up being free terms).
  130 + // Then, if a free term is stable, we tell the compiler to treat `free.apply()` specially and assume that it's stable.
  131 + if (!sym.isMutable) sym setFlag STABLE
105 132 if (sym.isCapturedVariable) {
106 133 assert(binding.isInstanceOf[Ident], showRaw(binding))
107 134 val capturedBinding = referenceCapturedVariable(sym)
9 src/compiler/scala/tools/reflect/ToolBoxFactory.scala
@@ -8,7 +8,7 @@ import scala.tools.nsc.typechecker.Modes
8 8 import scala.tools.nsc.io.VirtualDirectory
9 9 import scala.tools.nsc.interpreter.AbstractFileClassLoader
10 10 import scala.tools.nsc.util.FreshNameCreator
11   -import scala.reflect.internal.Flags
  11 +import scala.reflect.internal.Flags._
12 12 import scala.reflect.internal.util.{BatchSourceFile, NoSourceFile, NoFile}
13 13 import java.lang.{Class => jClass}
14 14 import scala.compat.Platform.EOL
@@ -211,12 +211,9 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
211 211
212 212 val meth = obj.moduleClass.newMethod(newTermName(wrapperMethodName))
213 213 def makeParam(schema: (FreeTermSymbol, TermName)) = {
  214 + // see a detailed explanation of the STABLE trick in `GenSymbols.reifyFreeTerm`
214 215 val (fv, name) = schema
215   - /* Free term symbol `fv` can contain flag <stable> which was set by
216   - * `scala.reflect.reify.codegen.GenSymbols.reifyFreeTerm` method.
217   - * It is recovered here, so value parameter can pass `isExprSafeToInline`
218   - * test in `scala.reflect.internal.TreeInfo`. */
219   - meth.newValueParameter(name, newFlags = if (fv.hasStableFlag) Flags.STABLE else 0) setInfo appliedType(definitions.FunctionClass(0).tpe, List(fv.tpe.resultType))
  216 + meth.newValueParameter(name, newFlags = if (fv.hasStableFlag) STABLE else 0) setInfo appliedType(definitions.FunctionClass(0).tpe, List(fv.tpe.resultType))
220 217 }
221 218 meth setInfo MethodType(freeTerms.map(makeParam).toList, AnyClass.tpe)
222 219 minfo.decls enter meth
15 src/reflect/scala/reflect/internal/TreeInfo.scala
@@ -89,18 +89,15 @@ abstract class TreeInfo {
89 89 tree.symbol.isStable && isExprSafeToInline(qual)
90 90 case TypeApply(fn, _) =>
91 91 isExprSafeToInline(fn)
92   - /* Special case for reified free terms. During reflective compilation,
93   - * reified value recovered flag <stable> from free term and wrapped into a
94   - * Function object, so it can pass stability check here to become a stable
95   - * identifier.*/
96 92 case Apply(Select(free @ Ident(_), nme.apply), _) if free.symbol.name endsWith nme.REIFY_FREE_VALUE_SUFFIX =>
  93 + // see a detailed explanation of this trick in `GenSymbols.reifyFreeTerm`
97 94 free.symbol.hasStableFlag && isExprSafeToInline(free)
98 95 case Apply(fn, List()) =>
99   - /* Note: After uncurry, field accesses are represented as Apply(getter, Nil),
100   - * so an Apply can also be pure.
101   - * However, before typing, applications of nullary functional values are also
102   - * Apply(function, Nil) trees. To prevent them from being treated as pure,
103   - * we check that the callee is a method. */
  96 + // Note: After uncurry, field accesses are represented as Apply(getter, Nil),
  97 + // so an Apply can also be pure.
  98 + // However, before typing, applications of nullary functional values are also
  99 + // Apply(function, Nil) trees. To prevent them from being treated as pure,
  100 + // we check that the callee is a method.
104 101 fn.symbol.isMethod && !fn.symbol.isLazy && isExprSafeToInline(fn)
105 102 case Typed(expr, _) =>
106 103 isExprSafeToInline(expr)
4 test/files/run/t6591_7.check
... ... @@ -0,0 +1,4 @@
  1 +name = x, stable = true
  2 +name = y, stable = true
  3 +name = z, stable = false
  4 +name = C, stable = true
26 test/files/run/t6591_7.scala
... ... @@ -0,0 +1,26 @@
  1 +import scala.reflect.runtime.universe._
  2 +import scala.tools.reflect.Eval
  3 +
  4 +object Test extends App {
  5 + locally {
  6 + val x = 2
  7 + def y = 3
  8 + var z = 4
  9 + class C {
  10 + var w = 5
  11 + locally {
  12 + val expr = reify(x + y + z + w)
  13 + // blocked by SI-7103, though it's not the focus of this test
  14 + // therefore I'm just commenting out the evaluation
  15 + // println(expr.eval)
  16 + expr.tree.freeTerms foreach (ft => {
  17 + // blocked by SI-7104, though it's not the focus of this test
  18 + // therefore I'm just commenting out the call to typeSignature
  19 + // println(s"name = ${ft.name}, sig = ${ft.typeSignature}, stable = ${ft.isStable}")
  20 + println(s"name = ${ft.name}, stable = ${ft.isStable}")
  21 + })
  22 + }
  23 + }
  24 + new C()
  25 + }
  26 +}

0 comments on commit 57c0e63

Please sign in to comment.
Something went wrong with that request. Please try again.