Skip to content

Simplify avoidance of local types of Hole #17571

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

Merged
Merged
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
48 changes: 12 additions & 36 deletions compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.ast.untpd
import dotty.tools.dotc.config.ScalaRelease.*

import scala.collection.mutable
import dotty.tools.dotc.core.Annotations._
import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.quoted._
import dotty.tools.dotc.inlines.Inlines

import scala.annotation.constructorOnly
import scala.collection.mutable

/** Translates quoted terms and types to `unpickleExprV2` or `unpickleType` method calls.
*
Expand Down Expand Up @@ -106,16 +106,19 @@ class PickleQuotes extends MacroTransform {
private def extractHolesContents(quote: tpd.Quote)(using Context): (List[Tree], tpd.Quote) =
class HoleContentExtractor extends Transformer:
private val holeContents = List.newBuilder[Tree]
private val stagedClasses = mutable.HashSet.empty[Symbol]
override def transform(tree: tpd.Tree)(using Context): tpd.Tree =
tree match
case tree @ Hole(isTerm, _, _, content) =>
assert(isTerm)
assert(!content.isEmpty)
holeContents += content
val holeType = getTermHoleType(tree.tpe)
val holeType = getPicklableHoleType(tree.tpe, stagedClasses)
val hole = untpd.cpy.Hole(tree)(content = EmptyTree).withType(holeType)
cpy.Inlined(tree)(EmptyTree, Nil, hole)
case tree: DefTree =>
if tree.symbol.isClass then
stagedClasses += tree.symbol
val newAnnotations = tree.symbol.annotations.mapconserve { annot =>
annot.derivedAnnotation(transform(annot.tree)(using ctx.withOwner(tree.symbol)))
}
Expand All @@ -134,19 +137,6 @@ class PickleQuotes extends MacroTransform {
}
}

/** Remove references to local types that will not be defined in this quote */
private def getTermHoleType(using Context) = new TypeMap() {
override def apply(tp: Type): Type = tp match
case tp @ TypeRef(NoPrefix, _) =>
// reference to term with a type defined in outer quote
getTypeHoleType(tp)
case tp @ TermRef(NoPrefix, _) =>
// widen term refs to terms defined in outer quote
apply(tp.widenTermRefExpr)
case tp =>
mapOver(tp)
}

/** Get the holeContents of the transformed tree */
def getContents() =
val res = holeContents.result
Expand Down Expand Up @@ -196,11 +186,11 @@ class PickleQuotes extends MacroTransform {
cpy.Quote(quote)(Block(tdefs, body1), quote.tags)

private def mkTagSymbolAndAssignType(typeArg: Tree, idx: Int)(using Context): TypeDef = {
val holeType = getTypeHoleType(typeArg.tpe.select(tpnme.Underlying))
val holeType = getPicklableHoleType(typeArg.tpe.select(tpnme.Underlying), _ => false)
val hole = untpd.cpy.Hole(typeArg)(isTerm = false, idx, Nil, EmptyTree).withType(holeType)
val local = newSymbol(
owner = ctx.owner,
name = UniqueName.fresh(hole.tpe.dealias.typeSymbol.name.toTypeName),
name = UniqueName.fresh(typeArg.symbol.name.toTypeName),
flags = Synthetic,
info = TypeAlias(typeArg.tpe.select(tpnme.Underlying)),
coord = typeArg.span
Expand All @@ -209,25 +199,11 @@ class PickleQuotes extends MacroTransform {
ctx.typeAssigner.assignType(untpd.TypeDef(local.name, hole), local).withSpan(typeArg.span)
}

/** Remove references to local types that will not be defined in this quote */
private def getTypeHoleType(using Context) = new TypeMap() {
override def apply(tp: Type): Type = tp match
case tp: TypeRef if tp.typeSymbol.isTypeSplice =>
apply(tp.dealias)
case tp @ TypeRef(pre, _) if isLocalPath(pre) =>
val hiBound = tp.typeSymbol.info match
case info: ClassInfo => info.parents.reduce(_ & _)
case info => info.hiBound
apply(hiBound)
case tp =>
mapOver(tp)

private def isLocalPath(tp: Type): Boolean = tp match
case NoPrefix => true
case tp: TermRef if !tp.symbol.is(Package) => isLocalPath(tp.prefix)
case tp => false
}

/** Avoid all non-static types except those defined in the quote. */
private def getPicklableHoleType(tpe: Type, isStagedClasses: Symbol => Boolean)(using Context) =
new TypeOps.AvoidMap {
def toAvoid(tp: NamedType) = !isStagedClasses(tp.typeSymbol) && !isStaticPrefix(tp)
}.apply(tpe)
}

object PickleQuotes {
Expand Down