Skip to content

[ClangImporter] Handle macros that are undefined and then redefined #12413

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
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
5 changes: 5 additions & 0 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2004,6 +2004,11 @@ static bool isVisibleFromModule(const ClangModuleUnit *ModuleFilter,
}
}

// Macros can be "redeclared" too, by putting an equivalent definition in two
// different modules.
if (ClangNode.getAsMacro())
return true;

return false;
}

Expand Down
36 changes: 30 additions & 6 deletions lib/ClangImporter/SwiftLookupTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1672,6 +1672,16 @@ void importer::addEntryToLookupTable(SwiftLookupTable &table,
}
}

/// Returns the nearest parent of \p module that is marked \c explicit in its
/// module map. If \p module is itself explicit, it is returned; if no module
/// in the parent chain is explicit, the top-level module is returned.
static const clang::Module *
getExplicitParentModule(const clang::Module *module) {
while (!module->IsExplicit && module->Parent)
module = module->Parent;
return module;
}

void importer::addMacrosToLookupTable(SwiftLookupTable &table,
NameImporter &nameImporter) {
auto &pp = nameImporter.getClangPreprocessor();
Expand Down Expand Up @@ -1729,16 +1739,30 @@ void importer::addMacrosToLookupTable(SwiftLookupTable &table,
maybeAddMacro(MD->getMacroInfo(), nullptr);

} else {
clang::Module *currentModule = pp.getCurrentModule();
SmallVector<clang::ModuleMacro *, 8> worklist;
worklist.append(moduleMacros.begin(), moduleMacros.end());
llvm::copy_if(moduleMacros, std::back_inserter(worklist),
[currentModule](const clang::ModuleMacro *next) -> bool {
return next->getOwningModule()->isSubModuleOf(currentModule);
});

while (!worklist.empty()) {
clang::ModuleMacro *moduleMacro = worklist.pop_back_val();
clang::Module *owningModule = moduleMacro->getOwningModule();
if (!owningModule->isSubModuleOf(pp.getCurrentModule()))
continue;
worklist.append(moduleMacro->overrides_begin(),
moduleMacro->overrides_end());
maybeAddMacro(moduleMacro->getMacroInfo(), moduleMacro);

// Also visit overridden macros that are in a different explicit
// submodule. This isn't a perfect way to tell if these two macros are
// supposed to be independent, but it's close enough in practice.
clang::Module *owningModule = moduleMacro->getOwningModule();
auto *explicitParent = getExplicitParentModule(owningModule);
llvm::copy_if(moduleMacro->overrides(), std::back_inserter(worklist),
[&](const clang::ModuleMacro *next) -> bool {
const clang::Module *nextModule =
getExplicitParentModule(next->getOwningModule());
if (!nextModule->isSubModuleOf(currentModule))
return false;
return nextModule != explicitParent;
});
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "Old.h"
#undef MDR_REDEF_1
#define MDR_REDEF_1 "hello"
#undef MDR_REDEF_2
#define MDR_REDEF_2 "swift"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#define MDR_REDEF_1 "hello-OLDTAG"
#define MDR_REDEF_2 "swift-OLDTAG"
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module MacrosDeliberateRedefA {
module Old {
header "Old.h"
}
module New {
header "New.h"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#undef MDR_REDEF_1
#define MDR_REDEF_1 "hello"
#undef MDR_REDEF_2
#define MDR_REDEF_2 "world"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module MacrosDeliberateRedefB {
module Newer {
header "Newer.h"
}
}
21 changes: 20 additions & 1 deletion test/ClangImporter/macros_redef.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/custom-modules -import-objc-header %S/Inputs/macros_redef.h -typecheck %s
// 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

// 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

// NEGATIVE-NOT: OLDTAG

import MacrosRedefA
import MacrosRedefB
import MacrosDeliberateRedefA
import MacrosDeliberateRedefB

#if CONFLICT
import MacrosRedefWithSubmodules
Expand Down Expand Up @@ -44,3 +48,18 @@ func testParallelSubmodules() {
_ = s
}

func testDeliberateRedef() {
var s: String
s = MacrosDeliberateRedefA.MDR_REDEF_1
s = MacrosDeliberateRedefB.MDR_REDEF_1
s = MDR_REDEF_1

#if CONFLICT
// The first two lines ought to work even when SILGen-ing, but the two
// definitions of MDR_REDEF_2 end up getting the same mangled name.
s = MacrosDeliberateRedefA.MDR_REDEF_2 // ok
s = MacrosDeliberateRedefB.MDR_REDEF_2 // ok
s = MDR_REDEF_2 // expected-error{{ambiguous use of 'MDR_REDEF_2'}}
#endif
}