Skip to content

Commit 1ec8041

Browse files
authored
Merge pull request #10670 from dotty-staging/parametric-top
Add Matchable trait
2 parents 15a25e7 + 7fad5c7 commit 1ec8041

File tree

34 files changed

+348
-71
lines changed

34 files changed

+348
-71
lines changed

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

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -397,17 +397,14 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
397397
case TypeApply(fn, _) =>
398398
if (fn.symbol.is(Erased) || fn.symbol == defn.QuotedTypeModule_of || fn.symbol == defn.Predef_classOf) Pure else exprPurity(fn)
399399
case Apply(fn, args) =>
400-
def isKnownPureOp(sym: Symbol) =
401-
sym.owner.isPrimitiveValueClass
402-
|| sym.owner == defn.StringClass
403-
|| defn.pureMethods.contains(sym)
404-
if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol) // A constant expression with pure arguments is pure.
405-
|| (fn.symbol.isStableMember && !fn.symbol.is(Lazy))) // constructors of no-inits classes are stable
400+
if isPureApply(tree, fn) then
406401
minOf(exprPurity(fn), args.map(exprPurity)) `min` Pure
407-
else if (fn.symbol.is(Erased)) Pure
408-
else if (fn.symbol.isStableMember) /* && fn.symbol.is(Lazy) */
402+
else if fn.symbol.is(Erased) then
403+
Pure
404+
else if fn.symbol.isStableMember /* && fn.symbol.is(Lazy) */ then
409405
minOf(exprPurity(fn), args.map(exprPurity)) `min` Idempotent
410-
else Impure
406+
else
407+
Impure
411408
case Typed(expr, _) =>
412409
exprPurity(expr)
413410
case Block(stats, expr) =>
@@ -440,6 +437,17 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
440437

441438
def isPureBinding(tree: Tree)(using Context): Boolean = statPurity(tree) >= Pure
442439

440+
/** Is the application `tree` with function part `fn` known to be pure?
441+
* Function value and arguments can still be impure.
442+
*/
443+
def isPureApply(tree: Tree, fn: Tree)(using Context): Boolean =
444+
def isKnownPureOp(sym: Symbol) =
445+
sym.owner.isPrimitiveValueClass
446+
|| sym.owner == defn.StringClass
447+
|| defn.pureMethods.contains(sym)
448+
tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol) // A constant expression with pure arguments is pure.
449+
|| fn.symbol.isStableMember && !fn.symbol.is(Lazy) // constructors of no-inits classes are stable
450+
443451
/** The purity level of this reference.
444452
* @return
445453
* PurePath if reference is (nonlazy and stable)

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

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -259,10 +259,19 @@ class Definitions {
259259
*/
260260
@tu lazy val AnyClass: ClassSymbol = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.Any, Abstract, Nil), ensureCtor = false)
261261
def AnyType: TypeRef = AnyClass.typeRef
262-
@tu lazy val AnyValClass: ClassSymbol = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.AnyVal, Abstract, List(AnyClass.typeRef)))
262+
@tu lazy val MatchableClass: ClassSymbol = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.Matchable, Trait, AnyType :: Nil), ensureCtor = false)
263+
def MatchableType: TypeRef = MatchableClass.typeRef
264+
@tu lazy val AnyValClass: ClassSymbol =
265+
val res = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.AnyVal, Abstract, List(AnyType, MatchableType)))
266+
// Mark companion as absent, so that class does not get re-completed
267+
val companion = ScalaPackageVal.info.decl(nme.AnyVal).symbol
268+
companion.moduleClass.markAbsent()
269+
companion.markAbsent()
270+
res
271+
263272
def AnyValType: TypeRef = AnyValClass.typeRef
264273

