Skip to content

Commit 417e7e7

Browse files
committed
Install the new ElimByName in place of ByNameClosures
... and drop the old ElimByName.
1 parent 89d90e6 commit 417e7e7

11 files changed

+161
-344
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

+1-3
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,11 @@ class Compiler {
6969
new InlineVals, // Check right hand-sides of an `inline val`s
7070
new ExpandSAMs, // Expand single abstract method closures to anonymous classes
7171
new ElimRepeated) :: // Rewrite vararg parameters and arguments
72-
List(new ElimByNameParams) ::
7372
List(new init.Checker) :: // Check initialization of objects
7473
List(new ProtectedAccessors, // Add accessors for protected members
7574
new ExtensionMethods, // Expand methods of value classes with extension methods
7675
new UncacheGivenAliases, // Avoid caching RHS of simple parameterless given aliases
77-
new ByNameClosures, // Expand arguments to by-name parameters to closures
76+
new ElimByName, // Map by-name parameters to functions
7877
new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope
7978
new SpecializeApplyMethods, // Adds specialized methods to FunctionN
8079
new RefChecks, // Various checks mostly related to abstract members and overriding
@@ -84,7 +83,6 @@ class Compiler {
8483
new sjs.ExplicitJSClasses, // Make all JS classes explicit (Scala.js only)
8584
new ExplicitOuter, // Add accessors to outer classes from nested ones.
8685
new ExplicitSelf, // Make references to non-trivial self types explicit as casts
87-
new ElimByName, // Expand by-name parameter references
8886
new StringInterpolatorOpt) :: // Optimizes raw and s string interpolators by rewriting them to string concatenations
8987
List(new PruneErasedDefs, // Drop erased definitions from scopes and simplify erased expressions
9088
new UninitializedDefs, // Replaces `compiletime.uninitialized` by `_`

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

-6
Original file line numberDiff line numberDiff line change
@@ -447,12 +447,6 @@ class Definitions {
447447
@tu lazy val andType: TypeSymbol = enterBinaryAlias(tpnme.AND, AndType(_, _))
448448
@tu lazy val orType: TypeSymbol = enterBinaryAlias(tpnme.OR, OrType(_, _, soft = false))
449449

450-
/** Marker method to indicate an argument to a call-by-name parameter.
451-
* Created by byNameClosures and elimByName, eliminated by Erasure,
452-
*/
453-
@tu lazy val cbnArg: TermSymbol = enterPolyMethod(OpsPackageClass, nme.cbnArg, 1,
454-
pt => MethodType(List(FunctionOf(Nil, pt.paramRefs(0))), pt.paramRefs(0)))
455-
456450
/** Method representing a throw */
457451
@tu lazy val throwMethod: TermSymbol = enterMethod(OpsPackageClass, nme.THROWkw,
458452
MethodType(List(ThrowableType), NothingType))

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ object Phases {
255255
myCollectNullableFieldsPhase = phaseOfClass(classOf[CollectNullableFields])
256256
myRefChecksPhase = phaseOfClass(classOf[RefChecks])
257257
myElimRepeatedPhase = phaseOfClass(classOf[ElimRepeated])
258-
myElimByNamePhase = phaseOfClass(classOf[ElimByNameParams])
258+
myElimByNamePhase = phaseOfClass(classOf[ElimByName])
259259
myExtensionMethodsPhase = phaseOfClass(classOf[ExtensionMethods])
260260
myErasurePhase = phaseOfClass(classOf[Erasure])
261261
myElimErasedValueTypePhase = phaseOfClass(classOf[ElimErasedValueType])

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

-1
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,6 @@ object StdNames {
446446
val bytes: N = "bytes"
447447
val canEqual_ : N = "canEqual"
448448
val canEqualAny : N = "canEqualAny"
449-
val cbnArg: N = "<cbn-arg>"
450449
val checkInitialized: N = "checkInitialized"
451450
val ClassManifestFactory: N = "ClassManifestFactory"
452451
val classOf: N = "classOf"

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package core
44

55
import Types._, Contexts._, Symbols._, Flags._, Names._, NameOps._, Denotations._
66
import Decorators._
7-
import Phases.gettersPhase
7+
import Phases.{gettersPhase, elimByNamePhase}
88
import StdNames.nme
99
import TypeOps.refineUsingParent
1010
import collection.mutable
@@ -1511,10 +1511,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
15111511
}
15121512
arg2.contains(arg1norm)
15131513
case ExprType(arg2res)
1514-
if ctx.phaseId > ctx.base.elimByNamePhase.id && !ctx.erasedTypes
1514+
if ctx.phaseId > elimByNamePhase.id && !ctx.erasedTypes
15151515
&& defn.isByNameFunction(arg1) =>
15161516
// ElimByName maps `=> T` to `()? => T`, but only in method parameters. It leaves
1517-
// embedded `=> T` alone. This clause needs to compensate for that.
1517+
// embedded `=> T` arguments alone. This clause needs to compensate for that.
15181518
isSubArg(arg1.argInfos.head, arg2res)
15191519
case _ =>
15201520
arg1 match {

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

-40
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,155 @@
1-
package dotty.tools.dotc
1+
package dotty.tools
2+
package dotc
23
package transform
34

45
import core._
5-
import DenotTransformers.InfoTransformer
6-
import Symbols._
76
import Contexts._
7+
import Symbols._
88
import Types._
9+
import Flags._
10+
import SymDenotations.*
11+
import DenotTransformers.InfoTransformer
12+
import NameKinds.SuperArgName
913
import core.StdNames.nme
10-
import ast.Trees._
14+
import MegaPhase.*
15+
import Decorators.*
16+
import reporting.trace
1117

12-
/** This phase eliminates ExprTypes `=> T` as types of method parameter references, and replaces them b
13-
* nullary function types. More precisely:
18+
/** This phase implements the following transformations:
1419
*
15-
* For the types of parameter symbols:
20+
* 1. For types of method and class parameters:
1621
*
17-
* => T ==> () => T
22+
* => T becomes () ?=> T
1823
*
19-
* For cbn parameter values
24+
* 2. For references to cbn-parameters:
2025
*
21-
* x ==> x()
26+
* x becomes x.apply()
2227
*
23-
* Note: This scheme to have inconsistent types between method types (whose formal types are still
24-
* ExprTypes and parameter valdefs (which are now FunctionTypes) is not pretty. There are two
25-
* other options which have been abandoned or not yet pursued.
28+
* 3. For arguments to cbn parameters
2629
*
27-
* Option 1: Transform => T to () => T also in method and function types. The problem with this is
28-
* that is that it requires to look at every type, and this forces too much, causing
29-
* Cyclic Reference errors. Abandoned for this reason.
30+
* e becomes () ?=> e
3031
*
31-
* Option 2: Merge ElimByName with erasure, or have it run immediately before. This has not been
32-
* tried yet.
32+
* An opimization is applied: If the argument `e` to a cbn parameter is already
33+
* of type `() ?=> T` and is a pure expression, we avoid (2) and (3), i.e. we
34+
* pass `e` directly instead of `() ?=> e.apply()`.
35+
*
36+
* Note that `() ?=> T` cannot be written in source since user-defined context functions
37+
* must have at least one parameter. We use the type here as a convenient marker
38+
* of something that will erase to Function0, and where we know that it came from
39+
* a by-name parameter.
40+
*
41+
* Note also that the transformation applies only to types of parameters, not to other
42+
* occurrences of ExprTypes. In particular, embedded occurrences in function types
43+
* such as `(=> T) => U` are left as-is. Trying to convert these as well would mean
44+
* traversing all the types, and that leads to cyclic reference errors in many cases.
45+
* This can cause problems in that we might have sometimes a `() ?=> T` where a
46+
* `=> T` is expected. To compensate, there is a new clause in TypeComparer#subArg that
47+
* declares `() ?=> T` to be a subtype of `T` for arguments of type applications,
48+
* after this phase and up to erasure.
3349
*/
34-
class ElimByName extends TransformByNameApply with InfoTransformer {
50+
class ElimByName extends MiniPhase, InfoTransformer:
51+
thisPhase =>
52+
3553
import ast.tpd._
3654

3755
override def phaseName: String = ElimByName.name
3856

39-
override def changesParents: Boolean = true // Only true for by-names
57+
override def runsAfterGroupsOf: Set[String] = Set(ExpandSAMs.name, ElimRepeated.name)
58+
// - ExpanSAMs applied to partial functions creates methods that need
59+
// to be fully defined before converting. Test case is pos/i9391.scala.
60+
// - ByNameLambda needs to run in a group after ElimRepeated since ElimRepeated
61+
// works on simple arguments but not converted closures, and it sees the arguments
62+
// after transformations by subsequent miniphases in the same group.
63+
64+
override def changesParents: Boolean = true
65+
// Expr types in parent type arguments are changed to function types.
66+
67+
/** If denotation had an ExprType before, it now gets a function type */
68+
private def exprBecomesFunction(symd: SymDenotation)(using Context): Boolean =
69+
symd.is(Param) || symd.is(ParamAccessor, butNot = Method)
70+
71+
def transformInfo(tp: Type, sym: Symbol)(using Context): Type = tp match {
72+
case ExprType(rt) if exprBecomesFunction(sym) =>
73+
defn.ByNameFunction(rt)
74+
case tp: MethodType =>
75+
def exprToFun(tp: Type) = tp match
76+
case ExprType(rt) => defn.ByNameFunction(rt)
77+
case tp => tp
78+
tp.derivedLambdaType(
79+
paramInfos = tp.paramInfos.mapConserve(exprToFun),
80+
resType = transformInfo(tp.resType, sym))
81+
case tp: PolyType =>
82+
tp.derivedLambdaType(resType = transformInfo(tp.resType, sym))
83+
case _ => tp
84+
}
85+
86+
override def infoMayChange(sym: Symbol)(using Context): Boolean =
87+
sym.is(Method) || exprBecomesFunction(sym)
88+
89+
def byNameClosure(arg: Tree, argType: Type)(using Context): Tree =
90+
val meth = newAnonFun(ctx.owner, MethodType(Nil, argType), coord = arg.span)
91+
Closure(meth,
92+
_ => arg.changeOwnerAfter(ctx.owner, meth, thisPhase),
93+
targetType = defn.ByNameFunction(argType)
94+
).withSpan(arg.span)
4095

41-
/** Map `tree` to `tree.apply()` is `ftree` was of ExprType and becomes now a function */
42-
private def applyIfFunction(tree: Tree, ftree: Tree)(using Context) =
43-
if (isByNameRef(ftree)) {
96+
private def isByNameRef(tree: Tree)(using Context): Boolean =
97+
defn.isByNameFunction(tree.tpe.widen)
98+
99+
/** Map `tree` to `tree.apply()` is `tree` is of type `() ?=> T` */
100+
private def applyIfFunction(tree: Tree)(using Context) =
101+
if isByNameRef(tree) then
44102
val tree0 = transformFollowing(tree)
45-
atPhase(next) { tree0.select(defn.Function0_apply).appliedToNone }
46-
}
103+
atPhase(next) { tree0.select(defn.ContextFunction0_apply).appliedToNone }
47104
else tree
48105

49106
override def transformIdent(tree: Ident)(using Context): Tree =
50-
applyIfFunction(tree, tree)
107+
applyIfFunction(tree)
51108

52109
override def transformSelect(tree: Select)(using Context): Tree =
53-
applyIfFunction(tree, tree)
110+
applyIfFunction(tree)
54111

55112
override def transformTypeApply(tree: TypeApply)(using Context): Tree = tree match {
56113
case TypeApply(Select(_, nme.asInstanceOf_), arg :: Nil) =>
57114
// tree might be of form e.asInstanceOf[x.type] where x becomes a function.
58115
// See pos/t296.scala
59-
applyIfFunction(tree, arg)
116+
applyIfFunction(tree)
60117
case _ => tree
61118
}
62119

120+
override def transformApply(tree: Apply)(using Context): Tree =
121+
trace(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) {
122+
123+
def transformArg(arg: Tree, formal: Type): Tree = formal match
124+
case defn.ByNameFunction(formalResult) =>
125+
def stripTyped(t: Tree): Tree = t match
126+
case Typed(expr, _) => stripTyped(expr)
127+
case _ => t
128+
stripTyped(arg) match
129+
case Apply(Select(qual, nme.apply), Nil)
130+
if isByNameRef(qual) && (isPureExpr(qual) || qual.symbol.isAllOf(InlineParam)) =>
131+
qual
132+
case _ =>
133+
if isByNameRef(arg) || arg.symbol.name.is(SuperArgName)
134+
then arg
135+
else
136+
var argType = arg.tpe.widenIfUnstable
137+
if argType.isBottomType then argType = formalResult
138+
byNameClosure(arg, argType)
139+
case _ =>
140+
arg
141+
142+
val mt @ MethodType(_) = tree.fun.tpe.widen
143+
val args1 = tree.args.zipWithConserve(mt.paramInfos)(transformArg)
144+
cpy.Apply(tree)(tree.fun, args1)
145+
}
146+
63147
override def transformValDef(tree: ValDef)(using Context): Tree =
64148
atPhase(next) {
65-
if (exprBecomesFunction(tree.symbol))
149+
if exprBecomesFunction(tree.symbol) then
66150
cpy.ValDef(tree)(tpt = tree.tpt.withType(tree.symbol.info))
67151
else tree
68152
}
69153

70-
def transformInfo(tp: Type, sym: Symbol)(using Context): Type = tp match {
71-
case ExprType(rt) => defn.FunctionOf(Nil, rt)
72-
case _ => tp
73-
}
74-
75-
override def infoMayChange(sym: Symbol)(using Context): Boolean = sym.isTerm && exprBecomesFunction(sym)
76-
}
77-
78-
object ElimByName {
79-
val name: String = "elimByName"
80-
}
154+
object ElimByName:
155+
val name: String = "elimByName"

0 commit comments

Comments
 (0)