Skip to content

Commit 0746196

Browse files
authored
Merge pull request #15851 from dwijnand/match-types-need-gadt-info
2 parents 9b00546 + 4236767 commit 0746196

File tree

6 files changed

+48
-17
lines changed

6 files changed

+48
-17
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
779779
&& (!caseLambda.exists || canWidenAbstract || tp1.widen.underlyingClassRef(refinementOK = true).exists)
780780
then
781781
isSubType(base, tp2, if (tp1.isRef(cls2)) approx else approx.addLow)
782+
&& { GADTused ||= MatchType.thatReducesUsingGadt(tp1); true }
782783
|| base.isInstanceOf[OrType] && fourthTry
783784
// if base is a disjunction, this might have come from a tp1 type that
784785
// expands to a match type. In this case, we should try to reduce the type

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4705,6 +4705,13 @@ object Types {
47054705
myReduced.nn
47064706
}
47074707

4708+
/** True if the reduction uses GADT constraints. */
4709+
def reducesUsingGadt(using Context): Boolean =
4710+
(reductionContext ne null) && reductionContext.keysIterator.exists {
4711+
case tp: TypeRef => reductionContext(tp).exists
4712+
case _ => false
4713+
}
4714+
47084715
override def computeHash(bs: Binders): Int = doHash(bs, scrutinee, bound :: cases)
47094716

47104717
override def eql(that: Type): Boolean = that match {
@@ -4719,6 +4726,21 @@ object Types {
47194726
object MatchType {
47204727
def apply(bound: Type, scrutinee: Type, cases: List[Type])(using Context): MatchType =
47214728
unique(new CachedMatchType(bound, scrutinee, cases))
4729+
4730+
def thatReducesUsingGadt(tp: Type)(using Context): Boolean = tp match
4731+
case MatchType.InDisguise(mt) => mt.reducesUsingGadt
4732+
case mt: MatchType => mt.reducesUsingGadt
4733+
case _ => false
4734+
4735+
/** Extractor for match types hidden behind an AppliedType/MatchAlias. */
4736+
object InDisguise:
4737+
def unapply(tp: AppliedType)(using Context): Option[MatchType] = tp match
4738+
case AppliedType(tycon: TypeRef, args) => tycon.info match
4739+
case MatchAlias(alias) => alias.applyIfParameterized(args) match
4740+
case mt: MatchType => Some(mt)
4741+
case _ => None
4742+
case _ => None
4743+
case _ => None
47224744
}
47234745

47244746
// ------ ClassInfo, Type Bounds --------------------------------------------------

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,21 +1571,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
15711571
val selType = rawSelectorTpe match
15721572
case c: ConstantType if tree.isInline => c
15731573
case otherTpe => otherTpe.widen
1574-
/** Extractor for match types hidden behind an AppliedType/MatchAlias */
1575-
object MatchTypeInDisguise {
1576-
def unapply(tp: AppliedType)(using Context): Option[MatchType] = tp match {
1577-
case AppliedType(tycon: TypeRef, args) =>
1578-
tycon.info match {
1579-
case MatchAlias(alias) =>
1580-
alias.applyIfParameterized(args) match {
1581-
case mt: MatchType => Some(mt)
1582-
case _ => None
1583-
}
1584-
case _ => None
1585-
}
1586-
case _ => None
1587-
}
1588-
}
15891574

15901575
/** Does `tree` has the same shape as the given match type?
15911576
* We only support typed patterns with empty guards, but
@@ -1618,7 +1603,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
16181603
}
16191604

16201605
val result = pt match {
1621-
case MatchTypeInDisguise(mt) if isMatchTypeShaped(mt) =>
1606+
case MatchType.InDisguise(mt) if isMatchTypeShaped(mt) =>
16221607
typedDependentMatchFinish(tree, sel1, selType, tree.cases, mt)
16231608
case _ =>
16241609
typedMatchFinish(tree, sel1, selType, tree.cases, pt)
@@ -3007,7 +2992,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
30072992
|| tree.isDef // ... unless tree is a definition
30082993
then
30092994
interpolateTypeVars(tree, pt, locked)
3010-
tree.overwriteType(tree.tpe.simplified)
2995+
val simplified = tree.tpe.simplified
2996+
if !MatchType.thatReducesUsingGadt(tree.tpe) then // needs a GADT cast. i15743
2997+
tree.overwriteType(simplified)
30112998
tree
30122999

30133000
protected def makeContextualFunction(tree: untpd.Tree, pt: Type)(using Context): Tree = {

tests/pos/i15743.gadt.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
abstract class C[Z] { def getZ: Z }
2+
final case class C1() extends C[Tuple] { def getZ = new Tuple1[String]("foo") }
3+
4+
// Like pos/i15743 but definitely requires the constraint on T to be stored as a GADT constraint
5+
// where in pos/i15743 it may have been reasonable to think that the constraint could be stored
6+
// in the regular type inference constraints
7+
class Alt:
8+
def test[T](e: C[T]) = e match
9+
case c1 @ C1() => // GADT constr: T := Tuple
10+
val t1: T = c1.getZ
11+
val t2: Int *: T = (1: Int) *: t1
12+
val i1: Int = (t2: Int *: T).head[Int *: T]

tests/pos/i15743.pass.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Like pos/i15743 but already passed, because the bounds are never lost so reduction never fails
2+
class Pass:
3+
def pass[T >: Tuple <: Tuple](t2: Int *: T) =
4+
val i1: Int = (t2: Int *: T).head[Int *: T]

tests/pos/i15743.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
case class Bar[T <: Tuple](val x: Int *: T)
2+
3+
class Test:
4+
def fail(e: Any): Int =
5+
e match { case b: Bar[t] => b.x.head[Int *: t] }

0 commit comments

Comments
 (0)