Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit cfe8992

Browse files
committed
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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@293815 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent cafee2d commit cfe8992

File tree

9 files changed

+100
-28
lines changed

9 files changed

+100
-28
lines changed

include/clang/Basic/DiagnosticSemaKinds.td

+2
Original file line numberDiff line numberDiff line change
@@ -4324,6 +4324,8 @@ def note_typename_refers_here : Note<
43244324
"referenced member %0 is declared here">;
43254325
def err_typename_missing : Error<
43264326
"missing 'typename' prior to dependent type name '%0%1'">;
4327+
def err_typename_missing_template : Error<
4328+
"missing 'typename' prior to dependent type template name '%0%1'">;
43274329
def ext_typename_missing : ExtWarn<
43284330
"missing 'typename' prior to dependent type name '%0%1'">,
43294331
InGroup<DiagGroup<"typename-missing">>;

include/clang/Parse/Parser.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -2694,7 +2694,7 @@ class Parser : public CodeCompletionHandler {
26942694
SourceLocation TemplateKWLoc,
26952695
UnqualifiedId &TemplateName,
26962696
bool AllowTypeAnnotation = true);
2697-
void AnnotateTemplateIdTokenAsType();
2697+
void AnnotateTemplateIdTokenAsType(bool IsClassName = false);
26982698
bool IsTemplateArgumentList(unsigned Skip = 0);
26992699
bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs);
27002700
ParsedTemplateArgument ParseTemplateTemplateArgument();

include/clang/Sema/Sema.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -5931,7 +5931,8 @@ class Sema {
59315931
SourceLocation LAngleLoc,
59325932
ASTTemplateArgsPtr TemplateArgs,
59335933
SourceLocation RAngleLoc,
5934-
bool IsCtorOrDtorName = false);
5934+
bool IsCtorOrDtorName = false,
5935+
bool IsClassName = false);
59355936

59365937
/// \brief Parsed an elaborated-type-specifier that refers to a template-id,
59375938
/// such as \c class T::template apply<U>.

lib/Parse/ParseDeclCXX.cpp

+6-6
Original file line numberDiff line numberDiff line change
@@ -1076,7 +1076,7 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
10761076
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
10771077
if (TemplateId->Kind == TNK_Type_template ||
10781078
TemplateId->Kind == TNK_Dependent_template_name) {
1079-
AnnotateTemplateIdTokenAsType();
1079+
AnnotateTemplateIdTokenAsType(/*IsClassName*/true);
10801080

10811081
assert(Tok.is(tok::annot_typename) && "template-id -> type failed");
10821082
ParsedType Type = getTypeAnnotation(Tok);
@@ -1124,10 +1124,10 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
11241124

11251125
// Parse the full template-id, then turn it into a type.
11261126
if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(),
1127-
TemplateName, true))
1127+
TemplateName))
11281128
return true;
1129-
if (TNK == TNK_Dependent_template_name)
1130-
AnnotateTemplateIdTokenAsType();
1129+
if (TNK == TNK_Type_template || TNK == TNK_Dependent_template_name)
1130+
AnnotateTemplateIdTokenAsType(/*IsClassName*/true);
11311131

