Skip to content

Commit 55bff22

Browse files
committed
Allow calling inherited methods on modules
1 parent e158370 commit 55bff22

File tree

4 files changed

+69
-46
lines changed

4 files changed

+69
-46
lines changed

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ object Splicer {
112112
}
113113
}
114114

115-
protected def interpretStaticMethodCall(fn: Symbol, args: => List[Object])(implicit env: Env): Object = {
116-
val instance = loadModule(fn.owner)
115+
protected def interpretStaticMethodCall(moduleClass: Symbol, fn: Symbol, args: => List[Object])(implicit env: Env): Object = {
116+
val instance = loadModule(moduleClass)
117117
def getDirectName(tp: Type, name: TermName): TermName = tp.widenDealias match {
118118
case tp: AppliedType if defn.isImplicitFunctionType(tp) =>
119119
getDirectName(tp.args.last, NameKinds.DirectMethodName(name))
@@ -270,7 +270,7 @@ object Splicer {
270270
protected def interpretVarargs(args: List[Boolean])(implicit env: Env): Boolean = args.forall(identity)
271271
protected def interpretTastyContext()(implicit env: Env): Boolean = true
272272
protected def interpretQuoteContext()(implicit env: Env): Boolean = true
273-
protected def interpretStaticMethodCall(fn: Symbol, args: => List[Boolean])(implicit env: Env): Boolean = args.forall(identity)
273+
protected def interpretStaticMethodCall(module: Symbol, fn: Symbol, args: => List[Boolean])(implicit env: Env): Boolean = args.forall(identity)
274274
protected def interpretModuleAccess(fn: Symbol)(implicit env: Env): Boolean = true
275275
protected def interpretNew(fn: Symbol, args: => List[Boolean])(implicit env: Env): Boolean = args.forall(identity)
276276

@@ -292,7 +292,7 @@ object Splicer {
292292
protected def interpretLiteral(value: Any)(implicit env: Env): Result
293293
protected def interpretVarargs(args: List[Result])(implicit env: Env): Result
294294
protected def interpretTastyContext()(implicit env: Env): Result
295-
protected def interpretStaticMethodCall(fn: Symbol, args: => List[Result])(implicit env: Env): Result
295+
protected def interpretStaticMethodCall(module: Symbol, fn: Symbol, args: => List[Result])(implicit env: Env): Result
296296
protected def interpretModuleAccess(fn: Symbol)(implicit env: Env): Result
297297
protected def interpretNew(fn: Symbol, args: => List[Result])(implicit env: Env): Result
298298
protected def unexpectedTree(tree: Tree)(implicit env: Env): Result
@@ -313,9 +313,10 @@ object Splicer {
313313
case Call(fn, args) =>
314314
if (fn.symbol.isConstructor && fn.symbol.owner.owner.is(Package)) {
315315
interpretNew(fn.symbol, args.map(interpretTree))
316-
} else if (fn.symbol.isStatic) {
317-
if (fn.symbol.is(Module)) interpretModuleAccess(fn.symbol)
318-
else interpretStaticMethodCall(fn.symbol, args.map(arg => interpretTree(arg)))
316+
} else if (fn.symbol.is(Module)) {
317+
interpretModuleAccess(fn.symbol)
318+
} else if (fn.symbol.isStatic || fn.qualifier.symbol.is(Module)) {
319+
interpretStaticMethodCall(fn.qualifier.symbol.moduleClass, fn.symbol, args.map(arg => interpretTree(arg)))
319320
} else if (env.contains(fn.name)) {
320321
env(fn.name)
321322
} else {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Hello world!
2+
Hello world!\n
3+
Hello foo!

tests/run-separate-compilation/tasty-interpolation-1/Macro.scala

Lines changed: 54 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,70 +8,88 @@ import scala.quoted.Toolbox.Default._
88
object Macro {
99

1010
class StringContextOps(strCtx: => StringContext) {
11-
inline def foo(args: Any*): String = ~SIntepolator('(strCtx), '(args))
11+
inline def s2(args: Any*): String = ~SIntepolator('(strCtx), '(args))
12+
inline def raw2(args: Any*): String = ~RawIntepolator('(strCtx), '(args))
13+
inline def foo(args: Any*): String = ~FooIntepolator('(strCtx), '(args))
1214
}
1315
implicit inline def SCOps(strCtx: => StringContext): StringContextOps = new StringContextOps(strCtx)
14-
1516
}
1617

1718
object SIntepolator extends MacroStringInterpolator[String] {
19+
protected def interpolate(strCtx: StringContext, args: List[Expr[Any]])(implicit reflect: Reflection): Expr[String] =
20+
'((~strCtx.toExpr).s(~args.toExprOfList: _*))
21+
}
1822

19-
// TODO remove this
20-
override def apply(strCtxExpr: Expr[StringContext], argsExpr: Expr[Seq[Any]])(implicit reflect: Reflection): Expr[String] = {
21-
super.apply(strCtxExpr, argsExpr)
22-
}
23-
24-
override def interpolate(strCtx: StringContext, args: List[Expr[Any]])(implicit reflect: Reflection): Expr[String] =
25-
'(StringContext(~strCtx.parts.toList.toExpr: _*).s(~args.toExprOfList: _*))
23+
object RawIntepolator extends MacroStringInterpolator[String] {
24+
protected def interpolate(strCtx: StringContext, args: List[Expr[Any]])(implicit reflect: Reflection): Expr[String] =
25+
'((~strCtx.toExpr).raw(~args.toExprOfList: _*))
26+
}
2627

27-
// TODO use stdlib
28-
implicit def l: Liftable[List[String]] = new Liftable[List[String]] {
29-
override def toExpr(list: List[String]): Expr[List[String]] = list match {
30-
case x :: xs => '(~x.toExpr :: ~toExpr(xs))
31-
case Nil => '(Nil)
32-
}
33-
}
28+
object FooIntepolator extends MacroStringInterpolator[String] {
29+
protected def interpolate(strCtx: StringContext, args: List[Expr[Any]])(implicit reflect: Reflection): Expr[String] =
30+
'((~strCtx.toExpr).s(~args.map(_ => '("foo")).toExprOfList: _*))
3431
}
3532

33+
// TODO put this class in the stdlib or separate project?
3634
abstract class MacroStringInterpolator[T] {
3735

38-
/*final*/ def apply(strCtxExpr: Expr[StringContext], argsExpr: Expr[Seq[Any]])(implicit reflect: Reflection): Expr[T] = {
39-
interpolate(strCtxExpr, argsExpr)
36+
final def apply(strCtxExpr: Expr[StringContext], argsExpr: Expr[Seq[Any]])(implicit reflect: Reflection): Expr[T] = {
37+
try interpolate(strCtxExpr, argsExpr)
38+
catch {
39+
case ex: NotStaticlyKnownError =>
40+
// TODO use ex.expr to recover the position
41+
throw new QuoteError(ex.getMessage)
42+
case ex: StringContextError =>
43+
// TODO use ex.idx to recover the position
44+
throw new QuoteError(ex.getMessage)
45+
case ex: ArgumentError =>
46+
// TODO use ex.idx to recover the position
47+
throw new QuoteError(ex.getMessage)
48+
}
4049
}
4150

42-
def interpolate(strCtxExpr: Expr[StringContext], argsExpr: Expr[Seq[Any]])(implicit reflect: Reflection): Expr[T] = {
43-
val strCtx = getStaticStringContext(strCtxExpr).getOrElse(throw new QuoteError("Expected statically known StringContext"))
44-
val argExprs = getArgsList(argsExpr).getOrElse(throw new QuoteError("Expected statically known argument list"))
45-
interpolate(strCtx, argExprs)
46-
}
51+
protected def interpolate(strCtxExpr: Expr[StringContext], argsExpr: Expr[Seq[Any]])(implicit reflect: Reflection): Expr[T] =
52+
interpolate(getStaticStringContext(strCtxExpr), getArgsList(argsExpr))
4753

48-
def interpolate(strCtx: StringContext, argExprs: List[Expr[Any]])(implicit reflect: Reflection): Expr[T] = {
49-
// argExprs.toExprOfList
50-
// '(StringContext(~strCtx.parts.toList.toExpr: _*).s(~args: _*))
51-
???
52-
}
54+
protected def interpolate(strCtx: StringContext, argExprs: List[Expr[Any]])(implicit reflect: Reflection): Expr[T]
5355

54-
protected def getStaticStringContext(strCtxExpr: Expr[StringContext])(implicit reflect: Reflection): Option[StringContext] = {
56+
protected def getStaticStringContext(strCtxExpr: Expr[StringContext])(implicit reflect: Reflection): StringContext = {
5557
import reflect._
5658
strCtxExpr.unseal.underlyingArgument match {
5759
case Term.Select(Term.Typed(Term.Apply(_, List(Term.Apply(_, List(Term.Typed(Term.Repeated(strCtxArgTrees), TypeTree.Inferred()))))), _), _) =>
5860
val strCtxArgs = strCtxArgTrees.map {
5961
case Term.Literal(Constant.String(str)) => str
60-
case _ => return None
62+
case tree => throw new NotStaticlyKnownError("Expected statically known StringContext", tree.seal[Any])
6163
}
62-
Some(StringContext(strCtxArgs: _*))
63-
case _ =>
64-
None
64+
StringContext(strCtxArgs: _*)
65+
case tree =>
66+
throw new NotStaticlyKnownError("Expected statically known StringContext", tree.seal[Any])
6567
}
6668
}
6769

68-
protected def getArgsList(argsExpr: Expr[Seq[Any]])(implicit reflect: Reflection): Option[List[Expr[Any]]] = {
70+
protected def getArgsList(argsExpr: Expr[Seq[Any]])(implicit reflect: Reflection): List[Expr[Any]] = {
6971
import reflect._
7072
argsExpr.unseal.underlyingArgument match {
71-
case Term.Typed(Term.Repeated(args), _) => Some(args.map(_.seal[Any]))
72-
case _ => None
73+
case Term.Typed(Term.Repeated(args), _) => args.map(_.seal[Any])
74+
case tree => throw new NotStaticlyKnownError("Expected statically known argument list", tree.seal[Any])
75+
}
76+
}
77+
78+
protected implicit def StringContextIsLiftable: Liftable[StringContext] = new Liftable[StringContext] {
79+
def toExpr(strCtx: StringContext): Expr[StringContext] = {
80+
// TODO define in stdlib?
81+
implicit def ListIsLiftable: Liftable[List[String]] = new Liftable[List[String]] {
82+
override def toExpr(list: List[String]): Expr[List[String]] = list match {
83+
case x :: xs => '(~x.toExpr :: ~toExpr(xs))
84+
case Nil => '(Nil)
85+
}
86+
}
87+
'(StringContext(~strCtx.parts.toList.toExpr: _*))
7388
}
7489
}
7590

91+
protected class NotStaticlyKnownError(msg: String, expr: Expr[Any]) extends Exception(msg)
92+
protected class StringContextError(msg: String, idx: Int, start: Int = -1, end: Int = -1) extends Exception(msg)
93+
protected class ArgumentError(msg: String, idx: Int) extends Exception(msg)
7694

7795
}

tests/run-separate-compilation/tasty-interpolation-1/Test_2.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import Macro._
22

33
object Test {
44
def main(args: Array[String]): Unit = {
5-
6-
println(foo"Hello ${"world"}!")
7-
5+
val w = "world"
6+
println(s2"Hello $w!")
7+
println(raw2"Hello $w!\n")
8+
println(foo"Hello $w!")
89
}
910
}

0 commit comments

Comments
 (0)