|
1 |
| -# "Re-usable" unique names in Macros. |
| 1 | +# "Reusable" unique names in Macros. |
2 | 2 |
|
3 | 3 | * Proposal: [SE-NNNN](nnnn-reusable-unique-names-in-macros.md)
|
4 | 4 | * Authors: [Joe Newton](https://github.com/SomeRandomiOSDev)
|
|
10 | 10 |
|
11 | 11 | ## Introduction
|
12 | 12 |
|
13 |
| -This document outlines a proposal for adding support for creating unique names in macro expansions that can be re-used across invocations of different roles for the same macro invocation. |
| 13 | +This document outlines a proposal for adding support for creating unique names in macro expansions that can be reused across invocations of different roles for the same macro invocation. |
14 | 14 |
|
15 | 15 | ## Motivation
|
16 | 16 |
|
@@ -146,7 +146,7 @@ class MyClass {
|
146 | 146 |
|
147 | 147 | ## Proposed solution
|
148 | 148 |
|
149 |
| -To account for cases like the one described above, we need to extend the `makeUniqueName(_:)` function, or create a new one, that allows for the creation of "re-usable" unique identifiers that will guaranteed to be equivalent across different roles of the same macro invocation. The proposed solution is to add an optional flag to the `makeUniqueName(_:)` function that, if provided and set to `true`, will create a unique name that follows the same pattern as above but will be the same when invoked from other macro role implementations: |
| 149 | +To account for cases like the one described above, we need to extend the `makeUniqueName(_:)` function, or create a new one, that allows for the creation of "reusable" unique identifiers that will guaranteed to be equivalent across different roles of the same macro invocation. The proposed solution is to add an optional flag to the `makeUniqueName(_:)` function that, if provided and set to `true`, will create a unique name that follows the same pattern as above but will be the same when invoked from other macro role implementations: |
150 | 150 |
|
151 | 151 | ```swift
|
152 | 152 | protocol MacroExpansionContext {
|
|
170 | 170 | macro-expansion-operator ::= decl-name identifier 'fMr' // reusable uniquely-named entity
|
171 | 171 | ```
|
172 | 172 |
|
173 |
| -The compiler should also be updated in the relevant reas to account for this new mangling identifier. |
| 173 | +The compiler should also be updated in the relevant areas to account for this new mangling specification/identifer. |
174 | 174 |
|
175 | 175 | Next the SwiftSyntax library would be updated to add in support for this additional parameter on the `makeUniqueName(_:)` function. The logic for this method wouldn't change very much from how its currently implemented today:
|
176 | 176 |
|
@@ -259,6 +259,69 @@ class MyClass {
|
259 | 259 | }
|
260 | 260 | ```
|
261 | 261 |
|
| 262 | +As a side note, this only applies for different *role* invocations of the same macro, if the same macro is used more than once on a given declaration, the reusable names generated for each macro invocation would be distinct. For example, if we have the following macro that's used more than once on the same declaration, the generated names would be as follows: |
| 263 | + |
| 264 | +```swift |
| 265 | +// Declaration |
| 266 | +@attached(member) |
| 267 | +macro FooBarMacro() = #externalMacro(...) |
| 268 | + |
| 269 | +// Usage (in module "MyModule") |
| 270 | +@FooBarMacro @FooBarMacro @FooBarMacro |
| 271 | +struct FooBar { |
| 272 | + ... |
| 273 | +} |
| 274 | + |
| 275 | +// Implementation |
| 276 | +struct FooBarMacroImpl: MemberMacro { |
| 277 | + static func expansion( |
| 278 | + of node: AttributeSyntax, |
| 279 | + providingMembersOf declaration: some DeclGroupSyntax, |
| 280 | + conformingTo protocols: [TypeSyntax], |
| 281 | + in context: some MacroExpansionContext |
| 282 | + ) throws -> [DeclSyntax] { |
| 283 | + let uniqueName = context.makeUniqueName("foobar") |
| 284 | + ... |
| 285 | + } |
| 286 | +} |
| 287 | + |
| 288 | +// Three unique names would be generated, one for each of the macros attached to `FooBar`: |
| 289 | +// |
| 290 | +// First `@FooBarMacro` attribute: |
| 291 | +// uniqueName == "$s8MyModule6FooBar11FooBarMacrofMm_6foobarfMu_" |
| 292 | +// ^~~~ |
| 293 | +// Second `@FooBarMacro` attribute: |
| 294 | +// uniqueName == "$s8MyModule6FooBar11FooBarMacrofMm0_6foobarfMu_" |
| 295 | +// ^~~~~ |
| 296 | +// Third `@FooBarMacro` attribute: |
| 297 | +// uniqueName == "$s8MyModule6FooBar11FooBarMacrofMm1_6foobarfMu_" |
| 298 | +// ^~~~~ |
| 299 | +``` |
| 300 | + |
| 301 | +If a given macro with multiple roles makes reusable unique identifers, each *macro invocation* will be able to reuse the created identifiers across *role invocations* of that same macro instanace, however, the other reusable identifiers created for the other *macro invocations* would be unique to their role invocations. Continuing with out associated object macro example, if the macro were used twice on the same property, the final output would be as follows: |
| 302 | + |
| 303 | +```swift |
| 304 | +class MyClass { |
| 305 | + private static let $s8MyModule7MyClass6foobarfMA_19associatedObjectKeyfMu_: UnsafeRawPointer = ... |
| 306 | + ^~~~ |
| 307 | + private static let $s8MyModule7MyClass6foobarfMA0_19associatedObjectKeyfMu_: UnsafeRawPointer = ... |
| 308 | + ^~~~~ |
| 309 | + |
| 310 | + var foobar: AnyObject? { |
| 311 | + get { objc_getAssociatedObject(self, Self.$s8MyModule7MyClass6foobarfMA_19associatedObjectKeyfMu_) } |
| 312 | + ^~~~ |
| 313 | + set { objc_getAssociatedObject(self, Self.$s8MyModule7MyClass6foobarfMA_19associatedObjectKeyfMu_, newValue, .OBJC_ASSOCIATION_POLICY) } |
| 314 | + ^~~~ |
| 315 | + get { objc_getAssociatedObject(self, Self.$s8MyModule7MyClass6foobarfMA0_19associatedObjectKeyfMu_) } |
| 316 | + ^~~~~ |
| 317 | + set { objc_getAssociatedObject(self, Self.$s8MyModule7MyClass6foobarfMA0_19associatedObjectKeyfMu_, newValue, .OBJC_ASSOCIATION_POLICY) } |
| 318 | + ^~~~~ |
| 319 | + } |
| 320 | +} |
| 321 | +``` |
| 322 | + |
| 323 | +Although this limits the reusability of the generated identifiers, this seems appropriate given a macro that would require or have different behavior being attached multiple times to the same declaration doesn't seem like a well-designed macro nor would be necessarily practical. This is an arbitrary decision that was made but isn't central to the propsal overall. |
| 324 | + |
262 | 325 | ## Source compatibility
|
263 | 326 |
|
264 | 327 | Describe the impact of this proposal on source compatibility. As a
|
|
0 commit comments