Teach TryAnnotateTypeOrScopeToken to deal with already-annotated

scope specifiers.  Fix a tentative parsing bug that came up in LLVM.
Incidentally fixes some random FIXMEs in an existing testcase.

llvm-svn: 91734
This commit is contained in:
John McCall 2009-12-19 00:35:18 +00:00
parent 357e8c94d6
commit e2ade289be
4 changed files with 61 additions and 12 deletions

View File

@ -680,10 +680,10 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() {
// Otherwise, not a typename. // Otherwise, not a typename.
return TPResult::False(); return TPResult::False();
case tok::coloncolon: // ::foo::bar case tok::coloncolon: // ::foo::bar
if (NextToken().is(tok::kw_new) || // ::new if (NextToken().is(tok::kw_new) || // ::new
NextToken().is(tok::kw_delete)) // ::delete NextToken().is(tok::kw_delete)) // ::delete
return TPResult::False(); return TPResult::False();
// Annotate typenames and C++ scope specifiers. If we get one, just // Annotate typenames and C++ scope specifiers. If we get one, just
// recurse to handle whatever we get. // recurse to handle whatever we get.
@ -750,6 +750,12 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() {
case tok::kw___forceinline: case tok::kw___forceinline:
return TPResult::True(); return TPResult::True();
case tok::annot_cxxscope: // foo::bar or ::foo::bar, but already parsed
// We've already annotated a scope; try to annotate a type.
if (!(TryAnnotateTypeOrScopeToken() && Tok.is(tok::annot_typename)))
return TPResult::False();
// If that succeeded, fallthrough into the generic simple-type-id case.
// The ambiguity resides in a simple-type-specifier/typename-specifier // The ambiguity resides in a simple-type-specifier/typename-specifier
// followed by a '('. The '(' could either be the start of: // followed by a '('. The '(' could either be the start of:
// //

View File

@ -880,7 +880,7 @@ Parser::OwningExprResult Parser::ParseSimpleAsm(SourceLocation *EndLoc) {
/// as the current tokens, so only call it in contexts where these are invalid. /// as the current tokens, so only call it in contexts where these are invalid.
bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) { bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) {
assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon)
|| Tok.is(tok::kw_typename)) && || Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope)) &&
"Cannot be a type or scope token!"); "Cannot be a type or scope token!");
if (Tok.is(tok::kw_typename)) { if (Tok.is(tok::kw_typename)) {
@ -935,6 +935,9 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) {
return true; return true;
} }
// Remembers whether the token was originally a scope annotation.
bool wasScopeAnnotation = Tok.is(tok::annot_cxxscope);
CXXScopeSpec SS; CXXScopeSpec SS;
if (getLang().CPlusPlus) if (getLang().CPlusPlus)
ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, EnteringContext); ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, EnteringContext);
@ -1017,9 +1020,11 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) {
Tok.setAnnotationValue(SS.getScopeRep()); Tok.setAnnotationValue(SS.getScopeRep());
Tok.setAnnotationRange(SS.getRange()); Tok.setAnnotationRange(SS.getRange());
// In case the tokens were cached, have Preprocessor replace them with the // In case the tokens were cached, have Preprocessor replace them
// annotation token. // with the annotation token. We don't need to do this if we've
PP.AnnotateCachedTokens(Tok); // just reverted back to the state we were in before being called.
if (!wasScopeAnnotation)
PP.AnnotateCachedTokens(Tok);
return true; return true;
} }

View File

@ -24,3 +24,43 @@ void f() {
// FIXME: Special case: "++" is postfix here, not prefix // FIXME: Special case: "++" is postfix here, not prefix
// (S())++; // (S())++;
} }
// Make sure we do tentative parsing correctly in conditions.
typedef int type;
struct rec { rec(int); };
namespace ns {
typedef int type;
struct rec { rec(int); };
}
struct cls {
typedef int type;
struct rec { rec(int); };
};
struct result {
template <class T> result(T);
bool check();
};
void test(int i) {
if (result((cls::type) i).check())
return;
if (result((ns::type) i).check())
return;
if (result((::type) i).check())
return;
if (result((cls::rec) i).check())
return;
if (result((ns::rec) i).check())
return;
if (result((::rec) i).check())
return;
}

View File

@ -32,11 +32,9 @@ apply1<add_reference, int>::type ir = i;
apply1<add_reference, float>::type fr = i; // expected-error{{non-const lvalue reference to type 'float' cannot bind to a value of unrelated type 'int'}} apply1<add_reference, float>::type fr = i; // expected-error{{non-const lvalue reference to type 'float' cannot bind to a value of unrelated type 'int'}}
void test() { void test() {
apply1<add_reference, void>::type t; // expected-note{{in instantiation of template class 'struct apply1<struct add_reference, void>' requested here}} \ apply1<add_reference, void>::type t; // expected-note{{in instantiation of template class 'struct apply1<struct add_reference, void>' requested here}}
// FIXME: expected-error{{unexpected type name 'type': expected expression}}
apply1<bogus, int>::type t2; // expected-note{{in instantiation of template class 'struct apply1<struct bogus, int>' requested here}} \ apply1<bogus, int>::type t2; // expected-note{{in instantiation of template class 'struct apply1<struct bogus, int>' requested here}}
// FIXME: expected-error{{unexpected type name 'type': expected expression}}
} }