Fix unloading functions declared external "C". Refactor VisitRedeclarable.
This commit is contained in:
parent
afb2746b64
commit
94672dd0d0
@ -107,31 +107,29 @@ void DeclUnloader::removeRedeclFromChain(DeclT* R) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///\brief Removes given declaration from the chain of redeclarations.
|
///\brief Adds the previous declaration into the lookup map on DC.
|
||||||
/// Rebuilds the chain and sets properly first and last redeclaration.
|
/// @param[in] D - The decl that is being removed.
|
||||||
/// @param[in] R - The redeclarable, its chain to be rebuilt.
|
/// @param[in] DC - The DeclContext to add the previous declaration of D.
|
||||||
/// @param[in] DC - Remove the redecl's lookup entry from this DeclContext.
|
///\returns the previous declaration.
|
||||||
///
|
///
|
||||||
///\returns the most recent redeclaration in the new chain.
|
static Decl* handleRedelaration(Decl* D, DeclContext* DC) {
|
||||||
///
|
NamedDecl* ND = dyn_cast<NamedDecl>(D);
|
||||||
template <typename T>
|
if (!ND)
|
||||||
bool DeclUnloader::VisitRedeclarable(clang::Redeclarable<T>* R, DeclContext* DC) {
|
return nullptr;
|
||||||
if (R->getFirstDecl() == R) {
|
|
||||||
// This is the only element in the chain.
|
DeclarationName Name = ND->getDeclName();
|
||||||
return true;
|
if (Name.isEmpty())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
NamedDecl* MostRecent = ND->getMostRecentDecl();
|
||||||
|
NamedDecl* MostRecentNotThis = MostRecent;
|
||||||
|
if (MostRecentNotThis == ND) {
|
||||||
|
MostRecentNotThis = dyn_cast_or_null<NamedDecl>(ND->getPreviousDecl());
|
||||||
|
if (!MostRecentNotThis || MostRecentNotThis == ND)
|
||||||
|
return MostRecentNotThis;
|
||||||
}
|
}
|
||||||
|
|
||||||
T* MostRecent = R->getMostRecentDecl();
|
|
||||||
T* MostRecentNotThis = MostRecent;
|
|
||||||
if (MostRecentNotThis == R)
|
|
||||||
MostRecentNotThis = R->getPreviousDecl();
|
|
||||||
|
|
||||||
if (StoredDeclsMap* Map = DC->getPrimaryContext()->getLookupPtr()) {
|
if (StoredDeclsMap* Map = DC->getPrimaryContext()->getLookupPtr()) {
|
||||||
// Make sure we update the lookup maps, because the removed decl might
|
|
||||||
// be registered in the lookup and still findable.
|
|
||||||
NamedDecl* ND = (T*)R;
|
|
||||||
DeclarationName Name = ND->getDeclName();
|
|
||||||
if (!Name.isEmpty()) {
|
|
||||||
StoredDeclsMap::iterator Pos = Map->find(Name);
|
StoredDeclsMap::iterator Pos = Map->find(Name);
|
||||||
if (Pos != Map->end() && !Pos->second.isNull()) {
|
if (Pos != Map->end() && !Pos->second.isNull()) {
|
||||||
DeclContext::lookup_result decls = Pos->second.getLookupResult();
|
DeclContext::lookup_result decls = Pos->second.getLookupResult();
|
||||||
@ -157,10 +155,30 @@ bool DeclUnloader::VisitRedeclarable(clang::Redeclarable<T>* R, DeclContext* DC)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return MostRecentNotThis;
|
||||||
|
}
|
||||||
|
|
||||||
|
///\brief Removes given declaration from the chain of redeclarations.
|
||||||
|
/// Rebuilds the chain and sets properly first and last redeclaration.
|
||||||
|
/// @param[in] R - The redeclarable, its chain to be rebuilt.
|
||||||
|
/// @param[in] DC - Remove the redecl's lookup entry from this DeclContext.
|
||||||
|
///
|
||||||
|
///\returns the most recent redeclaration in the new chain.
|
||||||
|
///
|
||||||
|
template <typename T>
|
||||||
|
bool DeclUnloader::VisitRedeclarable(clang::Redeclarable<T>* R, DeclContext* DC) {
|
||||||
|
if (R->getFirstDecl() == R) {
|
||||||
|
// This is the only element in the chain.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure we update the lookup maps, because the removed decl might
|
||||||
|
// be registered in the lookup and still findable.
|
||||||
|
T* MostRecentNotThis = (T*)handleRedelaration((T*)R, DC);
|
||||||
|
|
||||||
// Set a new latest redecl.
|
// Set a new latest redecl.
|
||||||
removeRedeclFromChain((T*)R);
|
removeRedeclFromChain((T*)R);
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
// Validate redecl chain by iterating through it.
|
// Validate redecl chain by iterating through it.
|
||||||
std::set<clang::Redeclarable<T>*> CheckUnique;
|
std::set<clang::Redeclarable<T>*> CheckUnique;
|
||||||
@ -169,6 +187,8 @@ bool DeclUnloader::VisitRedeclarable(clang::Redeclarable<T>* R, DeclContext* DC)
|
|||||||
assert(CheckUnique.insert(RD).second && "Dupe redecl chain element");
|
assert(CheckUnique.insert(RD).second && "Dupe redecl chain element");
|
||||||
(void)RD;
|
(void)RD;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
(void)MostRecentNotThis; // templated function issues a lot -Wunused-variable
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -701,23 +721,27 @@ bool DeclUnloader::VisitRedeclarable(clang::Redeclarable<T>* R, DeclContext* DC)
|
|||||||
bool DeclUnloader::VisitLinkageSpecDecl(LinkageSpecDecl* LSD) {
|
bool DeclUnloader::VisitLinkageSpecDecl(LinkageSpecDecl* LSD) {
|
||||||
// LinkageSpecDecl: DeclContext
|
// LinkageSpecDecl: DeclContext
|
||||||
|
|
||||||
// If this is an extern "C" context we have to remove any of our decls
|
// Re-add any previous declarations so they are reachable throughout the
|
||||||
// from Sema::ASTContext's record. This allows globals declared extern "C"
|
// translation unit. Also remove any global variables from:
|
||||||
// to be redeclared subsequently
|
// m_Sema->Context.getExternCContextDecl()
|
||||||
|
|
||||||
if (LSD->isExternCContext()) {
|
if (LSD->isExternCContext()) {
|
||||||
// Sadly ASTContext::getExternCContextDecl will create if it doesn't exist
|
// Sadly ASTContext::getExternCContextDecl will create if it doesn't exist
|
||||||
// Hopefully LSD->isExternCContext() means that it already does exist
|
// Hopefully LSD->isExternCContext() means that it already does exist
|
||||||
if (ExternCContextDecl* ECD = m_Sema->Context.getExternCContextDecl()) {
|
ExternCContextDecl* ECD = m_Sema->Context.getExternCContextDecl();
|
||||||
if (StoredDeclsMap* Map = ECD->getLookupPtr()) {
|
StoredDeclsMap* Map = ECD ? ECD->getLookupPtr() : nullptr;
|
||||||
for (Decl* D: LSD->decls()) {
|
|
||||||
if (NamedDecl *ND = dyn_cast<NamedDecl>(D)) {
|
for (Decl* D : LSD->noload_decls()) {
|
||||||
|
if (NamedDecl* ND = dyn_cast<NamedDecl>(D)) {
|
||||||
|
|
||||||
|
// extern "C" linkage goes in the translation unit
|
||||||
|
DeclContext* DC = m_Sema->getASTContext().getTranslationUnitDecl();
|
||||||
|
handleRedelaration(ND, DC);
|
||||||
|
if (Map)
|
||||||
eraseDeclFromMap(Map, ND);
|
eraseDeclFromMap(Map, ND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Successful = VisitDeclContext(LSD);
|
bool Successful = VisitDeclContext(LSD);
|
||||||
Successful &= VisitDecl(LSD);
|
Successful &= VisitDecl(LSD);
|
||||||
|
49
test/CodeUnloading/ExternC.C
Normal file
49
test/CodeUnloading/ExternC.C
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// CLING - the C++ LLVM-based InterpreterG :)
|
||||||
|
//
|
||||||
|
// This file is dual-licensed: you can choose to license it under the University
|
||||||
|
// of Illinois Open Source License or the GNU Lesser General Public License. See
|
||||||
|
// LICENSE.TXT for details.
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// RUN: cat %s | %cling -I%S -Xclang -verify | FileCheck %s
|
||||||
|
// Test externCUndo
|
||||||
|
|
||||||
|
.storeState "A"
|
||||||
|
|
||||||
|
extern "C" int printf(const char*,...);
|
||||||
|
|
||||||
|
.storeState "B"
|
||||||
|
|
||||||
|
extern "C" int printf(const char*,...);
|
||||||
|
extern "C" int printf(const char*,...);
|
||||||
|
extern "C" int printf(const char*,...);
|
||||||
|
extern "C" int printf(const char*,...);
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
int printf(const char*,...);
|
||||||
|
int abs(int);
|
||||||
|
int atexit(void (*)(void));
|
||||||
|
double atof(const char *);
|
||||||
|
void free(void* ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.undo // extern "C" {}
|
||||||
|
.undo // printf
|
||||||
|
.undo // printf
|
||||||
|
.undo // printf
|
||||||
|
.undo // printf
|
||||||
|
|
||||||
|
.compareState "B"
|
||||||
|
|
||||||
|
printf("Unloaded alot\n");
|
||||||
|
// CHECK: Unloaded alot
|
||||||
|
|
||||||
|
.undo // printf()
|
||||||
|
.undo // printf
|
||||||
|
|
||||||
|
.compareState "A"
|
||||||
|
|
||||||
|
printf("FAIL\n"); // expected-error@2 {{use of undeclared identifier 'printf'}}
|
||||||
|
|
||||||
|
.q
|
17
test/Driver/XexternC.C
Normal file
17
test/Driver/XexternC.C
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// CLING - the C++ LLVM-based InterpreterG :)
|
||||||
|
//
|
||||||
|
// This file is dual-licensed: you can choose to license it under the University
|
||||||
|
// of Illinois Open Source License or the GNU Lesser General Public License. See
|
||||||
|
// LICENSE.TXT for details.
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// RUN: %cling %s | FileCheck %s
|
||||||
|
|
||||||
|
extern "C" int printf(const char*,...);
|
||||||
|
|
||||||
|
extern "C" void XexternC() {
|
||||||
|
printf("XexternC\n");
|
||||||
|
// CHECK: XexternC
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user