swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67k stars 10.32k forks source link

Generic Macro With Closure Input #70322

Open FlickerSoul opened 7 months ago

FlickerSoul commented 7 months ago

Description

A macro can be generic. The official template stringify uses it.

@freestanding(expression)
public macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(module: "BugMacros", type: "StringifyMacro")

It's natural to create a macro that accepts a closure, for example,

@attached(extension)
public macro bug<T, R>(_ callback: @escaping (T) -> R) = #externalMacro(module: "BugMacros", type: "BugMacro")

The bug extension macro can be applied to a struct

@bug({ (input: Int) -> Int in input * 10 })
struct Test {}

However, the compiler fails to compile the Test struct above.

Reproduction

Create a macro package from xcode template named Bug. This can be reproduced using the 5.9.1 release toolchain or the latest snapshot (2023-12-07) toolchain.

In the BugMacro.swift under the BugMacros module,

import SwiftCompilerPlugin
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

public struct BugMacro: ExtensionMacro {
    public static func expansion(of node: SwiftSyntax.AttributeSyntax, attachedTo declaration: some SwiftSyntax.DeclGroupSyntax, providingExtensionsOf type: some SwiftSyntax.TypeSyntaxProtocol, conformingTo protocols: [SwiftSyntax.TypeSyntax], in context: some SwiftSyntaxMacros.MacroExpansionContext) throws -> [SwiftSyntax.ExtensionDeclSyntax] {
        return []
    }
}

@main
struct BugPlugin: CompilerPlugin {
    let providingMacros: [Macro.Type] = [
        BugMacro.self,
    ]
}

In Bug.swift under the Bug module,

@attached(extension)
public macro bug<T, R>(_ callback: @escaping (T) -> R) = #externalMacro(module: "BugMacros", type: "BugMacro")

In main.swift under the BugClient module,

import Bug

@bug({ (input: Int) -> Int in input * 10 })
struct Test {}

Stack dump

