Are `#if XXX #endif` clauses supposed to work in decl macros?

The compiler crashes when expanding the following declaration macro:

return [
    DeclSyntax(
        IfConfigDeclSyntax(clauses: [
            IfConfigClauseSyntax(poundKeyword: "#if").with(\.condition, "true")
        ])
    )
]

with

backtrace
Please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the crash backtrace.
Stack dump:
0.	Program arguments: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-frontend -frontend -c -primary-file /Users/wtedst/MockMacros/Sources/MockMacrosClient/main.swift -emit-dependencies-path /Users/wtedst/Library/Developer/Xcode/DerivedData/MockMacros-erdmzxuytwylzrfrnknoladxkzko/Build/Intermediates.noindex/MockMacros.build/Debug/MockMacrosClient.build/Objects-normal/arm64/main.d -emit-const-values-path /Users/wtedst/Library/Developer/Xcode/DerivedData/MockMacros-erdmzxuytwylzrfrnknoladxkzko/Build/Intermediates.noindex/MockMacros.build/Debug/MockMacrosClient.build/Objects-normal/arm64/main.swiftconstvalues -emit-reference-dependencies-path /Users/wtedst/Library/Developer/Xcode/DerivedData/MockMacros-erdmzxuytwylzrfrnknoladxkzko/Build/Intermediates.noindex/MockMacros.build/Debug/MockMacrosClient.build/Objects-normal/arm64/main.swiftdeps -serialize-diagnostics-path /Users/wtedst/Library/Developer/Xcode/DerivedData/MockMacros-erdmzxuytwylzrfrnknoladxkzko/Build/Intermediates.noindex/MockMacros.build/Debug/MockMacrosClient.build/Objects-normal/arm64/main.dia -target arm64-apple-macos10.15 -Xllvm -aarch64-use-tbi -enable-objc-interop -stack-check -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.5.sdk -I /Users/wtedst/Library/Developer/Xcode/DerivedData/MockMacros-erdmzxuytwylzrfrnknoladxkzko/Build/Products/Debug -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib -F /Users/wtedst/Library/Developer/Xcode/DerivedData/MockMacros-erdmzxuytwylzrfrnknoladxkzko/Build/Products/Debug/PackageFrameworks -F /Users/wtedst/Library/Developer/Xcode/DerivedData/MockMacros-erdmzxuytwylzrfrnknoladxkzko/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/wtedst/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 -serialize-debugging-options -load-plugin-executable /Users/wtedst/Library/Developer/Xcode/DerivedData/MockMacros-erdmzxuytwylzrfrnknoladxkzko/Build/Products/Debug/MockMacrosMacros#MockMacrosMacros -package-name mockmacros -const-gather-protocols-file /Users/wtedst/Library/Developer/Xcode/DerivedData/MockMacros-erdmzxuytwylzrfrnknoladxkzko/Build/Intermediates.noindex/MockMacros.build/Debug/MockMacrosClient.build/Objects-normal/arm64/MockMacrosClient_const_extract_protocols.json -empty-abi-descriptor -validate-clang-modules-once -clang-build-session-file /Users/wtedst/Library/Developer/Xcode/DerivedData/ModuleCache.noindex/Session.modulevalidation -Xcc -working-directory -Xcc /Users/wtedst/MockMacros -resource-dir /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift -enable-anonymous-context-mangled-names -Xcc -ivfsstatcache -Xcc /Users/wtedst/Library/Developer/Xcode/DerivedData/SDKStatCaches.noindex/macosx14.5-23F73-7e3fd6dc4171e65dbd154ba8db3956d2.sdkstatcache -Xcc -I/Users/wtedst/Library/Developer/Xcode/DerivedData/MockMacros-erdmzxuytwylzrfrnknoladxkzko/Build/Products/Debug/include -Xcc -I/Users/wtedst/Library/Developer/Xcode/DerivedData/MockMacros-erdmzxuytwylzrfrnknoladxkzko/Build/Intermediates.noindex/MockMacros.build/Debug/MockMacrosClient.build/DerivedSources-normal/arm64 -Xcc -I/Users/wtedst/Library/Developer/Xcode/DerivedData/MockMacros-erdmzxuytwylzrfrnknoladxkzko/Build/Intermediates.noindex/MockMacros.build/Debug/MockMacrosClient.build/DerivedSources/arm64 -Xcc -I/Users/wtedst/Library/Developer/Xcode/DerivedData/MockMacros-erdmzxuytwylzrfrnknoladxkzko/Build/Intermediates.noindex/MockMacros.build/Debug/MockMacrosClient.build/DerivedSources -Xcc -DSWIFT_PACKAGE -Xcc -DDEBUG=1 -module-name MockMacrosClient -frontend-parseable-output -disable-clang-spi -target-sdk-version 14.5 -target-sdk-name macosx14.5 -external-plugin-path /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.5.sdk/usr/lib/swift/host/plugins#/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.5.sdk/usr/bin/swift-plugin-server -external-plugin-path /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.5.sdk/usr/local/lib/swift/host/plugins#/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.5.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 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/host/plugins -plugin-path /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/local/lib/swift/host/plugins -o /Users/wtedst/Library/Developer/Xcode/DerivedData/MockMacros-erdmzxuytwylzrfrnknoladxkzko/Build/Intermediates.noindex/MockMacros.build/Debug/MockMacrosClient.build/Objects-normal/arm64/main.o -index-unit-output-path /MockMacros.build/Debug/MockMacrosClient.build/Objects-normal/arm64/main.o -index-store-path /Users/wtedst/Library/Developer/Xcode/DerivedData/MockMacros-erdmzxuytwylzrfrnknoladxkzko/Index.noindex/DataStore -index-system-modules
1.	Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
2.	Compiling with the current language version
3.	While evaluating request ASTLoweringRequest(Lowering AST to SIL for file "/Users/wtedst/MockMacros/Sources/MockMacrosClient/main.swift")
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           0x00000001061fff3c llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 56
1  swift-frontend           0x00000001061ff0f8 llvm::sys::RunSignalHandlers() + 112
2  swift-frontend           0x0000000106200544 SignalHandler(int) + 360
3  libsystem_platform.dylib 0x000000018436da24 _sigtramp + 56
4  swift-frontend           0x000000010231b638 swift::MacroExpansionDecl::forEachExpandedNode(llvm::function_ref<void (swift::ASTNode)>) const + 764
5  swift-frontend           0x000000010231b638 swift::MacroExpansionDecl::forEachExpandedNode(llvm::function_ref<void (swift::ASTNode)>) const + 764
6  swift-frontend           0x00000001011bd54c (anonymous namespace)::RValueEmitter::visitMacroExpansionExpr(swift::MacroExpansionExpr*, swift::Lowering::SGFContext) + 344
7  swift-frontend           0x000000010119b618 swift::Lowering::SILGenFunction::emitIgnoredExpr(swift::Expr*) + 892
8  swift-frontend           0x000000010123f970 swift::ASTVisitor<swift::Lowering::SILGenTopLevel, void, void, void, void, void, void>::visit(swift::Decl*) + 1860
9  swift-frontend           0x000000010123e5e0 swift::Lowering::SILGenModule::emitEntryPoint(swift::SourceFile*, swift::SILFunction*) + 1180
10 swift-frontend           0x0000000101122a48 swift::ASTLoweringRequest::evaluate(swift::Evaluator&, swift::ASTLoweringDescriptor) const + 1400
11 swift-frontend           0x0000000101231f1c swift::SimpleRequest<swift::ASTLoweringRequest, std::__1::unique_ptr<swift::SILModule, std::__1::default_delete<swift::SILModule>> (swift::ASTLoweringDescriptor), (swift::RequestFlags)9>::evaluateRequest(swift::ASTLoweringRequest const&, swift::Evaluator&) + 196
12 swift-frontend           0x0000000101125f44 llvm::Expected<swift::ASTLoweringRequest::OutputType> swift::Evaluator::getResultUncached<swift::ASTLoweringRequest>(swift::ASTLoweringRequest const&) + 584
13 swift-frontend           0x0000000100afd408 swift::performCompileStepsPostSema(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 548
14 swift-frontend           0x0000000100b01694 performCompile(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 1448
15 swift-frontend           0x0000000100aff6d0 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 4968
16 swift-frontend           0x0000000100a8ee8c swift::mainEntry(int, char const**) + 2612
17 dyld                     0x0000000183fbd0e0 start + 2360
LLVM Profile Error: Failed to write file "default.profraw": Operation not permitted
Command SwiftCompile failed with a nonzero exit code

The question is, however, is whether macros are allowed to produce #if XXX #endif blocks in principle; I haven't seen such being mentioned in proposals or in the macros test suite in the compiler (although I might have well missed something).

The original use case is to emit blocks like this:

#if TEST
typealias MyObj = MockObj
#else
typealias MyObj = RealObj
#endif

(and derive MockObj itself) to avoid "protocols of doom" that only exist to support swapping a dependency in tests.

Hi there,

swift-spyable is using a preprocessor flag to hide the declaration behind it, and it works as expected.
You can find the implementation here:

I believe the problem could be with the poundKeyword. If I'm not mistaken, you should not use string interpolation to create keywords. Try using the static method poundIfToken.

1 Like