From 5cf51d53dd9175036656786b723b7f1108ff251b Mon Sep 17 00:00:00 2001 From: Jonas Hahnfeld Date: Fri, 24 Nov 2023 22:15:41 +0100 Subject: [PATCH] Handle variable templates in DeclUnloader Fixes #13815 --- lib/Interpreter/DeclUnloader.cpp | 68 +++++++++++++++++++++++++++++ lib/Interpreter/DeclUnloader.h | 28 ++++++++++++ test/ErrorRecovery/IncompleteType.C | 32 ++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 test/ErrorRecovery/IncompleteType.C diff --git a/lib/Interpreter/DeclUnloader.cpp b/lib/Interpreter/DeclUnloader.cpp index d2f0b3adaf..783db326eb 100644 --- a/lib/Interpreter/DeclUnloader.cpp +++ b/lib/Interpreter/DeclUnloader.cpp @@ -408,6 +408,37 @@ namespace { removeSpecializationImpl(specs, spec); } }; + + // 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!?"); + + auto* This = static_cast(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(self); + auto& specs = This->getPartialSpecializations(); + removeSpecializationImpl(specs, spec); + } + }; } // end anonymous namespace namespace cling { @@ -1017,4 +1048,41 @@ namespace cling { 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/lib/Interpreter/DeclUnloader.h b/lib/Interpreter/DeclUnloader.h index 6a00309b15..720cbc30b2 100644 --- a/lib/Interpreter/DeclUnloader.h +++ b/lib/Interpreter/DeclUnloader.h @@ -258,6 +258,34 @@ namespace cling { bool VisitClassTemplateSpecializationDecl( 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); + ///@} void MaybeRemoveDeclFromModule(clang::GlobalDecl& GD) const; diff --git a/test/ErrorRecovery/IncompleteType.C b/test/ErrorRecovery/IncompleteType.C new file mode 100644 index 0000000000..37b778552d --- /dev/null +++ b/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