From 07dcca1b73800aa3623dce92323c494aad98b055 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Fri, 27 Jan 2023 09:52:06 +0100 Subject: [PATCH 1/2] Fix crashes of path dependent types in spliced Type.of Previously it was assumed that the type in Type.of could be captured as a whole, which meant that path dependent types for which a separate @SplicedType hole definitions were included in a block, would end up with missing references. Now when find a block in Type.of, we try to analise all parts of the type separately, adding additional hole definitions to the block as necessary. For types that can be captured as a whole (those which did not have a block generated previously, meaning they do not include any @SplicedType hole definitions), old method is used. --- .../dotty/tools/dotc/transform/Splicing.scala | 39 ++++++++++++++++--- tests/pos-macros/i16615.scala | 16 ++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 tests/pos-macros/i16615.scala diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index ad3f0322130d..799dbc6c09c9 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -242,9 +242,14 @@ class Splicing extends MacroTransform: else args.mapConserve(arg => transformLevel0QuoteContent(arg)(using quoteContext)) } cpy.Apply(tree)(cpy.Select(sel)(cpy.Apply(app)(fn, newArgs), nme.apply), quotesArgs) - case Apply(TypeApply(_, List(tpt)), List(quotes)) + case Apply(TypeApply(typeof, List(tpt)), List(quotes)) if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) => - ref(capturedType(tpt))(using ctx.withSource(tree.source)).withSpan(tree.span) + tpt match + case block: Block => + val newBlock = capturedBlockPartTypes(block) + Apply(TypeApply(typeof, List(newBlock)), List(quotes)).withSpan(tree.span) + case _ => + ref(capturedType(tpt))(using ctx.withSource(tree.source)).withSpan(tree.span) case CapturedApplication(fn, argss) => transformCapturedApplication(tree, fn, argss) case _ => @@ -335,17 +340,39 @@ class Splicing extends MacroTransform: val bindingSym = refBindingMap.getOrElseUpdate(tree.symbol, (tree, newBinding))._2 ref(bindingSym) - private def capturedType(tree: Tree)(using Context): Symbol = - val tpe = tree.tpe.widenTermRefExpr - def newBinding = newSymbol( + private def newQuotedTypeClassBinding(tpe: Type)(using Context) = + newSymbol( spliceOwner, UniqueName.fresh(nme.Type).toTermName, Param, defn.QuotedTypeClass.typeRef.appliedTo(tpe), ) - val bindingSym = refBindingMap.getOrElseUpdate(tree.symbol, (TypeTree(tree.tpe), newBinding))._2 + + 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 bindingSym + private def capturedBlockPartTypes(block: Block)(using Context): Tree = + val old = healedTypes + healedTypes = PCPCheckAndHeal.QuoteTypeTags(block.span) + val capturePartTypes = new TypeMap { + def apply(tp: Type) = tp match { + case typeRef @ TypeRef(prefix, _) if isCaptured(prefix.typeSymbol) || isCaptured(prefix.termSymbol) => + val termRef = refBindingMap + .getOrElseUpdate(typeRef.symbol, (TypeTree(typeRef), newQuotedTypeClassBinding(typeRef)))._2.termRef + val tagRef = healedTypes.nn.getTagRef(termRef) + tagRef + case _ => + mapOver(tp) + } + } + val captured = capturePartTypes(block.tpe.widenTermRefExpr) + val newHealedTypes = healedTypes.nn.getTypeTags + healedTypes = old + Block(newHealedTypes ::: block.stats, TypeTree(captured)) + private def getTagRefFor(tree: Tree)(using Context): Tree = val capturedTypeSym = capturedType(tree) TypeTree(healedTypes.nn.getTagRef(capturedTypeSym.termRef)) diff --git a/tests/pos-macros/i16615.scala b/tests/pos-macros/i16615.scala new file mode 100644 index 000000000000..510994f20423 --- /dev/null +++ b/tests/pos-macros/i16615.scala @@ -0,0 +1,16 @@ +import scala.quoted.* + +trait Api: + type Reader[E] + +def bugImpl[T: Type, Q[_]: Type](using Quotes) = + '{ + val p: Api = ??? + ${ + Type.of[p.Reader[T]] + Type.of[Q[p.Reader[T]]] + Type.of[p.Reader[Q[p.Reader[T]]]] + Type.of[p.Reader[Q[T]]] + Expr(1) + } + } \ No newline at end of file From 90cdb6957a78b14d838a5ec49b707d9b3504f695 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Wed, 8 Feb 2023 18:06:07 +0100 Subject: [PATCH 2/2] Make holes more consistent and address review comments --- .../dotty/tools/dotc/transform/Splicing.scala | 27 ++++++++++++------- tests/pos-macros/i16615.scala | 5 +++- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 799dbc6c09c9..393fe46b8438 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -244,12 +244,14 @@ class Splicing extends MacroTransform: cpy.Apply(tree)(cpy.Select(sel)(cpy.Apply(app)(fn, newArgs), nme.apply), quotesArgs) case Apply(TypeApply(typeof, List(tpt)), List(quotes)) if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) => - tpt match - case block: Block => - val newBlock = capturedBlockPartTypes(block) - Apply(TypeApply(typeof, List(newBlock)), List(quotes)).withSpan(tree.span) + val newContent = capturedPartTypes(tpt) + newContent match + case block: Block => + inContext(ctx.withSource(tree.source)) { + Apply(TypeApply(typeof, List(newContent)), List(quotes)).withSpan(tree.span) + } case _ => - ref(capturedType(tpt))(using ctx.withSource(tree.source)).withSpan(tree.span) + ref(capturedType(newContent))(using ctx.withSource(tree.source)).withSpan(tree.span) case CapturedApplication(fn, argss) => transformCapturedApplication(tree, fn, argss) case _ => @@ -354,9 +356,9 @@ class Splicing extends MacroTransform: .getOrElseUpdate(tree.symbol, (TypeTree(tree.tpe), newQuotedTypeClassBinding(tpe)))._2 bindingSym - private def capturedBlockPartTypes(block: Block)(using Context): Tree = + private def capturedPartTypes(tpt: Tree)(using Context): Tree = val old = healedTypes - healedTypes = PCPCheckAndHeal.QuoteTypeTags(block.span) + healedTypes = PCPCheckAndHeal.QuoteTypeTags(tpt.span) val capturePartTypes = new TypeMap { def apply(tp: Type) = tp match { case typeRef @ TypeRef(prefix, _) if isCaptured(prefix.typeSymbol) || isCaptured(prefix.termSymbol) => @@ -368,10 +370,17 @@ class Splicing extends MacroTransform: mapOver(tp) } } - val captured = capturePartTypes(block.tpe.widenTermRefExpr) + val captured = capturePartTypes(tpt.tpe.widenTermRefExpr) val newHealedTypes = healedTypes.nn.getTypeTags healedTypes = old - Block(newHealedTypes ::: block.stats, TypeTree(captured)) + tpt match + case block: Block => + cpy.Block(block)(newHealedTypes ::: block.stats, TypeTree(captured)) + case _ => + if newHealedTypes.nonEmpty then + cpy.Block(tpt)(newHealedTypes, TypeTree(captured)) + else + tpt private def getTagRefFor(tree: Tree)(using Context): Tree = val capturedTypeSym = capturedType(tree) diff --git a/tests/pos-macros/i16615.scala b/tests/pos-macros/i16615.scala index 510994f20423..3cc2d271fa87 100644 --- a/tests/pos-macros/i16615.scala +++ b/tests/pos-macros/i16615.scala @@ -10,7 +10,10 @@ def bugImpl[T: Type, Q[_]: Type](using Quotes) = Type.of[p.Reader[T]] Type.of[Q[p.Reader[T]]] Type.of[p.Reader[Q[p.Reader[T]]]] + Type.of[List[p.Reader[T]]] + Type.of[p.Reader[List[p.Reader[T]]]] + Type.of[p.Reader[List[T]]] Type.of[p.Reader[Q[T]]] Expr(1) } - } \ No newline at end of file + }