Skip to content

Commit

Permalink
Separate tree nodes for ByNames
Browse files Browse the repository at this point in the history
ByName nodes in arguments are not pickled, which means that we
can use the same Tasty version as before.
  • Loading branch information
odersky committed Jan 19, 2022
1 parent 3f51f11 commit b7fa78d
Show file tree
Hide file tree
Showing 23 changed files with 100 additions and 81 deletions.
15 changes: 15 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,12 @@ object Trees {
def name: Name = bind.name
}

/** By-name wrapper; created by Typer and TreUnpickler, eliminated in TreePickler and ByNameLambda */
case class ByName[-T >: Untyped] private[ast] (expr: Tree[T])(implicit @constructorOnly src: SourceFile)
extends TermTree[T] {
type ThisTree[-T >: Untyped] = ByName[T]
}

/** return expr
* where `from` refers to the method or label from which the return takes place
* After program transformations this is not necessarily the enclosing method, because
Expand Down Expand Up @@ -1075,6 +1081,7 @@ object Trees {
type InlineMatch = Trees.InlineMatch[T]
type CaseDef = Trees.CaseDef[T]
type Labeled = Trees.Labeled[T]
type ByName = Trees.ByName[T]
type Return = Trees.Return[T]
type WhileDo = Trees.WhileDo[T]
type Try = Trees.Try[T]
Expand Down Expand Up @@ -1228,6 +1235,10 @@ object Trees {
case tree: Labeled if (bind eq tree.bind) && (expr eq tree.expr) => tree
case _ => finalize(tree, untpd.Labeled(bind, expr)(sourceFile(tree)))
}
def ByName(tree: Tree)(expr: Tree)(using Context): ByName = tree match {
case tree: ByName if expr eq tree.expr => tree
case _ => finalize(tree, untpd.ByName(expr)(sourceFile(tree)))
}
def Return(tree: Tree)(expr: Tree, from: Tree)(using Context): Return = tree match {
case tree: Return if (expr eq tree.expr) && (from eq tree.from) => tree
case _ => finalize(tree, untpd.Return(expr, from)(sourceFile(tree)))
Expand Down Expand Up @@ -1411,6 +1422,8 @@ object Trees {
cpy.CaseDef(tree)(transform(pat), transform(guard), transform(body))
case Labeled(bind, expr) =>
cpy.Labeled(tree)(transformSub(bind), transform(expr))
case ByName(expr) =>
cpy.ByName(tree)(transform(expr))
case Return(expr, from) =>
cpy.Return(tree)(transform(expr), transformSub(from))
case WhileDo(cond, body) =>
Expand Down Expand Up @@ -1556,6 +1569,8 @@ object Trees {
this(this(this(x, pat), guard), body)
case Labeled(bind, expr) =>
this(this(x, bind), expr)
case ByName(expr) =>
this(x, expr)
case Return(expr, from) =>
this(this(x, expr), from)
case WhileDo(cond, body) =>
Expand Down
21 changes: 7 additions & 14 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -128,14 +128,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
Closure(meth, tss => rhsFn(tss.head).changeOwner(ctx.owner, meth))
}

/** A <byname>(...) application */
object ByName:
def apply(tree: Tree)(using Context): Apply =
Apply(ref(defn.byNameMethod), tree :: Nil)
def unapply(tree: Apply)(using Context): Option[Tree] =
if tree.fun.symbol == defn.byNameMethod then Some(tree.args.head)
else None

def CaseDef(pat: Tree, guard: Tree, body: Tree)(using Context): CaseDef =
ta.assignType(untpd.CaseDef(pat, guard, body), pat, body)

Expand All @@ -151,6 +143,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def Labeled(sym: TermSymbol, expr: Tree)(using Context): Labeled =
Labeled(Bind(sym, EmptyTree), expr)

def ByName(expr: Tree)(using Context): ByName =
ta.assignType(untpd.ByName(expr), expr)

def Return(expr: Tree, from: Tree)(using Context): Return =
ta.assignType(untpd.Return(expr, from))

Expand Down Expand Up @@ -706,6 +701,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
override def Labeled(tree: Tree)(bind: Bind, expr: Tree)(using Context): Labeled =
ta.assignType(untpdCpy.Labeled(tree)(bind, expr))

override def ByName(tree: Tree)(expr: Tree)(using Context): ByName =
ta.assignType(untpdCpy.ByName(tree)(expr), expr)

override def Return(tree: Tree)(expr: Tree, from: Tree)(using Context): Return =
ta.assignType(untpdCpy.Return(tree)(expr, from))

Expand Down Expand Up @@ -963,11 +961,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def ensureApplied(using Context): Tree =
if (tree.tpe.widen.isParameterless) tree else tree.appliedToNone

/** Is tree a by-name application `<byname>(arg)`? */
def isByName(using Context): Boolean = tree match
case Apply(fun, _) => fun.symbol == defn.byNameMethod
case _ => false

/** If tree is a by-name application `<byname>(arg)` return `arg`, otherwise the original tree */
def dropByName(using Context): Tree = tree match
case ByName(body) => body
Expand All @@ -980,7 +973,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {

/** Make sure tree is by-name application if `formal` is a by-name parameter type */
def alignByName(formal: Type)(using Context) = formal match
case ByNameType(underlying) => wrapByName
case ByNameType(_) if !tree.tpe.widen.isByName => ByName(tree)
case _ => tree

/** `tree == that` */
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def InlineMatch(selector: Tree, cases: List[CaseDef])(implicit src: SourceFile): Match = new InlineMatch(selector, cases)
def CaseDef(pat: Tree, guard: Tree, body: Tree)(implicit src: SourceFile): CaseDef = new CaseDef(pat, guard, body)
def Labeled(bind: Bind, expr: Tree)(implicit src: SourceFile): Labeled = new Labeled(bind, expr)
def ByName(expr: Tree)(implicit src: SourceFile): ByName = new ByName(expr)
def Return(expr: Tree, from: Tree)(implicit src: SourceFile): Return = new Return(expr, from)
def WhileDo(cond: Tree, body: Tree)(implicit src: SourceFile): WhileDo = new WhileDo(cond, body)
def Try(expr: Tree, cases: List[CaseDef], finalizer: Tree)(implicit src: SourceFile): Try = new Try(expr, cases, finalizer)
Expand Down
6 changes: 1 addition & 5 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -451,10 +451,6 @@ class Definitions {
@tu lazy val throwMethod: TermSymbol = enterMethod(OpsPackageClass, nme.THROWkw,
MethodType(List(ThrowableType), NothingType))

/** Method wrapping by-name arguments; created by Typer, eliminated in ByNameLambda */
@tu lazy val byNameMethod: TermSymbol = enterMethod(OpsPackageClass, nme.BYNAME,
MethodType(List(AnyType))(mt => FunctionOf(Nil, mt.paramRefs(0), isContextual = true)))

@tu lazy val NothingClass: ClassSymbol = enterCompleteClassSymbol(
ScalaPackageClass, tpnme.Nothing, AbstractFinal, List(AnyType))
def NothingType: TypeRef = NothingClass.typeRef
Expand Down Expand Up @@ -1812,7 +1808,7 @@ class Definitions {

/** Lists core methods that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */
@tu lazy val syntheticCoreMethods: List[TermSymbol] =
AnyMethods ++ ObjectMethods ++ List(String_+, throwMethod, byNameMethod)
AnyMethods ++ ObjectMethods ++ List(String_+, throwMethod)

@tu lazy val reservedScalaClassNames: Set[Name] = syntheticScalaClasses.map(_.name).toSet

Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ object StdNames {
val BITMAP_TRANSIENT: N = s"${BITMAP_PREFIX}trans$$" // initialization bitmap for transient lazy vals
val BITMAP_CHECKINIT: N = s"${BITMAP_PREFIX}init$$" // initialization bitmap for checkinit values
val BITMAP_CHECKINIT_TRANSIENT: N = s"${BITMAP_PREFIX}inittrans$$" // initialization bitmap for transient checkinit values
val BYNAME: N = "<byname>"
val DEFAULT_GETTER: N = str.DEFAULT_GETTER
val DEFAULT_GETTER_INIT: N = "$lessinit$greater"
val DO_WHILE_PREFIX: N = "doWhile$"
Expand Down
15 changes: 9 additions & 6 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -417,15 +417,13 @@ class TreePickler(pickler: TastyPickler) {
if fun.symbol eq defn.throwMethod then
writeByte(THROW)
pickleTree(args.head)
else if fun.symbol eq defn.byNameMethod then
pickleTree(args.head)
// <by-name>(...) applications are re-constituted when unpickling
// based on formal parameter types.
else
writeByte(APPLY)
withLength {
pickleTree(fun)
args.foreach(pickleTree)
args.foreach(arg => pickleTree(arg.dropByName))
// <by-name>(...) applications are re-constituted when unpickling
// based on formal parameter types.
}
case TypeApply(fun, args) =>
writeByte(TYPEAPPLY)
Expand Down Expand Up @@ -458,7 +456,7 @@ class TreePickler(pickler: TastyPickler) {
case NamedArg(name, arg) =>
writeByte(NAMEDARG)
pickleName(name)
pickleTree(arg)
pickleTree(arg.dropByName)
case Assign(lhs, rhs) =>
writeByte(ASSIGN)
withLength { pickleTree(lhs); pickleTree(rhs) }
Expand Down Expand Up @@ -493,6 +491,11 @@ class TreePickler(pickler: TastyPickler) {
case CaseDef(pat, guard, rhs) =>
writeByte(CASEDEF)
withLength { pickleTree(pat); pickleTree(rhs); pickleTreeUnlessEmpty(guard) }
case ByName(expr) =>
assert(false, i"ByName tree is not a method argument: $tree")
// If we do allow ByName types that are not parameters in a future 3.x version,
// we'd have to replace the assert with a -release check that these types are
// not issued in earlier Tasty versions.
case Return(expr, from) =>
writeByte(RETURN)
withLength { pickleSymRef(from.symbol); pickleTreeUnlessEmpty(expr) }
Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1142,11 +1142,14 @@ class TreeUnpickler(reader: TastyReader,
val (mixId, mixTpe) = ifBefore(end)(readQualId(), (untpd.EmptyTypeIdent, NoType))
tpd.Super(qual, mixId, mixTpe.typeSymbol)
case APPLY =>
def restoreByName(arg: Tree, formal: Type): Tree = arg match
case NamedArg(name, arg1) => cpy.NamedArg(arg)(name, restoreByName(arg1, formal))
case _ => arg.alignByName(formal)
val fn = readTerm()
var args = until(end)(readTerm())
fn.tpe.widen match
case mt: MethodType =>
args = args.zipWithConserve(mt.paramInfos)(_.alignByName(_))
args = args.zipWithConserve(mt.paramInfos)(restoreByName)
case _ =>
tpd.Apply(fn, args)
case TYPEAPPLY =>
Expand Down
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
changePrec (GlobalPrec) {
keywordStr("throw ") ~ toText(args.head)
}
else if fun.symbol == defn.byNameMethod && !printDebug && !ctx.settings.YtestPickler.value then
toText(args.head)
else if (!printDebug && fun.hasType && fun.symbol == defn.QuotedRuntime_exprQuote)
keywordStr("'{") ~ toTextGlobal(args, ", ") ~ keywordStr("}")
else if (!printDebug && fun.hasType && fun.symbol.isExprSplice)
Expand Down Expand Up @@ -501,6 +499,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
keywordStr("case ") ~ inPattern(toText(pat)) ~ optText(guard)(keywordStr(" if ") ~ _) ~ " => " ~ caseBlockText(body)
case Labeled(bind, expr) =>
changePrec(GlobalPrec) { toText(bind.name) ~ keywordStr("[") ~ toText(bind.symbol.info) ~ keywordStr("]: ") ~ toText(expr) }
case ByName(expr) =>
if printDebug || ctx.settings.YtestPickler.value then
"<ByName>(" ~ toText(expr) ~ ")"
else
toText(expr)
case Return(expr, from) =>
val sym = from.symbol
if (sym.is(Label))
Expand Down
13 changes: 5 additions & 8 deletions compiler/src/dotty/tools/dotc/transform/ByNameLambda.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,11 @@ class ByNameLambda extends MiniPhase, IdentityDenotTransformer:
// works on ByName arguments but not converted closures, and it sees the arguments
// after transformations by subsequent miniphases in the same group.

override def transformApply(app: Apply)(using Context): Tree = app match
case ByName(body) =>
body match
case Apply(Select(fn, nme.apply), Nil) if isPurePath(fn) && fn.tpe.widen.isByName =>
fn
case _ =>
byNameClosure(body)
case _ => app
override def transformByName(tree: ByName)(using Context): Tree = tree.expr match
case Apply(Select(fn, nme.apply), Nil) if isPurePath(fn) && fn.tpe.widen.isByName =>
fn
case _ =>
byNameClosure(tree.expr)

def byNameClosure(body: Tree)(using Context): Block =
val restpe = body.tpe.widenIfUnstable.deskolemized
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@ class CheckLoopingImplicits extends MiniPhase:
checkNotLooping(qual)
case Apply(fn, args) =>
checkNotLooping(fn)
if fn.symbol != defn.Boolean_&&
&& fn.symbol != defn.Boolean_||
&& fn.symbol != defn.byNameMethod then
if fn.symbol != defn.Boolean_&& && fn.symbol != defn.Boolean_|| then
args.foreach(checkNotLooping)
case TypeApply(fn, _) =>
checkNotLooping(fn)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
else
expr
case arg @ ByName(arg1) =>
cpy.Apply(arg)(arg.fun, transformArg(arg1, toArray) :: Nil)
cpy.ByName(arg)(transformArg(arg1, toArray))
case arg => arg

private def adaptToArray(tree: Tree)(implicit ctx: Context): Tree = tree match
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ object ExplicitOuter {
* fully qualified name `outerAccName` will fail, because the `outerAccName`'s
* result is phase dependent. In that case we use a backup strategy where we search all
* definitions in the class to find the one with the OuterAccessor flag.
* ^^^ check whether this is still needed
*/
def outerAccessor(cls: ClassSymbol)(using Context): Symbol =
if (cls.isStatic) NoSymbol // fast return to avoid scanning package decls
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class HoistSuperArgs extends MiniPhase with IdentityDenotTransformer { thisPhase
def phaseName: String = HoistSuperArgs.name

override def runsAfter: Set[String] = Set(ByNameLambda.name)
// Assumes by-name argments are already converted to closures. ^^^ or maybe run before ByNameLambda?
// Assumes by-name argments are already converted to closures.

/** Defines methods for hoisting complex supercall arguments out of
* parent super calls and constructor definitions.
Expand Down
25 changes: 25 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/MegaPhase.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ object MegaPhase {
def prepareForMatch(tree: Match)(using Context): Context = ctx
def prepareForCaseDef(tree: CaseDef)(using Context): Context = ctx
def prepareForLabeled(tree: Labeled)(using Context): Context = ctx
def prepareForByName(tree: ByName)(using Context): Context = ctx
def prepareForReturn(tree: Return)(using Context): Context = ctx
def prepareForWhileDo(tree: WhileDo)(using Context): Context = ctx
def prepareForTry(tree: Try)(using Context): Context = ctx
Expand Down Expand Up @@ -96,6 +97,7 @@ object MegaPhase {
def transformMatch(tree: Match)(using Context): Tree = tree
def transformCaseDef(tree: CaseDef)(using Context): Tree = tree
def transformLabeled(tree: Labeled)(using Context): Tree = tree
def transformByName(tree: ByName)(using Context): Tree = tree
def transformReturn(tree: Return)(using Context): Tree = tree
def transformWhileDo(tree: WhileDo)(using Context): Tree = tree
def transformTry(tree: Try)(using Context): Tree = tree
Expand Down Expand Up @@ -198,6 +200,7 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
case tree: PackageDef => goPackageDef(tree, start)
case tree: Try => goTry(tree, start)
case tree: Inlined => goInlined(tree, start)
case tree: ByName => goByName(tree, start)
case tree: Return => goReturn(tree, start)
case tree: WhileDo => goWhileDo(tree, start)
case tree: Alternative => goAlternative(tree, start)
Expand Down Expand Up @@ -397,6 +400,11 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
val expansion = transformTree(tree.expansion, start)(using inlineContext(tree.call))
goInlined(cpy.Inlined(tree)(tree.call, bindings, expansion), start)
}
case tree: ByName =>
inContext(prepByName(tree, start)(using outerCtx)) {
val expr = transformTree(tree.expr, start)
goByName(cpy.ByName(tree)(expr), start)
}
case tree: Return =>
inContext(prepReturn(tree, start)(using outerCtx)) {
val expr = transformTree(tree.expr, start)
Expand Down Expand Up @@ -535,6 +543,8 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
private val nxCaseDefTransPhase = init("transformCaseDef")
private val nxLabeledPrepPhase = init("prepareForLabeled")
private val nxLabeledTransPhase = init("transformLabeled")
private val nxByNamePrepPhase = init("prepareForByName")
private val nxByNameTransPhase = init("transformByName")
private val nxReturnPrepPhase = init("prepareForReturn")
private val nxReturnTransPhase = init("transformReturn")
private val nxWhileDoPrepPhase = init("prepareForWhileDo")
Expand Down Expand Up @@ -812,6 +822,21 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
}
}

def prepByName(tree: ByName, start: Int)(using Context): Context = {
val phase = nxByNamePrepPhase(start)
if (phase == null) ctx
else prepByName(tree, phase.idxInGroup + 1)(using phase.prepareForByName(tree))
}

def goByName(tree: ByName, start: Int)(using Context): Tree = {
val phase = nxByNameTransPhase(start)
if (phase == null) tree
else phase.transformByName(tree) match {
case tree1: ByName => goByName(tree1, phase.idxInGroup + 1)
case tree1 => transformNode(tree1, phase.idxInGroup + 1)
}
}

def prepReturn(tree: Return, start: Int)(using Context): Context = {
val phase = nxReturnPrepPhase(start)
if (phase == null) ctx
Expand Down
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ class ResolveSuper extends MiniPhase with IdentityDenotTransformer { thisPhase =

override def phaseName: String = ResolveSuper.name

override def runsAfter: Set[String] = Set(ByNameLambda.name, // verified empirically, need to figure out what the reason is.
PruneErasedDefs.name) // Erased decls make `isCurrent` work incorrectly
override def runsAfter: Set[String] = Set(
ByNameLambda.name, // verified empirically for ElimByName
//^^^ check whether this is also needed for ByNameLambda
PruneErasedDefs.name) // Erased decls make `isCurrent` work incorrectly

override def changesMembers: Boolean = true // the phase adds super accessors

Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/transform/Splicer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,10 @@ object Splicer {
summon[Env]
}

def checkIfValidArgument(tree: Tree)(using Env): Unit = tree.dropByName match {
def checkIfValidArgument(tree: Tree)(using Env): Unit = tree match {
case Block(Nil, expr) => checkIfValidArgument(expr)
case Typed(expr, _) => checkIfValidArgument(expr)
case ByName(expr) => checkIfValidArgument(expr)

case Apply(Select(Apply(fn, quoted :: Nil), nme.apply), _) if fn.symbol == defn.QuotedRuntime_exprQuote =>
val noSpliceChecker = new TreeTraverser {
Expand Down Expand Up @@ -259,9 +260,7 @@ object Splicer {

// TODO disallow interpreted method calls as arguments
case Call(fn, args) =>
if fn.symbol == defn.byNameMethod then
() => interpretTree(args.head.head)
else if (fn.symbol.isConstructor && fn.symbol.owner.owner.is(Package))
if (fn.symbol.isConstructor && fn.symbol.owner.owner.is(Package))
interpretNew(fn.symbol, args.flatten.map(interpretTree))
else if (fn.symbol.is(Module))
interpretModuleAccess(fn.symbol)
Expand Down Expand Up @@ -293,6 +292,7 @@ object Splicer {
// `val j$1 = x; val i$1 = y; foo(i = i$1, j = j$1)`
case Block(stats, expr) => interpretBlock(stats, expr)
case NamedArg(_, arg) => interpretTree(arg)
case ByName(arg) => () => interpretTree(arg)

case Inlined(_, bindings, expansion) => interpretBlock(bindings, expansion)

Expand Down
Loading

0 comments on commit b7fa78d

Please sign in to comment.