Skip to content

Handle QuoteContext paths explicitly #8497

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

Merged
Merged
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
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 7 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/SymUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 8 additions & 6 deletions compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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)
Expand Down
54 changes: 54 additions & 0 deletions library/src-bootstrapped/scala/internal/quoted/CompileTime.scala
Original file line number Diff line number Diff line change
@@ -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

}
4 changes: 2 additions & 2 deletions tests/neg/i4044b.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
13 changes: 13 additions & 0 deletions tests/run-macros/power-macro/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -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) } }
6 changes: 6 additions & 0 deletions tests/run-macros/power-macro/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@main def Test = {
println(power(5, 0))
println(power(5, 1))
println(power(5, 2))
println(power(5, 3))
}