diff --git a/Style.md b/Style.md index fb1ac28d7..045b8026b 100644 --- a/Style.md +++ b/Style.md @@ -98,6 +98,11 @@ which means it’s not always suitable for generating a parser for Swift code. # Terms and Rules +## attribute names + +In the guide, write `@` before attribute names; +in the reference, omit it. + ## back deploy Spelled as two words, not closed up or hyphenated. @@ -122,6 +127,13 @@ you should generally try to avoid them. Deeply nested headings often indicate that there’s a better way to organize the content. +## macro names + +In the guide, +write `@` before the name when referring to attached macros +and `#` before the name when referring to freestanding macros. +In the reference, omit it. + ## memberwise initializer Not hyphenated as “member-wise”. diff --git a/TSPL.docc/Assets/macro-ast-input@2x.png b/TSPL.docc/Assets/macro-ast-input@2x.png new file mode 100644 index 000000000..b2fed9f46 Binary files /dev/null and b/TSPL.docc/Assets/macro-ast-input@2x.png differ diff --git a/TSPL.docc/Assets/macro-ast-input~dark@2x.png b/TSPL.docc/Assets/macro-ast-input~dark@2x.png new file mode 100644 index 000000000..6291b40ca Binary files /dev/null and b/TSPL.docc/Assets/macro-ast-input~dark@2x.png differ diff --git a/TSPL.docc/Assets/macro-ast-original@2x.png b/TSPL.docc/Assets/macro-ast-original@2x.png new file mode 100644 index 000000000..35a468965 Binary files /dev/null and b/TSPL.docc/Assets/macro-ast-original@2x.png differ diff --git a/TSPL.docc/Assets/macro-ast-original~dark@2x.png b/TSPL.docc/Assets/macro-ast-original~dark@2x.png new file mode 100644 index 000000000..f8afac36f Binary files /dev/null and b/TSPL.docc/Assets/macro-ast-original~dark@2x.png differ diff --git a/TSPL.docc/Assets/macro-ast-output@2x.png b/TSPL.docc/Assets/macro-ast-output@2x.png new file mode 100644 index 000000000..6785a2c64 Binary files /dev/null and b/TSPL.docc/Assets/macro-ast-output@2x.png differ diff --git a/TSPL.docc/Assets/macro-ast-output~dark@2x.png b/TSPL.docc/Assets/macro-ast-output~dark@2x.png new file mode 100644 index 000000000..b94717537 Binary files /dev/null and b/TSPL.docc/Assets/macro-ast-output~dark@2x.png differ diff --git a/TSPL.docc/Assets/macro-ast-result@2x.png b/TSPL.docc/Assets/macro-ast-result@2x.png new file mode 100644 index 000000000..877f327c1 Binary files /dev/null and b/TSPL.docc/Assets/macro-ast-result@2x.png differ diff --git a/TSPL.docc/Assets/macro-ast-result~dark@2x.png b/TSPL.docc/Assets/macro-ast-result~dark@2x.png new file mode 100644 index 000000000..9b1944f17 Binary files /dev/null and b/TSPL.docc/Assets/macro-ast-result~dark@2x.png differ diff --git a/TSPL.docc/Assets/macro-expansion-full@2x.png b/TSPL.docc/Assets/macro-expansion-full@2x.png new file mode 100644 index 000000000..e25ca2aab Binary files /dev/null and b/TSPL.docc/Assets/macro-expansion-full@2x.png differ diff --git a/TSPL.docc/Assets/macro-expansion-full~dark@2x.png b/TSPL.docc/Assets/macro-expansion-full~dark@2x.png new file mode 100644 index 000000000..ae7bd15c8 Binary files /dev/null and b/TSPL.docc/Assets/macro-expansion-full~dark@2x.png differ diff --git a/TSPL.docc/Assets/macro-expansion@2x.png b/TSPL.docc/Assets/macro-expansion@2x.png new file mode 100644 index 000000000..1bf5d1733 Binary files /dev/null and b/TSPL.docc/Assets/macro-expansion@2x.png differ diff --git a/TSPL.docc/Assets/macro-expansion~dark@2x.png b/TSPL.docc/Assets/macro-expansion~dark@2x.png new file mode 100644 index 000000000..cd26ac7ce Binary files /dev/null and b/TSPL.docc/Assets/macro-expansion~dark@2x.png differ diff --git a/TSPL.docc/LanguageGuide/Macros.md b/TSPL.docc/LanguageGuide/Macros.md new file mode 100644 index 000000000..6dc75f24e --- /dev/null +++ b/TSPL.docc/LanguageGuide/Macros.md @@ -0,0 +1,732 @@ +# Macros + +Use macros to generate code at compile time. + +Macros transform your source code when you compile it, +letting you avoid writing repetitive code by hand. +During compilation, +Swift expands any macros in your code before building your code as usual. + +![A diagram showing an overview of macro expansion. On the left, a stylized representation of Swift code. On the right, the same code with several lines added by the macro.](macro-expansion) + +Expanding a macro is always an additive operation: +Macros add new code, +but they never delete or modify existing code. + +Both the input to a macro and the output of macro expansion +are checked to ensure they're syntactically valid Swift code. +Likewise, the values you pass to a macro +and the values in code generated by a macro +are checked to ensure they have the correct types. +In addition, +if the macro's implementation encounters an error when expanding that macro, +the compiler treats this as a compilation error. +These guarantees make it easier to reason about code that uses macros, +and they make it easier to identify issues +like using a macro incorrectly +or a macro implementation that has a bug. + +Swift has two kinds of macros: + +- *Freestanding macros* appear on their own, + without being attached to a declaration. + +- *Attached macros* modify the declaration that they're attached to. + +You call attached and freestanding macros slightly differently, +but they both follow the same model for macro expansion, +and you implement them both using the same approach. +The following sections describe both kinds of macros in more detail. + +## Freestanding Macros + +To call a freestanding macro, +you write a number sign (`#`) before its name, +and you write any arguments to the macro in parentheses after its name. +For example: + +```swift +func myFunction() { + print("Currently running \(#function)") + #warning("Something's wrong") +} +``` + +In the first line, +`#function` calls the [`function`][] macro from the Swift standard library. +When you compile this code, +Swift calls that macro's implementation, +which replaces `#function` with the name of the current function. +When you run this code and call `myFunction()`, +it prints `Currently running myFunction()`. +In the second line, +`#warning` calls the [`warning(_:)`][] macro from the Swift standard library +to produce a custom compile-time warning. + +[`function`]: https://developer.apple.com/documentation/swift/function +[`warning(_:)`]: https://developer.apple.comdocumentation/swift/warning(_:) + +Freestanding macros can produce a value, like `#function` does, +or they can perform an action at compile time, like `#warning` does. + + +## Attached Macros + +To call an attached macro, +you write an at sign (`@`) before its name, +and you write any arguments to the macro in parentheses after its name. + +Attached macros modify the declaration that they're attached to. +They add code to that declaration, +like defining a new method or adding conformance to a protocol. + +For example, consider the following code +that doesn't use macros: + +```swift +struct SundaeToppings: OptionSet { + let rawValue: Int + static let nuts = SundaeToppings(rawValue: 1 << 0) + static let cherry = SundaeToppings(rawValue: 1 << 1) + static let fudge = SundaeToppings(rawValue: 1 << 2) +} +``` + +In this code, +each of the options in the `SundaeToppings` option set +includes a call to the initializer, +which is repetitive and manual. +It would be easy to make a mistake when adding a new option, +like typing the wrong number at the end of the line. + +Here's a version of this code that uses a macro instead: + +```swift +@OptionSet +struct SundaeToppings { + private enum Options: Int { + case nuts + case cherry + case fudge + } +} +``` + +This version of `SundaeToppings` +calls the [`@OptionSet`][] macro from the Swift standard library. +The macro reads the list of cases in the private enumeration, +generates the list of constants for each option, +and adds a conformance to the [`OptionSet`][] protocol. + +[`@OptionSet`]: https://developer.apple.com/documentation/swift/optionset-swift.macro +[`OptionSet`]: https://developer.apple.com/documentation/swift/optionset-swift.protocol + +For comparison, +here's what the expanded version of the `@OptionSet` macro looks like. +You don't write this code, +and you would see it only if you specifically asked Swift +to show the macro's expansion. + +```swift +struct SundaeToppings { + private enum Options: Int { + case nuts + case cherry + case fudge + } + + typealias RawValue = Int + var rawValue: RawValue + init() { self.rawValue = 0 } + init(rawValue: RawValue) { self.rawValue = rawValue } + static let nuts: Self = Self(rawValue: 1 << Options.nuts.rawValue) + static let cherry: Self = Self(rawValue: 1 << Options.cherry.rawValue) + static let fudge: Self = Self(rawValue: 1 << Options.fudge.rawValue) +} +extension SundaeToppings: OptionSet { } +``` + +All of the code after the private enumeration +comes from the `@OptionSet` macro. +The version of `SundaeToppings` +that uses a macro to generate all of the static variables +is easier to read and easier to maintain +than the manually coded version, earlier. + +## Macro Declarations + +In most Swift code, +when you implement a symbol, like a function or type, +there's no separate declaration. +However, for macros, the declaration and implementation are separate. +A macro's declaration contains its name, +the parameters it takes, +where it can be used, +and what kind of code it generates. +A macro's implementation contains the code +that expands the macro by generating Swift code. + +You introduce a macro declaration with the `macro` keyword. +For example, +here's part of the declaration for +the `@OptionSet` macro used in the previous example: + +```swift +public macro OptionSet() = + #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") +``` + +The first line +specifies the macro's name and its arguments --- +the name is `OptionSet`, and it doesn't take any arguments. +The second line +uses the `#externalMacro(module:type:)` macro from the Swift standard library +to tell Swift where the macro's implementation is located. +In this case, +the `SwiftMacros` module +contains a type named `OptionSetMacro`, +which implements the `@OptionSet` macro. + +Because `OptionSet` is an attached macro, +its name uses upper camel case, +like the names for structures and classes. +Freestanding macros have lower camel case names, +like the names for variables and functions. + +> Note: +> Macros are always declared as `public`. +> Because the code that declares a macro +> is in a different module from code that uses that macro, +> there isn't anywhere you could apply a non-public macro. + +A macro declaration defines the macro's *roles* --- +the places in source code where that macro can be called, +and the kinds of code the macro can generate. +Every macro has one or more roles, +which you write as part of the attributes +at the beginning of the macro declaration. +Here's a bit more of the declaration for `@OptionSet`, +including the attributes for its roles: + +```swift +@attached(member) +@attached(conformance) +public macro OptionSet() = + #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") +``` + +The `@attached` attribute appears twice in this declaration, +once for each macro role. +The first use, `@attached(member)`, indicates that the macro +adds new members to the type you apply it to. +The `@OptionSet` macro adds an `init(rawValue:)` initializer +that's required by the `OptionSet` protocol, +as well as some additional members. +The second use, `@attached(conformance)`, tells you that `@OptionSet` +adds one or more protocol conformances. +The `@OptionSet` macro +extends the type that you apply the macro to, +to add conformance to the `OptionSet` protocol. + +For a freestanding macro, +you write the `@freestanding` attribute to specify its role: + +``` +@freestanding(expression) +public macro line() -> T = + /* ... location of the macro implementation... */ +``` + + + +The `#line` macro above has the `expression` role. +An expression macro produces a value, +or performs a compile-time action like generating a warning. + +In addition to the macro's role, +a macro's declaration provides information about +the names of the symbols that the macro generates. +When a macro declaration provides a list of names, +it's guaranteed to produce only declarations that use those names, +which helps you understand and debug the generated code. +Here's the full declaration of `@OptionSet`: + +```swift +@attached(member, names: named(RawValue), named(rawValue), + named(`init`), arbitrary) +@attached(conformance) +public macro OptionSet() = + #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") +``` + +In the declaration above, +the `@attached(member)` macro includes arguments after the `named:` label +for each of the symbols that the `@OptionSet` macro generates. +The macro adds declarations for symbols named +`RawValue`, `rawValue`, and `init` --- +because those names are known ahead of time, +the macro declaration lists them explicitly. + +The macro declaration also includes `arbitrary` after the list of names, +allowing the macro to generate declarations +whose names aren't known until you use the macro. +For example, +when the `@OptionSet` macro is applied to the `SundaeToppings` above, +it generates type properties that correspond to the enumeration cases, +`nuts`, `cherry`, and `fudge`. + +For more information, +including a full list of macro roles, +see and +in . + +## Macro Expansion + +When building Swift code that uses macros, +the compiler calls the macros' implementation to expand them. + +![Diagram showing the four steps of expanding macros. The input is Swift source code. This becomes a tree, representing the code's structure. The macro implementation adds branches to the tree. The result is Swift source with additional code.](macro-expansion-full) + +Specifically, Swift expands macros in the following way: + +1. The compiler reads the code, + creating an in-memory representation of the syntax. + +1. The compiler sends part of the in-memory representation + to the macro implementation, + which expands the macro. + +1. The compiler replaces the macro call with its expanded form. + +1. The compiler continues with compilation, + using the expanded source code. + +To go through the specific steps, consider the following: + +``` +let magicNumber = #fourCharacterCode("ABCD") +``` + +The `#fourCharacterCode` macro takes a string that's four characters long +and returns an unsigned 32-bit integer +that corresponds to the ASCII values in the string joined together. +Some file formats use integers like this to identify data +because they're compact but still readable in a debugger. +The section below +shows how to implement this macro. + +To expand the macros in the code above, +the compiler reads the Swift file +and creates an in-memory representation of that code +known an as *abstract syntax tree*, or AST. +The AST makes the code's structure explicit, +which makes it easier to write code that interacts with that structure --- +like a compiler or a macro implementation. +Here's a representation of the AST for the code above, +slightly simplified by omitting some extra detail: + +![A tree diagram, with a constant as the root element. The constant has a name, magic number, and a value. The constant's value is a macro call. The macro call has a name, fourCharacterCode, and arguments. The argument is a string literal, ABCD.](macro-ast-original) + +The diagram above shows how the structure of this code +is represented in memory. +Each element in the AST +corresponds to a part of the source code. +The "Constant declaration" AST element +has two child elements under it, +which represent the two parts of a constant declaration: +its name and its value. +The "Macro call" element has child elements +that represent the macro's name +and the list of arguments being passed to the macro. + +As part of constructing this AST, +the compiler checks that the source code is valid Swift. +For example, `#fourCharacterCode` takes a single argument, +which must be a string. +If you tried to pass an integer argument, +or forgot the quotation mark (`"`) at the end of the string literal, +you'd get an error at this point in the process. + +The compiler finds the places in the code where you call a macro, +and loads the external binary that implements those macros. +For each macro call, +the compiler passes part of the AST to that macro's implementation. +Here's a representation of that partial AST: + +![A tree diagram, with a macro call as the root element. The macro call has a name, fourCharacterCode, and arguments. The argument is a string literal, ABCD.](macro-ast-input) + +The implementation of the `#fourCharacterCode` macro +reads this partial AST as its input when expanding the macro. +A macro's implementation +operates only on the partial AST that it receives as its input, +meaning a macro always expands the same way +regardless of what code comes before and after it. +This limitation helps make macro expansion easier to understand, +and helps your code build faster +because Swift can avoid expanding macros that haven't changed. + +Swift helps macro authors avoid accidentally reading other input +by restricting the code that implements macros: + +- The AST passed to a macro implementation + contains only the AST elements that represent the macro, + not any of the code that comes before or after it. + +- The macro implementation runs in a sandboxed environment + that prevents it from accessing the file system or the network. + +In addition to these safeguards, +the macro's author is responsible for not reading or modifying anything +outside of the macro's inputs. +For example, a macro's expansion must not depend on the current time of day. + +The implementation of `#fourCharacterCode` +generates a new AST containing the expanded code. +Here's what that code returns to the compiler: + +![A tree diagram with a sigle node, the integer literal 1145258561.](macro-ast-output) + +When the compiler receives this expansion, +it replaces the AST element that contains the macro call +with the element that contains the macro's expansion. +After macro expansion, +the compiler checks again to ensure +the program is still syntactically valid Swift +and all the types are correct. +That produces a final AST that can be compiled as usual: + +![A tree diagram, with a constant as the root element. The constant has a name, magic number, and a value. The constant's value is the integer literal 1145258561](macro-ast-result) + +This AST corresponds to Swift code like this: + +``` +let magicNumber = 1145258561 +``` + +In this example, the input source code has only one macro, +but a real program could have several instances of the same macro +and several calls to different macros. +The compiler expands macros one at a time. + +If one macro appears inside another, +the outer macro is expanded first --- +this lets the outer macro modify the inner macro before it's expanded. + + + +## Implementing a Macro + +To implement a macro, you make two components: +A type that performs the macro expansion, +and a library that declares the macro to expose it as API. +These parts are built separately from code that uses the macro, +even if you're developing the macro and its clients together, +because the macro implementation runs +as part of building the macro's clients. + +To create a new macro using Swift Package Manager, +run `swift package init --type macro` --- +this creates several files, +including a template for a macro implementation and declaration. + +To add macros to an existing project, +add a target for the macro implementation +and a target for the macro library. +For example, +you can add something like the following to your `Package.swift` file, +changing the names to match your project: + +```swift +targets: [ + // Macro implementation that performs the source transformations. + .macro( + name: "MyProjectMacros", + dependencies: [ + .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), + .product(name: "SwiftCompilerPlugin", package: "swift-syntax") + ] + ), + + // Library that exposes a macro as part of its API. + .target(name: "MyProject", dependencies: ["MyProjectMacros"]), +] +``` + +The code above defines two targets: +`MyProjectMacros` contains the implementation of the macros, +and `MyProject` makes those macros available. + +The implementation of a macro +uses the [SwiftSyntax][] module to interact with Swift code +in a structured way, using an AST. +If you created a new macro package with Swift Package Manager, +the generated `Package.swift` file +automatically includes a dependency on SwiftSyntax. +If you're adding macros to an existing project, +add a dependency on SwiftSyntax in your `Package.swift` file: + +[SwiftSyntax]: http://github.com/apple/swift-syntax/ + +```swift +dependencies: [ + .package(url: "https://github.com/apple/swift-syntax.git", from: "some-tag"), +], +``` + +Replace the `some-tag` placeholder in the code above +with the Git tag for the version of SwiftSyntax you want to use. + +Depending on your macro's role, +there's a corresponding protocol from SwiftSystem +that the macro implementation conforms to. +For example, +consider `#fourCharacterCode` from the previous section. +Here's a structure that implements that macro: + +```swift +public struct FourCharacterCode: ExpressionMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) throws -> ExprSyntax { + guard let argument = node.argumentList.first?.expression, + let segments = argument.as(StringLiteralExprSyntax.self)?.segments, + segments.count == 1, + case .stringSegment(let literalSegment)? = segments.first + else { + throw CustomError.message("Need a static string") + } + + let string = literalSegment.content.text + guard let result = fourCharacterCode(for: string) else { + throw CustomError.message("Invalid four-character code") + } + + return "\(raw: result)" + } +} + +private func fourCharacterCode(for characters: String) -> UInt32? { + guard characters.count == 4 else { return nil } + + var result: UInt32 = 0 + for character in characters { + result = result << 8 + guard let asciiValue = character.asciiValue else { return nil } + result += UInt32(asciiValue) + } + return result.bigEndian +} +enum CustomError: Error { case message(String) } +``` + +The `#fourCharacterCode` macro +is a freestanding macro that produces an expression, +so the `FourCharacterCode` type that implements it +conforms to the `ExpressionMacro` protocol. +The `ExpressionMacro` protocol has one requirement, +a `expansion(of:in:)` method that expands the AST. +For the list of macro roles and their corresponding SwiftSystem protocols, +see and +in + +To expand the `#fourCharacterCode` macro, +Swift sends the AST for the code that uses this macro +to the library that contains the macro implementation. +Inside the library, Swift calls `FourCharacterCode.expansion(of:in:)`, +passing in the AST and the context as arguments to the method. +The implementation of `expansion(of:in:)` +finds the string that was passed as an argument to `#fourCharacterCode` +and calculates the corresponding integer literal value. + +In the example above, +the first `guard` block extracts the string literal from the AST, +assigning that AST element to `literalSegment`. +The second `guard` block +calls the private `FourCharacterCode(for:)` function. +Both of these blocks throw an error if the macro is used incorrectly --- +the error message becomes a compiler error +at the malformed call site. +For example, +if you try to call the macro as `#fourCharacterCode("AB" + "CD")` +the compiler shows the error "Need a static string". + +The `expansion(of:in:)` method returns an instance of `ExprSyntax`, +a type from SwiftSyntax that represents an expression in an AST. +Because this type conforms to the `StringLiteralConvertible` protocol, +the macro implementation uses a string literal +as a lightweight syntax to create its result. +All of the SwiftSyntax types that you return from a macro implementation +conform to `StringLiteralConvertible`, +so you can use this approach when implementing any kind of macro. + + + + + + + + +## Developing and Debugging Macros + +Macros are well suited to development using tests: +They transform one AST into another AST +without depending on any external state, +and without making changes to any external state. +In addition, you can create syntax nodes from a string literal, +which simplifies setting up the input for a unit test. +You can also read the `description` property of an AST +to get a string to compare against an expected value. +For example, +here's a test of the `#fourCharacterCode` macro from previous sections: + +```swift +let source: SourceFileSyntax = + """ + let abcd = #fourCharacterCode("ABCD") + """ + +let file = BasicMacroExpansionContext.KnownSourceFile( + moduleName: "MyModule", + fullFilePath: "test.swift" +) + +let context = BasicMacroExpansionContext(sourceFiles: [source: file]) + +let transformedSF = source.expand( + macros:["fourCharacterCode": FourCC.self], + in: context +) + +let expectedDescription = + """ + let abcd = 1145258561 + """ + +precondition(transformedSF.description == expectedDescription) +``` + +The example above tests the macro using a precondition, +but you could use a testing framework instead. + + + + diff --git a/TSPL.docc/ReferenceManual/Attributes.md b/TSPL.docc/ReferenceManual/Attributes.md index 56c3ff654..a70952ddf 100644 --- a/TSPL.docc/ReferenceManual/Attributes.md +++ b/TSPL.docc/ReferenceManual/Attributes.md @@ -28,6 +28,91 @@ and their format is defined by the attribute they belong to. You can apply a declaration attribute to declarations only. +### attached + +Apply the `attached` attribute to a macro declaration. +The arguments to this attribute indicate the macro's role. +For a macro that has multiple roles, +apply the `attached` macro multiple times, once for each role. + + + +The first argument to this attribute +indicates the macros role: + +- term Peer macros: + Write `peer` as the first argument to this attribute. + The type that implements the macro conforms to the `PeerMacro` protocol. + These macros produce new declarations + in the same scope as the declaration + that the macro is attached to. + For example, + applying a peer macro to a method of a structure + can define additional methods and properties on that structure. + +- term Member macros: + Write `member` as the first argument to this attribute. + The type that implements the macro conforms to the `MemberMacro` protocol. + These macros produce new declarations + that are members of the type or extension + that the macro is attached to. + For example, + applying a member macro to a structure declaration + can define additional methods and properties on that structure. + +- term Member attribute: + Write have `memberAttribute` as the first argument to this attribute. + The type that implements the macro conforms to the `MemberAttributeMacro` protocol. + These macros add attributes to members of the type or extension + that the macro is attached to. + +- term Accessor macros: + Write `accessor` as the first argument to this attribute. + The type that implements the macro conforms to the `AccessorMacro` protocol. + These macros add accessors to the stored property they're attached to, + turning it into a computed property. + +- term Conformance macros: + Write `conformance` as the first argument to this attribute. + The type that implements the macro conforms to the `ConformanceMacro` protocol. + These macros add protocol conformance to the type they're attached to. + +The peer, member, and accessor macro roles require a `named:` argument, +listing the names of the symbols that the macro generates. +When a macro declaration includes the `named:` argument, +the macro implementation must generate +only symbol with names that match that list. +That said, +a macro need not generate a symbol for every listed name. +The value for that argument is a list of one or more of the following: + +- `named(<#name#>)` + where *name* is that fixed symbol name, + for a name that's known in advance. + +- `overloaded` + for a name that's the same as an existing symbol. + +- `prefixed(<#prefix#>)` + where *prefix* is prepended to the symbol name, + for a name that starts with a fixed string. + +- `suffixed(<#suffix#>` + where *suffix* is appended to the symbol name, + for a name that ends with a fixed string. + +- `arbitrary` + for a name that can't be determined until macro expansion. + +As a special case, +you can write `prefixed($)` +for a macro that behaves similar to a property wrapper. + + ### available Apply this attribute to indicate a declaration's life cycle @@ -553,6 +638,30 @@ print(wrapper.x) ``` --> +### freestanding + +Apply the `freestanding` attribute +to the declaration of a freestanding macro. + + + ### frozen Apply this attribute to a structure or enumeration declaration diff --git a/TSPL.docc/ReferenceManual/Declarations.md b/TSPL.docc/ReferenceManual/Declarations.md index cdb7fca70..fee6231ea 100644 --- a/TSPL.docc/ReferenceManual/Declarations.md +++ b/TSPL.docc/ReferenceManual/Declarations.md @@ -45,6 +45,8 @@ the term *declaration* covers both declarations and definitions. > > *declaration* → *subscript-declaration* > +> *declaration* → *macro-declaration* +> > *declaration* → *operator-declaration* > > *declaration* → *precedence-group-declaration* @@ -3442,6 +3444,44 @@ with both the `class` and `final` declaration modifiers. > > *subscript-result* → **`->`** *attributes*_?_ *type* +## Macro Declaration + +A *macro declaration* introduces a new macro. +It begins with the `macro` keyword +and has the following form: + +```swift +macro <#name#> = <#macro implementation#> +``` + +The *macro implementation* is another macro, +and indicates the location of the code that performs this macro's expansion. +Call the `externalMacro(module:type:)` macro from the Swift standard library, +passing in the name of a type that contains the macro's implementation, +and the name of the module that contains that type. + +Macros can be overloaded, +following the same model used by functions. +A macro declaration appears only at file scope. + +For more information, see . + +> Grammar of a macro declaration: +> +> *macro-declaration* → *macro-head* *identifier* *generic-parameter-clause*_?_ *macro-signature* *macro-definition*_?_ *generic-where-clause* +> +> *macro-head* → *attributes*_?_ *declaration-modifiers*_?_ **`macro`** +> +> *macro-signature* → *parameter-clause* *macro-function-signature-result*_?_ +> +> *macro-function-signature-result* → **`->`** *type* +> +> *macro-definition* → **`=`** *expression* + + + ## Operator Declaration An *operator declaration* introduces a new infix, prefix, diff --git a/TSPL.docc/ReferenceManual/Expressions.md b/TSPL.docc/ReferenceManual/Expressions.md index 1e44658ab..52342ca43 100644 --- a/TSPL.docc/ReferenceManual/Expressions.md +++ b/TSPL.docc/ReferenceManual/Expressions.md @@ -564,6 +564,8 @@ to make prefix expressions, infix expressions, and postfix expressions. > > *primary-expression* → *wildcard-expression* > +> *primary-expression* → *macro-expansion-expression* +> > *primary-expression* → *key-path-expression* > > *primary-expression* → *selector-expression* @@ -588,46 +590,19 @@ to make prefix expressions, infix expressions, and postfix expressions. A *literal expression* consists of either an ordinary literal (such as a string or a number), an array or dictionary literal, -a playground literal, -or one of the following special literals: - -| Literal | Type | Value | -| ------- | ---- | ----- | -| `#file` | `String` | The path to the file in which it appears. | -| `#fileID` | `String` | The name of the file and module in which it appears. | -| `#filePath` | `String` | The path to the file in which it appears. | -| `#line` | `Int` | The line number on which it appears. | -| `#column` | `Int` | The column number in which it begins. | -| `#function` | `String` | The name of the declaration in which it appears. | -| `#dsohandle` | `UnsafeRawPointer` | The dynamic shared object (DSO) handle in use where it appears. | - -The string value of `#file` depends on the language version, -to enable migration from the old `#filePath` behavior -to the new `#fileID` behavior. -Currently, `#file` has the same value as `#filePath`. -In a future version of Swift, -`#file` will have the same value as `#fileID` instead. -To adopt the future behavior, -replace `#file` with `#fileID` or `#filePath` as appropriate. - -The string value of a `#fileID` expression has the form *module*/*file*, -where *file* is the name of the file in which the expression appears -and *module* is the name of the module that this file is part of. -The string value of a `#filePath` expression -is the full file-system path to the file in which the expression appears. -Both of these values can be changed by `#sourceLocation`, -as described in . -Because `#fileID` doesn't embed the full path to the source file, -unlike `#filePath`, -it gives you better privacy and reduces the size of the compiled binary. -Avoid using `#filePath` outside of tests, build scripts, -or other code that doesn't become part of the shipping program. - -> Note: To parse a `#fileID` expression, -> read the module name as the text before the first slash (`/`) -> and the filename as the text after the last slash. -> In the future, the string might contain multiple slashes, -> such as `MyModule/some/disambiguation/MyFile.swift`. +or a playground literal. + +> Note: +> Prior to Swift 5.9, +> the following special literals were recognized, +> which are now implemented as macros in the Swift standard library: +> `#column`, +> `#dsohandle`, +> `#fileID`, +> `#filePath`, +> `#file`, +> `#function`, +> and `#line`. -Inside a function, -the value of `#function` is the name of that function, -inside a method it's the name of that method, -inside a property getter or setter it's the name of that property, -inside special members like `init` or `subscript` -it's the name of that keyword, -and at the top level of a file it's the name of the current module. - -When used as the default value of a function or method parameter, -the special literal's value is determined -when the default value expression is evaluated at the call site. - - - -```swift -func logFunctionName(string: String = #function) { - print(string) -} -func myFunction() { - logFunctionName() // Prints "myFunction()". -} -``` - - - An *array literal* is an ordered collection of values. It has the following form: @@ -767,10 +692,6 @@ in Xcode Help. > > *literal-expression* → *array-literal* | *dictionary-literal* | *playground-literal* > -> *literal-expression* → **`#file`** | **`#fileID`** | **`#filePath`** -> -> *literal-expression* → **`#line`** | **`#column`** | **`#function`** | **`#dsohandle`** -> > > > *array-literal* → **`[`** *array-literal-items*_?_ **`]`** @@ -1549,6 +1470,33 @@ For example, in the following assignment > > *wildcard-expression* → **`_`** +### Macro-Expansion Expression + +A *macro-expansion expression* consists of a macro name +followed by a comma-separated list of the macro's arguments in parentheses. +The macro is expanded at compile time. +Macro-expansion expressions have the following form: + +```swift +<#macro name#>(<#macro argument 1#>, <#macro argument 2#>) +``` + +A macro-expansion expression omits the parentheses +if the macro doesn't take any arguments. + +A macro expression can't appear as the default value for a parameter, +except for the [`file`][] and [`line`][] macros from the Swift standard library. +When used as the default value of a function or method parameter, +These macros' value is determined +when the default value expression is evaluated at the call site. + +[`file`]: http://developer.apple.com/documentation/swift/documentation/swift/file +[`line`]: http://developer.apple.com/documentation/swift/documentation/swift/line + +> Grammar of a macro-expansion expression: +> +> *macro-expansion-expression* → **`#`** *identifier* *generic-argument-clause*_?_ *function-call-argument-clause*_?_ *trailing-closures*_?_ + ### Key-Path Expression A *key-path expression* diff --git a/TSPL.docc/ReferenceManual/LexicalStructure.md b/TSPL.docc/ReferenceManual/LexicalStructure.md index 3aff8f41f..26df4747a 100644 --- a/TSPL.docc/ReferenceManual/LexicalStructure.md +++ b/TSPL.docc/ReferenceManual/LexicalStructure.md @@ -368,24 +368,29 @@ so they must be escaped with backticks in that context. - Keywords that begin with a number sign (`#`): `#available`, `#colorLiteral`, - `#column`, - `#dsohandle`, `#elseif`, `#else`, `#endif`, - `#error`, - `#fileID`, - `#fileLiteral`, - `#filePath`, - `#file`, - `#function`, `#if`, `#imageLiteral`, `#keyPath`, - `#line`, `#selector`, - `#sourceLocation`, - and `#warning`. + `#sourceLocation`. + +> Note: +> Prior to Swift 5.9, +> the following keywords are reserved, +> which are now implemented as macros in the Swift standard library: +> `#column`, +> `#dsohandle`, +> `#error`, +> `#fileID`, +> `#fileLiteral`, +> `#filePath`, +> `#file`, +> `#function`, +> `#line`, +> and `#warning`. - - - - +[`warning(_:)`]: http://developer.apple.com/documentation/swift/documentation/swift/warning(_:) +[`error(_:)`]: http://developer.apple.com/documentation/swift/documentation/swift/error(_:) ## Availability Condition diff --git a/TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md b/TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md index cc00e3765..675d561bf 100644 --- a/TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md +++ b/TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md @@ -575,6 +575,8 @@ make the same change here also. > > *primary-expression* → *wildcard-expression* > +> *primary-expression* → *macro-expansion-expression* +> > *primary-expression* → *key-path-expression* > > *primary-expression* → *selector-expression* @@ -587,10 +589,6 @@ make the same change here also. > > *literal-expression* → *array-literal* | *dictionary-literal* | *playground-literal* > -> *literal-expression* → **`#file`** | **`#fileID`** | **`#filePath`** -> -> *literal-expression* → **`#line`** | **`#column`** | **`#function`** | **`#dsohandle`** -> > > > *array-literal* → **`[`** *array-literal-items*_?_ **`]`** @@ -697,6 +695,10 @@ make the same change here also. > > *wildcard-expression* → **`_`** +> Grammar of a macro-expansion expression: +> +> *macro-expansion-expression* → **`#`** *identifier* *generic-argument-clause*_?_ *function-call-argument-clause*_?_ *trailing-closures*_?_ + > Grammar of a key-path expression: > > *key-path-expression* → **`\`** *type*_?_ **`.`** *key-path-components* @@ -1063,16 +1065,6 @@ make the same change here also. > > *file-path* → *static-string-literal* -> Grammar of a compile-time diagnostic statement: -> -> *diagnostic-statement* → **`#error`** **`(`** *diagnostic-message* **`)`** -> -> *diagnostic-statement* → **`#warning`** **`(`** *diagnostic-message* **`)`** -> -> -> -> *diagnostic-message* → *static-string-literal* - > Grammar of an availability condition: > > *availability-condition* → **`#available`** **`(`** *availability-arguments* **`)`** @@ -1449,6 +1441,18 @@ make the same change here also. > > *subscript-result* → **`->`** *attributes*_?_ *type* +> Grammar of a macro declaration: +> +> *macro-declaration* → *macro-head* *identifier* *generic-parameter-clause*_?_ *macro-signature* *macro-definition*_?_ *generic-where-clause* +> +> *macro-head* → *attributes*_?_ *declaration-modifiers*_?_ **`macro`** +> +> *macro-signature* → *parameter-clause* *macro-function-signature-result*_?_ +> +> *macro-function-signature-result* → **`->`** *type* +> +> *macro-definition* → **`=`** *expression* + > Grammar of an operator declaration: > > *operator-declaration* → *prefix-operator-declaration* | *postfix-operator-declaration* | *infix-operator-declaration* diff --git a/TSPL.docc/The-Swift-Programming-Language.md b/TSPL.docc/The-Swift-Programming-Language.md index 57e8c0310..3b20a3e2b 100644 --- a/TSPL.docc/The-Swift-Programming-Language.md +++ b/TSPL.docc/The-Swift-Programming-Language.md @@ -38,6 +38,7 @@ - - - +- - - -