Skip to content

Commit 9859f07

Browse files
committed
Parse bounds expression in function parameter list scope.
This change addresses issue checkedc#7 (process bounds expressions for parameters with all the parameters available). Currently, bounds expressions for parameters are being parsed and type checked in the scope of the parameters seen so far in a parameter list. They need to be typechecked in a scope with all the parameters for the parameter list. To do this, parsing and type checking of bounds expressios for parameters is deferred until the entire parameter list has been seen. This is done in two steps. During the initial parse, the tokens for a superset of bounds expressions are parsed and stored in a list. They are then re-parsed using ParseBoundsExpressions after all parameters have been seen. This change also improve serror messages when a bounds expression is parsed with misspelled contextual keyword. For example, int x : boounds(x, x + 5). Before, the code would simply give up after parsing the misspelled keyword and the '('. This led to additional error messages when the code tried to match the closing ')'. This fix is to skip all the tokens up to but not including the closing ')'. Testing: * Passes existing clang test suite. * Added additional tests for parsing of function parameter lists with bounds expressions. This will be commited separtely to the checked repo.
1 parent b9c738c commit 9859f07

File tree

3 files changed

+101
-14
lines changed

3 files changed

+101
-14
lines changed

include/clang/Parse/Parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,6 +1633,8 @@ class Parser : public CodeCompletionHandler {
16331633
// Checked C Expressions
16341634

16351635
ExprResult ParseBoundsExpression();
1636+
bool ConsumeAndStoreBoundsExpression(CachedTokens &Toks);
1637+
ExprResult DeferredParseBoundsExpression(CachedTokens &Toks);
16361638

16371639
//===--------------------------------------------------------------------===//
16381640
// clang Expressions

lib/Parse/ParseDecl.cpp

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5859,21 +5859,29 @@ void Parser::ParseFunctionDeclaratorIdentifierList(
58595859
/// parameter-list ',' parameter-declaration
58605860
///
58615861
/// parameter-declaration: [C99 6.7.5]
5862-
/// declaration-specifiers declarator
5863-
/// [C++] declaration-specifiers declarator '=' assignment-expression
5864-
/// [C++11] initializer-clause
5865-
/// [GNU] declaration-specifiers declarator attributes
5866-
/// declaration-specifiers abstract-declarator[opt]
5867-
/// [C++] declaration-specifiers abstract-declarator[opt]
5868-
/// '=' assignment-expression
5869-
/// [GNU] declaration-specifiers abstract-declarator[opt] attributes
5870-
/// [C++11] attribute-specifier-seq parameter-declaration
5862+
/// declaration-specifiers declarator
5863+
/// [Checked C] declaration-specifiers declarator ':' bounds-expression
5864+
/// [C++] declaration-specifiers declarator '=' assignment-expression
5865+
/// [C++11] initializer-clause
5866+
/// [GNU] declaration-specifiers declarator attributes
5867+
/// declaration-specifiers abstract-declarator[opt]
5868+
/// [C++] declaration-specifiers abstract-declarator[opt]
5869+
/// '=' assignment-expression
5870+
/// [GNU] declaration-specifiers abstract-declarator[opt] attributes
5871+
/// [C++11] attribute-specifier-seq parameter-declaration
58715872
///
58725873
void Parser::ParseParameterDeclarationClause(
58735874
Declarator &D,
58745875
ParsedAttributes &FirstArgAttrs,
58755876
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo,
58765877
SourceLocation &EllipsisLoc) {
5878+
// Delay parsing/semantic checking of bounds expressions until after the
5879+
// parameter list is parsed. For each variable with a bounds declaration,
5880+
// keep a list of tokens for the bounds expression. Parsing/checking
5881+
// bounds expressions for parameters is done in a scope that includes all
5882+
// the parameters in the parameter list.
5883+
typedef std::pair<ParmVarDecl *, CachedTokens *> BoundsExprInfo;
5884+
SmallVector<BoundsExprInfo, 4> deferredBoundsExpressions;
58775885
do {
58785886
// FIXME: Issue a diagnostic if we parsed an attribute-specifier-seq
58795887
// before deciding this was a parameter-declaration-clause.
@@ -5944,14 +5952,21 @@ void Parser::ParseParameterDeclarationClause(
59445952
// added to the current scope.
59455953
ParmVarDecl *Param = Actions.ActOnParamDeclarator(getCurScope(), ParmDeclarator);
59465954

5947-
// Parse the bounds expression, if any.
5955+
// Parse and store the tokens for the bounds expression, if there is one.
59485956
if (getLangOpts().CheckedC && Tok.is(tok::colon)) {
59495957
ConsumeToken();
5950-
ExprResult Bounds = ParseBoundsExpression();
5951-
if (Bounds.isInvalid()) {
5958+
// Consume and store tokens until a bounds-like expression has been
5959+
// read or a parsing error has happened. Store the tokens even if a
5960+
// parsing error occurs so that ParseBoundsExpression can generate the
5961+
// error message. This way the error messages from parsing of bounds
5962+
// expressions will be the same or very similar regardless of whether
5963+
// parsing is deferred or not.
5964+
CachedTokens *BoundsExprTokens = new CachedTokens;
5965+
bool ParsingError = !ConsumeAndStoreBoundsExpression(*BoundsExprTokens);
5966+
deferredBoundsExpressions.push_back(BoundsExprInfo(Param, BoundsExprTokens));
5967+
if (ParsingError) {
59525968
SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch);
5953-
Actions.ActOnInvalidBoundsExpr(Param);
5954-
} else Actions.ActOnBoundsExpr(Param, cast<BoundsExpr>(Bounds.get()));
5969+
}
59555970
}
59565971

59575972
// Parse the default argument, if any. We parse the default
@@ -6044,6 +6059,18 @@ void Parser::ParseParameterDeclarationClause(
60446059

60456060
// If the next token is a comma, consume it and keep reading arguments.
60466061
} while (TryConsumeToken(tok::comma));
6062+
6063+
// Now parse the deferred bounds expressions
6064+
for (auto Pair : deferredBoundsExpressions) {
6065+
ParmVarDecl *Param = Pair.first;
6066+
CachedTokens *Tokens = Pair.second;
6067+
ExprResult Bounds = DeferredParseBoundsExpression(*Tokens);
6068+
delete Tokens;
6069+
if (Bounds.isInvalid())
6070+
Actions.ActOnInvalidBoundsExpr(Param);
6071+
else
6072+
Actions.ActOnBoundsExpr(Param, cast<BoundsExpr>(Bounds.get()));
6073+
}
60476074
}
60486075

60496076
/// [C90] direct-declarator '[' constant-expression[opt] ']'

lib/Parse/ParseExpr.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2813,13 +2813,71 @@ ExprResult Parser::ParseBoundsExpression() {
28132813
// The identifier is not a valid contextual keyword for the start of a
28142814
// a bounds expression.
28152815
Diag(Tok, diag::err_expected_bounds_expr);
2816+
SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch);
28162817
Result = ExprError();
28172818
}
28182819

28192820
PT.consumeClose();
28202821
return Result;
28212822
}
28222823

2824+
/// Consume and store tokens for an expression shaped like a bounds expression
2825+
/// in the passed token container. Returns \c true if it reached the end of
2826+
/// something bounds-expression shaped, \c false if a parsing error occurred,
2827+
///
2828+
/// Stop when the end of the expression is reached or a parsing error occurs
2829+
/// for which ParseBoundsExpression doesn't know how to make forward progress.
2830+
/// An expression is shaped like a bounds expression if it consists of
2831+
/// identifier '(' sequence of tokens ')'
2832+
/// where parentheses balance within the sequence of tokens.
2833+
bool Parser::ConsumeAndStoreBoundsExpression(CachedTokens &Toks) {
2834+
Toks.push_back(Tok);
2835+
if (Tok.getKind() != tok::identifier) {
2836+
return false;
2837+
}
2838+
ConsumeToken();
2839+
2840+
Toks.push_back(Tok);
2841+
if (Tok.getKind() != tok::l_paren) {
2842+
return false;
2843+
}
2844+
ConsumeParen();
2845+
2846+
return ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/true);
2847+
}
2848+
2849+
/// Given a list of tokens that have the same shape as a bounds
2850+
/// expression, parse them to create a bounds expression.
2851+
ExprResult Parser::DeferredParseBoundsExpression(CachedTokens &Toks) {
2852+
Token LastBoundsExprToken = Toks.back();
2853+
Token BoundsExprEnd;
2854+
BoundsExprEnd.startToken();
2855+
BoundsExprEnd.setKind(tok::eof);
2856+
SourceLocation OrigLoc = LastBoundsExprToken.getEndLoc();
2857+
BoundsExprEnd.setLocation(OrigLoc);
2858+
Toks.push_back(BoundsExprEnd);
2859+
2860+
Toks.push_back(Tok); // Save the current token at the end of the new tokens
2861+
// so it isn't lost.
2862+
PP.EnterTokenStream(Toks, true);
2863+
ConsumeAnyToken(); // Skip past the current token to the new tokens.
2864+
ExprResult Result = ParseBoundsExpression();
2865+
2866+
// There could be leftover tokens because of an error.
2867+
// Skip through them until we reach the eof token.
2868+
if (Tok.isNot(tok::eof)) {
2869+
assert(Result.isInvalid());
2870+
while (Tok.isNot(tok::eof))
2871+
ConsumeAnyToken();
2872+
}
2873+
2874+
// Clean up the remaining EOF token.
2875+
if (Tok.is(tok::eof) && Tok.getLocation() == OrigLoc)
2876+
ConsumeAnyToken();
2877+
2878+
return Result;
2879+
}
2880+
28232881
/// ParseBlockLiteralExpression - Parse a block literal, which roughly looks
28242882
/// like ^(int x){ return x+1; }
28252883
///

0 commit comments

Comments
 (0)