Skip to content

Commit 490aefa

Browse files
authored
[ClangImporter] Preserve macros from all implicit submodules. (#3983)
...instead of picking one definition arbitrarily. This comes from the new "lookup table" design in Swift 3---we no longer just look for any "visible" (imported) macro definition, but instead need to know them up front. This works fine when there's only one definition per module, but for modules like 'OpenGL' on macOS, with mutually-exclusive submodules 'GL' and 'GL3', the compiler was arbitrarily deciding that all of the macros the submodules had in common belonged to 'GL'. The new model tries to decide if it's possible for two modules to be imported separately, and keeps both macro entries if possible, only deduplicating equivalent definitions at the last minute (when importing into Swift). This /still/ doesn't perfectly match the behavior you'd get in C, where a submodule and its parent module could theoretically have conflicting definitions and you'd be fine as long as you only imported one of them, but hopefully (a) it's close enough, and (b) nobody is doing that. (The Swift compiler will prefer the definition in the parent module even if the submodule is the only one imported.) rdar://problem/26731529
1 parent 96e3e93 commit 490aefa

File tree

12 files changed

+177
-19
lines changed

12 files changed

+177
-19
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -856,22 +856,28 @@ void ClangImporter::Implementation::addMacrosToLookupTable(
856856
for (const auto &macro : pp.macros(false)) {
857857
// Find the local history of this macro directive.
858858
clang::MacroDirective *MD = pp.getLocalMacroDirectiveHistory(macro.first);
859-
if (!MD) continue;
860859

861860
// Walk the history.
862861
for (; MD; MD = MD->getPrevious()) {
863-
// Check whether we have a macro defined in this module.
864-
auto info = pp.getMacroInfo(macro.first);
865-
if (!info || info->isFromASTFile() || info->isBuiltinMacro()) continue;
862+
// Don't look at any definitions that are followed by undefs.
863+
// FIXME: This isn't quite correct across explicit submodules -- one
864+
// submodule might define a macro, while another defines and then
865+
// undefines the same macro. If they are processed in that order, the
866+
// history will have the undef at the end, and we'll miss the first
867+
// definition.
868+
if (isa<clang::UndefMacroDirective>(MD))
869+
break;
866870

867871
// Only interested in macro definitions.
868872
auto *defMD = dyn_cast<clang::DefMacroDirective>(MD);
869873
if (!defMD) continue;
870874

875+
// Is this definition from this module?
876+
auto info = defMD->getInfo();
877+
if (!info || info->isFromASTFile()) continue;
878+
871879
// If we hit a builtin macro, we're done.
872-
if (auto info = defMD->getInfo()) {
873-
if (info->isBuiltinMacro()) break;
874-
}
880+
if (info->isBuiltinMacro()) break;
875881

876882
// If we hit a macro with invalid or predefined location, we're done.
877883
auto loc = defMD->getLocation();
@@ -882,7 +888,7 @@ void ClangImporter::Implementation::addMacrosToLookupTable(
882888
// Add this entry.
883889
auto name = importMacroName(macro.first, info, clangCtx);
884890
if (name.empty()) continue;
885-
table.addEntry(name, info, clangCtx.getTranslationUnitDecl());
891+
table.addEntry(name, info, clangCtx.getTranslationUnitDecl(), &pp);
886892
}
887893
}
888894
}

lib/ClangImporter/SwiftLookupTable.cpp

Lines changed: 91 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "swift/Basic/Version.h"
2020
#include "clang/AST/DeclObjC.h"
2121
#include "clang/Lex/MacroInfo.h"
22+
#include "clang/Lex/Preprocessor.h"
2223
#include "clang/Serialization/ASTBitCodes.h"
2324
#include "clang/Serialization/ASTReader.h"
2425
#include "clang/Serialization/ASTWriter.h"
@@ -43,6 +44,76 @@ static bool matchesExistingDecl(clang::Decl *decl, clang::Decl *existingDecl) {
4344
return false;
4445
}
4546

47+
namespace {
48+
enum class MacroConflictAction {
49+
Discard,
50+
Replace,
51+
AddAsAlternative
52+
};
53+
}
54+
55+
/// Based on the Clang module structure, decides what to do when a new
56+
/// definition of an existing macro is seen: discard it, have it replace the
57+
/// old one, or add it as an alternative.
58+
///
59+
/// Specifically, if the innermost explicit submodule containing \p newMacro
60+
/// contains the innermost explicit submodule containing \p existingMacro,
61+
/// \p newMacro should replace \p existingMacro; if they're the same module,
62+
/// \p existingMacro should stay in place. Otherwise, they don't share an
63+
/// explicit module, and should be considered alternatives.
64+
///
65+
/// Note that the above assumes that macro definitions are processed in reverse
66+
/// order, i.e. the first definition seen is the last in a translation unit.
67+
///
68+
/// If we're not currently building a module, then the "latest" macro wins,
69+
/// which (by the same assumption) should be the existing macro.
70+
static MacroConflictAction
71+
considerReplacingExistingMacro(const clang::MacroInfo *newMacro,
72+
const clang::MacroInfo *existingMacro,
73+
const clang::Preprocessor *PP) {
74+
assert(PP);
75+
assert(newMacro);
76+
assert(existingMacro);
77+
assert(newMacro->getOwningModuleID() == 0);
78+
assert(existingMacro->getOwningModuleID() == 0);
79+
80+
if (PP->getLangOpts().CurrentModule.empty())
81+
return MacroConflictAction::Discard;
82+
83+
clang::ModuleMap &moduleInfo = PP->getHeaderSearchInfo().getModuleMap();
84+
const clang::SourceManager &sourceMgr = PP->getSourceManager();
85+
86+
auto findContainingExplicitModule =
87+
[&moduleInfo, &sourceMgr](const clang::MacroInfo *macro)
88+
-> const clang::Module * {
89+
90+
clang::SourceLocation definitionLoc = macro->getDefinitionLoc();
91+
assert(definitionLoc.isValid() &&
92+
"implicitly-defined macros shouldn't show up in a module's lookup");
93+
clang::FullSourceLoc fullLoc(definitionLoc, sourceMgr);
94+
95+
const clang::Module *module = moduleInfo.inferModuleFromLocation(fullLoc);
96+
assert(module && "we are building a module; everything should be modular");
97+
98+
while (module->isSubModule()) {
99+
if (module->IsExplicit)
100+
break;
101+
module = module->Parent;
102+
}
103+
return module;
104+
};
105+
106+
const clang::Module *newModule = findContainingExplicitModule(newMacro);
107+
const clang::Module *existingModule =
108+
findContainingExplicitModule(existingMacro);
109+
110+
if (existingModule == newModule)
111+
return MacroConflictAction::Discard;
112+
if (existingModule->isSubModuleOf(newModule))
113+
return MacroConflictAction::Replace;
114+
return MacroConflictAction::AddAsAlternative;
115+
}
116+
46117
bool SwiftLookupTable::contextRequiresName(ContextKind kind) {
47118
switch (kind) {
48119
case ContextKind::ObjCClass:
@@ -262,7 +333,8 @@ static bool isGlobalAsMember(SwiftLookupTable::SingleEntry entry,
262333
}
263334

264335
bool SwiftLookupTable::addLocalEntry(SingleEntry newEntry,
265-
SmallVectorImpl<uintptr_t> &entries) {
336+
SmallVectorImpl<uintptr_t> &entries,
337+
const clang::Preprocessor *PP) {
266338
// Check whether this entry matches any existing entry.
267339
auto decl = newEntry.dyn_cast<clang::NamedDecl *>();
268340
auto macro = newEntry.dyn_cast<clang::MacroInfo *>();
@@ -272,10 +344,21 @@ bool SwiftLookupTable::addLocalEntry(SingleEntry newEntry,
272344
matchesExistingDecl(decl, mapStoredDecl(existingEntry)))
273345
return false;
274346

275-
// If it matches an existing macro, overwrite the existing entry.
347+
// If it matches an existing macro, decide on the best course of action.
276348
if (macro && isMacroEntry(existingEntry)) {
277-
existingEntry = encodeEntry(macro);
278-
return false;
349+
MacroConflictAction action =
350+
considerReplacingExistingMacro(macro,
351+
mapStoredMacro(existingEntry),
352+
PP);
353+
switch (action) {
354+
case MacroConflictAction::Discard:
355+
return false;
356+
case MacroConflictAction::Replace:
357+
existingEntry = encodeEntry(macro);
358+
return false;
359+
case MacroConflictAction::AddAsAlternative:
360+
break;
361+
}
279362
}
280363
}
281364

@@ -288,7 +371,8 @@ bool SwiftLookupTable::addLocalEntry(SingleEntry newEntry,
288371
}
289372

290373
void SwiftLookupTable::addEntry(DeclName name, SingleEntry newEntry,
291-
EffectiveClangContext effectiveContext) {
374+
EffectiveClangContext effectiveContext,
375+
const clang::Preprocessor *PP) {
292376
assert(!Reader && "Cannot modify a lookup table stored on disk");
293377

294378
// Translate the context.
@@ -311,7 +395,7 @@ void SwiftLookupTable::addEntry(DeclName name, SingleEntry newEntry,
311395
// If this is a global imported as a member, record is as such.
312396
if (isGlobalAsMember(newEntry, context)) {
313397
auto &entries = GlobalsAsMembers[context];
314-
(void)addLocalEntry(newEntry, entries);
398+
(void)addLocalEntry(newEntry, entries, PP);
315399
}
316400

317401
// Find the list of entries for this base name.
@@ -321,7 +405,7 @@ void SwiftLookupTable::addEntry(DeclName name, SingleEntry newEntry,
321405
for (auto &entry : entries) {
322406
if (entry.Context == context) {
323407
// We have entries for this context.
324-
(void)addLocalEntry(newEntry, entry.DeclsOrMacros);
408+
(void)addLocalEntry(newEntry, entry.DeclsOrMacros, PP);
325409
return;
326410
}
327411
}

lib/ClangImporter/SwiftLookupTable.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,8 @@ class SwiftLookupTable {
297297
/// present.
298298
///
299299
/// \returns true if the entry was added, false otherwise.
300-
bool addLocalEntry(SingleEntry newEntry, SmallVectorImpl<uintptr_t> &entries);
300+
bool addLocalEntry(SingleEntry newEntry, SmallVectorImpl<uintptr_t> &entries,
301+
const clang::Preprocessor *PP);
301302

302303
public:
303304
explicit SwiftLookupTable(SwiftLookupTableReader *reader) : Reader(reader) { }
@@ -324,7 +325,8 @@ class SwiftLookupTable {
324325
/// \param newEntry The Clang declaration or macro.
325326
/// \param effectiveContext The effective context in which name lookup occurs.
326327
void addEntry(DeclName name, SingleEntry newEntry,
327-
EffectiveClangContext effectiveContext);
328+
EffectiveClangContext effectiveContext,
329+
const clang::Preprocessor *PP = nullptr);
328330

329331
/// Add an Objective-C category or extension to the table.
330332
void addCategory(clang::ObjCCategoryDecl *category);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#define MRWPS_REDEF_1 "hello"
2+
#define MRWPS_REDEF_2 "swift"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#define MRWPS_REDEF_1 "hello"
2+
#define MRWPS_REDEF_2 "world"
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module MacrosRedefWithParallelSubmodules {
2+
explicit module A {
3+
header "A.h"
4+
}
5+
explicit module B {
6+
header "B.h"
7+
}
8+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#define MRWS_REDEF_1 "hello"
2+
#define MRWS_REDEF_2 "swift"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#define MRWS_REDEF_1 "hello"
2+
#define MRWS_REDEF_2 "world"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module MacrosRedefWithSubmodules {
2+
header "Outer.h"
3+
explicit module TheSubmodule {
4+
header "Inner.h"
5+
}
6+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#define BRIDGING_HEADER_1 1
2+
#undef BRIDGING_HEADER_1
3+
#define BRIDGING_HEADER_1 "1"
4+
5+
#define BRIDGING_HEADER_2 2
6+
#define BRIDGING_HEADER_2 "2"
7+

test/ClangModules/macros_redef.swift

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,46 @@
1-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -parse -verify %s
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -import-objc-header %S/Inputs/macros_redef.h -parse %s
2+
3+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -import-objc-header %S/Inputs/macros_redef.h -DCONFLICT -parse -verify %s
24

35
import MacrosRedefA
46
import MacrosRedefB
57

6-
func testMacroRedef() {
8+
#if CONFLICT
9+
import MacrosRedefWithSubmodules
10+
import MacrosRedefWithSubmodules.TheSubmodule
11+
import MacrosRedefWithParallelSubmodules.A
12+
import MacrosRedefWithParallelSubmodules.B
13+
#else
14+
import MacrosRedefWithSubmodules.TheSubmodule
15+
import MacrosRedefWithParallelSubmodules.A
16+
#endif
17+
18+
func testFrameworkRedef() {
719
var s: String
820
s = REDEF_1
21+
#if CONFLICT
922
s = REDEF_2 // expected-error{{ambiguous use of 'REDEF_2'}}
23+
#endif
24+
}
25+
26+
func testBridgingHeaderRedef() {
27+
var s: String
28+
s = BRIDGING_HEADER_1
29+
s = BRIDGING_HEADER_2
30+
_ = s
31+
}
32+
33+
func testSubmodules() {
34+
var s: String
35+
s = MRWS_REDEF_1
36+
s = MRWS_REDEF_2
37+
_ = s
38+
}
39+
40+
func testParallelSubmodules() {
41+
var s: String
42+
s = MRWPS_REDEF_1
43+
s = MRWPS_REDEF_2 // expected-error{{ambiguous use of 'MRWPS_REDEF_2'}}
44+
_ = s
1045
}
1146

test/Interpreter/SDK/submodules_smoke_test.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import OpenGL.GL3
99
_ = glGetString
1010
_ = OpenGL.glGetString
11+
_ = GL_COLOR_BUFFER_BIT
12+
_ = OpenGL.GL_COLOR_BUFFER_BIT
1113

1214
import AppKit.NSPanGestureRecognizer
1315

0 commit comments

Comments
 (0)