Skip to content

Commit 5877755

Browse files
committed
Fix spliced-captured this
1 parent f1bad54 commit 5877755

File tree

5 files changed

+65
-8
lines changed

5 files changed

+65
-8
lines changed

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -717,9 +717,12 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) {
717717
writeNat(idx)
718718
pickleType(tree.tpe, richTypes = true)
719719
args.foreach { arg =>
720-
arg.tpe match
721-
case _: TermRef if arg.isType => writeByte(EXPLICITtpt)
722-
case _ =>
720+
if arg.isType then
721+
// Add EXPLICITtpt if the type can represent a type or a term.
722+
// This is only needed if the tree of the type would be unpickled as a term tree.
723+
arg.tpe match
724+
case _: TermRef | _: ThisType => writeByte(EXPLICITtpt)
725+
case _ =>
723726
pickleTree(arg)
724727
}
725728
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,16 @@ class PickleQuotes extends MacroTransform {
203203
private def getPicklableHoleType(tpe: Type, isStagedClasses: Symbol => Boolean)(using Context) =
204204
new TypeOps.AvoidMap {
205205
def toAvoid(tp: NamedType) = !isStagedClasses(tp.typeSymbol) && !isStaticPrefix(tp)
206+
override def apply(tp: Type): Type = tp match
207+
case tp: ThisType if tp.typeSymbol.isLocal =>
208+
// ThisType is only used inside a class.
209+
// In general, either they don't appear in the type to be avoided, or
210+
// it must be a class that encloses the block whose type is to be avoided.
211+
// However, as we may be extracting a splice that contains a reference to
212+
// a local class, we need to be careful to avoid the ThisType of the local
213+
// class.
214+
super.apply(tp.widen)
215+
case _ => super.apply(tp)
206216
}.apply(tpe)
207217
}
208218

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,15 @@ class Splicing extends MacroTransform:
167167
* ```
168168
*/
169169
private class SpliceTransformer(spliceOwner: Symbol, isCaptured: Symbol => Boolean) extends Transformer:
170-
private var refBindingMap = mutable.LinkedHashMap.empty[Symbol, (Tree, Symbol)]
170+
private var refTermBindingMap = mutable.LinkedHashMap.empty[Symbol, (Tree, Symbol)]
171+
private var refTypeBindingMap = mutable.LinkedHashMap.empty[Symbol, (Tree, Symbol)]
171172
/** Reference to the `Quotes` instance of the current level 1 splice */
172173
private var quotes: Tree | Null = null // TODO: add to the context
173174

174175
def transformSplice(tree: tpd.Tree, tpe: Type, holeIdx: Int)(using Context): tpd.Tree =
175176
assert(level == 0)
176177
val newTree = transform(tree)
177-
val (refs, bindings) = refBindingMap.values.toList.unzip
178+
val (refs, bindings) = (refTypeBindingMap.values ++ refTermBindingMap.values).toList.unzip
178179
val bindingsTypes = bindings.map(_.termRef.widenTermRefExpr)
179180
val methType = MethodType(bindingsTypes, newTree.tpe)
180181
val meth = newSymbol(spliceOwner, nme.ANON_FUN, Synthetic | Method, methType)
@@ -212,6 +213,11 @@ class Splicing extends MacroTransform:
212213
case _: TypeTree | _: SingletonTypeTree =>
213214
if containsCapturedType(tree.tpe) && level >= 1 then getTagRefFor(tree)
214215
else tree
216+
case _: This =>
217+
if isCaptured(tree.symbol) then
218+
val tag = getTagRefFor(tree)
219+
spliced(tag.tpe)(capturedTerm(tree))
220+
else super.transform(tree)
215221
case tree @ Assign(lhs: RefTree, rhs) =>
216222
if isCaptured(lhs.symbol) then transformSplicedAssign(tree)
217223
else super.transform(tree)
@@ -302,7 +308,7 @@ class Splicing extends MacroTransform:
302308
Param,
303309
defn.QuotedExprClass.typeRef.appliedTo(tpe),
304310
)
305-
val bindingSym = refBindingMap.getOrElseUpdate(tree.symbol, (tree, newBinding))._2
311+
val bindingSym = refTermBindingMap.getOrElseUpdate(tree.symbol, (tree, newBinding))._2
306312
ref(bindingSym)
307313

308314
private def newQuotedTypeClassBinding(tpe: Type)(using Context) =
@@ -314,14 +320,14 @@ class Splicing extends MacroTransform:
314320
)
315321

316322
private def capturedType(tree: Tree)(using Context): Symbol =
317-
refBindingMap.getOrElseUpdate(tree.symbol, (TypeTree(tree.tpe), newQuotedTypeClassBinding(tree.tpe)))._2
323+
refTypeBindingMap.getOrElseUpdate(tree.symbol, (TypeTree(tree.tpe), newQuotedTypeClassBinding(tree.tpe)))._2
318324

319325
private def capturedPartTypes(quote: Quote)(using Context): Tree =
320326
val (tags, body1) = inContextWithQuoteTypeTags {
321327
val capturePartTypes = new TypeMap {
322328
def apply(tp: Type) = tp match {
323329
case typeRef: TypeRef if containsCapturedType(typeRef) =>
324-
val termRef = refBindingMap
330+
val termRef = refTypeBindingMap
325331
.getOrElseUpdate(typeRef.symbol, (TypeTree(typeRef), newQuotedTypeClassBinding(typeRef)))._2.termRef
326332
val tagRef = getTagRef(termRef)
327333
tagRef
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import scala.quoted.*
2+
3+
object Macro:
4+
inline def generateCode: Unit = ${ testThisPaths }
5+
6+
def testThisPaths(using Quotes): Expr[Unit] =
7+
'{
8+
trait E extends G:
9+
type V
10+
val f: F
11+
${ val expr = '{ println(this) }; expr }
12+
${ val expr = '{ println(f) }; expr }
13+
${ val expr = '{ println(this.f) }; expr }
14+
${ val expr = '{ println(??? : this.type) }; expr }
15+
${ val expr = '{ println(??? : V) }; expr }
16+
${ val expr = '{ println(??? : this.V) }; expr }
17+
${ val expr = '{ println(??? : this.f.V) }; expr }
18+
${ val expr = '{ println(??? : this.f.type) }; expr }
19+
${
20+
val expr = '{
21+
println(this)
22+
println(f)
23+
println(this.f)
24+
println(??? : this.type)
25+
println(??? : V)
26+
println(??? : this.V)
27+
println(??? : this.f.V)
28+
println(??? : this.f.type)
29+
}
30+
expr
31+
}
32+
trait F:
33+
type V
34+
}
35+
36+
trait G:
37+
val f: Any
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@main def test = Macro.generateCode

0 commit comments

Comments
 (0)