mirror of
https://github.com/intel/llvm.git
synced 2026-01-21 04:14:03 +08:00
Recover better from a missing 'typename' in a function template definition.
Disambiguate past such a potential problem, and use the absence of 'typename' to break ties in favor of a parenthesized thingy being an initializer, if nothing else in the declaration disambiguates it as declaring a function. llvm-svn: 156963
This commit is contained in:
@@ -1753,7 +1753,8 @@ private:
|
||||
/// BracedCastResult.
|
||||
/// Doesn't consume tokens.
|
||||
TPResult
|
||||
isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False());
|
||||
isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False(),
|
||||
bool *HasMissingTypename = 0);
|
||||
|
||||
// "Tentative parsing" functions, used for disambiguation. If a parsing error
|
||||
// is encountered they will return TPResult::Error().
|
||||
@@ -1762,13 +1763,13 @@ private:
|
||||
// that more tentative parsing is necessary for disambiguation.
|
||||
// They all consume tokens, so backtracking should be used after calling them.
|
||||
|
||||
TPResult TryParseDeclarationSpecifier();
|
||||
TPResult TryParseDeclarationSpecifier(bool *HasMissingTypename = 0);
|
||||
TPResult TryParseSimpleDeclaration(bool AllowForRangeDecl);
|
||||
TPResult TryParseTypeofSpecifier();
|
||||
TPResult TryParseProtocolQualifiers();
|
||||
TPResult TryParseInitDeclaratorList();
|
||||
TPResult TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier=true);
|
||||
TPResult TryParseParameterDeclarationClause();
|
||||
TPResult TryParseParameterDeclarationClause(bool *InvalidAsDeclaration = 0);
|
||||
TPResult TryParseFunctionDeclarator();
|
||||
TPResult TryParseBracketDeclarator();
|
||||
|
||||
|
||||
@@ -827,6 +827,10 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) {
|
||||
/// be either a decl-specifier or a function-style cast, and TPResult::Error()
|
||||
/// if a parsing error was found and reported.
|
||||
///
|
||||
/// If HasMissingTypename is provided, a name with a dependent scope specifier
|
||||
/// will be treated as ambiguous if the 'typename' keyword is missing. If this
|
||||
/// happens, *HasMissingTypename will be set to 'true'.
|
||||
///
|
||||
/// decl-specifier:
|
||||
/// storage-class-specifier
|
||||
/// type-specifier
|
||||
@@ -918,7 +922,8 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) {
|
||||
/// [GNU] restrict
|
||||
///
|
||||
Parser::TPResult
|
||||
Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) {
|
||||
Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
|
||||
bool *HasMissingTypename) {
|
||||
switch (Tok.getKind()) {
|
||||
case tok::identifier: // foo::bar
|
||||
// Check for need to substitute AltiVec __vector keyword
|
||||
@@ -936,7 +941,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) {
|
||||
return (!getLangOpts().ObjC1 && Next.is(tok::identifier)) ?
|
||||
TPResult::True() : TPResult::False();
|
||||
}
|
||||
return isCXXDeclarationSpecifier(BracedCastResult);
|
||||
return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename);
|
||||
|
||||
case tok::coloncolon: { // ::foo::bar
|
||||
const Token &Next = NextToken();
|
||||
@@ -950,7 +955,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) {
|
||||
// recurse to handle whatever we get.
|
||||
if (TryAnnotateTypeOrScopeToken())
|
||||
return TPResult::Error();
|
||||
return isCXXDeclarationSpecifier(BracedCastResult);
|
||||
return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename);
|
||||
|
||||
// decl-specifier:
|
||||
// storage-class-specifier
|
||||
@@ -1052,12 +1057,20 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult) {
|
||||
bool isIdentifier = Tok.is(tok::identifier);
|
||||
TPResult TPR = TPResult::False();
|
||||
if (!isIdentifier)
|
||||
TPR = isCXXDeclarationSpecifier(BracedCastResult);
|
||||
TPR = isCXXDeclarationSpecifier(BracedCastResult,
|
||||
HasMissingTypename);
|
||||
PA.Revert();
|
||||
|
||||
if (isIdentifier ||
|
||||
TPR == TPResult::True() || TPR == TPResult::Error())
|
||||
return TPResult::Error();
|
||||
|
||||
if (HasMissingTypename) {
|
||||
// We can't tell whether this is a missing 'typename' or a valid
|
||||
// expression.
|
||||
*HasMissingTypename = true;
|
||||
return TPResult::Ambiguous();
|
||||
}
|
||||
}
|
||||
}
|
||||
return TPResult::False();
|
||||
@@ -1221,21 +1234,24 @@ Parser::TPResult Parser::TryParseProtocolQualifiers() {
|
||||
return TPResult::Error();
|
||||
}
|
||||
|
||||
Parser::TPResult Parser::TryParseDeclarationSpecifier() {
|
||||
TPResult TPR = isCXXDeclarationSpecifier();
|
||||
Parser::TPResult
|
||||
Parser::TryParseDeclarationSpecifier(bool *HasMissingTypename) {
|
||||
TPResult TPR = isCXXDeclarationSpecifier(TPResult::False(),
|
||||
HasMissingTypename);
|
||||
if (TPR != TPResult::Ambiguous())
|
||||
return TPR;
|
||||
|
||||
if (Tok.is(tok::kw_typeof))
|
||||
TryParseTypeofSpecifier();
|
||||
else {
|
||||
if (Tok.is(tok::annot_cxxscope))
|
||||
ConsumeToken();
|
||||
ConsumeToken();
|
||||
|
||||
if (getLangOpts().ObjC1 && Tok.is(tok::less))
|
||||
TryParseProtocolQualifiers();
|
||||
}
|
||||
|
||||
assert(Tok.is(tok::l_paren) && "Expected '('!");
|
||||
return TPResult::Ambiguous();
|
||||
}
|
||||
|
||||
@@ -1263,9 +1279,28 @@ bool Parser::isCXXFunctionDeclarator(bool warnIfAmbiguous) {
|
||||
TentativeParsingAction PA(*this);
|
||||
|
||||
ConsumeParen();
|
||||
TPResult TPR = TryParseParameterDeclarationClause();
|
||||
if (TPR == TPResult::Ambiguous() && Tok.isNot(tok::r_paren))
|
||||
TPR = TPResult::False();
|
||||
bool InvalidAsDeclaration = false;
|
||||
TPResult TPR = TryParseParameterDeclarationClause(&InvalidAsDeclaration);
|
||||
if (TPR == TPResult::Ambiguous()) {
|
||||
if (Tok.isNot(tok::r_paren))
|
||||
TPR = TPResult::False();
|
||||
else {
|
||||
const Token &Next = NextToken();
|
||||
if (Next.is(tok::amp) || Next.is(tok::ampamp) ||
|
||||
Next.is(tok::kw_const) || Next.is(tok::kw_volatile) ||
|
||||
Next.is(tok::kw_throw) || Next.is(tok::kw_noexcept) ||
|
||||
Next.is(tok::l_square) || isCXX0XVirtSpecifier(Next) ||
|
||||
Next.is(tok::l_brace) || Next.is(tok::kw_try) ||
|
||||
Next.is(tok::equal))
|
||||
// The next token cannot appear after a constructor-style initializer,
|
||||
// and can appear next in a function definition. This must be a function
|
||||
// declarator.
|
||||
TPR = TPResult::True();
|
||||
else if (InvalidAsDeclaration)
|
||||
// Use the absence of 'typename' as a tie-breaker.
|
||||
TPR = TPResult::False();
|
||||
}
|
||||
}
|
||||
|
||||
SourceLocation TPLoc = Tok.getLocation();
|
||||
PA.Revert();
|
||||
@@ -1303,7 +1338,8 @@ bool Parser::isCXXFunctionDeclarator(bool warnIfAmbiguous) {
|
||||
/// attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt]
|
||||
/// attributes[opt] '=' assignment-expression
|
||||
///
|
||||
Parser::TPResult Parser::TryParseParameterDeclarationClause() {
|
||||
Parser::TPResult
|
||||
Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration) {
|
||||
|
||||
if (Tok.is(tok::r_paren))
|
||||
return TPResult::True();
|
||||
@@ -1336,7 +1372,7 @@ Parser::TPResult Parser::TryParseParameterDeclarationClause() {
|
||||
// decl-specifier-seq
|
||||
// A parameter-declaration's initializer must be preceded by an '=', so
|
||||
// decl-specifier-seq '{' is not a parameter in C++11.
|
||||
TPResult TPR = TryParseDeclarationSpecifier();
|
||||
TPResult TPR = TryParseDeclarationSpecifier(InvalidAsDeclaration);
|
||||
if (TPR != TPResult::Ambiguous())
|
||||
return TPR;
|
||||
|
||||
|
||||
@@ -65,9 +65,9 @@ void fnptrs()
|
||||
void (*(*t7)())() throw(B1) = &s8; // valid
|
||||
void (*(*t8)())() throw(A) = &s8; // expected-error {{return types differ}}
|
||||
void (*(*t9)())() throw(D) = &s8; // expected-error {{return types differ}}
|
||||
void (*t10)(void (*)() throw(B1)) = &s9; // valid expected-warning{{disambiguated}}
|
||||
void (*t11)(void (*)() throw(A)) = &s9; // expected-error {{argument types differ}} expected-warning{{disambiguated}}
|
||||
void (*t12)(void (*)() throw(D)) = &s9; // expected-error {{argument types differ}} expected-warning{{disambiguated}}
|
||||
void (*t10)(void (*)() throw(B1)) = &s9; // valid
|
||||
void (*t11)(void (*)() throw(A)) = &s9; // expected-error {{argument types differ}}
|
||||
void (*t12)(void (*)() throw(D)) = &s9; // expected-error {{argument types differ}}
|
||||
}
|
||||
|
||||
// Member function stuff
|
||||
|
||||
@@ -22,6 +22,11 @@ struct A {
|
||||
type f();
|
||||
|
||||
type g();
|
||||
|
||||
static int n;
|
||||
static type m;
|
||||
static int h(T::type, int); // expected-error{{missing 'typename'}}
|
||||
static int h(T::type x, char); // expected-error{{missing 'typename'}}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@@ -30,12 +35,50 @@ A<T>::type g(T t) { return t; } // expected-error{{missing 'typename'}}
|
||||
template<typename T>
|
||||
A<T>::type A<T>::f() { return type(); } // expected-error{{missing 'typename'}}
|
||||
|
||||
template<typename T>
|
||||
void f(T::type) { } // expected-error{{missing 'typename'}}
|
||||
|
||||
template<typename T>
|
||||
void g(T::type x) { } // expected-error{{missing 'typename'}}
|
||||
|
||||
template<typename T>
|
||||
void f(T::type, int) { } // expected-error{{missing 'typename'}}
|
||||
|
||||
template<typename T>
|
||||
void f(T::type x, char) { } // expected-error{{missing 'typename'}}
|
||||
|
||||
template<typename T>
|
||||
void f(int, T::type) { } // expected-error{{missing 'typename'}}
|
||||
|
||||
template<typename T>
|
||||
void f(char, T::type x) { } // expected-error{{missing 'typename'}}
|
||||
|
||||
template<typename T>
|
||||
void f(int, T::type, int) { } // expected-error{{missing 'typename'}}
|
||||
|
||||
template<typename T>
|
||||
void f(int, T::type x, char) { } // expected-error{{missing 'typename'}}
|
||||
|
||||
template<typename T> int A<T>::n(T::value); // ok
|
||||
template<typename T>
|
||||
A<T>::type // expected-error{{missing 'typename'}}
|
||||
A<T>::m(T::value, 0); // ok
|
||||
|
||||
template<typename T> int A<T>::h(T::type, int) {} // expected-error{{missing 'typename'}}
|
||||
template<typename T> int A<T>::h(T::type x, char) {} // expected-error{{missing 'typename'}}
|
||||
|
||||
template<typename T> int h(T::type, int); // expected-error{{missing 'typename'}}
|
||||
template<typename T> int h(T::type x, char); // expected-error{{missing 'typename'}}
|
||||
|
||||
template<typename T> int junk1(T::junk); // expected-error{{declared as a template}}
|
||||
template<typename T> int junk2(T::junk) throw(); // expected-error{{missing 'typename'}}
|
||||
template<typename T> int junk3(T::junk) = delete; // expected-error{{missing 'typename'}} expected-warning{{C++11}}
|
||||
template<typename T> int junk4(T::junk j); // expected-error{{missing 'typename'}}
|
||||
|
||||
// FIXME: We can tell this was intended to be a function because it does not
|
||||
// have a dependent nested name specifier.
|
||||
template<typename T> int i(T::type, int()); // expected-error{{variable 'i' declared as a template}}
|
||||
|
||||
// FIXME: We know which type specifier should have been specified here. Provide
|
||||
// a fix-it to add 'typename A<T>::type'
|
||||
template<typename T>
|
||||
|
||||
Reference in New Issue
Block a user