Skip to content

Commit

Permalink
Remove tpt from Hole
Browse files Browse the repository at this point in the history
Now the type is kept in the `Hole` node.
  • Loading branch information
nicolasstucki committed May 10, 2023
1 parent 5a9b616 commit 10bbcd0
Show file tree
Hide file tree
Showing 12 changed files with 52 additions and 47 deletions.
43 changes: 21 additions & 22 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,19 @@ object Trees {
s"TypeTree${if (hasType) s"[$typeOpt]" else ""}"
}

/** Tree that replaces a level 1 splices in pickled (level 0) quotes.
* It is only used when picking quotes (will never be in a TASTy file).
*
* @param isTerm If this hole is a term, otherwise it is a type hole.
* @param idx The index of the hole in it's enclosing level 0 quote.
* @param args The arguments of the splice to compute its content
* @param content Lambda that computes the content of the hole. This tree is empty when in a quote pickle.
*/
case class Hole[+T <: Untyped](override val isTerm: Boolean, idx: Int, args: List[Tree[T]], content: Tree[T])(implicit @constructorOnly src: SourceFile) extends Tree[T] {
type ThisTree[+T <: Untyped] <: Hole[T]
override def isType: Boolean = !isTerm
}

/** A type tree whose type is inferred. These trees appear in two contexts
* - as an argument of a TypeApply. In that case its type is always a TypeVar
* - as a (result-)type of an inferred ValDef or DefDef.
Expand Down Expand Up @@ -1030,20 +1043,6 @@ object Trees {
def genericEmptyValDef[T <: Untyped]: ValDef[T] = theEmptyValDef.asInstanceOf[ValDef[T]]
def genericEmptyTree[T <: Untyped]: Thicket[T] = theEmptyTree.asInstanceOf[Thicket[T]]

/** Tree that replaces a level 1 splices in pickled (level 0) quotes.
* It is only used when picking quotes (will never be in a TASTy file).
*
* @param isTerm If this hole is a term, otherwise it is a type hole.
* @param idx The index of the hole in it's enclosing level 0 quote.
* @param args The arguments of the splice to compute its content
* @param content Lambda that computes the content of the hole. This tree is empty when in a quote pickle.
* @param tpt Type of the hole
*/
case class Hole[+T <: Untyped](override val isTerm: Boolean, idx: Int, args: List[Tree[T]], content: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends Tree[T] {
type ThisTree[+T <: Untyped] <: Hole[T]
override def isType: Boolean = !isTerm
}

def flatten[T <: Untyped](trees: List[Tree[T]]): List[Tree[T]] = {
def recur(buf: ListBuffer[Tree[T]] | Null, remaining: List[Tree[T]]): ListBuffer[Tree[T]] | Null =
remaining match {
Expand Down Expand Up @@ -1401,9 +1400,9 @@ object Trees {
case tree: Thicket if (trees eq tree.trees) => tree
case _ => finalize(tree, untpd.Thicket(trees)(sourceFile(tree)))
}
def Hole(tree: Tree)(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole = tree match {
def Hole(tree: Tree)(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree)(using Context): Hole = tree match {
case tree: Hole if isTerm == tree.isTerm && idx == tree.idx && args.eq(tree.args) && content.eq(tree.content) && content.eq(tree.content) => tree
case _ => finalize(tree, untpd.Hole(isTerm, idx, args, content, tpt)(sourceFile(tree)))
case _ => finalize(tree, untpd.Hole(isTerm, idx, args, content)(sourceFile(tree)))
}

// Copier methods with default arguments; these demand that the original tree
Expand All @@ -1426,8 +1425,8 @@ object Trees {
TypeDef(tree: Tree)(name, rhs)
def Template(tree: Template)(using Context)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template =
Template(tree: Tree)(constr, parents, derived, self, body)
def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, content: Tree = tree.content, tpt: Tree = tree.tpt)(using Context): Hole =
Hole(tree: Tree)(isTerm, idx, args, content, tpt)
def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, content: Tree = tree.content)(using Context): Hole =
Hole(tree: Tree)(isTerm, idx, args, content)

}

Expand Down Expand Up @@ -1562,8 +1561,8 @@ object Trees {
cpy.Quote(tree)(transform(body)(using quoteContext))
case tree @ Splice(expr) =>
cpy.Splice(tree)(transform(expr)(using spliceContext))
case tree @ Hole(isTerm, idx, args, content, tpt) =>
cpy.Hole(tree)(isTerm, idx, transform(args), transform(content), transform(tpt))
case tree @ Hole(isTerm, idx, args, content) =>
cpy.Hole(tree)(isTerm, idx, transform(args), transform(content))
case _ =>
transformMoreCases(tree)
}
Expand Down Expand Up @@ -1707,8 +1706,8 @@ object Trees {
this(x, body)(using quoteContext)
case Splice(expr) =>
this(x, expr)(using spliceContext)
case Hole(_, _, args, content, tpt) =>
this(this(this(x, args), content), tpt)
case Hole(_, _, args, content) =>
this(this(x, args), content)
case _ =>
foldMoreCases(x, tree)
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def Splice(expr: Tree, tpe: Type)(using Context): Splice =
untpd.Splice(expr).withType(tpe)

def Hole(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpe: Type)(using Context): Hole =
untpd.Hole(isTerm, idx, args, content).withType(tpe)

def TypeTree(tp: Type, inferred: Boolean = false)(using Context): TypeTree =
(if inferred then untpd.InferredTypeTree() else untpd.TypeTree()).withType(tp)

Expand Down Expand Up @@ -397,9 +400,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def Throw(expr: Tree)(using Context): Tree =
ref(defn.throwMethod).appliedTo(expr)

def Hole(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole =
ta.assignType(untpd.Hole(isTerm, idx, args, content, tpt), tpt)

// ------ Making references ------------------------------------------------------

def prefixIsElidable(tp: NamedType)(using Context): Boolean = {
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def Export(expr: Tree, selectors: List[ImportSelector])(implicit src: SourceFile): Export = new Export(expr, selectors)
def PackageDef(pid: RefTree, stats: List[Tree])(implicit src: SourceFile): PackageDef = new PackageDef(pid, stats)
def Annotated(arg: Tree, annot: Tree)(implicit src: SourceFile): Annotated = new Annotated(arg, annot)
def Hole(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(implicit src: SourceFile): Hole = new Hole(isTerm, idx, args, content, tpt)
def Hole(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree)(implicit src: SourceFile): Hole = new Hole(isTerm, idx, args, content)

// ------ Additional creation methods for untyped only -----------------

Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -685,11 +685,11 @@ class TreePickler(pickler: TastyPickler) {
.appliedTo(expr)
.withSpan(tree.span)
)
case Hole(_, idx, args, _, tpt) =>
case Hole(_, idx, args, _) =>
writeByte(HOLE)
withLength {
writeNat(idx)
pickleType(tpt.tpe, richTypes = true)
pickleType(tree.tpe, richTypes = true)
args.foreach(pickleTree)
}
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1457,7 +1457,7 @@ class TreeUnpickler(reader: TastyReader,
val idx = readNat()
val tpe = readType()
val args = until(end)(readTree())
Hole(true, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe)
Hole(true, idx, args, EmptyTree, tpe)
case _ =>
readPathTree()
}
Expand Down Expand Up @@ -1491,7 +1491,7 @@ class TreeUnpickler(reader: TastyReader,
val idx = readNat()
val tpe = readType()
val args = until(end)(readTree())
Hole(false, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe)
Hole(false, idx, args, EmptyTree, tpe)
case _ =>
if (isTypeTreeTag(nextByte)) readTree()
else {
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -724,12 +724,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case Splice(expr) =>
val spliceTypeText = (keywordStr("[") ~ toTextGlobal(tree.typeOpt) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists)
keywordStr("$") ~ spliceTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}")
case Hole(isTerm, idx, args, content, tpt) =>
case Hole(isTerm, idx, args, content) =>
val (prefix, postfix) = if isTerm then ("{{{", "}}}") else ("[[[", "]]]")
val argsText = toTextGlobal(args, ", ")
val contentText = toTextGlobal(content)
val tptText = toTextGlobal(tpt)
prefix ~~ idx.toString ~~ "|" ~~ tptText ~~ "|" ~~ argsText ~~ "|" ~~ contentText ~~ postfix
val tpeText = toTextGlobal(tree.typeOpt)
prefix ~~ idx.toString ~~ "|" ~~ tpeText ~~ "|" ~~ argsText ~~ "|" ~~ contentText ~~ postfix
case CapturingTypeTree(refs, parent) =>
parent match
case ImpureByNameTypeTree(bntpt) =>
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ object PickledQuotes {
private def spliceTerms(tree: Tree, typeHole: TypeHole, termHole: ExprHole)(using Context): Tree = {
def evaluateHoles = new TreeMap {
override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match {
case Hole(isTerm, idx, args, _, _) =>
case Hole(isTerm, idx, args, _) =>
inContext(SpliceScope.contextWithNewSpliceScope(tree.sourcePos)) {
if isTerm then
val quotedExpr = termHole match
Expand Down Expand Up @@ -165,15 +165,15 @@ object PickledQuotes {
val tree = typeHole match
case TypeHole.V1(evalHole) =>
tdef.rhs match
case TypeBoundsTree(_, Hole(_, idx, args, _, _), _) =>
case TypeBoundsTree(_, Hole(_, idx, args, _), _) =>
// To keep for backwards compatibility. In some older version holes where created in the bounds.
val quotedType = evalHole.nn.apply(idx, reifyTypeHoleArgs(args))
PickledQuotes.quotedTypeToTree(quotedType)
case TypeBoundsTree(_, tpt, _) =>
// To keep for backwards compatibility. In some older version we missed the creation of some holes.
tpt
case TypeHole.V2(types) =>
val Hole(_, idx, _, _, _) = tdef.rhs: @unchecked
val Hole(_, idx, _, _) = tdef.rhs: @unchecked
PickledQuotes.quotedTypeToTree(types.nn.apply(idx))
(tdef.symbol, tree.tpe)
}.toMap
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ast.TreeTypeMap
import SymUtils._
import NameKinds._
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.ast.untpd
import dotty.tools.dotc.config.ScalaRelease.*

import scala.collection.mutable
Expand Down Expand Up @@ -113,12 +114,12 @@ class PickleQuotes extends MacroTransform {
private val contents = List.newBuilder[Tree]
override def transform(tree: tpd.Tree)(using Context): tpd.Tree =
tree match
case tree @ Hole(isTerm, _, _, content, _) =>
case tree @ Hole(isTerm, _, _, content) =>
if !content.isEmpty then
contents += content
val holeType =
if isTerm then getTermHoleType(tree.tpe) else getTypeHoleType(tree.tpe)
val hole = cpy.Hole(tree)(content = EmptyTree, TypeTree(holeType))
val hole = untpd.cpy.Hole(tree)(content = EmptyTree).withType(holeType)
if isTerm then Inlined(EmptyTree, Nil, hole).withSpan(tree.span) else hole
case tree: DefTree =>
val newAnnotations = tree.symbol.annotations.mapconserve { annot =>
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/Splicing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ class Splicing extends MacroTransform:
case None =>
val holeIdx = numHoles
numHoles += 1
val hole = tpd.Hole(false, holeIdx, Nil, ref(qual), TypeTree(tp))
val hole = tpd.Hole(false, holeIdx, Nil, ref(qual), tp)
typeHoles.put(qual, hole)
hole
cpy.TypeDef(tree)(rhs = hole)
Expand Down Expand Up @@ -192,7 +192,7 @@ class Splicing extends MacroTransform:
val ddef = DefDef(meth, List(bindings), newTree.tpe, newTree.changeOwner(ctx.owner, meth))
val fnType = defn.FunctionType(bindings.size, isContextual = false).appliedTo(bindingsTypes :+ newTree.tpe)
val closure = Block(ddef :: Nil, Closure(Nil, ref(meth), TypeTree(fnType)))
tpd.Hole(true, holeIdx, refs, closure, TypeTree(tpe))
tpd.Hole(true, holeIdx, refs, closure, tpe)

override def transform(tree: tpd.Tree)(using Context): tpd.Tree =
tree match
Expand Down
13 changes: 8 additions & 5 deletions compiler/src/dotty/tools/dotc/transform/TreeChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -658,16 +658,19 @@ object TreeChecker {
super.typedPackageDef(tree)

override def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = {
val tree1 @ Hole(isTerm, _, args, content, tpt) = super.typedHole(tree, pt): @unchecked
val tree1 @ Hole(isTerm, idx, args, content) = super.typedHole(tree, pt): @unchecked

assert(idx >= 0, i"hole should not have negative index: $tree")
assert(isTerm || tree.args.isEmpty, i"type hole should not have arguments: $tree")

// 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)

// Check result type of the hole
if isTerm then assert(tpt.typeOpt <:< pt)
else assert(tpt.typeOpt =:= pt)
if isTerm then assert(tree1.typeOpt <:< pt)
else assert(tree1.typeOpt =:= pt)

// Check that the types of the args conform to the types of the contents of the hole
val argQuotedTypes = args.map { arg =>
Expand All @@ -682,8 +685,8 @@ object TreeChecker {
else defn.QuotedTypeClass.typeRef.appliedTo(arg.typeOpt.widenTermRefExpr)
}
val expectedResultType =
if isTerm then defn.QuotedExprClass.typeRef.appliedTo(tpt.typeOpt)
else defn.QuotedTypeClass.typeRef.appliedTo(tpt.typeOpt)
if isTerm then defn.QuotedExprClass.typeRef.appliedTo(tree1.typeOpt)
else defn.QuotedTypeClass.typeRef.appliedTo(tree1.typeOpt)
val contextualResult =
defn.FunctionOf(List(defn.QuotesClass.typeRef), expectedResultType, isContextual = true)
val expectedContentType =
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,7 @@ trait QuotesAndSplices {
}

def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree =
val tpt = typedType(tree.tpt)
assignType(tree, tpt)
assert(false, "Holes should only be typed by the ReTyper")

/** Types a splice applied to some arguments `$f(arg1, ..., argn)` in a quote pattern.
*
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/ReTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking
val expr1 = typed(tree.expr, quoteType)(using spliceContext)
untpd.cpy.Splice(tree)(expr1).withType(tree.typeOpt)

override def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree =
promote(tree)

override def localDummy(cls: ClassSymbol, impl: untpd.Template)(using Context): Symbol = impl.symbol

override def retrieveSym(tree: untpd.Tree)(using Context): Symbol = tree.symbol
Expand Down

0 comments on commit 10bbcd0

Please sign in to comment.