Skip to content

Commit 9acab67

Browse files
committed
Revert "Remove support -scala-output-version with quotes"
This reverts commit ca01392. Fixes #14989
1 parent 119e3d7 commit 9acab67

File tree

15 files changed

+184
-9
lines changed

15 files changed

+184
-9
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,9 @@ class Definitions {
827827

828828

829829
@tu lazy val QuoteUnpicklerClass: ClassSymbol = requiredClass("scala.quoted.runtime.QuoteUnpickler")
830+
@tu lazy val QuoteUnpickler_unpickleExpr: Symbol = QuoteUnpicklerClass.requiredMethod("unpickleExpr")
830831
@tu lazy val QuoteUnpickler_unpickleExprV2: Symbol = QuoteUnpicklerClass.requiredMethod("unpickleExprV2")
832+
@tu lazy val QuoteUnpickler_unpickleType: Symbol = QuoteUnpicklerClass.requiredMethod("unpickleType")
831833
@tu lazy val QuoteUnpickler_unpickleTypeV2: Symbol = QuoteUnpicklerClass.requiredMethod("unpickleTypeV2")
832834

833835
@tu lazy val QuoteMatchingClass: ClassSymbol = requiredClass("scala.quoted.runtime.QuoteMatching")

compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ object PickledQuotes {
5050
/** `typeHole`/`types` argument of `QuoteUnpickler.{unpickleExpr,unpickleExprV2,unpickleType,unpickleTypeV2}` */
5151
enum TypeHole:
5252
/** `termHole` argument of `QuoteUnpickler.{unpickleExpr, unpickleType}`.
53-
* From code compiled with Scala 3.0.x and 3.1.x.
53+
* From code compiled with Scala 3.0.x and 3.1.x or with -scala-output-version 3.0 and 3.1
5454
* Note: For `unpickleType` it will always be `null`.
5555
*/
5656
case V1(evalHole: Null | ((Int, Seq[scala.quoted.Type[?]]) => scala.quoted.Type[?]))
@@ -65,7 +65,7 @@ object PickledQuotes {
6565

6666
enum ExprHole:
6767
/** `termHole` argument of `QuoteUnpickler.{unpickleExpr, unpickleType}`.
68-
* From code compiled with Scala 3.0.x and 3.1.x.
68+
* From code compiled with Scala 3.0.x and 3.1.x or with -scala-output-version 3.0 and 3.1
6969
* Note: For `unpickleType` it will always be `null`.
7070
*/
7171
case V1(evalHole: Null | ((Int, Seq[ExprHole.ArgV1], scala.quoted.Quotes) => scala.quoted.Expr[?]))

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

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,9 @@ object PickleQuotes {
291291
* this closure is always applied directly to the actual context and the BetaReduce phase removes it.
292292
*/
293293
def pickleAsTasty() = {
294+
295+
val unpickleV1 = ctx.scalaRelease <= Release3_1
296+
294297
val pickleQuote = PickledQuotes.pickleQuote(body)
295298
val pickledQuoteStrings = pickleQuote match
296299
case x :: Nil => Literal(Constant(x))
@@ -303,8 +306,26 @@ object PickleQuotes {
303306

304307
// This and all closures in typeSplices are removed by the BetaReduce phase
305308
val types =
306-
if typeSplices.isEmpty then Literal(Constant(null)) // keep pickled quote without contents as small as possible
307-
else SeqLiteral(typeSplices.map(_._1), TypeTree(defn.QuotedTypeClass.typeRef.appliedTo(WildcardType)))
309+
if unpickleV1 then
310+
if typeSplices.isEmpty then Literal(Constant(null)) // keep pickled quote without contents as small as possible
311+
else
312+
Lambda(
313+
MethodType(
314+
List(nme.idx, nme.contents).map(name => UniqueName.fresh(name).toTermName),
315+
List(defn.IntType, defn.SeqType.appliedTo(defn.AnyType)),
316+
defn.QuotedTypeClass.typeRef.appliedTo(WildcardType)),
317+
args => {
318+
val cases = typeSplices.map { case (splice, idx) =>
319+
CaseDef(Literal(Constant(idx)), EmptyTree, splice)
320+
}
321+
cases match
322+
case CaseDef(_, _, rhs) :: Nil => rhs
323+
case _ => Match(args(0).annotated(New(ref(defn.UncheckedAnnot.typeRef))), cases)
324+
}
325+
)
326+
else // if unpickleV2 then
327+
if typeSplices.isEmpty then Literal(Constant(null)) // keep pickled quote without contents as small as possible
328+
else SeqLiteral(typeSplices.map(_._1), TypeTree(defn.QuotedTypeClass.typeRef.appliedTo(WildcardType)))
308329

309330
// This and all closures in termSplices are removed by the BetaReduce phase
310331
val termHoles =
@@ -320,7 +341,12 @@ object PickleQuotes {
320341
val defn.FunctionOf(argTypes, defn.FunctionOf(quotesType :: _, _, _, _), _, _) = splice.tpe
321342
val rhs = {
322343
val spliceArgs = argTypes.zipWithIndex.map { (argType, i) =>
323-
args(1).select(nme.apply).appliedTo(Literal(Constant(i))).asInstance(argType)
344+
val argi = args(1).select(nme.apply).appliedTo(Literal(Constant(i)))
345+
if unpickleV1 && argType.derivesFrom(defn.QuotedExprClass) then
346+
val argType1 = defn.FunctionType(1).appliedTo(defn.QuotesClass.typeRef, argType)
347+
argi.asInstance(argType1).select(nme.apply).appliedTo(args(2))
348+
else
349+
argi.asInstance(argType)
324350
}
325351
val Block(List(ddef: DefDef), _) = splice
326352
// TODO: beta reduce inner closure? Or wait until BetaReduce phase?
@@ -337,10 +363,14 @@ object PickleQuotes {
337363
val quotedType = quoteClass.typeRef.appliedTo(originalTp)
338364
val lambdaTpe = MethodType(defn.QuotesClass.typeRef :: Nil, quotedType)
339365
val unpickleMeth =
340-
if isType then defn.QuoteUnpickler_unpickleTypeV2
341-
else defn.QuoteUnpickler_unpickleExprV2
366+
if unpickleV1 then
367+
if isType then defn.QuoteUnpickler_unpickleType
368+
else defn.QuoteUnpickler_unpickleExpr
369+
else // if unpickleV2 then
370+
if isType then defn.QuoteUnpickler_unpickleTypeV2
371+
else defn.QuoteUnpickler_unpickleExprV2
342372
val unpickleArgs =
343-
if isType then List(pickledQuoteStrings, types)
373+
if isType && !unpickleV1 then List(pickledQuoteStrings, types)
344374
else List(pickledQuoteStrings, types, termHoles)
345375
quotes
346376
.asInstance(defn.QuoteUnpicklerClass.typeRef)

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,13 @@ class Splicing extends MacroTransform:
135135
val hole = tpd.Hole(false, holeIdx, Nil, ref(qual), TypeTree(tp))
136136
typeHoles.put(qual.symbol, hole)
137137
hole
138-
cpy.TypeDef(tree)(rhs = hole)
138+
val rhs =
139+
if ctx.scalaRelease <= Release3_1 then
140+
val secondHoleIdx = numHoles
141+
numHoles += 1
142+
TypeBoundsTree(hole, cpy.Hole(hole)(idx = secondHoleIdx))
143+
else hole
144+
cpy.TypeDef(tree)(rhs = rhs)
139145
case Apply(Select(Apply(TypeApply(fn,_), List(code)),nme.apply),List(quotes))
140146
if fn.symbol == defn.QuotedRuntime_exprQuote =>
141147
super.transform(tree)(using quoteContext)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package app
2+
3+
import lib.*
4+
5+
def powerTest(x: Double): Unit =
6+
power(x, 0)
7+
power(x, 1)
8+
power(x, 5)
9+
power(x, 10)
10+
11+
def letTest: Unit =
12+
let(0) { _ + 1 }
13+
let(0) { _.toString }
14+
let((4, 'a')) { _.swap }
15+
let(new Foo) { _.hashCode }
16+
17+
class Foo
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
lazy val checkOptions = Seq("-Xcheck-macros", "-Ycheck:all", "-Yno-double-bindings")
2+
3+
lazy val lib = project.in(file("lib"))
4+
.settings(
5+
scalacOptions ++= Seq("-scala-output-version", "3.0") ++ checkOptions
6+
)
7+
8+
lazy val app = project.in(file("app"))
9+
.dependsOn(lib)
10+
.settings(
11+
scalaVersion := "3.0.2",
12+
scalacOptions ++= checkOptions,
13+
dependencyOverrides += scalaOrganization.value %% "scala3-library" % scalaVersion.value,
14+
)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package lib
2+
3+
import scala.quoted.*
4+
5+
inline def power(x: Double, inline n: Int) = ${ powerCode('x, 'n) }
6+
7+
private def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] =
8+
unrolledPowerCode(x, n.valueOrError)
9+
10+
private def unrolledPowerCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] =
11+
if n == 0 then '{ 1.0 } // tests simple quotes without splices
12+
else if n % 2 == 1 then '{ $x * ${ unrolledPowerCode(x, n - 1) } } // tests simple splices
13+
else '{ val y = $x * $x; ${ unrolledPowerCode('y, n / 2) } } // tests splice with term capture
14+
15+
16+
inline def let[T, U](x: T)(inline body: T => U): U = ${ letCode('x, 'body) }
17+
18+
private def letCode[T: Type, U: Type](x: Expr[T], body: Expr[T => U])(using Quotes): Expr[U] =
19+
// tests use of Type
20+
'{ val y: T = $x; $body(y): U }
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import sbt._
2+
import Keys._
3+
4+
object DottyInjectedPlugin extends AutoPlugin {
5+
override def requires = plugins.JvmPlugin
6+
override def trigger = allRequirements
7+
8+
override val projectSettings = Seq(
9+
scalaVersion := sys.props("plugin.scalaVersion")
10+
)
11+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
> app/compile
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import scala.quoted.*
2+
3+
object Macros:
4+
5+
inline def power(x: Double, inline n: Int) = ${ powerCode('x, 'n) }
6+
7+
private def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] =
8+
unrolledPowerCode(x, n.valueOrError)
9+
10+
private def unrolledPowerCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] =
11+
if n == 0 then '{ 1.0 } // tests simple quotes without splices
12+
else if n % 2 == 1 then '{ $x * ${ unrolledPowerCode(x, n - 1) } } // tests simple splices
13+
else '{ val y = $x * $x; ${ unrolledPowerCode('y, n / 2) } } // tests splice with term capture
14+
15+
16+
inline def let[T, U](x: T)(inline body: T => U): U = ${ letCode('x, 'body) }
17+
18+
private def letCode[T: Type, U: Type](x: Expr[T], body: Expr[T => U])(using Quotes): Expr[U] =
19+
// tests use of Type
20+
'{ val y: T = $x; $body(y): U }
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import Macros.*
2+
3+
def powerTest(x: Double): Unit =
4+
power(x, 0)
5+
power(x, 1)
6+
power(x, 5)
7+
power(x, 10)
8+
9+
def letTest: Unit =
10+
let(0) { _ + 1 }
11+
let(0) { _.toString }
12+
let((4, 'a')) { _.swap }
13+
let(new Foo) { _.hashCode }
14+
15+
class Foo
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fails `testCompilation` as if the release flag was not set. But it was and the compile used it.
2+
3+
Manual tests show that this does work.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import scala.quoted.*
2+
3+
object Macros:
4+
5+
inline def power(x: Double, inline n: Int) = ${ powerCode('x, 'n) }
6+
7+
private def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] =
8+
unrolledPowerCode(x, n.valueOrError)
9+
10+
private def unrolledPowerCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] =
11+
if n == 0 then '{ 1.0 } // tests simple quotes without splices
12+
else if n % 2 == 1 then '{ $x * ${ unrolledPowerCode(x, n - 1) } } // tests simple splices
13+
else '{ val y = $x * $x; ${ unrolledPowerCode('y, n / 2) } } // tests splice with term capture
14+
15+
16+
inline def let[T, U](x: T)(inline body: T => U): U = ${ letCode('x, 'body) }
17+
18+
private def letCode[T: Type, U: Type](x: Expr[T], body: Expr[T => U])(using Quotes): Expr[U] =
19+
// tests use of Type
20+
'{ val y: T = $x; $body(y): U }
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import Macros.*
2+
3+
def powerTest(x: Double): Unit =
4+
power(x, 0)
5+
power(x, 1)
6+
power(x, 5)
7+
power(x, 10)
8+
9+
def letTest: Unit =
10+
let(0) { _ + 1 }
11+
let(0) { _.toString }
12+
let((4, 'a')) { _.swap }
13+
let(new Foo) { _.hashCode }
14+
15+
class Foo
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Disabled until https://github.com/lampepfl/dotty/issues/14306 is fixed

0 commit comments

Comments
 (0)