Skip to content

Commit 0d2bb9e

Browse files
authored
Merge pull request #11979 from dotty-staging/fix-11968
Fix treatment of bottom types in OrType#join and baseType computations
2 parents 775d881 + cbaac3f commit 0d2bb9e

File tree

5 files changed

+98
-13
lines changed

5 files changed

+98
-13
lines changed

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

+9-7
Original file line numberDiff line numberDiff line change
@@ -2098,25 +2098,27 @@ object SymDenotations {
20982098
computeTypeProxy
20992099

21002100
case tp: AndOrType =>
2101-
def computeAndOrType = {
2101+
def computeAndOrType: Type =
21022102
val tp1 = tp.tp1
21032103
val tp2 = tp.tp2
2104+
if !tp.isAnd then
2105+
if tp1.isBottomType && (tp1 frozen_<:< tp2) then return recur(tp2)
2106+
if tp2.isBottomType && (tp2 frozen_<:< tp1) then return recur(tp1)
21042107
val baseTp =
2105-
if (symbol.isStatic && tp.derivesFrom(symbol) && symbol.typeParams.isEmpty)
2108+
if symbol.isStatic && tp.derivesFrom(symbol) && symbol.typeParams.isEmpty then
21062109
symbol.typeRef
2107-
else {
2110+
else
21082111
val baseTp1 = recur(tp1)
21092112
val baseTp2 = recur(tp2)
21102113
val combined = if (tp.isAnd) baseTp1 & baseTp2 else baseTp1 | baseTp2
2111-
combined match {
2114+
combined match
21122115
case combined: AndOrType
21132116
if (combined.tp1 eq tp1) && (combined.tp2 eq tp2) && (combined.isAnd == tp.isAnd) => tp
21142117
case _ => combined
2115-
}
2116-
}
2118+
21172119
if (baseTp.exists && inCache(tp1) && inCache(tp2)) record(tp, baseTp)
21182120
baseTp
2119-
}
2121+
21202122
computeAndOrType
21212123

21222124
case JavaArrayType(_) if symbol == defn.ObjectClass =>

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

+10-6
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,16 @@ object TypeOps:
194194
*/
195195
def orDominator(tp: Type)(using Context): Type = {
196196

197-
/** a faster version of cs1 intersect cs2 */
198-
def intersect(cs1: List[ClassSymbol], cs2: List[ClassSymbol]): List[ClassSymbol] = {
199-
val cs2AsSet = new util.HashSet[ClassSymbol](128)
200-
cs2.foreach(cs2AsSet += _)
201-
cs1.filter(cs2AsSet.contains)
202-
}
197+
/** a faster version of cs1 intersect cs2 that treats bottom types correctly */
198+
def intersect(cs1: List[ClassSymbol], cs2: List[ClassSymbol]): List[ClassSymbol] =
199+
if cs1.head == defn.NothingClass then cs2
200+
else if cs2.head == defn.NothingClass then cs1
201+
else if cs1.head == defn.NullClass && !ctx.explicitNulls && cs2.head.derivesFrom(defn.ObjectClass) then cs2
202+
else if cs2.head == defn.NullClass && !ctx.explicitNulls && cs1.head.derivesFrom(defn.ObjectClass) then cs1
203+
else
204+
val cs2AsSet = new util.HashSet[ClassSymbol](128)
205+
cs2.foreach(cs2AsSet += _)
206+
cs1.filter(cs2AsSet.contains)
203207

204208
/** The minimal set of classes in `cs` which derive all other classes in `cs` */
205209
def dominators(cs: List[ClassSymbol], accu: List[ClassSymbol]): List[ClassSymbol] = (cs: @unchecked) match {

tests/pos/i11968.scala

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class C {
2+
def get(): Int = 0
3+
}
4+
5+
def g = {
6+
val s: String | Null = ???
7+
val l = s.length // ok
8+
val c: C | Null = ???
9+
c.get()
10+
}

tests/pos/i11968a.scala

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
2+
class A {
3+
def get(): Int = 0
4+
}
5+
6+
class B extends A {}
7+
8+
class C extends A {}
9+
10+
def test1 = {
11+
val s: String | Null = ???
12+
val l = s.length
13+
14+
val a: A | Null = new A
15+
a.get()
16+
17+
val bc: B | C = new B
18+
bc.get()
19+
20+
val bcn: B | (C | Null) = new C
21+
bcn.get()
22+
23+
val bnc: (B | Null) | C = null
24+
bnc.get()
25+
26+
val abcn: A | B | C | Null = new A
27+
abcn.get()
28+
}
29+
30+
def test2 = {
31+
val s: String | Nothing = ???
32+
val l = s.length
33+
34+
val a: A | Nothing = new A
35+
a.get()
36+
37+
val bc: B | C = new B
38+
bc.get()
39+
40+
val bcn: B | (C | Nothing) = new C
41+
bcn.get()
42+
43+
val bnc: (B | Nothing) | C = new B
44+
bnc.get()
45+
46+
val abcn: A | B | C | Nothing = new A
47+
abcn.get()
48+
}

tests/pos/i11981.scala

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
object Main:
2+
class Null
3+
type Optional[A] = A | Null
4+
5+
val maybeInt: Optional[Int] = 1
6+
7+
// simplest typeclass
8+
trait TC[F[_]]
9+
10+
// given instances for our Optional and standard Option[_]
11+
given g1: TC[Optional] = ???
12+
given g2: TC[Option] = ???
13+
14+
def summonTC[F[_], A](f: F[A])(using TC[F]): Unit = ???
15+
16+
summonTC(Option(42)) // OK
17+
18+
summonTC[Optional, Int](maybeInt) // OK
19+
20+
summonTC(maybeInt)
21+

0 commit comments

Comments
 (0)