Skip to content

Commit efd70b1

Browse files
authored
Prevent silgen for macro expansions with type errors (#81396)
Due to a bug in how macros on nodes imported from clang are evaluated, their function body is not always type checked. This forces type checking before silgen of a macro originating on a node imported from clang, to prevent crashing in silgen. rdar://150940383
1 parent 6bb2b64 commit efd70b1

File tree

8 files changed

+96
-3
lines changed

8 files changed

+96
-3
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,10 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
11511151
/// constructed from a serialized module.
11521152
bool isInMacroExpansionInContext() const;
11531153

1154+
/// Whether this declaration is within a macro expansion relative to
1155+
/// its decl context, and the macro was attached to a node imported from clang.
1156+
bool isInMacroExpansionFromClangHeader() const;
1157+
11541158
/// Returns the appropriate kind of entry point to generate for this class,
11551159
/// based on its attributes.
11561160
///

lib/AST/Decl.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,36 @@ bool Decl::isInMacroExpansionInContext() const {
10201020
return file->getFulfilledMacroRole() != std::nullopt;
10211021
}
10221022

1023+
bool Decl::isInMacroExpansionFromClangHeader() const {
1024+
SourceLoc declLoc = getLoc();
1025+
if (declLoc.isInvalid())
1026+
return false;
1027+
1028+
auto &ctx = getASTContext();
1029+
auto &SourceMgr = ctx.SourceMgr;
1030+
1031+
auto declBufferID = SourceMgr.findBufferContainingLoc(declLoc);
1032+
auto declGeneratedSourceInfo = SourceMgr.getGeneratedSourceInfo(declBufferID);
1033+
if (!declGeneratedSourceInfo)
1034+
return false;
1035+
CustomAttr *attr = declGeneratedSourceInfo->attachedMacroCustomAttr;
1036+
if (!attr)
1037+
return false;
1038+
1039+
SourceLoc macroAttrLoc = attr->AtLoc;
1040+
if (macroAttrLoc.isInvalid())
1041+
return false;
1042+
1043+
auto macroAttrBufferID = SourceMgr.findBufferContainingLoc(macroAttrLoc);
1044+
auto macroAttrGeneratedSourceInfo =
1045+
SourceMgr.getGeneratedSourceInfo(macroAttrBufferID);
1046+
if (!macroAttrGeneratedSourceInfo)
1047+
return false;
1048+
1049+
return macroAttrGeneratedSourceInfo->kind ==
1050+
GeneratedSourceInfo::AttributeFromClang;
1051+
}
1052+
10231053
SourceLoc Decl::getLocFromSource() const {
10241054
switch (getKind()) {
10251055
#define DECL(ID, X) \

lib/SILGen/SILGen.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -689,9 +689,11 @@ static bool shouldEmitFunctionBody(const AbstractFunctionDecl *AFD) {
689689
return false;
690690

691691
auto &ctx = AFD->getASTContext();
692-
if (ctx.TypeCheckerOpts.EnableLazyTypecheck) {
692+
if (ctx.TypeCheckerOpts.EnableLazyTypecheck || AFD->isInMacroExpansionFromClangHeader()) {
693693
// Force the function body to be type-checked and then skip it if there
694-
// have been any errors.
694+
// have been any errors. Normally macro expansions are type checked in the module they
695+
// expand in - this does not apply to swift macros applied to nodes imported from clang,
696+
// so force type checking of them here if they haven't already, to prevent crashing.
695697
(void)AFD->getTypecheckedBody();
696698

697699
// FIXME: Only skip bodies that contain type checking errors.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#if __SWIFT_ATTR_SUPPORTS_MACROS
2+
#define ERROR_MACRO __attribute__((swift_attr("@macro_library.ExpandTypeError")))
3+
#else
4+
#define ERROR_MACRO
5+
#endif
6+
7+
void foo() ERROR_MACRO;

test/Inputs/clang-importer-sdk/usr/include/module.modulemap

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,8 @@ module IncompleteTypes {
156156

157157
module CompletionHandlerGlobals {
158158
header "completion_handler_globals.h"
159-
}
159+
}
160+
161+
module ImportedMacroError {
162+
header "imported_macro_error.h"
163+
}

test/Macros/Inputs/macro_library.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,6 @@ case something
6666

6767
@attached(peer, names: overloaded)
6868
public macro AcceptedDotted(_: Something) = #externalMacro(module: "MacroDefinition", type: "EmptyPeerMacro")
69+
70+
@attached(peer, names: overloaded)
71+
public macro ExpandTypeError() = #externalMacro(module: "MacroDefinition", type: "ExpandTypeErrorMacro")

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,29 @@ public struct AddCompletionHandler: PeerMacro {
12151215
}
12161216
}
12171217

1218+
public struct ExpandTypeErrorMacro: PeerMacro {
1219+
public static func expansion<
1220+
Context: MacroExpansionContext,
1221+
Declaration: DeclSyntaxProtocol
1222+
>(
1223+
of node: AttributeSyntax,
1224+
providingPeersOf declaration: Declaration,
1225+
in context: Context
1226+
) throws -> [DeclSyntax] {
1227+
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
1228+
throw CustomError.message("@ExpandTypeError only works on functions")
1229+
}
1230+
return [
1231+
"""
1232+
public func \(funcDecl.name)(_ bar: Int) {
1233+
callToMissingFunction(foo)
1234+
}
1235+
"""
1236+
]
1237+
}
1238+
}
1239+
1240+
12181241
public struct InvalidMacro: PeerMacro, DeclarationMacro {
12191242
public static func expansion(
12201243
of node: AttributeSyntax,

test/Macros/imported_type_error.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// REQUIRES: swift_swift_parser, executable_test
2+
// REQUIRES: swift_feature_MacrosOnImports
3+
4+
// RUN: %empty-directory(%t)
5+
// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroDefinition) -module-name=MacroDefinition %S/Inputs/syntax_macro_definitions.swift -g -no-toolchain-stdlib-rpath -swift-version 5
6+
7+
// Build the macro library to give us access to ExpandTypeError.
8+
// RUN: %target-swift-frontend -swift-version 5 -emit-module -o %t/macro_library.swiftmodule %S/Inputs/macro_library.swift -module-name macro_library -load-plugin-library %t/%target-library-name(MacroDefinition)
9+
10+
// FIXME: we should typecheck these macro expansions before silgen
11+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify -swift-version 5 -enable-experimental-feature MacrosOnImports -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name ErrorModuleUser %s -I %t
12+
13+
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -swift-version 5 -enable-experimental-feature MacrosOnImports -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name ErrorModuleUser %s -I %t 2>&1 | %FileCheck %s
14+
15+
import ImportedMacroError
16+
import macro_library
17+
18+
foo(42)
19+
20+
// CHECK: error: cannot find 'callToMissingFunction' in scope

0 commit comments

Comments
 (0)