Skip to content

Commit 91dec07

Browse files
committed
suggested fixes
1 parent 94c9dec commit 91dec07

File tree

1 file changed

+30
-13
lines changed

1 file changed

+30
-13
lines changed

proposals/NNNN-opt-in-reflection-metadata.md

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,25 @@ Swift-evolution thread: [Discussion thread topic for that proposal](https://foru
1616

1717
## Motivation
1818

19-
There are two kinds of Swift metadata emitted be the compiler:
19+
There are two kinds of Swift metadata emitted by the compiler:
2020

2121
1. Core Metadata (type metadata record, nominal type descriptor, etc).
2222
2. Reflection metadata (reflection metadata field descriptor).
2323

24-
Core metadata must constantly be emitted and may only be stripped if provenly not used. (This metadata isn't affected by this proposal)
25-
Reflection metadata contains optional information about declarations' fields - their names and references to their types. This metadata isn't used by the language's runtime features, and the emission may be skipped if such types aren't passed to reflection-consuming APIs.
24+
Core metadata must constantly be emitted and may only be stripped if provenly not used. (This metadata isn't affected by this proposal.)
25+
Reflection metadata contains optional information about declarations' fields - their names and references to their types.
26+
This metadata isn't used by the language's runtime features, and the emission may be skipped if such types aren't passed to reflection-consuming APIs.
2627

27-
APIs can use Reflection Metadata differently. Some like `print`, and `dump` will still work with disabled reflection, but the output will be limited. Others, like SwiftUI, rely on it and won't work correctly if the reflection metadata is missing.
28+
APIs can use Reflection Metadata differently. Some like `print`, and `dump` will still work with disabled reflection, but the output will be limited.
29+
Others, like SwiftUI, rely on it and won't work correctly if the reflection metadata is missing.
2830
While the former can benefit as well, the main focus of this proposal is on the latter.
2931

30-
A developer can mistakenly turn off Reflection Metadata for a Swift module and won't be warned at compile-time if APIs that consume reflection are used by that module. An app with such a module won't behave as expected at runtime which may be challenging to notice and track down such bugs back to Reflection. For instance, SwiftUI implementation uses reflection metadata from user modules to trigger the re-rendering of the view hierarchy when a state has changed. If for some reason a user module was compiled with metadata generation disabled, changing the state won't trigger that behavior and will cause inconsistency between state and representation which will make such API less safe since it becomes a runtime issue rather than a compile-time one.
32+
A developer can mistakenly turn off Reflection Metadata for a Swift module and won't be warned at compile-time if APIs that consume reflection are used by that module. An app with such a module won't behave as expected at runtime which may be challenging to notice and track down such bugs back to Reflection.
33+
For instance, SwiftUI implementation uses reflection metadata from user modules to trigger the re-rendering of the view hierarchy when a state has changed.
34+
If for some reason a user module was compiled with metadata generation disabled, changing the state won't trigger that behavior and will cause inconsistency between state and representation which will make such API less safe since it becomes a runtime issue rather than a compile-time one.
3135

32-
On the other hand, excessive Reflection metadata may be preserved in a binary even if not used, because there is currently no way to statically determine its usage. There was an attempt to limit the amount of unused reflection metadata by improving its stripability by the Dead Code Elimination LLVM pass, but in many cases, it’s still preserved in the binary because it’s referenced by Full Type Metadata which prevents Reflection Metadata from stripping. This unnecessarily increases the binary size and may affect privacy by storing more information about the code's semantics in a binary, which might be used for reverse-engineering.
36+
On the other hand, excessive Reflection metadata may be preserved in a binary even if not used, because there is currently no way to statically determine its usage. There was an attempt to limit the amount of unused reflection metadata by improving its stripability by the Dead Code Elimination LLVM pass, but in many cases, it’s still preserved in the binary because it’s referenced by Full Type Metadata which prevents Reflection Metadata from stripping.
37+
This unnecessarily increases the binary size and may simplify reverse-engineering.
3338

3439
Introducing a static compilation check can help to solve both of mentioned issues by adding to the language a way to express the requirement to have Reflection metadata at runtime.
3540

@@ -38,7 +43,8 @@ Introducing a static compilation check can help to solve both of mentioned issue
3843

3944
Teaching the Type-checker and IRGen to ensure Reflection metadata is preserved in a binary if reflection-consuming APIs are used, will help to move the issue from runtime to compile time.
4045

41-
To achieve that, a new marker protocol `Reflectable` will be introduced. Firstly, APIs developers will gain an opportunity to express a dependency on Reflection Metadata through a generic requirement of their functions, which will make such APIs safer. Secondly, during IRGen, the compiler will be able to selectively emit Reflection symbols for the types that explicitly conform to the `Reflectable` protocol, which will reduce the overhead from reflection symbols for cases when reflection is emitted but not consumed.
46+
To achieve that, a new marker protocol `Reflectable` will be introduced. Firstly, APIs developers will gain an opportunity to express a dependency on Reflection Metadata through a generic requirement of their functions, which will make such APIs safer.
47+
Secondly, during IRGen, the compiler will be able to selectively emit Reflection symbols for the types that explicitly conform to the `Reflectable` protocol, which will reduce the overhead from reflection symbols for cases when reflection is emitted but not consumed.
4248

4349
### Case Study 1:
4450

@@ -101,7 +107,10 @@ public func testIsReflectable<T>(_ t: T) -> Bool {
101107
```
102108

103109
### Behavior change for Swift 6
104-
Starting with Swift 6, we propose to enable Opt-In mode by default, to make the user experience consistent and safe. However, if full reflection isn't enabled with a new flag (`-enable-full-reflection-metadata`), the emission of reflection metadata will be skipped for all types that don't conform to the `Reflectable` protocol. This may cause changes in the behavior of the code that wasn't audited to conform to Reflectable and uses reflection-consuming APIs.
110+
111+
Starting with Swift 6, we propose to enable Opt-In mode by default, to make the user experience consistent and safe.
112+
However, if full reflection isn't enabled with a new flag (`-enable-full-reflection-metadata`), the emission of reflection metadata will be skipped for all types that don't conform to the `Reflectable` protocol.
113+
This may cause changes in the behavior of the code that wasn't audited to conform to Reflectable and uses reflection-consuming APIs.
105114

106115
For instance, stdlib's APIs like `dump`, `debugPrint`, `String(describing:)` will be returning limited output.
107116
Library authors will have to prepare their APIs for Swift 6 and introduce generic requirements on `Reflectable` in their APIs.
@@ -110,14 +119,17 @@ We also propose to deprecate the compiler's options that can lead to missing ref
110119

111120

112121
### Stdlib behavior changes
113-
In Swift `Mirror(reflecting:)` is the only official way to access Reflection metadata, all other APIs are using it under the hood. We intentionally do not propose adding a Reflectable constraint on Mirror type, because it would impose restrictions on those developers who still don't want to require it. If the presence of reflection metadata is mandatory, the requirement on Reflectable protocol should be expressed in the signatures of calling functions.
122+
123+
In Swift `Mirror(reflecting:)` is the only official way to access Reflection metadata, all other APIs are using it under the hood.
124+
We intentionally do not propose adding a Reflectable constraint on Mirror type, because it would impose restrictions on those developers who still don't want to require it.
125+
If the presence of reflection metadata is mandatory, the requirement on Reflectable protocol should be expressed in the signatures of calling functions.
114126

115127

116128
## Detailed design
117129

118130
Since Reflection metadata might be used by the debugger, we propose to always keep that metadata if full emission of debugging information is enabled (`-gdwarf-types` or `-g` flags).
119131

120-
**Changes in flags**
132+
### Changes in flags
121133
To handle behavior change between Swift pre-6 and 6, we can introduce a new upcoming feature, which will allow to enable Opt-In mode explicitly for pre-6 Swift with `-enable-upcoming-feature OptInReflection` and will set this mode by default in Swift 6.
122134

123135
A new flag `-enable-full-reflection-metadata` will also have to be introduced to allow developers to enable reflection in full if they desire in Swift 6 and later.
@@ -146,7 +158,9 @@ Casting might be a good way to improve the feature's ergonomics because currentl
146158

147159
To implement this feature, we propose to introduce a new runtime function `swift_reflectableCast`, and emit a call to it instead of `swift_dynamicCast`during IRGen if Reflectable is a target type.
148160

149-
Because of the fact that the compiler emits a call to that function at compile-time, all casts must be statically visible. All other cases like implicit conversion to `Reflectable` must be banned. This could be done at CSSimplify, when a new conversion constraint is introduced between a type variable and `Reflectable` type, the compiler will emit an error.
161+
Because of the fact that the compiler emits a call to that function at compile-time, all casts must be statically visible.
162+
All other cases like implicit conversion to `Reflectable` must be banned.
163+
This could be done at CSSimplify, when a new conversion constraint is introduced between a type variable and `Reflectable` type, the compiler will emit an error.
150164

151165
```swift
152166
func cast<T, U>(_ x: U) -> T {
@@ -163,7 +177,8 @@ Since reflectable casting will require a new runtime function, it should be gate
163177

164178
## Source compatibility
165179

166-
The change won’t break source compatibility in versions prior to Swift 6, because of the gating by the new flag. If as proposed, it’s enabled by default in Swift 6, the code with types that has not been audited to conform to the `Reflectable` protocol will fail to compile if used with APIs that consume the reflection metadata.
180+
The change won’t break source compatibility in versions prior to Swift 6, because of the gating by the new flag.
181+
If as proposed, it’s enabled by default in Swift 6, the code with types that has not been audited to conform to the `Reflectable` protocol will fail to compile if used with APIs that consume the reflection metadata.
167182

168183

169184
## Effect on ABI stability
@@ -176,6 +191,8 @@ This proposal has no effect on API resilience.
176191

177192
## Alternatives considered
178193

179-
Dead Code Elimination and linker optimisations were also considered as a way to reduce the amount of present Reflection metadata in release builds. The optimiser could use a conformance to a `Reflectable` protocol as a hint about what reflection metadata should be preserved. However, turned out it was quite challenging to statically determine all usages of Reflection metadata even with hints.
194+
Dead Code Elimination and linker optimisations were also considered as a way to reduce the amount of present Reflection metadata in release builds.
195+
The optimiser could use a conformance to a `Reflectable` protocol as a hint about what reflection metadata should be preserved.
196+
However, turned out it was quite challenging to statically determine all usages of Reflection metadata even with hints.
180197

181198
It was also considered to use an attribute `@reflectable` on nominal type declaration to express the requirement to have reflection metadata, however, a lot of logic had to be re-implemented outside of type-checker to ensure all guarantees are fulfilled.

0 commit comments

Comments
 (0)