Skip to content

Commit e8f7fc2

Browse files
authored
Preserve implicits in Quotes context (#23263)
Fixes issue with certain implicits defined within quoted code not being able to be summoned in a splice, or when using Symbol.asQuotes. Fixes #22260
2 parents 99543c5 + ccc5d1a commit e8f7fc2

File tree

9 files changed

+92
-9
lines changed

9 files changed

+92
-9
lines changed

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,13 @@ object SymDenotations {
344344

345345
def recurWithoutParamss(info: Type): List[List[Symbol]] = info match
346346
case info: LambdaType =>
347-
val params = info.paramNames.lazyZip(info.paramInfos).map((pname, ptype) =>
348-
newSymbol(symbol, pname, SyntheticParam, ptype))
347+
val commonFlags =
348+
if info.isContextualMethod then Given | SyntheticParam
349+
else if info.isImplicitMethod then Implicit | SyntheticParam
350+
else SyntheticParam
351+
val params = info.paramNames.lazyZip(info.paramInfos).map: (pname, ptype) =>
352+
val flags = if ptype.hasAnnotation(defn.ErasedParamAnnot) then commonFlags | Erased else commonFlags
353+
newSymbol(symbol, pname, flags, ptype)
349354
val prefs = params.map(_.namedType)
350355
for param <- params do
351356
param.info = param.info.substParams(info, prefs)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import scala.collection.mutable
2222

2323
import QuoteUtils.*
2424
import dotty.tools.io.NoAbstractFile
25+
import dotty.tools.dotc.ast.TreeMapWithImplicits
2526

2627
object PickledQuotes {
2728
import tpd.*
@@ -99,7 +100,7 @@ object PickledQuotes {
99100

100101
/** Replace all term holes with the spliced terms */
101102
private def spliceTerms(tree: Tree, typeHole: TypeHole, termHole: ExprHole)(using Context): Tree = {
102-
def evaluateHoles = new TreeMapWithPreciseStatContexts {
103+
def evaluateHoles = new TreeMapWithImplicits {
103104
override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match {
104105
case Hole(isTerm, idx, args, _) =>
105106
inContext(SpliceScope.contextWithNewSpliceScope(tree.sourcePos)) {

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import dotty.tools.dotc.core.Contexts.*
1111
import dotty.tools.dotc.core.Decorators.*
1212
import dotty.tools.dotc.core.NameKinds
1313
import dotty.tools.dotc.core.NameOps.*
14+
import dotty.tools.dotc.core.Scopes.*
1415
import dotty.tools.dotc.core.StdNames.*
1516
import dotty.tools.dotc.core.Types
1617
import dotty.tools.dotc.NoCompilationUnit
@@ -2823,7 +2824,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
28232824
modFlags | dotc.core.Flags.ModuleValCreationFlags,
28242825
clsFlags | dotc.core.Flags.ModuleClassCreationFlags,
28252826
parents,
2826-
dotc.core.Scopes.newScope,
2827+
newScope,
28272828
privateWithin,
28282829
NoCoord,
28292830
compUnitInfo = null
@@ -2839,7 +2840,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
28392840
xCheckMacroAssert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`")
28402841
val privateWithin1 = if privateWithin.isTerm then Symbol.noSymbol else privateWithin
28412842
checkValidFlags(flags.toTermFlags, Flags.validMethodFlags)
2842-
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Method, tpe, privateWithin1)
2843+
val method = dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Method, tpe, privateWithin1)
2844+
method.setParamss(method.paramSymss)
2845+
method
28432846
def newVal(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol =
28442847
xCheckMacroAssert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`")
28452848
val privateWithin1 = if privateWithin.isTerm then Symbol.noSymbol else privateWithin
@@ -3066,7 +3069,11 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
30663069

30673070
def asQuotes: Nested =
30683071
assert(self.ownersIterator.contains(ctx.owner), s"$self is not owned by ${ctx.owner}")
3069-
new QuotesImpl(using ctx.withOwner(self))
3072+
val newCtx = if ctx.owner eq self then ctx else
3073+
val newCtx = ctx.fresh.setOwner(self)
3074+
if !self.flags.is(Flags.Method) then newCtx
3075+
else newCtx.setScope(newScopeWith(self.paramSymss.flatten*))
3076+
new QuotesImpl(using newCtx)
30703077

30713078
end extension
30723079

tests/neg-macros/i19842-a.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
|
1010
| at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
1111
| at dotty.tools.dotc.transform.TreeChecker$.checkParents(TreeChecker.scala:210)
12+
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:286)
1213
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:285)
13-
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:284)
1414
| at Macros$.makeSerializer(Macro.scala:25)
1515
|
1616
|---------------------------------------------------------------------------------------------------------------------

tests/neg-macros/i19842-b.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
|
1010
| at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
1111
| at dotty.tools.dotc.transform.TreeChecker$.checkParents(TreeChecker.scala:210)
12+
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:286)
1213
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:285)
13-
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:284)
1414
| at Macros$.makeSerializer(Macro.scala:27)
1515
|
1616
|---------------------------------------------------------------------------------------------------------------------

tests/neg-macros/i23008.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
| Exception occurred while executing macro expansion.
66
| java.lang.IllegalArgumentException: requirement failed: value of StringConstant cannot be `null`
77
| at scala.Predef$.require(Predef.scala:337)
8+
| at scala.quoted.runtime.impl.QuotesImpl$reflect$StringConstant$.apply(QuotesImpl.scala:2534)
89
| at scala.quoted.runtime.impl.QuotesImpl$reflect$StringConstant$.apply(QuotesImpl.scala:2533)
9-
| at scala.quoted.runtime.impl.QuotesImpl$reflect$StringConstant$.apply(QuotesImpl.scala:2532)
1010
| at scala.quoted.ToExpr$StringToExpr.apply(ToExpr.scala:80)
1111
| at scala.quoted.ToExpr$StringToExpr.apply(ToExpr.scala:78)
1212
| at scala.quoted.Expr$.apply(Expr.scala:70)

tests/run-macros/i22260.check

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
42
2+
42
3+
42
4+
42
5+
42
6+
42
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import scala.quoted.*
2+
3+
object Macros:
4+
inline def crMethod: Int = ${ createMethod }
5+
inline def inMethod: Int = ${ insideMethod }
6+
inline def usMethod: Int = ${ usingMethod }
7+
inline def inObject: Int = ${ insideObject }
8+
inline def inClass: Int = ${ insideClass }
9+
inline def usClass: Int = ${ usingClass }
10+
11+
def summon(using Quotes) =
12+
Expr.summon[Int].getOrElse('{0})
13+
14+
def createMethod(using Quotes): Expr[Int] =
15+
import quotes.reflect.*
16+
val tpe = MethodType(MethodTypeKind.Contextual)("x" :: Nil)(_ => TypeRepr.of[Int] :: Nil, _ => TypeRepr.of[Int])
17+
val sym = Symbol.newMethod(Symbol.spliceOwner, "foo", tpe)
18+
val method = DefDef(sym, _ => Some(summon(using sym.asQuotes).asTerm))
19+
Block(method :: Nil, Apply(Ref(sym), '{42}.asTerm :: Nil)).asExprOf[Int]
20+
21+
def insideMethod(using Quotes): Expr[Int] = '{
22+
def foo =
23+
given Int = 42
24+
${summon}
25+
26+
foo
27+
}
28+
29+
def usingMethod(using Quotes): Expr[Int] = '{
30+
def foo(using Int) =
31+
${summon}
32+
33+
foo(using 42)
34+
}
35+
36+
def insideObject(using Quotes): Expr[Int] = '{
37+
object Foo:
38+
given Int = 42
39+
val x = ${summon}
40+
41+
Foo.x
42+
}
43+
44+
def insideClass(using Quotes): Expr[Int] = '{
45+
class Foo:
46+
given Int = 42
47+
val x = ${summon}
48+
49+
new Foo().x
50+
}
51+
52+
def usingClass(using Quotes): Expr[Int] = '{
53+
class Foo(using Int):
54+
val x = ${summon}
55+
56+
new Foo(using 42).x
57+
}

tests/run-macros/i22260/Test_2.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@main def Test =
2+
println(Macros.crMethod)
3+
println(Macros.inMethod)
4+
println(Macros.usMethod)
5+
println(Macros.inObject)
6+
println(Macros.inClass)
7+
println(Macros.usClass)

0 commit comments

Comments
 (0)