Skip to content

Fix glb/lub with WildcardType (commutativity) #15027

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2060,8 +2060,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
/** The greatest lower bound of two types */
def glb(tp1: Type, tp2: Type): Type = /*>|>*/ trace(s"glb(${tp1.show}, ${tp2.show})", subtyping, show = true) /*<|<*/ {
if (tp1 eq tp2) tp1
else if (!tp1.exists) tp2
else if (!tp2.exists) tp1
else if !tp1.exists || (tp1 eq WildcardType) then tp2
else if !tp2.exists || (tp2 eq WildcardType) then tp1
else if tp1.isAny && !tp2.isLambdaSub || tp1.isAnyKind || isBottom(tp2) then tp2
else if tp2.isAny && !tp1.isLambdaSub || tp2.isAnyKind || isBottom(tp1) then tp1
else tp2 match
Expand Down Expand Up @@ -2110,8 +2110,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
*/
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) /*<|<*/ {
if (tp1 eq tp2) tp1
else if (!tp1.exists) tp1
else if (!tp2.exists) tp2
else if !tp1.exists || (tp2 eq WildcardType) then tp1
else if !tp2.exists || (tp1 eq WildcardType) then tp2
else if tp1.isAny && !tp2.isLambdaSub || tp1.isAnyKind || isBottom(tp2) then tp1
else if tp2.isAny && !tp1.isLambdaSub || tp2.isAnyKind || isBottom(tp1) then tp2
else
Expand Down
42 changes: 42 additions & 0 deletions compiler/test/dotty/tools/dotc/core/TypeComparerTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package dotty.tools
package dotc
package core

import Contexts.*, Decorators.*, Denotations.*, SymDenotations.*, Symbols.*, Types.*
import printing.Formatting.Show

import org.junit.Test
import org.junit.Assert.*

class TypeComparerTest extends DottyTest:
val LongType = defn.LongType

// Ensure glb and lub give lower and upper bounds when one of the inputs is WildcardType
// and that glb and lub honours left identity and right identity, and thus is commutative with WildcardType
@Test def glbWildcardL = identityL("glb", glb)(LongType, id = WildcardType)
@Test def glbWildcardR = identityR("glb", glb)(LongType, id = WildcardType)
@Test def lubWildcardL = identityL("lub", lub)(LongType, id = WildcardType)
@Test def lubWildcardR = identityR("lub", lub)(LongType, id = WildcardType)

def identityL[A: Show](op: String, fn: (A, A) => A)(a: A, id: A) =
val x = fn(id, a)
assertEquals(i"$op(id=$id, $a) = $x, expected $a (left identity)", a, x)

def identityR[A: Show](op: String, fn: (A, A) => A)(a: A, id: A) =
val x = fn(a, id)
assertEquals(i"$op($a, id=$id) = $x, expected $a (right identity)", a, x)

// glb(a, b) = x such that x <: a, x <: b, & forAll y, y <: a, y <: b ==> y <: x
def glb(a: Type, b: Type) =
val x = TypeComparer.glb(a, b)
assertTrue(i"glb($a, $b) = $x, but $x !<: $a", x <:< a)
assertTrue(i"glb($a, $b) = $x, but $x !<: $b", x <:< b)
x

// lub(a, b) = x such that a <: x, b <: x, & forAll y, a <: y, b <: y ==> x <: y
def lub(a: Type, b: Type) =
val x = TypeComparer.lub(a, b)
assertTrue(i"lub($a, $b) = $x, but $a !<: $x", a <:< x)
assertTrue(i"lub($a, $b) = $x, but $b !<: $x", b <:< x)
x
end TypeComparerTest