Skip to content

Revert "Remove support -scala-output-version with quotes" #14991

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,9 @@ class Definitions {


@tu lazy val QuoteUnpicklerClass: ClassSymbol = requiredClass("scala.quoted.runtime.QuoteUnpickler")
@tu lazy val QuoteUnpickler_unpickleExpr: Symbol = QuoteUnpicklerClass.requiredMethod("unpickleExpr")
@tu lazy val QuoteUnpickler_unpickleExprV2: Symbol = QuoteUnpicklerClass.requiredMethod("unpickleExprV2")
@tu lazy val QuoteUnpickler_unpickleType: Symbol = QuoteUnpicklerClass.requiredMethod("unpickleType")
@tu lazy val QuoteUnpickler_unpickleTypeV2: Symbol = QuoteUnpicklerClass.requiredMethod("unpickleTypeV2")

@tu lazy val QuoteMatchingClass: ClassSymbol = requiredClass("scala.quoted.runtime.QuoteMatching")
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ object PickledQuotes {
/** `typeHole`/`types` argument of `QuoteUnpickler.{unpickleExpr,unpickleExprV2,unpickleType,unpickleTypeV2}` */
enum TypeHole:
/** `termHole` argument of `QuoteUnpickler.{unpickleExpr, unpickleType}`.
* From code compiled with Scala 3.0.x and 3.1.x.
* From code compiled with Scala 3.0.x and 3.1.x or with -scala-output-version 3.0 and 3.1
* Note: For `unpickleType` it will always be `null`.
*/
case V1(evalHole: Null | ((Int, Seq[scala.quoted.Type[?]]) => scala.quoted.Type[?]))
Expand All @@ -65,7 +65,7 @@ object PickledQuotes {

enum ExprHole:
/** `termHole` argument of `QuoteUnpickler.{unpickleExpr, unpickleType}`.
* From code compiled with Scala 3.0.x and 3.1.x.
* From code compiled with Scala 3.0.x and 3.1.x or with -scala-output-version 3.0 and 3.1
* Note: For `unpickleType` it will always be `null`.
*/
case V1(evalHole: Null | ((Int, Seq[ExprHole.ArgV1], scala.quoted.Quotes) => scala.quoted.Expr[?]))
Expand Down
42 changes: 36 additions & 6 deletions compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ object PickleQuotes {
* this closure is always applied directly to the actual context and the BetaReduce phase removes it.
*/
def pickleAsTasty() = {

val unpickleV1 = ctx.scalaRelease <= Release3_1

val pickleQuote = PickledQuotes.pickleQuote(body)
val pickledQuoteStrings = pickleQuote match
case x :: Nil => Literal(Constant(x))
Expand All @@ -303,8 +306,26 @@ object PickleQuotes {

// This and all closures in typeSplices are removed by the BetaReduce phase
val types =
if typeSplices.isEmpty then Literal(Constant(null)) // keep pickled quote without contents as small as possible
else SeqLiteral(typeSplices.map(_._1), TypeTree(defn.QuotedTypeClass.typeRef.appliedTo(WildcardType)))
if unpickleV1 then
if typeSplices.isEmpty then Literal(Constant(null)) // keep pickled quote without contents as small as possible
else
Lambda(
MethodType(
List(nme.idx, nme.contents).map(name => UniqueName.fresh(name).toTermName),
List(defn.IntType, defn.SeqType.appliedTo(defn.AnyType)),
defn.QuotedTypeClass.typeRef.appliedTo(WildcardType)),
args => {
val cases = typeSplices.map { case (splice, idx) =>
CaseDef(Literal(Constant(idx)), EmptyTree, splice)
}
cases match
case CaseDef(_, _, rhs) :: Nil => rhs
case _ => Match(args(0).annotated(New(ref(defn.UncheckedAnnot.typeRef))), cases)
}
)
else // if unpickleV2 then
if typeSplices.isEmpty then Literal(Constant(null)) // keep pickled quote without contents as small as possible
else SeqLiteral(typeSplices.map(_._1), TypeTree(defn.QuotedTypeClass.typeRef.appliedTo(WildcardType)))

// This and all closures in termSplices are removed by the BetaReduce phase
val termHoles =
Expand All @@ -320,7 +341,12 @@ object PickleQuotes {
val defn.FunctionOf(argTypes, defn.FunctionOf(quotesType :: _, _, _, _), _, _) = splice.tpe
val rhs = {
val spliceArgs = argTypes.zipWithIndex.map { (argType, i) =>
args(1).select(nme.apply).appliedTo(Literal(Constant(i))).asInstance(argType)
val argi = args(1).select(nme.apply).appliedTo(Literal(Constant(i)))
if unpickleV1 && argType.derivesFrom(defn.QuotedExprClass) then
val argType1 = defn.FunctionType(1).appliedTo(defn.QuotesClass.typeRef, argType)
argi.asInstance(argType1).select(nme.apply).appliedTo(args(2))
else
argi.asInstance(argType)
}
val Block(List(ddef: DefDef), _) = splice
// TODO: beta reduce inner closure? Or wait until BetaReduce phase?
Expand All @@ -337,10 +363,14 @@ object PickleQuotes {
val quotedType = quoteClass.typeRef.appliedTo(originalTp)
val lambdaTpe = MethodType(defn.QuotesClass.typeRef :: Nil, quotedType)
val unpickleMeth =
if isType then defn.QuoteUnpickler_unpickleTypeV2
else defn.QuoteUnpickler_unpickleExprV2
if unpickleV1 then
if isType then defn.QuoteUnpickler_unpickleType
else defn.QuoteUnpickler_unpickleExpr
else // if unpickleV2 then
if isType then defn.QuoteUnpickler_unpickleTypeV2
else defn.QuoteUnpickler_unpickleExprV2
val unpickleArgs =
if isType then List(pickledQuoteStrings, types)
if isType && !unpickleV1 then List(pickledQuoteStrings, types)
else List(pickledQuoteStrings, types, termHoles)
quotes
.asInstance(defn.QuoteUnpicklerClass.typeRef)
Expand Down
8 changes: 7 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/Splicing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,13 @@ class Splicing extends MacroTransform:
val hole = tpd.Hole(false, holeIdx, Nil, ref(qual), TypeTree(tp))
typeHoles.put(qual.symbol, hole)
hole
cpy.TypeDef(tree)(rhs = hole)
val rhs =
if ctx.scalaRelease <= Release3_1 then
val secondHoleIdx = numHoles
numHoles += 1
TypeBoundsTree(hole, cpy.Hole(hole)(idx = secondHoleIdx))
else hole
cpy.TypeDef(tree)(rhs = rhs)
case Apply(Select(Apply(TypeApply(fn,_), List(code)),nme.apply),List(quotes))
if fn.symbol == defn.QuotedRuntime_exprQuote =>
super.transform(tree)(using quoteContext)
Expand Down
17 changes: 17 additions & 0 deletions sbt-test/scala3-compat/macros-forward-3.0/app/App.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package app

import lib.*

def powerTest(x: Double): Unit =
power(x, 0)
power(x, 1)
power(x, 5)
power(x, 10)

def letTest: Unit =
let(0) { _ + 1 }
let(0) { _.toString }
let((4, 'a')) { _.swap }
let(new Foo) { _.hashCode }

class Foo
14 changes: 14 additions & 0 deletions sbt-test/scala3-compat/macros-forward-3.0/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
lazy val checkOptions = Seq("-Xcheck-macros", "-Ycheck:all", "-Yno-double-bindings")

lazy val lib = project.in(file("lib"))
.settings(
scalacOptions ++= Seq("-scala-output-version", "3.0") ++ checkOptions
)

lazy val app = project.in(file("app"))
.dependsOn(lib)
.settings(
scalaVersion := "3.0.2",
scalacOptions ++= checkOptions,
dependencyOverrides += scalaOrganization.value %% "scala3-library" % scalaVersion.value,
)
20 changes: 20 additions & 0 deletions sbt-test/scala3-compat/macros-forward-3.0/lib/Macro.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package lib

import scala.quoted.*

inline def power(x: Double, inline n: Int) = ${ powerCode('x, 'n) }

private def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] =
unrolledPowerCode(x, n.valueOrError)

private def unrolledPowerCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] =
if n == 0 then '{ 1.0 } // tests simple quotes without splices
else if n % 2 == 1 then '{ $x * ${ unrolledPowerCode(x, n - 1) } } // tests simple splices
else '{ val y = $x * $x; ${ unrolledPowerCode('y, n / 2) } } // tests splice with term capture


inline def let[T, U](x: T)(inline body: T => U): U = ${ letCode('x, 'body) }

private def letCode[T: Type, U: Type](x: Expr[T], body: Expr[T => U])(using Quotes): Expr[U] =
// tests use of Type
'{ val y: T = $x; $body(y): U }
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import sbt._
import Keys._

object DottyInjectedPlugin extends AutoPlugin {
override def requires = plugins.JvmPlugin
override def trigger = allRequirements

override val projectSettings = Seq(
scalaVersion := sys.props("plugin.scalaVersion")
)
}
1 change: 1 addition & 0 deletions sbt-test/scala3-compat/macros-forward-3.0/test
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
> app/compile
20 changes: 20 additions & 0 deletions tests/disabled/pos-macros/forwardCompat-3.0/Macro_1_r3.0.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import scala.quoted.*

object Macros:

inline def power(x: Double, inline n: Int) = ${ powerCode('x, 'n) }

private def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] =
unrolledPowerCode(x, n.valueOrError)

private def unrolledPowerCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] =
if n == 0 then '{ 1.0 } // tests simple quotes without splices
else if n % 2 == 1 then '{ $x * ${ unrolledPowerCode(x, n - 1) } } // tests simple splices
else '{ val y = $x * $x; ${ unrolledPowerCode('y, n / 2) } } // tests splice with term capture


