Skip to content

Commit

Permalink
Fix regression in exhausitivity of HK types (#18303)
Browse files Browse the repository at this point in the history
Reverts a key part of #16958, which is a complicated case, to fix the
regression(s).
  • Loading branch information
dwijnand authored Aug 8, 2023
2 parents 965818a + 0839123 commit 512fbd5
Show file tree
Hide file tree
Showing 10 changed files with 65 additions and 20 deletions.
24 changes: 8 additions & 16 deletions compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ object SpaceEngine {
* The types should be atomic (non-decomposable) and unrelated (neither
* should be a subtype of the other).
*/
def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type)(sp: Space)(using Context): Space = trace(i"atomic intersection: ${AndType(tp1, tp2)}", debug) {
def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type)(sp: Space)(using Context): Space = trace(i"atomic intersection: ${AndType(tp1, tp2)}", debug, show) {
// Precondition: !isSubType(tp1, tp2) && !isSubType(tp2, tp1).
if !ctx.mode.is(Mode.SafeNulls) && (tp1.isNullType || tp2.isNullType) then
// Since projections of types don't include null, intersection with null is empty.
Expand Down Expand Up @@ -459,17 +459,8 @@ object SpaceEngine {
WildcardType

case tp @ AppliedType(tycon, args) =>
val args2 =
if tycon.isRef(defn.ArrayClass) then
args.map(arg => erase(arg, inArray = true, isValue = false))
else tycon.typeParams.lazyZip(args).map { (tparam, arg) =>
if isValue && tparam.paramVarianceSign == 0 then
// when matching against a value,
// any type argument for an invariant type parameter will be unchecked,
// meaning it won't fail to match against anything; thus the wildcard replacement
WildcardType
else erase(arg, inArray = false, isValue = false)
}
val inArray = tycon.isRef(defn.ArrayClass)
val args2 = args.map(arg => erase(arg, inArray = inArray, isValue = false))
tp.derivedAppliedType(erase(tycon, inArray, isValue = false), args2)

case tp @ OrType(tp1, tp2) =>
Expand Down Expand Up @@ -640,7 +631,7 @@ object SpaceEngine {
// For instance, from i15029, `decompose((X | Y).Field[T]) = [X.Field[T], Y.Field[T]]`.
parts.map(tp.derivedAppliedType(_, targs))

case tp if tp.classSymbol.isDecomposableToChildren =>
case tp if tp.isDecomposableToChildren =>
def getChildren(sym: Symbol): List[Symbol] =
sym.children.flatMap { child =>
if child eq sym then List(sym) // i3145: sealed trait Baz, val x = new Baz {}, Baz.children returns Baz...
Expand Down Expand Up @@ -676,8 +667,8 @@ object SpaceEngine {
rec(tp, Nil)
}

extension (cls: Symbol)
/** A type is decomposable to children if it's sealed,
extension (tp: Type)
/** A type is decomposable to children if it has a simple kind, it's sealed,
* abstract (or a trait) - so its not a sealed concrete class that can be instantiated on its own,
* has no anonymous children, which we wouldn't be able to name as counter-examples,
* but does have children.
Expand All @@ -686,7 +677,8 @@ object SpaceEngine {
* A sealed trait with subclasses that then get removed after `refineUsingParent`, decomposes to the empty list.
* So that's why we consider whether a type has children. */
def isDecomposableToChildren(using Context): Boolean =
cls.is(Sealed) && cls.isOneOf(AbstractOrTrait) && !cls.hasAnonymousChild && cls.children.nonEmpty
val cls = tp.classSymbol
tp.hasSimpleKind && cls.is(Sealed) && cls.isOneOf(AbstractOrTrait) && !cls.hasAnonymousChild && cls.children.nonEmpty

val ListOfNoType = List(NoType)
val ListOfTypNoType = ListOfNoType.map(Typ(_, decomposed = true))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ object Test {
def err2[A, B](value: Foo[A, B], a: A => Int): B = value match {
case b: Bar[B] => // spurious // error
b.x
case _ => ??? // avoid fatal inexhaustivity warnings suppressing the uncheckable warning
}

def fail[A, B](value: Foo[A, B], a: A => Int): B = value match {
case b: Bar[Int] => // error
b.x
case _ => ??? // avoid fatal inexhaustivity warnings suppressing the uncheckable warning
}
}
2 changes: 2 additions & 0 deletions tests/neg-custom-args/isInstanceOf/enum-approx2.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ case class Fun[A, B](f: Exp[A => B]) extends Exp[A => B]
class Test {
def eval(e: Fun[Int, Int]) = e match {
case Fun(x: Fun[Int, Double]) => ??? // error
case Fun(x: Exp[Int => String]) => ??? // error
case _ =>
}
}
1 change: 1 addition & 0 deletions tests/neg-custom-args/isInstanceOf/i11178.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ object Test1 {
def test[A](bar: Bar[A]) =
bar match {
case _: Bar[Boolean] => ??? // error
case _ => ???
}
}

Expand Down
1 change: 1 addition & 0 deletions tests/neg-custom-args/isInstanceOf/i8932.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class Dummy extends Bar[Nothing] with Foo[String]
def bugReport[A](foo: Foo[A]): Foo[A] =
foo match {
case bar: Bar[A] => bar // error
case dummy: Dummy => ???
}

def test = bugReport(new Dummy: Foo[String])
File renamed without changes.
4 changes: 0 additions & 4 deletions tests/neg/i16451.scala → tests/pending/neg/i16451.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
// scalac: -Werror
enum Color:
case Red, Green
//sealed trait Color
//object Color:
// case object Red extends Color
// case object Green extends Color

case class Wrapper[A](value: A)

Expand Down
16 changes: 16 additions & 0 deletions tests/pos/i17230.bootstrap.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
type Untyped = Type | Null

class Type
abstract class SearchFailureType extends Type

abstract class Tree[+T <: Untyped]:
def tpe: T = null.asInstanceOf[T]

class SearchFailureIdent[+T <: Untyped] extends Tree[T]

class Test_i17230_bootstrap:
def t1(arg: Tree[Type]) = arg match
case arg: SearchFailureIdent[?] => arg.tpe match
case x: SearchFailureType =>
case _ =>
case _ =>
15 changes: 15 additions & 0 deletions tests/pos/i17230.min1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// scalac: -Werror
trait Foo:
type Bar[_]

object Foo:
type Aux[B[_]] = Foo { type Bar[A] = B[A] }

class Test:
def t1[B[_]](self: Option[Foo.Aux[B]]) = self match
case Some(_) => 1
case None => 2

def t2[B[_]](self: Option[Foo.Aux[B]]) = self match
case Some(f) => 1
case None => 2
20 changes: 20 additions & 0 deletions tests/pos/i17230.orig.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// scalac: -Werror
import scala.util.*

trait Transaction {
type State[_]
}
object Transaction {
type of[S[_]] = Transaction { type State[A] = S[A] }
}
trait DynamicScope[State[_]]

case class ScopeSearch[State[_]](self: Either[Transaction.of[State], DynamicScope[State]]) {

def embedTransaction[T](f: Transaction.of[State] => T): T =
self match {
case Left(integrated) => ???
case Right(ds) => ???
}
}

0 comments on commit 512fbd5

Please sign in to comment.