-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[SE-0280] Enum cases as protocol witnesses #28916
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
Conversation
ping @slavapestov |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm OK with this as long as the core team feels it doesn't need their approval.
Hmm. We might. I'll raise it to the Core Team. |
@rjmccall Any update on this from the core team? |
No, sorry. It slipped through the cracks during the last meeting, but I made sure it went back on the agenda for tomorrow. |
Thank you! |
Okay. Three points from the Core Team. The first is that we're generally on board with this change: it makes sense, and we think it'd be good for the language. The second is that this change does need an evolution proposal, and that proposal will feel pretty incomplete if it doesn't also cover cases with payloads. The third is that requirement-matching is an area of the language where we really need to set out (and get some community agreement on) the basic design principles. That probably means pitching and defending a sort of mini-manifesto, out of which a number of other proposals would arise. I don't know if you're willing to do all that, but if you are, it probably makes sense to have that conversation before we raise this specifically. |
Let me give you an idea of what we're thinking about the "mini-manifesto". The basic question here is: what kinds of differences in form and type are reasonable between a protocol requirement and the declaration that "witnesses" it? There's a sort of rough continuum here:
A mini-manifesto here would go into these points, propose a basic model, and so on. My sense is that most of the Core Team would currently favor something that allowed subtyping and defaulting but probably nothing further, and we'd accept your change specifically as a sort of desugaring. But I don't want to lead you too much. |
@rjmccall Okay, sounds good! I did manage to get it to work with cases with payloads (I'll update this PR) while I was waiting for a response so I think the proposal + implementation part is covered and I can pitch it on the forums. In terms of the "mini-manifesto", I am happy to write one down based on the points you've provided and pitch on the forums. Does this "mini-manifesto" need to be "approved" by the core team before the proposal can be considered for review? |
That's awesome and great to hear.
We'll need to talk about what process we want to use for this, but you can go ahead and start the conversation in the meantime, I think. Thanks for taking this on! |
Hmm, I wanted to build a toolchain, but it seems like there's some sort of linking issue because when the witness is a payload case and (1) I build with
I don't get this with |
@theblixguy I believe we only generate the case constructor symbols by need as shared-linkage symbols so that they don't have to be ABI. You might need to trigger the case constructor's generation when it's used as a witness. |
@jckarter Thank you for the tip! I inserted a call to |
Enum case constructors should be emitted as needed already by getGlobalFunctionRef(). You can reference a case constructor across modules already, via currying syntax. Can you see why it's not working when referenced from the witness thunk code path? |
Eg, if I write
It emits two thunks:
This is the thunk emitted by emitEnumConstructor(). It has shared linkage and a body. Then there's the curry thunk which gives it the AST type
|
@slavapestov Yeah, I think I have now found the actual problem! So, it's my fault, I made a mistake in |
@swift-ci please build toolchain |
1 similar comment
@swift-ci please build toolchain |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Glad you sorted out the problem with emitting enum constructors. The change looks good to me now.
addSymbolIfNecessary(reqtAccessor, witnessAccessor); | ||
}); | ||
} else if (isa<EnumElementDecl>(witnessDecl)) { | ||
addSymbolIfNecessary(valueReq, witnessDecl); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In practice, enum constructors never have public linkage so they don't need to be visited by TBDGen. However in case this ever changes, there's one less spot to update...
…t-only property with Self type protocol requirement
Also properly handle the matching in some cases
…e is used as a witness Also, tweak SILDeclRef::getLinkage to update the 'limit' to 'OnDemand' if we have an enum declaration
Also remove a FIXME and code added to workaround the original bug
@swift-ci please test |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@swift-ci please test |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@swift-ci please test |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@swift-ci please test |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@swift-ci please test source compatibility |
Hi all. I have found some issues! import Foundation
public protocol Foo {
static var foo: Self { get }
}
enum Bar: Foo {
case foo
}
func load<F: Foo>(_ foo: F) {
print(foo) // Printed: "foo"
}
load(Bar.foo) // Printed: "foo"
enum Bar2 {
case foo
case bar(Int)
}
func load2(_ bar2: Bar2) {
switch bar2 {
case .foo: break
case let .bar(val): print(val) // Printed: "5"
}
}
load2(.bar(5)) // Printed: "5"
protocol Foo3: Equatable {
static var foo: Self { get }
static func bar(_ value: Int) -> Self
}
enum Bar3: Foo3 {
case foo
case bar(Int)
}
func load3<F3: Foo3>(_ foo3: F3) {
switch foo3 {
case .foo: break
case let .bar(val): print(val); #error("Pattern variable binding cannot appear in an expression")
default: break
}
}
load3(Bar3.bar(5)) // Compiler failure We have the only comparable ability like this: func load3<F3: Foo3>(_ foo3: F3) {
switch foo3 {
case .foo: break
case .bar(5): print("Compared"); // Printed: "Compared"
default: break
}
}
load3(Bar3.bar(5)) // Printed: "Compared" |
Hi @damienissa, That behavior is correct and also unrelated to this proposal feature. (Incidentally, even if it were both incorrect and related to this proposal, the forums and/or bug tracker would be the place to discuss it.) It is unrelated to the proposal feature because the proposal feature is about enum cases serving as witnesses for protocol requirements; this does not mean that static protocol requirements are equivalent to enum cases. It is correct behavior because the protocol does not guarantee any way of binding the argument passed to |
@xwu Thanks! |
Allow enum cases to witness static protocol requirements. For example:
Swift-evolution thread: https://forums.swift.org/t/pitch-enum-cases-as-protocol-witnesses/32753
Resolves SR-3170, SR-3510, SR-9099