diff --git a/compiler/src/dotty/tools/dotc/interpreter/Interpreter.scala b/compiler/src/dotty/tools/dotc/interpreter/Interpreter.scala index e108d8c6cb8e..5fa447af602d 100644 --- a/compiler/src/dotty/tools/dotc/interpreter/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/interpreter/Interpreter.scala @@ -8,6 +8,7 @@ import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.core.Constants._ import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.Names._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.quoted.Quoted @@ -82,20 +83,20 @@ class Interpreter(implicit ctx: Context) { val constructor = getConstructor(clazz, paramClasses) interpreted(constructor.newInstance(interpretedArgs: _*)) - case _: RefTree | _: Apply if tree.symbol.isStatic => + case _: RefTree if tree.symbol.isStatic => val clazz = loadClass(tree.symbol.owner.companionModule.fullName) + val method = getMethod(clazz, tree.symbol.name, Nil) + interpreted(method.invoke(null)) + + case tree: Apply => + val evaluatedPrefix = if (tree.symbol.isStatic) null else interpretPrefix(tree, env) + val clazz = + if (tree.symbol.isStatic) loadClass(tree.symbol.owner.companionModule.fullName) + else evaluatedPrefix.getClass val paramClasses = paramsSig(tree.symbol) - val interpretedArgs = Array.newBuilder[Object] - def interpretArgs(tree: Tree): Unit = tree match { - case Apply(fn, args) => - interpretArgs(fn) - args.foreach(arg => interpretedArgs += interpretTreeImpl(arg, env)) - case _ => - } - interpretArgs(tree) - + val interpretedArgs = interpretArgs(tree, env) val method = getMethod(clazz, tree.symbol.name, paramClasses) - interpreted(method.invoke(null, interpretedArgs.result(): _*)) + interpreted(method.invoke(evaluatedPrefix, interpretedArgs: _*)) case tree: Ident if env.contains(tree.symbol) => env(tree.symbol) @@ -114,6 +115,15 @@ class Interpreter(implicit ctx: Context) { case Typed(expr, _) => interpretTreeImpl(expr, env) + // Getting the underlying value of a value class. The value class is evaluated as its boxed representation + // as values in the interpreter are `Object`s. Therefore we just get it from the enviroment as is. + case Select(qualifier, _) + if tree.symbol.owner.isValueClass && tree.symbol.is(ParamAccessor) && env.contains(qualifier.symbol) => + env(qualifier.symbol) + + case SeqLiteral(elems, _) => + elems.map(elem => interpretTreeImpl(elem, env)) + case _ => // TODO Add more precise descriptions of why it could not be interpreted. // This should be done after the full interpreter is implemented. @@ -121,6 +131,24 @@ class Interpreter(implicit ctx: Context) { } } + private def interpretArgs(tree: Tree, env: Env): Seq[Object] = { + val b = Seq.newBuilder[Object] + def interpretArgs(tree: Tree): Unit = tree match { + case Apply(fn, args) => + interpretArgs(fn) + args.foreach(arg => b += interpretTreeImpl(arg, env)) + case _ => + } + interpretArgs(tree) + b.result() + } + + private def interpretPrefix(tree: Tree, env: Env): Object = tree match { + case Apply(qual, _) => interpretPrefix(qual, env) + case TypeApply(qual, _) => interpretPrefix(qual, env) + case Select(qual, _) => interpretTreeImpl(qual, env) + } + /** Interprets the statement and returns the updated environment */ private def interpretStat(stat: Tree, env: Env): Env = stat match { case tree: ValDef => diff --git a/tests/pos/quote-interpolator-core/quoted_1.scala b/tests/pos/quote-interpolator-core/quoted_1.scala new file mode 100644 index 000000000000..f32b1a32f6f6 --- /dev/null +++ b/tests/pos/quote-interpolator-core/quoted_1.scala @@ -0,0 +1,24 @@ +import scala.quoted._ + +// This test checks the correct interpretation of the inlined value class + +object FInterpolation { + + implicit class FInterpolatorHelper(val sc: StringContext) extends AnyVal { + inline def ff(arg1: Any): String = ~fInterpolation(sc, Seq('(arg1))) + inline def ff(arg1: Any, arg2: Any): String = ~fInterpolation(sc, Seq('(arg1), '(arg2))) + inline def ff(arg1: Any, arg2: Any, arg3: Any): String = ~fInterpolation(sc, Seq('(arg1), '(arg2), '(arg3))) + // ... + } + + private def liftSeq(args: Seq[Expr[Any]]): Expr[Seq[Any]] = args match { + case x :: xs => '{ (~x) +: ~(liftSeq(xs)) } + case Nil => '(Seq(): Seq[Any]) + } + + def fInterpolation(sc: StringContext, args: Seq[Expr[Any]]): Expr[String] = { + val str: Expr[String] = sc.parts.mkString("") + val args1: Expr[Seq[Any]] = liftSeq(args) + '{ (~str).format(~args1: _*) } + } +} \ No newline at end of file diff --git a/tests/pos/quote-interpolator-core/quoted_2.scala b/tests/pos/quote-interpolator-core/quoted_2.scala new file mode 100644 index 000000000000..e1e65d5c18d3 --- /dev/null +++ b/tests/pos/quote-interpolator-core/quoted_2.scala @@ -0,0 +1,8 @@ + +import FInterpolation._ + +object Test { + println(ff"integer: ${5}%d") + println(ff"string: ${"l"}%s") + println(ff"${5}%s, ${6}%d, ${"hello"}%s") +} \ No newline at end of file