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@-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 } } 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}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits