Skip to content

Commit 826a089

Browse files
Merge pull request #9929 from dotty-staging/intrinsify-StringContext.f
Intrinsify StringContext.f
2 parents 94970a0 + 73b14ae commit 826a089

File tree

10 files changed

+178
-302
lines changed

10 files changed

+178
-302
lines changed

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,9 @@ class Definitions {
553553
@tu lazy val Seq_length : Symbol = SeqClass.requiredMethod(nme.length)
554554
@tu lazy val Seq_toSeq : Symbol = SeqClass.requiredMethod(nme.toSeq)
555555

556+
@tu lazy val StringOps: Symbol = requiredClass("scala.collection.StringOps")
557+
@tu lazy val StringOps_format: Symbol = StringOps.requiredMethod(nme.format)
558+
556559
@tu lazy val ArrayType: TypeRef = requiredClassRef("scala.Array")
557560
def ArrayClass(using Context): ClassSymbol = ArrayType.symbol.asClass
558561
@tu lazy val Array_apply : Symbol = ArrayClass.requiredMethod(nme.apply)
@@ -684,7 +687,13 @@ class Definitions {
684687
@tu lazy val SerializableType: TypeRef = JavaSerializableClass.typeRef
685688
def SerializableClass(using Context): ClassSymbol = SerializableType.symbol.asClass
686689

687-
@tu lazy val JavaEnumClass: ClassSymbol = {
690+
@tu lazy val JavaBigIntegerClass: ClassSymbol = requiredClass("java.math.BigInteger")
691+
@tu lazy val JavaBigDecimalClass: ClassSymbol = requiredClass("java.math.BigDecimal")
692+
@tu lazy val JavaCalendarClass: ClassSymbol = requiredClass("java.util.Calendar")
693+
@tu lazy val JavaDateClass: ClassSymbol = requiredClass("java.util.Date")
694+
@tu lazy val JavaFormattableClass: ClassSymbol = requiredClass("java.util.Formattable")
695+
696+
@tu lazy val JavaEnumClass: ClassSymbol = {
688697
val cls = requiredClass("java.lang.Enum")
689698
// jl.Enum has a single constructor protected(name: String, ordinal: Int).
690699
// We remove the arguments from the primary constructor, and enter
@@ -733,9 +742,6 @@ class Definitions {
733742
@tu lazy val StringContextModule_standardInterpolator: Symbol = StringContextModule.requiredMethod(nme.standardInterpolator)
734743
@tu lazy val StringContextModule_processEscapes: Symbol = StringContextModule.requiredMethod(nme.processEscapes)
735744

736-
@tu lazy val InternalStringContextMacroModule: Symbol = requiredModule("dotty.internal.StringContextMacro")
737-
@tu lazy val InternalStringContextMacroModule_f: Symbol = InternalStringContextMacroModule.requiredMethod(nme.f)
738-
739745
@tu lazy val PartialFunctionClass: ClassSymbol = requiredClass("scala.PartialFunction")
740746
@tu lazy val PartialFunction_isDefinedAt: Symbol = PartialFunctionClass.requiredMethod(nme.isDefinedAt)
741747
@tu lazy val PartialFunction_applyOrElse: Symbol = PartialFunctionClass.requiredMethod(nme.applyOrElse)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ object StdNames {
477477
val flagsFromBits : N = "flagsFromBits"
478478
val flatMap: N = "flatMap"
479479
val foreach: N = "foreach"
480+
val format: N = "format"
480481
val fromDigits: N = "fromDigits"
481482
val fromProduct: N = "fromProduct"
482483
val genericArrayOps: N = "genericArrayOps"

library/src-bootstrapped/dotty/internal/StringContextMacro.scala renamed to compiler/src/dotty/tools/dotc/transform/localopt/StringContextChecker.scala

Lines changed: 74 additions & 76 deletions
Large diffs are not rendered by default.

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import dotty.tools.dotc.core.Decorators._
77
import dotty.tools.dotc.core.Constants.Constant
88
import dotty.tools.dotc.core.Contexts._
99
import dotty.tools.dotc.core.StdNames._
10+
import dotty.tools.dotc.core.NameKinds._
1011
import dotty.tools.dotc.core.Symbols._
11-
import dotty.tools.dotc.core.Types.MethodType
12+
import dotty.tools.dotc.core.Types._
1213
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
1314

1415
/**
@@ -116,6 +117,7 @@ class StringInterpolatorOpt extends MiniPhase {
116117
val sym = tree.symbol
117118
val isInterpolatedMethod = // Test names first to avoid loading scala.StringContext if not used
118119
(sym.name == nme.raw_ && sym.eq(defn.StringContext_raw)) ||
120+
(sym.name == nme.f && sym.eq(defn.StringContext_f)) ||
119121
(sym.name == nme.s && sym.eq(defn.StringContext_s))
120122
if (isInterpolatedMethod)
121123
tree match {
@@ -132,6 +134,11 @@ class StringInterpolatorOpt extends MiniPhase {
132134
if (!str.const.stringValue.isEmpty) concat(str)
133135
}
134136
result
137+
case Apply(intp, args :: Nil) if sym.eq(defn.StringContext_f) =>
138+
val partsStr = StringContextChecker.checkedParts(intp, args).mkString
139+
resolveConstructor(defn.StringOps.typeRef, List(Literal(Constant(partsStr))))
140+
.select(nme.format)
141+
.appliedTo(args)
135142
// Starting with Scala 2.13, s and raw are macros in the standard
136143
// library, so we need to expand them manually.
137144
// sc.s(args) --> standardInterpolator(processEscapes, args, sc.parts)

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

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3257,21 +3257,10 @@ class Typer extends Namer
32573257
if ((inlined ne tree) && errorCount == ctx.reporter.errorCount) readaptSimplified(inlined)
32583258
else inlined
32593259
}
3260-
else if tree.symbol.name == nme.f && tree.symbol == defn.StringContext_f then
3261-
// To avoid forcing StringContext_f when compiling StingContex
3262-
// we test the name before accession symbol StringContext_f.
3263-
3264-
// As scala.StringContext.f is defined in the standard library which
3265-
// we currently do not bootstrap we cannot implement the macro in the library.
3266-
// To overcome the current limitation we intercept the call and rewrite it into
3267-
// a call to dotty.internal.StringContext.f which we can implement using the new macros.
3268-
// As the macro is implemented in the bootstrapped library, it can only be used from the bootstrapped compiler.
3269-
val Apply(TypeApply(Select(sc, _), _), args) = tree
3270-
val newCall = ref(defn.InternalStringContextMacroModule_f).appliedTo(sc).appliedToArgs(args).withSpan(tree.span)
3271-
readaptSimplified(Inliner.inlineCall(newCall))
32723260
else if (tree.symbol.isScala2Macro &&
3273-
// raw and s are eliminated by the StringInterpolatorOpt phase
3261+
// `raw`, `f` and `s` are eliminated by the StringInterpolatorOpt phase
32743262
tree.symbol != defn.StringContext_raw &&
3263+
tree.symbol != defn.StringContext_f &&
32753264
tree.symbol != defn.StringContext_s)
32763265
if (ctx.settings.XignoreScala2Macros.value) {
32773266
report.warning("Scala 2 macro cannot be used in Dotty, this call will crash at runtime. See https://dotty.epfl.ch/docs/reference/dropped-features/macros.html", tree.srcPos.startPos)

library/src-non-bootstrapped/dotty/internal/StringContextMacro.scala

Lines changed: 0 additions & 13 deletions
This file was deleted.

project/Build.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1045,7 +1045,6 @@ object Build {
10451045
)).get
10461046

10471047
++ (dir / "js/src/test/scala/org/scalajs/testsuite/javalib" ** (("*.scala": FileFilter)
1048-
-- "FormatterJSTest.scala" // compile error with the f"" interpolator
10491048
-- "ObjectJSTest.scala" // non-native JS classes
10501049
)).get
10511050

tests/neg/f-interpolator-neg.scala

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
object Test {
2+
3+
def numberArgumentsTests(s : String, d : Int) = {
4+
new StringContext().f() // error
5+
new StringContext("", " is ", "%2d years old").f(s) // error
6+
new StringContext("", " is ", "%2d years old").f(s, d, d) // error
7+
new StringContext("", "").f() // error
8+
}
9+
10+
def interpolationMismatches(s : String, f : Double, b : Boolean) = {
11+
f"$s%b" // error
12+
f"$s%c" // error
13+
f"$f%c" // error
14+
f"$s%x" // error
15+
f"$b%d" // error
16+
f"$s%d" // error
17+
f"$f%o" // error
18+
f"$s%e" // error
19+
f"$b%f" // error
20+
f"$s%i" // error
21+
}
22+
23+
def flagMismatches(s : String, c : Char, d : Int, f : Double, t : java.util.Date) = {
24+
f"$s%+ 0,(s" // error
25+
f"$c%#+ 0,(c" // error
26+
f"$d%#d" // error
27+
f"$d%,x" // error
28+
f"$d%+ (x" // error
29+
f"$f%,(a" // error
30+
f"$t%#+ 0,(tT" // error
31+
f"%-#+ 0,(n" // error
32+
f"%#+ 0,(%" // error
33+
}
34+
35+
def badPrecisions(c : Char, d : Int, f : Double, t : java.util.Date) = {
36+
f"$c%.2c" // error
37+
f"$d%.2d" // error
38+
f"%.2%" // error
39+
f"%.2n" // error
40+
f"$f%.2a" // error
41+
f"$t%.2tT" // error
42+
}
43+
44+
def badIndexes() = {
45+
f"%<s" // error
46+
f"%<c" // error
47+
f"%<tT" // error
48+
f"${8}%d ${9}%d %3$$d" // error
49+
f"${8}%d ${9}%d%0$$d" // error
50+
}
51+
52+
def warnings(s : String) = {
53+
f"${8}%d ${9}%1$$d"
54+
f"$s%s $s%s %1$$<s"
55+
f"$s%s $s%1$$s"
56+
}
57+
58+
def badArgTypes(s : String) = {
59+
f"$s%#s" // error
60+
}
61+
62+
def misunderstoodConversions(t : java.util.Date, s : String) = {
63+
f"$t%tG" // error
64+
f"$t%t" // error
65+
f"$s%10.5" // error
66+
}
67+
68+
def otherBrainFailures(d : Int) = {
69+
f"${d}random-leading-junk%d" // error
70+
f"%1$$n"
71+
f"%1$$d" // error
72+
f"blablablabla %% %.2d" // error
73+
f"blablablabla %.2b %%" // error
74+
75+
f"ana${3}%.2f%2${true}%bb" // error
76+
f"ac{2c{2{c.ca "
77+
78+
f"b%c.%2ii%iin" // error
79+
f"b}22%2.c<{%{" // error
80+
f"%%bci.2${'i'}%..2c2" // error
81+
}
82+
83+
}

tests/run-macros/f-interpolator-neg/Macros_1.scala

Lines changed: 0 additions & 73 deletions
This file was deleted.

0 commit comments

Comments
 (0)