Skip to content

Prevent silgen for macro expansions with type errors (#81396) #81409

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,10 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
/// 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.
///
Expand Down
30 changes: 30 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,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) \
Expand Down
6 changes: 4 additions & 2 deletions lib/SILGen/SILGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,8 @@ module IncompleteTypes {

module CompletionHandlerGlobals {
header "completion_handler_globals.h"
}
}

module ImportedMacroError {
header "imported_macro_error.h"
}
3 changes: 3 additions & 0 deletions test/Macros/Inputs/macro_library.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
23 changes: 23 additions & 0 deletions test/Macros/Inputs/syntax_macro_definitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
20 changes: 20 additions & 0 deletions test/Macros/imported_type_error.swift
Original file line number Diff line number Diff line change
@@ -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