forked from scala/scala3
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix scala#21402: Always allow type member extraction for stable scrut…
…inees 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.
- Loading branch information
Showing
3 changed files
with
97 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Test that match types can extract path-dependent abstract types out of singleton types | ||
|
||
trait Base: | ||
type Value | ||
|
||
def getValue(): Value | ||
def setValue(v: Value): Unit | ||
end Base | ||
|
||
object Extractor: | ||
type Helper[X] = Base { type Value = X } | ||
|
||
type Extract[B <: Base] = B match | ||
case Helper[x] => x | ||
end Extractor | ||
|
||
object Test: | ||
import Extractor.Extract | ||
|
||
/* As is, this is a bit silly, since we could use `b.Value` instead. However, | ||
* in larger examples with more indirections, it is not always possible to | ||
* directly use the path-dependent version. See i21402 for a real-world use | ||
* case. | ||
*/ | ||
def foo(b: Base): Extract[b.type] = b.getValue() | ||
def bar(b: Base, v: Extract[b.type]): Unit = b.setValue(v) | ||
end Test |