- Notifications
You must be signed in to change notification settings - Fork 470
Description
Issue
Swift's concurrency features cannot be fully utilized in macros without being able to implement expansion function requirements of macro protocols as async functions.
Cause
While the Swift Evolution proposals for expression and attached macros state that the expansion function requirements of macro protocols should be async, the macro protocol declarations in swift-syntax are missing the async keyword from their expansion function requirements (see comparison below), making it impossible to implement them as async functions.
Solution
Changing the declarations in swift-syntax to async shouldn't break existing macros, as async function requirements can be implemented as non-async functions.
Example
protocolMacro{func expansion()}structSyncMacro:Macro{func expansion(){} // OK ✅ }structAsyncMacro:Macro{func expansion()async{} // ERROR ❌ }protocolMacro{func expansion()async}structSyncMacro:Macro{func expansion(){} // OK ✅ }structAsyncMacro:Macro{func expansion()async{} // OK ✅ }I acknowledge that additional changes would have to be made within the swift-syntax package (changes at the expansion function call sites and possible further structural changes to facilitate asynchronous code), but depending on how they are implemented, the changes don't have to affect the user-facing parts of the package in a source breaking way.
Differences between Swift Evolution proposals and swift-syntax
ExpressionMacro
publicprotocolExpressionMacro:FreestandingMacro{ /// Expand a macro described by the given freestanding macro expansion /// within the given context to produce a replacement expression. staticfunc expansion( of node:someFreestandingMacroExpansionSyntax, in context:someMacroExpansionContext)asyncthrows->ExprSyntax}swift-syntax
swift-syntax/Sources/SwiftSyntaxMacros/MacroProtocols/ExpressionMacro.swift
Lines 20 to 27 in 0b324f8
| publicprotocolExpressionMacro:FreestandingMacro{ | |
| /// Expand a macro described by the given freestanding macro expansion | |
| /// within the given context to produce a replacement expression. | |
| staticfunc expansion( | |
| of node:someFreestandingMacroExpansionSyntax, | |
| in context:someMacroExpansionContext | |
| )throws->ExprSyntax | |
| } |
PeerMacro
public PeerMacro: AttachedMacro { /// Expand a macro described by the given attribute to /// produce "peer" declarations of the declaration to which it /// is attached. /// /// The macro expansion can introduce "peer" declarations that /// go alongside the given declaration. static func expansion( of node: AttributeSyntax, providingPeersOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext )async throws ->[DeclSyntax]}swift-syntax
swift-syntax/Sources/SwiftSyntaxMacros/MacroProtocols/PeerMacro.swift
Lines 17 to 29 in 0b324f8
| publicprotocolPeerMacro:AttachedMacro{ | |
| /// Expand a macro described by the given custom attribute and | |
| /// attached to the given declaration and evaluated within a | |
| /// particular expansion context. | |
| /// | |
| /// The macro expansion can introduce "peer" declarations that sit alongside | |
| /// the given declaration. | |
| staticfunc expansion( | |
| of node:AttributeSyntax, | |
| providingPeersOf declaration:someDeclSyntaxProtocol, | |
| in context:someMacroExpansionContext | |
| )throws->[DeclSyntax] | |
| } |
MemberMacro
protocolMemberMacro:AttachedMacro{ /// Expand a macro described by the given attribute to /// produce additional members of the given declaration to which /// the attribute is attached. staticfunc expansion( of node:AttributeSyntax, providingMembersOf declaration:someDeclGroupSyntax, in context:someMacroExpansionContext)asyncthrows->[DeclSyntax]}swift-syntax
| publicprotocolMemberMacro:AttachedMacro{ | |
| /// Expand an attached declaration macro to produce a set of members. | |
| /// | |
| /// - Parameters: | |
| /// - node: The custom attribute describing the attached macro. | |
| /// - declaration: The declaration the macro attribute is attached to. | |
| /// - context: The context in which to perform the macro expansion. | |
| /// | |
| /// - Returns: the set of member declarations introduced by this macro, which | |
| /// are nested inside the `attachedTo` declaration. | |
| /// | |
| /// - Warning: This is the legacy `expansion` function of `MemberMacro` that is provided for backwards-compatiblity. | |
| /// Use ``expansion(of:providingMembersOf:conformingTo:in:)-1sxoe`` instead. | |
| staticfunc expansion( | |
| of node:AttributeSyntax, | |
| providingMembersOf declaration:someDeclGroupSyntax, | |
| in context:someMacroExpansionContext | |
| )throws->[DeclSyntax] | |
| /// Expand an attached declaration macro to produce a set of members. | |
| /// | |
| /// - Parameters: | |
| /// - node: The custom attribute describing the attached macro. | |
| /// - declaration: The declaration the macro attribute is attached to. | |
| /// - conformingTo: The set of protocols that were declared | |
| /// in the set of conformances for the macro and to which the declaration | |
| /// does not explicitly conform. The member macro itself cannot declare | |
| /// conformances to these protocols (only an extension macro can do that), | |
| /// but can provide supporting declarations, such as a required | |
| /// initializer or stored property, that cannot be written in an | |
| /// extension. | |
| /// - context: The context in which to perform the macro expansion. | |
| /// | |
| /// - Returns: the set of member declarations introduced by this macro, which | |
| /// are nested inside the `attachedTo` declaration. | |
| staticfunc expansion( | |
| of node:AttributeSyntax, | |
| providingMembersOf declaration:someDeclGroupSyntax, | |
| conformingTo protocols:[TypeSyntax], | |
| in context:someMacroExpansionContext | |
| )throws->[DeclSyntax] | |
| } | |
| privatestructUnimplementedExpansionMethodError:Error,CustomStringConvertible{ | |
| vardescription:String{ | |
| """ | |
| Types conforming to `MemberMacro` must implement either \ | |
| expansion(of:providingMembersOf:in:) or \ | |
| expansion(of:providingMembersOf:conformingTo:in:) | |
| """ | |
| } | |
| } |
AccessorMacro
protocolAccessorMacro:AttachedMacro{ /// Expand a macro described by the given attribute to /// produce accessors for the given declaration to which /// the attribute is attached. staticfunc expansion( of node:AttributeSyntax, providingAccessorsOf declaration:someDeclSyntaxProtocol, in context:someMacroExpansionContext)asyncthrows->[AccessorDeclSyntax]}swift-syntax
| publicprotocolAccessorMacro:AttachedMacro{ | |
| /// Expand a macro that's expressed as a custom attribute attached to | |
| /// the given declaration. The result is a set of accessors for the | |
| /// declaration. | |
| staticfunc expansion( | |
| of node:AttributeSyntax, | |
| providingAccessorsOf declaration:someDeclSyntaxProtocol, | |
| in context:someMacroExpansionContext | |
| )throws->[AccessorDeclSyntax] | |
| } |
MemberAttributeMacro
protocolMemberAttributeMacro:AttachedMacro{ /// Expand a macro described by the given custom attribute to /// produce additional attributes for the members of the type. staticfunc expansion( of node:AttributeSyntax, attachedTo declaration:someDeclGroupSyntax, providingAttributesOf member:someDeclSyntaxProtocol, in context:someMacroExpansionContext)asyncthrows->[AttributeSyntax]}swift-syntax
swift-syntax/Sources/SwiftSyntaxMacros/MacroProtocols/MemberAttributeMacro.swift
Lines 21 to 38 in 0b324f8
| publicprotocolMemberAttributeMacro:AttachedMacro{ | |
| /// Expand an attached declaration macro to produce an attribute list for | |
| /// a given member. | |
| /// | |
| /// - Parameters: | |
| /// - node: The custom attribute describing the attached macro. | |
| /// - declaration: The declaration the macro attribute is attached to. | |
| /// - member: The member declaration to attach the resulting attributes to. | |
| /// - context: The context in which to perform the macro expansion. | |
| /// | |
| /// - Returns: the set of attributes to apply to the given member. | |
| staticfunc expansion( | |
| of node:AttributeSyntax, | |
| attachedTo declaration:someDeclGroupSyntax, | |
| providingAttributesFor member:someDeclSyntaxProtocol, | |
| in context:someMacroExpansionContext | |
| )throws->[AttributeSyntax] | |
| } |
Additional quotes from Expression Macros proposal
* Make the
ExpressionMacro.expansion(of:in:)requirementasync.