diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index f7ac72382a83..5921e5b055b7 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -690,6 +690,7 @@ class Definitions { @tu lazy val InternalQuotedModule: Symbol = ctx.requiredModule("scala.internal.quoted.CompileTime") @tu lazy val InternalQuoted_exprQuote : Symbol = InternalQuotedModule.requiredMethod("exprQuote") @tu lazy val InternalQuoted_exprSplice : Symbol = InternalQuotedModule.requiredMethod("exprSplice") + @tu lazy val InternalQuoted_exprNestedSplice : Symbol = InternalQuotedModule.requiredMethod("exprNestedSplice") @tu lazy val InternalQuoted_typeQuote : Symbol = InternalQuotedModule.requiredMethod("typeQuote") @tu lazy val InternalQuoted_patternHole: Symbol = InternalQuotedModule.requiredMethod("patternHole") @tu lazy val InternalQuoted_patternBindHoleAnnot: ClassSymbol = InternalQuotedModule.requiredClass("patternBindHole") diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 3e2fe80ee3c1..7269aacf584c 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -385,7 +385,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } else if (!printDebug && fun.hasType && fun.symbol == defn.InternalQuoted_exprQuote) keywordStr("'{") ~ toTextGlobal(args, ", ") ~ keywordStr("}") - else if (!printDebug && fun.hasType && fun.symbol == defn.InternalQuoted_exprSplice) + else if (!printDebug && fun.hasType && (fun.symbol == defn.InternalQuoted_exprSplice || fun.symbol == defn.InternalQuoted_exprNestedSplice)) keywordStr("${") ~ toTextGlobal(args, ", ") ~ keywordStr("}") else toTextLocal(fun) diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala index 37d86c95cf08..9fd31c872b3e 100644 --- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala @@ -80,11 +80,16 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( protected def transformSplice(body: Tree, splice: Tree)(implicit ctx: Context): Tree = { val body1 = transform(body)(spliceContext) splice match { - case Apply(fun @ TypeApply(_, _ :: qctx :: Nil), _) if splice.isTerm => + case Apply(fun @ TypeApply(_, _ :: Nil), _) if splice.isTerm => // Type of the splice itsel must also be healed // internal.Quoted.expr[F[T]](... T ...) --> internal.Quoted.expr[F[$t]](... T ...) val tp = checkType(splice.sourcePos).apply(splice.tpe.widenTermRefExpr) - cpy.Apply(splice)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: qctx :: Nil), body1 :: Nil) + cpy.Apply(splice)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), body1 :: Nil) + case Apply(f @ Apply(fun @ TypeApply(_, _), qctx :: Nil), _) if splice.isTerm => + // Type of the splice itsel must also be healed + // internal.Quoted.expr[F[T]](... T ...) --> internal.Quoted.expr[F[$t]](... T ...) + val tp = checkType(splice.sourcePos).apply(splice.tpe.widenTermRefExpr) + cpy.Apply(splice)(cpy.Apply(f)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), qctx :: Nil), body1 :: Nil) case splice: Select => val tagRef = getQuoteTypeTags.getTagRef(splice.qualifier.tpe.asInstanceOf[TermRef]) ref(tagRef).withSpan(splice.span) diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index 8c0b652846d7..ac9acb130ad5 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -343,7 +343,7 @@ class ReifyQuotes extends MacroTransform { val body = capturers(tree.symbol).apply(tree) val splice: Tree = if (tree.isType) body.select(tpnme.splice) - else ref(defn.InternalQuoted_exprSplice).appliedToTypes(List(tree.tpe, defn.QuoteContextClass.typeRef)).appliedTo(body) + else ref(defn.InternalQuoted_exprSplice).appliedToType(tree.tpe).appliedTo(body) transformSplice(body, splice) diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index ab57c3a3bc93..e05593193b5e 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -219,7 +219,7 @@ class SymUtils(val self: Symbol) extends AnyVal { /** Is symbol a splice operation? */ def isSplice(implicit ctx: Context): Boolean = - self == defn.InternalQuoted_exprSplice || self == defn.QuotedType_splice + self == defn.InternalQuoted_exprSplice || self == defn.InternalQuoted_exprNestedSplice || self == defn.QuotedType_splice def isCollectiveExtensionClass(using Context): Boolean = self.is(ModuleClass) && self.sourceModule.is(Extension, butNot = Method) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 62317485090e..fa33349ab1e9 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -80,7 +80,7 @@ trait QuotesAndSplices { spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx))) val baseType = pat.tpe.baseType(defn.QuotedExprClass) val argType = if baseType != NoType then baseType.argTypesHi.head else defn.NothingType - ref(defn.InternalQuoted_exprSplice).appliedToTypes(List(argType, defn.QuoteContextClass.typeRef)).appliedTo(pat) + ref(defn.InternalQuoted_exprSplice).appliedToType(argType).appliedTo(pat) } else { ctx.error(i"Type must be fully defined.\nConsider annotating the splice using a type ascription:\n ($tree: XYZ).", tree.expr.sourcePos) @@ -113,7 +113,12 @@ trait QuotesAndSplices { val qctxParam = untpd.makeParameter(qctxParamName, qctxParamTpt, untpd.Modifiers(Given)) val expr = untpd.Function(List(qctxParam), tree.expr).withSpan(tree.span) - typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprSplice.termRef), expr), pt)(ctx1).withSpan(tree.span) + val internalSplice = + outerQctx match + case Some(qctxRef) => untpd.Apply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprNestedSplice.termRef), qctxRef), expr) + case _ => untpd.Apply(untpd.ref(defn.InternalQuoted_exprSplice.termRef), expr) + + typedApply(internalSplice, pt)(ctx1).withSpan(tree.span) } } @@ -210,10 +215,7 @@ trait QuotesAndSplices { case Typed(Apply(fn, pat :: Nil), tpt) if fn.symbol == defn.InternalQuoted_exprSplice && !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => val tpt1 = transform(tpt) // Transform type bindings val exprTpt = AppliedTypeTree(TypeTree(defn.QuotedExprClass.typeRef), tpt1 :: Nil) - val newSplice = - ref(defn.InternalQuoted_exprSplice) - .appliedToTypes(List(tpt1.tpe, defn.QuoteContextClass.typeRef)) - .appliedTo(Typed(pat, exprTpt)) + val newSplice = ref(defn.InternalQuoted_exprSplice).appliedToType(tpt1.tpe).appliedTo(Typed(pat, exprTpt)) transform(newSplice) case Apply(fn, pat :: Nil) if fn.symbol == defn.InternalQuoted_exprSplice => try ref(defn.InternalQuoted_patternHole.termRef).appliedToType(tree.tpe).withSpan(tree.span) diff --git a/library/src-bootstrapped/scala/internal/quoted/CompileTime.scala b/library/src-bootstrapped/scala/internal/quoted/CompileTime.scala new file mode 100644 index 000000000000..c44b34d1ae6b --- /dev/null +++ b/library/src-bootstrapped/scala/internal/quoted/CompileTime.scala @@ -0,0 +1,54 @@ +package scala.internal.quoted + +import scala.annotation.{Annotation, compileTimeOnly} +import scala.quoted._ + +@compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime`") +object CompileTime { + + /** A term quote is desugared by the compiler into a call to this method */ + @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.exprQuote`") + def exprQuote[T](x: T): QuoteContext ?=> Expr[T] = ??? + + /** A term splice is desugared by the compiler into a call to this method */ + @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.exprSplice`") + def exprSplice[T](x: QuoteContext ?=> Expr[T]): T = ??? + + /** A term splice nested within a quote is desugared by the compiler into a call to this method. + * `ctx` is the `QuoteContext` that the quote of this splice uses. + */ + @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.exprNestedSplice`") + def exprNestedSplice[T](ctx: QuoteContext)(x: ctx.NestedContext ?=> Expr[T]): T = ??? + + /** A type quote is desugared by the compiler into a call to this method */ + @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.typeQuote`") + def typeQuote[T <: AnyKind]: Type[T] = ??? + + /** A splice in a quoted pattern is desugared by the compiler into a call to this method */ + @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.patternHole`") + def patternHole[T]: T = ??? + + /** A splice of a name in a quoted pattern is desugared by wrapping getting this annotation */ + @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.patternBindHole`") + class patternBindHole extends Annotation + + /** A splice of a name in a quoted pattern is that marks the definition of a type splice */ + @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.patternType`") + class patternType extends Annotation + + /** A type pattern that must be aproximated from above */ + @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.fromAbove`") + class fromAbove extends Annotation + + /** Artifact of pickled type splices + * + * During quote reification a quote `'{ ... F[$t] ... }` will be transformed into + * `'{ @quoteTypeTag type T$1 = $t ... F[T$1] ... }` to have a tree for `$t`. + * This artifact is removed during quote unpickling. + * + * See ReifyQuotes.scala and PickledQuotes.scala + */ + @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.quoteTypeTag`") + class quoteTypeTag extends Annotation + +} diff --git a/library/src/scala/internal/quoted/CompileTime.scala b/library/src-non-bootstrapped/scala/internal/quoted/CompileTime.scala similarity index 100% rename from library/src/scala/internal/quoted/CompileTime.scala rename to library/src-non-bootstrapped/scala/internal/quoted/CompileTime.scala diff --git a/tests/neg/i4044b.scala b/tests/neg/i4044b.scala index 58ea1d82440b..a81343c7c465 100644 --- a/tests/neg/i4044b.scala +++ b/tests/neg/i4044b.scala @@ -4,13 +4,13 @@ def test(using QuoteContext) = { '{ val qctx: QuoteContext = ??? - given qctx.type = qctx + given x1 as qctx.type = qctx val b = '{3} '{ val qctx: QuoteContext = ??? - given qctx.type = qctx + given x2 as qctx.type = qctx b // error ${b} diff --git a/tests/run-macros/power-macro/Macro_1.scala b/tests/run-macros/power-macro/Macro_1.scala new file mode 100644 index 000000000000..83eb63b8d25a --- /dev/null +++ b/tests/run-macros/power-macro/Macro_1.scala @@ -0,0 +1,13 @@ + +import scala.quoted._ + +inline def power(x: Double, inline n: Int) = ${ powerCode1('x, 'n) } + +private def powerCode1(using qctx: QuoteContext)(x: Expr[Double], n: Expr[Int]): Expr[Double] = + powerCode(x, n.value) + +private def powerCode(using qctx: QuoteContext)(x: Expr[Double], n: Int): Expr[Double] = + if (n == 0) Expr(1.0) + else if (n == 1) x + else if (n % 2 == 0) '{ val y = $x * $x; ${ powerCode('y, n / 2) } } + else '{ $x * ${ powerCode(x, n - 1) } } diff --git a/tests/run-macros/power-macro/Test_2.scala b/tests/run-macros/power-macro/Test_2.scala new file mode 100644 index 000000000000..0394ddffa411 --- /dev/null +++ b/tests/run-macros/power-macro/Test_2.scala @@ -0,0 +1,6 @@ +@main def Test = { + println(power(5, 0)) + println(power(5, 1)) + println(power(5, 2)) + println(power(5, 3)) +}