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.
This commit is contained in:
Richard Smith 2020-03-27 20:59:49 -07:00
parent 9dd92a5697
commit 499b2a8d63
8 changed files with 168 additions and 88 deletions

View File

@ -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<

View File

@ -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(),

View File

@ -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();

View File

@ -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;
}

View File

@ -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@-2 {{'typename' occurs outside of a template}}
// expected-error@-3 {{'template' keyword outside of a template}}
// expected-error@-2 {{'template' keyword outside of a template}}
#endif
}
}

View File

@ -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}}
}

View File

@ -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); }
}
}

View File

@ -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}}