Skip to content

Commit b7fa78d

Browse files
committed
Separate tree nodes for ByNames
ByName nodes in arguments are not pickled, which means that we can use the same Tasty version as before.
1 parent 3f51f11 commit b7fa78d

23 files changed

+100
-81
lines changed

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,12 @@ object Trees {
623623
def name: Name = bind.name
624624
}
625625

626+
/** By-name wrapper; created by Typer and TreUnpickler, eliminated in TreePickler and ByNameLambda */
627+
case class ByName[-T >: Untyped] private[ast] (expr: Tree[T])(implicit @constructorOnly src: SourceFile)
628+
extends TermTree[T] {
629+
type ThisTree[-T >: Untyped] = ByName[T]
630+
}
631+
626632
/** return expr
627633
* where `from` refers to the method or label from which the return takes place
628634
* After program transformations this is not necessarily the enclosing method, because
@@ -1075,6 +1081,7 @@ object Trees {
10751081
type InlineMatch = Trees.InlineMatch[T]
10761082
type CaseDef = Trees.CaseDef[T]
10771083
type Labeled = Trees.Labeled[T]
1084+
type ByName = Trees.ByName[T]
10781085
type Return = Trees.Return[T]
10791086
type WhileDo = Trees.WhileDo[T]
10801087
type Try = Trees.Try[T]
@@ -1228,6 +1235,10 @@ object Trees {
12281235
case tree: Labeled if (bind eq tree.bind) && (expr eq tree.expr) => tree
12291236
case _ => finalize(tree, untpd.Labeled(bind, expr)(sourceFile(tree)))
12301237
}
1238+
def ByName(tree: Tree)(expr: Tree)(using Context): ByName = tree match {
1239+
case tree: ByName if expr eq tree.expr => tree
1240+
case _ => finalize(tree, untpd.ByName(expr)(sourceFile(tree)))
1241+
}
12311242
def Return(tree: Tree)(expr: Tree, from: Tree)(using Context): Return = tree match {
12321243
case tree: Return if (expr eq tree.expr) && (from eq tree.from) => tree
12331244
case _ => finalize(tree, untpd.Return(expr, from)(sourceFile(tree)))
@@ -1411,6 +1422,8 @@ object Trees {
14111422
cpy.CaseDef(tree)(transform(pat), transform(guard), transform(body))
14121423
case Labeled(bind, expr) =>
14131424
cpy.Labeled(tree)(transformSub(bind), transform(expr))
1425+
case ByName(expr) =>
1426+
cpy.ByName(tree)(transform(expr))
14141427
case Return(expr, from) =>
14151428
cpy.Return(tree)(transform(expr), transformSub(from))
14161429
case WhileDo(cond, body) =>
@@ -1556,6 +1569,8 @@ object Trees {
15561569
this(this(this(x, pat), guard), body)
15571570
case Labeled(bind, expr) =>
15581571
this(this(x, bind), expr)
1572+
case ByName(expr) =>
1573+
this(x, expr)
15591574
case Return(expr, from) =>
15601575
this(this(x, expr), from)
15611576
case WhileDo(cond, body) =>

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
128128
Closure(meth, tss => rhsFn(tss.head).changeOwner(ctx.owner, meth))
129129
}
130130

131-
/** A <byname>(...) application */
132-
object ByName:
133-
def apply(tree: Tree)(using Context): Apply =
134-
Apply(ref(defn.byNameMethod), tree :: Nil)
135-
def unapply(tree: Apply)(using Context): Option[Tree] =
136-
if tree.fun.symbol == defn.byNameMethod then Some(tree.args.head)
137-
else None
138-
139131
def CaseDef(pat: Tree, guard: Tree, body: Tree)(using Context): CaseDef =
140132
ta.assignType(untpd.CaseDef(pat, guard, body), pat, body)
141133

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

146+
def ByName(expr: Tree)(using Context): ByName =
147+
ta.assignType(untpd.ByName(expr), expr)
148+
154149
def Return(expr: Tree, from: Tree)(using Context): Return =
155150
ta.assignType(untpd.Return(expr, from))
156151

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

704+
override def ByName(tree: Tree)(expr: Tree)(using Context): ByName =
705+
ta.assignType(untpdCpy.ByName(tree)(expr), expr)
706+
709707
override def Return(tree: Tree)(expr: Tree, from: Tree)(using Context): Return =
710708
ta.assignType(untpdCpy.Return(tree)(expr, from))
711709

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

966-
/** Is tree a by-name application `<byname>(arg)`? */
967-
def isByName(using Context): Boolean = tree match
968-
case Apply(fun, _) => fun.symbol == defn.byNameMethod
969-
case _ => false
970-
971964
/** If tree is a by-name application `<byname>(arg)` return `arg`, otherwise the original tree */
972965
def dropByName(using Context): Tree = tree match
973966
case ByName(body) => body
@@ -980,7 +973,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
980973

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

986979
/** `tree == that` */

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
383383
def InlineMatch(selector: Tree, cases: List[CaseDef])(implicit src: SourceFile): Match = new InlineMatch(selector, cases)
384384
def CaseDef(pat: Tree, guard: Tree, body: Tree)(implicit src: SourceFile): CaseDef = new CaseDef(pat, guard, body)
385385
def Labeled(bind: Bind, expr: Tree)(implicit src: SourceFile): Labeled = new Labeled(bind, expr)
386+
def ByName(expr: Tree)(implicit src: SourceFile): ByName = new ByName(expr)
386387
def Return(expr: Tree, from: Tree)(implicit src: SourceFile): Return = new Return(expr, from)
387388
def WhileDo(cond: Tree, body: Tree)(implicit src: SourceFile): WhileDo = new WhileDo(cond, body)
388389
def Try(expr: Tree, cases: List[CaseDef], finalizer: Tree)(implicit src: SourceFile): Try = new Try(expr, cases, finalizer)

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -451,10 +451,6 @@ class Definitions {
451451
@tu lazy val throwMethod: TermSymbol = enterMethod(OpsPackageClass, nme.THROWkw,
452452
MethodType(List(ThrowableType), NothingType))
453453

454-
/** Method wrapping by-name arguments; created by Typer, eliminated in ByNameLambda */
455-
@tu lazy val byNameMethod: TermSymbol = enterMethod(OpsPackageClass, nme.BYNAME,
456-
MethodType(List(AnyType))(mt => FunctionOf(Nil, mt.paramRefs(0), isContextual = true)))
457-
458454
@tu lazy val NothingClass: ClassSymbol = enterCompleteClassSymbol(
459455
ScalaPackageClass, tpnme.Nothing, AbstractFinal, List(AnyType))
460456
def NothingType: TypeRef = NothingClass.typeRef
@@ -1812,7 +1808,7 @@ class Definitions {
18121808

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

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

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@ object StdNames {
119119
val BITMAP_TRANSIENT: N = s"${BITMAP_PREFIX}trans$$" // initialization bitmap for transient lazy vals
120120
val BITMAP_CHECKINIT: N = s"${BITMAP_PREFIX}init$$" // initialization bitmap for checkinit values
121121
val BITMAP_CHECKINIT_TRANSIENT: N = s"${BITMAP_PREFIX}inittrans$$" // initialization bitmap for transient checkinit values
122-
val BYNAME: N = "<byname>"
123122
val DEFAULT_GETTER: N = str.DEFAULT_GETTER
124123
val DEFAULT_GETTER_INIT: N = "$lessinit$greater"
125124
val DO_WHILE_PREFIX: N = "doWhile$"

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -417,15 +417,13 @@ class TreePickler(pickler: TastyPickler) {
417417
if fun.symbol eq defn.throwMethod then
418418
writeByte(THROW)
419419
pickleTree(args.head)
420-
else if fun.symbol eq defn.byNameMethod then
421-
pickleTree(args.head)
422-
// <by-name>(...) applications are re-constituted when unpickling
423-
// based on formal parameter types.
424420
else
425421
writeByte(APPLY)
426422
withLength {
427423
pickleTree(fun)
428-
args.foreach(pickleTree)
424+
args.foreach(arg => pickleTree(arg.dropByName))
425+
// <by-name>(...) applications are re-constituted when unpickling
426+
// based on formal parameter types.
429427
}
430428
case TypeApply(fun, args) =>
431429
writeByte(TYPEAPPLY)
@@ -458,7 +456,7 @@ class TreePickler(pickler: TastyPickler) {
458456
case NamedArg(name, arg) =>
459457
writeByte(NAMEDARG)
460458
pickleName(name)
461-
pickleTree(arg)
459+
pickleTree(arg.dropByName)
462460
case Assign(lhs, rhs) =>
463461
writeByte(ASSIGN)
464462
withLength { pickleTree(lhs); pickleTree(rhs) }
@@ -493,6 +491,11 @@ class TreePickler(pickler: TastyPickler) {
493491
case CaseDef(pat, guard, rhs) =>
494492
writeByte(CASEDEF)
495493
withLength { pickleTree(pat); pickleTree(rhs); pickleTreeUnlessEmpty(guard) }
494+
case ByName(expr) =>
495+
assert(false, i"ByName tree is not a method argument: $tree")
496+
// If we do allow ByName types that are not parameters in a future 3.x version,
497+
// we'd have to replace the assert with a -release check that these types are
498+
// not issued in earlier Tasty versions.
496499
case Return(expr, from) =>
497500
writeByte(RETURN)
498501
withLength { pickleSymRef(from.symbol); pickleTreeUnlessEmpty(expr) }

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1142,11 +1142,14 @@ class TreeUnpickler(reader: TastyReader,
11421142
val (mixId, mixTpe) = ifBefore(end)(readQualId(), (untpd.EmptyTypeIdent, NoType))
11431143
tpd.Super(qual, mixId, mixTpe.typeSymbol)
11441144
case APPLY =>
1145+
def restoreByName(arg: Tree, formal: Type): Tree = arg match
1146+
case NamedArg(name, arg1) => cpy.NamedArg(arg)(name, restoreByName(arg1, formal))
1147+
case _ => arg.alignByName(formal)
11451148
val fn = readTerm()
11461149
var args = until(end)(readTerm())
11471150
fn.tpe.widen match
11481151
case mt: MethodType =>
1149-
args = args.zipWithConserve(mt.paramInfos)(_.alignByName(_))
1152+
args = args.zipWithConserve(mt.paramInfos)(restoreByName)
11501153
case _ =>
11511154
tpd.Apply(fn, args)
11521155
case TYPEAPPLY =>

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,8 +428,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
428428
changePrec (GlobalPrec) {
429429
keywordStr("throw ") ~ toText(args.head)
430430
}
431-
else if fun.symbol == defn.byNameMethod && !printDebug && !ctx.settings.YtestPickler.value then
432-
toText(args.head)
433431
else if (!printDebug && fun.hasType && fun.symbol == defn.QuotedRuntime_exprQuote)
434432
keywordStr("'{") ~ toTextGlobal(args, ", ") ~ keywordStr("}")
435433
else if (!printDebug && fun.hasType && fun.symbol.isExprSplice)
@@ -501,6 +499,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
501499
keywordStr("case ") ~ inPattern(toText(pat)) ~ optText(guard)(keywordStr(" if ") ~ _) ~ " => " ~ caseBlockText(body)
502500
case Labeled(bind, expr) =>
503501
changePrec(GlobalPrec) { toText(bind.name) ~ keywordStr("[") ~ toText(bind.symbol.info) ~ keywordStr("]: ") ~ toText(expr) }
502+
case ByName(expr) =>
503+
if printDebug || ctx.settings.YtestPickler.value then
504+
"<ByName>(" ~ toText(expr) ~ ")"
505+
else
506+
toText(expr)
504507
case Return(expr, from) =>
505508
val sym = from.symbol
506509
if (sym.is(Label))

compiler/src/dotty/tools/dotc/transform/ByNameLambda.scala

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,11 @@ class ByNameLambda extends MiniPhase, IdentityDenotTransformer:
2323
// works on ByName arguments but not converted closures, and it sees the arguments
2424
// after transformations by subsequent miniphases in the same group.
2525

26-
override def transformApply(app: Apply)(using Context): Tree = app match
27-
case ByName(body) =>
28-
body match
29-
case Apply(Select(fn, nme.apply), Nil) if isPurePath(fn) && fn.tpe.widen.isByName =>
30-
fn
31-
case _ =>
32-
byNameClosure(body)
33-
case _ => app
26+
override def transformByName(tree: ByName)(using Context): Tree = tree.expr match
27+
case Apply(Select(fn, nme.apply), Nil) if isPurePath(fn) && fn.tpe.widen.isByName =>
28+
fn
29+
case _ =>
30+
byNameClosure(tree.expr)
3431

3532
def byNameClosure(body: Tree)(using Context): Block =
3633
val restpe = body.tpe.widenIfUnstable.deskolemized

compiler/src/dotty/tools/dotc/transform/CheckLoopingImplicits.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ class CheckLoopingImplicits extends MiniPhase:
4545
checkNotLooping(qual)
4646
case Apply(fn, args) =>
4747
checkNotLooping(fn)
48-
if fn.symbol != defn.Boolean_&&
49-
&& fn.symbol != defn.Boolean_||
50-
&& fn.symbol != defn.byNameMethod then
48+
if fn.symbol != defn.Boolean_&& && fn.symbol != defn.Boolean_|| then
5149
args.foreach(checkNotLooping)
5250
case TypeApply(fn, _) =>
5351
checkNotLooping(fn)

0 commit comments

Comments
 (0)