New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Typechecking TypTrees is not idempotent #8123
Comments
Imported From: https://issues.scala-lang.org/browse/SI-8123?orig=1 |
@retronym said (edited on Jan 7, 2014 4:09:16 PM UTC): Here's what I suggested: import scala.language.experimental.macros
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.{ universe => ru }
import scala.tools.reflect.ToolBox
class A[T] {
def foo(lam: T => Any): Unit = macro FooMacros.fooImpl[T]
}
object B {
def fooTree[T](lam: T => Any, expr: ru.Expr[T => Any], tp: ru.TypeTag[T]): Unit = {
// no inferred types in the trees
println(expr)
expr.tree.asInstanceOf[ru.FunctionApi].vparams.foreach(t => println((t, t.tpe)))
// Approach 1: make a copy of the Function node with `Trade` as the explicit parameter
// type
import ru._
val tree2 = expr.tree match {
case Function(param :: Nil, body) =>
val newTpt = TypeTree(tp.tpe)
Function(treeCopy.ValDef(param, param.mods, param.name, newTpt, param.rhs) :: Nil, body)
}
println(tree2)
// Approach 2: typecheck the tree with the original expected type of `Trade => Any`.
import scala.tools.reflect.ToolBox
val tb = reflect.runtime.currentMirror.mkToolBox()
implicit val implicitTT = tp
val typed = tb.typeCheck(expr.tree, pt = typeOf[T => Any])
println(s"typed: $typed")
}
}
object FooMacros {
import scala.reflect.macros.Context
def fooImpl[T: c.WeakTypeTag](c: Context)(lam: c.Expr[T => Any]): c.Expr[Unit] = {
import c.{ universe => cu }
val fExpr = c.Expr[ru.Expr[T => Any]](c.reifyTree(cu.treeBuild.mkRuntimeUniverseRef, cu.EmptyTree, c.typeCheck(lam.tree)))
// reify (by design) won't carry inferred types to the next meta-level. Instead, we reify the inferred
// type argument of the macro application, and transport that up a level. This enables us to typecheck
// at that level and recover the inferred types.
val fType = c.reifyType(cu.treeBuild.mkRuntimeUniverseRef, cu.EmptyTree, cu.weakTypeOf[T])
cu.reify { B.fooTree(lam.splice, fExpr.splice, c.Expr[scala.reflect.runtime.universe.TypeTag[T]](fType).splice ) }
}
} So that: This would be able to reconstitute the inferred type: class Trade(val id: String, val symbol: String, val amount: Int)
object FreeVarTest {
val c = "xx"
def main(args: Array[String]): Unit = {
var freevar = "ms"
val am = 20
val a = new A[Trade]
println("try more")
a.foo(t => t.id == freevar && (t.amount < am || t.symbol != c))
}
}
The original code didn't use |
@xeno-by said: It wouldn't work in general case, when someone assigned crazy symbols to some parts of a TypeTree using a macro or a compiler plugin, but within the compiler and for well-behaving macros, I believe, it should work fine. I'm going to explore this avenue for #8066. |
@xeno-by said (edited on Jan 7, 2014 4:21:06 PM UTC): |
@retronym said (edited on Jan 7, 2014 5:17:52 PM UTC): |
consolidating under #5464 |
It is sort of well-known that TypeTrees are very touchy (e.g. see https://github.com/scala/scala/blob/527fd9aea58cf5c1b8f638d0321a8d0947d2916a/src/compiler/scala/reflect/reify/phases/Reshape.scala#L119), but I haven't found any issues exposing that.
The text was updated successfully, but these errors were encountered: