Skip to content

Commit 5543ea8

Browse files
authored
Merge pull request #14295 from dotty-staging/test-elim-repeated
Rework ElimByName
2 parents dc24b74 + a9ad052 commit 5543ea8

16 files changed

+564
-504
lines changed

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

+8-7
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import typer.{TyperPhase, RefChecks}
77
import parsing.Parser
88
import Phases.Phase
99
import transform._
10-
import dotty.tools.backend.jvm.{CollectSuperCalls, GenBCode}
1110
import dotty.tools.backend
12-
import dotty.tools.dotc.transform.localopt.StringInterpolatorOpt
11+
import backend.jvm.{CollectSuperCalls, GenBCode}
12+
import localopt.StringInterpolatorOpt
1313

1414
/** The central class of the dotc compiler. The job of a compiler is to create
1515
* runs, which process given `phases` in a given `rootContext`.
@@ -67,23 +67,24 @@ class Compiler {
6767
new CheckLoopingImplicits, // Check that implicit defs do not call themselves in an infinite loop
6868
new BetaReduce, // Reduce closure applications
6969
new InlineVals, // Check right hand-sides of an `inline val`s
70-
new ExpandSAMs) :: // Expand single abstract method closures to anonymous classes
70+
new ExpandSAMs, // Expand single abstract method closures to anonymous classes
71+
new ElimRepeated, // Rewrite vararg parameters and arguments
72+
new RefChecks) :: // Various checks mostly related to abstract members and overriding
7173
List(new init.Checker) :: // Check initialization of objects
72-
List(new ElimRepeated, // Rewrite vararg parameters and arguments
74+
List(new CrossVersionChecks, // Check issues related to deprecated and experimental
7375
new ProtectedAccessors, // Add accessors for protected members
7476
new ExtensionMethods, // Expand methods of value classes with extension methods
7577
new UncacheGivenAliases, // Avoid caching RHS of simple parameterless given aliases
76-
new ByNameClosures, // Expand arguments to by-name parameters to closures
78+
new ElimByName, // Map by-name parameters to functions
7779
new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope
80+
new ForwardDepChecks, // Check that there are no forward references to local vals
7881
new SpecializeApplyMethods, // Adds specialized methods to FunctionN
79-
new RefChecks, // Various checks mostly related to abstract members and overriding
8082
new TryCatchPatterns, // Compile cases in try/catch
8183
new PatternMatcher) :: // Compile pattern matches
8284
List(new ElimOpaque, // Turn opaque into normal aliases
8385
new sjs.ExplicitJSClasses, // Make all JS classes explicit (Scala.js only)
8486
new ExplicitOuter, // Add accessors to outer classes from nested ones.
8587
new ExplicitSelf, // Make references to non-trivial self types explicit as casts
86-
new ElimByName, // Expand by-name parameter references
8788
new StringInterpolatorOpt) :: // Optimizes raw and s string interpolators by rewriting them to string concatenations
8889
List(new PruneErasedDefs, // Drop erased definitions from scopes and simplify erased expressions
8990
new UninitializedDefs, // Replaces `compiletime.uninitialized` by `_`

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

+22-7
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))
@@ -1082,6 +1076,24 @@ class Definitions {
10821076
}
10831077
}
10841078

1079+
object ByNameFunction:
1080+
def apply(tp: Type)(using Context): Type =
1081+
defn.ContextFunction0.typeRef.appliedTo(tp :: Nil)
1082+
def unapply(tp: Type)(using Context): Option[Type] = tp match
1083+
case tp @ AppliedType(tycon, arg :: Nil) if defn.isByNameFunctionClass(tycon.typeSymbol) =>
1084+
Some(arg)
1085+
case tp @ AnnotatedType(parent, _) =>
1086+
unapply(parent)
1087+
case _ =>
1088+
None
1089+
1090+
final def isByNameFunctionClass(sym: Symbol): Boolean =
1091+
sym eq ContextFunction0
1092+
1093+
def isByNameFunction(tp: Type)(using Context): Boolean = tp match
1094+
case ByNameFunction(_) => true
1095+
case _ => false
1096+
10851097
final def isCompiletime_S(sym: Symbol)(using Context): Boolean =
10861098
sym.name == tpnme.S && sym.owner == CompiletimeOpsIntModuleClass
10871099

@@ -1295,10 +1307,12 @@ class Definitions {
12951307
).symbol.asClass
12961308

12971309
@tu lazy val Function0_apply: Symbol = Function0.requiredMethod(nme.apply)
1310+
@tu lazy val ContextFunction0_apply: Symbol = ContextFunction0.requiredMethod(nme.apply)
12981311

12991312
@tu lazy val Function0: Symbol = FunctionClass(0)
13001313
@tu lazy val Function1: Symbol = FunctionClass(1)
13011314
@tu lazy val Function2: Symbol = FunctionClass(2)
1315+
@tu lazy val ContextFunction0: Symbol = FunctionClass(0, isContextual = true)
13021316

13031317
def FunctionType(n: Int, isContextual: Boolean = false, isErased: Boolean = false)(using Context): TypeRef =
13041318
FunctionClass(n, isContextual && !ctx.erasedTypes, isErased).typeRef
@@ -1545,7 +1559,8 @@ class Definitions {
15451559
new PerRun(Function2SpecializedReturnTypes.map(_.symbol))
15461560

15471561
def isSpecializableFunction(cls: ClassSymbol, paramTypes: List[Type], retType: Type)(using Context): Boolean =
1548-
paramTypes.length <= 2 && cls.derivesFrom(FunctionClass(paramTypes.length))
1562+
paramTypes.length <= 2
1563+
&& (cls.derivesFrom(FunctionClass(paramTypes.length)) || isByNameFunctionClass(cls))
15491564
&& isSpecializableFunctionSAM(paramTypes, retType)
15501565

15511566
/** If the Single Abstract Method of a Function class has this type, is it specializable? */

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

+4
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ object Phases {
207207
private var myRefChecksPhase: Phase = _
208208
private var myPatmatPhase: Phase = _
209209
private var myElimRepeatedPhase: Phase = _
210+
private var myElimByNamePhase: Phase = _
210211
private var myExtensionMethodsPhase: Phase = _
211212
private var myExplicitOuterPhase: Phase = _
212213
private var myGettersPhase: Phase = _
@@ -229,6 +230,7 @@ object Phases {
229230
final def refchecksPhase: Phase = myRefChecksPhase
230231
final def patmatPhase: Phase = myPatmatPhase
231232
final def elimRepeatedPhase: Phase = myElimRepeatedPhase
233+
final def elimByNamePhase: Phase = myElimByNamePhase
232234
final def extensionMethodsPhase: Phase = myExtensionMethodsPhase
233235
final def explicitOuterPhase: Phase = myExplicitOuterPhase
234236
final def gettersPhase: Phase = myGettersPhase
@@ -253,6 +255,7 @@ object Phases {
253255
myCollectNullableFieldsPhase = phaseOfClass(classOf[CollectNullableFields])
254256
myRefChecksPhase = phaseOfClass(classOf[RefChecks])
255257
myElimRepeatedPhase = phaseOfClass(classOf[ElimRepeated])
258+
myElimByNamePhase = phaseOfClass(classOf[ElimByName])
256259
myExtensionMethodsPhase = phaseOfClass(classOf[ExtensionMethods])
257260
myErasurePhase = phaseOfClass(classOf[Erasure])
258261
myElimErasedValueTypePhase = phaseOfClass(classOf[ElimErasedValueType])
@@ -427,6 +430,7 @@ object Phases {
427430
def firstTransformPhase(using Context): Phase = ctx.base.firstTransformPhase
428431
def refchecksPhase(using Context): Phase = ctx.base.refchecksPhase
429432
def elimRepeatedPhase(using Context): Phase = ctx.base.elimRepeatedPhase
433+
def elimByNamePhase(using Context): Phase = ctx.base.elimByNamePhase
430434
def extensionMethodsPhase(using Context): Phase = ctx.base.extensionMethodsPhase
431435
def explicitOuterPhase(using Context): Phase = ctx.base.explicitOuterPhase
432436
def gettersPhase(using Context): Phase = ctx.base.gettersPhase

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

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

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

+14-6
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
@@ -846,7 +846,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
846846
case _ => tp2.isAnyRef
847847
}
848848
compareJavaArray
849-
case tp1: ExprType if ctx.phase.id > gettersPhase.id =>
849+
case tp1: ExprType if ctx.phaseId > gettersPhase.id =>
850850
// getters might have converted T to => T, need to compensate.
851851
recur(tp1.widenExpr, tp2)
852852
case _ =>
@@ -1499,7 +1499,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
14991499
false
15001500
}
15011501

1502-
def isSubArg(arg1: Type, arg2: Type): Boolean = arg2 match {
1502+
def isSubArg(arg1: Type, arg2: Type): Boolean = arg2 match
15031503
case arg2: TypeBounds =>
15041504
val arg1norm = arg1 match {
15051505
case arg1: TypeBounds =>
@@ -1510,15 +1510,23 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
15101510
case _ => arg1
15111511
}
15121512
arg2.contains(arg1norm)
1513+
case ExprType(arg2res)
1514+
if ctx.phaseId > elimByNamePhase.id && !ctx.erasedTypes
1515+
&& defn.isByNameFunction(arg1.dealias) =>
1516+
// ElimByName maps `=> T` to `()? => T`, but only in method parameters. It leaves
1517+
// embedded `=> T` arguments alone. This clause needs to compensate for that.
1518+
isSubArg(arg1.dealias.argInfos.head, arg2res)
15131519
case _ =>
1514-
arg1 match {
1520+
arg1 match
15151521
case arg1: TypeBounds =>
15161522
compareCaptured(arg1, arg2)
1523+
case ExprType(arg1res)
1524+
if ctx.phaseId > elimByNamePhase.id && !ctx.erasedTypes
1525+
&& defn.isByNameFunction(arg2.dealias) =>
1526+
isSubArg(arg1res, arg2.argInfos.head)
15171527
case _ =>
15181528
(v > 0 || isSubType(arg2, arg1)) &&
15191529
(v < 0 || isSubType(arg1, arg2))
1520-
}
1521-
}
15221530

15231531
isSubArg(args1.head, args2.head)
15241532
} && recurArgs(args1.tail, args2.tail, tparams2.tail)

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

-40
This file was deleted.

0 commit comments

Comments
 (0)