Skip to content

Commit c13cab0

Browse files
committed
See through aliases before decomposing And/Or in isSubType
There seem to be two missing cases in TypeComparer where we have a TypeParamRef on one side and an And/Or type under an aloas on the other. Examples: type AND = A & B type OR = A | B p <:< AND OR <:< p In this case we missed the decomposition into smaller types that would happen otherwise. This broke i16311.scala in Ycheck and broke i15365.scala with an infinite recursion in avoidance. I verified that having an AndType as super or subtype of an abstract type works as expected. So if in the example above type AND >: A & B or type AND <: A & B it worked before. It was just aliases that were the problem (I assume it's the same for OrTypes as lower bounds).
1 parent 0a88360 commit c13cab0

File tree

6 files changed

+74
-16
lines changed

6 files changed

+74
-16
lines changed

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

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -418,16 +418,16 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
418418
true
419419
}
420420
def compareTypeParamRef =
421-
assumedTrue(tp1) ||
422-
tp2.match {
423-
case tp2: TypeParamRef => constraint.isLess(tp1, tp2)
424-
case _ => false
425-
} ||
426-
isSubTypeWhenFrozen(bounds(tp1).hi.boxed, tp2) || {
427-
if (canConstrain(tp1) && !approx.high)
428-
addConstraint(tp1, tp2, fromBelow = false) && flagNothingBound
429-
else thirdTry
430-
}
421+
assumedTrue(tp1)
422+
|| tp2.dealias.match
423+
case tp2a: TypeParamRef => constraint.isLess(tp1, tp2a)
424+
case tp2a: AndType => recur(tp1, tp2a)
425+
case _ => false
426+
|| isSubTypeWhenFrozen(bounds(tp1).hi.boxed, tp2)
427+
|| (if canConstrain(tp1) && !approx.high then
428+
addConstraint(tp1, tp2, fromBelow = false) && flagNothingBound
429+
else thirdTry)
430+
431431
compareTypeParamRef
432432
case tp1: ThisType =>
433433
val cls1 = tp1.cls
@@ -585,7 +585,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
585585
}
586586

587587
def compareTypeParamRef(tp2: TypeParamRef): Boolean =
588-
assumedTrue(tp2) || {
588+
assumedTrue(tp2)
589+
|| {
589590
val alwaysTrue =
590591
// The following condition is carefully formulated to catch all cases
591592
// where the subtype relation is true without needing to add a constraint
@@ -596,11 +597,13 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
596597
// widening in `fourthTry` before adding to the constraint.
597598
if (frozenConstraint) recur(tp1, bounds(tp2).lo.boxed)
598599
else isSubTypeWhenFrozen(tp1, tp2)
599-
alwaysTrue || {
600-
if (canConstrain(tp2) && !approx.low)
601-
addConstraint(tp2, tp1.widenExpr, fromBelow = true)
602-
else fourthTry
603-
}
600+
alwaysTrue
601+
|| tp1.dealias.match
602+
case tp1a: OrType => recur(tp1a, tp2)
603+
case _ => false
604+
|| (if canConstrain(tp2) && !approx.low then
605+
addConstraint(tp2, tp1.widenExpr, fromBelow = true)
606+
else fourthTry)
604607
}
605608

606609
def thirdTry: Boolean = tp2 match {
File renamed without changes.
File renamed without changes.

tests/pos/i16311a.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
object test:
2+
3+
trait Tagged[U]
4+
type WithTag[+T, U] = T & Tagged[U]
5+
6+
trait FromInput[Val]
7+
implicit def coercedScalaInput[T]: FromInput[WithTag[T, Int]] = ???
8+
implicit def optionInput[T](implicit ev: FromInput[T]): FromInput[Option[T]] = ???
9+
10+
trait WithoutInputTypeTags[T]
11+
implicit def coercedOptArgTpe[T]: WithoutInputTypeTags[Option[T & Tagged[Int]]] = ???
12+
13+
trait InputType[+T]
14+
class OptionInputType[T](ofType: InputType[T]) extends InputType[Option[T]]
15+
16+
type Argument[T]
17+
def argument[T](argumentType: InputType[T])(implicit fromInput: FromInput[T], res: WithoutInputTypeTags[T]): Argument[Option[T]] = ???
18+
19+
def test = argument(OptionInputType(??? : InputType[Boolean & Tagged[Int]]))

tests/pos/i16311c.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class C:
2+
trait Tagged[U]
3+
type WithTag[+T, U] >: T & Tagged[U]
4+
5+
trait FromInput[Val]
6+
implicit def coercedScalaInput[T]: FromInput[WithTag[T, Int]] = ???
7+
implicit def optionInput[T](implicit ev: FromInput[T]): FromInput[Option[T]] = ???
8+
9+
trait WithoutInputTypeTags[T]
10+
implicit def coercedOptArgTpe[T]: WithoutInputTypeTags[Option[WithTag[T, Int]]] = ???
11+
12+
trait InputType[+T]
13+
class OptionInputType[T](ofType: InputType[T]) extends InputType[Option[T]]
14+
15+
type Argument[T]
16+
def argument[T](argumentType: InputType[T])(implicit fromInput: FromInput[T], res: WithoutInputTypeTags[T]): Argument[Option[T]] = ???
17+
18+
def test = argument(OptionInputType(??? : InputType[WithTag[Boolean, Int]]))

tests/pos/i16311d.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class C:
2+
trait Tagged[U]
3+
type WithTag[+T, U] <: T & Tagged[U]
4+
5+
trait FromInput[Val]
6+
implicit def coercedScalaInput[T]: FromInput[WithTag[T, Int]] = ???
7+
implicit def optionInput[T](implicit ev: FromInput[T]): FromInput[Option[T]] = ???
8+
9+
trait WithoutInputTypeTags[T]
10+
implicit def coercedOptArgTpe[T]: WithoutInputTypeTags[Option[WithTag[T, Int]]] = ???
11+
12+
trait InputType[+T]
13+
class OptionInputType[T](ofType: InputType[T]) extends InputType[Option[T]]
14+
15+
type Argument[T]
16+
def argument[T](argumentType: InputType[T])(implicit fromInput: FromInput[T], res: WithoutInputTypeTags[T]): Argument[Option[T]] = ???
17+
18+
def test = argument(OptionInputType(??? : InputType[WithTag[Boolean, Int]]))

0 commit comments

Comments
 (0)