Skip to content

Commit deac2f9

Browse files
committed
WIP
1 parent f6a44fe commit deac2f9

File tree

14 files changed

+129
-265
lines changed

14 files changed

+129
-265
lines changed

compiler/src/dotty/tools/dotc/interpreter/Interpreter.scala

Lines changed: 22 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -29,25 +29,40 @@ import java.lang.reflect.Method
2929
* previously compiled and is present in the classpath of the current context.
3030
*/
3131
class Interpreter(implicit ctx: Context) {
32-
import tpd._
33-
34-
type Env = Map[Symbol, Object]
3532

3633
private[this] val classLoader = {
3734
val urls = ctx.settings.classpath.value.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL)
3835
new URLClassLoader(urls, getClass.getClassLoader)
3936
}
4037

41-
/** Returns the interpreted result of interpreting the code represented by the tree.
38+
/** Returns the interpreted result of interpreting the code a call to the symbol with default arguments.
4239
* Return Some of the result or None if some error happen during the interpretation.
4340
*/
44-
def interpretTree[T](tree: Tree)(implicit ct: ClassTag[T]): Option[T] = {
41+
def interpretCallToSymbol[T](sym: Symbol)(implicit pos: Position, ct: ClassTag[T]): Option[T] = {
4542
try {
46-
interpretTreeImpl(tree, Map.empty) match {
43+
val clazz = loadClass(sym.owner.companionModule.fullName)(pos)
44+
val paramClasses = paramsSig(sym)
45+
val interpretedArgs = paramClasses.map { clazz =>
46+
if (clazz == classOf[Boolean]) false.asInstanceOf[Object]
47+
else if (clazz == classOf[Byte]) 0.toByte.asInstanceOf[Object]
48+
else if (clazz == classOf[Char]) 0.toChar.asInstanceOf[Object]
49+
else if (clazz == classOf[Short]) 0.asInstanceOf[Object]
50+
else if (clazz == classOf[Int]) 0.asInstanceOf[Object]
51+
else if (clazz == classOf[Long]) 0L.asInstanceOf[Object]
52+
else if (clazz == classOf[Float]) 0f.asInstanceOf[Object]
53+
else if (clazz == classOf[Double]) 0d.asInstanceOf[Object]
54+
else null
55+
}
56+
57+
val method = getMethod(clazz, sym.name, paramClasses)
58+
59+
val o = stopIfRuntimeException(method.invoke(null, interpretedArgs: _*))(pos)
60+
61+
o match {
4762
case obj: T => Some(obj)
4863
case obj =>
4964
// TODO upgrade to a full type tag check or something similar
50-
ctx.error(s"Interpreted tree returned a result of an unexpected type. Expected ${ct.runtimeClass} but was ${obj.getClass}", tree.pos)
65+
ctx.error(s"Interpreted tree returned a result of an unexpected type. Expected ${ct.runtimeClass} but was ${obj.getClass}", pos)
5166
None
5267
}
5368
} catch {
@@ -57,133 +72,6 @@ class Interpreter(implicit ctx: Context) {
5772
}
5873
}
5974

60-
def interpretCallToSymbol[T](sym: Symbol)(implicit pos: Position): T = {
61-
val clazz = loadClass(sym.owner.companionModule.fullName)
62-
val paramClasses = paramsSig(sym)
63-
val interpretedArgs = paramClasses.map(_ => 1.asInstanceOf[Object])
64-
println("----------------")
65-
println(clazz)
66-
println(paramClasses)
67-
println(interpretedArgs)
68-
println()
69-
println(sym.name)
70-
println()
71-
println()
72-
println()
73-
74-
val method =
75-
clazz.getMethod(sym.name.toString, paramClasses: _*)
76-
stopIfRuntimeException(method.invoke(null, interpretedArgs: _*)).asInstanceOf[T]
77-
}
78-
79-
/** Returns the interpreted result of interpreting the code represented by the tree.
80-
* Returns the result of the interpreted tree.
81-
*
82-
* If some error is encountered while interpreting a ctx.error is emitted and a StopInterpretation is thrown.
83-
*/
84-
private def interpretTreeImpl(tree: Tree, env: Env): Object = {
85-
// println(s"Interpreting:\n${tree.show}\n$env\n")
86-
87-
implicit val pos: Position = tree.pos
88-
89-
tree match {
90-
case Quoted(quotedTree) =>
91-
if (quotedTree.isTerm) new scala.quoted.Exprs.TreeExpr(quotedTree)
92-
else new scala.quoted.Types.TreeType(quotedTree)
93-
94-
case Literal(Constant(c)) => c.asInstanceOf[Object]
95-
96-
case Apply(fn, args) if fn.symbol.isConstructor =>
97-
val clazz = loadClass(fn.symbol.owner.symbol.fullName)
98-
val paramClasses = paramsSig(fn.symbol)
99-
val interpretedArgs = args.map(arg => interpretTreeImpl(arg, env))
100-
val constructor = getConstructor(clazz, paramClasses)
101-
stopIfRuntimeException(constructor.newInstance(interpretedArgs: _*))
102-
103-
case _: RefTree if tree.symbol.isStatic =>
104-
val clazz = loadClass(tree.symbol.owner.companionModule.fullName)
105-
val method = getMethod(clazz, tree.symbol.name, Nil)
106-
stopIfRuntimeException(method.invoke(null))
107-
108-
case tree: Apply =>
109-
val evaluatedPrefix = if (tree.symbol.isStatic) null else interpretPrefix(tree, env)
110-
val clazz =
111-
if (tree.symbol.isStatic) loadClass(tree.symbol.owner.companionModule.fullName)
112-
else evaluatedPrefix.getClass
113-
val paramClasses = paramsSig(tree.symbol)
114-
val interpretedArgs = interpretArgs(tree, env)
115-
val method = getMethod(clazz, tree.symbol.name, paramClasses)
116-
stopIfRuntimeException(method.invoke(evaluatedPrefix, interpretedArgs: _*))
117-
118-
case tree: Ident if env.contains(tree.symbol) =>
119-
env(tree.symbol)
120-
121-
case Block(stats, expr) =>
122-
val env2 = stats.foldLeft(env)((acc, x) => interpretStat(x, acc))
123-
interpretTreeImpl(expr, env2)
124-
125-
case tree: NamedArg =>
126-
interpretTreeImpl(tree.arg, env)
127-
128-
case Inlined(_, bindings, expansion) =>
129-
val env2 = bindings.foldLeft(env)((acc, x) => interpretStat(x, acc))
130-
interpretTreeImpl(expansion, env2)
131-
132-
case TypeApply(fn, _) =>
133-
interpretTreeImpl(fn, env)
134-
135-
case Typed(expr, _) =>
136-
interpretTreeImpl(expr, env)
137-
138-
case Select(qualifier, name)
139-
if tree.symbol.owner.isValueClass && tree.symbol.is(ParamAccessor) && env.contains(qualifier.symbol) =>
140-
val value = env(qualifier.symbol)
141-
val clazz = value.getClass
142-
if (clazz.getCanonicalName != tree.symbol.owner.showFullName) value // Already unboxed
143-
else {
144-
val method = getMethod(clazz, name, Nil)
145-
stopIfRuntimeException(method.invoke(value))
146-
}
147-
148-
case SeqLiteral(elems, _) =>
149-
elems.map(elem => interpretTreeImpl(elem, env))
150-
151-
case _ =>
152-
// TODO Add more precise descriptions of why it could not be interpreted.
153-
// This should be done after the full interpreter is implemented.
154-
throw new StopInterpretation(s"Could not interpret ${tree.show}. Consider extracting logic into a helper def.", tree.pos)
155-
}
156-
}
157-
158-
private def interpretArgs(tree: Tree, env: Env): Seq[Object] = {
159-
val b = Seq.newBuilder[Object]
160-
def interpretArgs(tree: Tree): Unit = tree match {
161-
case Apply(fn, args) =>
162-
interpretArgs(fn)
163-
args.foreach(arg => b += interpretTreeImpl(arg, env))
164-
case _ =>
165-
}
166-
interpretArgs(tree)
167-
b.result()
168-
}
169-
170-
private def interpretPrefix(tree: Tree, env: Env): Object = tree match {
171-
case Apply(qual, _) => interpretPrefix(qual, env)
172-
case TypeApply(qual, _) => interpretPrefix(qual, env)
173-
case Select(qual, _) => interpretTreeImpl(qual, env)
174-
}
175-
176-
/** Interprets the statement and returns the updated environment */
177-
private def interpretStat(stat: Tree, env: Env): Env = stat match {
178-
case tree: ValDef =>
179-
val obj = interpretTreeImpl(tree.rhs, env)
180-
env.updated(tree.symbol, obj)
181-
182-
case _ =>
183-
interpretTreeImpl(stat, env)
184-
env
185-
}
186-
18775
private def loadClass(name: Name)(implicit pos: Position): Class[_] = {
18876
try classLoader.loadClass(name.toString)
18977
catch {
@@ -202,15 +90,6 @@ class Interpreter(implicit ctx: Context) {
20290
}
20391
}
20492

205-
private def getConstructor(clazz: Class[_], paramClasses: List[Class[_]])(implicit pos: Position): Constructor[Object] = {
206-
try clazz.getConstructor(paramClasses: _*).asInstanceOf[Constructor[Object]]
207-
catch {
208-
case _: NoSuchMethodException =>
209-
val msg = s"Could not find interpreted constructor of ${clazz.getCanonicalName} with parameters $paramClasses"
210-
throw new StopInterpretation(msg, pos)
211-
}
212-
}
213-
21493
private def stopIfRuntimeException[T](thunk: => T)(implicit pos: Position): T = {
21594
try thunk
21695
catch {

compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala

Lines changed: 14 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -415,14 +415,15 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
415415
var i = 0
416416
transformWithCapturer(tree)(
417417
(captured: mutable.Map[Symbol, Tree]) => {
418-
(tree: RefTree) => {
418+
(tree: Tree) => {
419419
def newCapture = {
420+
val tpw = tree.tpe.widen
420421
val argTpe =
421-
if (tree.symbol.is(Inline)) tree.tpe.widen
422-
else if (tree.isTerm) defn.QuotedExprType.appliedTo(tree.tpe.widen)
423-
else defn.QuotedTypeType.appliedTo(defn.AnyType)
422+
if (tree.isType) defn.QuotedTypeType.appliedTo(tpw)
423+
else if (tree.symbol.is(Inline)) tpw // inlined term
424+
else defn.QuotedExprType.appliedTo(tpw)
424425
val selectArg = arg.select(nme.apply).appliedTo(Literal(Constant(i))).asInstance(argTpe)
425-
val capturedArg = SyntheticValDef(UniqueName.fresh(tree.name.toTermName).toTermName, selectArg)
426+
val capturedArg = SyntheticValDef(UniqueName.fresh(tree.symbol.name.toTermName).toTermName, selectArg)
426427
i += 1
427428
embedded += tree
428429
captured.put(tree.symbol, capturedArg)
@@ -440,16 +441,15 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
440441
Closure(meth, tss => body(tss.head.head)(ctx.withOwner(meth)).changeOwner(ctx.owner, meth))
441442
}
442443

443-
private def transformWithCapturer(tree: Tree)(
444-
capturer: mutable.Map[Symbol, Tree] => RefTree => Tree)(implicit ctx: Context): Tree = {
444+
private def transformWithCapturer(tree: Tree)(capturer: mutable.Map[Symbol, Tree] => Tree => Tree)(implicit ctx: Context): Tree = {
445445
val captured = mutable.LinkedHashMap.empty[Symbol, Tree]
446446
val captured2 = capturer(captured)
447447
outer.enteredSyms.foreach(s => capturers.put(s, captured2))
448448
if (ctx.owner.owner.is(Macro)) {
449-
outer.enteredSyms.foreach(s => {
449+
outer.enteredSyms.reverse.foreach(s => {
450450
assert(s.is(Param))
451451
assert(s.owner == ctx.owner.owner)
452-
captured2(ref(s).asInstanceOf[RefTree])
452+
captured2(ref(s))
453453
})
454454
}
455455
val tree2 = transform(tree)
@@ -515,42 +515,7 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
515515
}
516516

517517
val tree1 =
518-
if (level == 0) {
519-
def getArgs(tree: Tree, acc: List[Tree]): List[Tree] = tree match {
520-
case Apply(fun, args) => getArgs(fun, args ::: acc)
521-
case TypeApply(fun, args) => getArgs(fun, acc)
522-
case _ => acc
523-
}
524-
val args = getArgs(call, Nil)
525-
println("========================")
526-
println(tree.show)
527-
println()
528-
println(call.show)
529-
println(call)
530-
println()
531-
println(call.symbol)
532-
println(args)
533-
println()
534-
val lambda = (new Interpreter).interpretCallToSymbol(call.symbol)(tree.pos).asInstanceOf[Seq[Any] => Object]
535-
val args1 = args.map {
536-
case Literal(Constant(v)) => v
537-
case arg => new scala.quoted.Exprs.TreeExpr(tree)
538-
}
539-
println()
540-
println("~~~~~~~~~~~~~~~~~~~")
541-
println(call.symbol.info)
542-
println(call.symbol.info.show)
543-
println()
544-
println()
545-
println(args1)
546-
println()
547-
println(lambda(args1))
548-
println()
549-
println()
550-
println()
551-
552-
cpy.Inlined(tree)(call, stagedBindings, Splicer.splice(seq(splicedBindings, body).withPos(tree.pos)))
553-
}
518+
if (level == 0) cpy.Inlined(tree)(call, bindings, Splicer.splice(body, call, bindings).withPos(tree.pos))
554519
else seq(stagedBindings, cpy.Select(expansion)(cpy.Inlined(tree)(call, splicedBindings, body), name))
555520
val tree2 = transform(tree1)
556521

@@ -560,9 +525,12 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
560525
case _: Import =>
561526
tree
562527
case tree: DefDef if tree.symbol.is(Macro) && level == 0 =>
528+
if (!tree.symbol.isStatic)
529+
ctx.error("Inline macro method must be a static method.", tree.pos)
563530
markDef(tree)
564531
val reifier = nested(isQuote = true)
565532
reifier.transform(tree) // Ignore output, we only need the its embedding
533+
assert(reifier.embedded.size == 1)
566534
val macroLambda = reifier.embedded.head
567535
// replace macro code by lambda used to evaluate the macro expansion
568536
cpy.DefDef(tree)(tpt = TypeTree(defn.ObjectType), rhs = macroLambda)
@@ -602,6 +570,7 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
602570
def transform(tp: Type): Type = tp match {
603571
case tp: PolyType => PolyType(tp.paramNames, tp.paramInfos, transform(tp.resType))
604572
case tp: MethodType => MethodType(tp.paramNames, tp.paramInfos, transform(tp.resType))
573+
case _: ExprType => ExprType(defn.ObjectType)
605574
case _ => defn.ObjectType
606575
}
607576
transform(tp)

compiler/src/dotty/tools/dotc/transform/Splicer.scala

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import dotty.tools.dotc.ast.tpd
55
import dotty.tools.dotc.core.Contexts._
66
import dotty.tools.dotc.core.Decorators._
77
import dotty.tools.dotc.core.quoted._
8+
import dotty.tools.dotc.core.Types._
9+
import dotty.tools.dotc.core.Symbols._
810
import dotty.tools.dotc.interpreter._
911

1012
import scala.util.control.NonFatal
1113

12-
import java.lang.reflect.InvocationTargetException
14+
import dotty.tools.dotc.util.Positions.Position
1315

1416
/** Utility class to splice quoted expressions */
1517
object Splicer {
@@ -19,30 +21,61 @@ object Splicer {
1921
* and for `~xyz` the tree of `xyz` is interpreted for which the
2022
* resulting expression is returned as a `Tree`
2123
*/
22-
def splice(tree: Tree)(implicit ctx: Context): Tree = tree match {
24+
def splice(tree: Tree, call: Tree, bindings: List[Tree])(implicit ctx: Context): Tree = tree match {
2325
case Quoted(quotedTree) => quotedTree
24-
case _ => reflectiveSplice(tree)
26+
case _ => reflectiveSplice(tree, call, bindings)
2527
}
2628

27-
/** Splice the Tree for a Quoted expression which is constructed via a reflective call to the given method */
28-
private def reflectiveSplice(tree: Tree)(implicit ctx: Context): Tree = {
29+
private def reflectiveSplice(tree: Tree, call: Tree, bindings: List[Tree])(implicit ctx: Context): Tree = {
30+
val liftedArgs = {
31+
val bindMap = bindings.map {
32+
case vdef: ValDef => (vdef.rhs, ref(vdef.symbol))
33+
}.toMap
34+
def allArgs(call: Tree, acc: List[List[Tree]]): List[List[Tree]] = call match {
35+
case call: Apply => allArgs(call.fun, call.args :: acc)
36+
case call: TypeApply => allArgs(call.fun, call.args :: acc)
37+
case _ => acc
38+
}
39+
def liftArgs(tpe: Type, args: List[List[Tree]]): List[Any] = tpe match {
40+
case tp: MethodType =>
41+
val args1 = args.head.zip(tp.paramInfos).map {
42+
case (arg: Literal, tp) if tp.hasAnnotation(defn.InlineParamAnnot) => arg.const.value
43+
case (arg, tp) =>
44+
assert(!tp.hasAnnotation(defn.InlineParamAnnot))
45+
new scala.quoted.Exprs.TreeExpr(bindMap.getOrElse(arg, arg))
46+
}
47+
args1 ::: liftArgs(tp.resType, args.tail)
48+
case tp: PolyType =>
49+
val args1 = args.head.map(tp => new scala.quoted.Types.TreeType(tp))
50+
args1 ::: liftArgs(tp.resType, args.tail)
51+
case _ => Nil
52+
}
53+
54+
liftArgs(call.symbol.info, allArgs(call, Nil))
55+
}
56+
57+
implicit val pos: Position = tree.pos
2958
val interpreter = new Interpreter
30-
val interpreted =
31-
try interpreter.interpretTree[scala.quoted.Expr[_]](tree)
32-
catch { case ex: InvocationTargetException => handleTargetException(tree, ex); None }
33-
interpreted.fold(tree)(PickledQuotes.quotedExprToTree)
59+
val interpreted = interpreter.interpretCallToSymbol[Seq[Any] => Object](call.symbol)
60+
interpreted.flatMap(lambda => evaluateLambda(lambda, liftedArgs)).fold(tree)(PickledQuotes.quotedExprToTree)
3461
}
3562

36-
private def handleTargetException(tree: Tree, ex: InvocationTargetException)(implicit ctx: Context): Unit = ex.getCause match {
37-
case ex: scala.quoted.QuoteError => ctx.error(ex.getMessage, tree.pos)
38-
case NonFatal(ex) =>
39-
val msg =
40-
s"""Failed to evaluate inlined quote.
41-
| Caused by: ${ex.getMessage}
42-
| ${ex.getStackTrace.takeWhile(_.getClassName != "sun.reflect.NativeMethodAccessorImpl").mkString("\n ")}
63+
private def evaluateLambda(lambda: Seq[Any] => Object, args: Seq[Any])(implicit pos: Position, ctx: Context): Option[scala.quoted.Expr[_]] = {
64+
try Some(lambda(args).asInstanceOf[scala.quoted.Expr[_]])
65+
catch {
66+
case ex: scala.quoted.QuoteError =>
67+
ctx.error(ex.getMessage, pos)
68+
None
69+
case NonFatal(ex) =>
70+
val msg =
71+
s"""Failed to evaluate inlined quote.
72+
| Caused by: ${ex.getMessage}
73+
| ${ex.getStackTrace.takeWhile(_.getClassName != "dotty.tools.dotc.transform.Splicer$").init.mkString("\n ")}
4374
""".stripMargin
44-
ctx.error(msg, tree.pos)
45-
case _ => throw ex
75+
ctx.error(msg, pos)
76+
None
77+
case ex: Throwable => throw ex
78+
}
4679
}
4780

4881
}

0 commit comments

Comments
 (0)