diff --git a/interpreter/cling/lib/Interpreter/DeclUnloader.cpp b/interpreter/cling/lib/Interpreter/DeclUnloader.cpp index 85874db9a5f42..783db326eb252 100644 --- a/interpreter/cling/lib/Interpreter/DeclUnloader.cpp +++ b/interpreter/cling/lib/Interpreter/DeclUnloader.cpp @@ -327,6 +327,22 @@ namespace { } } + template + void removeSpecializationImpl(llvm::FoldingSetVector& Specs, + const EntryType* Entry) { + // Remove only Entry from Specs, keep all others. + llvm::FoldingSetVector Keep; + for (auto& Spec : Specs) { + if (&Spec != Entry) { + // Avoid assertion on add. + Spec.SetNextInBucket(nullptr); + Keep.InsertNode(&Spec); + } + } + + std::swap(Specs, Keep); + } + // Template instantiation of templated function first creates a canonical // declaration and after the actual template specialization. For example: // template T TemplatedF(T t); @@ -344,43 +360,18 @@ namespace { class FunctionTemplateDeclExt : public FunctionTemplateDecl { public: static void removeSpecialization(FunctionTemplateDecl* self, - const FunctionDecl* specialization) { - assert(self && specialization && "Cannot be null!"); - assert(specialization == specialization->getCanonicalDecl() && + const FunctionDecl* spec) { + assert(self && spec && "Cannot be null!"); + assert(spec == spec->getCanonicalDecl() && "Not the canonical specialization!?"); - typedef llvm::SmallVector Specializations; - typedef llvm::FoldingSetVector Set; - FunctionTemplateDeclExt* This = (FunctionTemplateDeclExt*)self; - Specializations specializations; - const Set& specs = This->getCommonPtr()->Specializations; + auto* This = static_cast(self); + auto& specs = This->getCommonPtr()->Specializations; + removeSpecializationImpl(specs, spec->getTemplateSpecializationInfo()); - if (!specs.size()) // nothing to remove - return; - - // Collect all the specializations without the one to remove. - for (Set::const_iterator I = specs.begin(), E = specs.end(); I != E; - ++I) { - assert(I->getFunction() && "Must have a specialization."); - if (I->getFunction() != specialization) - specializations.push_back(I->getFunction()); - } - - This->getCommonPtr()->Specializations.clear(); - - // Readd the collected specializations. - void* InsertPos = nullptr; - FunctionTemplateSpecializationInfo* FTSI = nullptr; - for (size_t i = 0, e = specializations.size(); i < e; ++i) { - FTSI = specializations[i]->getTemplateSpecializationInfo(); - assert(FTSI && "Must not be null."); - // Avoid assertion on add. - FTSI->SetNextInBucket(nullptr); - This->addSpecialization(FTSI, InsertPos); - } #ifndef NDEBUG - const TemplateArgumentList* args = - specialization->getTemplateSpecializationArgs(); + const TemplateArgumentList* args = spec->getTemplateSpecializationArgs(); + void* InsertPos = nullptr; assert(!self->findSpecialization(args->asArray(), InsertPos) && "Finds the removed decl again!"); #endif @@ -399,35 +390,10 @@ namespace { assert(self && spec && "Cannot be null!"); assert(spec == spec->getCanonicalDecl() && "Not the canonical specialization!?"); - typedef llvm::SmallVector - Specializations; - typedef llvm::FoldingSetVector Set; - ClassTemplateDeclExt* This = (ClassTemplateDeclExt*)self; - Specializations specializations; - Set& specs = This->getCommonPtr()->Specializations; - - if (!specs.size()) // nothing to remove - return; - - // Collect all the specializations without the one to remove. - for (Set::iterator I = specs.begin(), E = specs.end(); I != E; ++I) { - if (&*I != spec) - specializations.push_back(&*I); - } - - This->getCommonPtr()->Specializations.clear(); - - // Readd the collected specializations. - void* InsertPos = nullptr; - ClassTemplateSpecializationDecl* CTSD = nullptr; - for (size_t i = 0, e = specializations.size(); i < e; ++i) { - CTSD = specializations[i]; - assert(CTSD && "Must not be null."); - // Avoid assertion on add. - CTSD->SetNextInBucket(nullptr); - This->AddSpecialization(CTSD, InsertPos); - } + auto* This = static_cast(self); + auto& specs = This->getCommonPtr()->Specializations; + removeSpecializationImpl(specs, spec); } static void @@ -436,36 +402,41 @@ namespace { assert(self && spec && "Cannot be null!"); assert(spec == spec->getCanonicalDecl() && "Not the canonical specialization!?"); - typedef llvm::SmallVector - Specializations; - typedef llvm::FoldingSetVector - Set; - ClassTemplateDeclExt* This = (ClassTemplateDeclExt*)self; - Specializations specializations; - Set& specs = This->getPartialSpecializations(); + auto* This = static_cast(self); + auto& specs = This->getPartialSpecializations(); + removeSpecializationImpl(specs, spec); + } + }; - if (!specs.size()) // nothing to remove - return; + // 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(spec) && + "Use removePartialSpecialization"); + assert(self && spec && "Cannot be null!"); + assert(spec == spec->getCanonicalDecl() && + "Not the canonical specialization!?"); - // Collect all the specializations without the one to remove. - for (Set::iterator I = specs.begin(), E = specs.end(); I != E; ++I) { - if (&*I != spec) - specializations.push_back(&*I); - } + auto* This = static_cast(self); + auto& specs = This->getCommonPtr()->Specializations; + removeSpecializationImpl(specs, spec); + } - This->getPartialSpecializations().clear(); + static void + removePartialSpecialization(VarTemplateDecl* self, + VarTemplatePartialSpecializationDecl* spec) { + assert(self && spec && "Cannot be null!"); + assert(spec == spec->getCanonicalDecl() && + "Not the canonical specialization!?"); - // Readd the collected specializations. - void* InsertPos = nullptr; - ClassTemplatePartialSpecializationDecl* CTPSD = nullptr; - for (size_t i = 0, e = specializations.size(); i < e; ++i) { - CTPSD = specializations[i]; - assert(CTPSD && "Must not be null."); - // Avoid assertion on add. - CTPSD->SetNextInBucket(nullptr); - This->AddPartialSpecialization(CTPSD, InsertPos); - } + auto* This = static_cast(self); + auto& specs = This->getPartialSpecializations(); + removeSpecializationImpl(specs, spec); } }; } // end anonymous namespace @@ -636,7 +607,7 @@ namespace cling { return Successful; } - bool DeclUnloader::VisitFunctionDecl(FunctionDecl* FD) { + bool DeclUnloader::VisitFunctionDecl(FunctionDecl* FD, bool RemoveSpec) { // The Structors need to be handled differently. if (!isa(FD) && !isa(FD)) { // Cleanup the module if the transaction was committed and code was @@ -696,7 +667,7 @@ namespace cling { Successful &= VisitRedeclarable(FD, FD->getDeclContext()); Successful &= VisitDeclaratorDecl(FD); - if (FD->isFunctionTemplateSpecialization() && wasCanonical) { + if (RemoveSpec && FD->isFunctionTemplateSpecialization() && wasCanonical) { // Only the canonical declarations are registered in the list of the // specializations. FunctionTemplateDecl* FTD @@ -724,6 +695,10 @@ namespace cling { return Successful; } + bool DeclUnloader::VisitFunctionDecl(FunctionDecl* FD) { + return VisitFunctionDecl(FD, /*RemoveSpec=*/true); + } + 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 @@ -1027,10 +1002,10 @@ namespace cling { bool DeclUnloader::VisitFunctionTemplateDecl(FunctionTemplateDecl* FTD) { bool Successful = true; - // Remove specializations: + // Remove specializations, but do not invalidate the iterator! for (FunctionTemplateDecl::spec_iterator I = FTD->loaded_spec_begin(), E = FTD->loaded_spec_end(); I != E; ++I) - Successful &= Visit(*I); + Successful &= VisitFunctionDecl(*I, /*RemoveSpec=*/false); Successful &= VisitRedeclarableTemplateDecl(FTD); Successful &= VisitFunctionDecl(FTD->getTemplatedDecl()); @@ -1040,10 +1015,11 @@ namespace cling { bool DeclUnloader::VisitClassTemplateDecl(ClassTemplateDecl* CTD) { // ClassTemplateDecl: TemplateDecl, Redeclarable bool Successful = true; - // Remove specializations: + // Remove specializations, but do not invalidate the iterator! for (ClassTemplateDecl::spec_iterator I = CTD->loaded_spec_begin(), E = CTD->loaded_spec_end(); I != E; ++I) - Successful &= Visit(*I); + Successful &= + VisitClassTemplateSpecializationDecl(*I, /*RemoveSpec=*/false); Successful &= VisitRedeclarableTemplateDecl(CTD); Successful &= Visit(CTD->getTemplatedDecl()); @@ -1051,18 +1027,62 @@ namespace cling { } bool DeclUnloader::VisitClassTemplateSpecializationDecl( - ClassTemplateSpecializationDecl* CTSD) { + ClassTemplateSpecializationDecl* CTSD, bool RemoveSpec) { // ClassTemplateSpecializationDecl: CXXRecordDecl, FoldingSet bool Successful = VisitCXXRecordDecl(CTSD); - ClassTemplateSpecializationDecl* CanonCTSD = - static_cast(CTSD->getCanonicalDecl()); - if (auto D = dyn_cast(CanonCTSD)) - ClassTemplateDeclExt::removePartialSpecialization( - D->getSpecializedTemplate(), - D); - else - ClassTemplateDeclExt::removeSpecialization(CTSD->getSpecializedTemplate(), - CanonCTSD); + if (RemoveSpec) { + ClassTemplateSpecializationDecl* CanonCTSD = + static_cast( + CTSD->getCanonicalDecl()); + if (auto D = dyn_cast(CanonCTSD)) + ClassTemplateDeclExt::removePartialSpecialization( + D->getSpecializedTemplate(), D); + else + ClassTemplateDeclExt::removeSpecialization( + CTSD->getSpecializedTemplate(), CanonCTSD); + } + return Successful; + } + + bool DeclUnloader::VisitClassTemplateSpecializationDecl( + ClassTemplateSpecializationDecl* CTSD) { + return VisitClassTemplateSpecializationDecl(CTSD, /*RemoveSpec=*/true); + } + + 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(VTSD->getCanonicalDecl()); + if (auto D = dyn_cast(CanonVTSD)) + VarTemplateDeclExt::removePartialSpecialization( + D->getSpecializedTemplate(), D); + else + VarTemplateDeclExt::removeSpecialization(VTSD->getSpecializedTemplate(), + CanonVTSD); + } return Successful; } + + bool DeclUnloader::VisitVarTemplateSpecializationDecl( + VarTemplateSpecializationDecl* VTSD) { + return VisitVarTemplateSpecializationDecl(VTSD, /*RemoveSpec=*/true); + } } // end namespace cling diff --git a/interpreter/cling/lib/Interpreter/DeclUnloader.h b/interpreter/cling/lib/Interpreter/DeclUnloader.h index b5829155a96d6..720cbc30b22a8 100644 --- a/interpreter/cling/lib/Interpreter/DeclUnloader.h +++ b/interpreter/cling/lib/Interpreter/DeclUnloader.h @@ -120,6 +120,16 @@ namespace cling { /// bool VisitVarDecl(clang::VarDecl* VD); + ///\brief Removes the declaration from the lookup chains and from the + /// declaration context and it rebuilds the redeclaration chain. + /// @param[in] FD - The declaration to be removed. + /// @param[in] RemoveSpec - If a template specialization, whether to remove + /// it from the parent. + /// + ///\returns true on success. + /// + bool VisitFunctionDecl(clang::FunctionDecl* FD, bool RemoveSpec); + ///\brief Removes the declaration from the lookup chains and from the /// declaration context and it rebuilds the redeclaration chain. /// @param[in] FD - The declaration to be removed. @@ -228,6 +238,17 @@ namespace cling { /// bool VisitClassTemplateDecl(clang::ClassTemplateDecl* CTD); + ///\brief Removes a class template specialization declaration from clang's + /// internal structures. + /// @param[in] CTSD - The declaration to be removed. + /// @param[in] RemoveSpec - Whether to remove the specialization from its + /// parent. + /// + ///\returns true on success. + /// + bool VisitClassTemplateSpecializationDecl( + clang::ClassTemplateSpecializationDecl* CTSD, bool RemoveSpec); + ///\brief Removes a class template specialization declaration from clang's /// internal structures. /// @param[in] CTSD - The declaration to be removed. @@ -235,7 +256,35 @@ namespace cling { ///\returns true on success. /// bool VisitClassTemplateSpecializationDecl( - clang::ClassTemplateSpecializationDecl* CTSD); + clang::ClassTemplateSpecializationDecl* CTSD); + + ///\brief Removes a var template declaration from clang's internal + /// structures. + /// @param[in] VTD - The declaration to be removed. + /// + ///\returns true on success. + /// + bool VisitVarTemplateDecl(clang::VarTemplateDecl* VTD); + + ///\brief Removes a var template specialization declaration from clang's + /// internal structures. + /// @param[in] CTSD - The declaration to be removed. + /// @param[in] RemoveSpec - Whether to remove the specialization from its + /// parent. + /// + ///\returns true on success. + /// + bool VisitVarTemplateSpecializationDecl( + clang::VarTemplateSpecializationDecl* VTSD, bool RemoveSpec); + + ///\brief Removes a var template specialization declaration from clang's + /// internal structures. + /// @param[in] CTSD - The declaration to be removed. + /// + ///\returns true on success. + /// + bool VisitVarTemplateSpecializationDecl( + clang::VarTemplateSpecializationDecl* VTSD); ///@} diff --git a/interpreter/cling/test/ErrorRecovery/IncompleteType.C b/interpreter/cling/test/ErrorRecovery/IncompleteType.C new file mode 100644 index 0000000000000..37b778552d794 --- /dev/null +++ b/interpreter/cling/test/ErrorRecovery/IncompleteType.C @@ -0,0 +1,32 @@ +//------------------------------------------------------------------------------ +// 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 2>&1 | FileCheck %s + +#include + +struct A { int v; }; +std::is_default_constructible_v +// CHECK: (const bool) true + +struct B; +std::is_default_constructible_v +// CHECK: incomplete type 'B' +struct B { int v; }; +std::is_default_constructible_v +// CHECK: (const bool) true + +template struct C; +template <> struct C; +std::is_default_constructible_v> +// CHECK: incomplete type 'C' +template <> struct C { int v; }; +std::is_default_constructible_v> +// CHECK: (const bool) true + +.q