-
Notifications
You must be signed in to change notification settings - Fork 79
Parse bounds expressions in function parameter list scopes. #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0c09929
8ddb7f0
73a8a94
b9c738c
9859f07
65b3a84
5543e6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5859,21 +5859,30 @@ void Parser::ParseFunctionDeclaratorIdentifierList( | |
/// parameter-list ',' parameter-declaration | ||
/// | ||
/// parameter-declaration: [C99 6.7.5] | ||
/// declaration-specifiers declarator | ||
/// [C++] declaration-specifiers declarator '=' assignment-expression | ||
/// [C++11] initializer-clause | ||
/// [GNU] declaration-specifiers declarator attributes | ||
/// declaration-specifiers abstract-declarator[opt] | ||
/// [C++] declaration-specifiers abstract-declarator[opt] | ||
/// '=' assignment-expression | ||
/// [GNU] declaration-specifiers abstract-declarator[opt] attributes | ||
/// [C++11] attribute-specifier-seq parameter-declaration | ||
/// declaration-specifiers declarator | ||
/// [Checked C] declaration-specifiers declarator ':' bounds-expression | ||
/// [C++] declaration-specifiers declarator '=' assignment-expression | ||
/// [C++11] initializer-clause | ||
/// [GNU] declaration-specifiers declarator attributes | ||
/// declaration-specifiers abstract-declarator[opt] | ||
/// [C++] declaration-specifiers abstract-declarator[opt] | ||
/// '=' assignment-expression | ||
/// [GNU] declaration-specifiers abstract-declarator[opt] attributes | ||
/// [C++11] attribute-specifier-seq parameter-declaration | ||
/// | ||
void Parser::ParseParameterDeclarationClause( | ||
Declarator &D, | ||
ParsedAttributes &FirstArgAttrs, | ||
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo, | ||
SourceLocation &EllipsisLoc) { | ||
// Delay parsing/semantic checking of bounds expressions until after the | ||
// parameter list is parsed. For each variable with a bounds declaration, | ||
// keep a list of tokens for the bounds expression. Parsing/checking | ||
// bounds expressions for parameters is done in a scope that includes all | ||
// the parameters in the parameter list. | ||
typedef std::pair<ParmVarDecl *, CachedTokens *> BoundsExprInfo; | ||
// We are guessing that most functions take 4 or fewer parameters. | ||
SmallVector<BoundsExprInfo, 4> deferredBoundsExpressions; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Is there specific thinking behind the selection of 4 here? It would be helpful to understand if there's something special about that value or if it was a reasonable guess at likely length... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a guess that most functions take 4 parameters or less. SmallVector<T,n> doesn't heap-allocate unless you add more than n elements. I'll add a comment about the magic constant. |
||
do { | ||
// FIXME: Issue a diagnostic if we parsed an attribute-specifier-seq | ||
// before deciding this was a parameter-declaration-clause. | ||
|
@@ -5944,14 +5953,22 @@ void Parser::ParseParameterDeclarationClause( | |
// added to the current scope. | ||
ParmVarDecl *Param = Actions.ActOnParamDeclarator(getCurScope(), ParmDeclarator); | ||
|
||
// Parse the bounds expression, if any. | ||
// Parse and store the tokens for the bounds expression, if there is one. | ||
if (getLangOpts().CheckedC && Tok.is(tok::colon)) { | ||
ConsumeToken(); | ||
ExprResult Bounds = ParseBoundsExpression(); | ||
if (Bounds.isInvalid()) { | ||
// Consume and store tokens until a bounds-like expression has been | ||
// read or a parsing error has happened. Store the tokens even if a | ||
// parsing error occurs so that ParseBoundsExpression can generate the | ||
// error message. This way the error messages from parsing of bounds | ||
// expressions will be the same or very similar regardless of whether | ||
// parsing is deferred or not. | ||
// FIXME: Can we use a smart pointer for BoundsExprTokens? | ||
CachedTokens *BoundsExprTokens = new CachedTokens; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Similar code below has a FIXME comment asking if we can use a smart pointer of some kind; can we? Otherwise, a thrown exception means leaking the memory. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. clang and LLVM use an explicit error code/error return value style, instead of C++ exceptions. I couldn't find documentation on which smart pointer class they want to use. I'll add a FIXME comment like there is in the similar code below. |
||
bool ParsingError = !ConsumeAndStoreBoundsExpression(*BoundsExprTokens); | ||
deferredBoundsExpressions.emplace_back(Param, BoundsExprTokens); | ||
if (ParsingError) { | ||
SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch); | ||
Actions.ActOnInvalidBoundsExpr(Param); | ||
} else Actions.ActOnBoundsExpr(Param, cast<BoundsExpr>(Bounds.get())); | ||
} | ||
} | ||
|
||
// Parse the default argument, if any. We parse the default | ||
|
@@ -6044,6 +6061,18 @@ void Parser::ParseParameterDeclarationClause( | |
|
||
// If the next token is a comma, consume it and keep reading arguments. | ||
} while (TryConsumeToken(tok::comma)); | ||
|
||
// Now parse the deferred bounds expressions | ||
for (const auto &Pair : deferredBoundsExpressions) { | ||
ParmVarDecl *Param = Pair.first; | ||
CachedTokens *Tokens = Pair.second; | ||
// DeferredParseBoundsExpression deletes Tokens | ||
ExprResult Bounds = DeferredParseBoundsExpression(Tokens); | ||
if (Bounds.isInvalid()) | ||
Actions.ActOnInvalidBoundsExpr(Param); | ||
else | ||
Actions.ActOnBoundsExpr(Param, cast<BoundsExpr>(Bounds.get())); | ||
} | ||
} | ||
|
||
/// [C90] direct-declarator '[' constant-expression[opt] ']' | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2813,13 +2813,73 @@ ExprResult Parser::ParseBoundsExpression() { | |
// The identifier is not a valid contextual keyword for the start of a | ||
// a bounds expression. | ||
Diag(Tok, diag::err_expected_bounds_expr); | ||
SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch); | ||
Result = ExprError(); | ||
} | ||
|
||
PT.consumeClose(); | ||
return Result; | ||
} | ||
|
||
/// Consume and store tokens for an expression shaped like a bounds expression | ||
/// in the passed token container. Returns \c true if it reached the end of | ||
/// something bounds-expression shaped, \c false if a parsing error occurred, | ||
/// | ||
/// Stop when the end of the expression is reached or a parsing error occurs | ||
/// for which ParseBoundsExpression doesn't know how to make forward progress. | ||
/// An expression is shaped like a bounds expression if it consists of | ||
/// identifier '(' sequence of tokens ')' | ||
/// where parentheses balance within the sequence of tokens. | ||
bool Parser::ConsumeAndStoreBoundsExpression(CachedTokens &Toks) { | ||
Toks.push_back(Tok); | ||
if (Tok.getKind() != tok::identifier) { | ||
return false; | ||
} | ||
ConsumeToken(); | ||
|
||
Toks.push_back(Tok); | ||
if (Tok.getKind() != tok::l_paren) { | ||
return false; | ||
} | ||
ConsumeParen(); | ||
|
||
return ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Does this take care of ignoring nested matched parens? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it does take care of handling nested matched parens. |
||
} | ||
|
||
/// Given a list of tokens that have the same shape as a bounds | ||
/// expression, parse them to create a bounds expression. Delete | ||
/// the list of tokens at the end. | ||
ExprResult Parser::DeferredParseBoundsExpression(CachedTokens *Toks) { | ||
Token LastBoundsExprToken = Toks->back(); | ||
Token BoundsExprEnd; | ||
BoundsExprEnd.startToken(); | ||
BoundsExprEnd.setKind(tok::eof); | ||
SourceLocation OrigLoc = LastBoundsExprToken.getEndLoc(); | ||
BoundsExprEnd.setLocation(OrigLoc); | ||
Toks->push_back(BoundsExprEnd); | ||
|
||
Toks->push_back(Tok); // Save the current token at the end of the new tokens | ||
// so it isn't lost. | ||
PP.EnterTokenStream(*Toks, true); | ||
ConsumeAnyToken(); // Skip past the current token to the new tokens. | ||
ExprResult Result = ParseBoundsExpression(); | ||
|
||
// There could be leftover tokens because of an error. | ||
// Skip through them until we reach the eof token. | ||
if (Tok.isNot(tok::eof)) { | ||
assert(Result.isInvalid()); | ||
while (Tok.isNot(tok::eof)) | ||
ConsumeAnyToken(); | ||
} | ||
|
||
// Clean up the remaining EOF token. | ||
if (Tok.is(tok::eof) && Tok.getLocation() == OrigLoc) | ||
ConsumeAnyToken(); | ||
|
||
delete Toks; | ||
return Result; | ||
} | ||
|
||
/// ParseBlockLiteralExpression - Parse a block literal, which roughly looks | ||
/// like ^(int x){ return x+1; } | ||
/// | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
High-level comment: the changes to this function to support parsing bounds expressions are getting more and more interesting; is there any factoring that can be done to simplify the logic needed inline here (vs. logic in helper classes/structs/functions) and thus also reduce repeated logic across the other functions you'll later be extending to support bounds exprs? Or are you planning to defer that work until you change those other parse routines?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to wait until I've changed the other parse routines. The code will need to be a little different at each location, so I want to see what can be shared.