265-
@tu lazy val Any_== : TermSymbol = enterMethod(AnyClass, nme.EQ, methOfAny(BooleanType), Final)
274+
@tu lazy val Any_== : TermSymbol = enterMethod(AnyClass, nme.EQ, methOfAny(BooleanType), Final)
266275
@tu lazy val Any_!= : TermSymbol = enterMethod(AnyClass, nme.NE, methOfAny(BooleanType), Final)
267276
@tu lazy val Any_equals: TermSymbol = enterMethod(AnyClass, nme.equals_, methOfAny(BooleanType))
268277
@tu lazy val Any_hashCode: TermSymbol = enterMethod(AnyClass, nme.hashCode_, MethodType(Nil, IntType))
@@ -288,7 +297,7 @@ class Definitions {
288297
@tu lazy val ObjectClass: ClassSymbol = {
289298
val cls = requiredClass("java.lang.Object")
290299
assert(!cls.isCompleted, "race for completing java.lang.Object")
291-
cls.info = ClassInfo(cls.owner.thisType, cls, AnyClass.typeRef :: Nil, newScope)
300+
cls.info = ClassInfo(cls.owner.thisType, cls, List(AnyType, MatchableType), newScope)
292301
cls.setFlag(NoInits | JavaDefined)
293302

294303
// The companion object doesn't really exist, so it needs to be marked as
@@ -444,7 +453,7 @@ class Definitions {
444453
MethodType(List(ThrowableType), NothingType))
445454

446455
@tu lazy val NothingClass: ClassSymbol = enterCompleteClassSymbol(
447-
ScalaPackageClass, tpnme.Nothing, AbstractFinal, List(AnyClass.typeRef))
456+
ScalaPackageClass, tpnme.Nothing, AbstractFinal, List(AnyType))
448457
def NothingType: TypeRef = NothingClass.typeRef
449458
@tu lazy val NullClass: ClassSymbol = {
450459
val parent = if (ctx.explicitNulls) AnyType else ObjectType
@@ -520,7 +529,7 @@ class Definitions {
520529
// but does not define it as an explicit class.
521530
enterCompleteClassSymbol(
522531
ScalaPackageClass, tpnme.Singleton, PureInterfaceCreationFlags | Final,
523-
List(AnyClass.typeRef), EmptyScope)
532+
List(AnyType), EmptyScope)
524533
@tu lazy val SingletonType: TypeRef = SingletonClass.typeRef
525534

526535
@tu lazy val CollectionSeqType: TypeRef = requiredClassRef("scala.collection.Seq")
@@ -1144,6 +1153,8 @@ class Definitions {
11441153

11451154
// ----- Symbol sets ---------------------------------------------------
11461155

1156+
@tu lazy val topClasses: Set[Symbol] = Set(AnyClass, MatchableClass, ObjectClass, AnyValClass)
1157+
11471158
@tu lazy val AbstractFunctionType: Array[TypeRef] = mkArityArray("scala.runtime.AbstractFunction", MaxImplementedFunctionArity, 0)
11481159
val AbstractFunctionClassPerRun: PerRun[Array[Symbol]] = new PerRun(AbstractFunctionType.map(_.symbol.asClass))
11491160
def AbstractFunctionClass(n: Int)(using Context): Symbol = AbstractFunctionClassPerRun()(using ctx)(n)
@@ -1372,7 +1383,7 @@ class Definitions {
13721383
@tu lazy val ShadowableImportNames: Set[TermName] = Set("Predef".toTermName)
13731384

13741385
/** Class symbols for which no class exist at runtime */
1375-
@tu lazy val NotRuntimeClasses: Set[Symbol] = Set(AnyClass, AnyValClass, NullClass, NothingClass)
1386+
@tu lazy val NotRuntimeClasses: Set[Symbol] = Set(AnyClass, MatchableClass, AnyValClass, NullClass, NothingClass)
13761387

13771388
@tu lazy val SpecialClassTagClasses: Set[Symbol] = Set(UnitClass, AnyClass, AnyValClass)
13781389

@@ -1672,6 +1683,7 @@ class Definitions {
16721683
@tu lazy val specialErasure: SimpleIdentityMap[Symbol, ClassSymbol] =
16731684
SimpleIdentityMap.empty[Symbol]
16741685
.updated(AnyClass, ObjectClass)
1686+
.updated(MatchableClass, ObjectClass)
16751687
.updated(AnyValClass, ObjectClass)
16761688
.updated(SingletonClass, ObjectClass)
16771689
.updated(TupleClass, ProductClass)
@@ -1683,6 +1695,7 @@ class Definitions {
16831695
@tu lazy val syntheticScalaClasses: List[TypeSymbol] = {
16841696
val synth = List(
16851697
AnyClass,
1698+
MatchableClass,
16861699
AnyRefAlias,
16871700
AnyKindClass,
16881701
andType,

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,7 @@ object Mode {
116116

117117
/** Are we typechecking the rhs of an extension method? */
118118
val InExtensionMethod: Mode = newMode(26, "InExtensionMethod")
119+
120+
/** Are we resolving a TypeTest node? */
121+
val InTypeTest: Mode = newMode(27, "InTypeTest")
119122
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
297297
/** If `inst` is a TypeBounds, make sure it does not contain toplevel
298298
* references to `param` (see `Constraint#occursAtToplevel` for a definition
299299
* of "toplevel").
300-
* Any such references are replace by `Nothing` in the lower bound and `Any`
300+
* Any such references are replaced by `Nothing` in the lower bound and `Any`
301301
* in the upper bound.
302302
* References can be direct or indirect through instantiations of other
303303
* parameters in the constraint.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ object StdNames {
371371
val Import: N = "Import"
372372
val Literal: N = "Literal"
373373
val LiteralAnnotArg: N = "LiteralAnnotArg"
374+
val Matchable: N = "Matchable"
374375
val MatchCase: N = "MatchCase"
375376
val MirroredElemTypes: N = "MirroredElemTypes"
376377
val MirroredElemLabels: N = "MirroredElemLabels"

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2246,9 +2246,7 @@ object SymDenotations {
22462246
if (pcls.isCompleting) recur(pobjs1, acc)
22472247
else
22482248
val pobjMembers = pcls.nonPrivateMembersNamed(name).filterWithPredicate { d =>
2249-
// Drop members of `Any` and `Object`
2250-
val owner = d.symbol.maybeOwner
2251-
(owner ne defn.AnyClass) && (owner ne defn.ObjectClass)
2249+
!defn.topClasses.contains(d.symbol.maybeOwner) // Drop members of top classes
22522250
}
22532251
recur(pobjs1, acc.union(pobjMembers))
22542252
case nil =>

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

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,10 @@ object Types {
209209
def isAnyRef(using Context): Boolean = isRef(defn.ObjectClass, skipRefined = false)
210210
def isAnyKind(using Context): Boolean = isRef(defn.AnyKindClass, skipRefined = false)
211211

212+
def isTopType(using Context): Boolean = dealias match
213+
case tp: TypeRef => defn.topClasses.contains(tp.symbol)
214+
case _ => false
215+
212216
/** Is this type exactly Nothing (no vars, aliases, refinements etc allowed)? */
213217
def isExactlyNothing(using Context): Boolean = this match {
214218
case tp: TypeRef =>
@@ -512,6 +516,20 @@ object Types {
512516
case _ =>
513517
false
514518

519+
/** Same as hasClassSmbol(MatchableClass), except that we also follow the constraint
520+
* bounds of type variables in the constraint.
521+
*/
522+
def isMatchableBound(using Context): Boolean = dealias match
523+
case tp: TypeRef => tp.symbol == defn.MatchableClass
524+
case tp: TypeParamRef =>
525+
ctx.typerState.constraint.entry(tp) match
526+
case bounds: TypeBounds => bounds.hi.isMatchableBound
527+
case _ => false
528+
case tp: TypeProxy => tp.underlying.isMatchableBound
529+
case tp: AndType => tp.tp1.isMatchableBound || tp.tp2.isMatchableBound
530+
case tp: OrType => tp.tp1.isMatchableBound && tp.tp2.isMatchableBound
531+
case _ => false
532+
515533
/** The term symbol associated with the type */
516534
@tailrec final def termSymbol(using Context): Symbol = this match {
517535
case tp: TermRef => tp.symbol
@@ -3812,10 +3830,6 @@ object Types {
38123830

38133831
def unapply(tl: PolyType): Some[(List[LambdaParam], Type)] =
38143832
Some((tl.typeParams, tl.resType))
3815-
3816-
def any(n: Int)(using Context): PolyType =
3817-
apply(syntheticParamNames(n))(
3818-
pt => List.fill(n)(TypeBounds.empty), pt => defn.AnyType)
38193833
}
38203834

38213835
private object DepStatus {

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,10 +266,7 @@ import transform.SymUtils._
266266
val postScript = addenda.find(!_.isEmpty) match
267267
case Some(p) => p
268268
case None =>
269-
if expected.isAny
270-
|| expected.isAnyRef
271-
|| expected.isRef(defn.AnyValClass)
272-
|| found.isBottomType
269+
if expected.isTopType || found.isBottomType
273270
then ""
274271
else ctx.typer.importSuggestionAddendum(ViewProto(found.widen, expected))
275272
val (where, printCtx) = Formatting.disambiguateTypes(found2, expected2)

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ class Erasure extends Phase with DenotTransformer {
6161
// After erasure, all former Any members are now Object members
6262
val ClassInfo(pre, _, ps, decls, selfInfo) = ref.info
6363
val extendedScope = decls.cloneScope
64-
for (decl <- defn.AnyClass.classInfo.decls)
65-
if (!decl.isConstructor) extendedScope.enter(decl)
64+
for decl <- defn.AnyClass.classInfo.decls do
65+
if !decl.isConstructor then extendedScope.enter(decl)
6666
ref.copySymDenotation(
6767
info = transformInfo(ref.symbol,
6868
ClassInfo(pre, defn.ObjectClass, ps, extendedScope, selfInfo))
@@ -71,11 +71,12 @@ class Erasure extends Phase with DenotTransformer {
7171
else {
7272
val oldSymbol = ref.symbol
7373
val newSymbol =
74-
if ((oldSymbol.owner eq defn.AnyClass) && oldSymbol.isConstructor)
74+
if ((oldSymbol.owner eq defn.AnyClass) && oldSymbol.isConstructor) then
75+
//assert(false)
7576
defn.ObjectClass.primaryConstructor
7677
else oldSymbol
7778
val oldOwner = ref.owner
78-
val newOwner = if (oldOwner eq defn.AnyClass) defn.ObjectClass else oldOwner
79+
val newOwner = if oldOwner == defn.AnyClass then defn.ObjectClass else oldOwner
7980
val oldName = ref.name
8081
val newName = ref.targetName
8182
val oldInfo = ref.info

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,8 @@ trait Applications extends Compatibility {
11581158
def typedUnApply(tree: untpd.Apply, selType: Type)(using Context): Tree = {
11591159
record("typedUnApply")
11601160
val Apply(qual, args) = tree
1161+
if !ctx.mode.is(Mode.InTypeTest) then
1162+
checkMatchable(selType, tree.srcPos, pattern = true)
11611163

11621164
def notAnExtractor(tree: Tree): Tree =
11631165
// prefer inner errors

0 commit comments

Comments
 (0)