Skip to content

Splice hole with this capture #18385

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
10 changes: 10 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Comment on lines +211 to +213
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I still don't really understand why we need to do this, what do we do with this approximated type exactly? What happens if we just use Any as an approximation?

super.apply(tp.widen)
case _ => super.apply(tp)
}.apply(tpe)
}

Expand Down
16 changes: 11 additions & 5 deletions compiler/src/dotty/tools/dotc/transform/Splicing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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) =
Expand All @@ -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
Expand Down
37 changes: 37 additions & 0 deletions tests/pos-macros/this-type-capture/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions tests/pos-macros/this-type-capture/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@main def test = Macro.generateCode