SE-0438: Metatype keypaths

Hi everyone. The review of SE-0438: Metatype keypaths begins now and runs through June 13th, 2024.

Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to me as the review manager, either by DM or email. When contacting the review manager directly, please put "SE-0438" in the subject line.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  • What is your evaluation of the proposal?
  • Is the problem being addressed significant enough to warrant a change to Swift?
  • Does this proposal fit well with the feel and direction of Swift?
  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at

swift-evolution/process.md at main · apple/swift-evolution · GitHub

Thank you for helping to evolve Swift!

Joe Groff
Review Manager

39 Likes

We'd love to adopt this in swift-testing to enhance some of our features such as the @Tag macro.

If I misspecify \T.staticProperty instead of \T.Type.staticProperty, it seems to me that a Fix-It is trivially applicable, so I'd like to see one added to the emitted diagnostic. (If one's already there, yay!)

8 Likes

+1 finally.gif

I'm very excited to see this come, as it fills one of the gaping holes in keypath APIs. I can think of a half-dozen immediate uses I'll have for it.

5 Likes

Keypath expressions where the component referencing a static property is not the first component do not require .Type

let kpSecondComponentIsStatic = \Wasp.species.isNative

The lack of symmetry here seems less intuitive to me, is this behavior intuitive for experienced Swift programmers?

PS: I'm still learning Swift, so this comment comes from a place of curiosity and a desire to understand. Apologies if this question seems simplistic or obvious.

Edit: On second thoughts, this does seem natural considering Wasp().species.isNative is how one would normally (without keypaths) access this. I should probably delete this comment, but I will leave this for posterity. :upside_down_face:

3 Likes

May be this is off topic
I have noticed that the spelling or phrasing of KeyPath varies in swift-evolution. Should these variations be standardized?

It has so many variants: KeyPath(s), Key Path, key path(s), key-path, Keypath, Key path.

SE-0161 Smart KeyPaths: Better Key-Value Coding for Swift
The title uses the spelling KeyPaths, the proposal content includes Key Path.

SE-0062 Referencing Objective-C key-paths
The title uses key-paths, while the proposal content includes key paths and key-paths.

This proposal also includes the variant Keypath.

I agree, what’s the motivation behind this? And wouldn’t it ambiguous if there was also a isNative instance variable in the struct? How would we differentiate a keypath to the static variable from a keypath to the instance variable?

2 Likes

I saw this part in the proposal:

ABI compatibility / Implications on adoption

This feature does not affect ABI compatibility and has no implications on adoption.

However looking at the implementation PR, I see that we now emit more public symbols when we compile a binary, namely property descriptors for static properties.

Does this mean that I can only form a static keypath to a property declared within a module that was built with a sufficiently new compiler? Is the failure mode a linker error? We don't have a way to statically check this kind of availability, but perhaps it should be documented in the proposal.

3 Likes

Does this mean that I can only form a static keypath to a property declared within a module that was built with a sufficiently new compiler?

Yeah, it turns out this is how weak linking works, there has to be a symbol during build... We have a test-case in the PR that builds module with a new compiler first and then with an old version that doesn't support these descriptors and then runs clients with the old version of the library.

I agree that it should be mentioned, we had that in the draft but then it got lost...

2 Likes

I'lm happy that this hole
Is finally completed. I'm still morally shocked that incomplete features can ship. Of course it's good that partial features ship, and that they are eventually completed. Yet I can't be satisfied with the spirit than accepts such unfisnished job to lay for so long. Thank you, but not 100%.

1 Like

Could we perhaps use availability annotations in some way? Because unless a user is tinkering with the linker themselves, I think encountering an error at link time is quite surprising.

That is something we considered but couldn’t find an appropriate way to express because, as Slava mentioned, we don’t know whether the symbol is going to be there or not until link time. I think to do something like that we’d need this feature to be attached to some future language mode (cannot use Swift 6) that we could use to determine whether a swiftmodule is new enough to support property descriptors for static properties. This would limit adoption but might be more user-friendly.

2 Likes

The version of the compiler that built the module is embedded in each of the representations of the module. Could we check whether the module was built with a compiler that has version 6.0 or greater, and then diagnose the use of these keypaths if the module come from doesn't support them?

2 Likes

That is not sufficient because not all of the modules produced by swift 6 compiler would have this available.

1 Like

That is not sufficient because not all of the modules produced by swift 6 compiler would have this available.

Can you elaborate on that? What are the conditions under which a Swift 6 compiler won't produce these symbols?

2 Likes

The changes are not in yet on main and they won’t be merged into 6.0 even if this is accepted.

1 Like

Could this change include an increment to SWIFTMODULE_VERSION_MINOR and the logic could be based around that? There's no expectation that modules from 6.0 would be compatible with 6.x where x > 0 anyway.

Edit: On second thought, I might have misunderstood the problem, since we wouldn't be compiling 6.0 modules together with 6.x modules anyway. Does that mean the information would need to be in the object files, not the modules?

I think we might be better off limiting it to next swift language mode as I suggested in my previous post, I am not sure whether it’s a good idea to conditionalize it on such implementation details, was there a precedent for that?

1 Like

So, I'm trying to make sure I fully understand the conditions where this is an issue. At first I thought it was the case above, but I guess if you're building everything from source, it's not going to matter because you're either using a compiler that doesn't have this feature, or you're using a newer one that does, for the entire build.

But the case that's problematic is if you have a binary framework + .swiftinterface that was built with an older compiler, and you're consuming it with a newer compiler and trying to use a static keypath into the older framework, right? In that case, since the compiler still has to compile the .swiftinterface file, can we either:

  1. Use information written in the .swiftinterface file to determine whether the compiler that produced it was new enough
  2. Emit the static keypath symbols directly into the client based on what's present in the .swiftinterface file?
2 Likes

The changes are not in yet on main and they won’t be merged into 6.0 even if this is accepted.

We can gate it on whichever version of the compiler actually implements the feature. To me, the suggestion is just meant to be a minor enhancement to the development experience. Instead of a link time failure, you can get a clear diagnostic at compile time if it's not going to work. Limiting the feature to a future language version also works, but it seems unnecessarily strict to me. Both approaches are roughly the same technically speaking; the only difference is whether it is the language mode or the compiler version that is read from the module to be used in the determination of whether the capability is present. Language version here is just a course-grained proxy for version of the compiler in for the purposes of this diagnostic.

1 Like