Skip to content

Fix #8045: Refine QuoteContext{val tasty} provided by splices #8281

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 4 commits into from
Feb 20, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1228,7 +1228,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
val argType =
if (baseType != NoType) baseType.argTypesHi.head
else defn.NothingType
ref(defn.InternalQuoted_exprSplice).appliedToType(argType).appliedTo(tree)
ref(defn.InternalQuoted_exprSplice).appliedToTypes(List(argType, defn.QuoteContextClass.typeRef)).appliedTo(tree)
}
def unapply(tree: Tree)(implicit ctx: Context): Option[Tree] = tree match {
case Apply(fn, arg :: Nil) if fn.symbol == defn.InternalQuoted_exprSplice => Some(arg)
Expand Down
27 changes: 26 additions & 1 deletion compiler/src/dotty/tools/dotc/core/StagingContext.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dotty.tools.dotc.core

import dotty.tools.dotc.core.Contexts._
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.util.Property

import scala.collection.mutable
Expand All @@ -10,6 +11,11 @@ object StagingContext {
/** A key to be used in a context property that tracks the quoteation level */
private val QuotationLevel = new Property.Key[Int]

/** A key to be used in a context property that tracks the quoteation stack.
* Stack containing the QuoteContext references recieved by the surrounding quotes.
*/
private val QuoteQontextStack = new Property.Key[List[tpd.Tree]]

/** All enclosing calls that are currently inlined, from innermost to outermost. */
def level(implicit ctx: Context): Int =
ctx.property(QuotationLevel).getOrElse(0)
Expand All @@ -18,8 +24,27 @@ object StagingContext {
def quoteContext(implicit ctx: Context): Context =
ctx.fresh.setProperty(QuotationLevel, level + 1)

/** Context with an incremented quotation level and pushes a refecence to a QuoteContext on the quote context stack */
def pushQuoteContext(qctxRef: tpd.Tree)(implicit ctx: Context): Context =
val old = ctx.property(QuoteQontextStack).getOrElse(List.empty)
ctx.fresh.setProperty(QuotationLevel, level + 1)
.setProperty(QuoteQontextStack, qctxRef :: old)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if the stack can be avoided using scoping rules for implicits.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it could but we would need to track a level context and a splice context. The errors when those implicit are not found would also be hard to interpret.


/** Context with a decremented quotation level. */
def spliceContext(implicit ctx: Context): Context =
ctx.fresh.setProperty(QuotationLevel, level - 1)
}

/** Context with a decremented quotation level and pops the Some of top of the quote context stack or None if the stack is empty.
* The quotation stack could be empty if we are in a top level splice or an eroneous splice directly witin a top level splice.
*/
def popQuoteContext()(implicit ctx: Context): (Option[tpd.Tree], Context) =
val ctx1 = ctx.fresh.setProperty(QuotationLevel, level - 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The property QuotationLevel is also mutated in spliceContext and quoteContext. This may cause accidental errors when used without care. Maybe remove spliceContext and quoteContext, since they are not used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use those in PCPCheckAndHeal and ReifyQuotes. There we only need to track the level.

val head =
ctx.property(QuoteQontextStack) match
case Some(x :: xs) =>
ctx1.setProperty(QuoteQontextStack, xs)
Some(x)
case _ =>
None // Splice at level 0 or lower
(head, ctx1)
}
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ object PickledQuotes {
}.apply(tp)

/** Unpickle the tree contained in the TastyExpr */
def unpickleExpr(tasty: PickledQuote, args: PickledExprArgs)(implicit ctx: Context): Tree = {
def unpickleExpr(tasty: PickledQuote, args: PickledArgs)(implicit ctx: Context): Tree = {
val tastyBytes = TastyString.unpickle(tasty)
val unpickled = unpickle(tastyBytes, args, isType = false)(ctx.addMode(Mode.ReadPositions))
/** Force unpickling of the tree, removes the spliced type `@quotedTypeTag type` definitions and dealiases references to `@quotedTypeTag type` */
Expand All @@ -76,7 +76,7 @@ object PickledQuotes {
}

/** Unpickle the tree contained in the TastyType */
def unpickleType(tasty: PickledQuote, args: PickledTypeArgs)(implicit ctx: Context): Tree = {
def unpickleType(tasty: PickledQuote, args: PickledArgs)(implicit ctx: Context): Tree = {
val tastyBytes = TastyString.unpickle(tasty)
val unpickled = unpickle(tastyBytes, args, isType = true)(ctx.addMode(Mode.ReadPositions))
val tpt = unpickled match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend
// QUOTE UNPICKLING //
//////////////////////

def unpickleExpr(repr: Unpickler.PickledQuote, args: Unpickler.PickledExprArgs): scala.quoted.Expr[?] =
def unpickleExpr(repr: Unpickler.PickledQuote, args: Unpickler.PickledArgs): scala.quoted.Expr[?] =
new TastyTreeExpr(PickledQuotes.unpickleExpr(repr, args), compilerId)

def unpickleType(repr: Unpickler.PickledQuote, args: Unpickler.PickledTypeArgs): scala.quoted.Type[?] =
def unpickleType(repr: Unpickler.PickledQuote, args: Unpickler.PickledArgs): scala.quoted.Type[?] =
new TreeType(PickledQuotes.unpickleType(repr, args), compilerId)


Expand Down Expand Up @@ -67,7 +67,7 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend
def Context_requiredMethod(self: Context)(path: String): Symbol = self.requiredMethod(path)
def Context_isJavaCompilationUnit(self: Context): Boolean = self.compilationUnit.isInstanceOf[fromtasty.JavaCompilationUnit]
def Context_isScala2CompilationUnit(self: Context): Boolean = self.compilationUnit.isInstanceOf[fromtasty.Scala2CompilationUnit]
def Context_compilationUnitClassname(self: Context): String =
def Context_compilationUnitClassname(self: Context): String =
self.compilationUnit match {
case cu: fromtasty.JavaCompilationUnit => cu.className
case cu: fromtasty.Scala2CompilationUnit => cu.className
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ 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, _) if splice.isTerm =>
case 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.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), body1 :: Nil)
cpy.Apply(splice)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: qctx :: Nil), body1 :: Nil)
case splice: Select => cpy.Select(splice)(body1, splice.name)
}
}
Expand Down
7 changes: 2 additions & 5 deletions compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,8 @@ class ReifyQuotes extends MacroTransform {
val meth =
if (isType) ref(defn.Unpickler_unpickleType).appliedToType(originalTp)
else ref(defn.Unpickler_unpickleExpr).appliedToType(originalTp.widen)
val spliceResType =
if (isType) defn.QuotedTypeClass.typeRef.appliedTo(WildcardType)
else defn.FunctionType(1, isContextual = true).appliedTo(defn.QuoteContextClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(defn.AnyType)) | defn.QuotedTypeClass.typeRef.appliedTo(WildcardType)
val pickledQuoteStrings = liftList(PickledQuotes.pickleQuote(body).map(x => Literal(Constant(x))), defn.StringType)
val splicesList = liftList(splices, defn.FunctionType(1).appliedTo(defn.SeqType.appliedTo(defn.AnyType), spliceResType))
val splicesList = liftList(splices, defn.FunctionType(1).appliedTo(defn.SeqType.appliedTo(defn.AnyType), defn.AnyType))
meth.appliedTo(pickledQuoteStrings, splicesList)
}

Expand Down Expand Up @@ -394,7 +391,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).appliedToType(tree.tpe).appliedTo(body)
else ref(defn.InternalQuoted_exprSplice).appliedToTypes(List(tree.tpe, defn.QuoteContextClass.typeRef)).appliedTo(body)

transformSplice(body, splice)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap

case Quoted(quotedTree) =>
dropEmptyBlocks(quotedTree) match {
case Spliced(t) => transform(t) // '{ $x } --> x
case Spliced(t) =>
// '{ $x } --> x
// and adapt the refinment of `QuoteContext { type tasty: ... } ?=> Expr[T]`
transform(t).asInstance(tree.tpe)
case _ => transformQuotation(quotedTree, tree)
}

Expand Down
34 changes: 20 additions & 14 deletions compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,16 @@ trait QuotesAndSplices {
ctx.warning("Canceled splice directly inside a quote. '[ ${ XYZ } ] is equivalent to XYZ.", tree.sourcePos)
case _ =>
}
val qctx = inferImplicitArg(defn.QuoteContextClass.typeRef, tree.span)
if (level == 0 && qctx.tpe.isInstanceOf[SearchFailureType])
ctx.error(missingArgMsg(qctx, defn.QuoteContextClass.typeRef, ""), ctx.source.atSpan(tree.span))
val tree1 =
if (ctx.mode.is(Mode.Pattern) && level == 0) typedQuotePattern(tree, pt)
else if (tree.quoted.isType) typedTypeApply(untpd.TypeApply(untpd.ref(defn.InternalQuoted_typeQuote.termRef), tree.quoted :: Nil), pt)(quoteContext)
else typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprQuote.termRef), tree.quoted), pt)(quoteContext)
if ctx.mode.is(Mode.Pattern) && level == 0 then
typedQuotePattern(tree, pt, qctx)
else if (tree.quoted.isType)
typedTypeApply(untpd.TypeApply(untpd.ref(defn.InternalQuoted_typeQuote.termRef), tree.quoted :: Nil), pt)(quoteContext)
else
typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprQuote.termRef), tree.quoted), pt)(pushQuoteContext(qctx)).select(nme.apply).appliedTo(qctx)
tree1.withSpan(tree.span)
}

Expand Down Expand Up @@ -85,19 +91,23 @@ trait QuotesAndSplices {
markAsMacro(ctx)
}

val (outerQctx, ctx1) = popQuoteContext()

// Explicitly provide the given QuoteContext of the splice.
// * Avoids leaking implementation details of scala.internal.quoted.CompileTime.exprSplice,
// such as exprSplice taking a ?=> function argument
// * Provide meaningful names for QuoteContext synthesized by within `${ ... }`
// * TODO preserve QuoteContext.tasty path dependent type (see comment below and #8045)
val qctxParamName = NameKinds.UniqueName.fresh(s"qctx${level - 1}_".toTermName)
// TODO: Refine QuoteContext with the tasty context that the quote received
// If encoloseing quote receives `qctx` then this type should be `QuoteContext { val tasty: qxtx.tasty.type }`
val qctxParamTpt = untpd.TypedSplice(TypeTree(defn.QuoteContextClass.typeRef))
// * If within a quote, provide a QuoteContext is linked typewise with the outer QuoteContext
val qctxParamName = NameKinds.UniqueName.fresh(s"qctx${if level > 0 then level - 1 else ""}_".toTermName)
val qctxParamTpe = outerQctx match {
case Some(qctxRef) => qctxRef.tpe.select("NestedContext".toTypeName)
case _ => defn.QuoteContextClass.typeRef // splice at level 0 (or lower)
}
val qctxParamTpt = untpd.TypedSplice(TypeTree(qctxParamTpe))
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)(spliceContext).withSpan(tree.span)
typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprSplice.termRef), expr), pt)(ctx1).withSpan(tree.span)
}
}

Expand Down Expand Up @@ -326,11 +336,7 @@ trait QuotesAndSplices {
* ) => ...
* ```
*/
private def typedQuotePattern(tree: untpd.Quote, pt: Type)(implicit ctx: Context): Tree = {
val qctx = inferImplicitArg(defn.QuoteContextClass.typeRef, tree.span)
if (level == 0 && qctx.tpe.isInstanceOf[SearchFailureType])
ctx.error(missingArgMsg(qctx, defn.QuoteContextClass.typeRef, ""), ctx.source.atSpan(tree.span))

private def typedQuotePattern(tree: untpd.Quote, pt: Type, qctx: Tree)(implicit ctx: Context): Tree = {
val quoted = tree.quoted
val exprPt = pt.baseType(if quoted.isType then defn.QuotedTypeClass else defn.QuotedExprClass)
val quotedPt = exprPt.argInfos.headOption match {
Expand Down
48 changes: 48 additions & 0 deletions library/src-bootstrapped/scala/internal/quoted/CompileTime.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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, QCtx <: QuoteContext](x: QCtx ?=> 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

}
7 changes: 3 additions & 4 deletions library/src/scala/internal/quoted/Unpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,18 @@ import scala.quoted.{Expr, QuoteContext, Type}
object Unpickler {

type PickledQuote = List[String]
type PickledExprArgs = Seq[Seq[Any] => ((QuoteContext ?=> Expr[Any]) | Type[_])]
type PickledTypeArgs = Seq[Seq[Any] => Type[_]]
type PickledArgs = Seq[Seq[Any] => Any/*(([QCtx <: QuoteContext] =>> QCtx ?=> Expr[Any]) | Type[_])*/]

/** Unpickle `repr` which represents a pickled `Expr` tree,
* replacing splice nodes with `args`
*/
def unpickleExpr[T](repr: PickledQuote, args: PickledExprArgs): QuoteContext ?=> Expr[T] =
def unpickleExpr[T](repr: PickledQuote, args: PickledArgs): QuoteContext ?=> Expr[T] =
summon[QuoteContext].tasty.internal.unpickleExpr(repr, args).asInstanceOf[Expr[T]]

/** Unpickle `repr` which represents a pickled `Type` tree,
* replacing splice nodes with `args`
*/
def unpickleType[T](repr: PickledQuote, args: PickledTypeArgs): QuoteContext ?=> Type[T] =
def unpickleType[T](repr: PickledQuote, args: PickledArgs): QuoteContext ?=> Type[T] =
summon[QuoteContext].tasty.internal.unpickleType(repr, args).asInstanceOf[Type[T]]

}
21 changes: 20 additions & 1 deletion library/src/scala/quoted/QuoteContext.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,32 @@ import scala.quoted.show.SyntaxHighlight
*
* @param tasty Typed AST API. Usage: `def f(qctx: QuoteContext) = { import qctx.tasty.{_, given}; ... }`.
*/
class QuoteContext(val tasty: scala.tasty.Reflection) {
class QuoteContext(val tasty: scala.tasty.Reflection) { self =>

/** Type of a QuoteContext profided by a splice within a quote that took this context.
* It is only required if working with the reflection API.
*
* Usually it is infered by the quotes an splices typing. But sometimes it is necessary
* to explicitly state that a context is nested as in the following example:
*
* ```scala
* def run(using qctx: QuoteContext)(tree: qctx.tasty.Tree): Unit =
* def nested()(using qctx.NestedContext): Expr[Int] = '{ ${ makeExpr(tree) } + 1 }
* '{ ${ nested() } + 2 }
* def makeExpr(using qctx: QuoteContext)(tree: qctx.tasty.Tree): Expr[Int] = ???
* ```
*/
type NestedContext = QuoteContext {
val tasty: self.tasty.type
}

/** Show the fully elaborated source code representation of an expression */
def show(expr: Expr[_], syntaxHighlight: SyntaxHighlight): String = {
import tasty.{_, given}
expr.unseal.showWith(syntaxHighlight)
}

/** Show the fully elaborated source code representation of a type */
def show(tpe: Type[_], syntaxHighlight: SyntaxHighlight): String = {
import tasty.{_, given}
tpe.unseal.showWith(syntaxHighlight)
Expand Down
4 changes: 2 additions & 2 deletions library/src/scala/tasty/reflect/CompilerInterface.scala
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,12 @@ trait CompilerInterface {
/** Unpickle `repr` which represents a pickled `Expr` tree,
* replacing splice nodes with `args`
*/
def unpickleExpr(repr: Unpickler.PickledQuote, args: Unpickler.PickledExprArgs): scala.quoted.Expr[_]
def unpickleExpr(repr: Unpickler.PickledQuote, args: Unpickler.PickledArgs): scala.quoted.Expr[_]

/** Unpickle `repr` which represents a pickled `Type` tree,
* replacing splice nodes with `args`
*/
def unpickleType(repr: Unpickler.PickledQuote, args: Unpickler.PickledTypeArgs): scala.quoted.Type[_]
def unpickleType(repr: Unpickler.PickledQuote, args: Unpickler.PickledArgs): scala.quoted.Type[_]


/////////////
Expand Down
5 changes: 5 additions & 0 deletions tests/pos/i8045.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import scala.quoted._
object Test
def run(using qctx: QuoteContext)(tree: qctx.tasty.Tree): Unit =
'{ ${ makeExpr(tree) } + 1 }
def makeExpr(using qctx: QuoteContext)(tree: qctx.tasty.Tree): Expr[Int] = ???
8 changes: 8 additions & 0 deletions tests/pos/i8045b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import scala.quoted._
object Test
def run(using qctx: QuoteContext)(tree: qctx.tasty.Tree): Unit =
def nested()(using qctx.NestedContext): Expr[Int] =
'{ ${ makeExpr(tree) } + 1 }
'{ ${ nested() } + 2 }

def makeExpr(using qctx: QuoteContext)(tree: qctx.tasty.Tree): Expr[Int] = ???
2 changes: 1 addition & 1 deletion tests/run-staging/quote-nested-2.check
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
((qctx: scala.quoted.QuoteContext) ?=> {
val a: scala.quoted.Expr[scala.Int] = scala.internal.quoted.CompileTime.exprQuote[scala.Int](4).apply(using qctx)
((qctx1_$1: scala.quoted.QuoteContext) ?=> a).apply(using qctx)
((qctx1_$1: qctx.NestedContext) ?=> a).asInstanceOf[scala.ContextFunction1[scala.quoted.QuoteContext, scala.quoted.Expr[scala.Int]]].apply(using qctx)
})
2 changes: 1 addition & 1 deletion tests/run-staging/quote-nested-5.check
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
((qctx: scala.quoted.QuoteContext) ?=> {
val a: scala.quoted.Expr[scala.Int] = scala.internal.quoted.CompileTime.exprQuote[scala.Int](4).apply(using qctx)
((qctx2: scala.quoted.QuoteContext) ?=> ((qctx1_$1: scala.quoted.QuoteContext) ?=> a).apply(using qctx2)).apply(using qctx)
((qctx2: scala.quoted.QuoteContext) ?=> ((qctx1_$1: qctx2.NestedContext) ?=> a).asInstanceOf[scala.ContextFunction1[scala.quoted.QuoteContext, scala.quoted.Expr[scala.Int]]].apply(using qctx2)).apply(using qctx)
})