Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #21402: Always allow type member extraction for stable scrutinees in match types. #21700

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 29 additions & 11 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3681,19 +3681,37 @@ class MatchReducer(initctx: Context) extends TypeComparer(initctx) {

stableScrut.member(typeMemberName) match
case denot: SingleDenotation if denot.exists =>
val info = denot.info match
case alias: AliasingBounds => alias.alias // Extract the alias
case ClassInfo(prefix, cls, _, _, _) => prefix.select(cls) // Re-select the class from the prefix
case info => info // Notably, RealTypeBounds, which will eventually give a MatchResult.NoInstances
val info1 = stableScrut match
val info = stableScrut match
case skolem: SkolemType =>
dropSkolem(info, skolem).orElse:
info match
case info: TypeBounds => info // Will already trigger a MatchResult.NoInstances
case _ => RealTypeBounds(info, info) // Explicitly trigger a MatchResult.NoInstances
case _ => info
rec(capture, info1, variance = 0, scrutIsWidenedAbstract)
/* If it is a skolem type, we cannot have class selections nor
* abstract type selections. If it is an alias, we try to remove
* any reference to the skolem from the right-hand-side. If that
* succeeds, we take the result, otherwise we fail as not-specific.
*/

def adaptToTriggerNotSpecific(info: Type): Type = info match
case info: TypeBounds => info
case _ => RealTypeBounds(info, info)

denot.info match
case denotInfo: AliasingBounds =>
val alias = denotInfo.alias
dropSkolem(alias, skolem).orElse(adaptToTriggerNotSpecific(alias))
case ClassInfo(prefix, cls, _, _, _) =>
// for clean error messages
adaptToTriggerNotSpecific(prefix.select(cls))
case denotInfo =>
adaptToTriggerNotSpecific(denotInfo)

case _ =>
// The scrutinee type is truly stable. We select the type member directly on it.
stableScrut.select(typeMemberName)
end info

rec(capture, info, variance = 0, scrutIsWidenedAbstract)

case _ =>
// The type member was not found; no match
false
end rec

Expand Down
41 changes: 41 additions & 0 deletions tests/pos/i21402.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
abstract class AbstractServiceKey:
type Protocol

abstract class ServiceKey[T] extends AbstractServiceKey:
type Protocol = T

type Aux[P] = AbstractServiceKey { type Protocol = P }
type Service[K <: Aux[?]] = K match
case Aux[t] => ActorRef[t]
type Subscriber[K <: Aux[?]] = K match
case Aux[t] => ActorRef[ReceptionistMessages.Listing[t]]

trait ActorRef[-T]

object ReceptionistMessages:
final case class Listing[T](key: ServiceKey[T])

class TypedMultiMap[T <: AnyRef, K[_ <: T]]:
def get(key: T): Set[K[key.type]] = ???
transparent inline def getInlined(key: T): Set[K[key.type]] = ???
inline def inserted(key: T, value: K[key.type]): TypedMultiMap[T, K] = ???

object LocalReceptionist {
final case class State(
services: TypedMultiMap[AbstractServiceKey, Service],
subscriptions: TypedMultiMap[AbstractServiceKey, Subscriber]
):
def testInsert(key: AbstractServiceKey)(serviceInstance: ActorRef[key.Protocol]): State = {
val fails = services.inserted(key, serviceInstance) // error
???
}

def testGet[T](key: AbstractServiceKey): Unit = {
val newState: State = ???
val fails: Set[ActorRef[key.Protocol]] = newState.services.get(key) // error
val works: Set[ActorRef[key.Protocol]] = newState.services.getInlined(key) // workaround

val fails2: Set[ActorRef[ReceptionistMessages.Listing[key.Protocol]]] = newState.subscriptions.get(key) // error
val works2: Set[ActorRef[ReceptionistMessages.Listing[key.Protocol]]] = newState.subscriptions.getInlined(key) // workaround
}
}