2016-06-26 11:06:18 +03:00
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author: Vassil Vassilev <vvasilev@cern.ch>
//
// 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.
//------------------------------------------------------------------------------
# include "DeclUnloader.h"
# include "cling/Utils/AST.h"
2020-10-05 09:22:33 +03:00
# ifdef _WIN32
2017-03-30 10:46:13 +03:00
# include "cling/Utils/Diagnostics.h"
# endif
2016-06-26 11:06:18 +03:00
# include "clang/AST/ASTContext.h"
2016-06-26 18:14:35 +03:00
# include "clang/AST/DeclContextInternals.h"
2016-06-26 11:06:18 +03:00
# include "clang/AST/GlobalDecl.h"
# include "clang/AST/RecursiveASTVisitor.h"
# include "clang/Basic/SourceManager.h"
# include "clang/CodeGen/ModuleBuilder.h"
# include "clang/Lex/MacroInfo.h"
# include "clang/Lex/Preprocessor.h"
# include "clang/Sema/Sema.h"
# include "llvm/IR/Constants.h"
2023-08-24 16:39:44 +03:00
namespace {
using namespace clang ;
constexpr bool isDefinition ( void * ) { return false ; }
bool isDefinition ( TagDecl * R ) {
return R - > isCompleteDefinition ( ) & & isa < CXXRecordDecl > ( R ) ;
2016-06-26 18:09:16 +03:00
}
2023-08-24 16:39:44 +03:00
// Copied and adapted from: ASTReaderDecl.cpp
template < typename DeclT > void removeRedeclFromChain ( DeclT * R ) {
// RedeclLink is a protected member.
struct RedeclDerived : public Redeclarable < DeclT > {
// FIXME: Report this false positive diagnostic to clang.
2021-02-12 20:15:42 +03:00
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wunused-local-typedef"
# endif // __clang__
2017-02-10 10:29:58 +03:00
typedef typename Redeclarable < DeclT > : : DeclLink DeclLink_t ;
static DeclLink_t & getLink ( DeclT * LR ) {
Redeclarable < DeclT > * D = LR ;
return ( ( RedeclDerived * ) D ) - > RedeclLink ;
}
static void setLatest ( DeclT * Latest ) {
// Convert A -> Latest -> B into A -> Latest
getLink ( Latest - > getFirstDecl ( ) ) . setLatest ( Latest ) ;
}
static void skipPrev ( DeclT * Next ) {
// Convert A -> B -> Next into A -> Next
DeclT * Skip = Next - > getPreviousDecl ( ) ;
getLink ( Next ) . setPrevious ( Skip - > getPreviousDecl ( ) ) ;
}
static void setFirst ( DeclT * First ) {
// Convert A -> First -> B into First -> B
DeclT * Latest = First - > getMostRecentDecl ( ) ;
getLink ( First )
= DeclLink_t ( DeclLink_t : : LatestLink , First - > getASTContext ( ) ) ;
getLink ( First ) . setLatest ( Latest ) ;
}
2021-02-12 20:15:42 +03:00
# ifdef __clang__
2023-08-24 16:39:44 +03:00
# pragma clang diagnostic pop
2021-02-12 20:15:42 +03:00
# endif // __clang__
2023-08-24 16:39:44 +03:00
} ;
2017-02-10 10:29:58 +03:00
2023-08-24 16:39:44 +03:00
assert ( R ! = R - > getFirstDecl ( ) & & " Cannot remove only redecl from chain " ) ;
const bool isdef = isDefinition ( R ) ;
2016-06-26 18:09:16 +03:00
2023-08-24 16:39:44 +03:00
// In the following cases, A marks the first, Z the most recent and
// R the decl to be removed from the chain.
DeclT * Prev = R - > getPreviousDecl ( ) ;
if ( R = = R - > getMostRecentDecl ( ) ) {
// A -> .. -> R
RedeclDerived : : setLatest ( Prev ) ;
2016-06-26 18:09:16 +03:00
} else {
2023-08-24 16:39:44 +03:00
// Find the next redecl, starting at the end
DeclT * Next = R - > getMostRecentDecl ( ) ;
while ( Next & & Next - > getPreviousDecl ( ) ! = R )
Next = Next - > getPreviousDecl ( ) ;
if ( ! Next ) {
// R is not (yet?) wired up.
return ;
}
2016-06-21 04:05:32 +03:00
2023-08-24 16:39:44 +03:00
if ( R - > getPreviousDecl ( ) ) {
// A -> .. -> R -> .. -> Z
RedeclDerived : : skipPrev ( Next ) ;
} else {
assert ( R - > getFirstDecl ( ) = = R & & " Logic error " ) ;
// R -> .. -> Z
RedeclDerived : : setFirst ( Next ) ;
2016-06-21 04:05:32 +03:00
}
}
2023-08-24 16:39:44 +03:00
// If the decl was the definition, the other decl might have their
// DefinitionData pointing to it.
// This is really need only if DeclT is a TagDecl or derived.
if ( isdef )
cling : : DeclUnloader : : resetDefinitionData ( Prev ) ;
2016-06-26 18:09:16 +03:00
}
2023-08-24 16:39:44 +03:00
///\brief Adds the previous declaration into the lookup map on DC.
/// @param[in] D - The decl that is being removed.
/// @param[in] DC - The DeclContext to add the previous declaration of D.
///\returns the previous declaration.
///
Decl * handleRedelaration ( Decl * D , DeclContext * DC ) {
NamedDecl * ND = dyn_cast < NamedDecl > ( D ) ;
if ( ! ND )
return nullptr ;
DeclarationName Name = ND - > getDeclName ( ) ;
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 ;
}
2016-06-21 04:05:32 +03:00
2023-08-24 16:39:44 +03:00
if ( StoredDeclsMap * Map = DC - > getPrimaryContext ( ) - > getLookupPtr ( ) ) {
StoredDeclsMap : : iterator Pos = Map - > find ( Name ) ;
if ( Pos ! = Map - > end ( ) & & ! Pos - > second . isNull ( ) ) {
DeclContext : : lookup_result decls = Pos - > second . getLookupResult ( ) ;
// FIXME: A decl meant to be added in the lookup already exists
// in the lookup table. My assumption is that the DeclUnloader
// adds it here. This needs to be investigated mode. For now
// std::find gets promoted from assert to condition :)
// DeclContext::lookup_result::iterator is not an InputIterator
// (const member, thus no op=(const iterator&)), thus we cannot use
// std::find. MSVC actually cares!
auto hasDecl = [ ] ( const DeclContext : : lookup_result & Result ,
const NamedDecl * Needle ) - > bool {
for ( auto IDecl : Result ) {
if ( IDecl = = Needle )
return true ;
}
return false ;
} ;
if ( ! hasDecl ( decls , MostRecentNotThis ) & & hasDecl ( decls , ND ) ) {
// The decl was registered in the lookup, update it.
Pos - > second . addOrReplaceDecl ( MostRecentNotThis ) ;
}
}
}
return MostRecentNotThis ;
2016-06-26 18:09:16 +03:00
}
2016-06-26 11:06:18 +03:00
// Copied and adapted from GlobalDCE.cpp
class GlobalValueEraser {
private :
typedef llvm : : SmallPtrSet < llvm : : GlobalValue * , 32 > Globals ;
Globals VisitedGlobals ;
llvm : : SmallPtrSet < llvm : : Constant * , 8 > SeenConstants ;
clang : : CodeGenerator * m_CodeGen ;
public :
GlobalValueEraser ( clang : : CodeGenerator * CG )
: m_CodeGen ( CG ) { }
///\brief Erases the given global value and all unused leftovers
///
///\param[in] GV - The removal starting point.
///
///\returns true if something was erased.
///
bool EraseGlobalValue ( llvm : : GlobalValue * GV ) {
using namespace llvm ;
bool Changed = false ;
Changed | = RemoveUnusedGlobalValue ( * GV ) ;
// Collect all uses of globals by GV
CollectAllUsesOfGlobals ( GV ) ;
FindUsedValues ( * GV - > getParent ( ) ) ;
// The first pass is to drop initializers of global vars which are dead.
for ( Globals : : iterator I = VisitedGlobals . begin ( ) ,
E = VisitedGlobals . end ( ) ; I ! = E ; + + I )
2016-09-12 22:52:00 +03:00
if ( GlobalVariable * GVar = dyn_cast < GlobalVariable > ( * I ) ) {
2022-09-13 09:34:32 +03:00
GVar - > setInitializer ( nullptr ) ;
2016-06-26 11:06:18 +03:00
}
else if ( GlobalAlias * GA = dyn_cast < GlobalAlias > ( * I ) ) {
2022-09-13 09:34:32 +03:00
GA - > setAliasee ( nullptr ) ;
2016-06-26 11:06:18 +03:00
}
else {
Function * F = cast < Function > ( * I ) ;
if ( ! F - > isDeclaration ( ) )
F - > deleteBody ( ) ;
}
if ( ! VisitedGlobals . empty ( ) ) {
// Now that all interferences have been dropped, delete the actual
// objects themselves.
for ( Globals : : iterator I = VisitedGlobals . begin ( ) ,
E = VisitedGlobals . end ( ) ; I ! = E ; + + I ) {
RemoveUnusedGlobalValue ( * * I ) ;
if ( ( * I ) - > getNumUses ( ) )
continue ;
// Required by ::DwarfEHPrepare::InsertUnwindResumeCalls (in the JIT)
if ( ( * I ) - > getName ( ) . equals ( " _Unwind_Resume " ) )
continue ;
m_CodeGen - > forgetGlobal ( * I ) ;
( * I ) - > eraseFromParent ( ) ;
}
Changed = true ;
}
// Make sure that all memory is released
VisitedGlobals . clear ( ) ;
SeenConstants . clear ( ) ;
return Changed ;
}
private :
/// Find values that are marked as llvm.used.
void FindUsedValues ( const llvm : : Module & m ) {
for ( const llvm : : GlobalVariable & GV : m . globals ( ) ) {
2024-02-02 13:11:47 +03:00
if ( ! GV . getName ( ) . starts_with ( " llvm.used " ) )
2016-06-26 11:06:18 +03:00
continue ;
const llvm : : ConstantArray * Inits
= cast < llvm : : ConstantArray > ( GV . getInitializer ( ) ) ;
for ( unsigned i = 0 , e = Inits - > getNumOperands ( ) ; i ! = e ; + + i ) {
llvm : : Value * Operand
2021-09-11 23:50:32 +03:00
= Inits - > getOperand ( i ) - > stripPointerCasts ( ) ;
2016-06-26 11:06:18 +03:00
VisitedGlobals . erase ( cast < llvm : : GlobalValue > ( Operand ) ) ;
}
}
}
/// CollectAllUsesOfGlobals - collects recursively all referenced globals by
/// GV.
void CollectAllUsesOfGlobals ( llvm : : GlobalValue * G ) {
using namespace llvm ;
// If the global is already in the set, no need to reprocess it.
if ( ! VisitedGlobals . insert ( G ) . second )
return ;
if ( GlobalVariable * GV = dyn_cast < GlobalVariable > ( G ) ) {
// If this is a global variable, we must make sure to add any global
// values referenced by the initializer to the collection set.
if ( GV - > hasInitializer ( ) )
MarkConstant ( GV - > getInitializer ( ) ) ;
} else if ( GlobalAlias * GA = dyn_cast < GlobalAlias > ( G ) ) {
// The target of a global alias as referenced.
2016-09-29 03:17:23 +03:00
// GA->getAliasee() is sometimes returning NULL on Windows
if ( llvm : : Constant * C = GA - > getAliasee ( ) )
MarkConstant ( C ) ;
2016-06-26 11:06:18 +03:00
} else {
// Otherwise this must be a function object. We have to scan the body
// of the function looking for constants and global values which are
// used as operands. Any operands of these types must be processed to
// ensure that any globals used will be marked as collected.
Function * F = cast < Function > ( G ) ;
if ( F - > hasPrefixData ( ) )
MarkConstant ( F - > getPrefixData ( ) ) ;
for ( Function : : iterator BB = F - > begin ( ) , E = F - > end ( ) ; BB ! = E ; + + BB )
for ( BasicBlock : : iterator I = BB - > begin ( ) , E = BB - > end ( ) ; I ! = E ; + + I )
for ( User : : op_iterator U = I - > op_begin ( ) , E = I - > op_end ( ) ; U ! = E ; + + U )
if ( GlobalValue * GV = dyn_cast < GlobalValue > ( * U ) )
CollectAllUsesOfGlobals ( GV ) ;
else if ( Constant * C = dyn_cast < Constant > ( * U ) )
MarkConstant ( C ) ;
}
}
void MarkConstant ( llvm : : Constant * C ) {
using namespace llvm ;
if ( GlobalValue * GV = dyn_cast < GlobalValue > ( C ) )
return CollectAllUsesOfGlobals ( GV ) ;
// Loop over all of the operands of the constant, adding any globals they
// use to the list of needed globals.
for ( User : : op_iterator I = C - > op_begin ( ) , E = C - > op_end ( ) ; I ! = E ; + + I ) {
Constant * Op = dyn_cast < Constant > ( * I ) ;
// We already processed this constant there's no need to do it again.
if ( Op & & SeenConstants . insert ( Op ) . second )
MarkConstant ( Op ) ;
}
}
// RemoveUnusedGlobalValue - Loop over all of the uses of the specified
// GlobalValue, looking for the constant pointer ref that may be pointing to
// it. If found, check to see if the constant pointer ref is safe to
// destroy, and if so, nuke it. This will reduce the reference count on the
// global value, which might make it deader.
//
bool RemoveUnusedGlobalValue ( llvm : : GlobalValue & GV ) {
using namespace llvm ;
if ( GV . use_empty ( ) )
return false ;
GV . removeDeadConstantUsers ( ) ;
return GV . use_empty ( ) ;
}
} ;
2023-08-24 16:39:44 +03:00
// Remove a decl and possibly it's parent entry in lookup tables.
static void eraseDeclFromMap ( StoredDeclsMap * Map , NamedDecl * ND ) {
assert ( Map & & ND & & " eraseDeclFromMap recieved NULL value(s) " ) ;
// Make sure we the decl doesn't exist in the lookup tables.
StoredDeclsMap : : iterator Pos = Map - > find ( ND - > getDeclName ( ) ) ;
if ( Pos ! = Map - > end ( ) ) {
StoredDeclsList & List = Pos - > second ;
// In some cases clang puts an entry in the list without a decl pointer.
// Clean it up.
if ( List . isNull ( ) ) {
Map - > erase ( Pos ) ;
return ;
}
List . remove ( ND ) ;
if ( List . isNull ( ) )
Map - > erase ( Pos ) ;
}
}
2023-11-24 19:55:30 +03:00
template < class EntryType >
void removeSpecializationImpl ( llvm : : FoldingSetVector < EntryType > & Specs ,
const EntryType * Entry ) {
// Remove only Entry from Specs, keep all others.
llvm : : FoldingSetVector < EntryType > Keep ;
for ( auto & Spec : Specs ) {
if ( & Spec ! = Entry ) {
// Avoid assertion on add.
Spec . SetNextInBucket ( nullptr ) ;
Keep . InsertNode ( & Spec ) ;
}
}
std : : swap ( Specs , Keep ) ;
}
2023-08-24 16:39:44 +03:00
// Template instantiation of templated function first creates a canonical
// declaration and after the actual template specialization. For example:
// template<typename T> T TemplatedF(T t);
// template<> int TemplatedF(int i) { return i + 1; } creates:
// 1. Canonical decl: int TemplatedF(int i);
// 2. int TemplatedF(int i){ return i + 1; }
//
// The template specialization is attached to the list of specialization of
// the templated function.
// When TemplatedF is looked up it finds the templated function and the
// lookup is extended by the templated function with its specializations.
// In the end we don't need to remove the canonical decl because, it
// doesn't end up in the lookup table.
//
class FunctionTemplateDeclExt : public FunctionTemplateDecl {
public :
static void removeSpecialization ( FunctionTemplateDecl * self ,
2023-11-24 19:55:30 +03:00
const FunctionDecl * spec ) {
assert ( self & & spec & & " Cannot be null! " ) ;
assert ( spec = = spec - > getCanonicalDecl ( ) & &
2023-08-24 16:39:44 +03:00
" Not the canonical specialization!? " ) ;
2023-11-24 19:55:30 +03:00
auto * This = static_cast < FunctionTemplateDeclExt * > ( self ) ;
auto & specs = This - > getCommonPtr ( ) - > Specializations ;
removeSpecializationImpl ( specs , spec - > getTemplateSpecializationInfo ( ) ) ;
2023-08-24 16:39:44 +03:00
# ifndef NDEBUG
2023-11-24 19:55:30 +03:00
const TemplateArgumentList * args = spec - > getTemplateSpecializationArgs ( ) ;
void * InsertPos = nullptr ;
2023-08-24 16:39:44 +03:00
assert ( ! self - > findSpecialization ( args - > asArray ( ) , InsertPos ) & &
" Finds the removed decl again! " ) ;
# endif
}
} ;
// A template specialization is attached to the list of specialization of
// the templated class.
//
class ClassTemplateDeclExt : public ClassTemplateDecl {
public :
static void removeSpecialization ( ClassTemplateDecl * self ,
ClassTemplateSpecializationDecl * spec ) {
assert ( ! isa < ClassTemplatePartialSpecializationDecl > ( spec ) & &
" Use removePartialSpecialization " ) ;
assert ( self & & spec & & " Cannot be null! " ) ;
assert ( spec = = spec - > getCanonicalDecl ( ) & &
" Not the canonical specialization!? " ) ;
2023-11-24 19:55:30 +03:00
auto * This = static_cast < ClassTemplateDeclExt * > ( self ) ;
auto & specs = This - > getCommonPtr ( ) - > Specializations ;
removeSpecializationImpl ( specs , spec ) ;
2023-08-24 16:39:44 +03:00
}
static void
removePartialSpecialization ( ClassTemplateDecl * self ,
ClassTemplatePartialSpecializationDecl * spec ) {
assert ( self & & spec & & " Cannot be null! " ) ;
assert ( spec = = spec - > getCanonicalDecl ( ) & &
" Not the canonical specialization!? " ) ;
2023-11-24 19:55:30 +03:00
auto * This = static_cast < ClassTemplateDeclExt * > ( self ) ;
auto & specs = This - > getPartialSpecializations ( ) ;
removeSpecializationImpl ( specs , spec ) ;
2023-08-24 16:39:44 +03:00
}
} ;
2023-11-25 00:15:41 +03:00
// A template specialization is attached to the list of specialization of
// the templated variable.
//
class VarTemplateDeclExt : public VarTemplateDecl {
public :
static void removeSpecialization ( VarTemplateDecl * self ,
VarTemplateSpecializationDecl * spec ) {
assert ( ! isa < VarTemplatePartialSpecializationDecl > ( spec ) & &
" Use removePartialSpecialization " ) ;
assert ( self & & spec & & " Cannot be null! " ) ;
assert ( spec = = spec - > getCanonicalDecl ( ) & &
" Not the canonical specialization!? " ) ;
auto * This = static_cast < VarTemplateDeclExt * > ( self ) ;
auto & specs = This - > getCommonPtr ( ) - > Specializations ;
removeSpecializationImpl ( specs , spec ) ;
}
static void
removePartialSpecialization ( VarTemplateDecl * self ,
VarTemplatePartialSpecializationDecl * spec ) {
assert ( self & & spec & & " Cannot be null! " ) ;
assert ( spec = = spec - > getCanonicalDecl ( ) & &
" Not the canonical specialization!? " ) ;
auto * This = static_cast < VarTemplateDeclExt * > ( self ) ;
auto & specs = This - > getPartialSpecializations ( ) ;
removeSpecializationImpl ( specs , spec ) ;
}
} ;
2023-08-24 16:39:44 +03:00
} // end anonymous namespace
namespace cling {
using namespace clang ;
void DeclUnloader : : resetDefinitionData ( TagDecl * decl ) {
auto canon = dyn_cast < CXXRecordDecl > ( decl - > getCanonicalDecl ( ) ) ;
assert ( canon & & " Only CXXRecordDecl have DefinitionData " ) ;
for ( auto iter = canon - > getMostRecentDecl ( ) ; iter ;
iter = iter - > getPreviousDecl ( ) ) {
auto declcxx = dyn_cast < CXXRecordDecl > ( iter ) ;
assert ( declcxx & & " Only CXXRecordDecl have DefinitionData " ) ;
declcxx - > DefinitionData = nullptr ;
}
}
///\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.
removeRedeclFromChain ( ( T * ) R ) ;
# ifndef NDEBUG
// Validate redecl chain by iterating through it.
std : : set < clang : : Redeclarable < T > * > CheckUnique ;
( void ) CheckUnique ;
for ( auto RD : MostRecentNotThis - > redecls ( ) ) {
assert ( CheckUnique . insert ( RD ) . second & & " Dupe redecl chain element " ) ;
( void ) RD ;
}
# else
( void )
MostRecentNotThis ; // templated function issues a lot -Wunused-variable
# endif
return true ;
}
2016-06-26 11:06:18 +03:00
DeclUnloader : : ~ DeclUnloader ( ) {
SourceManager & SM = m_Sema - > getSourceManager ( ) ;
for ( FileIDs : : iterator I = m_FilesToUncache . begin ( ) ,
E = m_FilesToUncache . end ( ) ; I ! = E ; + + I ) {
// We need to reset the cache
SM . invalidateCache ( * I ) ;
}
}
void DeclUnloader : : CollectFilesToUncache ( SourceLocation Loc ) {
if ( ! m_CurTransaction )
return ;
const SourceManager & SM = m_Sema - > getSourceManager ( ) ;
FileID FID = SM . getFileID ( SM . getSpellingLoc ( Loc ) ) ;
2017-06-09 10:50:58 +03:00
// FID == m_CurTransaction->getBufferFID() done last in TransactionUnloader
if ( ! FID . isInvalid ( ) & & FID > m_CurTransaction - > getBufferFID ( ) )
2016-06-26 11:06:18 +03:00
m_FilesToUncache . insert ( FID ) ;
}
bool DeclUnloader : : VisitDecl ( Decl * D ) {
assert ( D & & " The Decl is null " ) ;
2020-03-15 15:30:01 +03:00
CollectFilesToUncache ( D - > getBeginLoc ( ) ) ;
2016-06-26 11:06:18 +03:00
DeclContext * DC = D - > getLexicalDeclContext ( ) ;
2023-09-05 18:07:43 +03:00
if ( DC - > containsDecl ( D ) ) {
if ( auto * ND = dyn_cast < NamedDecl > ( D ) ) {
auto * LookupDC = DC ;
while ( LookupDC - > getDeclKind ( ) = = Decl : : LinkageSpec | |
LookupDC - > getDeclKind ( ) = = Decl : : Export )
LookupDC = LookupDC - > getParent ( ) ;
if ( ! LookupDC - > noload_lookup ( ND - > getDeclName ( ) ) . empty ( ) )
DC - > removeDecl ( D ) ;
} else {
DC - > removeDecl ( D ) ;
}
}
2016-06-26 11:06:18 +03:00
2020-10-30 19:27:20 +03:00
// With the bump allocator this is a no-op.
m_Sema - > getASTContext ( ) . Deallocate ( D ) ;
return true ;
2016-06-26 11:06:18 +03:00
}
bool DeclUnloader : : VisitNamedDecl ( NamedDecl * ND ) {
bool Successful = VisitDecl ( ND ) ;
DeclContext * DC = ND - > getDeclContext ( ) ;
2019-08-15 12:05:59 +03:00
while ( DC - > isTransparentContext ( ) | | DC - > isInlineNamespace ( ) )
2016-06-26 11:06:18 +03:00
DC = DC - > getLookupParent ( ) ;
// if the decl was anonymous we are done.
if ( ! ND - > getIdentifier ( ) )
return Successful ;
// If the decl was removed make sure that we fix the lookup
if ( Successful ) {
if ( Scope * S = m_Sema - > getScopeForContext ( DC ) )
S - > RemoveDecl ( ND ) ;
if ( utils : : Analyze : : isOnScopeChains ( ND , * m_Sema ) )
m_Sema - > IdResolver . RemoveDecl ( ND ) ;
}
// Cleanup the lookup tables.
2016-06-13 17:56:42 +03:00
// DeclContexts like EnumDecls don't have lookup maps.
2021-12-17 10:02:14 +03:00
// FIXME: Remove once we upstream this patch: D119675
if ( StoredDeclsMap * Map = DC - > getPrimaryContext ( ) - > getLookupPtr ( ) )
2016-06-13 17:56:42 +03:00
eraseDeclFromMap ( Map , ND ) ;
2016-06-26 11:06:18 +03:00
return Successful ;
}
bool DeclUnloader : : VisitDeclaratorDecl ( DeclaratorDecl * DD ) {
// VisitDeclaratorDecl: ValueDecl
2022-09-13 09:34:32 +03:00
auto found = std : : find ( m_Sema - > UnusedFileScopedDecls . begin ( /*ExtSource*/ nullptr ,
2016-06-26 11:06:18 +03:00
/*Local*/ true ) ,
m_Sema - > UnusedFileScopedDecls . end ( ) , DD ) ;
if ( found ! = m_Sema - > UnusedFileScopedDecls . end ( ) )
m_Sema - > UnusedFileScopedDecls . erase ( found ,
m_Sema - > UnusedFileScopedDecls . end ( ) ) ;
return VisitValueDecl ( DD ) ;
}
bool DeclUnloader : : VisitUsingShadowDecl ( UsingShadowDecl * USD ) {
// UsingShadowDecl: NamedDecl, Redeclarable
bool Successful = true ;
2017-02-10 10:29:58 +03:00
// FIXME: This is needed when we have newest clang:
//Successful = VisitRedeclarable(USD, USD->getDeclContext());
2016-06-26 11:06:18 +03:00
Successful & = VisitNamedDecl ( USD ) ;
// Unregister from the using decl that it shadows.
2021-12-17 10:02:14 +03:00
USD - > getIntroducer ( ) - > removeShadowDecl ( USD ) ;
2016-06-26 11:06:18 +03:00
return Successful ;
}
bool DeclUnloader : : VisitTypedefNameDecl ( TypedefNameDecl * TND ) {
// TypedefNameDecl: TypeDecl, Redeclarable
bool Successful = VisitRedeclarable ( TND , TND - > getDeclContext ( ) ) ;
Successful & = VisitTypeDecl ( TND ) ;
return Successful ;
}
2017-02-10 10:29:51 +03:00
2016-06-26 11:06:18 +03:00
bool DeclUnloader : : VisitVarDecl ( VarDecl * VD ) {
// llvm::Module cannot contain:
// * variables and parameters with dependent context;
// * mangled names for parameters;
if ( ! isa < ParmVarDecl > ( VD ) & & ! VD - > getDeclContext ( ) - > isDependentContext ( ) ) {
// Cleanup the module if the transaction was committed and code was
// generated. This has to go first, because it may need the AST
// information which we will remove soon. (Eg. mangleDeclName iterates the
// redecls)
GlobalDecl GD ( VD ) ;
MaybeRemoveDeclFromModule ( GD ) ;
}
// VarDecl : DeclaratiorDecl, Redeclarable
bool Successful = VisitRedeclarable ( VD , VD - > getDeclContext ( ) ) ;
Successful & = VisitDeclaratorDecl ( VD ) ;
return Successful ;
}
2023-11-26 16:14:44 +03:00
bool DeclUnloader : : VisitFunctionDecl ( FunctionDecl * FD , bool RemoveSpec ) {
2016-06-26 11:06:18 +03:00
// The Structors need to be handled differently.
if ( ! isa < CXXConstructorDecl > ( FD ) & & ! isa < CXXDestructorDecl > ( FD ) ) {
// Cleanup the module if the transaction was committed and code was
// generated. This has to go first, because it may need the AST info
// which we will remove soon. (Eg. mangleDeclName iterates the redecls)
GlobalDecl GD ( FD ) ;
MaybeRemoveDeclFromModule ( GD ) ;
2023-08-29 17:43:05 +03:00
2016-06-26 11:06:18 +03:00
// Handle static locals. void func() { static int var; } is represented in
// the llvm::Module is a global named @func.var
2023-08-29 17:43:05 +03:00
for ( auto D : FunctionDecl : : castToDeclContext ( FD ) - > noload_decls ( ) ) {
if ( auto VD = dyn_cast < VarDecl > ( D ) )
if ( VD - > isStaticLocal ( ) ) {
GlobalDecl GD ( VD ) ;
MaybeRemoveDeclFromModule ( GD ) ;
}
2016-06-26 11:06:18 +03:00
}
}
2023-08-29 17:43:05 +03:00
2016-06-26 11:06:18 +03:00
// VisitRedeclarable() will mess around with this!
bool wasCanonical = FD - > isCanonicalDecl ( ) ;
// FunctionDecl : DeclaratiorDecl, DeclContext, Redeclarable
// We start with the decl context first, because parameters are part of the
// DeclContext and when trying to remove them we need the full redecl chain
// still in place.
2017-02-10 10:30:08 +03:00
bool Successful = VisitDeclContext ( FD ) ;
2023-08-29 17:43:05 +03:00
// The body of member functions of a templated class only gets instantiated
// when the function is used, i.e.
// `-ClassTemplateDecl
// |-TemplateTypeParmDecl referenced typename depth 0 index 0 T
// |-CXXRecordDecl struct Foo definition
// | |-DefinitionData
// | `-CXXMethodDecl f 'T (T)'
// | |-ParmVarDecl 0x55e5787cac70 referenced x 'T'
// | `-CompoundStmt
// | `-ReturnStmt
// | `-DeclRefExpr 'T' lvalue ParmVar 0x55e5787cac70 'x' 'T'
// `-ClassTemplateSpecializationDecl struct Foo definition
// |-DefinitionData
// |-TemplateArgument type 'int'
// | `-BuiltinType 'int'
// |-CXXMethodDecl f 'int (int)' <<<< Instantiation pending
// | `-ParmVarDecl x 'int':'int'
// |-CXXConstructorDecl implicit used constexpr Foo 'void () noexcept'
// inline default trivial
//
// Such functions should not be deleted from the AST, but returned to the
// 'pending instantiation' state.
if ( auto MSI = FD - > getMemberSpecializationInfo ( ) ) {
MSI - > setPointOfInstantiation ( SourceLocation ( ) ) ;
MSI - > setTemplateSpecializationKind (
TemplateSpecializationKind : : TSK_ImplicitInstantiation ) ;
FD - > setBody ( nullptr ) ;
FD - > setInstantiationIsPending ( true ) ;
return Successful ;
}
2017-02-10 10:30:08 +03:00
Successful & = VisitRedeclarable ( FD , FD - > getDeclContext ( ) ) ;
2016-06-26 11:06:18 +03:00
Successful & = VisitDeclaratorDecl ( FD ) ;
2023-11-26 16:14:44 +03:00
if ( RemoveSpec & & FD - > isFunctionTemplateSpecialization ( ) & & wasCanonical ) {
2016-06-26 11:06:18 +03:00
// Only the canonical declarations are registered in the list of the
// specializations.
FunctionTemplateDecl * FTD
= FD - > getTemplateSpecializationInfo ( ) - > getTemplate ( ) ;
// The canonical declaration of every specialization is registered with
// the FunctionTemplateDecl.
// Note this might unload too much in the case:
// template<typename T> T f(){ return T();}
// template<> int f();
// template<> int f() { return 0;}
// when the template specialization was forward declared the canonical
// becomes the first forward declaration. If the canonical forward
// declaration was declared outside the set of the decls to unload we have
// to keep it registered as a template specialization.
//
// In order to diagnose mismatches of the specializations, clang 'injects'
// a implicit forward declaration making it very hard distinguish between
// the explicit and the implicit forward declaration. So far the only way
// to distinguish is by source location comparison.
// FIXME: When the misbehavior of clang is fixed we must avoid relying on
// source locations
FunctionTemplateDeclExt : : removeSpecialization ( FTD , FD ) ;
}
return Successful ;
}
2023-11-26 16:14:44 +03:00
bool DeclUnloader : : VisitFunctionDecl ( FunctionDecl * FD ) {
return VisitFunctionDecl ( FD , /*RemoveSpec=*/ true ) ;
}
2016-06-26 11:06:18 +03:00
bool DeclUnloader : : VisitCXXConstructorDecl ( CXXConstructorDecl * CXXCtor ) {
// Cleanup the module if the transaction was committed and code was
// generated. This has to go first, because it may need the AST information
// which we will remove soon. (Eg. mangleDeclName iterates the redecls)
// Brute-force all possibly generated ctors.
// Ctor_Complete Complete object ctor.
// Ctor_Base Base object ctor.
// Ctor_Comdat The COMDAT used for ctors.
GlobalDecl GD ( CXXCtor , Ctor_Complete ) ;
MaybeRemoveDeclFromModule ( GD ) ;
GD = GlobalDecl ( CXXCtor , Ctor_Base ) ;
MaybeRemoveDeclFromModule ( GD ) ;
GD = GlobalDecl ( CXXCtor , Ctor_Comdat ) ;
MaybeRemoveDeclFromModule ( GD ) ;
bool Successful = VisitCXXMethodDecl ( CXXCtor ) ;
return Successful ;
}
bool DeclUnloader : : VisitCXXDestructorDecl ( CXXDestructorDecl * CXXDtor ) {
// Cleanup the module if the transaction was committed and code was
// generated. This has to go first, because it may need the AST information
// which we will remove soon. (Eg. mangleDeclName iterates the redecls)
// Brute-force all possibly generated dtors.
// Dtor_Deleting Deleting dtor.
// Dtor_Complete Complete object dtor.
// Dtor_Base Base object dtor.
// Dtor_Comdat The COMDAT used for dtors.
GlobalDecl GD ( CXXDtor , Dtor_Deleting ) ;
MaybeRemoveDeclFromModule ( GD ) ;
GD = GlobalDecl ( CXXDtor , Dtor_Complete ) ;
MaybeRemoveDeclFromModule ( GD ) ;
GD = GlobalDecl ( CXXDtor , Dtor_Base ) ;
MaybeRemoveDeclFromModule ( GD ) ;
GD = GlobalDecl ( CXXDtor , Dtor_Comdat ) ;
MaybeRemoveDeclFromModule ( GD ) ;
bool Successful = VisitCXXMethodDecl ( CXXDtor ) ;
return Successful ;
}
bool DeclUnloader : : VisitDeclContext ( DeclContext * DC ) {
bool Successful = true ;
2023-05-18 15:35:51 +03:00
llvm : : SmallVector < Decl * , 64 > tagDecls , otherDecls ;
// The order in which declarations are removed makes a difference, e.g.
// `MaybeRemoveDeclFromModule()` may require access to type information to
// make up the mangled name.
// Thus, we segregate declarations to be removed in `TagDecl`s (i.e., struct
// / union / class / enum) and other declarations. Removal of `TagDecl`s
// is deferred until all the other declarations have been processed.
// Declarations in each group are iterated in reverse order.
// Note that removing from single-linked list invalidates the iterators.
2016-06-26 11:06:18 +03:00
for ( DeclContext : : decl_iterator I = DC - > noload_decls_begin ( ) ;
I ! = DC - > noload_decls_end ( ) ; + + I ) {
2023-05-18 15:35:51 +03:00
if ( isa < TagDecl > ( * I ) )
tagDecls . push_back ( * I ) ;
else
otherDecls . push_back ( * I ) ;
2016-06-26 11:06:18 +03:00
}
2023-05-18 15:35:51 +03:00
for ( auto I = otherDecls . rbegin ( ) , E = otherDecls . rend ( ) ; I ! = E ; + + I ) {
Successful = Visit ( * I ) & & Successful ;
assert ( Successful ) ;
}
for ( auto I = tagDecls . rbegin ( ) , E = tagDecls . rend ( ) ; I ! = E ; + + I ) {
2016-06-26 11:06:18 +03:00
Successful = Visit ( * I ) & & Successful ;
assert ( Successful ) ;
}
return Successful ;
}
bool DeclUnloader : : VisitNamespaceDecl ( NamespaceDecl * NSD ) {
2022-02-28 19:40:45 +03:00
// The first declaration of an unnamed namespace, creates an implicit
// UsingDirectiveDecl that makes the names available in the parent DC (see
// `Sema::ActOnStartNamespaceDef()`).
// If we are reverting such first declaration, make sure we reset the
// anonymous namespace for the parent DeclContext so that the
// implicit UsingDirectiveDecl is created again when parsing the next
// anonymous namespace.
if ( NSD - > isAnonymousNamespace ( ) & & NSD - > isFirstDecl ( ) ) {
auto Parent = NSD - > getParent ( ) ;
if ( auto TU = dyn_cast < TranslationUnitDecl > ( Parent ) ) {
TU - > setAnonymousNamespace ( nullptr ) ;
} else if ( auto NS = dyn_cast < NamespaceDecl > ( Parent ) ) {
NS - > setAnonymousNamespace ( nullptr ) ;
}
}
2016-06-26 11:06:18 +03:00
// NamespaceDecl: NamedDecl, DeclContext, Redeclarable
2017-02-10 10:30:08 +03:00
bool Successful = VisitDeclContext ( NSD ) ;
Successful & = VisitRedeclarable ( NSD , NSD - > getDeclContext ( ) ) ;
2016-06-26 11:06:18 +03:00
Successful & = VisitNamedDecl ( NSD ) ;
return Successful ;
}
2016-06-13 17:56:42 +03:00
bool DeclUnloader : : VisitLinkageSpecDecl ( LinkageSpecDecl * LSD ) {
// LinkageSpecDecl: DeclContext
2016-06-21 04:05:32 +03:00
// Re-add any previous declarations so they are reachable throughout the
// translation unit. Also remove any global variables from:
// m_Sema->Context.getExternCContextDecl()
2016-06-13 17:56:42 +03:00
if ( LSD - > isExternCContext ( ) ) {
// Sadly ASTContext::getExternCContextDecl will create if it doesn't exist
// Hopefully LSD->isExternCContext() means that it already does exist
2016-06-21 04:05:32 +03:00
ExternCContextDecl * ECD = m_Sema - > Context . getExternCContextDecl ( ) ;
StoredDeclsMap * Map = ECD ? ECD - > getLookupPtr ( ) : nullptr ;
2022-09-13 09:34:32 +03:00
2016-06-21 04:05:32 +03:00
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 ) ;
2016-06-13 17:56:42 +03:00
}
}
}
bool Successful = VisitDeclContext ( LSD ) ;
Successful & = VisitDecl ( LSD ) ;
return Successful ;
}
2016-06-26 11:06:18 +03:00
bool DeclUnloader : : VisitTagDecl ( TagDecl * TD ) {
// TagDecl: TypeDecl, DeclContext, Redeclarable
2017-02-10 10:30:08 +03:00
bool Successful = VisitDeclContext ( TD ) ;
Successful & = VisitRedeclarable ( TD , TD - > getDeclContext ( ) ) ;
2016-06-26 11:06:18 +03:00
Successful & = VisitTypeDecl ( TD ) ;
return Successful ;
}
bool DeclUnloader : : VisitRecordDecl ( RecordDecl * RD ) {
if ( RD - > isInjectedClassName ( ) )
return true ;
/// The injected class name in C++ is the name of the class that
/// appears inside the class itself. For example:
///
/// \code
/// struct C {
/// // C is implicitly declared here as a synonym for the class name.
/// };
///
/// C::C c; // same as "C c;"
/// \endcode
// It is another question why it is on the redecl chain.
// The test show it can be either:
// ... <- InjectedC <- C <- ..., i.e previous decl or
// ... <- C <- InjectedC <- ...
RecordDecl * InjectedRD = RD - > getPreviousDecl ( ) ;
if ( ! ( InjectedRD & & InjectedRD - > isInjectedClassName ( ) ) ) {
InjectedRD = RD - > getMostRecentDecl ( ) ;
while ( InjectedRD ) {
if ( InjectedRD - > isInjectedClassName ( )
& & InjectedRD - > getPreviousDecl ( ) = = RD )
break ;
InjectedRD = InjectedRD - > getPreviousDecl ( ) ;
}
}
bool Successful = true ;
if ( InjectedRD ) {
assert ( InjectedRD - > isInjectedClassName ( ) & & " Not injected classname? " ) ;
Successful & = VisitRedeclarable ( InjectedRD , InjectedRD - > getDeclContext ( ) ) ;
}
Successful & = VisitTagDecl ( RD ) ;
return Successful ;
}
2016-12-01 20:30:25 +03:00
void DeclUnloader : : MaybeRemoveDeclFromModule ( GlobalDecl & GD ) const {
2020-11-03 13:58:01 +03:00
if ( ! m_CurTransaction | | ! m_CodeGen ) // syntax-only mode exit
2016-06-26 11:06:18 +03:00
return ;
using namespace llvm ;
if ( const auto VD = dyn_cast_or_null < clang : : ValueDecl > ( GD . getDecl ( ) ) ) {
const QualType QT = VD - > getType ( ) ;
if ( ! QT . isNull ( ) & & QT - > isDependentType ( ) ) {
// The module cannot contain symbols for dependent decls.
return ;
}
}
// if it was successfully removed from the AST we have to check whether
// code was generated and remove it.
// From llvm's mailing list, explanation of the RAUW'd assert:
//
// The problem isn't with your call to
// replaceAllUsesWith per se, the problem is that somebody (I would guess
// the JIT?) is holding it in a ValueMap.
//
// We used to have a problem that some parts of the code would keep a
// mapping like so:
// std::map<Value *, ...>
// while somebody else would modify the Value* without them noticing,
// leading to a dangling pointer in the map. To fix that, we invented the
// ValueMap which puts a Use that doesn't show up in the use_iterator on
// the Value it holds. When the Value is erased or RAUW'd, the ValueMap is
// notified and in this case decides that's not okay and terminates the
// program.
//
// Probably what's happened here is that the calling function has had its
// code generated by the JIT, but not the callee. Thus the JIT emitted a
// call to a generated stub, and will do the codegen of the callee once
// that stub is reached. Of course, once the JIT is in this state, it holds
// on to the Function with a ValueMap in order to prevent things from
// getting out of sync.
//
if ( m_CurTransaction - > getState ( ) = = Transaction : : kCommitted ) {
2017-03-30 10:46:13 +03:00
2016-06-26 11:06:18 +03:00
std : : string mangledName ;
2017-03-30 10:46:13 +03:00
{
2020-10-05 09:22:33 +03:00
# if _WIN32
2017-03-30 10:46:13 +03:00
// clang cannot mangle everything in the ms-abi.
# ifndef NDEBUG
utils : : DiagnosticsStore Errors ( m_Sema - > getDiagnostics ( ) , false , false ) ;
2024-02-02 13:11:47 +03:00
assert ( Errors . empty ( ) | |
( Errors . size ( ) = = 1 & &
Errors [ 0 ] . getMessage ( ) . starts_with ( " cannot mangle this " ) ) ) ;
2017-03-30 10:46:13 +03:00
# else
utils : : DiagnosticsOverride IgnoreMangleErrors ( m_Sema - > getDiagnostics ( ) ) ;
# endif
# endif
utils : : Analyze : : maybeMangleDeclName ( GD , mangledName ) ;
}
2016-12-01 20:30:25 +03:00
// Handle static locals. void func() { static int var; } is represented
// in the llvm::Module is a global named @func.var
if ( const VarDecl * VD = dyn_cast < VarDecl > ( GD . getDecl ( ) ) ) {
if ( VD - > isStaticLocal ( ) ) {
std : : string functionMangledName ;
GlobalDecl FDGD ( cast < FunctionDecl > ( VD - > getDeclContext ( ) ) ) ;
utils : : Analyze : : maybeMangleDeclName ( FDGD , functionMangledName ) ;
mangledName = functionMangledName + " . " + mangledName ;
2016-06-26 11:06:18 +03:00
}
2016-12-01 20:30:25 +03:00
}
2016-06-26 11:06:18 +03:00
2020-11-03 13:58:01 +03:00
if ( auto M = m_CurTransaction - > getModule ( ) ) {
GlobalValue * GV = M - > getNamedValue ( mangledName ) ;
if ( GV ) { // May be deferred decl and thus 0
GlobalValueEraser GVEraser ( m_CodeGen ) ;
GVEraser . EraseGlobalValue ( GV ) ;
}
2016-06-26 11:06:18 +03:00
}
2020-11-03 13:58:01 +03:00
// DeferredDecls exist even without Module.
2023-07-11 17:52:29 +03:00
m_CodeGen - > forgetDecl ( mangledName ) ;
2016-06-26 11:06:18 +03:00
}
}
bool DeclUnloader : : VisitMacro ( Transaction : : MacroDirectiveInfo MacroD ) {
assert ( MacroD . m_MD & & " The MacroDirective is null " ) ;
assert ( MacroD . m_II & & " The IdentifierInfo is null " ) ;
CollectFilesToUncache ( MacroD . m_MD - > getLocation ( ) ) ;
Preprocessor & PP = m_Sema - > getPreprocessor ( ) ;
# ifndef NDEBUG
bool ExistsInPP = false ;
// Make sure the macro is in the Preprocessor. Not sure if not redundant
// because removeMacro looks for the macro anyway in the DenseMap Macros[]
for ( Preprocessor : : macro_iterator
I = PP . macro_begin ( /*IncludeExternalMacros*/ false ) ,
E = PP . macro_end ( /*IncludeExternalMacros*/ false ) ; E ! = I ; + + I ) {
if ( ( * I ) . first = = MacroD . m_II ) {
// FIXME:check whether we have the concrete directive on the macro chain
// && (*I).second == MacroD.m_MD
ExistsInPP = true ;
break ;
}
}
assert ( ExistsInPP & & " Not in the Preprocessor?! " ) ;
# endif
const MacroDirective * MD = MacroD . m_MD ;
// Undef the definition
const MacroInfo * MI = MD - > getMacroInfo ( ) ;
// If the macro is not defined, this is a noop undef, just return.
2022-09-13 09:34:32 +03:00
if ( ! MI )
2016-06-26 11:06:18 +03:00
return false ;
// Remove the pair from the macros
PP . removeMacro ( MacroD . m_II , const_cast < MacroDirective * > ( MacroD . m_MD ) ) ;
return true ;
}
bool DeclUnloader : : VisitRedeclarableTemplateDecl ( RedeclarableTemplateDecl * R ) {
// RedeclarableTemplateDecl: TemplateDecl, Redeclarable
bool Successful = VisitRedeclarable ( R , R - > getDeclContext ( ) ) ;
Successful & = VisitTemplateDecl ( R ) ;
return Successful ;
}
bool DeclUnloader : : VisitFunctionTemplateDecl ( FunctionTemplateDecl * FTD ) {
bool Successful = true ;
2023-11-26 16:14:44 +03:00
// Remove specializations, but do not invalidate the iterator!
2023-06-30 17:39:18 +03:00
for ( FunctionTemplateDecl : : spec_iterator I = FTD - > loaded_spec_begin ( ) ,
E = FTD - > loaded_spec_end ( ) ; I ! = E ; + + I )
2023-11-26 16:14:44 +03:00
Successful & = VisitFunctionDecl ( * I , /*RemoveSpec=*/ false ) ;
2016-06-26 11:06:18 +03:00
Successful & = VisitRedeclarableTemplateDecl ( FTD ) ;
Successful & = VisitFunctionDecl ( FTD - > getTemplatedDecl ( ) ) ;
return Successful ;
}
bool DeclUnloader : : VisitClassTemplateDecl ( ClassTemplateDecl * CTD ) {
// ClassTemplateDecl: TemplateDecl, Redeclarable
bool Successful = true ;
2023-11-26 16:14:44 +03:00
// Remove specializations, but do not invalidate the iterator!
2023-06-30 17:39:18 +03:00
for ( ClassTemplateDecl : : spec_iterator I = CTD - > loaded_spec_begin ( ) ,
E = CTD - > loaded_spec_end ( ) ; I ! = E ; + + I )
2023-11-26 16:14:44 +03:00
Successful & =
VisitClassTemplateSpecializationDecl ( * I , /*RemoveSpec=*/ false ) ;
2016-06-26 11:06:18 +03:00
Successful & = VisitRedeclarableTemplateDecl ( CTD ) ;
Successful & = Visit ( CTD - > getTemplatedDecl ( ) ) ;
return Successful ;
}
bool DeclUnloader : : VisitClassTemplateSpecializationDecl (
2023-11-26 16:14:44 +03:00
ClassTemplateSpecializationDecl * CTSD , bool RemoveSpec ) {
2016-06-26 11:06:18 +03:00
// ClassTemplateSpecializationDecl: CXXRecordDecl, FoldingSet
bool Successful = VisitCXXRecordDecl ( CTSD ) ;
2023-11-26 16:14:44 +03:00
if ( RemoveSpec ) {
ClassTemplateSpecializationDecl * CanonCTSD =
static_cast < ClassTemplateSpecializationDecl * > (
CTSD - > getCanonicalDecl ( ) ) ;
if ( auto D = dyn_cast < ClassTemplatePartialSpecializationDecl > ( CanonCTSD ) )
ClassTemplateDeclExt : : removePartialSpecialization (
D - > getSpecializedTemplate ( ) , D ) ;
else
ClassTemplateDeclExt : : removeSpecialization (
CTSD - > getSpecializedTemplate ( ) , CanonCTSD ) ;
}
2016-06-26 11:06:18 +03:00
return Successful ;
}
2023-11-26 16:14:44 +03:00
bool DeclUnloader : : VisitClassTemplateSpecializationDecl (
ClassTemplateSpecializationDecl * CTSD ) {
return VisitClassTemplateSpecializationDecl ( CTSD , /*RemoveSpec=*/ true ) ;
}
2023-11-25 00:15:41 +03:00
bool DeclUnloader : : VisitVarTemplateDecl ( VarTemplateDecl * VTD ) {
// VarTemplateDecl: TemplateDecl, Redeclarable
bool Successful = true ;
// Remove specializations, but do not invalidate the iterator!
for ( VarTemplateDecl : : spec_iterator I = VTD - > loaded_spec_begin ( ) ,
E = VTD - > loaded_spec_end ( ) ;
I ! = E ; + + I )
Successful & =
VisitVarTemplateSpecializationDecl ( * I , /*RemoveSpec=*/ false ) ;
Successful & = VisitRedeclarableTemplateDecl ( VTD ) ;
Successful & = Visit ( VTD - > getTemplatedDecl ( ) ) ;
return Successful ;
}
bool DeclUnloader : : VisitVarTemplateSpecializationDecl (
VarTemplateSpecializationDecl * VTSD , bool RemoveSpec ) {
// VarTemplateSpecializationDecl: VarDecl, FoldingSet
bool Successful = VisitVarDecl ( VTSD ) ;
if ( RemoveSpec ) {
VarTemplateSpecializationDecl * CanonVTSD =
static_cast < VarTemplateSpecializationDecl * > ( VTSD - > getCanonicalDecl ( ) ) ;
if ( auto D = dyn_cast < VarTemplatePartialSpecializationDecl > ( CanonVTSD ) )
VarTemplateDeclExt : : removePartialSpecialization (
D - > getSpecializedTemplate ( ) , D ) ;
else
VarTemplateDeclExt : : removeSpecialization ( VTSD - > getSpecializedTemplate ( ) ,
CanonVTSD ) ;
}
return Successful ;
}
bool DeclUnloader : : VisitVarTemplateSpecializationDecl (
VarTemplateSpecializationDecl * VTSD ) {
return VisitVarTemplateSpecializationDecl ( VTSD , /*RemoveSpec=*/ true ) ;
}
2016-09-10 00:23:32 +03:00
} // end namespace cling