-
Notifications
You must be signed in to change notification settings - Fork 59
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
Properly deal with multiple conditional sources of conformances #1566
Comments
In fact, it's possible that the problem isn't as hard as I thought if we frame it in the context of universally quantified extensions. Let's think about what trait refinement means in Hylo. When we say that Consider the following to illustrate:
If we give an equivalent program to Swift, the compiler will complain that "conditional conformance of type 'A' to protocol 'Q' does not imply conformance to inherited protocol 'P'". I think the error makes sense iff
I'm under the impression that none of these requirements is actually justified. To understand, we first have to realize that a trait nothing more than an abstract type with a single generic parameter, named
When we're saying that a trait "refines" another trait, we're merely placing a bound on the
The failure that Swift reports is due to the fact that nothing proves Okay, but what's the difference between this program and one in which the conformance is not conditional?
Well, there is almost none. We still have to prove Now regarding the second requirement, let's assume we can write the following:
Here I'm asking for a conformance These observations give us two design choices:
|
The difference is that Swift treats the unconditional conformance of a protocol as a conformance to that protocol as well as all protocols the stated protocol refines. This is much easier to see when the protocols have requirements: protocol P {
func foo()
}
protocol Q: P {
associatedtype Bar
var bar: Bar { get }
}
struct Baz<T> { } An unconditional conformance to extension Baz: Q {
typealias Bar = Void
var bar: Bar { () }
}
This is because the unconditional extension is actually just sugar for: extension Baz: P, Q {
...
} Whereas the conditional conformance: extension Baz: Q where T: Hashable {
typealias Bar = Void
var bar: Bar { () }
} Is properly: extension Baz: Q where T: Hashable, Self: P {
typealias Bar = Void
var bar: Bar { () }
} The unconditional conformance includes a promise that |
A "source of conformance" is a declaration that introduces a conformance of a type
T
to a traitP
. Even with global coherence, a program may contain multiple sources of conformances introducingT : P
because of trait refinement:Because
Hashable
refinesEquatable
, the above program has two sources of conformances forA: Equatable
. Indeed, even if we remove the first conformance declaration, we'll still be able to concludeA: Equatable
from the last declaration.I naively thought we could ignore this issue but I didn't consider conditional conformances. For example:
Again, there are two sources of conformances for
B<Int>: Equatable
but one is conditional and so we can't use any source at random. We have to use the one introducingEquatable
specifically, or the one introducing its least specified super trait. Sadly that causes a problem because trait refinement doesn't form a tree so there could be ambiguous choices.There are two sources of conformances for
B<Int>: P
with different where clauses and there's no criterion deciding that one is a better choice than the other.AFAIU, Swift addresses this issue with two crucial restrictions. First, Swift says that a conditional conformance
T: P where C
does not imply a conformanceT: P0 where C
for any traitP0
refined byP
. Under this rule, the above example would be illegal becauseB: Q where T: Hashable
does not implyB: P
. Second, given two traitsP
,P0
such that the former refines the latter, Swift says that ifT : P where C
andT : P0 where C0
, thenC
must subsumeC0
.We most likely have to impose the same rules.
The text was updated successfully, but these errors were encountered: