From 165486275593675d154a0ff431888c27afcc6da8 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski Date: Wed, 16 Oct 2024 08:40:03 -0600 Subject: [PATCH] [Clang][Sema] Use the correct injected template arguments for partial specializations when collecting multi-level template argument lists (#112381) After #111852 refactored multi-level template argument list collection, the following results in a crash: ``` template struct A; template struct A { void f() requires B; }; template void A::f() requires B { } // crash here ``` This happens because when collecting template arguments for constraint normalization from a partial specialization, we incorrectly use the template argument list of the partial specialization. We should be using the template argument list of the _template-head_ (as defined in [temp.arg.general] p2). Fixes #112222. --- clang/include/clang/AST/DeclTemplate.h | 18 +- clang/lib/AST/DeclTemplate.cpp | 28 ++ clang/lib/Sema/SemaTemplateInstantiate.cpp | 4 +- .../temp/temp.constr/temp.constr.decl/p4.cpp | 284 ++++++++++-------- 4 files changed, 211 insertions(+), 123 deletions(-) diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 141f58c4600af0..0f0c0bf6e4ef4f 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -2085,7 +2085,11 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl, class ClassTemplatePartialSpecializationDecl : public ClassTemplateSpecializationDecl { /// The list of template parameters - TemplateParameterList* TemplateParams = nullptr; + TemplateParameterList *TemplateParams = nullptr; + + /// The set of "injected" template arguments used within this + /// partial specialization. + TemplateArgument *InjectedArgs = nullptr; /// The class template partial specialization from which this /// class template partial specialization was instantiated. @@ -2132,6 +2136,10 @@ class ClassTemplatePartialSpecializationDecl return TemplateParams; } + /// Retrieve the template arguments list of the template parameter list + /// of this template. + ArrayRef getInjectedTemplateArgs(); + /// \brief All associated constraints of this partial specialization, /// including the requires clause and any constraints derived from /// constrained-parameters. @@ -2856,6 +2864,10 @@ class VarTemplatePartialSpecializationDecl /// The list of template parameters TemplateParameterList *TemplateParams = nullptr; + /// The set of "injected" template arguments used within this + /// partial specialization. + TemplateArgument *InjectedArgs = nullptr; + /// The variable template partial specialization from which this /// variable template partial specialization was instantiated. /// @@ -2902,6 +2914,10 @@ class VarTemplatePartialSpecializationDecl return TemplateParams; } + /// Retrieve the template arguments list of the template parameter list + /// of this template. + ArrayRef getInjectedTemplateArgs(); + /// \brief All associated constraints of this partial specialization, /// including the requires clause and any constraints derived from /// constrained-parameters. diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index d9b67b7bedf5a5..d2d8907b884ec8 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -1185,6 +1185,20 @@ SourceRange ClassTemplatePartialSpecializationDecl::getSourceRange() const { return Range; } +ArrayRef +ClassTemplatePartialSpecializationDecl::getInjectedTemplateArgs() { + TemplateParameterList *Params = getTemplateParameters(); + auto *First = cast(getFirstDecl()); + if (!First->InjectedArgs) { + auto &Context = getASTContext(); + SmallVector TemplateArgs; + Context.getInjectedTemplateArgs(Params, TemplateArgs); + First->InjectedArgs = new (Context) TemplateArgument[TemplateArgs.size()]; + std::copy(TemplateArgs.begin(), TemplateArgs.end(), First->InjectedArgs); + } + return llvm::ArrayRef(First->InjectedArgs, Params->size()); +} + //===----------------------------------------------------------------------===// // FriendTemplateDecl Implementation //===----------------------------------------------------------------------===// @@ -1535,6 +1549,20 @@ SourceRange VarTemplatePartialSpecializationDecl::getSourceRange() const { return Range; } +ArrayRef +VarTemplatePartialSpecializationDecl::getInjectedTemplateArgs() { + TemplateParameterList *Params = getTemplateParameters(); + auto *First = cast(getFirstDecl()); + if (!First->InjectedArgs) { + auto &Context = getASTContext(); + SmallVector TemplateArgs; + Context.getInjectedTemplateArgs(Params, TemplateArgs); + First->InjectedArgs = new (Context) TemplateArgument[TemplateArgs.size()]; + std::copy(TemplateArgs.begin(), TemplateArgs.end(), First->InjectedArgs); + } + return llvm::ArrayRef(First->InjectedArgs, Params->size()); +} + static TemplateParameterList * createMakeIntegerSeqParameterList(const ASTContext &C, DeclContext *DC) { // typename T diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 8c7f694c09042e..8665c099903dc3 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -237,7 +237,7 @@ struct TemplateInstantiationArgumentCollecter if (Innermost) AddInnermostTemplateArguments(VTPSD); else if (ForConstraintInstantiation) - AddOuterTemplateArguments(VTPSD, VTPSD->getTemplateArgs().asArray(), + AddOuterTemplateArguments(VTPSD, VTPSD->getInjectedTemplateArgs(), /*Final=*/false); if (VTPSD->isMemberSpecialization()) @@ -274,7 +274,7 @@ struct TemplateInstantiationArgumentCollecter if (Innermost) AddInnermostTemplateArguments(CTPSD); else if (ForConstraintInstantiation) - AddOuterTemplateArguments(CTPSD, CTPSD->getTemplateArgs().asArray(), + AddOuterTemplateArguments(CTPSD, CTPSD->getInjectedTemplateArgs(), /*Final=*/false); if (CTPSD->isMemberSpecialization()) diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.decl/p4.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.decl/p4.cpp index 70064f867e18e3..f144e14cd122f9 100644 --- a/clang/test/CXX/temp/temp.constr/temp.constr.decl/p4.cpp +++ b/clang/test/CXX/temp/temp.constr/temp.constr.decl/p4.cpp @@ -1,175 +1,219 @@ // RUN: %clang_cc1 -std=c++20 -verify %s // expected-no-diagnostics -template -concept D = true; +namespace Primary { + template + concept D = true; -template -struct A { - template - void f() requires V; + template + struct A { + template + void f() requires V; - template<> - void f(); + template<> + void f(); + + template + void g(); + + template requires V + struct B; + + template requires V + struct B; + + template<> + struct B; + + template + struct C; + + template + struct C; + template requires V + static int x; + + template requires V + static int x; + + template<> + int x; + + template + static int y; + + template + static int y; + }; + + template + template + void A::f() requires V { } + + template template - void g(); + void A::g() { } + template template requires V - struct B; + struct A::B { }; + template template requires V - struct B; + struct A::B { }; - template<> - struct B; + template + template requires V + struct A::B { }; + template template - struct C; + struct A::C { }; + template template - struct C; + struct A::C { }; + template template requires V - static int x; + int A::x = 0; + template template requires V - static int x; + int A::x = 0; - template<> - int x; + template + template requires V + int A::x = 0; + template template - static int y; + int A::y = 0; + template template - static int y; -}; - -template -template -void A::f() requires V { } + int A::y = 0; -template -template -void A::g() { } - -template -template requires V -struct A::B { }; + template<> + template + void A::f() requires V; -template -template requires V -struct A::B { }; + template<> + template<> + void A::f(); -template -template requires V -struct A::B { }; + template<> + template<> + void A::f(); -template -template -struct A::C { }; + template<> + template + void A::g(); -template -template -struct A::C { }; + template<> + template requires V + struct A::B; -template -template requires V -int A::x = 0; + template<> + template<> + struct A::B; -template -template requires V -int A::x = 0; + template<> + template<> + struct A::B; -template -template requires V -int A::x = 0; + template<> + template requires V + struct A::B; -template -template -int A::y = 0; + template<> + template requires V + struct A::B; -template -template -int A::y = 0; + template<> + template + struct A::C; -template<> -template -void A::f() requires V; + template<> + template + struct A::C; -template<> -template<> -void A::f(); + template<> + template + struct A::C; -template<> -template<> -void A::f(); + template<> + template requires V + int A::x; -template<> -template -void A::g(); + template<> + template<> + int A::x; -template<> -template requires V -struct A::B; + template<> + template<> + int A::x; -template<> -template<> -struct A::B; + template<> + template requires V + int A::x; -template<> -template<> -struct A::B; + template<> + template requires V + int A::x; -template<> -template requires V -struct A::B; + template<> + template + int A::y; -template<> -template requires V -struct A::B; + template<> + template + int A::y; -template<> -template -struct A::C; + template<> + template + int A::y; +} // namespace Primary -template<> -template -struct A::C; +namespace Partial { + template + struct A; -template<> -template -struct A::C; + template + struct A + { + template requires U + void f(); -template<> -template requires V -int A::x; + template requires U + static const int x; -template<> -template<> -int A::x; + template requires U + struct B; + }; -template<> -template<> -int A::x; + template + template requires U + void A::f() { } -template<> -template requires V -int A::x; + template + template requires U + constexpr int A::x = 0; -template<> -template requires V -int A::x; + template + template requires U + struct A::B { }; -template<> -template -int A::y; + template<> + template requires true + void A::f() { } -template<> -template -int A::y; + template<> + template requires true + constexpr int A::x = 1; -template<> -template -int A::y; + template<> + template requires true + struct A::B { }; +} // namespace Partial