Skip to content

Commit 531bf4d

Browse files
committed
Allow refineUsingParent to infer GADT bounds
Not doing so makes the type var interpolation between typer and the match space analysis different, which can result in false positive and negative exhaustivity and reachability warnings
1 parent 19eff87 commit 531bf4d

File tree

4 files changed

+26
-2
lines changed

4 files changed

+26
-2
lines changed

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,16 @@ object TypeOps:
712712

713713
val childTp = if (child.isTerm) child.termRef else child.typeRef
714714

715-
inContext(ctx.fresh.setExploreTyperState().setFreshGADTBounds) {
715+
val ctx1 = ctx.fresh.setExploreTyperState().setFreshGADTBounds
716+
val ctx2 = parent match
717+
case _: RefinedType =>
718+
ctx1
719+
// patmat/t9657
720+
// When running Bicycle.type <:< Vehicle { A = P }
721+
// TypeComparer is happy to infer GADT bounds P >: Pedal.type <: Petrol.type & Pedal.type
722+
// Despite the fact that Bicycle is an object, and thus final, so its type A can only be Pedal.type.
723+
case _ => ctx1.addMode(Mode.GadtConstraintInference)
724+
inContext(ctx2) {
716725
instantiateToSubType(childTp, parent).dealias
717726
}
718727
}
@@ -829,6 +838,13 @@ object TypeOps:
829838
val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) }
830839
val protoTp1 = inferThisMap.apply(tp1).appliedTo(tvars)
831840

841+
val getAbstractSymbols = new TypeAccumulator[List[Symbol]]:
842+
def apply(xs: List[Symbol], tp: Type) = tp.dealias match
843+
case tp: TypeRef if !tp.symbol.isClass => foldOver(tp.symbol :: xs, tp)
844+
case tp => foldOver(xs, tp)
845+
val syms2 = getAbstractSymbols(Nil, tp2).reverse
846+
if syms2.nonEmpty then ctx.gadt.addToConstraint(syms2)
847+
832848
// If parent contains a reference to an abstract type, then we should
833849
// refine subtype checking to eliminate abstract types according to
834850
// variance. As this logic is only needed in exhaustivity check,

compiler/test/dotty/tools/vulpix/ParallelTesting.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
168168
) extends TestSource {
169169
def sourceFiles: Array[JFile] = files.filter(isSourceFile)
170170

171-
override def toString() = outDir.toString
171+
override def toString() = sourceFiles match { case Array(f) => f.getPath case _ => outDir.getPath }
172172
}
173173

174174
/** A test source whose files will be compiled separately according to their

tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ object Test {
1818
def err2[A, B](value: Foo[A, B], a: A => Int): B = value match {
1919
case b: Bar[B] => // spurious // error
2020
b.x
21+
case _ => ??? // avoid fatal inexhaustivity warnings suppressing the uncheckable warning
2122
}
2223

2324
def fail[A, B](value: Foo[A, B], a: A => Int): B = value match {
2425
case b: Bar[Int] => // error
2526
b.x
27+
case _ => ??? // avoid fatal inexhaustivity warnings suppressing the uncheckable warning
2628
}
2729
}

tests/pos/i15289.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// scalac: -Werror
2+
sealed abstract class Foo[A, B]
3+
final case class Bar[C](baz: C) extends Foo[C, C]
4+
5+
class Test:
6+
def m1[X](f1: Foo[X, String]): String = f1 match { case Bar(_) => "" }

0 commit comments

Comments
 (0)