Skip to content

Commit caa4d32

Browse files
committed
Improve isRef queries against top types
A refinement type would previously qualify as an `isRef` of Any, AnyKind, or AnyRef. Often that is not what was intended. Break out `isAny`, `isAnyKind`, and `isAnyRef` methods for tests that don't go through refinements.
1 parent 9fa3ea6 commit caa4d32

File tree

13 files changed

+105
-40
lines changed

13 files changed

+105
-40
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,9 @@ trait ConstraintHandling[AbstractContext] {
379379
case bounds: TypeBounds =>
380380
val lower = constraint.lower(param)
381381
val upper = constraint.upper(param)
382-
if (lower.nonEmpty && !bounds.lo.isRef(defn.NothingClass) ||
383-
upper.nonEmpty && !bounds.hi.isRef(defn.AnyClass)) constr_println(i"INIT*** $tl")
382+
if lower.nonEmpty && !bounds.lo.isRef(defn.NothingClass)
383+
|| upper.nonEmpty && !bounds.hi.isAny
384+
then constr_println(i"INIT*** $tl")
384385
lower.forall(addOneBound(_, bounds.hi, isUpper = true)) &&
385386
upper.forall(addOneBound(_, bounds.lo, isUpper = false))
386387
case _ =>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ class Definitions {
535535
@tu lazy val StringModule: Symbol = StringClass.linkedClass
536536
@tu lazy val String_+ : TermSymbol = enterMethod(StringClass, nme.raw.PLUS, methOfAny(StringType), Final)
537537
@tu lazy val String_valueOf_Object: Symbol = StringModule.info.member(nme.valueOf).suchThat(_.info.firstParamTypes match {
538-
case List(pt) => pt.isRef(AnyClass) || pt.isRef(ObjectClass)
538+
case List(pt) => pt.isAny || pt.isAnyRef
539539
case _ => false
540540
}).symbol
541541

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

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
501501
val base = nonExprBaseType(tp1, cls2)
502502
if (base.typeSymbol == cls2) return true
503503
}
504-
else if (tp1.isLambdaSub && !tp1.isRef(AnyKindClass))
504+
else if tp1.isLambdaSub && !tp1.isAnyKind then
505505
return recur(tp1, EtaExpansion(cls2.typeRef))
506506
fourthTry
507507
}
@@ -734,7 +734,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
734734
(gbounds1 != null) &&
735735
(isSubTypeWhenFrozen(gbounds1.hi, tp2) ||
736736
narrowGADTBounds(tp1, tp2, approx, isUpper = true)) &&
737-
{ tp2.isRef(AnyClass) || GADTusage(tp1.symbol) }
737+
{ tp2.isAny || GADTusage(tp1.symbol) }
738738
}
739739
isSubType(hi1, tp2, approx.addLow) || compareGADT || tryLiftedToThis1
740740
case _ =>
@@ -816,7 +816,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
816816
case JavaArrayType(elem1) =>
817817
def compareJavaArray = tp2 match {
818818
case JavaArrayType(elem2) => isSubType(elem1, elem2)
819-
case _ => tp2 isRef ObjectClass
819+
case _ => tp2.isAnyRef
820820
}
821821
compareJavaArray
822822
case tp1: ExprType if ctx.phase.id > ctx.gettersPhase.id =>
@@ -1629,20 +1629,21 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
16291629
// 'Object|Null', depending on whether explicit nulls are enabled.
16301630
def formal1IsObject =
16311631
if (ctx.explicitNulls) formal1 match {
1632-
case OrNull(formal1b) => formal1b.isRef(ObjectClass)
1632+
case OrNull(formal1b) => formal1b.isAnyRef
16331633
case _ => false
16341634
}
1635-
else formal1.isRef(ObjectClass)
1635+
else formal1.isAnyRef
16361636
def formal2IsObject =
16371637
if (ctx.explicitNulls) formal2 match {
1638-
case OrNull(formal2b) => formal2b.isRef(ObjectClass)
1638+
case OrNull(formal2b) => formal2b.isAnyRef
16391639
case _ => false
16401640
}
1641-
else formal2.isRef(ObjectClass)
1641+
else formal2.isAnyRef
16421642
(isSameTypeWhenFrozen(formal1, formal2a)
1643-
|| tp1.isJavaMethod && formal2IsObject && (formal1 isRef AnyClass)
1644-
|| tp2.isJavaMethod && formal1IsObject && (formal2 isRef AnyClass)) &&
1645-
loop(rest1, rest2)
1643+
|| tp1.isJavaMethod && formal2IsObject && formal1.isAny
1644+
|| tp2.isJavaMethod && formal1IsObject && formal2.isAny
1645+
)
1646+
&& loop(rest1, rest2)
16461647
case nil =>
16471648
false
16481649
}
@@ -1723,8 +1724,8 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
17231724
if (tp1 eq tp2) tp1
17241725
else if (!tp1.exists) tp2
17251726
else if (!tp2.exists) tp1
1726-
else if ((tp1 isRef AnyClass) && !tp2.isLambdaSub || (tp1 isRef AnyKindClass) || (tp2 isRef NothingClass)) tp2
1727-
else if ((tp2 isRef AnyClass) && !tp1.isLambdaSub || (tp2 isRef AnyKindClass) || (tp1 isRef NothingClass)) tp1
1727+
else if tp1.isAny && !tp2.isLambdaSub || tp1.isAnyKind || tp2.isRef(NothingClass) then tp2
1728+
else if tp2.isAny && !tp1.isLambdaSub || tp2.isAnyKind || tp1.isRef(NothingClass) then tp1
17281729
else tp2 match { // normalize to disjunctive normal form if possible.
17291730
case OrType(tp21, tp22) =>
17301731
tp1 & tp21 | tp1 & tp22
@@ -1770,11 +1771,12 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
17701771
* @note We do not admit singleton types in or-types as lubs.
17711772
*/
17721773
def lub(tp1: Type, tp2: Type, canConstrain: Boolean = false): Type = /*>|>*/ trace(s"lub(${tp1.show}, ${tp2.show}, canConstrain=$canConstrain)", subtyping, show = true) /*<|<*/ {
1774+
17731775
if (tp1 eq tp2) tp1
17741776
else if (!tp1.exists) tp1
17751777
else if (!tp2.exists) tp2
1776-
else if ((tp1 isRef AnyClass) || (tp1 isRef AnyKindClass) || (tp2 isRef NothingClass)) tp1
1777-
else if ((tp2 isRef AnyClass) || (tp2 isRef AnyKindClass) || (tp1 isRef NothingClass)) tp2
1778+
else if tp1.isAny && !tp2.isLambdaSub || tp1.isAnyKind || tp2.isRef(NothingClass) then tp1
1779+
else if tp2.isAny && !tp1.isLambdaSub || tp2.isAnyKind || tp1.isRef(NothingClass) then tp2
17781780
else {
17791781
def mergedLub: Type = {
17801782
val atoms1 = tp1.atoms

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
493493
case tr :: trs1 =>
494494
assert(!tr.classSymbol.is(Trait), i"$cls has bad parents $parents%, %")
495495
val tr1 = if (cls.is(Trait)) defn.ObjectType else tr
496-
tr1 :: trs1.filterNot(_ isRef defn.ObjectClass)
496+
tr1 :: trs1.filterNot(_.isAnyRef)
497497
case nil => nil
498498
}
499499
val erasedDecls = decls.filteredScope(sym => !sym.isType || sym.isClass)

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -177,18 +177,18 @@ object Types {
177177
* It makes no sense for it to be an alias type because isRef would always
178178
* return false in that case.
179179
*/
180-
def isRef(sym: Symbol)(implicit ctx: Context): Boolean = stripAnnots.stripTypeVar match {
180+
def isRef(sym: Symbol, skipRefined: Boolean = true)(implicit ctx: Context): Boolean = stripAnnots.stripTypeVar match {
181181
case this1: TypeRef =>
182182
this1.info match { // see comment in Namer#typeDefSig
183-
case TypeAlias(tp) => tp.isRef(sym)
183+
case TypeAlias(tp) => tp.isRef(sym, skipRefined)
184184
case _ => this1.symbol eq sym
185185
}
186-
case this1: RefinedOrRecType =>
187-
this1.parent.isRef(sym)
186+
case this1: RefinedOrRecType if skipRefined =>
187+
this1.parent.isRef(sym, skipRefined)
188188
case this1: AppliedType =>
189189
val this2 = this1.dealias
190-
if (this2 ne this1) this2.isRef(sym)
191-
else this1.underlying.isRef(sym)
190+
if (this2 ne this1) this2.isRef(sym, skipRefined)
191+
else this1.underlying.isRef(sym, skipRefined)
192192
case _ => false
193193
}
194194

@@ -201,6 +201,10 @@ object Types {
201201
false
202202
}
203203

204+
def isAny(given Context): Boolean = isRef(defn.AnyClass, skipRefined = false)
205+
def isAnyRef(given Context): Boolean = isRef(defn.ObjectClass, skipRefined = false)
206+
def isAnyKind(given Context): Boolean = isRef(defn.AnyKindClass, skipRefined = false)
207+
204208
/** Does this type refer exactly to class symbol `sym`, instead of to a subclass of `sym`?
205209
* Implemented like `isRef`, but follows more types: all type proxies as well as and- and or-types
206210
*/

compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ object Scala2Unpickler {
6969
assert(lastArg isRef defn.ArrayClass)
7070
val elemtp0 :: Nil = lastArg.baseType(defn.ArrayClass).argInfos
7171
val elemtp = elemtp0 match {
72-
case AndType(t1, t2) => // drop intersection with Object for abstract types an parameters in varargs. Erasure can handle them.
73-
if (t2.isRef(defn.ObjectClass))
72+
case AndType(t1, t2) => // drop intersection with Object for abstract types and parameters in varargs. Erasure can handle them.
73+
if t2.isAnyRef then
7474
t1 match {
7575
case t1: TypeParamRef => t1
7676
case t1: TypeRef if t1.symbol.isAbstractOrParamType => t1

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
175175
def casesText = Text(cases.map(caseText), "\n")
176176
atPrec(InfixPrec) { toText(scrutinee) } ~
177177
keywordStr(" match ") ~ "{" ~ casesText ~ "}" ~
178-
(" <: " ~ toText(bound) provided !bound.isRef(defn.AnyClass))
178+
(" <: " ~ toText(bound) provided !bound.isAny)
179179
}.close
180180
case tp: ErrorType =>
181181
s"<error ${tp.msg.msg}>"
@@ -358,7 +358,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
358358
" = " ~ toText(tp.alias)
359359
case TypeBounds(lo, hi) =>
360360
(if (lo isRef defn.NothingClass) Text() else " >: " ~ toText(lo))
361-
~ (if (hi isRef defn.AnyClass) Text() else " <: " ~ toText(hi))
361+
~ (if hi.isAny then Text() else " <: " ~ toText(hi))
362362
tparamStr ~ binder
363363
case tp @ ClassInfo(pre, cls, cparents, decls, selfInfo) =>
364364
val preText = toTextLocal(pre)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -460,9 +460,9 @@ object Erasure {
460460
untpd.cpy.Select(tree)(qual, sym.name).withType(NamedType(qual.tpe, sym))
461461

462462
def selectArrayMember(qual: Tree, erasedPre: Type): Tree =
463-
if (erasedPre isRef defn.ObjectClass)
463+
if erasedPre.isAnyRef then
464464
runtimeCallWithProtoArgs(tree.name.genericArrayOp, pt, qual)
465-
else if (!(qual.tpe <:< erasedPre))
465+
else if !(qual.tpe <:< erasedPre) then
466466
selectArrayMember(cast(qual, erasedPre), erasedPre)
467467
else
468468
assignType(untpd.cpy.Select(tree)(qual, tree.name.primitiveArrayOp), qual)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,9 @@ object ErrorReporting {
152152
if (found1 frozen_<:< expected1) (found, expected) else (found1, expected1)
153153
val postScript1 =
154154
if !postScript.isEmpty
155-
|| expected.isRef(defn.AnyClass)
155+
|| expected.isAny
156+
|| expected.isAnyRef
156157
|| expected.isRef(defn.AnyValClass)
157-
|| expected.isRef(defn.ObjectClass)
158158
|| defn.isBottomType(found)
159159
then postScript
160160
else ctx.typer.importSuggestionAddendum(ViewProto(found.widen, expected))

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -662,14 +662,15 @@ trait Implicits { self: Typer =>
662662
*/
663663
def inferView(from: Tree, to: Type)(implicit ctx: Context): SearchResult = {
664664
record("inferView")
665-
if ((to isRef defn.AnyClass)
666-
|| (to isRef defn.ObjectClass)
667-
|| (to isRef defn.UnitClass)
668-
|| (from.tpe isRef defn.NothingClass)
669-
|| (from.tpe isRef defn.NullClass)
670-
|| !(ctx.mode is Mode.ImplicitsEnabled)
671-
|| from.isInstanceOf[Super]
672-
|| (from.tpe eq NoPrefix)) NoMatchingImplicitsFailure
665+
if to.isAny
666+
|| to.isAnyRef
667+
|| to.isRef(defn.UnitClass)
668+
|| from.tpe.isRef(defn.NothingClass)
669+
|| from.tpe.isRef(defn.NullClass)
670+
|| !ctx.mode.is(Mode.ImplicitsEnabled)
671+
|| from.isInstanceOf[Super]
672+
|| (from.tpe eq NoPrefix)
673+
then NoMatchingImplicitsFailure
673674
else {
674675
def adjust(to: Type) = to.stripTypeVar.widenExpr match {
675676
case SelectionProto(name, memberProto, compat, true) =>

tests/neg/hk-variance2.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
trait S {
3+
type F[+X]
4+
}
5+
trait T {
6+
type F[-X]
7+
}
8+
object Test2 {
9+
object O extends S, T {
10+
type F[X] = Int // OK
11+
}
12+
object O2 extends S, T {
13+
type F[X] = X // error
14+
}
15+
object O3 extends S, T {
16+
type F[X] = X => X // error
17+
}
18+
}
19+
object Test3 {
20+
object O extends S, T {
21+
type F = [X] =>> Int // OK
22+
}
23+
object O2 extends S, T {
24+
type F = [X] =>> X // error
25+
}
26+
object O3 extends S, T {
27+
type F = [X] =>> X => X // error
28+
}
29+
}

tests/neg/hk-variance3.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object Test4 {
2+
def x: Any { type F[+X] } = ???
3+
def y: Any { type F[X]} = ???
4+
val z = if ??? then x else y
5+
val z2 = if ??? then y else x
6+
val zc: Any { type F[+X] } = z // error
7+
val z2c: Any { type F[+X] } = z2 // error
8+
}

tests/pos/hk-variance.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
class C[F[X]]
2+
class D[F[+X]]
3+
4+
type Id[X] = X
5+
6+
def test =
7+
val x = C[Id]()
8+
val y = D[Id]()
9+
10+
object Test2 {
11+
trait S {
12+
type F[+X]
13+
}
14+
trait T {
15+
type F[-X]
16+
}
17+
object O extends S, T {
18+
type F[X] = Int
19+
}
20+
}

0 commit comments

Comments
 (0)