Author: Richard Smith Date: 2020-07-29T17:44:32-07:00 New Revision: a64883431369f28f3fac311c496a4dfad480058f
URL: https://github.com/llvm/llvm-project/commit/a64883431369f28f3fac311c496a4dfad480058f DIFF: https://github.com/llvm/llvm-project/commit/a64883431369f28f3fac311c496a4dfad480058f.diff LOG: PR46729: Reject explicit and partial specializations with C linkage. Added: clang/test/CXX/temp/temp.pre/p6.cpp Modified: clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaTemplate.cpp clang/test/SemaTemplate/class-template-decl.cpp Removed: ################################################################################ diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 9b9d164dcbc1..531c2801bf92 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6994,19 +6994,18 @@ NamedDecl *Sema::ActOnVariableDeclarator( TemplateParams->getRAngleLoc()); TemplateParams = nullptr; } else { + // Check that we can declare a template here. + if (CheckTemplateDeclScope(S, TemplateParams)) + return nullptr; + if (D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId) { // This is an explicit specialization or a partial specialization. - // FIXME: Check that we can declare a specialization here. IsVariableTemplateSpecialization = true; IsPartialSpecialization = TemplateParams->size() > 0; } else { // if (TemplateParams->size() > 0) // This is a template declaration. IsVariableTemplate = true; - // Check that we can declare a template here. - if (CheckTemplateDeclScope(S, TemplateParams)) - return nullptr; - // Only C++1y supports variable templates (N3651). Diag(D.getIdentifierLoc(), getLangOpts().CPlusPlus14 @@ -7015,6 +7014,10 @@ NamedDecl *Sema::ActOnVariableDeclarator( } } } else { + // Check that we can declare a member specialization here. + if (!TemplateParamLists.empty() && IsMemberSpecialization && + CheckTemplateDeclScope(S, TemplateParamLists.back())) + return nullptr; assert((Invalid || D.getName().getKind() != UnqualifiedIdKind::IK_TemplateId) && "should have a 'template<>' for this decl"); @@ -8941,13 +8944,13 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, TemplateParamLists, isFriend, isMemberSpecialization, Invalid); if (TemplateParams) { + // Check that we can declare a template here. + if (CheckTemplateDeclScope(S, TemplateParams)) + NewFD->setInvalidDecl(); + if (TemplateParams->size() > 0) { // This is a function template - // Check that we can declare a template here. - if (CheckTemplateDeclScope(S, TemplateParams)) - NewFD->setInvalidDecl(); - // A destructor cannot be a template. if (Name.getNameKind() == DeclarationName::CXXDestructorName) { Diag(NewFD->getLocation(), diag::err_destructor_template); @@ -9006,6 +9009,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, } } } else { + // Check that we can declare a template here. + if (!TemplateParamLists.empty() && isMemberSpecialization && + CheckTemplateDeclScope(S, TemplateParamLists.back())) + NewFD->setInvalidDecl(); + // All template param lists were matched against the scope specifier: // this is NOT (an explicit specialization of) a template. if (TemplateParamLists.size() > 0) @@ -15301,6 +15309,10 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, isMemberSpecialization = true; } } + + if (!TemplateParameterLists.empty() && isMemberSpecialization && + CheckTemplateDeclScope(S, TemplateParameterLists.back())) + return nullptr; } // Figure out the underlying type if this a enum declaration. We need to do @@ -17300,7 +17312,7 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, CXXRecordDecl *CXXRecord = cast<CXXRecordDecl>(Record); CheckForZeroSize = CXXRecord->getLexicalDeclContext()->isExternCContext() && - !CXXRecord->isDependentType() && + !CXXRecord->isDependentType() && !inTemplateInstantiation() && CXXRecord->isCLike(); } if (CheckForZeroSize) { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 76ba12303d84..3991f2b47977 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -7771,8 +7771,9 @@ Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) { (S->getFlags() & Scope::TemplateParamScope) != 0) S = S->getParent(); - // C++ [temp]p4: - // A template [...] shall not have C linkage. + // C++ [temp.pre]p6: [P2096] + // A template, explicit specialization, or partial specialization shall not + // have C linkage. DeclContext *Ctx = S->getEntity(); if (Ctx && Ctx->isExternCContext()) { Diag(TemplateParams->getTemplateLoc(), diag::err_template_linkage) @@ -7786,6 +7787,12 @@ Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) { // C++ [temp]p2: // A template-declaration can appear only as a namespace scope or // class scope declaration. + // C++ [temp.expl.spec]p3: + // An explicit specialization may be declared in any scope in which the + // corresponding primary template may be defined. + // C++ [temp.class.spec]p6: [P2096] + // A partial specialization may be declared in any scope in which the + // corresponding primary template may be defined. if (Ctx) { if (Ctx->isFileContext()) return false; @@ -8105,6 +8112,10 @@ DeclResult Sema::ActOnClassTemplateSpecialization( if (Invalid) return true; + // Check that we can declare a template specialization here. + if (TemplateParams && CheckTemplateDeclScope(S, TemplateParams)) + return true; + if (TemplateParams && TemplateParams->size() > 0) { isPartialSpecialization = true; diff --git a/clang/test/CXX/temp/temp.pre/p6.cpp b/clang/test/CXX/temp/temp.pre/p6.cpp new file mode 100644 index 000000000000..cb8c70ca3abe --- /dev/null +++ b/clang/test/CXX/temp/temp.pre/p6.cpp @@ -0,0 +1,79 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s + +// Templates and partial and explicit specializations can't have C linkage. +namespace extern_c_templates { + +template<typename T> struct A { + static int a; + struct b; + void c(); + enum class d; + + template<typename U> static int e; + template<typename U> struct f; + template<typename U> void g(); +}; + +template<typename T> int B; +template<typename T> void C(); + +extern "C" { // expected-note 1+{{begins here}} + // templates + template<typename T> struct A; // expected-error {{templates must have C++ linkage}} + template<typename T> int B; // expected-error {{templates must have C++ linkage}} + template<typename T> void C(); // expected-error {{templates must have C++ linkage}} + + // non-template members of a template + // FIXME: Should these really be valid? + template<typename T> int A<T>::a; + template<typename T> struct A<T>::b {}; + template<typename T> void A<T>::c() {} + template<typename T> enum class A<T>::d {}; + + // templates + template<typename T> template<typename U> int A<T>::e; // expected-error {{templates must have C++ linkage}} + template<typename T> template<typename U> struct A<T>::f {}; // expected-error {{templates must have C++ linkage}} + template<typename T> template<typename U> void A<T>::g() {} // expected-error {{templates must have C++ linkage}} + + // partial specializations + template<typename T> struct A<int*>; // expected-error {{templates must have C++ linkage}} + template<typename T> int B<int*>; // expected-error {{templates must have C++ linkage}} + template<typename T> template<typename U> int A<T>::e<U*>; // expected-error {{templates must have C++ linkage}} + template<typename T> template<typename U> struct A<T>::f<U*> {}; // expected-error {{templates must have C++ linkage}} + + // explicit specializations of templates + template<> struct A<char> {}; // expected-error {{templates must have C++ linkage}} + template<> int B<char>; // expected-error {{templates must have C++ linkage}} + template<> void C<char>() {} // expected-error {{templates must have C++ linkage}} + + // explicit specializations of members of a template + template<> int A<int>::a; // expected-error {{templates must have C++ linkage}} + template<> struct A<int>::b {}; // expected-error {{templates must have C++ linkage}} + template<> void A<int>::c() {} // expected-error {{templates must have C++ linkage}} + template<> enum class A<int>::d {}; // expected-error {{templates must have C++ linkage}} + + // explicit specializations of member templates + template<> template<typename U> int A<int>::e; // expected-error {{templates must have C++ linkage}} + template<> template<typename U> struct A<int>::f {}; // expected-error {{templates must have C++ linkage}} + template<> template<typename U> void A<int>::g() {} // expected-error {{templates must have C++ linkage}} +} + +// Provide valid definitions for the explicit instantiations below. +// FIXME: Our recovery from the invalid definitions above isn't very good. +template<typename T> template<typename U> int A<T>::e; +template<typename T> template<typename U> struct A<T>::f {}; +template<typename T> template<typename U> void A<T>::g() {} + +extern "C" { + // explicit instantiations + // FIXME: Should these really be valid? + template struct A<double>; + template int A<float>::a; + template struct A<float>::b; + template void A<float>::c(); + template int A<float>::e<float>; + template struct A<float>::f<float>; + template void A<float>::g<float>(); +} + +} diff --git a/clang/test/SemaTemplate/class-template-decl.cpp b/clang/test/SemaTemplate/class-template-decl.cpp index dd9dcd2de9f4..453218ac3b40 100644 --- a/clang/test/SemaTemplate/class-template-decl.cpp +++ b/clang/test/SemaTemplate/class-template-decl.cpp @@ -1,17 +1,26 @@ // RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s -template<typename T> class A; +template<typename T> class A {}; extern "C++" { - template<typename T> class B; + template<typename T> class B {}; + template<typename T> class A<T *>; + template<> class A<int[1]>; + template class A<int[2]>; + template<typename T> class B<T *>; + template<> class B<int[1]>; + template class B<int[2]>; } namespace N { template<typename T> class C; } -extern "C" { // expected-note {{extern "C" language linkage specification begins here}} +extern "C" { // expected-note 3 {{extern "C" language linkage specification begins here}} template<typename T> class D; // expected-error{{templates must have C++ linkage}} + template<typename T> class A<T **>; // expected-error{{templates must have C++ linkage}} + template<> class A<int[3]>; // expected-error{{templates must have C++ linkage}} + template class A<int[4]>; // OK (surprisingly) FIXME: Should we warn on this? } extern "C" { // expected-note 2 {{extern "C" language linkage specification begins here}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits