Skip to content

Commit 1168003

Browse files
smarterbishabosha
authored andcommitted
Handle s and raw interpolator with 2.13 stdlib
These methods are now macros, so we always need to expand them.
1 parent 662b7e0 commit 1168003

File tree

4 files changed

+67
-27
lines changed

4 files changed

+67
-27
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ class Definitions {
347347
@tu lazy val ScalaPredefModule: Symbol = ctx.requiredModule("scala.Predef")
348348
@tu lazy val Predef_conforms : Symbol = ScalaPredefModule.requiredMethod(nme.conforms_)
349349
@tu lazy val Predef_classOf : Symbol = ScalaPredefModule.requiredMethod(nme.classOf)
350+
@tu lazy val Predef_identity : Symbol = ScalaPredefModule.requiredMethod(nme.identity)
350351
@tu lazy val Predef_undefined: Symbol = ScalaPredefModule.requiredMethod(nme.???)
351352

352353
def SubTypeClass(implicit ctx: Context): ClassSymbol = ctx.requiredClass("scala.<:<")
@@ -564,11 +565,14 @@ class Definitions {
564565
@tu lazy val StringAdd_+ : Symbol = StringAddClass.requiredMethod(nme.raw.PLUS)
565566

566567
@tu lazy val StringContextClass: ClassSymbol = ctx.requiredClass("scala.StringContext")
567-
@tu lazy val StringContextS : Symbol = StringContextClass.requiredMethod(nme.s)
568-
@tu lazy val StringContextRaw: Symbol = StringContextClass.requiredMethod(nme.raw_)
569-
@tu lazy val StringContext_f : Symbol = StringContextClass.requiredMethod(nme.f)
568+
@tu lazy val StringContext_s : Symbol = StringContextClass.requiredMethod(nme.s)
569+
@tu lazy val StringContext_raw: Symbol = StringContextClass.requiredMethod(nme.raw_)
570+
@tu lazy val StringContext_f : Symbol = StringContextClass.requiredMethod(nme.f)
571+
@tu lazy val StringContext_parts: Symbol = StringContextClass.requiredMethod(nme.parts)
570572
@tu lazy val StringContextModule: Symbol = StringContextClass.companionModule
571573
@tu lazy val StringContextModule_apply: Symbol = StringContextModule.requiredMethod(nme.apply)
574+
@tu lazy val StringContextModule_standardInterpolator: Symbol = StringContextModule.requiredMethod(nme.standardInterpolator)
575+
@tu lazy val StringContextModule_processEscapes: Symbol = StringContextModule.requiredMethod(nme.processEscapes)
572576

573577
@tu lazy val InternalStringContextMacroModule: Symbol = ctx.requiredModule("dotty.internal.StringContextMacro")
574578
@tu lazy val InternalStringContextMacroModule_f: Symbol = InternalStringContextMacroModule.requiredMethod(nme.f)

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,9 @@ object StdNames {
504504
val ordinalDollar: N = "$ordinal"
505505
val ordinalDollar_ : N = "_$ordinal"
506506
val origin: N = "origin"
507+
val parts: N = "parts"
507508
val prefix : N = "prefix"
509+
val processEscapes: N = "processEscapes"
508510
val productArity: N = "productArity"
509511
val productElement: N = "productElement"
510512
val productElementName: N = "productElementName"
@@ -536,6 +538,7 @@ object StdNames {
536538
val setType: N = "setType"
537539
val setTypeSignature: N = "setTypeSignature"
538540
val splice: N = "$splice"
541+
val standardInterpolator: N = "standardInterpolator"
539542
val staticClass : N = "staticClass"
540543
val staticModule : N = "staticModule"
541544
val staticPackage : N = "staticPackage"

compiler/src/dotty/tools/dotc/transform/localopt/StringInterpolatorOpt.scala

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ package dotty.tools.dotc.transform.localopt
22

33
import dotty.tools.dotc.ast.Trees._
44
import dotty.tools.dotc.ast.tpd
5+
import dotty.tools.dotc.core.Decorators._
56
import dotty.tools.dotc.core.Constants.Constant
67
import dotty.tools.dotc.core.Contexts.Context
78
import dotty.tools.dotc.core.StdNames._
89
import dotty.tools.dotc.core.Symbols._
10+
import dotty.tools.dotc.core.Types.MethodType
911
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
1012

1113
/**
@@ -21,6 +23,16 @@ class StringInterpolatorOpt extends MiniPhase {
2123

2224
override def phaseName: String = "stringInterpolatorOpt"
2325

26+
override def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = {
27+
tree match {
28+
case tree: RefTree =>
29+
val sym = tree.symbol
30+
assert(sym != defn.StringContext_raw && sym != defn.StringContext_s,
31+
i"$tree in ${ctx.owner.showLocated} should have been rewritten by phase $phaseName")
32+
case _ =>
33+
}
34+
}
35+
2436
/** Matches a list of constant literals */
2537
private object Literals {
2638
def unapply(tree: SeqLiteral)(implicit ctx: Context): Option[List[Literal]] = {
@@ -60,7 +72,7 @@ class StringInterpolatorOpt extends MiniPhase {
6072
def unapply(tree: Apply)(implicit ctx: Context): Option[(List[Literal], List[Tree])] = {
6173
tree match {
6274
case SOrRawInterpolator(strs, elems) =>
63-
if (tree.symbol == defn.StringContextRaw) Some(strs, elems)
75+
if (tree.symbol == defn.StringContext_raw) Some(strs, elems)
6476
else { // tree.symbol == defn.StringContextS
6577
try {
6678
val escapedStrs = strs.map { str =>
@@ -80,28 +92,46 @@ class StringInterpolatorOpt extends MiniPhase {
8092
override def transformApply(tree: Apply)(implicit ctx: Context): Tree = {
8193
val sym = tree.symbol
8294
val isInterpolatedMethod = // Test names first to avoid loading scala.StringContext if not used
83-
(sym.name == nme.raw_ && sym.eq(defn.StringContextRaw)) ||
84-
(sym.name == nme.s && sym.eq(defn.StringContextS))
85-
if (isInterpolatedMethod) transformInterpolator(tree)
86-
else tree
87-
}
95+
(sym.name == nme.raw_ && sym.eq(defn.StringContext_raw)) ||
96+
(sym.name == nme.s && sym.eq(defn.StringContext_s))
97+
if (isInterpolatedMethod)
98+
tree match {
99+
case StringContextIntrinsic(strs: List[Literal], elems: List[Tree]) =>
100+
val stri = strs.iterator
101+
val elemi = elems.iterator
102+
var result: Tree = stri.next
103+
def concat(tree: Tree): Unit = {
104+
result = result.select(defn.String_+).appliedTo(tree)
105+
}
106+
while (elemi.hasNext) {
107+
concat(elemi.next)
108+
val str = stri.next
109+
if (!str.const.stringValue.isEmpty) concat(str)
110+
}
111+
result
112+
// Starting with Scala 2.13, s and raw are macros in the standard
113+
// library, so we need to expand them manually.
114+
// sc.s(args) --> standardInterpolator(processEscapes, args, sc.parts)
115+
// sc.raw(args) --> standardInterpolator(x => x, args, sc.parts)
116+
case Apply(intp, args :: Nil) =>
117+
val pre = intp match {
118+
case Select(pre, _) => pre
119+
case intp: Ident => tpd.desugarIdentPrefix(intp)
120+
}
121+
val isRaw = sym eq defn.StringContext_raw
122+
val stringToString = defn.StringContextModule_processEscapes.info.asInstanceOf[MethodType]
88123

89-
private def transformInterpolator(tree: Tree)(implicit ctx: Context): Tree = {
90-
tree match {
91-
case StringContextIntrinsic(strs: List[Literal], elems: List[Tree]) =>
92-
val stri = strs.iterator
93-
val elemi = elems.iterator
94-
var result: Tree = stri.next
95-
def concat(tree: Tree): Unit = {
96-
result = result.select(defn.String_+).appliedTo(tree)
97-
}
98-
while (elemi.hasNext) {
99-
concat(elemi.next)
100-
val str = stri.next
101-
if (!str.const.stringValue.isEmpty) concat(str)
102-
}
103-
result
104-
case _ => tree
105-
}
124+
val process = tpd.Lambda(stringToString, args =>
125+
if (isRaw) args.head else ref(defn.StringContextModule_processEscapes).appliedToArgs(args))
126+
127+
evalOnce(pre) { sc =>
128+
val parts = sc.select(defn.StringContext_parts)
129+
130+
ref(defn.StringContextModule_standardInterpolator)
131+
.appliedToArgs(List(process, args, parts))
132+
}
133+
}
134+
else
135+
tree
106136
}
107137
}

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2688,7 +2688,10 @@ class Typer extends Namer
26882688
tree.tpe <:< wildApprox(pt)
26892689
readaptSimplified(Inliner.inlineCall(tree))
26902690
}
2691-
else if (tree.symbol.isScala2Macro) {
2691+
else if (tree.symbol.isScala2Macro &&
2692+
// raw and s are eliminated by the StringInterpolatorOpt phase
2693+
tree.symbol != defn.StringContext_raw &&
2694+
tree.symbol != defn.StringContext_s) {
26922695
if (tree.symbol eq defn.StringContext_f) {
26932696
// As scala.StringContext.f is defined in the standard library which
26942697
// we currently do not bootstrap we cannot implement the macro in the library.

0 commit comments

Comments
 (0)