Skip to content

Commit 9b85c1c

Browse files
authored
Parse bounds declarations for variable declarations. (#18)
This change adds parsing of variable declarations with bounds declarations (issue #13). For each declarator in a declaration, the declarator is parsed and then the optional Checked C bounds declaration is parsed. The bounds declaration is parsed before the optional initializing expression for the declarator. Because the declarator has already been parsed and added to the current scope, the bounds expression can be eagerly parsed. One surprise with clang was that placing declarators for a declaration on multiple lines caused a parsing error in the initial implementation, while having all the declarators on one line did not. I traced this back to special case code that looks for typographical mistakes at line endings by calling MightBeDeclarator and generating an error if MightBeDeclarator is false. MightBeDeclarator returns true for syntactic items that might start a declarator. It has special case checks to make sure that an identifier is followed by something that might also be part of a declarator. For Checked C, an identifier that starts a declarator may be followed by ':' and a bounds expression, so allow ':' when the language options include Checked C. This change also improves error handling during the parsing of bounds expressions. - When an error occurs after having parsed an identifier and a left parenthesis, always scan for the matching right parenthesis. The scan for the matching right parenthesis was only happening in one specific case, leading to hard-to-understand spurious parsing errors. - Make a best effort to continue if an error occurs while parsing a bounds expression of the form bounds '(' e1 ',' e2, ')'. clang does not differentiate during parsing of expressions between semantic errors and parsing failures. It is important to continue parsing so that a semantic error does not cause a cascade of parsing errors. These problems were uncovered during testing of parsing of variable declarations with bounds expressions. Specifically, using an incorrect bounds expression in a variable declaration with an initializer caused a spurious parsing errors. Testing: - Created a new feature test for parsing of declarations with bounds (parsing_bounds_var_declarations.c). This will be committed separately to the checkedc repo. - Passes existing Checked C tests. - Passes existing clang base line tests.
1 parent 4ca4bb4 commit 9b85c1c

File tree

2 files changed

+46
-15
lines changed

2 files changed

+46
-15
lines changed

lib/Parse/ParseDecl.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1622,8 +1622,13 @@ bool Parser::MightBeDeclarator(unsigned Context) {
16221622
// At namespace scope, 'identifier:' is probably a typo for 'identifier::'
16231623
// and in block scope it's probably a label. Inside a class definition,
16241624
// this is a bit-field.
1625+
//
1626+
// For Checked C 'identifier:' is a valid start to a declarator because
1627+
// it may be followed by a bounds expression declaring the bounds of
1628+
// identifier.
16251629
return Context == Declarator::MemberContext ||
1626-
(getLangOpts().CPlusPlus && Context == Declarator::FileContext);
1630+
(getLangOpts().CPlusPlus && Context == Declarator::FileContext) ||
1631+
getLangOpts().CheckedC;
16271632

16281633
case tok::identifier: // Possible virt-specifier.
16291634
return getLangOpts().CPlusPlus11 && isCXX11VirtSpecifier(NextToken());
@@ -1936,6 +1941,8 @@ bool Parser::ParseAsmAttributesAfterDeclarator(Declarator &D) {
19361941
/// [GNU] declarator simple-asm-expr[opt] attributes[opt]
19371942
/// [GNU] declarator simple-asm-expr[opt] attributes[opt] '=' initializer
19381943
/// [C++] declarator initializer[opt]
1944+
/// [Checked C] declarator ':' bounds-expression
1945+
/// [Checked C] declarator ':' bounds-expression '=' initializer
19391946
///
19401947
/// [C++] initializer:
19411948
/// [C++] '=' initializer-clause
@@ -2018,6 +2025,23 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
20182025

20192026
bool TypeContainsAuto = D.getDeclSpec().containsPlaceholderType();
20202027

2028+
// Parse the optional Checked C bounds expression.
2029+
if (getLangOpts().CheckedC && Tok.is(tok::colon)) {
2030+
ConsumeToken();
2031+
ExprResult Bounds = ParseBoundsExpression();
2032+
if (Bounds.isInvalid())
2033+
SkipUntil(tok::comma, tok::equal, StopAtSemi | StopBeforeMatch);
2034+
2035+
VarDecl *ThisVarDecl = dyn_cast<VarDecl>(ThisDecl);
2036+
if (ThisVarDecl) {
2037+
if (Bounds.isInvalid())
2038+
Actions.ActOnInvalidBoundsExpr(ThisVarDecl);
2039+
else
2040+
Actions.ActOnBoundsExpr(ThisVarDecl, cast<BoundsExpr>(Bounds.get()));
2041+
} else
2042+
llvm_unreachable("Unexpected decl type");
2043+
}
2044+
20212045
// Parse declarator '=' initializer.
20222046
// If a '==' or '+=' is found, suggest a fixit to '='.
20232047
if (isTokenEqualOrEqualTypo()) {

lib/Parse/ParseExpr.cpp

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2794,20 +2794,20 @@ ExprResult Parser::ParseBoundsExpression() {
27942794
// Look for e1 "," e2
27952795
ExprResult LowerBound =
27962796
Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression());
2797-
if (LowerBound.isInvalid())
2798-
Result = ExprError();
2799-
else if (ExpectAndConsume(tok::comma))
2800-
Result = ExprError();
2801-
else {
2802-
ExprResult UpperBound =
2803-
Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression());
2804-
if (UpperBound.isInvalid())
2805-
Result = ExprError();
2806-
else
2807-
Result = Actions.ActOnRangeBoundsExpr(BoundsKWLoc, LowerBound.get(),
2808-
UpperBound.get(),
2809-
Tok.getLocation());
2810-
}
2797+
2798+
if (ExpectAndConsume(tok::comma))
2799+
// We didn't find a comma, so don't try to parse the upper bounds expression.
2800+
Result = ExprError();
2801+
else {
2802+
ExprResult UpperBound =
2803+
Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression());
2804+
if (LowerBound.isInvalid() || UpperBound.isInvalid())
2805+
Result = ExprError();
2806+
else
2807+
Result = Actions.ActOnRangeBoundsExpr(BoundsKWLoc, LowerBound.get(),
2808+
UpperBound.get(),
2809+
Tok.getLocation());
2810+
}
28112811
} // if (!FoundNullaryOperator)
28122812
} else {
28132813
// The identifier is not a valid contextual keyword for the start of a
@@ -2817,6 +2817,13 @@ ExprResult Parser::ParseBoundsExpression() {
28172817
Result = ExprError();
28182818
}
28192819

2820+
// Result could be invalid because of a syntax error or a semantic checking
2821+
// error. We don't know which. Skip tokens until a right paren is found.
2822+
// If this was only a semantic checking error, the input will already be at
2823+
// a right paren, so skipping will be do nothing.
2824+
if (Result.isInvalid())
2825+
SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch);
2826+
28202827
PT.consumeClose();
28212828
return Result;
28222829
}

0 commit comments

Comments
 (0)