0.  Program arguments: /Library/Developer/Toolchains/swift-5.9.1-RELEASE.xctoolchain/usr/bin/swift-frontend -frontend -emit-module -experimental-skip-non-inlinable-function-bodies-without-types /Users/username/Documents/Developer/Bug/Sources/BugClient/main.swift -target arm64-apple-macos10.15 -Xllvm -aarch64-use-tbi -enable-objc-interop -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk -I /Users/username/Library/Developer/Xcode/DerivedData/Bug-guhkwjponomzltddpdvmfwyoovlh/Build/Products/Debug -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib -F /Users/username/Library/Developer/Xcode/DerivedData/Bug-guhkwjponomzltddpdvmfwyoovlh/Build/Products/Debug/PackageFrameworks -F /Users/username/Library/Developer/Xcode/DerivedData/Bug-guhkwjponomzltddpdvmfwyoovlh/Build/Products/Debug -F /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks -no-color-diagnostics -enable-testing -g -module-cache-path /Users/username/Library/Developer/Xcode/DerivedData/ModuleCache.noindex -profile-generate -profile-coverage-mapping -swift-version 5 -enforce-exclusivity=checked -Onone -D SWIFT_PACKAGE -D DEBUG -D Xcode -external-plugin-path /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/host/plugins#/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-plugin-server -external-plugin-path /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/local/lib/swift/host/plugins#/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-plugin-server -serialize-debugging-options -load-plugin-executable /Users/username/Library/Developer/Xcode/DerivedData/Bug-guhkwjponomzltddpdvmfwyoovlh/Build/Products/Debug/BugMacros#BugMacros -package-name bug -const-gather-protocols-file /Users/username/Library/Developer/Xcode/DerivedData/Bug-guhkwjponomzltddpdvmfwyoovlh/Build/Intermediates.noindex/Bug.build/Debug/BugClient.build/Objects-normal/arm64/BugClient_const_extract_protocols.json -empty-abi-descriptor -validate-clang-modules-once -clang-build-session-file /Users/username/Library/Developer/Xcode/DerivedData/ModuleCache.noindex/Session.modulevalidation -Xcc -working-directory -Xcc /Users/username/Documents/Developer/Bug -resource-dir /Library/Developer/Toolchains/swift-5.9.1-RELEASE.xctoolchain/usr/lib/swift -enable-anonymous-context-mangled-names -Xcc -I/Users/username/Library/Developer/Xcode/DerivedData/Bug-guhkwjponomzltddpdvmfwyoovlh/Build/Products/Debug/include -Xcc -I/Users/username/Library/Developer/Xcode/DerivedData/Bug-guhkwjponomzltddpdvmfwyoovlh/Build/Intermediates.noindex/Bug.build/Debug/BugClient.build/DerivedSources-normal/arm64 -Xcc -I/Users/username/Library/Developer/Xcode/DerivedData/Bug-guhkwjponomzltddpdvmfwyoovlh/Build/Intermediates.noindex/Bug.build/Debug/BugClient.build/DerivedSources/arm64 -Xcc -I/Users/username/Library/Developer/Xcode/DerivedData/Bug-guhkwjponomzltddpdvmfwyoovlh/Build/Intermediates.noindex/Bug.build/Debug/BugClient.build/DerivedSources -Xcc -DSWIFT_PACKAGE -Xcc -DDEBUG=1 -module-name BugClient -disable-clang-spi -target-sdk-version 14.0 -target-sdk-name macosx14.0 -external-plugin-path /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk/usr/lib/swift/host/plugins#/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk/usr/bin/swift-plugin-server -external-plugin-path /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk/usr/local/lib/swift/host/plugins#/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk/usr/bin/swift-plugin-server -external-plugin-path /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib/swift/host/plugins#/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/bin/swift-plugin-server -external-plugin-path /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/local/lib/swift/host/plugins#/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/bin/swift-plugin-server -plugin-path /Library/Developer/Toolchains/swift-5.9.1-RELEASE.xctoolchain/usr/lib/swift/host/plugins -plugin-path /Library/Developer/Toolchains/swift-5.9.1-RELEASE.xctoolchain/usr/local/lib/swift/host/plugins -emit-module-doc-path /Users/username/Library/Developer/Xcode/DerivedData/Bug-guhkwjponomzltddpdvmfwyoovlh/Build/Intermediates.noindex/Bug.build/Debug/BugClient.build/Objects-normal/arm64/BugClient.swiftdoc -emit-module-source-info-path /Users/username/Library/Developer/Xcode/DerivedData/Bug-guhkwjponomzltddpdvmfwyoovlh/Build/Intermediates.noindex/Bug.build/Debug/BugClient.build/Objects-normal/arm64/BugClient.swiftsourceinfo -serialize-diagnostics-path /Users/username/Library/Developer/Xcode/DerivedData/Bug-guhkwjponomzltddpdvmfwyoovlh/Build/Intermediates.noindex/Bug.build/Debug/BugClient.build/Objects-normal/arm64/BugClient-master-emit-module.dia -emit-dependencies-path /Users/username/Library/Developer/Xcode/DerivedData/Bug-guhkwjponomzltddpdvmfwyoovlh/Build/Intermediates.noindex/Bug.build/Debug/BugClient.build/Objects-normal/arm64/BugClient-master-emit-module.d -o /Users/username/Library/Developer/Xcode/DerivedData/Bug-guhkwjponomzltddpdvmfwyoovlh/Build/Intermediates.noindex/Bug.build/Debug/BugClient.build/Objects-normal/arm64/BugClient.swiftmodule -emit-abi-descriptor-path /Users/username/Library/Developer/Xcode/DerivedData/Bug-guhkwjponomzltddpdvmfwyoovlh/Build/Intermediates.noindex/Bug.build/Debug/BugClient.build/Objects-normal/arm64/BugClient.abi.json
1.  Apple Swift version 5.9.1 (swift-5.9.1-RELEASE)
2.  Compiling with the current language version
3.  While evaluating request TypeCheckSourceFileRequest(source_file "/Users/username/Documents/Developer/Bug/Sources/BugClient/main.swift")
4.  While evaluating request ExpandPeerMacroRequest(BugClient.(file).Test@/Users/username/Documents/Developer/Bug/Sources/BugClient/main.swift:4:8)
5.  While evaluating request ResolveMacroRequest(custom-attr, 0x13f8389b0 FileUnit file="/Users/username/Documents/Developer/Bug/Sources/BugClient/main.swift")
6.  While type-checking expression at [/Users/username/Documents/Developer/Bug/Sources/BugClient/main.swift:3:1 - line:3:43] RangeText="@bug({ (input: Int) -> Int in input * 10 }"
7.  While type-checking-target starting at /Users/username/Documents/Developer/Bug/Sources/BugClient/main.swift:3:1
8.  While evaluating request FragileFunctionKindRequest(0x13f843b80 AbstractClosureExpr line=3 : (Int) -> Int)
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0  swift-frontend           0x0000000104fabce4 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 56
1  swift-frontend           0x0000000104fab088 llvm::sys::RunSignalHandlers() + 112
2  swift-frontend           0x0000000104fac324 SignalHandler(int) + 304
3  libsystem_platform.dylib 0x00000001848d7a24 _sigtramp + 56
4  swift-frontend           0x00000001017ba4fc swift::FragileFunctionKindRequest::evaluate(swift::Evaluator&, swift::DeclContext*) const + 756
5  swift-frontend           0x00000001017ba4fc swift::FragileFunctionKindRequest::evaluate(swift::Evaluator&, swift::DeclContext*) const + 756
6  swift-frontend           0x00000001017be5b4 llvm::Expected<swift::FragileFunctionKindRequest::OutputType> swift::Evaluator::getResultUncached<swift::FragileFunctionKindRequest>(swift::FragileFunctionKindRequest const&) + 376
7  swift-frontend           0x00000001017be2b8 llvm::Expected<swift::FragileFunctionKindRequest::OutputType> swift::Evaluator::getResultCached<swift::FragileFunctionKindRequest, (void*)0>(swift::FragileFunctionKindRequest const&) + 384
8  swift-frontend           0x00000001017ba114 swift::FragileFunctionKindRequest::OutputType swift::evaluateOrDefault<swift::FragileFunctionKindRequest>(swift::Evaluator&, swift::FragileFunctionKindRequest, swift::FragileFunctionKindRequest::OutputType) + 52
9  swift-frontend           0x00000001012ea524 swift::ExportContext::forFunctionBody(swift::DeclContext*, swift::SourceLoc) + 52
10 swift-frontend           0x00000001012f5dd0 (anonymous namespace)::ExprAvailabilityWalker::walkToExprPre(swift::Expr*) + 2524
11 swift-frontend           0x00000001016710f8 (anonymous namespace)::Traversal::doIt(swift::Expr*) + 40
12 swift-frontend           0x0000000101675338 (anonymous namespace)::Traversal::visit(swift::ArgumentList*) + 80
13 swift-frontend           0x00000001016719ec (anonymous namespace)::Traversal::doIt(swift::ArgumentList*) + 100
14 swift-frontend           0x00000001016727d0 swift::ASTVisitor<(anonymous namespace)::Traversal, swift::Expr*, swift::Stmt*, bool, swift::Pattern*, bool, void>::visit(swift::Expr*) + 3424
15 swift-frontend           0x000000010167114c (anonymous namespace)::Traversal::doIt(swift::Expr*) + 124
16 swift-frontend           0x00000001016710c4 swift::Expr::walk(swift::ASTWalker&) + 32
17 swift-frontend           0x00000001012f0978 swift::diagnoseExprAvailability(swift::Expr const*, swift::DeclContext*) + 160
18 swift-frontend           0x00000001012923c0 swift::performSyntacticExprDiagnostics(swift::Expr const*, swift::DeclContext const*, bool, bool) + 1524
19 swift-frontend           0x000000010131b640 swift::TypeChecker::typeCheckTarget(swift::constraints::SyntacticElementTarget&, swift::OptionSet<swift::TypeCheckExprFlags, unsigned int>) + 436
20 swift-frontend           0x000000010131b434 swift::TypeChecker::typeCheckExpression(swift::constraints::SyntacticElementTarget&, swift::OptionSet<swift::TypeCheckExprFlags, unsigned int>) + 184
21 swift-frontend           0x000000010131b2f4 swift::TypeChecker::typeCheckExpression(swift::Expr*&, swift::DeclContext*, swift::constraints::ContextualTypeInfo, swift::OptionSet<swift::TypeCheckExprFlags, unsigned int>) + 84
22 swift-frontend           0x000000010138c6fc swift::ResolveMacroRequest::evaluate(swift::Evaluator&, swift::UnresolvedMacroReference, swift::DeclContext*) const + 720
23 swift-frontend           0x00000001016f182c llvm::Expected<swift::ResolveMacroRequest::OutputType> swift::Evaluator::getResultUncached<swift::ResolveMacroRequest>(swift::ResolveMacroRequest const&) + 380
24 swift-frontend           0x00000001016f1574 llvm::Expected<swift::ResolveMacroRequest::OutputType> swift::Evaluator::getResultCached<swift::ResolveMacroRequest, (void*)0>(swift::ResolveMacroRequest const&) + 452
25 swift-frontend           0x00000001016be948 swift::ResolveMacroRequest::OutputType swift::evaluateOrDefault<swift::ResolveMacroRequest>(swift::Evaluator&, swift::ResolveMacroRequest, swift::ResolveMacroRequest::OutputType) + 56
26 swift-frontend           0x00000001016be868 swift::Decl::getResolvedMacro(swift::CustomAttr*) const + 132
27 swift-frontend           0x00000001016be750 swift::Decl::forEachAttachedMacro(swift::MacroRole, llvm::function_ref<void (swift::CustomAttr*, swift::MacroDecl*)>) const + 188
28 swift-frontend           0x00000001013894dc swift::ExpandPeerMacroRequest::evaluate(swift::Evaluator&, swift::Decl*) const + 92
29 swift-frontend           0x00000001016ed148 llvm::Expected<swift::ExpandPeerMacroRequest::OutputType> swift::Evaluator::getResultUncached<swift::ExpandPeerMacroRequest>(swift::ExpandPeerMacroRequest const&) + 384
30 swift-frontend           0x00000001016ece48 llvm::Expected<swift::ExpandPeerMacroRequest::OutputType> swift::Evaluator::getResultCached<swift::ExpandPeerMacroRequest, (void*)0>(swift::ExpandPeerMacroRequest const&) + 376
31 swift-frontend           0x00000001016be474 swift::ExpandPeerMacroRequest::OutputType swift::evaluateOrDefault<swift::ExpandPeerMacroRequest>(swift::Evaluator&, swift::ExpandPeerMacroRequest, swift::ExpandPeerMacroRequest::OutputType) + 56
32 swift-frontend           0x00000001016be2c4 swift::Decl::visitAuxiliaryDecls(llvm::function_ref<void (swift::Decl*)>, bool) const + 152
33 swift-frontend           0x000000010135bfa0 (anonymous namespace)::DeclChecker::visit(swift::Decl*) + 132
34 swift-frontend           0x000000010135bf04 swift::TypeChecker::typeCheckDecl(swift::Decl*, bool) + 140
35 swift-frontend           0x0000000101430798 swift::TypeCheckSourceFileRequest::evaluate(swift::Evaluator&, swift::SourceFile*) const + 164
36 swift-frontend           0x0000000101432b88 llvm::Expected<swift::TypeCheckSourceFileRequest::OutputType> swift::Evaluator::getResultUncached<swift::TypeCheckSourceFileRequest>(swift::TypeCheckSourceFileRequest const&) + 388
37 swift-frontend           0x0000000101432918 llvm::Expected<swift::TypeCheckSourceFileRequest::OutputType> swift::Evaluator::getResultCached<swift::TypeCheckSourceFileRequest, (void*)0>(swift::TypeCheckSourceFileRequest const&) + 84
38 swift-frontend           0x000000010143060c swift::TypeCheckSourceFileRequest::OutputType swift::evaluateOrDefault<swift::TypeCheckSourceFileRequest>(swift::Evaluator&, swift::TypeCheckSourceFileRequest, swift::TypeCheckSourceFileRequest::OutputType) + 44
39 swift-frontend           0x0000000100498238 bool llvm::function_ref<bool (swift::SourceFile&)>::callback_fn<swift::CompilerInstance::performSema()::$_7>(long, swift::SourceFile&) + 16
40 swift-frontend           0x0000000100492ef4 swift::CompilerInstance::forEachFileToTypeCheck(llvm::function_ref<bool (swift::SourceFile&)>) + 160
41 swift-frontend           0x0000000100492e34 swift::CompilerInstance::performSema() + 76
42 swift-frontend           0x000000010030fe00 withSemanticAnalysis(swift::CompilerInstance&, swift::FrontendObserver*, llvm::function_ref<bool (swift::CompilerInstance&)>, bool) + 60
43 swift-frontend           0x0000000100302a58 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 2848
44 swift-frontend           0x0000000100187a60 swift::mainEntry(int, char const**) + 3220
45 dyld                     0x000000018452d0e0 start + 2360

Expected behavior

The program should compile?

If the closure is extract out as the following, the program compiles

import Bug

let lambda = { (input: Int) -> Int in input * 10 }

@bug(lambda)
struct Test {}

Environment

Apple Swift version 5.9.1 (swift-5.9.1-RELEASE)
Target: arm64-apple-macosx14.0
Apple Swift version 5.11-dev (LLVM 91cd37b9110872c, Swift 703a4719b0c6f3a)
Target: arm64-apple-macosx14.0

Additional information

No response

FlickerSoul commented 7 months ago

In addition, if the macro is applied in the following way

enum Test {
    @bug({ (input: Int) -> Int in input * 10 })
    case test
}

Xcode prompts that input in input * 10 cannot be found.

image

If the bug macro were not generic parametrized, anonymous arguments will work, for example { $0 * 10 }. But if the function arguments are generic, compilers cannot infer the type of the arguments, thus leading us annotate the types explicitly. And in this situation, xcode informs the problem above.

And again, if the closure is extracted, the program compiles

let lambda = { (input: Int) -> Int in input * 10 }

enum Test {
    @bug(lambda)
    case test
}