inline def let[T, U](x: T)(inline body: T => U): U = ${ letCode('x, 'body) }

private def letCode[T: Type, U: Type](x: Expr[T], body: Expr[T => U])(using Quotes): Expr[U] =
// tests use of Type
'{ val y: T = $x; $body(y): U }
15 changes: 15 additions & 0 deletions tests/disabled/pos-macros/forwardCompat-3.0/Test_2_c3.0.2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Macros.*

def powerTest(x: Double): Unit =
power(x, 0)
power(x, 1)
power(x, 5)
power(x, 10)

def letTest: Unit =
let(0) { _ + 1 }
let(0) { _.toString }
let((4, 'a')) { _.swap }
let(new Foo) { _.hashCode }

class Foo
3 changes: 3 additions & 0 deletions tests/disabled/pos-macros/forwardCompat-3.0/why.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fails `testCompilation` as if the release flag was not set. But it was and the compile used it.

Manual tests show that this does work.
20 changes: 20 additions & 0 deletions tests/disabled/pos-macros/forwardCompat-3.1/Macro_1_r3.1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import scala.quoted.*

object Macros:

inline def power(x: Double, inline n: Int) = ${ powerCode('x, 'n) }

private def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] =
unrolledPowerCode(x, n.valueOrError)

private def unrolledPowerCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] =
if n == 0 then '{ 1.0 } // tests simple quotes without splices
else if n % 2 == 1 then '{ $x * ${ unrolledPowerCode(x, n - 1) } } // tests simple splices
else '{ val y = $x * $x; ${ unrolledPowerCode('y, n / 2) } } // tests splice with term capture


inline def let[T, U](x: T)(inline body: T => U): U = ${ letCode('x, 'body) }

private def letCode[T: Type, U: Type](x: Expr[T], body: Expr[T => U])(using Quotes): Expr[U] =
// tests use of Type
'{ val y: T = $x; $body(y): U }
15 changes: 15 additions & 0 deletions tests/disabled/pos-macros/forwardCompat-3.1/Test_2_c3.1.0.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Macros.*

def powerTest(x: Double): Unit =
power(x, 0)
power(x, 1)
power(x, 5)
power(x, 10)

def letTest: Unit =
let(0) { _ + 1 }
let(0) { _.toString }
let((4, 'a')) { _.swap }
let(new Foo) { _.hashCode }

class Foo
1 change: 1 addition & 0 deletions tests/disabled/pos-macros/forwardCompat-3.1/why.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Disabled until https://github.com/lampepfl/dotty/issues/14306 is fixed