From 52aaeb59014daba04eada8d846f6635963fa8e40 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 15 Mar 2023 13:59:25 +0100 Subject: [PATCH] Fix captured references to singleton types When we had a reference to a `x.type` we mistakenly captured `x` instead of `x.type`. This was caused because `SingletonTypeTree` was not handled in `Splicing`. Fixing this uncovered some inconsistencies with the types in the encoding of the hole captured types and contents. These have been fixed as well. --- .../tools/dotc/transform/PickleQuotes.scala | 4 +++ .../dotty/tools/dotc/transform/Splicing.scala | 5 ++-- .../tools/dotc/transform/TreeChecker.scala | 2 +- .../this-type-capture/Macro_1.scala | 29 +++++++++++++++++++ .../pos-macros/this-type-capture/Test_2.scala | 1 + 5 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 tests/pos-macros/this-type-capture/Macro_1.scala create mode 100644 tests/pos-macros/this-type-capture/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 62174c806f09..29f43e0f63de 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -162,6 +162,10 @@ class PickleQuotes extends MacroTransform { case info: ClassInfo => info.parents.reduce(_ & _) case info => info.hiBound apply(hiBound) + case tp @ TermRef(pre, _) if isLocalPath(pre) => + apply(tp.widenTermRefExpr) + case ThisType(ref @ TypeRef(pre, _)) if isLocalPath(pre) => + apply(ref) case tp => mapOver(tp) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index bb82fba32a7c..b0f6501c1907 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -231,7 +231,7 @@ class Splicing extends MacroTransform: // Dealias references to captured types TypeTree(tree.tpe.dealias) else super.transform(tree) - case tree: TypeTree => + case _: TypeTree | _: SingletonTypeTree => if containsCapturedType(tree.tpe) && level >= 1 then getTagRefFor(tree) else tree case tree @ Assign(lhs: RefTree, rhs) => @@ -360,9 +360,8 @@ class Splicing extends MacroTransform: ) private def capturedType(tree: Tree)(using Context): Symbol = - val tpe = tree.tpe.widenTermRefExpr val bindingSym = refBindingMap - .getOrElseUpdate(tree.symbol, (TypeTree(tree.tpe), newQuotedTypeClassBinding(tpe)))._2 + .getOrElseUpdate(tree.symbol, (TypeTree(tree.tpe), newQuotedTypeClassBinding(tree.tpe)))._2 bindingSym private def capturedPartTypes(tpt: Tree)(using Context): Tree = diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index f9240d6091c4..bacda2d315ca 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -663,7 +663,7 @@ object TreeChecker { // Check that we only add the captured type `T` instead of a more complex type like `List[T]`. // If we have `F[T]` with captured `F` and `T`, we should list `F` and `T` separately in the args. for arg <- args do - assert(arg.isTerm || arg.tpe.isInstanceOf[TypeRef], "Expected TypeRef in Hole type args but got: " + arg.tpe) + assert(arg.isTerm || arg.tpe.isInstanceOf[TypeRef] || arg.tpe.isInstanceOf[TermRef] || arg.tpe.isInstanceOf[ThisType], "Expected TypeRef or TermRef in Hole type args but got: " + arg.tpe) // Check result type of the hole if isTermHole then assert(tpt.typeOpt <:< pt) 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..cc0b2c9adcfe --- /dev/null +++ b/tests/pos-macros/this-type-capture/Macro_1.scala @@ -0,0 +1,29 @@ +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 = '{ + // val _: Any = this // FIXME: this should work + // val _: Any = f // FIXME: this should work + val _: this.type = ??? + val _: V = ??? + val _: this.V = ??? + val _: this.f.V = ??? + val _: this.type = ??? + val _: 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