Constant declaration generated from Swift Macro not available in scope

I have a macro that generates constant declarations, but those declarations seem not to be available in the code following the macro.

The following code demonstrates the issue:

@freestanding(declaration, names: arbitrary)
public macro constantInt(name: String) = #externalMacro(module: "AgentMacroMacros", type: "ConstantIntMacro")

// just checking if arbitrary identifiers has anything to do with it
@freestanding(declaration, names: named(intValue))
public macro constantIntValue() = #externalMacro(module: "AgentMacroMacros", type: "ConstantIntValueMacro")

The definitions:

public enum ConstantIntMacro : DeclarationMacro {
    public static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext) throws -> [DeclSyntax]
    {
        let str = node.argumentList.first!.expression.as(StringLiteralExprSyntax.self)!
        // assume no interpolations for simplicity
        guard case .stringSegment(let strSyntax) = str.segments.first else { fatalError() }
        return ["let \(raw: strSyntax.content.text) = 0"]
    }
}

public enum ConstantIntValueMacro : DeclarationMacro {
    public static func expansion(of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) throws -> [DeclSyntax] {
        ["let intValue = 0"]
    }
}

Client code:

func demo() {
    #constantIntValue
    #constantInt(name: "someInt")
    intValue // error
    someInt // error
}

Expanding the macros gives me this:
Screenshot 2024-04-11 at 8.49.41 AM

I'm wondering if constant declarations cannot be used in this manner, or if this is a bug.

Hmm… what happens when you use the declaration macro at the top level (outside the scope of the function). Same problem?

Have you tried building from the latest swift-syntax commit from main? Have you tried building from the latest Swift toolchain (from main)?

It seems like declaring local variables with a macro is indeed not supported. I filed Declaration macros can’t introduce local variables · Issue #72968 · apple/swift · GitHub for it.

3 Likes

Update: Declaration macros not being able to introduce local variables is intended, as described here: swift-evolution/proposals/0389-attached-macros.md at main · apple/swift-evolution · GitHub

Therefore, a macro used within a closure or function body can only introduce declarations using names produced by createUniqueName . This maintains the two-phase of checking macros where type checking and inference is performed without expanding the macro, then the macro is expanded and its result type-checked independently, with no ability to influence type inference further.

4 Likes