Skip to content

Commit

Permalink
[Clang][Sema] Correctly look up primary template for variable templat…
Browse files Browse the repository at this point in the history
…e specializations (#80359)

Consider the following:
```
namespace N0 {
  namespace N1 {
    template<typename T>
    int x1 = 0;
  }
  using namespace N1;
}
template<>
int N0::x1<int>;
```

According to [dcl.meaning.general] p3.3:
> - If the _declarator_ declares an explicit instantiation or a partial
or explicit specialization, the _declarator_ does not bind a name. If it
declares a class member, the terminal name of the _declarator-id_ is not
looked up; otherwise, **only those lookup results that are nominable in
`S` are considered when identifying any function template specialization
being declared**.

In particular, the requirement for lookup results to be nominal in the
lookup context of the terminal name of the _declarator-id_ only applies
to function template specializations -- not variable template
specializations. We currently reject the above declaration, but we do
(correctly) accept it if the using-directive is replaced with a `using`
declaration naming `N0::N1::x1`. This patch makes it so the above
specialization is (correctly) accepted.
  • Loading branch information
sdkrystian committed Feb 2, 2024
1 parent 7b08b43 commit 7ecfb66
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 19 deletions.
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ Bug Fixes in This Version

- Fixed missing warnings when doing bool-like conversions in C23 (`#79435 <https://github.com/llvm/llvm-project/issues/79435>`_).

- Clang now accepts qualified partial/explicit specializations of variable templates that
are not nominable in the lookup context of the specialization.

Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -8456,7 +8456,7 @@ class Sema final {
SourceLocation RAngleLoc);

DeclResult ActOnVarTemplateSpecialization(
Scope *S, Declarator &D, TypeSourceInfo *DI,
Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous,
SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams,
StorageClass SC, bool IsPartialSpecialization);

Expand Down
11 changes: 6 additions & 5 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7727,7 +7727,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
? TemplateParamLists[0]->getTemplateLoc()
: SourceLocation();
DeclResult Res = ActOnVarTemplateSpecialization(
S, D, TInfo, TemplateKWLoc, TemplateParams, SC,
S, D, TInfo, Previous, TemplateKWLoc, TemplateParams, SC,
IsPartialSpecialization);
if (Res.isInvalid())
return nullptr;
Expand Down Expand Up @@ -8070,8 +8070,8 @@ NamedDecl *Sema::ActOnVariableDeclarator(
D.setRedeclaration(CheckVariableDeclaration(NewVD, Previous));
} else {
// If this is an explicit specialization of a static data member, check it.
if (IsMemberSpecialization && !NewVD->isInvalidDecl() &&
CheckMemberSpecialization(NewVD, Previous))
if (IsMemberSpecialization && !IsVariableTemplateSpecialization &&
!NewVD->isInvalidDecl() && CheckMemberSpecialization(NewVD, Previous))
NewVD->setInvalidDecl();

// Merge the decl with the existing one if appropriate.
Expand All @@ -8086,15 +8086,16 @@ NamedDecl *Sema::ActOnVariableDeclarator(
Previous.clear();
NewVD->setInvalidDecl();
}
} else if (D.getCXXScopeSpec().isSet()) {
} else if (D.getCXXScopeSpec().isSet() &&
!IsVariableTemplateSpecialization) {
// No previous declaration in the qualifying scope.
Diag(D.getIdentifierLoc(), diag::err_no_member)
<< Name << computeDeclContext(D.getCXXScopeSpec(), true)
<< D.getCXXScopeSpec().getRange();
NewVD->setInvalidDecl();
}

if (!IsVariableTemplateSpecialization && !IsPlaceholderVariable)
if (!IsPlaceholderVariable)
D.setRedeclaration(CheckVariableDeclaration(NewVD, Previous));

// CheckVariableDeclaration will set NewVD as invalid if something is in
Expand Down
21 changes: 8 additions & 13 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4601,9 +4601,9 @@ void Sema::CheckDeductionGuideTemplate(FunctionTemplateDecl *TD) {
}

DeclResult Sema::ActOnVarTemplateSpecialization(
Scope *S, Declarator &D, TypeSourceInfo *DI, SourceLocation TemplateKWLoc,
TemplateParameterList *TemplateParams, StorageClass SC,
bool IsPartialSpecialization) {
Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous,
SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams,
StorageClass SC, bool IsPartialSpecialization) {
// D must be variable template id.
assert(D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId &&
"Variable template specialization is declared with a template id.");
Expand Down Expand Up @@ -4783,17 +4783,12 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
// Note that this is an explicit specialization.
Specialization->setSpecializationKind(TSK_ExplicitSpecialization);

if (PrevDecl) {
// Check that this isn't a redefinition of this specialization,
// merging with previous declarations.
LookupResult PrevSpec(*this, GetNameForDeclarator(D), LookupOrdinaryName,
forRedeclarationInCurContext());
PrevSpec.addDecl(PrevDecl);
D.setRedeclaration(CheckVariableDeclaration(Specialization, PrevSpec));
} else if (Specialization->isStaticDataMember() &&
Specialization->isOutOfLine()) {
Previous.clear();
if (PrevDecl)
Previous.addDecl(PrevDecl);
else if (Specialization->isStaticDataMember() &&
Specialization->isOutOfLine())
Specialization->setAccess(VarTemplate->getAccess());
}

return Specialization;
}
Expand Down
112 changes: 112 additions & 0 deletions clang/test/CXX/dcl.decl/dcl.meaning/dcl.meaning.general/p3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s

namespace N0 {
template<typename T>
void f0();

template<typename T>
int x0 = 0;

template<typename T>
class C0;
}
using namespace N0;

template<>
void f0<int>(); // expected-error {{no function template matches}}

template<>
int x0<int>;

template<>
class C0<int>;

namespace N1 {
namespace N2 {
template<typename T>
void f2();

template<typename T>
int x2 = 0;

template<typename T>
class C2;
}
using namespace N2;
}

template<>
void N1::f2<int>(); // expected-error {{no function template matches}}

template<>
int N1::x2<int>;

template<>
class N1::C2<int>;

namespace N3 {
namespace N4 {
template<typename T>
void f4();

template<typename T>
int x4 = 0;

template<typename T>
class C4;
}
using N4::f4;
using N4::x4;
using N4::C4;
}

template<>
void N3::f4<int>(); // expected-error {{no function template matches}}

template<>
int N3::x4<int>;

template<>
class N3::C4<int>;

inline namespace N5 {
template<typename T>
void f5();

template<typename T>
int x5 = 0;

template<typename T>
class C5;
}

template<>
void f5<int>();

template<>
int x5<int>;

template<>
class C5<int>;

namespace N6 {
inline namespace N7 {
template<typename T>
void f7();

template<typename T>
int x7 = 0;

template<typename T>
class C7;
}
}

template<>
void N6::f7<int>();

template<>
int N6::x7<int>;

template<>
class N6::C7<int>;

0 comments on commit 7ecfb66

Please sign in to comment.