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

[SE-0438] Homogenize KeyPath and update diagnostic. #2481

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
28 changes: 14 additions & 14 deletions proposals/0438-metatype-keypath.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Metatype Keypaths
# Metatype KeyPaths

* Proposal: [SE-0438](0438-metatype-keypath.md)
* Authors: [Amritpan Kaur](https://github.com/amritpan), [Pavel Yaskevich](https://github.com/xedin)
Expand All @@ -9,15 +9,15 @@

## Introduction

Key path expressions access properties dynamically. They are declared with a concrete root type and one or more key path components that define a path to a resulting value via the type’s properties, subscripts, optional-chaining expressions, forced unwrapped expressions, or self. This proposal expands key path expression access to include static properties of a type, i.e., metatype keypaths.
KeyPath expressions access properties dynamically. They are declared with a concrete root type and one or more KeyPath components that define a path to a resulting value via the type’s properties, subscripts, optional-chaining expressions, forced unwrapped expressions, or self. This proposal expands KeyPath expression access to include static properties of a type, i.e., metatype KeyPaths.

## Motivation

Metatype keypaths were briefly explored in the pitch for [SE-0254](https://forums.swift.org/t/pitch-static-and-class-subscripts/21850) and the [proposal](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0254-static-subscripts.md#metatype-key-paths) later recommended them as a future direction. Allowing key path expressions to directly refer to static properties has also been discussed on the Swift Forums for database lookups when used [in conjunction with @dynamicMemberLookup](https://forums.swift.org/t/dynamic-key-path-member-lookup-cannot-refer-to-static-member/30212) and as a way to avoid verbose hacks like [referring to a static property through another computed property](https://forums.swift.org/t/key-path-cannot-refer-to-static-member/28055). Supporting metatype keypaths in the Swift language will address these challenges and improve language semantics.

## Proposed solution

We propose to allow keypath expressions to define a reference to static properties. The following usage, which currently generates a compiler error, will be allowed as valid Swift code.
We propose to allow KeyPath expressions to define a reference to static properties. The following usage, which currently generates a compiler error, will be allowed as valid Swift code.

```swift
struct Bee {
Expand All @@ -31,24 +31,24 @@ let kp = \Bee.Type.name

### Metatype syntax

Keypath expressions where the first component refers to a static property will include `.Type` on their root types stated in the key path contextual type or in the key path literal. For example:
KeyPath expressions where the first component refers to a static property will include `.Type` on their root types stated in the KeyPath contextual type or in the KeyPath literal. For example:

```swift
struct Bee {
static let name = "honeybee"
}

let kpWithContextualType: KeyPath<Bee.Type, String> = \.name // key path contextual root type of Bee.Type
let kpWithLiteral = \Bee.Type.name // key path literal \Bee.Type
let kpWithContextualType: KeyPath<Bee.Type, String> = \.name // KeyPath contextual root type of Bee.Type
let kpWithLiteral = \Bee.Type.name // KeyPath literal \Bee.Type
```

Attempting to write the above metatype keypath without including `.Type will trigger an error diagnostic:
Attempting to write the above metatype KeyPath without including `.Type` will trigger an error diagnostic with a fix-it recommending the addition of `Type` after the root type `Bee`:

```swift
let kpWithLiteral = \Bee.name // error: static member 'name' cannot be used on instance of type 'Bee'
```

Keypath expressions where the component referencing a static property is not the first component do not require `.Type`:
KeyPath expressions where the component referencing a static property is not the first component do not require `.Type`:
```swift
struct Species {
static let isNative = true
Expand All @@ -62,7 +62,7 @@ let kpSecondComponentIsStatic = \Wasp.species.isNative
```
### Access semantics

Immutable static properties will form the read-only keypaths just like immutable instance properties.
Immutable static properties will form the read-only KeyPaths just like immutable instance properties.
```swift
struct Tip {
static let isIncluded = True
Expand All @@ -72,7 +72,7 @@ struct Tip {
let kpStaticImmutable: KeyPath<Tip.Type, Bool> = \.isIncluded
let kpInstanceImmutable: KeyPath<Tip, Bool> = \.isVoluntary
```
However, unlike instance members, keypaths to mutable static properties will always conform to `ReferenceWritableKeyPath` because metatypes are reference types.
However, unlike instance members, KeyPaths to mutable static properties will always conform to `ReferenceWritableKeyPath` because metatypes are reference types.
```swift
struct Tip {
static var total = 0
Expand All @@ -84,7 +84,7 @@ let kpInstanceMutable: WriteableKeyPath<Tip, Int> = \.flatRate
```
## Effect on source compatibility

This feature breaks source compatibility for key path expressions that reference static properties after subscript overloads. For example, the compiler cannot differentiate between subscript keypath components by return type in the following:
This feature breaks source compatibility for KeyPath expressions that reference static properties after subscript overloads. For example, the compiler cannot differentiate between subscript KeyPath components by return type in the following:

```swift
struct S {
Expand All @@ -99,7 +99,7 @@ struct Test {
let kpViaSubscript = \Test.[42] // fails to typecheck
```

This keypath does not specify a contextual type, without which the key path value type is unknown. To form a keypath to the metatype subscript and return an `Int`, we can specify a contextual type with a value type of `S.Type` and chain the metatype keypath:
This KeyPath does not specify a contextual type, without which the KeyPath value type is unknown. To form a KeyPath to the metatype subscript and return an `Int`, we can specify a contextual type with a value type of `S.Type` and chain the metatype KeyPath:

```swift
let kpViaSubscript: KeyPath<Test, S.Type> = \Test.[42]
Expand All @@ -114,9 +114,9 @@ This feature does not affect ABI compatibility and has no implications on adopti

### Key Paths to Enum cases

Adding language support for read-only key paths to enum cases has been widely discussed on the [Swift Forums](https://forums.swift.org/t/enum-case-key-paths-an-update/68436) but has been left out of this proposal as this merits a separate discussion around [syntax design and implementation concerns](https://forums.swift.org/t/enum-case-keypaths/60899/32).
Adding language support for read-only KeyPaths to enum cases has been widely discussed on the [Swift Forums](https://forums.swift.org/t/enum-case-key-paths-an-update/68436) but has been left out of this proposal as this merits a separate discussion around [syntax design and implementation concerns](https://forums.swift.org/t/enum-case-keypaths/60899/32).

Since references to enum cases must be metatypes, extending keypath expressions to include references to metatypes will hopefully bring the Swift language closer to adopting keypaths to enum cases in a future pitch.
Since references to enum cases must be metatypes, extending KeyPath expressions to include references to metatypes will hopefully bring the Swift language closer to adopting KeyPaths to enum cases in a future pitch.

## Acknowledgments

Expand Down