Author: rsmith Date: Wed Feb 1 15:36:38 2017 New Revision: 293815 URL: http://llvm.org/viewvc/llvm-project?rev=293815&view=rev Log: Fix hole in our enforcement of rule requiring 'typename' prior to a dependent name. If the dependent name happened to end in a template-id (X<T>::Y<U>), we would fail to notice that the 'typename' keyword is missing when resolving it to a type.
It turns out that GCC has a similar bug. If this shows up in much real code, we can easily downgrade this to an ExtWarn. Added: cfe/trunk/test/CXX/temp/temp.res/p3.cpp Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/include/clang/Parse/Parser.h cfe/trunk/include/clang/Sema/Sema.h cfe/trunk/lib/Parse/ParseDeclCXX.cpp cfe/trunk/lib/Parse/ParseTemplate.cpp cfe/trunk/lib/Sema/SemaTemplate.cpp cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/sizeofpack.cpp Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=293815&r1=293814&r2=293815&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Feb 1 15:36:38 2017 @@ -4324,6 +4324,8 @@ def note_typename_refers_here : Note< "referenced member %0 is declared here">; def err_typename_missing : Error< "missing 'typename' prior to dependent type name '%0%1'">; +def err_typename_missing_template : Error< + "missing 'typename' prior to dependent type template name '%0%1'">; def ext_typename_missing : ExtWarn< "missing 'typename' prior to dependent type name '%0%1'">, InGroup<DiagGroup<"typename-missing">>; Modified: cfe/trunk/include/clang/Parse/Parser.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=293815&r1=293814&r2=293815&view=diff ============================================================================== --- cfe/trunk/include/clang/Parse/Parser.h (original) +++ cfe/trunk/include/clang/Parse/Parser.h Wed Feb 1 15:36:38 2017 @@ -2694,7 +2694,7 @@ private: SourceLocation TemplateKWLoc, UnqualifiedId &TemplateName, bool AllowTypeAnnotation = true); - void AnnotateTemplateIdTokenAsType(); + void AnnotateTemplateIdTokenAsType(bool IsClassName = false); bool IsTemplateArgumentList(unsigned Skip = 0); bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs); ParsedTemplateArgument ParseTemplateTemplateArgument(); Modified: cfe/trunk/include/clang/Sema/Sema.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=293815&r1=293814&r2=293815&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/Sema.h (original) +++ cfe/trunk/include/clang/Sema/Sema.h Wed Feb 1 15:36:38 2017 @@ -5931,7 +5931,8 @@ public: SourceLocation LAngleLoc, ASTTemplateArgsPtr TemplateArgs, SourceLocation RAngleLoc, - bool IsCtorOrDtorName = false); + bool IsCtorOrDtorName = false, + bool IsClassName = false); /// \brief Parsed an elaborated-type-specifier that refers to a template-id, /// such as \c class T::template apply<U>. Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=293815&r1=293814&r2=293815&view=diff ============================================================================== --- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original) +++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Wed Feb 1 15:36:38 2017 @@ -1076,7 +1076,7 @@ TypeResult Parser::ParseBaseTypeSpecifie TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); if (TemplateId->Kind == TNK_Type_template || TemplateId->Kind == TNK_Dependent_template_name) { - AnnotateTemplateIdTokenAsType(); + AnnotateTemplateIdTokenAsType(/*IsClassName*/true); assert(Tok.is(tok::annot_typename) && "template-id -> type failed"); ParsedType Type = getTypeAnnotation(Tok); @@ -1124,10 +1124,10 @@ TypeResult Parser::ParseBaseTypeSpecifie // Parse the full template-id, then turn it into a type. if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(), - TemplateName, true)) + TemplateName)) return true; - if (TNK == TNK_Dependent_template_name) - AnnotateTemplateIdTokenAsType(); + if (TNK == TNK_Type_template || TNK == TNK_Dependent_template_name) + AnnotateTemplateIdTokenAsType(/*IsClassName*/true); // If we didn't end up with a typename token, there's nothing more we // can do. @@ -1145,7 +1145,7 @@ TypeResult Parser::ParseBaseTypeSpecifie // We have an identifier; check whether it is actually a type. IdentifierInfo *CorrectedII = nullptr; ParsedType Type = Actions.getTypeName( - *Id, IdLoc, getCurScope(), &SS, true, false, nullptr, + *Id, IdLoc, getCurScope(), &SS, /*IsClassName=*/true, false, nullptr, /*IsCtorOrDtorName=*/false, /*NonTrivialTypeSourceInfo=*/true, /*IsClassTemplateDeductionContext*/ false, &CorrectedII); @@ -3377,7 +3377,7 @@ MemInitResult Parser::ParseMemInitialize TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); if (TemplateId->Kind == TNK_Type_template || TemplateId->Kind == TNK_Dependent_template_name) { - AnnotateTemplateIdTokenAsType(); + AnnotateTemplateIdTokenAsType(/*IsClassName*/true); assert(Tok.is(tok::annot_typename) && "template-id -> type failed"); TemplateTypeTy = getTypeAnnotation(Tok); } Modified: cfe/trunk/lib/Parse/ParseTemplate.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseTemplate.cpp?rev=293815&r1=293814&r2=293815&view=diff ============================================================================== --- cfe/trunk/lib/Parse/ParseTemplate.cpp (original) +++ cfe/trunk/lib/Parse/ParseTemplate.cpp Wed Feb 1 15:36:38 2017 @@ -1064,7 +1064,12 @@ bool Parser::AnnotateTemplateIdToken(Tem /// If there was a failure when forming the type from the template-id, /// a type annotation token will still be created, but will have a /// NULL type pointer to signify an error. -void Parser::AnnotateTemplateIdTokenAsType() { +/// +/// \param IsClassName Is this template-id appearing in a context where we +/// know it names a class, such as in an elaborated-type-specifier or +/// base-specifier? ('typename' and 'template' are unneeded and disallowed +/// in those contexts.) +void Parser::AnnotateTemplateIdTokenAsType(bool IsClassName) { assert(Tok.is(tok::annot_template_id) && "Requires template-id tokens"); TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); @@ -1083,7 +1088,9 @@ void Parser::AnnotateTemplateIdTokenAsTy TemplateId->TemplateNameLoc, TemplateId->LAngleLoc, TemplateArgsPtr, - TemplateId->RAngleLoc); + TemplateId->RAngleLoc, + /*IsCtorOrDtorName*/false, + IsClassName); // Create the new "type" annotation token. Tok.setKind(tok::annot_typename); setTypeAnnotation(Tok, Type.isInvalid() ? nullptr : Type.get()); Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=293815&r1=293814&r2=293815&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaTemplate.cpp (original) +++ cfe/trunk/lib/Sema/SemaTemplate.cpp Wed Feb 1 15:36:38 2017 @@ -2426,17 +2426,36 @@ Sema::ActOnTemplateIdType(CXXScopeSpec & SourceLocation LAngleLoc, ASTTemplateArgsPtr TemplateArgsIn, SourceLocation RAngleLoc, - bool IsCtorOrDtorName) { + bool IsCtorOrDtorName, bool IsClassName) { if (SS.isInvalid()) return true; - // Per C++ [class.qual]p2, if the template-id was an injected-class-name, - // it's not actually allowed to be used as a type in most cases. Because - // we annotate it before we know whether it's valid, we have to check for - // this case here. - if (!IsCtorOrDtorName) { - auto *LookupRD = - dyn_cast_or_null<CXXRecordDecl>(computeDeclContext(SS, true)); + if (!IsCtorOrDtorName && !IsClassName && SS.isSet()) { + DeclContext *LookupCtx = computeDeclContext(SS, /*EnteringContext*/false); + + // C++ [temp.res]p3: + // A qualified-id that refers to a type and in which the + // nested-name-specifier depends on a template-parameter (14.6.2) + // shall be prefixed by the keyword typename to indicate that the + // qualified-id denotes a type, forming an + // elaborated-type-specifier (7.1.5.3). + if (!LookupCtx && isDependentScopeSpecifier(SS)) { + Diag(TemplateIILoc, diag::err_typename_missing_template) + << SS.getScopeRep() << TemplateII->getName(); + // Recover as if 'typename' were specified. + // FIXME: This is not quite correct recovery as we don't transform SS + // into the corresponding dependent form (and we don't diagnose missing + // 'template' keywords within SS as a result). + return ActOnTypenameType(nullptr, SourceLocation(), SS, TemplateKWLoc, + TemplateD, TemplateII, TemplateIILoc, LAngleLoc, + TemplateArgsIn, RAngleLoc); + } + + // Per C++ [class.qual]p2, if the template-id was an injected-class-name, + // it's not actually allowed to be used as a type in most cases. Because + // we annotate it before we know whether it's valid, we have to check for + // this case here. + auto *LookupRD = dyn_cast_or_null<CXXRecordDecl>(LookupCtx); if (LookupRD && LookupRD->getIdentifier() == TemplateII) { Diag(TemplateIILoc, TemplateKWLoc.isInvalid() @@ -8588,13 +8607,15 @@ Sema::ActOnTypenameType(Scope *S, // Strangely, non-type results are not ignored by this lookup, so the // program is ill-formed if it finds an injected-class-name. - auto *LookupRD = - dyn_cast_or_null<CXXRecordDecl>(computeDeclContext(SS, true)); - if (LookupRD && LookupRD->getIdentifier() == TemplateII) { - Diag(TemplateIILoc, - diag::ext_out_of_line_qualified_id_type_names_constructor) - << TemplateII << 0 /*injected-class-name used as template name*/ - << (TemplateKWLoc.isValid() ? 1 : 0 /*'template'/'typename' keyword*/); + if (TypenameLoc.isValid()) { + auto *LookupRD = + dyn_cast_or_null<CXXRecordDecl>(computeDeclContext(SS, false)); + if (LookupRD && LookupRD->getIdentifier() == TemplateII) { + Diag(TemplateIILoc, + diag::ext_out_of_line_qualified_id_type_names_constructor) + << TemplateII << 0 /*injected-class-name used as template name*/ + << (TemplateKWLoc.isValid() ? 1 : 0 /*'template'/'typename' keyword*/); + } } // Translate the parser's template argument list in our AST format. Modified: cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp?rev=293815&r1=293814&r2=293815&view=diff ============================================================================== --- cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp (original) +++ cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp Wed Feb 1 15:36:38 2017 @@ -18,6 +18,8 @@ struct X1 : X0 { X1 f2(int); X1 f2(float); X1 f2(double); + X1 f2(short); + X1 f2(long); }; // Error recovery: out-of-line constructors whose names have template arguments. @@ -29,10 +31,12 @@ X0::X0 X0::f1() { return X0(); } // expe struct X0::X0 X0::f2() { return X0(); } -template<typename T> X1<T>::X1<T> X1<T>::f2() { } // expected-error{{qualified reference to 'X1' is a constructor name rather than a template name in this context}} -template<typename T> X1<T>::X1<T> (X1<T>::f2)(int) { } // expected-error{{qualified reference to 'X1' is a constructor name rather than a template name in this context}} +template<typename T> X1<T>::X1<T> X1<T>::f2() { } // expected-error{{missing 'typename'}} +template<typename T> X1<T>::X1<T> (X1<T>::f2)(int) { } // expected-error{{missing 'typename'}} template<typename T> struct X1<T>::X1<T> (X1<T>::f2)(float) { } template<typename T> struct X1<T>::X1 (X1<T>::f2)(double) { } +template<typename T> typename X1<T>::template X1<T> X1<T>::f2(short) { } // expected-warning {{qualified reference to 'X1' is a constructor name rather than a template name in this context}} +template<typename T> typename X1<T>::template X1<T> (X1<T>::f2)(long) { } // expected-warning {{qualified reference to 'X1' is a constructor name rather than a template name in this context}} void x1test(X1<int> x1i) { x1i.f2(); Modified: cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/sizeofpack.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/sizeofpack.cpp?rev=293815&r1=293814&r2=293815&view=diff ============================================================================== --- cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/sizeofpack.cpp (original) +++ cfe/trunk/test/CXX/temp/temp.decls/temp.variadic/sizeofpack.cpp Wed Feb 1 15:36:38 2017 @@ -61,7 +61,7 @@ struct X { template<class... Members> template<int i> -X<Members...>::get_t<i> X<Members...>::get() +typename X<Members...>::template get_t<i> X<Members...>::get() { return 0; } Added: cfe/trunk/test/CXX/temp/temp.res/p3.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.res/p3.cpp?rev=293815&view=auto ============================================================================== --- cfe/trunk/test/CXX/temp/temp.res/p3.cpp (added) +++ cfe/trunk/test/CXX/temp/temp.res/p3.cpp Wed Feb 1 15:36:38 2017 @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -verify %s -std=c++11 + +template<typename T> struct A { + template<typename U> struct B; + template<typename U> using C = U; // expected-note {{here}} +}; + +struct X { + template<typename T> X(T); + struct Y { + template<typename T> Y(T); + }; +}; + +template<typename T> A<T>::B<T> f1(); // expected-error {{missing 'typename' prior to dependent type template name 'A<T>::B'}} +template<typename T> A<T>::C<T> f2(); // expected-error {{missing 'typename' prior to dependent type template name 'A<T>::C'}} + +// FIXME: Should these cases really be valid? There doesn't appear to be a rule prohibiting them... +template<typename T> A<T>::C<X>::X(T) {} +template<typename T> A<T>::C<X>::X::Y::Y(T) {} + +// FIXME: This is ill-formed +template<typename T> int A<T>::B<T>::*f3() {} +template<typename T> int A<T>::C<X>::*f4() {} + +// FIXME: This is valid +template<typename T> int A<T>::template C<int>::*f5() {} // expected-error {{has no members}} + +template<typename T> template<typename U> struct A<T>::B { + friend A<T>::C<T> f6(); // ok, same as 'friend T f6();' + + // FIXME: Error recovery here is awful; we decide that the template-id names + // a type, and then complain about the rest of the tokens, and then complain + // that we didn't get a function declaration. + friend A<U>::C<T> f7(); // expected-error {{use 'template' keyword to treat 'C' as a dependent template name}} expected-error 3{{}} + friend A<U>::template C<T> f8(); // expected-error 3{{}} +}; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits