Skip to content

Commit 253b491

Browse files
committed
Represent by-name types => T as () ?=> T
1 parent 582228b commit 253b491

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+533
-296
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class Compiler {
7070
new ExpandSAMs) :: // Expand single abstract method closures to anonymous classes
7171
List(new init.Checker) :: // Check initialization of objects
7272
List(new ElimRepeated, // Rewrite vararg parameters and arguments
73+
new ByNameLambda, // Replace by-name applications with closures
7374
new ProtectedAccessors, // Add accessors for protected members
7475
new ExtensionMethods, // Expand methods of value classes with extension methods
7576
new UncacheGivenAliases, // Avoid caching RHS of simple parameterless given aliases

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,14 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
726726
case _ => tree
727727
}
728728

729+
/** An anonyous function and a closure node referring to it in a block, without any wrappigs */
730+
object simpleClosure:
731+
def unapply(tree: Tree)(using Context): Option[(DefDef, Closure)] = tree match
732+
case Block((meth : DefDef) :: Nil, closure: Closure) if meth.symbol == closure.meth.symbol =>
733+
Some((meth, closure))
734+
case _ =>
735+
None
736+
729737
/** The variables defined by a pattern, in reverse order of their appearance. */
730738
def patVars(tree: Tree)(using Context): List[Symbol] = {
731739
val acc = new TreeAccumulator[List[Symbol]] {

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

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,14 @@ 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+
131139
def CaseDef(pat: Tree, guard: Tree, body: Tree)(using Context): CaseDef =
132140
ta.assignType(untpd.CaseDef(pat, guard, body), pat, body)
133141

@@ -955,6 +963,26 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
955963
def ensureApplied(using Context): Tree =
956964
if (tree.tpe.widen.isParameterless) tree else tree.appliedToNone
957965

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+
971+
/** If tree is a by-name application `<byname>(arg)` return `arg`, otherwise the original tree */
972+
def dropByName(using Context): Tree = tree match
973+
case ByName(body) => body
974+
case _ => tree
975+
976+
/** Wrap tree in a by-name application unless it is already one */
977+
def wrapByName(using Context): Tree = tree match
978+
case ByName(_) => tree
979+
case _ => ByName(tree)
980+
981+
/** Make sure tree is by-name application if `formal` is a by-name parameter type */
982+
def alignByName(formal: Type)(using Context) = formal match
983+
case ByNameType(underlying) => wrapByName
984+
case _ => tree
985+
958986
/** `tree == that` */
959987
def equal(that: Tree)(using Context): Tree =
960988
if (that.tpe.widen.isRef(defn.NothingClass))
@@ -1108,7 +1136,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
11081136

11091137
def etaExpandCFT(using Context): Tree =
11101138
def expand(target: Tree, tp: Type)(using Context): Tree = tp match
1111-
case defn.ContextFunctionType(argTypes, resType, isErased) =>
1139+
case defn.ContextFunctionType(argTypes, resType, isErased) if argTypes.nonEmpty =>
11121140
val anonFun = newAnonFun(
11131141
ctx.owner,
11141142
MethodType.companion(isContextual = true, isErased = isErased)(argTypes, resType),

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,10 @@ class Definitions {
457457
@tu lazy val throwMethod: TermSymbol = enterMethod(OpsPackageClass, nme.THROWkw,
458458
MethodType(List(ThrowableType), NothingType))
459459

460+
/** Method wrapping by-name arguments; created by Typer, eliminated in ByNameLambda */
461+
@tu lazy val byNameMethod: TermSymbol = enterMethod(OpsPackageClass, nme.BYNAME,
462+
MethodType(List(AnyType))(mt => FunctionOf(Nil, mt.paramRefs(0), isContextual = true)))
463+
460464
@tu lazy val NothingClass: ClassSymbol = enterCompleteClassSymbol(
461465
ScalaPackageClass, tpnme.Nothing, AbstractFinal, List(AnyType))
462466
def NothingType: TypeRef = NothingClass.typeRef
@@ -1081,6 +1085,9 @@ class Definitions {
10811085
}
10821086
}
10831087

1088+
final def isByNameClass(sym: Symbol): Boolean =
1089+
sym eq ContextFunction0
1090+
10841091
final def isCompiletime_S(sym: Symbol)(using Context): Boolean =
10851092
sym.name == tpnme.S && sym.owner == CompiletimeOpsIntModuleClass
10861093

@@ -1298,6 +1305,7 @@ class Definitions {
12981305
@tu lazy val Function0: Symbol = FunctionClass(0)
12991306
@tu lazy val Function1: Symbol = FunctionClass(1)
13001307
@tu lazy val Function2: Symbol = FunctionClass(2)
1308+
@tu lazy val ContextFunction0: Symbol = FunctionClass(0, isContextual = true)
13011309

13021310
def FunctionType(n: Int, isContextual: Boolean = false, isErased: Boolean = false)(using Context): TypeRef =
13031311
FunctionClass(n, isContextual && !ctx.erasedTypes, isErased).typeRef
@@ -1809,7 +1817,7 @@ class Definitions {
18091817

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

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

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ object JavaNullInterop {
111111
// then its Scala signature will be `def setNames(names: (String|Null)*): Unit`.
112112
// This is because `setNames(null)` passes as argument a single-element array containing the value `null`,
113113
// and not a `null` array.
114-
!tp.isRef(defn.RepeatedParamClass)
114+
!tp.isRepeatedParam
115115
case _ => true
116116
})
117117

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ 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>"
122123
val DEFAULT_GETTER: N = str.DEFAULT_GETTER
123124
val DEFAULT_GETTER_INIT: N = "$lessinit$greater"
124125
val DO_WHILE_PREFIX: N = "doWhile$"

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,11 +412,13 @@ class TypeApplications(val self: Type) extends AnyVal {
412412

413413
/** Translate a type of the form From[T] to either To[T] or To[? <: T] (if `wildcardArg` is set). Keep other types as they are.
414414
* `from` and `to` must be static classes, both with one type parameter, and the same variance.
415-
* Do the same for by name types => From[T] and => To[T]
415+
* Do the same for ExprTypes and by-name types => From[T] and => To[T].
416416
*/
417417
def translateParameterized(from: ClassSymbol, to: ClassSymbol, wildcardArg: Boolean = false)(using Context): Type = self match {
418418
case self @ ExprType(tp) =>
419419
self.derivedExprType(tp.translateParameterized(from, to))
420+
case self @ ByNameType(tp) =>
421+
self.derivedByNameType(tp.translateParameterized(from, to))
420422
case _ =>
421423
if (self.derivesFrom(from)) {
422424
def elemType(tp: Type): Type = tp.widenDealias match

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

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2335,13 +2335,6 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
23352335
}
23362336
case tp1: RecType =>
23372337
tp1.rebind(distributeAnd(tp1.parent, tp2))
2338-
case ExprType(rt1) =>
2339-
tp2 match {
2340-
case ExprType(rt2) =>
2341-
ExprType(rt1 & rt2)
2342-
case _ =>
2343-
NoType
2344-
}
23452338
case tp1: TypeVar if tp1.isInstantiated =>
23462339
tp1.underlying & tp2
23472340
case tp1: AnnotatedType if !tp1.isRefining =>
@@ -2359,13 +2352,6 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
23592352
* The rhs is a proper supertype of the lhs.
23602353
*/
23612354
private def distributeOr(tp1: Type, tp2: Type, isSoft: Boolean = true): Type = tp1 match {
2362-
case ExprType(rt1) =>
2363-
tp2 match {
2364-
case ExprType(rt2) =>
2365-
ExprType(lub(rt1, rt2, isSoft = isSoft))
2366-
case _ =>
2367-
NoType
2368-
}
23692355
case tp1: TypeVar if tp1.isInstantiated =>
23702356
lub(tp1.underlying, tp2, isSoft = isSoft)
23712357
case tp1: AnnotatedType if !tp1.isRefining =>

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

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -603,8 +603,6 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
603603
this(tp.widen)
604604
case SuperType(thistpe, supertpe) =>
605605
SuperType(this(thistpe), this(supertpe))
606-
case ExprType(rt) =>
607-
defn.FunctionType(0)
608606
case RefinedType(parent, nme.apply, refinedInfo) if parent.typeSymbol eq defn.PolyFunctionClass =>
609607
erasePolyFunctionApply(refinedInfo)
610608
case tp: TypeProxy =>
@@ -692,7 +690,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
692690
else defn.TupleXXLClass.typeRef
693691
}
694692

695-
/** The erasure of a symbol's info. This is different from `apply` in the way `ExprType`s and
693+
/** The erasure of a symbol's info. This is different from `apply` in the way
696694
* `PolyType`s are treated. `eraseInfo` maps them them to method types, whereas `apply` maps them
697695
* to the underlying type.
698696
*/
@@ -702,14 +700,9 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
702700
case _ => tp
703701
tp1 match
704702
case ExprType(rt) =>
705-
if sym.is(Param) then apply(tp1)
706-
// Note that params with ExprTypes are eliminated by ElimByName,
707-
// but potentially re-introduced by ResolveSuper, when we add
708-
// forwarders to mixin methods.
709-
// See doc comment for ElimByName for speculation how we could improve this.
710-
else
711-
MethodType(Nil, Nil,
712-
eraseResult(rt.translateFromRepeated(toArray = sourceLanguage.isJava)))
703+
assert(!sym.is(Param))
704+
MethodType(Nil, Nil,
705+
eraseResult(rt.translateFromRepeated(toArray = sourceLanguage.isJava)))
713706
case tp1: PolyType =>
714707
eraseResult(tp1.resultType) match
715708
case rt: MethodType => rt
@@ -820,8 +813,6 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
820813
sigName(elem) ++ "[]"
821814
case tp: TermRef =>
822815
sigName(tp.widen)
823-
case ExprType(rt) =>
824-
sigName(defn.FunctionOf(Nil, rt))
825816
case tp: TypeVar =>
826817
val inst = tp.instanceOpt
827818
if (inst.exists) sigName(inst) else tpnme.Uninstantiated

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ class MissingType(pre: Type, name: Name) extends TypeError {
4141
}
4242
}
4343

44+
class InvalidPrefix(pre: Type, desig: Designator) extends TypeError:
45+
override def produceMessage(using Context): Message =
46+
i"malformed type: $pre is not a legal prefix for $desig"
47+
4448
class RecursionOverflow(val op: String, details: => String, val previous: Throwable, val weight: Int) extends TypeError {
4549

4650
def explanation: String = s"$op $details"

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

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ object Types {
9090
*
9191
* Note: please keep in sync with copy in `docs/docs/internals/type-system.md`.
9292
*/
93-
abstract class Type extends Hashable with printing.Showable {
93+
abstract class Type extends Hashable with printing.Showable:
9494

9595
// ----- Tests -----------------------------------------------------
9696

@@ -398,8 +398,10 @@ object Types {
398398
(new isGroundAccumulator).apply(true, this)
399399

400400
/** Is this a type of a repeated parameter? */
401-
def isRepeatedParam(using Context): Boolean =
402-
typeSymbol eq defn.RepeatedParamClass
401+
def isRepeatedParam(using Context): Boolean = this match
402+
case ExprType(underlying) => underlying.isRepeatedParam
403+
case ByNameType(underlying) => underlying.isRepeatedParam
404+
case _ => typeSymbol eq defn.RepeatedParamClass
403405

404406
/** Is this the type of a method that has a repeated parameter type as
405407
* last parameter type?
@@ -1517,10 +1519,13 @@ object Types {
15171519
}
15181520

15191521
/** If this is a repeated type, its element type, otherwise the type itself */
1520-
def repeatedToSingle(using Context): Type = this match {
1521-
case tp @ ExprType(tp1) => tp.derivedExprType(tp1.repeatedToSingle)
1522-
case _ => if (isRepeatedParam) this.argTypesHi.head else this
1523-
}
1522+
def repeatedToSingle(using Context): Type = this match
1523+
case tp @ ExprType(tp1) =>
1524+
tp.derivedExprType(tp1.repeatedToSingle)
1525+
case tp @ ByNameType(tp1) =>
1526+
tp.derivedByNameType(tp1.repeatedToSingle)
1527+
case _ =>
1528+
if (isRepeatedParam) this.argTypesHi.head else this
15241529

15251530
// ----- Normalizing typerefs over refined types ----------------------------
15261531

@@ -1841,7 +1846,10 @@ object Types {
18411846
def dropRepeatedAnnot(using Context): Type = dropAnnot(defn.RepeatedAnnot)
18421847

18431848
def annotatedToRepeated(using Context): Type = this match {
1844-
case tp @ ExprType(tp1) => tp.derivedExprType(tp1.annotatedToRepeated)
1849+
case tp @ ExprType(tp1) =>
1850+
tp.derivedExprType(tp1.annotatedToRepeated)
1851+
case tp @ ByNameType(tp1) =>
1852+
tp.derivedByNameType(tp1.annotatedToRepeated)
18451853
case AnnotatedType(tp, annot) if annot matches defn.RepeatedAnnot =>
18461854
val typeSym = tp.typeSymbol.asClass
18471855
assert(typeSym == defn.SeqClass || typeSym == defn.ArrayClass)
@@ -1916,9 +1924,8 @@ object Types {
19161924

19171925
/** Is the `hash` of this type the same for all possible sequences of enclosing binders? */
19181926
def hashIsStable: Boolean = true
1919-
}
19201927

1921-
// end Type
1928+
end Type
19221929

19231930
// ----- Type categories ----------------------------------------------
19241931

@@ -2085,7 +2092,7 @@ object Types {
20852092
def designator: Designator
20862093
protected def designator_=(d: Designator): Unit
20872094

2088-
assert(prefix.isValueType || (prefix eq NoPrefix), s"invalid prefix $prefix")
2095+
if !prefix.isValueType && (prefix ne NoPrefix) then throw InvalidPrefix(prefix, designator)
20892096

20902097
private var myName: Name = null
20912098
private var lastDenotation: Denotation = null
@@ -5448,6 +5455,41 @@ object Types {
54485455
else None
54495456
}
54505457

5458+
// ----- ByName Param Type Encoding ------------------------------------------------
5459+
5460+
object ByNameType:
5461+
def apply(tp: Type)(using Context): Type =
5462+
defn.FunctionOf(Nil, tp, isContextual = true)
5463+
def unapply(tp: Type)(using Context): Option[Type] = tp match
5464+
case tp @ AppliedType(tycon, arg :: Nil) if defn.isByNameClass(tycon.typeSymbol) =>
5465+
Some(arg)
5466+
case tp @ AnnotatedType(parent, _) =>
5467+
unapply(parent)
5468+
case _ =>
5469+
None
5470+
end ByNameType
5471+
5472+
extension (tp: Type)
5473+
def isByName(using Context): Boolean = tp match
5474+
case tp @ AppliedType(tycon, arg :: Nil) =>
5475+
defn.isByNameClass(tycon.typeSymbol)
5476+
case tp @ AnnotatedType(parent, _) =>
5477+
parent.isByName
5478+
case _ =>
5479+
false
5480+
5481+
def derivedByNameType(arg: Type)(using Context): Type = tp match
5482+
case tp @ AppliedType(tycon, arg0 :: Nil) =>
5483+
assert(defn.isByNameClass(tycon.typeSymbol))
5484+
if arg0 eq arg then tp
5485+
else tp.derivedAppliedType(tycon, arg :: Nil)
5486+
case tp @ AnnotatedType(parent, annot) =>
5487+
tp.derivedAnnotatedType(parent.derivedByNameType(arg), annot)
5488+
5489+
def widenByName(using Context): Type = tp match
5490+
case ByNameType(underlying) => underlying
5491+
case _ => tp
5492+
54515493
// ----- TypeMaps --------------------------------------------------------------------
54525494

54535495
/** Where a traversal should stop */
@@ -6140,6 +6182,7 @@ object Types {
61406182
}
61416183
}
61426184

6185+
/** Type size as defined in implicit divergence checking */
61436186
class TypeSizeAccumulator(using Context) extends TypeAccumulator[Int] {
61446187
var seen = util.HashSet[Type](initialCapacity = 8)
61456188
def apply(n: Int, tp: Type): Int =
@@ -6148,7 +6191,8 @@ object Types {
61486191
seen += tp
61496192
tp match {
61506193
case tp: AppliedType =>
6151-
foldOver(n + 1, tp)
6194+
if defn.isByNameClass(tp.tycon.typeSymbol) then foldOver(n, tp.args)
6195+
else foldOver(n + 1, tp)
61526196
case tp: RefinedType =>
61536197
foldOver(n + 1, tp)
61546198
case tp: TypeRef if tp.info.isTypeAlias =>
@@ -6161,6 +6205,7 @@ object Types {
61616205
}
61626206
}
61636207

6208+
/** Covering set as defined in implicit divergence checking */
61646209
class CoveringSetAccumulator(using Context) extends TypeAccumulator[Set[Symbol]] {
61656210
var seen = util.HashSet[Type](initialCapacity = 8)
61666211
def apply(cs: Set[Symbol], tp: Type): Set[Symbol] =
@@ -6171,7 +6216,9 @@ object Types {
61716216
case tp if tp.isExactlyAny || tp.isExactlyNothing =>
61726217
cs
61736218
case tp: AppliedType =>
6174-
foldOver(cs + tp.typeSymbol, tp)
6219+
val tsym = tp.tycon.typeSymbol
6220+
if defn.isByNameClass(tsym) then foldOver(cs, tp.args)
6221+
else foldOver(cs + tsym, tp)
61756222
case tp: RefinedType =>
61766223
foldOver(cs + tp.typeSymbol, tp)
61776224
case tp: TypeRef if tp.info.isTypeAlias =>

0 commit comments

Comments
 (0)