Skip to content

Commit 9cc0c57

Browse files
committed
Add scala.quoted.Scope
1 parent 0655346 commit 9cc0c57

File tree

238 files changed

+2079
-1637
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

238 files changed

+2079
-1637
lines changed

.github/workflows/ci.yaml

Lines changed: 341 additions & 341 deletions
Large diffs are not rendered by default.

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,8 +681,10 @@ class Definitions {
681681
@tu lazy val QuotedExprModule: Symbol = QuotedExprClass.companionModule
682682
@tu lazy val QuotedExprModule_nullExpr: Symbol = QuotedExprModule.requiredMethod(nme.nullExpr)
683683
@tu lazy val QuotedExprModule_unitExpr: Symbol = QuotedExprModule.requiredMethod(nme.unitExpr)
684+
@tu lazy val QuotedExprModule_unsafeExpr: Symbol = QuotedExprModule.requiredMethod("unsafeExpr")
684685

685686
@tu lazy val QuoteContextClass: ClassSymbol = requiredClass("scala.quoted.QuoteContext")
687+
@tu lazy val ScopeClass: ClassSymbol = requiredClass("scala.quoted.Scope")
686688

687689
@tu lazy val LiftableModule: Symbol = requiredModule("scala.quoted.Liftable")
688690
@tu lazy val LiftableModule_BooleanIsLiftable: Symbol = LiftableModule.requiredMethod("BooleanIsLiftable")
@@ -719,6 +721,7 @@ class Definitions {
719721

720722
@tu lazy val QuotedTypeModule: Symbol = QuotedTypeClass.companionModule
721723
@tu lazy val QuotedTypeModule_apply: Symbol = QuotedTypeModule.requiredMethod("apply")
724+
@tu lazy val QuotedTypeModule_unsafeType: Symbol = QuotedTypeModule.requiredMethod("unsafeType")
722725

723726
@tu lazy val TastyReflectionClass: ClassSymbol = requiredClass("scala.tasty.Reflection")
724727

compiler/src/dotty/tools/dotc/core/StagingContext.scala

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ object StagingContext {
3232
ctx.fresh.setProperty(QuotationLevel, level + 1)
3333

3434
/** Context with an incremented quotation level and pushes a refecence to a QuoteContext on the quote context stack */
35-
def pushQuoteContext(qctxRef: tpd.Tree)(using Context): Context =
35+
def pushQuoteContext(scopeRef: tpd.Tree)(using Context): Context =
3636
val old = ctx.property(QuoteContextStack).getOrElse(List.empty)
3737
ctx.fresh.setProperty(QuotationLevel, level + 1)
38-
.setProperty(QuoteContextStack, qctxRef :: old)
38+
.setProperty(QuoteContextStack, scopeRef :: old)
3939

4040
/** Context with a decremented quotation level. */
4141
def spliceContext(using Context): Context =
@@ -60,4 +60,10 @@ object StagingContext {
6060
case _ =>
6161
None // Splice at level 0 or lower
6262
(head, ctx1)
63+
64+
def peekQuoteContext()(implicit ctx: Context): Option[tpd.Tree] =
65+
ctx.property(QuoteContextStack) match
66+
case Some(x :: xs) => Some(x)
67+
case _ => None // Splice at level 0 or lower
68+
6369
}

compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ object PickledQuotes {
7676
override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match {
7777
case Hole(isTerm, idx, args) =>
7878
val reifiedArgs = args.map { arg =>
79-
if (arg.isTerm) (using qctx: scala.quoted.QuoteContext) => new scala.internal.quoted.Expr(arg, QuoteContext.scopeId)
79+
if (arg.isTerm) (using scope: scala.quoted.QuoteContext) => new scala.internal.quoted.Expr(arg, QuoteContext.scopeId)
8080
else new scala.internal.quoted.Type(arg, QuoteContext.scopeId)
8181
}
8282
if isTerm then

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,7 @@ class TreePickler(pickler: TastyPickler) {
734734

735735
def pickle(trees: List[Tree])(using Context): Unit = {
736736
trees.foreach(tree => if (!tree.isEmpty) pickleTree(tree))
737-
def missing = forwardSymRefs.keysIterator.map(sym => sym.showLocated + "(line " + sym.sourcePos.line + ")").toList
737+
def missing = forwardSymRefs.keysIterator.map(sym => sym.showLocated + (if sym.sourcePos.exists then "(line " + sym.sourcePos.line + ")" else "")).toList
738738
assert(forwardSymRefs.isEmpty, i"unresolved symbols: $missing%, % when pickling ${ctx.source}")
739739
}
740740

compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,14 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
131131
// internal.Quoted.expr[F[T]](... T ...) --> internal.Quoted.expr[F[$t]](... T ...)
132132
val tp = healType(splice.sourcePos)(splice.tpe.widenTermRefExpr)
133133
cpy.Apply(splice)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), body1 :: Nil)
134-
case f @ Apply(fun @ TypeApply(_, _), qctx :: Nil) =>
134+
case f @ Apply(fun @ TypeApply(_, _), scope :: Nil) if splice.isTerm =>
135135
// Type of the splice itsel must also be healed
136136
// internal.Quoted.expr[F[T]](... T ...) --> internal.Quoted.expr[F[$t]](... T ...)
137137
val tp = healType(splice.sourcePos)(splice.tpe.widenTermRefExpr)
138-
cpy.Apply(splice)(cpy.Apply(f)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), qctx :: Nil), body1 :: Nil)
138+
cpy.Apply(splice)(cpy.Apply(f)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), scope :: Nil), body1 :: Nil)
139+
case splice: Select =>
140+
val tagRef = getQuoteTypeTags.getTagRef(splice.qualifier.tpe.asInstanceOf[TermRef])
141+
ref(tagRef).withSpan(splice.span)
139142
}
140143
}
141144

compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ class ReifyQuotes extends MacroTransform {
179179
else pickleAsTasty()
180180
}
181181
else getLiteral(body) match {
182-
case Some(lit) => pickleAsLiteral(lit)
182+
// FIXME: support pickleAsLiteral
183+
// case Some(lit) => pickleAsLiteral(lit)
183184
case _ => pickleAsTasty()
184185
}
185186
}
@@ -262,7 +263,7 @@ class ReifyQuotes extends MacroTransform {
262263
assert(tpw.isInstanceOf[ValueType])
263264
val argTpe =
264265
if (tree.isType) defn.QuotedTypeClass.typeRef.appliedTo(tpw)
265-
else defn.FunctionType(1, isContextual = true).appliedTo(defn.QuoteContextClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpw))
266+
else defn.FunctionType(1, isContextual = true).appliedTo(defn.ScopeClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpw))
266267
val selectArg = arg.select(nme.apply).appliedTo(Literal(Constant(i))).cast(argTpe)
267268
val capturedArg = SyntheticValDef(UniqueName.fresh(tree.symbol.name.toTermName).toTermName, selectArg)
268269
i += 1

compiler/src/dotty/tools/dotc/transform/Splicer.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,11 @@ object Splicer {
162162
case tree: Ident if tree.symbol.is(Inline) || summon[Env].contains(tree.symbol) =>
163163
// OK
164164

165+
// TODO remove case
166+
case Apply(Select(TypeApply(unsafeExpr, _), nme.apply), List(expr))
167+
if unsafeExpr.symbol.showFullName == "scala.quoted.Scope.unsafeExpr" =>
168+
checkIfValidArgument(expr)
169+
165170
case _ =>
166171
val extra = if tree.span.isZeroExtent then ": " + tree.show else ""
167172
report.error(
@@ -192,6 +197,13 @@ object Splicer {
192197
report.error("Macro cannot be implemented with an `inline` method", fn.sourcePos)
193198
args.flatten.foreach(checkIfValidArgument)
194199

200+
case TypeApply(aio @ Select(expr, _), _) if aio.symbol == defn.Any_typeCast =>
201+
checkIfValidStaticCall(expr)
202+
203+
case Apply(Select(Apply(unsafe, _), nme.apply), List(expr))
204+
if unsafe.symbol == defn.QuotedExprModule_unsafeExpr || unsafe.symbol == defn.QuotedTypeModule_unsafeType =>
205+
checkIfValidStaticCall(expr)
206+
195207
case _ =>
196208
report.error(
197209
"""Malformed macro.
@@ -234,6 +246,18 @@ object Splicer {
234246
case Apply(Select(TypeApply(fn, quoted :: Nil), _), _) if fn.symbol == defn.QuotedTypeModule_apply =>
235247
interpretTypeQuote(quoted)
236248

249+
case Apply(Select(Apply(unsafe, _), nme.apply), List(expr))
250+
if unsafe.symbol == defn.QuotedExprModule_unsafeExpr || unsafe.symbol == defn.QuotedTypeModule_unsafeType =>
251+
interpretTree(expr)
252+
253+
// TODO remove case
254+
case Apply(Select(TypeApply(unsafeExpr, _), nme.apply), List(expr))
255+
if unsafeExpr.symbol.showFullName == "scala.quoted.Scope.unsafeExpr" =>
256+
interpretTree(expr)
257+
258+
case TypeApply(aio @ Select(expr, _), _) if aio.symbol == defn.Any_typeCast =>
259+
interpretTree(expr)
260+
237261
case Literal(Constant(value)) =>
238262
interpretLiteral(value)
239263

compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,20 @@ trait QuotesAndSplices {
4747
report.warning("Canceled splice directly inside a quote. '[ ${ XYZ } ] is equivalent to XYZ.", tree.sourcePos)
4848
case _ =>
4949
}
50-
val qctx = inferImplicitArg(defn.QuoteContextClass.typeRef, tree.span)
50+
val scope = inferImplicitArg(defn.ScopeClass.typeRef, tree.span)
5151

52-
if qctx.tpe.isInstanceOf[SearchFailureType] then
53-
report.error(missingArgMsg(qctx, defn.QuoteContextClass.typeRef, ""), ctx.source.atSpan(tree.span))
54-
else if !qctx.tpe.isStable then
55-
report.error(em"Quotes require stable QuoteContext, but found non stable $qctx", qctx.sourcePos)
52+
if scope.tpe.isInstanceOf[SearchFailureType] then
53+
report.error(missingArgMsg(scope, defn.ScopeClass.typeRef, ""), ctx.source.atSpan(tree.span))
54+
else if !scope.tpe.isStable then
55+
report.error(em"Quotes require stable Scope, but found non stable $scope", scope.sourcePos)
5656

5757
val tree1 =
5858
if ctx.mode.is(Mode.Pattern) then
59-
typedQuotePattern(tree, pt, qctx)
59+
typedQuotePattern(tree, pt, scope)(using pushQuoteContext(scope))
6060
else if (tree.quoted.isType)
61-
typedTypeApply(untpd.TypeApply(untpd.ref(defn.QuotedTypeModule_apply.termRef), tree.quoted :: Nil), pt)(using quoteContext).select(nme.apply).appliedTo(qctx)
61+
typedTypeApply(untpd.TypeApply(untpd.ref(defn.QuotedTypeModule_apply.termRef), tree.quoted :: Nil), pt)(using quoteContext).select(nme.apply).appliedTo(scope)
6262
else
63-
typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprQuote.termRef), tree.quoted), pt)(using pushQuoteContext(qctx)).select(nme.apply).appliedTo(qctx)
63+
typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprQuote.termRef), tree.quoted), pt)(using pushQuoteContext(scope)).select(nme.apply).appliedTo(scope)
6464
tree1.withSpan(tree.span)
6565
}
6666

@@ -75,12 +75,14 @@ trait QuotesAndSplices {
7575
}
7676
if (ctx.mode.is(Mode.QuotedPattern))
7777
if (isFullyDefined(pt, ForceDegree.flipBottom)) {
78+
val Some(scope) = peekQuoteContext()
7879
def spliceOwner(ctx: Context): Symbol =
7980
if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner
80-
val pat = typedPattern(tree.expr, defn.QuotedExprClass.typeRef.appliedTo(pt))(
81+
val pat = typedPattern(tree.expr, scope.tpe.select(tpnme.Expr).appliedTo(pt))(
8182
using spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx)))
82-
val baseType = pat.tpe.baseType(defn.QuotedExprClass)
83-
val argType = if baseType != NoType then baseType.argTypesHi.head else defn.NothingType
83+
val argType = pat.tpe.widenTermRefExpr.argTypesHi match
84+
case x :: Nil => x
85+
case _ => defn.NothingType
8486
ref(defn.InternalQuoted_exprSplice).appliedToType(argType).appliedTo(pat)
8587
}
8688
else {
@@ -102,7 +104,7 @@ trait QuotesAndSplices {
102104

103105
val internalSplice =
104106
outerQctx match
105-
case Some(qctxRef) => untpd.Apply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprNestedSplice.termRef), qctxRef), tree.expr)
107+
case Some(scopeRef) => untpd.Apply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprNestedSplice.termRef), scopeRef), tree.expr)
106108
case _ => untpd.Apply(untpd.ref(defn.InternalQuoted_exprSplice.termRef), tree.expr)
107109

108110
typedApply(internalSplice, pt)(using ctx1).withSpan(tree.span)
@@ -152,7 +154,9 @@ trait QuotesAndSplices {
152154
case _ =>
153155
}
154156

155-
if (ctx.mode.is(Mode.QuotedPattern) && level == 1)
157+
if ctx.mode.is(Mode.QuotedPattern) && level == 1 then
158+
val (outerQctx, ctx1) = popQuoteContext()
159+
156160
def spliceOwner(ctx: Context): Symbol =
157161
if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner
158162
val name = tree.expr match {
@@ -165,10 +169,10 @@ trait QuotesAndSplices {
165169
val typeSymInfo = pt match
166170
case pt: TypeBounds => pt
167171
case _ => TypeBounds.empty
168-
val typeSym = newSymbol(spliceOwner(ctx), name, EmptyFlags, typeSymInfo, NoSymbol, tree.expr.span)
172+
val typeSym = newSymbol(spliceOwner(ctx1), name, EmptyFlags, typeSymInfo, NoSymbol, tree.expr.span)
169173
typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuotedMatcher_patternTypeAnnot.typeRef)).withSpan(tree.expr.span)))
170174
val pat = typedPattern(tree.expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))(
171-
using spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx)))
175+
using spliceContext(using ctx1).retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx1)))
172176
pat.select(tpnme.spliceType)
173177
else
174178
typedSelect(untpd.Select(tree.expr, tpnme.spliceType), pt)(using spliceContext).withSpan(tree.span)
@@ -204,7 +208,7 @@ trait QuotesAndSplices {
204208
* )
205209
* ```
206210
*/
207-
private def splitQuotePattern(quoted: Tree)(using Context): (Map[Symbol, Bind], Tree, List[Tree]) = {
211+
private def splitQuotePattern(quoted: Tree, scope: Tree)(using Context): (Map[Symbol, Bind], Tree, List[Tree]) = {
208212
val ctx0 = ctx
209213

210214
val typeBindings: collection.mutable.Map[Symbol, Bind] = collection.mutable.Map.empty
@@ -233,7 +237,7 @@ trait QuotesAndSplices {
233237
override def transform(tree: Tree)(using Context) = tree match {
234238
case Typed(Apply(fn, pat :: Nil), tpt) if fn.symbol.isExprSplice && !tpt.tpe.derivesFrom(defn.RepeatedParamClass) =>
235239
val tpt1 = transform(tpt) // Transform type bindings
236-
val exprTpt = AppliedTypeTree(TypeTree(defn.QuotedExprClass.typeRef), tpt1 :: Nil)
240+
val exprTpt = AppliedTypeTree(TypeTree(scope.tpe.select(tpnme.Expr)), tpt1 :: Nil)
237241
val newSplice = ref(defn.InternalQuoted_exprSplice).appliedToType(tpt1.tpe).appliedTo(Typed(pat, exprTpt))
238242
transform(newSplice)
239243
case Apply(TypeApply(fn, targs), Apply(sp, pat :: Nil) :: args :: Nil) if fn.symbol == defn.InternalQuotedMatcher_patternHigherOrderHole =>
@@ -377,8 +381,8 @@ trait QuotesAndSplices {
377381
* ) => ...
378382
* ```
379383
*/
380-
private def typedQuotePattern(tree: untpd.Quote, pt: Type, qctx: Tree)(using Context): Tree = {
381-
if tree.quoted.isTerm && !pt.derivesFrom(defn.QuotedExprClass) then
384+
private def typedQuotePattern(tree: untpd.Quote, pt: Type, scope: Tree)(using Context): Tree = {
385+
if tree.quoted.isTerm && !(pt <:< scope.tpe.select(tpnme.Expr).appliedTo(defn.AnyType)) then
382386
report.error("Quote pattern can only match scrutinees of type scala.quoted.Expr", tree.sourcePos)
383387
else if tree.quoted.isType && !pt.derivesFrom(defn.QuotedTypeClass) then
384388
report.error("Quote pattern can only match scrutinees of type scala.quoted.Type", tree.sourcePos)
@@ -390,12 +394,12 @@ trait QuotesAndSplices {
390394
case _ => defn.AnyType
391395
}
392396
val quoted0 = desugar.quotedPattern(quoted, untpd.TypedSplice(TypeTree(quotedPt)))
393-
val quoteCtx = quoteContext.addMode(Mode.QuotedPattern)
397+
val quoteCtx = ctx.addMode(Mode.QuotedPattern)
394398
val quoted1 =
395399
if quoted.isType then typedType(quoted0, WildcardType)(using quoteCtx)
396400
else typedExpr(quoted0, WildcardType)(using quoteCtx)
397401

398-
val (typeBindings, shape, splices) = splitQuotePattern(quoted1)
402+
val (typeBindings, shape, splices) = splitQuotePattern(quoted1, scope)
399403

400404
class ReplaceBindings extends TypeMap() {
401405
override def apply(tp: Type): Type = tp match {
@@ -436,14 +440,14 @@ trait QuotesAndSplices {
436440
else typed(untpd.Tuple(splices.map(x => untpd.TypedSplice(replaceBindingsInTree.transform(x)))).withSpan(quoted.span), patType)
437441

438442
val unapplySym = if (tree.quoted.isTerm) defn.InternalQuotedExpr_unapply else defn.InternalQuotedType_unapply
439-
val quoteClass = if (tree.quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass
443+
val quotedClass = scope.tpe.select(if tree.quoted.isTerm then tpnme.Expr else tpnme.Type)
440444
val quotedPattern =
441-
if (tree.quoted.isTerm) ref(defn.InternalQuoted_exprQuote.termRef).appliedToType(defn.AnyType).appliedTo(shape).select(nme.apply).appliedTo(qctx)
442-
else ref(defn.QuotedTypeModule_apply.termRef).appliedToTypeTree(shape).select(nme.apply).appliedTo(qctx)
445+
if (tree.quoted.isTerm) ref(defn.InternalQuoted_exprQuote.termRef).appliedToType(defn.AnyType).appliedTo(shape).select(nme.apply).appliedTo(scope)
446+
else ref(defn.QuotedTypeModule_apply.termRef).appliedToTypeTree(shape).select(nme.apply).appliedTo(scope)
443447
UnApply(
444-
fun = ref(unapplySym.termRef).appliedToTypeTrees(typeBindingsTuple :: TypeTree(patType) :: Nil),
445-
implicits = quotedPattern :: Literal(Constant(typeBindings.nonEmpty)) :: qctx :: Nil,
448+
fun = ref(unapplySym.termRef).appliedToTypeTrees(typeBindingsTuple :: TypeTree(patType) :: Nil).appliedTo(scope),
449+
implicits = quotedPattern :: Literal(Constant(typeBindings.nonEmpty)) :: Nil,
446450
patterns = splicePat :: Nil,
447-
proto = quoteClass.typeRef.appliedTo(replaceBindings(quoted1.tpe) & quotedPt))
451+
proto = quotedClass.appliedTo(replaceBindings(quoted1.tpe) & quotedPt))
448452
}
449453
}

compiler/test-resources/repl/i9227

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
scala> import scala.language.implicitConversions
12
scala> import scala.quoted._; inline def myMacro[T]: Unit = ${ myMacroImpl[T] }; def myMacroImpl[T](using qctx: QuoteContext): Expr[Unit] = '{}; println(myMacro[Int])
23
1 | import scala.quoted._; inline def myMacro[T]: Unit = ${ myMacroImpl[T] }; def myMacroImpl[T](using qctx: QuoteContext): Expr[Unit] = '{}; println(myMacro[Int])
34
| ^^^^^^^^^^^^
45
| Cannot call macro method myMacroImpl defined in the same source file
5-
| This location contains code that was inlined from rs$line$1:1
6+
| This location contains code that was inlined from rs$line$2:1

library/src-bootstrapped/dotty/internal/CompileTimeMacros.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ package dotty.internal
33
import scala.quoted._
44

55
object CompileTimeMacros:
6-
def codeExpr(using qctx: QuoteContext)(sc: Expr[StringContext], args: Expr[Seq[Any]]): Expr[String] =
6+
def codeExpr(using s: Scope)(sc: s.Expr[StringContext], args: s.Expr[Seq[Any]]): s.Expr[String] =
77
(sc, args) match
8-
case (Expr.StringContext(Consts(parts)), Varargs(args2)) =>
8+
case (s.Expr.StringContext(Consts(parts)), s.Varargs(args2)) =>
99
Expr(StringContext(parts: _*).s(args2.map(_.show): _*))
1010
case _ =>
1111
report.throwError("compiletime.code must be used as a string interpolator `code\"...\"`")

0 commit comments

Comments
 (0)