r293815 - Fix hole in our enforcement of rule requiring 'typename' prior to a dependent
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Wed Feb 1 13:36:38 PST 2017
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{{}}
+};
More information about the cfe-commits
mailing list