diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 0c516a2ad6c4..c3f254a6b0b5 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -3354,23 +3354,33 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) { val info = denot.info match case TypeAlias(alias) => alias case info => info // Notably, RealTypeBounds, which will eventually give a MatchResult.NoInstances - if info.isInstanceOf[ClassInfo] then - /* The member is not an alias (we'll get Stuck instead of NoInstances, - * which is not ideal, but we cannot make a RealTypeBounds of ClassInfo). + val infoRefersToSkolem = stableScrut match + case stableScrut: SkolemType => + new TypeAccumulator[Boolean] { + def apply(prev: Boolean, tp: Type): Boolean = + prev || (tp eq stableScrut) || foldOver(prev, tp) + }.apply(false, info) + case _ => + false + if infoRefersToSkolem && info.isInstanceOf[ClassInfo] then + /* We would like to create a `RealTypeBounds(info, info)` to get a `MatchResult.NoInstances` + * but that is not allowed for `ClassInfo`. So instead we return `false`, which will result + * in a `MatchResult.Stuck` instead. */ false else - val infoRefersToSkolem = stableScrut match - case stableScrut: SkolemType => - new TypeAccumulator[Boolean] { - def apply(prev: Boolean, tp: Type): Boolean = - prev || (tp eq stableScrut) || foldOver(prev, tp) - }.apply(false, info) + val info1 = info match + case ClassInfo(prefix, cls, _, _, _) => + // Re-select the class from the prefix + prefix.select(cls) + case info: TypeBounds => + // Will already trigger a MatchResult.NoInstances + info + case _ if infoRefersToSkolem => + // Explicitly trigger a MatchResult.NoInstances + RealTypeBounds(info, info) case _ => - false - val info1 = - if infoRefersToSkolem && !info.isInstanceOf[TypeBounds] then RealTypeBounds(info, info) // to trigger a MatchResult.NoInstances - else info + info rec(capture, info1, variance = 0, scrutIsWidenedAbstract) case _ => false diff --git a/tests/neg/match-type-enumeration-value-hack.check b/tests/neg/match-type-enumeration-value-hack.check new file mode 100644 index 000000000000..0847ae5ba18f --- /dev/null +++ b/tests/neg/match-type-enumeration-value-hack.check @@ -0,0 +1,11 @@ +-- [E172] Type Error: tests/neg/match-type-enumeration-value-hack.scala:11:40 ------------------------------------------ +11 | summon[Suit#Value =:= EnumValue[Suit]] // error + | ^ + | Cannot prove that Suit#Value =:= EnumValue[Suit]. + | + | Note: a match type could not be fully reduced: + | + | trying to reduce EnumValue[Suit] + | failed since selector Suit + | does not match case EnumValueAux[t] => t + | and cannot be shown to be disjoint from it either. diff --git a/tests/neg/match-type-enumeration-value-hack.scala b/tests/neg/match-type-enumeration-value-hack.scala new file mode 100644 index 000000000000..4c6176b9b637 --- /dev/null +++ b/tests/neg/match-type-enumeration-value-hack.scala @@ -0,0 +1,12 @@ +type EnumValueAux[A] = ({ type Value }) { type Value = A } + +type EnumValue[E <: Enumeration] = E match + case EnumValueAux[t] => t + +// A class extending Enumeration does not yet define a concrete enumeration +class Suit extends Enumeration: + val Hearts, Diamonds, Clubs, Spades = Val() + +object Test: + summon[Suit#Value =:= EnumValue[Suit]] // error +end Test diff --git a/tests/pos/match-type-enumeration-value-hack.scala b/tests/pos/match-type-enumeration-value-hack.scala new file mode 100644 index 000000000000..b1f0146c012d --- /dev/null +++ b/tests/pos/match-type-enumeration-value-hack.scala @@ -0,0 +1,11 @@ +type EnumValueAux[A] = ({ type Value }) { type Value = A } + +type EnumValue[E <: Enumeration] = E match + case EnumValueAux[t] => t + +object Suit extends Enumeration: + val Hearts, Diamonds, Clubs, Spades = Val() + +object Test: + summon[Suit.Value =:= EnumValue[Suit.type]] +end Test