Skip to content

Commit

Permalink
See through aliases before decomposing And/Or in isSubType
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
odersky committed Nov 16, 2022
1 parent 0a88360 commit c13cab0
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 16 deletions.
35 changes: 19 additions & 16 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -418,16 +418,16 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
true
}
def compareTypeParamRef =
assumedTrue(tp1) ||
tp2.match {
case tp2: TypeParamRef => constraint.isLess(tp1, tp2)
case _ => false
} ||
isSubTypeWhenFrozen(bounds(tp1).hi.boxed, tp2) || {
if (canConstrain(tp1) && !approx.high)
addConstraint(tp1, tp2, fromBelow = false) && flagNothingBound
else thirdTry
}
assumedTrue(tp1)
|| tp2.dealias.match
case tp2a: TypeParamRef => constraint.isLess(tp1, tp2a)
case tp2a: AndType => recur(tp1, tp2a)
case _ => false
|| isSubTypeWhenFrozen(bounds(tp1).hi.boxed, tp2)
|| (if canConstrain(tp1) && !approx.high then
addConstraint(tp1, tp2, fromBelow = false) && flagNothingBound
else thirdTry)

compareTypeParamRef
case tp1: ThisType =>
val cls1 = tp1.cls
Expand Down Expand Up @@ -585,7 +585,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
}

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

def thirdTry: Boolean = tp2 match {
Expand Down
File renamed without changes.
File renamed without changes.
19 changes: 19 additions & 0 deletions tests/pos/i16311a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
object test:

trait Tagged[U]
type WithTag[+T, U] = T & Tagged[U]

trait FromInput[Val]
implicit def coercedScalaInput[T]: FromInput[WithTag[T, Int]] = ???
implicit def optionInput[T](implicit ev: FromInput[T]): FromInput[Option[T]] = ???

trait WithoutInputTypeTags[T]
implicit def coercedOptArgTpe[T]: WithoutInputTypeTags[Option[T & Tagged[Int]]] = ???

trait InputType[+T]
class OptionInputType[T](ofType: InputType[T]) extends InputType[Option[T]]

type Argument[T]
def argument[T](argumentType: InputType[T])(implicit fromInput: FromInput[T], res: WithoutInputTypeTags[T]): Argument[Option[T]] = ???

def test = argument(OptionInputType(??? : InputType[Boolean & Tagged[Int]]))
18 changes: 18 additions & 0 deletions tests/pos/i16311c.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class C:
trait Tagged[U]
type WithTag[+T, U] >: T & Tagged[U]

trait FromInput[Val]
implicit def coercedScalaInput[T]: FromInput[WithTag[T, Int]] = ???
implicit def optionInput[T](implicit ev: FromInput[T]): FromInput[Option[T]] = ???

trait WithoutInputTypeTags[T]
implicit def coercedOptArgTpe[T]: WithoutInputTypeTags[Option[WithTag[T, Int]]] = ???

trait InputType[+T]
class OptionInputType[T](ofType: InputType[T]) extends InputType[Option[T]]

type Argument[T]
def argument[T](argumentType: InputType[T])(implicit fromInput: FromInput[T], res: WithoutInputTypeTags[T]): Argument[Option[T]] = ???

def test = argument(OptionInputType(??? : InputType[WithTag[Boolean, Int]]))
18 changes: 18 additions & 0 deletions tests/pos/i16311d.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class C:
trait Tagged[U]
type WithTag[+T, U] <: T & Tagged[U]

trait FromInput[Val]
implicit def coercedScalaInput[T]: FromInput[WithTag[T, Int]] = ???
implicit def optionInput[T](implicit ev: FromInput[T]): FromInput[Option[T]] = ???

trait WithoutInputTypeTags[T]
implicit def coercedOptArgTpe[T]: WithoutInputTypeTags[Option[WithTag[T, Int]]] = ???

trait InputType[+T]
class OptionInputType[T](ofType: InputType[T]) extends InputType[Option[T]]

type Argument[T]
def argument[T](argumentType: InputType[T])(implicit fromInput: FromInput[T], res: WithoutInputTypeTags[T]): Argument[Option[T]] = ???

def test = argument(OptionInputType(??? : InputType[WithTag[Boolean, Int]]))

0 comments on commit c13cab0

Please sign in to comment.