Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1633,6 +1633,8 @@ class Parser : public CodeCompletionHandler {
// Checked C Expressions

ExprResult ParseBoundsExpression();
bool ConsumeAndStoreBoundsExpression(CachedTokens &Toks);
ExprResult DeferredParseBoundsExpression(CachedTokens *Toks);

//===--------------------------------------------------------------------===//
// clang Expressions
Expand Down
57 changes: 43 additions & 14 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ParseParameterDeclarationClause [](start = 13, length = 31)

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?

Copy link
Member Author

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.

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;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 [](start = 30, length = 1)

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...

Copy link
Member Author

Choose a reason for hiding this comment

The 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.
Expand Down Expand Up @@ -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;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new CachedTokens [](start = 41, length = 16)

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.

Copy link
Member Author

@dtarditi dtarditi Jul 11, 2016

Choose a reason for hiding this comment

The 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
Expand Down Expand Up @@ -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] ']'
Expand Down
60 changes: 60 additions & 0 deletions lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ConsumeAndStoreUntil [](start = 9, length = 20)

Does this take care of ignoring nested matched parens?

Copy link
Member Author

Choose a reason for hiding this comment

The 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; }
///
Expand Down