Skip to content

Commit 78bd3dd

Browse files
Merge pull request #3913 from dotty-staging/upstreams-interpreter-enhancements
Allow interpretation of inline methods in value classes and varargs
2 parents 0fb7f4a + 25a0aa2 commit 78bd3dd

File tree

3 files changed

+71
-11
lines changed

3 files changed

+71
-11
lines changed

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

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import dotty.tools.dotc.ast.Trees._
88
import dotty.tools.dotc.core.Constants._
99
import dotty.tools.dotc.core.Contexts._
1010
import dotty.tools.dotc.core.Decorators._
11+
import dotty.tools.dotc.core.Flags._
1112
import dotty.tools.dotc.core.Names._
1213
import dotty.tools.dotc.core.Symbols._
1314
import dotty.tools.dotc.core.quoted.Quoted
@@ -82,20 +83,20 @@ class Interpreter(implicit ctx: Context) {
8283
val constructor = getConstructor(clazz, paramClasses)
8384
interpreted(constructor.newInstance(interpretedArgs: _*))
8485

85-
case _: RefTree | _: Apply if tree.symbol.isStatic =>
86+
case _: RefTree if tree.symbol.isStatic =>
8687
val clazz = loadClass(tree.symbol.owner.companionModule.fullName)
88+
val method = getMethod(clazz, tree.symbol.name, Nil)
89+
interpreted(method.invoke(null))
90+
91+
case tree: Apply =>
92+
val evaluatedPrefix = if (tree.symbol.isStatic) null else interpretPrefix(tree, env)
93+
val clazz =
94+
if (tree.symbol.isStatic) loadClass(tree.symbol.owner.companionModule.fullName)
95+
else evaluatedPrefix.getClass
8796
val paramClasses = paramsSig(tree.symbol)
88-
val interpretedArgs = Array.newBuilder[Object]
89-
def interpretArgs(tree: Tree): Unit = tree match {
90-
case Apply(fn, args) =>
91-
interpretArgs(fn)
92-
args.foreach(arg => interpretedArgs += interpretTreeImpl(arg, env))
93-
case _ =>
94-
}
95-
interpretArgs(tree)
96-
97+
val interpretedArgs = interpretArgs(tree, env)
9798
val method = getMethod(clazz, tree.symbol.name, paramClasses)
98-
interpreted(method.invoke(null, interpretedArgs.result(): _*))
99+
interpreted(method.invoke(evaluatedPrefix, interpretedArgs: _*))
99100

100101
case tree: Ident if env.contains(tree.symbol) =>
101102
env(tree.symbol)
@@ -114,13 +115,40 @@ class Interpreter(implicit ctx: Context) {
114115
case Typed(expr, _) =>
115116
interpretTreeImpl(expr, env)
116117

118+
// Getting the underlying value of a value class. The value class is evaluated as its boxed representation
119+
// as values in the interpreter are `Object`s. Therefore we just get it from the enviroment as is.
120+
case Select(qualifier, _)
121+
if tree.symbol.owner.isValueClass && tree.symbol.is(ParamAccessor) && env.contains(qualifier.symbol) =>
122+
env(qualifier.symbol)
123+
124+
case SeqLiteral(elems, _) =>
125+
elems.map(elem => interpretTreeImpl(elem, env))
126+
117127
case _ =>
118128
// TODO Add more precise descriptions of why it could not be interpreted.
119129
// This should be done after the full interpreter is implemented.
120130
throw new StopInterpretation(s"Could not interpret ${tree.show}. Consider extracting logic into a helper def.", tree.pos)
121131
}
122132
}
123133

134+
private def interpretArgs(tree: Tree, env: Env): Seq[Object] = {
135+
val b = Seq.newBuilder[Object]
136+
def interpretArgs(tree: Tree): Unit = tree match {
137+
case Apply(fn, args) =>
138+
interpretArgs(fn)
139+
args.foreach(arg => b += interpretTreeImpl(arg, env))
140+
case _ =>
141+
}
142+
interpretArgs(tree)
143+
b.result()
144+
}
145+
146+
private def interpretPrefix(tree: Tree, env: Env): Object = tree match {
147+
case Apply(qual, _) => interpretPrefix(qual, env)
148+
case TypeApply(qual, _) => interpretPrefix(qual, env)
149+
case Select(qual, _) => interpretTreeImpl(qual, env)
150+
}
151+
124152
/** Interprets the statement and returns the updated environment */
125153
private def interpretStat(stat: Tree, env: Env): Env = stat match {
126154
case tree: ValDef =>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import scala.quoted._
2+
3+
// This test checks the correct interpretation of the inlined value class
4+
5+
object FInterpolation {
6+
7+
implicit class FInterpolatorHelper(val sc: StringContext) extends AnyVal {
8+
inline def ff(arg1: Any): String = ~fInterpolation(sc, Seq('(arg1)))
9+
inline def ff(arg1: Any, arg2: Any): String = ~fInterpolation(sc, Seq('(arg1), '(arg2)))
10+
inline def ff(arg1: Any, arg2: Any, arg3: Any): String = ~fInterpolation(sc, Seq('(arg1), '(arg2), '(arg3)))
11+
// ...
12+
}
13+
14+
private def liftSeq(args: Seq[Expr[Any]]): Expr[Seq[Any]] = args match {
15+
case x :: xs => '{ (~x) +: ~(liftSeq(xs)) }
16+
case Nil => '(Seq(): Seq[Any])
17+
}
18+
19+
def fInterpolation(sc: StringContext, args: Seq[Expr[Any]]): Expr[String] = {
20+
val str: Expr[String] = sc.parts.mkString("")
21+
val args1: Expr[Seq[Any]] = liftSeq(args)
22+
'{ (~str).format(~args1: _*) }
23+
}
24+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
import FInterpolation._
3+
4+
object Test {
5+
println(ff"integer: ${5}%d")
6+
println(ff"string: ${"l"}%s")
7+
println(ff"${5}%s, ${6}%d, ${"hello"}%s")
8+
}

0 commit comments

Comments
 (0)