From 69264584f50858571bcd2b8fa9134bc8b60f02bb Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Sun, 7 Jun 2020 21:44:37 +0200 Subject: [PATCH 1/9] Retroactive protocol refinement --- .../NNNN-retroactive-protocol-refinement.md | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 proposals/NNNN-retroactive-protocol-refinement.md diff --git a/proposals/NNNN-retroactive-protocol-refinement.md b/proposals/NNNN-retroactive-protocol-refinement.md new file mode 100644 index 0000000000..b1a5d6fb4e --- /dev/null +++ b/proposals/NNNN-retroactive-protocol-refinement.md @@ -0,0 +1,100 @@ +# Feature name + +* Proposal: [SE-NNNN](0000-retroactive-protocol-refinement.md) +* Authors: [John Holdsworth](https://github.com/johnno1962) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [apple/swift#32228](https://github.com/apple/swift/pull/32228) +* Decision Notes: TBD +* Bugs: [SR-11013](https://bugs.swift.org/browse/SR-11013) + +## Introduction + +Protocol extensions are a powerful feature of the Swift language but it is not currently possible to specify that an extension provides a conformance to a particular protocol. This is mentioned as "Retroactive protocol refinement" in the [Generics Manifesto](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#retroactive-protocol-refinement). The PR mentioned above explores as a proof of concept how difficult it would be to implement this feature and whether it could perhaps be considered as an additive change to the Swift Language. + +Swift-evolution thread: [Protocol extensions inheriting protocols](https://forums.swift.org/t/protocol-extensions-inheriting-protocols/25491/8) + +## Motivation + +Extensions to classes or structs (nominals) are able to specify conformances that they implement. When one extends a protocol however, it seems reasonable that specifying a conformance would add that functionality to that an entire class of nominals i.e. those that already adopt the protocol being extended or protocols that adopt that protocol etc. One example that was discussed was something that should be a simple case, where one wants to make all fixed width integers expressible by a UnicodeScalarLiteral (i.e. Strings). On the face of it one might assume the following would be possible: + +```Swift +extension FixedWidthInteger: ExpressibleByUnicodeScalarLiteral { + @_transparent + public init(unicodeScalarLiteral value: Unicode.Scalar) { + self = Self(value.value) + } +} +``` +In practical terms, for the Swift compiler, this effectively means creating ad-hoc extensions on Int8, Int16 and all types which conform to FixedWidthInteger along with their associated witnesses. It's more powerful however as an idea and in more abstract terms (from the Generics Manifesto): + +``` +protocol P { + func foo() +} + +protocol Q { + func bar() +} + +extension Q : P { // Make every type that conforms to Q also conforms to P + func foo() { // Implement `P.foo` requirement in terms of `Q.bar` + bar() + } +} + +func f(t: T) { ... } + +struct X : Q { + func bar() { ... } +} + +f(X()) // okay: X conforms to P through the conformance of Q to P +``` +``` +This is an extremely powerful feature: it allows one +to map the abstractions of one domain into another +domain (e.g., every Matrix is a Graph). +``` +It continues though: + +``` +However, similar to private conformances, it puts a +major burden on the dynamic-casting runtime to chase +down arbitrarily long and potentially cyclic chains +of conformances, which makes efficient implementation +nearly impossible. +``` +The PR above strives to demonstrate that it may be possible to avoid these pitfalls and refine protocols retroactively at compile time, providing summary conformance data to the existing runtime for an efficient implementation of dynamic casts. + +## Proposed solution + +Allow conformances to be added to protocol extensions with all that implies in terms of all nominals conforming to the protocol being extended acquiring the added conformances. It would be useful if this also worked for synthesised conformances (and indeed it does). + +## Detailed design + +The PR above makes changes in three principal areas internal to the compiler. + +1) The ConformanceLookupTable — a cache of the protocols a nominal conforms to has been adapted to traverse the inherited conformances of protocol extensions. + +2) The "Generic Signature" builder and module deserialising code has been adapted to traverse these extended sources of conformances. + +3) While the above takes place, a registry of extended conformances is kept in the compiler. This is used to emit the ad-hoc witness tables used by extended conformances in a manner that is compatible across modules. + +In terms of what is surfaced to the language, extending conformances of a protocol is fully functional including across modules. A limitation of the current implementation however is that you cannot extend a protocol that a nominal already conforms to in another module though it might be possible remove this limitation at a later date. + +## Source compatibility + +This is an additive feature that has a syntax which while it follows of the norms of extensions in the Swift language is currently not permitted. Therefore, there is no effect on source compatibility. + +## Effect on ABI stability + +This is a compiler level addition to the language that does not require any changes to the Swift runtime in order to work so it has no effect on ABI stability. + +## Effect on API resilience + +This proposal does not make additions to the public API but is rather is a change to the capabilities of the compiler. + +## Alternatives considered + +There is a more ambitious and well advanced proposal: "[parameterised extensions](https://github.com/apple/swift/pull/25263)" of which it has been said this is a subset. This proposal has more modest goals which are hopefully more accessible to the average programmer and requires only targeted surgical changes to the compiler. \ No newline at end of file From 30fbe6a2464056d146006352c78bf9c08daf6755 Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Wed, 10 Jun 2020 08:25:08 +0200 Subject: [PATCH 2/9] Limitation removed in in prototype implementation. --- proposals/NNNN-retroactive-protocol-refinement.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-retroactive-protocol-refinement.md b/proposals/NNNN-retroactive-protocol-refinement.md index b1a5d6fb4e..84716ee398 100644 --- a/proposals/NNNN-retroactive-protocol-refinement.md +++ b/proposals/NNNN-retroactive-protocol-refinement.md @@ -81,11 +81,11 @@ The PR above makes changes in three principal areas internal to the compiler. 3) While the above takes place, a registry of extended conformances is kept in the compiler. This is used to emit the ad-hoc witness tables used by extended conformances in a manner that is compatible across modules. -In terms of what is surfaced to the language, extending conformances of a protocol is fully functional including across modules. A limitation of the current implementation however is that you cannot extend a protocol that a nominal already conforms to in another module though it might be possible remove this limitation at a later date. +In terms of what is surfaced to the language, extending conformances of a protocol is fully functional including across modules. ## Source compatibility -This is an additive feature that has a syntax which while it follows of the norms of extensions in the Swift language is currently not permitted. Therefore, there is no effect on source compatibility. +This is an additive feature that has a syntax which while it follows of the norms of extensions in the Swift language it is currently not permitted. Therefore, there is no effect on source compatibility. ## Effect on ABI stability From c3b1bffddfed8ce8271b741ac2b89381ef919d77 Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Sat, 27 Jun 2020 10:15:45 +0200 Subject: [PATCH 3/9] Update proposals/NNNN-retroactive-protocol-refinement.md Co-authored-by: Yogi --- proposals/NNNN-retroactive-protocol-refinement.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-retroactive-protocol-refinement.md b/proposals/NNNN-retroactive-protocol-refinement.md index 84716ee398..cd61d17c09 100644 --- a/proposals/NNNN-retroactive-protocol-refinement.md +++ b/proposals/NNNN-retroactive-protocol-refinement.md @@ -85,7 +85,7 @@ In terms of what is surfaced to the language, extending conformances of a protoc ## Source compatibility -This is an additive feature that has a syntax which while it follows of the norms of extensions in the Swift language it is currently not permitted. Therefore, there is no effect on source compatibility. +This is an additive feature that has a syntax which while it follows the norms of extensions in the Swift language it is currently not permitted. Therefore, there is no effect on source compatibility. ## Effect on ABI stability @@ -97,4 +97,4 @@ This proposal does not make additions to the public API but is rather is a chang ## Alternatives considered -There is a more ambitious and well advanced proposal: "[parameterised extensions](https://github.com/apple/swift/pull/25263)" of which it has been said this is a subset. This proposal has more modest goals which are hopefully more accessible to the average programmer and requires only targeted surgical changes to the compiler. \ No newline at end of file +There is a more ambitious and well advanced proposal: "[parameterised extensions](https://github.com/apple/swift/pull/25263)" of which it has been said this is a subset. This proposal has more modest goals which are hopefully more accessible to the average programmer and requires only targeted surgical changes to the compiler. From 5108ab5347a409473c7eb5a976cefd10251e4b6e Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Sat, 27 Jun 2020 10:17:48 +0200 Subject: [PATCH 4/9] Update proposals/NNNN-retroactive-protocol-refinement.md Co-authored-by: Yogi --- proposals/NNNN-retroactive-protocol-refinement.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-retroactive-protocol-refinement.md b/proposals/NNNN-retroactive-protocol-refinement.md index cd61d17c09..ba20f4bedc 100644 --- a/proposals/NNNN-retroactive-protocol-refinement.md +++ b/proposals/NNNN-retroactive-protocol-refinement.md @@ -16,7 +16,7 @@ Swift-evolution thread: [Protocol extensions inheriting protocols](https://forum ## Motivation -Extensions to classes or structs (nominals) are able to specify conformances that they implement. When one extends a protocol however, it seems reasonable that specifying a conformance would add that functionality to that an entire class of nominals i.e. those that already adopt the protocol being extended or protocols that adopt that protocol etc. One example that was discussed was something that should be a simple case, where one wants to make all fixed width integers expressible by a UnicodeScalarLiteral (i.e. Strings). On the face of it one might assume the following would be possible: +Extensions to classes or structs (nominals) are able to specify conformances that they implement. When one extends a protocol however, it seems reasonable that specifying conformance would add that functionality to that an entire class of nominals i.e. those that already adopt the protocol being extended or protocols that adopt that protocol, etc. One example that was discussed was something that should be a simple case, where one wants to make all fixed width integers expressible by a UnicodeScalarLiteral (i.e. Strings). On the face of it one might assume the following would be possible: ```Swift extension FixedWidthInteger: ExpressibleByUnicodeScalarLiteral { From 2b1eb7ad0d07e6c3d76894c50aad0615fc6f6fe4 Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Sat, 27 Jun 2020 10:18:14 +0200 Subject: [PATCH 5/9] Update proposals/NNNN-retroactive-protocol-refinement.md Co-authored-by: Yogi --- proposals/NNNN-retroactive-protocol-refinement.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-retroactive-protocol-refinement.md b/proposals/NNNN-retroactive-protocol-refinement.md index ba20f4bedc..c62add135b 100644 --- a/proposals/NNNN-retroactive-protocol-refinement.md +++ b/proposals/NNNN-retroactive-protocol-refinement.md @@ -10,7 +10,7 @@ ## Introduction -Protocol extensions are a powerful feature of the Swift language but it is not currently possible to specify that an extension provides a conformance to a particular protocol. This is mentioned as "Retroactive protocol refinement" in the [Generics Manifesto](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#retroactive-protocol-refinement). The PR mentioned above explores as a proof of concept how difficult it would be to implement this feature and whether it could perhaps be considered as an additive change to the Swift Language. +Protocol extensions are a powerful feature of the Swift language but it is not currently possible to specify that an extension provides conformance to a particular protocol. This is mentioned as "Retroactive protocol refinement" in the [Generics Manifesto](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#retroactive-protocol-refinement). The PR mentioned above explores as a proof of concept how difficult it would be to implement this feature and whether it could perhaps be considered as an additive change to the Swift Language. Swift-evolution thread: [Protocol extensions inheriting protocols](https://forums.swift.org/t/protocol-extensions-inheriting-protocols/25491/8) From 31064cacacf6e008a0485c2341e2ab8dd19745ec Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Sat, 27 Jun 2020 10:18:31 +0200 Subject: [PATCH 6/9] Update proposals/NNNN-retroactive-protocol-refinement.md Co-authored-by: Yogi --- proposals/NNNN-retroactive-protocol-refinement.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-retroactive-protocol-refinement.md b/proposals/NNNN-retroactive-protocol-refinement.md index c62add135b..23dbb0c588 100644 --- a/proposals/NNNN-retroactive-protocol-refinement.md +++ b/proposals/NNNN-retroactive-protocol-refinement.md @@ -69,7 +69,7 @@ The PR above strives to demonstrate that it may be possible to avoid these pitfa ## Proposed solution -Allow conformances to be added to protocol extensions with all that implies in terms of all nominals conforming to the protocol being extended acquiring the added conformances. It would be useful if this also worked for synthesised conformances (and indeed it does). +Allow conformances to be added to protocol extensions with all that implies in terms of all nominals conforming to the protocol being extended acquiring the added conformances. It would be useful if this also worked for synthesized conformances (and indeed it does). ## Detailed design From f6c6ad378ff9dc25d4fb3a049b49c63e899e60df Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Sat, 27 Jun 2020 10:18:41 +0200 Subject: [PATCH 7/9] Update proposals/NNNN-retroactive-protocol-refinement.md Co-authored-by: Yogi --- proposals/NNNN-retroactive-protocol-refinement.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-retroactive-protocol-refinement.md b/proposals/NNNN-retroactive-protocol-refinement.md index 23dbb0c588..92c366c07c 100644 --- a/proposals/NNNN-retroactive-protocol-refinement.md +++ b/proposals/NNNN-retroactive-protocol-refinement.md @@ -77,7 +77,7 @@ The PR above makes changes in three principal areas internal to the compiler. 1) The ConformanceLookupTable — a cache of the protocols a nominal conforms to has been adapted to traverse the inherited conformances of protocol extensions. -2) The "Generic Signature" builder and module deserialising code has been adapted to traverse these extended sources of conformances. +2) The "Generic Signature" builder and module deserializing code has been adapted to traverse these extended sources of conformances. 3) While the above takes place, a registry of extended conformances is kept in the compiler. This is used to emit the ad-hoc witness tables used by extended conformances in a manner that is compatible across modules. From 101a8e90d110f3a93090b0ab19a27351f0dd6478 Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Tue, 7 Jul 2020 17:00:05 +0200 Subject: [PATCH 8/9] Resilient protocol limitation. --- proposals/NNNN-retroactive-protocol-refinement.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-retroactive-protocol-refinement.md b/proposals/NNNN-retroactive-protocol-refinement.md index 92c366c07c..198385af2a 100644 --- a/proposals/NNNN-retroactive-protocol-refinement.md +++ b/proposals/NNNN-retroactive-protocol-refinement.md @@ -93,7 +93,18 @@ This is a compiler level addition to the language that does not require any chan ## Effect on API resilience -This proposal does not make additions to the public API but is rather is a change to the capabilities of the compiler. +This proposal does not affect API resilience as it does not modify an API +but it's more appropriate here to talk about how api resilience affects +this proposal. In order that Swift nominals in the standard library can be +evolved in future, some are declared "resilient" which introduces extra +indirection so that implementations are not dependent on a particular +memory layout at run-time. This applies to protocols in that the final +layout of their witness tables is not known at compile time so as not to be +depended on at run time. This prevents emitting the ad-hoc witness tables +used by this proposal for newly conforming types and the prototype has +disabled the assertion which would normally fire in the compiler. This +erodes the utility of the proposal as often the protocols one is looking +to extend are those defined in the standard library. ## Alternatives considered From ccfeb4934960054b599c614b14e5ad26cb15d947 Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Tue, 21 Jul 2020 00:55:34 +0200 Subject: [PATCH 9/9] Feature name --- proposals/NNNN-retroactive-protocol-refinement.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-retroactive-protocol-refinement.md b/proposals/NNNN-retroactive-protocol-refinement.md index 198385af2a..ff29413b33 100644 --- a/proposals/NNNN-retroactive-protocol-refinement.md +++ b/proposals/NNNN-retroactive-protocol-refinement.md @@ -1,4 +1,4 @@ -# Feature name +# Retroactive Protocol Refinement * Proposal: [SE-NNNN](0000-retroactive-protocol-refinement.md) * Authors: [John Holdsworth](https://github.com/johnno1962)