mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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
This commit is contained in:
@@ -1151,6 +1151,10 @@ public:
|
||||
/// constructed from a serialized module.
|
||||
bool isInMacroExpansionInContext() const;
|
||||
|
||||
/// Whether this declaration is within a macro expansion relative to
|
||||
/// its decl context, and the macro was attached to a node imported from clang.
|
||||
bool isInMacroExpansionFromClangHeader() const;
|
||||
|
||||
/// Returns the appropriate kind of entry point to generate for this class,
|
||||
/// based on its attributes.
|
||||
///
|
||||
|
||||
@@ -1020,6 +1020,36 @@ bool Decl::isInMacroExpansionInContext() const {
|
||||
return file->getFulfilledMacroRole() != std::nullopt;
|
||||
}
|
||||
|
||||
bool Decl::isInMacroExpansionFromClangHeader() const {
|
||||
SourceLoc declLoc = getLoc();
|
||||
if (declLoc.isInvalid())
|
||||
return false;
|
||||
|
||||
auto &ctx = getASTContext();
|
||||
auto &SourceMgr = ctx.SourceMgr;
|
||||
|
||||
auto declBufferID = SourceMgr.findBufferContainingLoc(declLoc);
|
||||
auto declGeneratedSourceInfo = SourceMgr.getGeneratedSourceInfo(declBufferID);
|
||||
if (!declGeneratedSourceInfo)
|
||||
return false;
|
||||
CustomAttr *attr = declGeneratedSourceInfo->attachedMacroCustomAttr;
|
||||
if (!attr)
|
||||
return false;
|
||||
|
||||
SourceLoc macroAttrLoc = attr->AtLoc;
|
||||
if (macroAttrLoc.isInvalid())
|
||||
return false;
|
||||
|
||||
auto macroAttrBufferID = SourceMgr.findBufferContainingLoc(macroAttrLoc);
|
||||
auto macroAttrGeneratedSourceInfo =
|
||||
SourceMgr.getGeneratedSourceInfo(macroAttrBufferID);
|
||||
if (!macroAttrGeneratedSourceInfo)
|
||||
return false;
|
||||
|
||||
return macroAttrGeneratedSourceInfo->kind ==
|
||||
GeneratedSourceInfo::AttributeFromClang;
|
||||
}
|
||||
|
||||
SourceLoc Decl::getLocFromSource() const {
|
||||
switch (getKind()) {
|
||||
#define DECL(ID, X) \
|
||||
|
||||
@@ -689,9 +689,11 @@ static bool shouldEmitFunctionBody(const AbstractFunctionDecl *AFD) {
|
||||
return false;
|
||||
|
||||
auto &ctx = AFD->getASTContext();
|
||||
if (ctx.TypeCheckerOpts.EnableLazyTypecheck) {
|
||||
if (ctx.TypeCheckerOpts.EnableLazyTypecheck || AFD->isInMacroExpansionFromClangHeader()) {
|
||||
// Force the function body to be type-checked and then skip it if there
|
||||
// have been any errors.
|
||||
// have been any errors. Normally macro expansions are type checked in the module they
|
||||
// expand in - this does not apply to swift macros applied to nodes imported from clang,
|
||||
// so force type checking of them here if they haven't already, to prevent crashing.
|
||||
(void)AFD->getTypecheckedBody();
|
||||
|
||||
// FIXME: Only skip bodies that contain type checking errors.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#if __SWIFT_ATTR_SUPPORTS_MACROS
|
||||
#define ERROR_MACRO __attribute__((swift_attr("@macro_library.ExpandTypeError")))
|
||||
#else
|
||||
#define ERROR_MACRO
|
||||
#endif
|
||||
|
||||
void foo() ERROR_MACRO;
|
||||
@@ -156,4 +156,8 @@ module IncompleteTypes {
|
||||
|
||||
module CompletionHandlerGlobals {
|
||||
header "completion_handler_globals.h"
|
||||
}
|
||||
}
|
||||
|
||||
module ImportedMacroError {
|
||||
header "imported_macro_error.h"
|
||||
}
|
||||
|
||||
@@ -66,3 +66,6 @@ case something
|
||||
|
||||
@attached(peer, names: overloaded)
|
||||
public macro AcceptedDotted(_: Something) = #externalMacro(module: "MacroDefinition", type: "EmptyPeerMacro")
|
||||
|
||||
@attached(peer, names: overloaded)
|
||||
public macro ExpandTypeError() = #externalMacro(module: "MacroDefinition", type: "ExpandTypeErrorMacro")
|
||||
|
||||
@@ -1215,6 +1215,29 @@ public struct AddCompletionHandler: PeerMacro {
|
||||
}
|
||||
}
|
||||
|
||||
public struct ExpandTypeErrorMacro: PeerMacro {
|
||||
public static func expansion<
|
||||
Context: MacroExpansionContext,
|
||||
Declaration: DeclSyntaxProtocol
|
||||
>(
|
||||
of node: AttributeSyntax,
|
||||
providingPeersOf declaration: Declaration,
|
||||
in context: Context
|
||||
) throws -> [DeclSyntax] {
|
||||
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
|
||||
throw CustomError.message("@ExpandTypeError only works on functions")
|
||||
}
|
||||
return [
|
||||
"""
|
||||
public func \(funcDecl.name)(_ bar: Int) {
|
||||
callToMissingFunction(foo)
|
||||
}
|
||||
"""
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct InvalidMacro: PeerMacro, DeclarationMacro {
|
||||
public static func expansion(
|
||||
of node: AttributeSyntax,
|
||||
|
||||
20
test/Macros/imported_type_error.swift
Normal file
20
test/Macros/imported_type_error.swift
Normal file
@@ -0,0 +1,20 @@
|
||||
// REQUIRES: swift_swift_parser, executable_test
|
||||
// REQUIRES: swift_feature_MacrosOnImports
|
||||
|
||||
// RUN: %empty-directory(%t)
|
||||
// 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
|
||||
|
||||
// Build the macro library to give us access to ExpandTypeError.
|
||||
// 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)
|
||||
|
||||
// FIXME: we should typecheck these macro expansions before silgen
|
||||
// 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
|
||||
|
||||
// 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
|
||||
|
||||
import ImportedMacroError
|
||||
import macro_library
|
||||
|
||||
foo(42)
|
||||
|
||||
// CHECK: error: cannot find 'callToMissingFunction' in scope
|
||||
Reference in New Issue
Block a user