[clang] 499b2a8 - PR45294: Fix handling of assumed template names looked up in the lexical
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Fri Mar 27 21:13:15 PDT 2020
Author: Richard Smith
Date: 2020-03-27T21:07:06-07:00
New Revision: 499b2a8d63ca9b319ce3aae462029f37ce7d96dd
URL: https://github.com/llvm/llvm-project/commit/499b2a8d63ca9b319ce3aae462029f37ce7d96dd
DIFF: https://github.com/llvm/llvm-project/commit/499b2a8d63ca9b319ce3aae462029f37ce7d96dd.diff
LOG: PR45294: Fix handling of assumed template names looked up in the lexical
scope.
There are a few contexts in which we assume a name is a template name;
if such a context is one where we should perform an unqualified lookup,
and lookup finds nothing, we would form a dependent template name even
if the name is not dependent. This happens in particular for the lookup
of a pseudo-destructor.
In passing, rename ActOnDependentTemplateName to just ActOnTemplateName
given that we apply it for non-dependent template names too.
Added:
Modified:
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Parse/ParseExprCXX.cpp
clang/lib/Parse/Parser.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/test/Parser/cxx-decl.cpp
clang/test/SemaCXX/literal-operators.cpp
clang/test/SemaCXX/pseudo-destructors.cpp
clang/test/SemaTemplate/nested-name-spec-template.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d2aa2902bb2a..b48f92f38939 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4919,6 +4919,9 @@ def err_template_kw_refers_to_non_template : Error<
"%0 following the 'template' keyword does not refer to a template">;
def note_template_kw_refers_to_non_template : Note<
"declared as a non-template here">;
+def err_template_kw_refers_to_dependent_non_template : Error<
+ "%0%select{| following the 'template' keyword}1 "
+ "cannot refer to a dependent template">;
def err_template_kw_refers_to_class_template : Error<
"'%0%1' instantiated to a class template, not a function template">;
def note_referenced_class_template : Note<
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 985bcf689d21..761fad9456be 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1773,6 +1773,12 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc,
// If there is a '<', the second type name is a template-id. Parse
// it as such.
+ //
+ // FIXME: This is not a context in which a '<' is assumed to start a template
+ // argument list. This affects examples such as
+ // void f(auto *p) { p->~X<int>(); }
+ // ... but there's no ambiguity, and nowhere to write 'template' in such an
+ // example, so we accept it anyway.
if (Tok.is(tok::less) &&
ParseUnqualifiedIdTemplateId(
SS, ObjectType, Base && Base->containsErrors(), SourceLocation(),
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 47b5de320ebf..a33e40b8768d 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1878,11 +1878,7 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
Tok.getLocation());
} else if (Tok.is(tok::annot_template_id)) {
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
- if (TemplateId->isInvalid())
- return true;
- if (TemplateId->Kind != TNK_Type_template &&
- TemplateId->Kind != TNK_Dependent_template_name &&
- TemplateId->Kind != TNK_Undeclared_template) {
+ if (!TemplateId->mightBeType()) {
Diag(Tok, diag::err_typename_refers_to_non_type_template)
<< Tok.getAnnotationRange();
return true;
@@ -1891,14 +1887,13 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
TemplateId->NumArgs);
- Ty = Actions.ActOnTypenameType(getCurScope(), TypenameLoc, SS,
- TemplateId->TemplateKWLoc,
- TemplateId->Template,
- TemplateId->Name,
- TemplateId->TemplateNameLoc,
- TemplateId->LAngleLoc,
- TemplateArgsPtr,
- TemplateId->RAngleLoc);
+ Ty = TemplateId->isInvalid()
+ ? TypeError()
+ : Actions.ActOnTypenameType(
+ getCurScope(), TypenameLoc, SS, TemplateId->TemplateKWLoc,
+ TemplateId->Template, TemplateId->Name,
+ TemplateId->TemplateNameLoc, TemplateId->LAngleLoc,
+ TemplateArgsPtr, TemplateId->RAngleLoc);
} else {
Diag(Tok, diag::err_expected_type_name_after_typename)
<< SS.getRange();
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 74d1fbe2ec77..1e2ebc5037bc 100755
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -377,6 +377,9 @@ bool Sema::LookupTemplateName(LookupResult &Found,
if (ATK)
*ATK = AssumedTemplateKind::None;
+ if (SS.isInvalid())
+ return true;
+
Found.setTemplateNameLookup(true);
// Determine where to perform name lookup
@@ -386,7 +389,7 @@ bool Sema::LookupTemplateName(LookupResult &Found,
if (!ObjectType.isNull()) {
// This nested-name-specifier occurs in a member access expression, e.g.,
// x->B::f, and we are looking into the type of the object.
- assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist");
+ assert(SS.isEmpty() && "ObjectType and scope specifier cannot coexist");
LookupCtx = computeDeclContext(ObjectType);
IsDependent = !LookupCtx && ObjectType->isDependentType();
assert((IsDependent || !ObjectType->isIncompleteType() ||
@@ -412,11 +415,11 @@ bool Sema::LookupTemplateName(LookupResult &Found,
Found.clear();
return false;
}
- } else if (SS.isSet()) {
+ } else if (SS.isNotEmpty()) {
// This nested-name-specifier occurs after another nested-name-specifier,
// so long into the context associated with the prior nested-name-specifier.
LookupCtx = computeDeclContext(SS, EnteringContext);
- IsDependent = !LookupCtx;
+ IsDependent = !LookupCtx && isDependentScopeSpecifier(SS);
// The declaration context must be complete.
if (LookupCtx && RequireCompleteDeclContext(SS, LookupCtx))
@@ -443,7 +446,7 @@ bool Sema::LookupTemplateName(LookupResult &Found,
IsDependent |= Found.wasNotFoundInCurrentInstantiation();
}
- if (!SS.isSet() && (ObjectType.isNull() || Found.empty())) {
+ if (SS.isEmpty() && (ObjectType.isNull() || Found.empty())) {
// C++ [basic.lookup.classref]p1:
// In a class member access expression (5.2.5), if the . or -> token is
// immediately followed by an identifier followed by a <, the
@@ -470,7 +473,7 @@ bool Sema::LookupTemplateName(LookupResult &Found,
if (Found.isAmbiguous())
return false;
- if (ATK && !SS.isSet() && ObjectType.isNull() && TemplateKWLoc.isInvalid()) {
+ if (ATK && SS.isEmpty() && ObjectType.isNull() && TemplateKWLoc.isInvalid()) {
// C++2a [temp.names]p2:
// A name is also considered to refer to a template if it is an
// unqualified-id followed by a < and name lookup finds either one or more
@@ -3470,6 +3473,10 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,
DTN->getIdentifier(),
TemplateArgs);
+ if (Name.getAsAssumedTemplateName() &&
+ resolveAssumedTemplateNameAsType(/*Scope*/nullptr, Name, TemplateLoc))
+ return QualType();
+
TemplateDecl *Template = Name.getAsTemplateDecl();
if (!Template || isa<FunctionTemplateDecl>(Template) ||
isa<VarTemplateDecl>(Template) || isa<ConceptDecl>(Template)) {
@@ -4652,95 +4659,111 @@ TemplateNameKind Sema::ActOnTemplateName(Scope *S,
diag::ext_template_outside_of_template)
<< FixItHint::CreateRemoval(TemplateKWLoc);
+ if (SS.isInvalid())
+ return TNK_Non_template;
+
+ // Figure out where isTemplateName is going to look.
DeclContext *LookupCtx = nullptr;
- if (SS.isSet())
+ if (SS.isNotEmpty())
LookupCtx = computeDeclContext(SS, EnteringContext);
- if (!LookupCtx && ObjectType)
- LookupCtx = computeDeclContext(ObjectType.get());
- if (LookupCtx) {
- // C++0x [temp.names]p5:
- // If a name prefixed by the keyword template is not the name of
- // a template, the program is ill-formed. [Note: the keyword
- // template may not be applied to non-template members of class
- // templates. -end note ] [ Note: as is the case with the
- // typename prefix, the template prefix is allowed in cases
- // where it is not strictly necessary; i.e., when the
- // nested-name-specifier or the expression on the left of the ->
- // or . is not dependent on a template-parameter, or the use
- // does not appear in the scope of a template. -end note]
- //
- // Note: C++03 was more strict here, because it banned the use of
- // the "template" keyword prior to a template-name that was not a
- // dependent name. C++ DR468 relaxed this requirement (the
- // "template" keyword is now permitted). We follow the C++0x
- // rules, even in C++03 mode with a warning, retroactively applying the DR.
- bool MemberOfUnknownSpecialization;
- TemplateNameKind TNK = isTemplateName(S, SS, TemplateKWLoc.isValid(), Name,
- ObjectType, EnteringContext, Result,
- MemberOfUnknownSpecialization);
- if (TNK == TNK_Non_template && MemberOfUnknownSpecialization) {
- // This is a dependent template. Handle it below.
- } else if (TNK == TNK_Non_template) {
- // Do the lookup again to determine if this is a "nothing found" case or
- // a "not a template" case. FIXME: Refactor isTemplateName so we don't
- // need to do this.
- DeclarationNameInfo DNI = GetNameFromUnqualifiedId(Name);
- LookupResult R(*this, DNI.getName(), Name.getBeginLoc(),
- LookupOrdinaryName);
- bool MOUS;
- if (!LookupTemplateName(R, S, SS, ObjectType.get(), EnteringContext,
- MOUS, TemplateKWLoc) && !R.isAmbiguous())
+ else if (ObjectType)
+ LookupCtx = computeDeclContext(GetTypeFromParser(ObjectType));
+
+ // C++0x [temp.names]p5:
+ // If a name prefixed by the keyword template is not the name of
+ // a template, the program is ill-formed. [Note: the keyword
+ // template may not be applied to non-template members of class
+ // templates. -end note ] [ Note: as is the case with the
+ // typename prefix, the template prefix is allowed in cases
+ // where it is not strictly necessary; i.e., when the
+ // nested-name-specifier or the expression on the left of the ->
+ // or . is not dependent on a template-parameter, or the use
+ // does not appear in the scope of a template. -end note]
+ //
+ // Note: C++03 was more strict here, because it banned the use of
+ // the "template" keyword prior to a template-name that was not a
+ // dependent name. C++ DR468 relaxed this requirement (the
+ // "template" keyword is now permitted). We follow the C++0x
+ // rules, even in C++03 mode with a warning, retroactively applying the DR.
+ bool MemberOfUnknownSpecialization;
+ TemplateNameKind TNK = isTemplateName(S, SS, TemplateKWLoc.isValid(), Name,
+ ObjectType, EnteringContext, Result,
+ MemberOfUnknownSpecialization);
+ if (TNK != TNK_Non_template) {
+ // We resolved this to a (non-dependent) template name. Return it.
+ auto *LookupRD = dyn_cast_or_null<CXXRecordDecl>(LookupCtx);
+ if (!AllowInjectedClassName && SS.isNotEmpty() && LookupRD &&
+ Name.getKind() == UnqualifiedIdKind::IK_Identifier &&
+ Name.Identifier && LookupRD->getIdentifier() == Name.Identifier) {
+ // C++14 [class.qual]p2:
+ // In a lookup in which function names are not ignored and the
+ // nested-name-specifier nominates a class C, if the name specified
+ // [...] is the injected-class-name of C, [...] the name is instead
+ // considered to name the constructor
+ //
+ // We don't get here if naming the constructor would be valid, so we
+ // just reject immediately and recover by treating the
+ // injected-class-name as naming the template.
+ Diag(Name.getBeginLoc(),
+ diag::ext_out_of_line_qualified_id_type_names_constructor)
+ << Name.Identifier
+ << 0 /*injected-class-name used as template name*/
+ << TemplateKWLoc.isValid();
+ }
+ return TNK;
+ }
+
+ if (!MemberOfUnknownSpecialization) {
+ // Didn't find a template name, and the lookup wasn't dependent.
+ // Do the lookup again to determine if this is a "nothing found" case or
+ // a "not a template" case. FIXME: Refactor isTemplateName so we don't
+ // need to do this.
+ DeclarationNameInfo DNI = GetNameFromUnqualifiedId(Name);
+ LookupResult R(*this, DNI.getName(), Name.getBeginLoc(),
+ LookupOrdinaryName);
+ bool MOUS;
+ // FIXME: If LookupTemplateName fails here, we'll have produced its
+ // diagnostics twice.
+ if (!LookupTemplateName(R, S, SS, ObjectType.get(), EnteringContext,
+ MOUS, TemplateKWLoc) && !R.isAmbiguous()) {
+ if (LookupCtx)
Diag(Name.getBeginLoc(), diag::err_no_member)
<< DNI.getName() << LookupCtx << SS.getRange();
- return TNK_Non_template;
- } else {
- // We found something; return it.
- auto *LookupRD = dyn_cast<CXXRecordDecl>(LookupCtx);
- if (!AllowInjectedClassName && SS.isSet() && LookupRD &&
- Name.getKind() == UnqualifiedIdKind::IK_Identifier &&
- Name.Identifier && LookupRD->getIdentifier() == Name.Identifier) {
- // C++14 [class.qual]p2:
- // In a lookup in which function names are not ignored and the
- // nested-name-specifier nominates a class C, if the name specified
- // [...] is the injected-class-name of C, [...] the name is instead
- // considered to name the constructor
- //
- // We don't get here if naming the constructor would be valid, so we
- // just reject immediately and recover by treating the
- // injected-class-name as naming the template.
- Diag(Name.getBeginLoc(),
- diag::ext_out_of_line_qualified_id_type_names_constructor)
- << Name.Identifier
- << 0 /*injected-class-name used as template name*/
- << 1 /*'template' keyword was used*/;
- }
- return TNK;
+ else
+ Diag(Name.getBeginLoc(), diag::err_undeclared_use)
+ << DNI.getName() << SS.getRange();
}
+ return TNK_Non_template;
}
NestedNameSpecifier *Qualifier = SS.getScopeRep();
switch (Name.getKind()) {
case UnqualifiedIdKind::IK_Identifier:
- Result = TemplateTy::make(Context.getDependentTemplateName(Qualifier,
- Name.Identifier));
+ Result = TemplateTy::make(
+ Context.getDependentTemplateName(Qualifier, Name.Identifier));
return TNK_Dependent_template_name;
case UnqualifiedIdKind::IK_OperatorFunctionId:
- Result = TemplateTy::make(Context.getDependentTemplateName(Qualifier,
- Name.OperatorFunctionId.Operator));
+ Result = TemplateTy::make(Context.getDependentTemplateName(
+ Qualifier, Name.OperatorFunctionId.Operator));
return TNK_Function_template;
case UnqualifiedIdKind::IK_LiteralOperatorId:
- llvm_unreachable("literal operator id cannot have a dependent scope");
+ // This is a kind of template name, but can never occur in a dependent
+ // scope (literal operators can only be declared at namespace scope).
+ break;
default:
break;
}
- Diag(Name.getBeginLoc(), diag::err_template_kw_refers_to_non_template)
+ // This name cannot possibly name a dependent template. Diagnose this now
+ // rather than building a dependent template name that can never be valid.
+ Diag(Name.getBeginLoc(),
+ diag::err_template_kw_refers_to_dependent_non_template)
<< GetNameFromUnqualifiedId(Name).getName() << Name.getSourceRange()
- << TemplateKWLoc;
+ << TemplateKWLoc.isValid() << TemplateKWLoc;
return TNK_Non_template;
}
diff --git a/clang/test/Parser/cxx-decl.cpp b/clang/test/Parser/cxx-decl.cpp
index ba1cce419a46..dbf3a3e70bb0 100644
--- a/clang/test/Parser/cxx-decl.cpp
+++ b/clang/test/Parser/cxx-decl.cpp
@@ -240,8 +240,7 @@ namespace PR17255 {
void foo() {
typename A::template B<> c; // expected-error {{use of undeclared identifier 'A'}}
#if __cplusplus <= 199711L
- // expected-error at -2 {{'typename' occurs outside of a template}}
- // expected-error at -3 {{'template' keyword outside of a template}}
+ // expected-error at -2 {{'template' keyword outside of a template}}
#endif
}
}
diff --git a/clang/test/SemaCXX/literal-operators.cpp b/clang/test/SemaCXX/literal-operators.cpp
index 304aa7cab7f3..834d5ec7923e 100644
--- a/clang/test/SemaCXX/literal-operators.cpp
+++ b/clang/test/SemaCXX/literal-operators.cpp
@@ -47,3 +47,7 @@ template <unsigned long long...> void operator "" _invalid(); // expected-error
_Complex float operator""if(long double); // expected-warning {{reserved}}
_Complex float test_if_1() { return 2.0f + 1.5if; };
void test_if_2() { "foo"if; } // expected-error {{no matching literal operator for call to 'operator""if'}}
+
+template<typename T> void dependent_member_template() {
+ T().template operator""_foo<int>(); // expected-error {{'operator""_foo' following the 'template' keyword cannot refer to a dependent template}}
+}
diff --git a/clang/test/SemaCXX/pseudo-destructors.cpp b/clang/test/SemaCXX/pseudo-destructors.cpp
index b71b523de683..292324893dd0 100644
--- a/clang/test/SemaCXX/pseudo-destructors.cpp
+++ b/clang/test/SemaCXX/pseudo-destructors.cpp
@@ -119,3 +119,54 @@ void test2(Foo d) {
d.~Derived(); // expected-error {{member reference type 'dotPointerAccess::Foo' (aka 'dotPointerAccess::Derived *') is a pointer; did you mean to use '->'}}
}
}
+
+int pr45294 = 1 .~undeclared_tempate_name<>(); // expected-error {{use of undeclared 'undeclared_tempate_name'}}
+
+namespace TwoPhaseLookup {
+ namespace NonTemplate {
+ struct Y {};
+ using G = Y;
+ template<typename T> void f(T *p) { p->~G(); } // expected-error {{no member named '~Y'}}
+ void h1(Y *p) { p->~G(); }
+ void h2(Y *p) { f(p); }
+ namespace N { struct G{}; }
+ void h3(N::G *p) { p->~G(); }
+ void h4(N::G *p) { f(p); } // expected-note {{instantiation of}}
+ }
+
+ namespace NonTemplateUndeclared {
+ struct Y {};
+ template<typename T> void f(T *p) { p->~G(); } // expected-error {{undeclared identifier 'G' in destructor name}}
+ using G = Y;
+ void h1(Y *p) { p->~G(); }
+ void h2(Y *p) { f(p); } // expected-note {{instantiation of}}
+ namespace N { struct G{}; }
+ void h3(N::G *p) { p->~G(); }
+ void h4(N::G *p) { f(p); }
+ }
+
+ namespace Template {
+ template<typename T> struct Y {};
+ template<class U> using G = Y<U>;
+ template<typename T> void f(T *p) { p->~G<int>(); } // expected-error {{no member named '~Y'}}
+ void h1(Y<int> *p) { p->~G<int>(); }
+ void h2(Y<int> *p) { f(p); }
+ namespace N { template<typename T> struct G {}; }
+ void h3(N::G<int> *p) { p->~G<int>(); }
+ void h4(N::G<int> *p) { f(p); } // expected-note {{instantiation of}}
+ }
+
+ namespace TemplateUndeclared {
+ template<typename T> struct Y {};
+ // FIXME: Formally, this is ill-formed before we hit any instantiation,
+ // because we aren't supposed to treat the '<' as introducing a template
+ // name.
+ template<typename T> void f(T *p) { p->~G<int>(); } // expected-error {{no member named 'G'}}
+ template<class U> using G = Y<U>;
+ void h1(Y<int> *p) { p->~G<int>(); }
+ void h2(Y<int> *p) { f(p); } // expected-note {{instantiation of}}
+ namespace N { template<typename T> struct G {}; }
+ void h3(N::G<int> *p) { p->~G<int>(); }
+ void h4(N::G<int> *p) { f(p); }
+ }
+}
diff --git a/clang/test/SemaTemplate/nested-name-spec-template.cpp b/clang/test/SemaTemplate/nested-name-spec-template.cpp
index 3e7f506040a6..07dd41bfe27b 100644
--- a/clang/test/SemaTemplate/nested-name-spec-template.cpp
+++ b/clang/test/SemaTemplate/nested-name-spec-template.cpp
@@ -142,8 +142,7 @@ namespace PR9449 {
template <typename T>
void f() {
- int s<T>::template n<T>::* f; // expected-error{{implicit instantiation of undefined template 'PR9449::s<int>'}} \
- // expected-error{{no member named 'n'}}
+ int s<T>::template n<T>::* f; // expected-error{{implicit instantiation of undefined template 'PR9449::s<int>'}}
}
template void f<int>(); // expected-note{{in instantiation of}}
More information about the cfe-commits
mailing list