11321132
// If we didn't end up with a typename token, there's nothing more we
11331133
// can do.
@@ -1145,7 +1145,7 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
11451145
// We have an identifier; check whether it is actually a type.
11461146
IdentifierInfo *CorrectedII = nullptr;
11471147
ParsedType Type = Actions.getTypeName(
1148-
*Id, IdLoc, getCurScope(), &SS, true, false, nullptr,
1148+
*Id, IdLoc, getCurScope(), &SS, /*IsClassName=*/true, false, nullptr,
11491149
/*IsCtorOrDtorName=*/false,
11501150
/*NonTrivialTypeSourceInfo=*/true,
11511151
/*IsClassTemplateDeductionContext*/ false, &CorrectedII);
@@ -3377,7 +3377,7 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) {
33773377
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
33783378
if (TemplateId->Kind == TNK_Type_template ||
33793379
TemplateId->Kind == TNK_Dependent_template_name) {
3380-
AnnotateTemplateIdTokenAsType();
3380+
AnnotateTemplateIdTokenAsType(/*IsClassName*/true);
33813381
assert(Tok.is(tok::annot_typename) && "template-id -> type failed");
33823382
TemplateTypeTy = getTypeAnnotation(Tok);
33833383
}

lib/Parse/ParseTemplate.cpp

+9-2
Original file line numberDiff line numberDiff line change
@@ -1064,7 +1064,12 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
10641064
/// If there was a failure when forming the type from the template-id,
10651065
/// a type annotation token will still be created, but will have a
10661066
/// NULL type pointer to signify an error.
1067-
void Parser::AnnotateTemplateIdTokenAsType() {
1067+
///
1068+
/// \param IsClassName Is this template-id appearing in a context where we
1069+
/// know it names a class, such as in an elaborated-type-specifier or
1070+
/// base-specifier? ('typename' and 'template' are unneeded and disallowed
1071+
/// in those contexts.)
1072+
void Parser::AnnotateTemplateIdTokenAsType(bool IsClassName) {
10681073
assert(Tok.is(tok::annot_template_id) && "Requires template-id tokens");
10691074

10701075
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
@@ -1083,7 +1088,9 @@ void Parser::AnnotateTemplateIdTokenAsType() {
10831088
TemplateId->TemplateNameLoc,
10841089
TemplateId->LAngleLoc,
10851090
TemplateArgsPtr,
1086-
TemplateId->RAngleLoc);
1091+
TemplateId->RAngleLoc,
1092+
/*IsCtorOrDtorName*/false,
1093+
IsClassName);
10871094
// Create the new "type" annotation token.
10881095
Tok.setKind(tok::annot_typename);
10891096
setTypeAnnotation(Tok, Type.isInvalid() ? nullptr : Type.get());

lib/Sema/SemaTemplate.cpp

+36-15
Original file line numberDiff line numberDiff line change
@@ -2426,17 +2426,36 @@ Sema::ActOnTemplateIdType(CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
24262426
SourceLocation LAngleLoc,
24272427
ASTTemplateArgsPtr TemplateArgsIn,
24282428
SourceLocation RAngleLoc,
2429-
bool IsCtorOrDtorName) {
2429+
bool IsCtorOrDtorName, bool IsClassName) {
24302430
if (SS.isInvalid())
24312431
return true;
24322432

2433-
// Per C++ [class.qual]p2, if the template-id was an injected-class-name,
2434-
// it's not actually allowed to be used as a type in most cases. Because
2435-
// we annotate it before we know whether it's valid, we have to check for
2436-
// this case here.
2437-
if (!IsCtorOrDtorName) {
2438-
auto *LookupRD =
2439-
dyn_cast_or_null<CXXRecordDecl>(computeDeclContext(SS, true));
2433+
if (!IsCtorOrDtorName && !IsClassName && SS.isSet()) {
2434+
DeclContext *LookupCtx = computeDeclContext(SS, /*EnteringContext*/false);
2435+
2436+
// C++ [temp.res]p3:
2437+
// A qualified-id that refers to a type and in which the
2438+
// nested-name-specifier depends on a template-parameter (14.6.2)
2439+
// shall be prefixed by the keyword typename to indicate that the
2440+
// qualified-id denotes a type, forming an
2441+
// elaborated-type-specifier (7.1.5.3).
2442+
if (!LookupCtx && isDependentScopeSpecifier(SS)) {
2443+
Diag(TemplateIILoc, diag::err_typename_missing_template)
2444+
<< SS.getScopeRep() << TemplateII->getName();
2445+
// Recover as if 'typename' were specified.
2446+
// FIXME: This is not quite correct recovery as we don't transform SS
2447+
// into the corresponding dependent form (and we don't diagnose missing
2448+
// 'template' keywords within SS as a result).
2449+
return ActOnTypenameType(nullptr, SourceLocation(), SS, TemplateKWLoc,
2450+
TemplateD, TemplateII, TemplateIILoc, LAngleLoc,
2451+
TemplateArgsIn, RAngleLoc);
2452+
}
2453+
2454+
// Per C++ [class.qual]p2, if the template-id was an injected-class-name,
2455+
// it's not actually allowed to be used as a type in most cases. Because
2456+
// we annotate it before we know whether it's valid, we have to check for
2457+
// this case here.
2458+
auto *LookupRD = dyn_cast_or_null<CXXRecordDecl>(LookupCtx);
24402459
if (LookupRD && LookupRD->getIdentifier() == TemplateII) {
24412460
Diag(TemplateIILoc,
24422461
TemplateKWLoc.isInvalid()
@@ -8588,13 +8607,15 @@ Sema::ActOnTypenameType(Scope *S,
85888607

85898608
// Strangely, non-type results are not ignored by this lookup, so the
85908609
// program is ill-formed if it finds an injected-class-name.
8591-
auto *LookupRD =
8592-
dyn_cast_or_null<CXXRecordDecl>(computeDeclContext(SS, true));
8593-
if (LookupRD && LookupRD->getIdentifier() == TemplateII) {
8594-
Diag(TemplateIILoc,
8595-
diag::ext_out_of_line_qualified_id_type_names_constructor)
8596-
<< TemplateII << 0 /*injected-class-name used as template name*/
8597-
<< (TemplateKWLoc.isValid() ? 1 : 0 /*'template'/'typename' keyword*/);
8610+
if (TypenameLoc.isValid()) {
8611+
auto *LookupRD =
8612+
dyn_cast_or_null<CXXRecordDecl>(computeDeclContext(SS, false));
8613+
if (LookupRD && LookupRD->getIdentifier() == TemplateII) {
8614+
Diag(TemplateIILoc,
8615+
diag::ext_out_of_line_qualified_id_type_names_constructor)
8616+
<< TemplateII << 0 /*injected-class-name used as template name*/
8617+
<< (TemplateKWLoc.isValid() ? 1 : 0 /*'template'/'typename' keyword*/);
8618+
}
85988619
}
85998620

86008621
// Translate the parser's template argument list in our AST format.

test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ struct X1 : X0 {
1818
X1 f2(int);
1919
X1 f2(float);
2020
X1 f2(double);
21+
X1 f2(short);
22+
X1 f2(long);
2123
};
2224

2325
// Error recovery: out-of-line constructors whose names have template arguments.
@@ -29,10 +31,12 @@ X0::X0 X0::f1() { return X0(); } // expected-error{{qualified reference to 'X0'
2931

3032
struct X0::X0 X0::f2() { return X0(); }
3133

32-
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}}
33-
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}}
34+
template<typename T> X1<T>::X1<T> X1<T>::f2() { } // expected-error{{missing 'typename'}}
35+
template<typename T> X1<T>::X1<T> (X1<T>::f2)(int) { } // expected-error{{missing 'typename'}}
3436
template<typename T> struct X1<T>::X1<T> (X1<T>::f2)(float) { }
3537
template<typename T> struct X1<T>::X1 (X1<T>::f2)(double) { }
38+
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}}
39+
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}}
3640

3741
void x1test(X1<int> x1i) {
3842
x1i.f2();

test/CXX/temp/temp.decls/temp.variadic/sizeofpack.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ struct X {
6161

6262
template<class... Members>
6363
template<int i>
64-
X<Members...>::get_t<i> X<Members...>::get()
64+
typename X<Members...>::template get_t<i> X<Members...>::get()
6565
{
6666
return 0;
6767
}

test/CXX/temp/temp.res/p3.cpp

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %clang_cc1 -verify %s -std=c++11
2+
3+
template<typename T> struct A {
4+
template<typename U> struct B;
5+
template<typename U> using C = U; // expected-note {{here}}
6+
};
7+
8+
struct X {
9+
template<typename T> X(T);
10+
struct Y {
11+
template<typename T> Y(T);
12+
};
13+
};
14+
15+
template<typename T> A<T>::B<T> f1(); // expected-error {{missing 'typename' prior to dependent type template name 'A<T>::B'}}
16+
template<typename T> A<T>::C<T> f2(); // expected-error {{missing 'typename' prior to dependent type template name 'A<T>::C'}}
17+
18+
// FIXME: Should these cases really be valid? There doesn't appear to be a rule prohibiting them...
19+
template<typename T> A<T>::C<X>::X(T) {}
20+
template<typename T> A<T>::C<X>::X::Y::Y(T) {}
21+
22+
// FIXME: This is ill-formed
23+
template<typename T> int A<T>::B<T>::*f3() {}
24+
template<typename T> int A<T>::C<X>::*f4() {}
25+
26+
// FIXME: This is valid
27+
template<typename T> int A<T>::template C<int>::*f5() {} // expected-error {{has no members}}
28+
29+
template<typename T> template<typename U> struct A<T>::B {
30+
friend A<T>::C<T> f6(); // ok, same as 'friend T f6();'
31+
32+
// FIXME: Error recovery here is awful; we decide that the template-id names
33+
// a type, and then complain about the rest of the tokens, and then complain
34+
// that we didn't get a function declaration.
35+
friend A<U>::C<T> f7(); // expected-error {{use 'template' keyword to treat 'C' as a dependent template name}} expected-error 3{{}}
36+
friend A<U>::template C<T> f8(); // expected-error 3{{}}
37+
};

0 commit comments

Comments
 (0)