-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||
|
@@ -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) | ||
|
@@ -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) | ||
|
||
/** 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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The property There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use those in |
||
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) | ||
} |
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 | ||
|
||
} |
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] = ??? |
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] = ??? |
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) | ||
}) |
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) | ||
}) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.