diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index d70b56fca43d..e907f6f1255d 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -717,9 +717,12 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) { writeNat(idx) pickleType(tree.tpe, richTypes = true) args.foreach { arg => - arg.tpe match - case _: TermRef if arg.isType => writeByte(EXPLICITtpt) - case _ => + if arg.isType then + // Add EXPLICITtpt if the type can represent a type or a term. + // This is only needed if the tree of the type would be unpickled as a term tree. + arg.tpe match + case _: TermRef | _: ThisType => writeByte(EXPLICITtpt) + case _ => pickleTree(arg) } } diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 624ec9628d87..005f8bf80187 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -203,6 +203,16 @@ class PickleQuotes extends MacroTransform { private def getPicklableHoleType(tpe: Type, isStagedClasses: Symbol => Boolean)(using Context) = new TypeOps.AvoidMap { def toAvoid(tp: NamedType) = !isStagedClasses(tp.typeSymbol) && !isStaticPrefix(tp) + override def apply(tp: Type): Type = tp match + case tp: ThisType if tp.typeSymbol.isLocal => + // ThisType is only used inside a class. + // In general, either they don't appear in the type to be avoided, or + // it must be a class that encloses the block whose type is to be avoided. + // However, as we may be extracting a splice that contains a reference to + // a local class, we need to be careful to avoid the ThisType of the local + // class. + super.apply(tp.widen) + case _ => super.apply(tp) }.apply(tpe) } diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 967c1cb6d19b..8bceaaef2f2f 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -167,14 +167,15 @@ class Splicing extends MacroTransform: * ``` */ private class SpliceTransformer(spliceOwner: Symbol, isCaptured: Symbol => Boolean) extends Transformer: - private var refBindingMap = mutable.LinkedHashMap.empty[Symbol, (Tree, Symbol)] + private var refTermBindingMap = mutable.LinkedHashMap.empty[Symbol, (Tree, Symbol)] + private var refTypeBindingMap = mutable.LinkedHashMap.empty[Symbol, (Tree, Symbol)] /** Reference to the `Quotes` instance of the current level 1 splice */ private var quotes: Tree | Null = null // TODO: add to the context def transformSplice(tree: tpd.Tree, tpe: Type, holeIdx: Int)(using Context): tpd.Tree = assert(level == 0) val newTree = transform(tree) - val (refs, bindings) = refBindingMap.values.toList.unzip + val (refs, bindings) = (refTypeBindingMap.values ++ refTermBindingMap.values).toList.unzip val bindingsTypes = bindings.map(_.termRef.widenTermRefExpr) val methType = MethodType(bindingsTypes, newTree.tpe) val meth = newSymbol(spliceOwner, nme.ANON_FUN, Synthetic | Method, methType) @@ -212,6 +213,11 @@ class Splicing extends MacroTransform: case _: TypeTree | _: SingletonTypeTree => if containsCapturedType(tree.tpe) && level >= 1 then getTagRefFor(tree) else tree + case _: This => + if isCaptured(tree.symbol) then + val tag = getTagRefFor(tree) + spliced(tag.tpe)(capturedTerm(tree)) + else super.transform(tree) case tree @ Assign(lhs: RefTree, rhs) => if isCaptured(lhs.symbol) then transformSplicedAssign(tree) else super.transform(tree) @@ -302,7 +308,7 @@ class Splicing extends MacroTransform: Param, defn.QuotedExprClass.typeRef.appliedTo(tpe), ) - val bindingSym = refBindingMap.getOrElseUpdate(tree.symbol, (tree, newBinding))._2 + val bindingSym = refTermBindingMap.getOrElseUpdate(tree.symbol, (tree, newBinding))._2 ref(bindingSym) private def newQuotedTypeClassBinding(tpe: Type)(using Context) = @@ -314,14 +320,14 @@ class Splicing extends MacroTransform: ) private def capturedType(tree: Tree)(using Context): Symbol = - refBindingMap.getOrElseUpdate(tree.symbol, (TypeTree(tree.tpe), newQuotedTypeClassBinding(tree.tpe)))._2 + refTypeBindingMap.getOrElseUpdate(tree.symbol, (TypeTree(tree.tpe), newQuotedTypeClassBinding(tree.tpe)))._2 private def capturedPartTypes(quote: Quote)(using Context): Tree = val (tags, body1) = inContextWithQuoteTypeTags { val capturePartTypes = new TypeMap { def apply(tp: Type) = tp match { case typeRef: TypeRef if containsCapturedType(typeRef) => - val termRef = refBindingMap + val termRef = refTypeBindingMap .getOrElseUpdate(typeRef.symbol, (TypeTree(typeRef), newQuotedTypeClassBinding(typeRef)))._2.termRef val tagRef = getTagRef(termRef) tagRef diff --git a/tests/pos-macros/this-type-capture/Macro_1.scala b/tests/pos-macros/this-type-capture/Macro_1.scala new file mode 100644 index 000000000000..8046a04cb531 --- /dev/null +++ b/tests/pos-macros/this-type-capture/Macro_1.scala @@ -0,0 +1,37 @@ +import scala.quoted.* + +object Macro: + inline def generateCode: Unit = ${ testThisPaths } + + def testThisPaths(using Quotes): Expr[Unit] = + '{ + trait E extends G: + type V + val f: F + ${ val expr = '{ println(this) }; expr } + ${ val expr = '{ println(f) }; expr } + ${ val expr = '{ println(this.f) }; expr } + ${ val expr = '{ println(??? : this.type) }; expr } + ${ val expr = '{ println(??? : V) }; expr } + ${ val expr = '{ println(??? : this.V) }; expr } + ${ val expr = '{ println(??? : this.f.V) }; expr } + ${ val expr = '{ println(??? : this.f.type) }; expr } + ${ + val expr = '{ + println(this) + println(f) + println(this.f) + println(??? : this.type) + println(??? : V) + println(??? : this.V) + println(??? : this.f.V) + println(??? : this.f.type) + } + expr + } + trait F: + type V + } + +trait G: + val f: Any diff --git a/tests/pos-macros/this-type-capture/Test_2.scala b/tests/pos-macros/this-type-capture/Test_2.scala new file mode 100644 index 000000000000..c12cd8d2436a --- /dev/null +++ b/tests/pos-macros/this-type-capture/Test_2.scala @@ -0,0 +1 @@ +@main def test = Macro.generateCode