Skip to content

Commit

Permalink
Fix #16615 - crashes of path dependent types in spliced Type.of (#16773)
Browse files Browse the repository at this point in the history
Fixes #16615

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.

In essence, ended up replicating the trees proposed in the original
issue thread, which were incredibly helpful.
  • Loading branch information
nicolasstucki committed Feb 9, 2023
2 parents 1fce02e + 90cdb69 commit 0ab89e6
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 6 deletions.
48 changes: 42 additions & 6 deletions compiler/src/dotty/tools/dotc/transform/Splicing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,16 @@ 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)
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(newContent))(using ctx.withSource(tree.source)).withSpan(tree.span)
case CapturedApplication(fn, argss) =>
transformCapturedApplication(tree, fn, argss)
case _ =>
Expand Down Expand Up @@ -335,17 +342,46 @@ 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 capturedPartTypes(tpt: Tree)(using Context): Tree =
val old = healedTypes
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) =>
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(tpt.tpe.widenTermRefExpr)
val newHealedTypes = healedTypes.nn.getTypeTags
healedTypes = old
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)
TypeTree(healedTypes.nn.getTagRef(capturedTypeSym.termRef))
Expand Down
19 changes: 19 additions & 0 deletions tests/pos-macros/i16615.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
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[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)
}
}

0 comments on commit 0ab89e6

Please sign in to comment.