Skip to content

Commit 11ed132

Browse files
committed
[Clang importer + macros] Handle name lookup and type checking for expanded macros
Introduce a number of fixes to allow us to fully use declarations that are produced by applying a peer macro to an imported declarations. These changes include: * Ensuring that we have the right set of imports in the source file containing the macro expansion, because it depends only on the module it comes from * Ensuring that name lookup looks in that file even when the DeclContext hierarchy doesn't contain the source file (because it's based on the Clang module structure) Expand testing to be sure that we're getting the right calls, diagnostics, and generated IR symbols.
1 parent 1769ae9 commit 11ed132

19 files changed

+159
-27
lines changed

include/swift/AST/ASTScope.h

+2
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,8 @@ class AbstractFunctionDeclScope final : public ASTScopeImpl {
808808
protected:
809809
void printSpecifics(llvm::raw_ostream &out) const override;
810810

811+
bool lookupLocalsOrMembers(DeclConsumer) const override;
812+
811813
public:
812814
Decl *getDecl() const { return decl; }
813815

include/swift/AST/NameLookup.h

+9
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,15 @@ void lookupVisibleMemberDecls(VisibleDeclConsumer &Consumer,
528528
bool includeProtocolExtensionMembers,
529529
GenericSignature genericSig = GenericSignature());
530530

531+
/// Determine the module-scope context from which lookup should proceed.
532+
///
533+
/// In the common case, module-scope context is the source file in which
534+
/// the declaration context is nested. However, when declaration context is
535+
/// part of an imported Clang declaration context, it won't be nested within a
536+
/// source file. Rather, the source file will be on the side, and will be
537+
/// provided here because it contains information about the available imports.
538+
DeclContext *getModuleScopeLookupContext(DeclContext *dc);
539+
531540
namespace namelookup {
532541

533542
/// Add semantic members to \p type before attempting a semantic lookup.

include/swift/Subsystems.h

+6
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ namespace swift {
120120
/// This walks the AST to resolve imports.
121121
void performImportResolution(SourceFile &SF);
122122

123+
/// Resolve imports for a source file generated to adapt a given
124+
/// Clang module.
125+
void performImportResolutionForClangMacroBuffer(
126+
SourceFile &SF, ModuleDecl *clangModule
127+
);
128+
123129
/// Once type-checking is complete, this instruments code with calls to an
124130
/// intrinsic that record the expected values of local variables so they can
125131
/// be compared against the results from the debugger.

lib/AST/ASTScopeLookup.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "swift/AST/Stmt.h"
3131
#include "swift/AST/TypeRepr.h"
3232
#include "swift/Basic/STLExtras.h"
33+
#include "swift/ClangImporter/ClangModule.h"
3334
#include "swift/Parse/Lexer.h"
3435
#include "llvm/Support/Compiler.h"
3536

@@ -264,6 +265,20 @@ bool ASTScopeImpl::lookupLocalsOrMembers(DeclConsumer) const {
264265
return false; // many kinds of scopes have none
265266
}
266267

268+
bool AbstractFunctionDeclScope::lookupLocalsOrMembers(DeclConsumer consumer) const {
269+
// Special case: if we're within a function inside a type context, but the
270+
// parent context is within a Clang module unit, we need to make sure to
271+
// look for members in it.
272+
auto dc = decl->getDeclContext();
273+
if (!dc->isTypeContext())
274+
return false;
275+
276+
if (!isa<ClangModuleUnit>(dc->getModuleScopeContext()))
277+
return false;
278+
279+
return consumer.lookInMembers(cast<GenericContext>(dc->getAsDecl()));
280+
}
281+
267282
bool GenericTypeOrExtensionScope::lookupLocalsOrMembers(
268283
ASTScopeImpl::DeclConsumer consumer) const {
269284
return portion->lookupMembersOf(this, consumer);

lib/AST/AvailabilityScope.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@ AvailabilityScope::createForSourceFile(SourceFile *SF,
5555
case SourceFileKind::DefaultArgument: {
5656
// Look up the parent context in the enclosing file that this file's
5757
// root context should be nested under.
58-
if (auto parentScope =
59-
SF->getEnclosingSourceFile()->getAvailabilityScope()) {
58+
auto enclosingSF = SF->getEnclosingSourceFile();
59+
if (!enclosingSF)
60+
break;
61+
if (auto parentScope = enclosingSF->getAvailabilityScope()) {
6062
auto charRange = Ctx.SourceMgr.getRangeForBuffer(SF->getBufferID());
6163
range = SourceRange(charRange.getStart(), charRange.getEnd());
6264
auto originalNode = SF->getNodeInEnclosingSourceFile();

lib/AST/NameLookup.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1698,7 +1698,7 @@ SmallVector<MacroDecl *, 1> namelookup::lookupMacros(DeclContext *dc,
16981698
DeclNameRef macroName,
16991699
MacroRoles roles) {
17001700
SmallVector<MacroDecl *, 1> choices;
1701-
auto moduleScopeDC = dc->getModuleScopeContext();
1701+
auto moduleScopeDC = getModuleScopeLookupContext(dc);
17021702
ASTContext &ctx = moduleScopeDC->getASTContext();
17031703

17041704
auto addChoiceIfApplicable = [&](ValueDecl *decl) {

lib/AST/UnqualifiedLookup.cpp

+19-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "swift/Basic/STLExtras.h"
3131
#include "swift/Basic/SourceManager.h"
3232
#include "swift/Basic/Statistic.h"
33+
#include "swift/ClangImporter/ClangModule.h"
3334
#include "swift/Parse/Lexer.h"
3435
#include "llvm/ADT/DenseMap.h"
3536
#include "llvm/ADT/TinyPtrVector.h"
@@ -265,7 +266,8 @@ void UnqualifiedLookupFactory::performUnqualifiedLookup() {
265266
if (!isFirstResultEnough()) {
266267
// If no result has been found yet, the dependency must be on a top-level
267268
// name, since up to now, the search has been for non-top-level names.
268-
auto *moduleScopeContext = DC->getModuleScopeContext();
269+
auto *moduleScopeContext = getModuleScopeLookupContext(DC);
270+
269271
lookUpTopLevelNamesInModuleScopeContext(moduleScopeContext);
270272
}
271273
}
@@ -939,3 +941,19 @@ ValueDecl *ASTScope::lookupSingleLocalDecl(SourceFile *sf, DeclName name,
939941
return nullptr;
940942
return result[0];
941943
}
944+
945+
DeclContext *swift::getModuleScopeLookupContext(DeclContext *dc) {
946+
auto moduleScopeContext = dc->getModuleScopeContext();
947+
948+
// When the module scope context is in a Clang module but we actually
949+
// have a parent source file, it's because we are within a macro
950+
// expansion triggered for the imported declaration. In such cases,
951+
// we want to use the parent source file as the lookup context, becauae
952+
// it has the appropriate resolved imports.
953+
if (isa<ClangModuleUnit>(moduleScopeContext)) {
954+
if (auto parentSF = dc->getParentSourceFile())
955+
return parentSF;
956+
}
957+
958+
return moduleScopeContext;
959+
}

lib/ClangImporter/ClangImporter.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -2798,7 +2798,11 @@ ClangModuleUnit *ClangImporter::Implementation::getWrapperForModule(
27982798
Identifier name = underlying->Name == "std"
27992799
? SwiftContext.Id_CxxStdlib
28002800
: SwiftContext.getIdentifier(underlying->Name);
2801-
auto wrapper = ModuleDecl::create(name, SwiftContext);
2801+
ImplicitImportInfo implicitImportInfo;
2802+
if (auto mainModule = SwiftContext.MainModule) {
2803+
implicitImportInfo = mainModule->getImplicitImportInfo();
2804+
}
2805+
auto wrapper = ModuleDecl::create(name, SwiftContext, implicitImportInfo);
28022806
wrapper->setIsSystemModule(underlying->IsSystem);
28032807
wrapper->setIsNonSwiftModule();
28042808
wrapper->setHasResolvedImports();

lib/Parse/ParseRequests.cpp

+15-3
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,23 @@ ParseAbstractFunctionBodyRequest::evaluate(Evaluator &evaluator,
114114

115115
case BodyKind::Unparsed: {
116116
// FIXME: How do we configure code completion?
117-
SourceFile &sf = *afd->getDeclContext()->getParentSourceFile();
118-
SourceManager &sourceMgr = sf.getASTContext().SourceMgr;
117+
SourceManager &sourceMgr = afd->getASTContext().SourceMgr;
119118
unsigned bufferID =
120119
sourceMgr.findBufferContainingLoc(afd->getBodySourceRange().Start);
121-
Parser parser(bufferID, sf, /*SIL*/ nullptr);
120+
SourceFile *sf = afd->getDeclContext()->getParentSourceFile();
121+
if (!sf) {
122+
auto sourceFiles = sourceMgr.getSourceFilesForBufferID(bufferID);
123+
auto expectedModule = afd->getParentModule();
124+
for (auto checkSF: sourceFiles) {
125+
if (checkSF->getParentModule() == expectedModule) {
126+
sf = checkSF;
127+
break;
128+
}
129+
}
130+
assert(sf && "Could not find source file containing parsed body");
131+
}
132+
133+
Parser parser(bufferID, *sf, /*SIL*/ nullptr);
122134
auto result = parser.parseAbstractFunctionBodyDelayed(afd);
123135
afd->setBodyKind(BodyKind::Parsed);
124136
return result;

lib/Sema/ImportResolution.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,11 @@ class ImportResolver final : public DeclVisitor<ImportResolver> {
192192

193193
void addImplicitImports();
194194

195+
void addImplicitImport(ModuleDecl *module) {
196+
boundImports.push_back(ImportedModule(module));
197+
bindPendingImports();
198+
}
199+
195200
/// Retrieve the finalized imports.
196201
ArrayRef<AttributedImport<ImportedModule>> getFinishedImports() const {
197202
return boundImports;
@@ -301,6 +306,22 @@ void swift::performImportResolution(SourceFile &SF) {
301306
verify(SF);
302307
}
303308

309+
void swift::performImportResolutionForClangMacroBuffer(
310+
SourceFile &SF, ModuleDecl *clangModule
311+
) {
312+
// If we've already performed import resolution, bail.
313+
if (SF.ASTStage == SourceFile::ImportsResolved)
314+
return;
315+
316+
ImportResolver resolver(SF);
317+
resolver.addImplicitImport(clangModule);
318+
319+
SF.setImports(resolver.getFinishedImports());
320+
SF.setImportedUnderlyingModule(resolver.getUnderlyingClangModule());
321+
322+
SF.ASTStage = SourceFile::ImportsResolved;
323+
}
324+
304325
//===----------------------------------------------------------------------===//
305326
// MARK: Import handling generally
306327
//===----------------------------------------------------------------------===//

lib/Sema/TypeCheckDecl.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@
5252
#include "swift/Basic/Assertions.h"
5353
#include "swift/Basic/Defer.h"
5454
#include "swift/Bridging/ASTGen.h"
55+
#include "swift/ClangImporter/ClangModule.h"
5556
#include "swift/Sema/IDETypeChecking.h"
5657
#include "swift/Serialization/SerializedModuleLoader.h"
5758
#include "swift/Strings.h"
59+
#include "swift/Subsystems.h"
5860
#include "llvm/ADT/APFloat.h"
5961
#include "llvm/ADT/APInt.h"
6062
#include "llvm/ADT/APSInt.h"

lib/Sema/TypeCheckMacros.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -1033,8 +1033,11 @@ createMacroSourceFile(std::unique_ptr<llvm::MemoryBuffer> buffer,
10331033
/*parsingOpts=*/{}, /*isPrimary=*/false);
10341034
if (auto parentSourceFile = dc->getParentSourceFile())
10351035
macroSourceFile->setImports(parentSourceFile->getImports());
1036-
else if (isa<ClangModuleUnit>(dc->getModuleScopeContext()))
1037-
macroSourceFile->setImports({});
1036+
else if (auto clangModuleUnit =
1037+
dyn_cast<ClangModuleUnit>(dc->getModuleScopeContext())) {
1038+
auto clangModule = clangModuleUnit->getParentModule();
1039+
performImportResolutionForClangMacroBuffer(*macroSourceFile, clangModule);
1040+
}
10381041
return macroSourceFile;
10391042
}
10401043

Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
#if __SWIFT_ATTR_SUPPORTS_MACROS
22
#define ADD_ASYNC __attribute__((swift_attr("@macro_library.AddAsync")))
3+
#define ADD_ASYNC_FINAL __attribute__((swift_attr("@macro_library.AddAsyncFinal")))
34
#else
45
#define ADD_ASYNC
6+
#define ADD_ASYNC_FINAL
57
#endif
68

7-
void async_divide(double x, double y, void (* _Nonnull completionHandler)(double x)) ADD_ASYNC;
9+
void async_divide(double x, double y, void (^ _Nonnull completionHandler)(double x)) ADD_ASYNC;
810

911
typedef struct SlowComputer {
1012
} SlowComputer;
1113

12-
void computer_divide(const SlowComputer *computer, double x, double y, void (* _Nonnull completionHandler)(double x))
14+
void computer_divide(const SlowComputer *computer, double x, double y, void (^ _Nonnull completionHandler)(double x))
1315
ADD_ASYNC
1416
__attribute__((swift_name("SlowComputer.divide(self:_:_:completionHandler:)")));
1517

@@ -19,7 +21,7 @@ void computer_divide(const SlowComputer *computer, double x, double y, void (* _
1921

2022
@interface Computer: NSObject
2123
-(void)multiply:(double)x by:(double)y afterDone:(void (^ _Nonnull)(double x))afterDone
22-
ADD_ASYNC
24+
ADD_ASYNC_FINAL
2325
__attribute__((swift_async(none)));
2426
@end
2527
#endif

test/Macros/Inputs/macro_library.swift

+3
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,6 @@ public macro declareVarValuePeerShadowed() = #externalMacro(module: "MacroDefini
5656

5757
@attached(peer, names: overloaded)
5858
public macro AddAsync() = #externalMacro(module: "MacroDefinition", type: "AddAsyncMacro")
59+
60+
@attached(peer, names: overloaded)
61+
public macro AddAsyncFinal() = #externalMacro(module: "MacroDefinition", type: "AddAsyncMacro")

test/Macros/Inputs/syntax_macro_definitions.swift

+31-1
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,6 @@ public struct AddAsyncMacro: PeerMacro {
935935
providingPeersOf declaration: Declaration,
936936
in context: Context
937937
) throws -> [DeclSyntax] {
938-
939938
// Only on functions at the moment.
940939
guard var funcDecl = declaration.as(FunctionDeclSyntax.self) else {
941940
throw CustomError.message("@addAsync only works on functions")
@@ -1084,6 +1083,37 @@ public struct AddAsyncMacro: PeerMacro {
10841083

10851084
funcDecl.attributes = newAttributeList
10861085

1086+
// If this declaration needs to be final, make it so.
1087+
let isFinal = node.attributeName.trimmedDescription.contains("AddAsyncFinal")
1088+
if isFinal {
1089+
var allModifiers = Array(funcDecl.modifiers)
1090+
if let openIndex = allModifiers.firstIndex(where: { $0.name.text == "open" }) {
1091+
allModifiers[openIndex].name = .keyword(.public)
1092+
} else {
1093+
allModifiers.append(
1094+
DeclModifierSyntax(
1095+
name: .keyword(.public),
1096+
trailingTrivia: .space
1097+
)
1098+
)
1099+
}
1100+
1101+
allModifiers.append(
1102+
DeclModifierSyntax(
1103+
name: .keyword(.final),
1104+
trailingTrivia: .space
1105+
)
1106+
)
1107+
1108+
funcDecl.modifiers = DeclModifierListSyntax(allModifiers)
1109+
1110+
var allAttributes = Array(funcDecl.attributes)
1111+
allAttributes.append(
1112+
.attribute("@_alwaysEmitIntoClient ")
1113+
)
1114+
funcDecl.attributes = AttributeListSyntax(allAttributes)
1115+
}
1116+
10871117
funcDecl.leadingTrivia = .newlines(2)
10881118

10891119
return [DeclSyntax(funcDecl)]

test/Macros/expand_on_imported.swift

+6-9
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,17 @@
1010
// Diagnostics testing
1111
// 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 ModuleUser %s -I %t
1212

13+
// Emit IR to ensure that we are handling the created body end-to-end.
14+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-ir -swift-version 5 -g -enable-experimental-feature MacrosOnImports -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name ModuleUser %s -I %t -validate-tbd-against-ir=none | %FileCheck %s
15+
1316
import CompletionHandlerGlobals
1417
import macro_library
1518

16-
// Make sure that @AddAsync works at all.
17-
@AddAsync
18-
@available(SwiftStdlib 5.1, *)
19-
func asyncTest(_ value: Int, completionHandler: @escaping (String) -> Void) {
20-
completionHandler(String(value))
21-
}
22-
2319
@available(SwiftStdlib 5.1, *)
2420
func testAll(x: Double, y: Double, computer: SlowComputer) async {
25-
_ = await asyncTest(17)
26-
2721
let _: Double = await async_divide(1.0, 2.0)
2822
let _: Double = await computer.divide(x, y)
2923
}
24+
25+
// CHECK: define swifttailcc void @"$sSC12async_divideyS2d_SdtYaF"
26+
// CHECK: define swifttailcc void @"$sSo12SlowComputerV6divideyS2d_SdtYaF"

test/Macros/expand_on_imported_objc.swift

+8-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
// 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)
99

1010
// Diagnostics testing
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 ModuleUser %s -I %t
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 ModuleUser %s -I %t -DTEST_DIAGNOSTICS
12+
13+
// Emit IR just to make sure nothing else fails.
14+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-ir -swift-version 5 -g -enable-experimental-feature MacrosOnImports -load-plugin-library %t/%target-library-name(MacroDefinition) -module-name ModuleUser %s -I %t | %FileCheck %s
1215

1316
import CompletionHandlerGlobals
1417
import macro_library
@@ -17,6 +20,10 @@ import macro_library
1720
func testAll(x: Double, y: Double, computer: Computer, untypedComputer: AnyObject) async {
1821
let _: Double = await computer.multiply(x, by: y)
1922

23+
#if TEST_DIAGNOSTICS
2024
// expected-error@+1{{missing argument for parameter 'afterDone' in call}}
2125
untypedComputer.multiply(x, by: y)
26+
#endif
2227
}
28+
29+
// CHECK: define linkonce_odr hidden swifttailcc void @"$sSo8ComputerC8multiply_2byS2d_SdtYaF"

test/Macros/print_clang_expand_on_imported.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import CompletionHandlerGlobals
1616

17-
// CHECK: func async_divide(_ x: Double, _ y: Double, _ completionHandler: @convention(c) (Double) -> Void)
17+
// CHECK: func async_divide(_ x: Double, _ y: Double, _ completionHandler: @escaping (Double) -> Void)
1818

1919
// CHECK: extension SlowComputer
2020
// CHECK: public func divide(_ x: Double, _ y: Double) async -> Double

test/Macros/print_clang_expand_on_imported_objc.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,4 @@
1313
import CompletionHandlerGlobals
1414

1515
// CHECK: class Computer
16-
// FIXME: The "open" is odd here. We want this to be "final", but can't yet.
17-
// CHECK: open func multiply(_ x: Double, by y: Double) async -> Double
16+
// CHECK: @_alwaysEmitIntoClient public final func multiply(_ x: Double, by y: Double) async -> Double

0 commit comments

Comments
 (0)