Delay parsing of default arguments of member functions until the class
is completely defined (C++ [class.mem]p2).
Reverse the order in which we process the definitions of member
functions specified inline. This way, we'll get diagnostics in the
order in which the member functions were declared in the class.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@61103 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp
index 824847a..a9712fe 100644
--- a/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/lib/Parse/ParseCXXInlineMethods.cpp
@@ -31,13 +31,13 @@
// Consume the tokens and store them for later parsing.
- getCurTopClassStack().push(LexedMethod(FnD));
- TokensTy &Toks = getCurTopClassStack().top().Toks;
+ getCurTopClassStack().MethodDefs.push_back(LexedMethod(FnD));
+ CachedTokens &Toks = getCurTopClassStack().MethodDefs.back().Toks;
// We may have a constructor initializer here.
if (Tok.is(tok::colon)) {
// Consume everything up to (and including) the left brace.
- if (!ConsumeAndStoreUntil(tok::l_brace, Toks, tok::semi)) {
+ if (!ConsumeAndStoreUntil(tok::l_brace, tok::unknown, Toks, tok::semi)) {
// We didn't find the left-brace we expected after the
// constructor initializer.
if (Tok.is(tok::semi)) {
@@ -45,7 +45,7 @@
// don't try to parse this method later.
Diag(Tok.getLocation(), diag::err_expected_lbrace);
ConsumeAnyToken();
- getCurTopClassStack().pop();
+ getCurTopClassStack().MethodDefs.pop_back();
return FnD;
}
}
@@ -56,17 +56,66 @@
ConsumeBrace();
}
// Consume everything up to (and including) the matching right brace.
- ConsumeAndStoreUntil(tok::r_brace, Toks);
+ ConsumeAndStoreUntil(tok::r_brace, tok::unknown, Toks);
return FnD;
}
+/// ParseLexedMethodDeclarations - We finished parsing the member
+/// specification of a top (non-nested) C++ class. Now go over the
+/// stack of method declarations with some parts for which parsing was
+/// delayed (such as default arguments) and parse them.
+void Parser::ParseLexedMethodDeclarations() {
+ for (; !getCurTopClassStack().MethodDecls.empty();
+ getCurTopClassStack().MethodDecls.pop_front()) {
+ LateParsedMethodDeclaration &LM = getCurTopClassStack().MethodDecls.front();
+
+ // Start the delayed C++ method declaration
+ Actions.ActOnStartDelayedCXXMethodDeclaration(CurScope, LM.Method);
+
+ // Introduce the parameters into scope and parse their default
+ // arguments.
+ ParseScope PrototypeScope(this, Scope::FnScope|Scope::DeclScope);
+ for (unsigned I = 0, N = LM.DefaultArgs.size(); I != N; ++I) {
+ // Introduce the parameter into scope.
+ Actions.ActOnDelayedCXXMethodParameter(CurScope, LM.DefaultArgs[I].Param);
+
+ if (CachedTokens *Toks = LM.DefaultArgs[I].Toks) {
+ // Parse the default argument from its saved token stream.
+ Toks->push_back(Tok); // So that the current token doesn't get lost
+ PP.EnterTokenStream(&Toks->front(), Toks->size(), true, false);
+
+ // Consume the previously-pushed token.
+ ConsumeAnyToken();
+
+ // Consume the '='.
+ assert(Tok.is(tok::equal) && "Default argument not starting with '='");
+ SourceLocation EqualLoc = ConsumeToken();
+
+ OwningExprResult DefArgResult(ParseAssignmentExpression());
+ if (DefArgResult.isInvalid())
+ Actions.ActOnParamDefaultArgumentError(LM.DefaultArgs[I].Param);
+ else
+ Actions.ActOnParamDefaultArgument(LM.DefaultArgs[I].Param, EqualLoc,
+ DefArgResult.release());
+ delete Toks;
+ LM.DefaultArgs[I].Toks = 0;
+ }
+ }
+ PrototypeScope.Exit();
+
+ // Finish the delayed C++ method declaration.
+ Actions.ActOnFinishDelayedCXXMethodDeclaration(CurScope, LM.Method);
+ }
+}
+
/// ParseLexedMethodDefs - We finished parsing the member specification of a top
/// (non-nested) C++ class. Now go over the stack of lexed methods that were
/// collected during its parsing and parse them all.
void Parser::ParseLexedMethodDefs() {
- for (; !getCurTopClassStack().empty(); getCurTopClassStack().pop()) {
- LexedMethod &LM = getCurTopClassStack().top();
+ for (; !getCurTopClassStack().MethodDefs.empty();
+ getCurTopClassStack().MethodDefs.pop_front()) {
+ LexedMethod &LM = getCurTopClassStack().MethodDefs.front();
assert(!LM.Toks.empty() && "Empty body!");
// Append the current token at the end of the new token stream so that it
@@ -92,21 +141,26 @@
}
/// ConsumeAndStoreUntil - Consume and store the token at the passed token
-/// container until the token 'T' is reached (which gets consumed/stored too).
+/// container until the token 'T' is reached (which gets
+/// consumed/stored too, if ConsumeFinalToken).
/// If EarlyAbortIf is specified, then we will stop early if we find that
/// token at the top level.
-/// Returns true if token 'T' was found.
+/// Returns true if token 'T1' or 'T2' was found.
/// NOTE: This is a specialized version of Parser::SkipUntil.
-bool Parser::ConsumeAndStoreUntil(tok::TokenKind T, TokensTy &Toks,
- tok::TokenKind EarlyAbortIf) {
+bool Parser::ConsumeAndStoreUntil(tok::TokenKind T1, tok::TokenKind T2,
+ CachedTokens &Toks,
+ tok::TokenKind EarlyAbortIf,
+ bool ConsumeFinalToken) {
// We always want this function to consume at least one token if the first
// token isn't T and if not at EOF.
bool isFirstTokenConsumed = true;
while (1) {
// If we found one of the tokens, stop and return true.
- if (Tok.is(T)) {
- Toks.push_back(Tok);
- ConsumeAnyToken();
+ if (Tok.is(T1) || Tok.is(T2)) {
+ if (ConsumeFinalToken) {
+ Toks.push_back(Tok);
+ ConsumeAnyToken();
+ }
return true;
}
@@ -123,19 +177,19 @@
// Recursively consume properly-nested parens.
Toks.push_back(Tok);
ConsumeParen();
- ConsumeAndStoreUntil(tok::r_paren, Toks);
+ ConsumeAndStoreUntil(tok::r_paren, tok::unknown, Toks);
break;
case tok::l_square:
// Recursively consume properly-nested square brackets.
Toks.push_back(Tok);
ConsumeBracket();
- ConsumeAndStoreUntil(tok::r_square, Toks);
+ ConsumeAndStoreUntil(tok::r_square, tok::unknown, Toks);
break;
case tok::l_brace:
// Recursively consume properly-nested braces.
Toks.push_back(Tok);
ConsumeBrace();
- ConsumeAndStoreUntil(tok::r_brace, Toks);
+ ConsumeAndStoreUntil(tok::r_brace, tok::unknown, Toks);
break;
// Okay, we found a ']' or '}' or ')', which we think should be balanced.
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index ed15140..1a8e5d9 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -1771,6 +1771,10 @@
// Remember this parsed parameter in ParamInfo.
IdentifierInfo *ParmII = ParmDecl.getIdentifier();
+ // DefArgToks is used when the parsing of default arguments needs
+ // to be delayed.
+ CachedTokens *DefArgToks = 0;
+
// If no parameter was specified, verify that *something* was specified,
// otherwise we have a missing type and identifier.
if (DS.getParsedSpecifiers() == DeclSpec::PQ_None &&
@@ -1790,24 +1794,39 @@
// ActOnParamDefaultArgument will reject the default argument in
// C.
if (Tok.is(tok::equal)) {
- SourceLocation EqualLoc = Tok.getLocation();
-
- // Consume the '='.
- ConsumeToken();
-
// Parse the default argument
- OwningExprResult DefArgResult(ParseAssignmentExpression());
- if (DefArgResult.isInvalid()) {
- SkipUntil(tok::comma, tok::r_paren, true, true);
+ if (D.getContext() == Declarator::MemberContext) {
+ // If we're inside a class definition, cache the tokens
+ // corresponding to the default argument. We'll actually parse
+ // them when we see the end of the class definition.
+ // FIXME: Templates will require something similar.
+ // FIXME: Can we use a smart pointer for Toks?
+ DefArgToks = new CachedTokens;
+
+ if (!ConsumeAndStoreUntil(tok::comma, tok::r_paren, *DefArgToks,
+ tok::semi, false)) {
+ delete DefArgToks;
+ DefArgToks = 0;
+ }
} else {
- // Inform the actions module about the default argument
- Actions.ActOnParamDefaultArgument(Param, EqualLoc,
- DefArgResult.release());
+ // Consume the '='.
+ SourceLocation EqualLoc = ConsumeToken();
+
+ OwningExprResult DefArgResult(ParseAssignmentExpression());
+ if (DefArgResult.isInvalid()) {
+ Actions.ActOnParamDefaultArgumentError(Param);
+ SkipUntil(tok::comma, tok::r_paren, true, true);
+ } else {
+ // Inform the actions module about the default argument
+ Actions.ActOnParamDefaultArgument(Param, EqualLoc,
+ DefArgResult.release());
+ }
}
}
ParamInfo.push_back(DeclaratorChunk::ParamInfo(ParmII,
- ParmDecl.getIdentifierLoc(), Param));
+ ParmDecl.getIdentifierLoc(), Param,
+ DefArgToks));
}
// If the next token is a comma, consume it and keep reading arguments.
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index edcbfad..3771867 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -533,6 +533,40 @@
Init.release(),
LastDeclInGroup);
+ if (DeclaratorInfo.isFunctionDeclarator() &&
+ DeclaratorInfo.getDeclSpec().getStorageClassSpec()
+ != DeclSpec::SCS_typedef) {
+ // We just declared a member function. If this member function
+ // has any default arguments, we'll need to parse them later.
+ LateParsedMethodDeclaration *LateMethod = 0;
+ DeclaratorChunk::FunctionTypeInfo &FTI
+ = DeclaratorInfo.getTypeObject(0).Fun;
+ for (unsigned ParamIdx = 0; ParamIdx < FTI.NumArgs; ++ParamIdx) {
+ if (LateMethod || FTI.ArgInfo[ParamIdx].DefaultArgTokens) {
+ if (!LateMethod) {
+ // Push this method onto the stack of late-parsed method
+ // declarations.
+ getCurTopClassStack().MethodDecls.push_back(
+ LateParsedMethodDeclaration(LastDeclInGroup));
+ LateMethod = &getCurTopClassStack().MethodDecls.back();
+
+ // Add all of the parameters prior to this one (they don't
+ // have default arguments).
+ LateMethod->DefaultArgs.reserve(FTI.NumArgs);
+ for (unsigned I = 0; I < ParamIdx; ++I)
+ LateMethod->DefaultArgs.push_back(
+ LateParsedDefaultArgument(FTI.ArgInfo[ParamIdx].Param));
+ }
+
+ // Add this parameter to the list of parameters (it or may
+ // not have a default argument).
+ LateMethod->DefaultArgs.push_back(
+ LateParsedDefaultArgument(FTI.ArgInfo[ParamIdx].Param,
+ FTI.ArgInfo[ParamIdx].DefaultArgTokens));
+ }
+ }
+ }
+
// If we don't have a comma, it is either the end of the list (a ';')
// or an error, bail out.
if (Tok.isNot(tok::comma))
@@ -642,10 +676,13 @@
// exception-specifications, and constructor ctor-initializers (including
// such things in nested classes).
//
- // FIXME: Only function bodies are parsed correctly, fix the rest.
+ // FIXME: Only function bodies and constructor ctor-initializers are
+ // parsed correctly, fix the rest.
if (!CurScope->getParent()->isCXXClassScope()) {
// We are not inside a nested class. This class and its nested classes
- // are complete and we can parse the lexed inline method definitions.
+ // are complete and we can parse the delayed portions of method
+ // declarations and the lexed inline method definitions.
+ ParseLexedMethodDeclarations();
ParseLexedMethodDefs();
// For a local class of inline method, pop the LexedMethodsForTopClass that