Skip to content

Commit dc95aea

Browse files
committed
Fix #21402: Always allow type member extraction for stable scrutinees in match types.
Previously, through the various code paths, we basically allowed type member extraction for stable scrutinees if the type member was an alias or a class member. In the alias case, we took the alias, whereas in the class case, we recreated a selection on the stable scrutinee. We did not allow that on abstract type members. We now uniformly do it for all kinds of type members. If the scrutinee is a (non-skolem) stable type, we do not even look at the info of the type member. We directly create a selection to it, which corresponds to what we did before for class members. We only try to dealias type members if the scrutinee type is not a stable type.
1 parent a672e05 commit dc95aea

File tree

2 files changed

+69
-11
lines changed

2 files changed

+69
-11
lines changed

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

+28-11
Original file line numberDiff line numberDiff line change
@@ -3681,19 +3681,36 @@ class MatchReducer(initctx: Context) extends TypeComparer(initctx) {
36813681

36823682
stableScrut.member(typeMemberName) match
36833683
case denot: SingleDenotation if denot.exists =>
3684-
val info = denot.info match
3685-
case alias: AliasingBounds => alias.alias // Extract the alias
3686-
case ClassInfo(prefix, cls, _, _, _) => prefix.select(cls) // Re-select the class from the prefix
3687-
case info => info // Notably, RealTypeBounds, which will eventually give a MatchResult.NoInstances
3688-
val info1 = stableScrut match
3684+
val info = stableScrut match
36893685
case skolem: SkolemType =>
3690-
dropSkolem(info, skolem).orElse:
3691-
info match
3692-
case info: TypeBounds => info // Will already trigger a MatchResult.NoInstances
3693-
case _ => RealTypeBounds(info, info) // Explicitly trigger a MatchResult.NoInstances
3694-
case _ => info
3695-
rec(capture, info1, variance = 0, scrutIsWidenedAbstract)
3686+
/* If it is a skolem type, we cannot have class selections nor
3687+
* abstract type selections. If it is an alias, we try to remove
3688+
* any reference to the skolem from the right-hand-side. If that
3689+
* succeeds, we take the result, otherwise we fail as not-specific.
3690+
*/
3691+
def adaptToTriggerNotSpecific(info: Type): Type = info match
3692+
case info: TypeBounds => info
3693+
case _ => RealTypeBounds(info, info)
3694+
3695+
denot.info match
3696+
case denotInfo: AliasingBounds =>
3697+
val alias = denotInfo.alias
3698+
dropSkolem(alias, skolem).orElse(adaptToTriggerNotSpecific(alias))
3699+
case ClassInfo(prefix, cls, _, _, _) =>
3700+
// for clean error messages
3701+
adaptToTriggerNotSpecific(prefix.select(cls))
3702+
case denotInfo =>
3703+
adaptToTriggerNotSpecific(denotInfo)
3704+
3705+
case _ =>
3706+
// The scrutinee type is truly stable. We select the type member directly on it.
3707+
stableScrut.select(typeMemberName)
3708+
end info
3709+
3710+
rec(capture, info, variance = 0, scrutIsWidenedAbstract)
3711+
36963712
case _ =>
3713+
// The type member was not found; no match
36973714
false
36983715
end rec
36993716

tests/pos/i21402.scala

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
abstract class AbstractServiceKey:
2+
type Protocol
3+
4+
abstract class ServiceKey[T] extends AbstractServiceKey:
5+
type Protocol = T
6+
7+
type Aux[P] = AbstractServiceKey { type Protocol = P }
8+
type Service[K <: Aux[?]] = K match
9+
case Aux[t] => ActorRef[t]
10+
type Subscriber[K <: Aux[?]] = K match
11+
case Aux[t] => ActorRef[ReceptionistMessages.Listing[t]]
12+
13+
trait ActorRef[-T]
14+
15+
object ReceptionistMessages:
16+
final case class Listing[T](key: ServiceKey[T])
17+
18+
class TypedMultiMap[T <: AnyRef, K[_ <: T]]:
19+
def get(key: T): Set[K[key.type]] = ???
20+
transparent inline def getInlined(key: T): Set[K[key.type]] = ???
21+
inline def inserted(key: T, value: K[key.type]): TypedMultiMap[T, K] = ???
22+
23+
object LocalReceptionist {
24+
final case class State(
25+
services: TypedMultiMap[AbstractServiceKey, Service],
26+
subscriptions: TypedMultiMap[AbstractServiceKey, Subscriber]
27+
):
28+
def testInsert(key: AbstractServiceKey)(serviceInstance: ActorRef[key.Protocol]): State = {
29+
val fails = services.inserted(key, serviceInstance) // error
30+
???
31+
}
32+
33+
def testGet[T](key: AbstractServiceKey): Unit = {
34+
val newState: State = ???
35+
val fails: Set[ActorRef[key.Protocol]] = newState.services.get(key) // error
36+
val works: Set[ActorRef[key.Protocol]] = newState.services.getInlined(key) // workaround
37+
38+
val fails2: Set[ActorRef[ReceptionistMessages.Listing[key.Protocol]]] = newState.subscriptions.get(key) // error
39+
val works2: Set[ActorRef[ReceptionistMessages.Listing[key.Protocol]]] = newState.subscriptions.getInlined(key) // workaround
40+
}
41+
}

0 commit comments

Comments
 (0)