Skip to content

Commit b53967f

Browse files
authored
[ClangImporter] Handle macros that are undefined and then redefined (#12413)
This includes 'LONG_MAX' in the Darwin module, which is defined by the system and then immediately redefined by Clang's copy of limits.h. The general strategy here is that if two definitions of a macro in the same module are in separate explicit submodules, we need to keep track of both of them, but if they're in the same explicit submodule then one is probably deliberately redefining the other. This isn't fully correct because one explicit submodule could include another explicit submodule, but it's a good first approximation, which we can refine if we come across problem cases in practice. Swift /also/ has the ability to distinguish between ModuleA.MACRO and ModuleB.MACRO if you've imported both modules, although there's an issue that the two of them end up getting the same mangled name if you try to use both in the same compilation unit. (Filed rdar://problem/34968281.) That ability is preserved with this change. All of this will likely change if/when we switch to Clang's "local submodule visibility" mode, which is needed for the C++ Modules TS but is also incompatible with Apple's SDKs at the moment. In that case, macros in two separate explicit submodules will no longer have an 'override' relationship just because they're mentioned sequentially in the module map. rdar://problem/34413934
1 parent 4142d23 commit b53967f

File tree

8 files changed

+79
-7
lines changed

8 files changed

+79
-7
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2004,6 +2004,11 @@ static bool isVisibleFromModule(const ClangModuleUnit *ModuleFilter,
20042004
}
20052005
}
20062006

2007+
// Macros can be "redeclared" too, by putting an equivalent definition in two
2008+
// different modules.
2009+
if (ClangNode.getAsMacro())
2010+
return true;
2011+
20072012
return false;
20082013
}
20092014

lib/ClangImporter/SwiftLookupTable.cpp

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,6 +1672,16 @@ void importer::addEntryToLookupTable(SwiftLookupTable &table,
16721672
}
16731673
}
16741674

1675+
/// Returns the nearest parent of \p module that is marked \c explicit in its
1676+
/// module map. If \p module is itself explicit, it is returned; if no module
1677+
/// in the parent chain is explicit, the top-level module is returned.
1678+
static const clang::Module *
1679+
getExplicitParentModule(const clang::Module *module) {
1680+
while (!module->IsExplicit && module->Parent)
1681+
module = module->Parent;
1682+
return module;
1683+
}
1684+
16751685
void importer::addMacrosToLookupTable(SwiftLookupTable &table,
16761686
NameImporter &nameImporter) {
16771687
auto &pp = nameImporter.getClangPreprocessor();
@@ -1729,16 +1739,30 @@ void importer::addMacrosToLookupTable(SwiftLookupTable &table,
17291739
maybeAddMacro(MD->getMacroInfo(), nullptr);
17301740

17311741
} else {
1742+
clang::Module *currentModule = pp.getCurrentModule();
17321743
SmallVector<clang::ModuleMacro *, 8> worklist;
1733-
worklist.append(moduleMacros.begin(), moduleMacros.end());
1744+
llvm::copy_if(moduleMacros, std::back_inserter(worklist),
1745+
[currentModule](const clang::ModuleMacro *next) -> bool {
1746+
return next->getOwningModule()->isSubModuleOf(currentModule);
1747+
});
1748+
17341749
while (!worklist.empty()) {
17351750
clang::ModuleMacro *moduleMacro = worklist.pop_back_val();
1736-
clang::Module *owningModule = moduleMacro->getOwningModule();
1737-
if (!owningModule->isSubModuleOf(pp.getCurrentModule()))
1738-
continue;
1739-
worklist.append(moduleMacro->overrides_begin(),
1740-
moduleMacro->overrides_end());
17411751
maybeAddMacro(moduleMacro->getMacroInfo(), moduleMacro);
1752+
1753+
// Also visit overridden macros that are in a different explicit
1754+
// submodule. This isn't a perfect way to tell if these two macros are
1755+
// supposed to be independent, but it's close enough in practice.
1756+
clang::Module *owningModule = moduleMacro->getOwningModule();
1757+
auto *explicitParent = getExplicitParentModule(owningModule);
1758+
llvm::copy_if(moduleMacro->overrides(), std::back_inserter(worklist),
1759+
[&](const clang::ModuleMacro *next) -> bool {
1760+
const clang::Module *nextModule =
1761+
getExplicitParentModule(next->getOwningModule());
1762+
if (!nextModule->isSubModuleOf(currentModule))
1763+
return false;
1764+
return nextModule != explicitParent;
1765+
});
17421766
}
17431767
}
17441768
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#include "Old.h"
2+
#undef MDR_REDEF_1
3+
#define MDR_REDEF_1 "hello"
4+
#undef MDR_REDEF_2
5+
#define MDR_REDEF_2 "swift"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#define MDR_REDEF_1 "hello-OLDTAG"
2+
#define MDR_REDEF_2 "swift-OLDTAG"
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module MacrosDeliberateRedefA {
2+
module Old {
3+
header "Old.h"
4+
}
5+
module New {
6+
header "New.h"
7+
}
8+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#undef MDR_REDEF_1
2+
#define MDR_REDEF_1 "hello"
3+
#undef MDR_REDEF_2
4+
#define MDR_REDEF_2 "world"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module MacrosDeliberateRedefB {
2+
module Newer {
3+
header "Newer.h"
4+
}
5+
}

test/ClangImporter/macros_redef.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -import-objc-header %S/Inputs/macros_redef.h -typecheck %s
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -import-objc-header %S/Inputs/macros_redef.h -emit-silgen %s | %FileCheck -check-prefix=NEGATIVE %s
22

33
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -import-objc-header %S/Inputs/macros_redef.h -DCONFLICT -typecheck -verify %s
44

5+
// NEGATIVE-NOT: OLDTAG
6+
57
import MacrosRedefA
68
import MacrosRedefB
9+
import MacrosDeliberateRedefA
10+
import MacrosDeliberateRedefB
711

812
#if CONFLICT
913
import MacrosRedefWithSubmodules
@@ -44,3 +48,18 @@ func testParallelSubmodules() {
4448
_ = s
4549
}
4650

51+
func testDeliberateRedef() {
52+
var s: String
53+
s = MacrosDeliberateRedefA.MDR_REDEF_1
54+
s = MacrosDeliberateRedefB.MDR_REDEF_1
55+
s = MDR_REDEF_1
56+
57+
#if CONFLICT
58+
// The first two lines ought to work even when SILGen-ing, but the two
59+
// definitions of MDR_REDEF_2 end up getting the same mangled name.
60+
s = MacrosDeliberateRedefA.MDR_REDEF_2 // ok
61+
s = MacrosDeliberateRedefB.MDR_REDEF_2 // ok
62+
s = MDR_REDEF_2 // expected-error{{ambiguous use of 'MDR_REDEF_2'}}
63+
#endif
64+
}
65+

0 commit comments

Comments
 (0)