Skip to content

Commit cad8db8

Browse files
authored
Merge pull request #15027 from dwijnand/fix-glb-lub-WildcardType
Fix glb/lub with WildcardType (commutativity)
2 parents 9585868 + f7eca62 commit cad8db8

File tree

2 files changed

+46
-4
lines changed

2 files changed

+46
-4
lines changed

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -2060,8 +2060,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
20602060
/** The greatest lower bound of two types */
20612061
def glb(tp1: Type, tp2: Type): Type = /*>|>*/ trace(s"glb(${tp1.show}, ${tp2.show})", subtyping, show = true) /*<|<*/ {
20622062
if (tp1 eq tp2) tp1
2063-
else if (!tp1.exists) tp2
2064-
else if (!tp2.exists) tp1
2063+
else if !tp1.exists || (tp1 eq WildcardType) then tp2
2064+
else if !tp2.exists || (tp2 eq WildcardType) then tp1
20652065
else if tp1.isAny && !tp2.isLambdaSub || tp1.isAnyKind || isBottom(tp2) then tp2
20662066
else if tp2.isAny && !tp1.isLambdaSub || tp2.isAnyKind || isBottom(tp1) then tp1
20672067
else tp2 match
@@ -2110,8 +2110,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
21102110
*/
21112111
def lub(tp1: Type, tp2: Type, canConstrain: Boolean = false, isSoft: Boolean = true): Type = /*>|>*/ trace(s"lub(${tp1.show}, ${tp2.show}, canConstrain=$canConstrain, isSoft=$isSoft)", subtyping, show = true) /*<|<*/ {
21122112
if (tp1 eq tp2) tp1
2113-
else if (!tp1.exists) tp1
2114-
else if (!tp2.exists) tp2
2113+
else if !tp1.exists || (tp2 eq WildcardType) then tp1
2114+
else if !tp2.exists || (tp1 eq WildcardType) then tp2
21152115
else if tp1.isAny && !tp2.isLambdaSub || tp1.isAnyKind || isBottom(tp2) then tp1
21162116
else if tp2.isAny && !tp1.isLambdaSub || tp2.isAnyKind || isBottom(tp1) then tp2
21172117
else
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package dotty.tools
2+
package dotc
3+
package core
4+
5+
import Contexts.*, Decorators.*, Denotations.*, SymDenotations.*, Symbols.*, Types.*
6+
import printing.Formatting.Show
7+
8+
import org.junit.Test
9+
import org.junit.Assert.*
10+
11+
class TypeComparerTest extends DottyTest:
12+
val LongType = defn.LongType
13+
14+
// Ensure glb and lub give lower and upper bounds when one of the inputs is WildcardType
15+
// and that glb and lub honours left identity and right identity, and thus is commutative with WildcardType
16+
@Test def glbWildcardL = identityL("glb", glb)(LongType, id = WildcardType)
17+
@Test def glbWildcardR = identityR("glb", glb)(LongType, id = WildcardType)
18+
@Test def lubWildcardL = identityL("lub", lub)(LongType, id = WildcardType)
19+
@Test def lubWildcardR = identityR("lub", lub)(LongType, id = WildcardType)
20+
21+
def identityL[A: Show](op: String, fn: (A, A) => A)(a: A, id: A) =
22+
val x = fn(id, a)
23+
assertEquals(i"$op(id=$id, $a) = $x, expected $a (left identity)", a, x)
24+
25+
def identityR[A: Show](op: String, fn: (A, A) => A)(a: A, id: A) =
26+
val x = fn(a, id)
27+
assertEquals(i"$op($a, id=$id) = $x, expected $a (right identity)", a, x)
28+
29+
// glb(a, b) = x such that x <: a, x <: b, & forAll y, y <: a, y <: b ==> y <: x
30+
def glb(a: Type, b: Type) =
31+
val x = TypeComparer.glb(a, b)
32+
assertTrue(i"glb($a, $b) = $x, but $x !<: $a", x <:< a)
33+
assertTrue(i"glb($a, $b) = $x, but $x !<: $b", x <:< b)
34+
x
35+
36+
// lub(a, b) = x such that a <: x, b <: x, & forAll y, a <: y, b <: y ==> x <: y
37+
def lub(a: Type, b: Type) =
38+
val x = TypeComparer.lub(a, b)
39+
assertTrue(i"lub($a, $b) = $x, but $a !<: $x", a <:< x)
40+
assertTrue(i"lub($a, $b) = $x, but $b !<: $x", b <:< x)
41+
x
42+
end TypeComparerTest

0 commit comments

Comments
 (0)