Skip to content

Commit 3e625d9

Browse files
authored
Parsing of member bounds declarations. (#23)
This change adds parsing of member bounds declarations. Member bounds declarations use member bounds expressions, which can only use other members of the structure. The changes include: - IR: no change is needed to represent member bounds expressions. Because clang handles C++, it already can represent members directly in the IR for expressions. Bounds expressions are a subclass of expressions. We need to attach bounds expressions to member declarations. We already have bounds expressions on function declarations and variable declarations, which have the same base class as member declarations. Instead of having a 3rd copy of the code, we factor the common code for representing bounds expressions and move it to the common base. - Parsing: we modify the parsing of member declarators that are followed by ':' to parse either a bounds expression or a constant-expression that is a bit-field size. We check whether the first token after the ':' is an identifier that is contextual keyword for bounds expressions. The parsing of bounds expressions is deferred until all members of the structure have been seen. This is so that member bounds expressions may refer to any member of the structure. Constant expressions for bitfield sizes continue to be eagerly parsed. A unique_ptr is used for the pointer to the CachedTokens object. This makes it clear when the CachedTokens object is deallocated. Clean up existing code for deferred parsing of bounds expressions to use unique_ptrs for CachedTokens as well. The decision to just look for a bounds expression keyword can lead to several error messages from clang if there is a spelling error in a contextual keyword. Clang interprets the misspelled keyword as an implicit function declaration and issues unknown identifier errors for uses of members. For now, we'll live this because this specific case is a fairly low priority to address. The type for the identifier in the declarator hasn't been built yet at the time that we make the parsing decision. We could move that up or defer parsing of constant expressions. Either change seems likely orthogonal to this change. - Semantic processing: member bounds expressions are restricted to accessing only members. We add a state flag to Sema that changes the lookup of C identifiers during expression parsing to look up identifiers in the member namespace instead of the ordinary namespace. The state flag is protected by an RAII class. Testing: - Wrote a new feature tests of parsing member bounds declaration. These wil be checked in separately in the Checked C repo in parsing\member_bounds.c. - Passes existing clang test baseline.
1 parent 8080f9d commit 3e625d9

File tree

11 files changed

+200
-103
lines changed

11 files changed

+200
-103
lines changed

include/clang/AST/Decl.h

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -664,9 +664,12 @@ class DeclaratorDecl : public ValueDecl {
664664
DeclaratorDecl(Kind DK, DeclContext *DC, SourceLocation L,
665665
DeclarationName N, QualType T, TypeSourceInfo *TInfo,
666666
SourceLocation StartL)
667-
: ValueDecl(DK, DC, L, N, T), DeclInfo(TInfo), InnerLocStart(StartL) {
667+
: ValueDecl(DK, DC, L, N, T), DeclInfo(TInfo), InnerLocStart(StartL),
668+
Bounds(nullptr) {
668669
}
669670

671+
BoundsExpr *Bounds;
672+
670673
public:
671674
TypeSourceInfo *getTypeSourceInfo() const {
672675
return hasExtInfo()
@@ -731,6 +734,28 @@ class DeclaratorDecl : public ValueDecl {
731734

732735
friend class ASTDeclReader;
733736
friend class ASTDeclWriter;
737+
738+
// Checked C bounds information
739+
// For function declarations, this is the return bounds of the function.
740+
741+
/// \brief Return true if this declaration has bounds declared for it.
742+
bool hasBoundsExpr() const;
743+
744+
/// \brief The declared bounds for this declaration. For function
745+
/// declarations, this is the return bounds of the function. Null if no
746+
/// bounds have been declared.
747+
const BoundsExpr *getBoundsExpr() const {
748+
return const_cast<DeclaratorDecl *>(this)->getBoundsExpr();
749+
}
750+
751+
/// \brief The declared bounds for this declaration. For function
752+
/// declarations, this is the return bounds of the function. Null if no
753+
/// bounds have been declared.
754+
BoundsExpr *getBoundsExpr();
755+
756+
/// \brief Set the declared bounds for this declaration. For function
757+
/// declarations, this is the return bounds of the function.
758+
void setBoundsExpr(BoundsExpr *E);
734759
};
735760

736761
/// \brief Structure used to store a statement, the constant value to
@@ -801,15 +826,6 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
801826
/// C++ default argument.
802827
mutable InitType Init;
803828

804-
// TODO: like the Init member above, it wastes space to have a pointer to a
805-
// BoundsExpr in every VarDecl when many of them won't have bounds
806-
// declarations. We could move the Init member and the Bounds to an
807-
// "extension" object that is allocated on demand, at least not increasing
808-
// the space usage of every single VarDecl.
809-
810-
/// \brief The bounds expression for the variable, if any.
811-
BoundsExpr *Bounds;
812-
813829
private:
814830
class VarDeclBitfields {
815831
friend class VarDecl;
@@ -1130,21 +1146,6 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
11301146
return false;
11311147
}
11321148

1133-
/// \brief Return true if this variable has bounds declared for it.
1134-
bool hasBoundsExpr() const;
1135-
1136-
/// \brief The declared bounds for this variable. Null if no
1137-
/// bounds have been declared.
1138-
const BoundsExpr *getBoundsExpr() const {
1139-
return const_cast<VarDecl *>(this)->getBoundsExpr();
1140-
}
1141-
1142-
/// \brief The declared bounds for this variable. Null if no
1143-
/// bounds have been declared.
1144-
BoundsExpr *getBoundsExpr();
1145-
1146-
void setBoundsExpr(BoundsExpr *E);
1147-
11481149
/// getAnyInitializer - Get the initializer for this variable, no matter which
11491150
/// declaration it is attached to.
11501151
const Expr *getAnyInitializer() const {
@@ -1584,8 +1585,6 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
15841585
/// 'enum Y' in 'void f(enum Y {AA} x) {}'.
15851586
ArrayRef<NamedDecl *> DeclsInPrototypeScope;
15861587

1587-
BoundsExpr *ReturnBoundsExpr;
1588-
15891588
LazyDeclStmtPtr Body;
15901589

15911590
// FIXME: This can be packed into the bitfields in DeclContext.
@@ -1689,7 +1688,7 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
16891688
StartLoc),
16901689
DeclContext(DK),
16911690
redeclarable_base(C),
1692-
ParamInfo(nullptr), ReturnBoundsExpr(nullptr), Body(),
1691+
ParamInfo(nullptr), Body(),
16931692
SClass(S),
16941693
IsInline(isInlineSpecified), IsInlineSpecified(isInlineSpecified),
16951694
IsVirtualAsWritten(false), IsPure(false), HasInheritedPrototype(false),
@@ -2067,11 +2066,6 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
20672066
/// computed linkage of symbol, see getLinkage.
20682067
StorageClass getStorageClass() const { return StorageClass(SClass); }
20692068

2070-
/// \brief Returns the bounds expression for the return value of this function, if
2071-
/// any.
2072-
BoundsExpr *getReturnBoundsExpr() { return ReturnBoundsExpr; }
2073-
void setReturnBoundsExpr(BoundsExpr *E) { ReturnBoundsExpr = E; }
2074-
20752069
/// \brief Determine whether the "inline" keyword was specified for this
20762070
/// function.
20772071
bool isInlineSpecified() const { return IsInlineSpecified; }

include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8430,4 +8430,8 @@ def err_checked_vla : Error<
84308430
def err_checked_cplusplus : Error<
84318431
"checked extension not supported for C++">;
84328432

8433+
def err_undeclared_member_use : Error<"use of undeclared member %0">;
8434+
8435+
def err_expected_bounds_expr_for_member : Error<
8436+
"expected bounds expression">;
84338437
} // end of sema component.

include/clang/Parse/Parser.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1632,9 +1632,12 @@ class Parser : public CodeCompletionHandler {
16321632
//===--------------------------------------------------------------------===//
16331633
// Checked C Expressions
16341634

1635+
/// \brief Return true if this token can start a bounds expression.
1636+
bool StartsBoundsExpression(Token &Tok);
16351637
ExprResult ParseBoundsExpression();
16361638
bool ConsumeAndStoreBoundsExpression(CachedTokens &Toks);
1637-
ExprResult DeferredParseBoundsExpression(CachedTokens *Toks);
1639+
ExprResult DeferredParseBoundsExpression(std::unique_ptr<CachedTokens> Toks);
1640+
16381641

16391642
//===--------------------------------------------------------------------===//
16401643
// clang Expressions
@@ -1895,7 +1898,7 @@ class Parser : public CodeCompletionHandler {
18951898

18961899
void ParseStructDeclaration(
18971900
ParsingDeclSpec &DS,
1898-
llvm::function_ref<void(ParsingFieldDeclarator &)> FieldsCallback);
1901+
llvm::function_ref<void(ParsingFieldDeclarator &, std::unique_ptr<CachedTokens>)> FieldsCallback);
18991902

19001903
bool isDeclarationSpecifier(bool DisambiguatingWithExpression = false);
19011904
bool isTypeSpecifierQualifier();

include/clang/Sema/Sema.h

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,11 @@ class Sema {
420420
/// element type here is ExprWithCleanups::Object.
421421
SmallVector<BlockDecl*, 8> ExprCleanupObjects;
422422

423+
/// True if the current expression is a member bounds expression
424+
/// for a structure. Member bounds expressions can only reference
425+
/// members and cannot reference variables.
426+
bool IsMemberBoundsExpr;
427+
423428
/// \brief Store a list of either DeclRefExprs or MemberExprs
424429
/// that contain a reference to a variable (constant) that may or may not
425430
/// be odr-used in this Expr, and we won't know until all lvalue-to-rvalue
@@ -1920,8 +1925,8 @@ class Sema {
19201925
void ActOnDefs(Scope *S, Decl *TagD, SourceLocation DeclStart,
19211926
IdentifierInfo *ClassName,
19221927
SmallVectorImpl<Decl *> &Decls);
1923-
Decl *ActOnField(Scope *S, Decl *TagD, SourceLocation DeclStart,
1924-
Declarator &D, Expr *BitfieldWidth);
1928+
FieldDecl *ActOnField(Scope *S, Decl *TagD, SourceLocation DeclStart,
1929+
Declarator &D, Expr *BitfieldWidth);
19251930

19261931
FieldDecl *HandleField(Scope *S, RecordDecl *TagD, SourceLocation DeclStart,
19271932
Declarator &D, Expr *BitfieldWidth,
@@ -4114,8 +4119,8 @@ class Sema {
41144119
ExprResult ActOnRangeBoundsExpr(SourceLocation BoundsKWLoc, Expr *LowerBound,
41154120
Expr *UpperBound, SourceLocation RParenLoc);
41164121

4117-
void ActOnBoundsExpr(VarDecl *D, BoundsExpr *Expr);
4118-
void ActOnInvalidBoundsExpr(VarDecl *D);
4122+
void ActOnBoundsExpr(DeclaratorDecl *D, BoundsExpr *Expr);
4123+
void ActOnInvalidBoundsExpr(DeclaratorDecl *D);
41194124
BoundsExpr *CreateInvalidBoundsExpr();
41204125

41214126
//===---------------------------- Clang Extensions ----------------------===//
@@ -9437,6 +9442,24 @@ class EnterExpressionEvaluationContext {
94379442
}
94389443
};
94399444

9445+
/// \brief RAII object that handles state changes for processing a member
9446+
// bounds expressions.
9447+
class EnterMemberBoundsExprRAII {
9448+
Sema &S;
9449+
bool SavedMemberBounds;
9450+
9451+
public:
9452+
EnterMemberBoundsExprRAII(Sema &S)
9453+
: S(S), SavedMemberBounds(S.IsMemberBoundsExpr)
9454+
{
9455+
S.IsMemberBoundsExpr = true;
9456+
}
9457+
9458+
~EnterMemberBoundsExprRAII() {
9459+
S.IsMemberBoundsExpr = SavedMemberBounds;
9460+
}
9461+
};
9462+
94409463
DeductionFailureInfo
94419464
MakeDeductionFailureInfo(ASTContext &Context, Sema::TemplateDeductionResult TDK,
94429465
sema::TemplateDeductionInfo &Info);

lib/AST/Decl.cpp

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,6 +1764,20 @@ void QualifierInfo::setTemplateParameterListsInfo(
17641764
}
17651765
}
17661766

1767+
// Checked C bounds information
1768+
1769+
bool DeclaratorDecl::hasBoundsExpr() const {
1770+
return Bounds != nullptr;
1771+
}
1772+
1773+
BoundsExpr *DeclaratorDecl::getBoundsExpr() {
1774+
return Bounds;
1775+
}
1776+
1777+
void DeclaratorDecl::setBoundsExpr(BoundsExpr *E) {
1778+
Bounds = E;
1779+
}
1780+
17671781
//===----------------------------------------------------------------------===//
17681782
// VarDecl Implementation
17691783
//===----------------------------------------------------------------------===//
@@ -1795,7 +1809,6 @@ VarDecl::VarDecl(Kind DK, ASTContext &C, DeclContext *DC,
17951809
"NonParmVarDeclBitfields too large!");
17961810
AllBits = 0;
17971811
VarDeclBits.SClass = SC;
1798-
Bounds = nullptr;
17991812
// Everything else is implicitly initialized to false.
18001813
}
18011814

@@ -2010,20 +2023,6 @@ VarDecl *VarDecl::getDefinition(ASTContext &C) {
20102023
return nullptr;
20112024
}
20122025

2013-
// Checked C bounds information
2014-
2015-
bool VarDecl::hasBoundsExpr() const {
2016-
return Bounds != nullptr;
2017-
}
2018-
2019-
BoundsExpr *VarDecl::getBoundsExpr() {
2020-
return Bounds;
2021-
}
2022-
2023-
void VarDecl::setBoundsExpr(BoundsExpr *E) {
2024-
Bounds = E;
2025-
}
2026-
20272026
VarDecl::DefinitionKind VarDecl::hasDefinition(ASTContext &C) const {
20282027
DefinitionKind Kind = DeclarationOnly;
20292028

lib/Parse/ParseDecl.cpp

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3575,7 +3575,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
35753575
///
35763576
void Parser::ParseStructDeclaration(
35773577
ParsingDeclSpec &DS,
3578-
llvm::function_ref<void(ParsingFieldDeclarator &)> FieldsCallback) {
3578+
llvm::function_ref<void(ParsingFieldDeclarator &, std::unique_ptr<CachedTokens>)> FieldsCallback) {
35793579

35803580
if (Tok.is(tok::kw___extension__)) {
35813581
// __extension__ silences extension warnings in the subexpression.
@@ -3618,19 +3618,29 @@ void Parser::ParseStructDeclaration(
36183618
} else
36193619
DeclaratorInfo.D.SetIdentifier(nullptr, Tok.getLocation());
36203620

3621+
std::unique_ptr<CachedTokens> BoundsExprTokens;
36213622
if (TryConsumeToken(tok::colon)) {
3622-
ExprResult Res(ParseConstantExpression());
3623-
if (Res.isInvalid())
3624-
SkipUntil(tok::semi, StopBeforeMatch);
3625-
else
3626-
DeclaratorInfo.BitfieldSize = Res.get();
3623+
if (getLangOpts().CheckedC && StartsBoundsExpression(Tok)) {
3624+
BoundsExprTokens.reset(new CachedTokens);
3625+
bool ParsingError = !ConsumeAndStoreBoundsExpression(*BoundsExprTokens);
3626+
if (ParsingError) {
3627+
SkipUntil(tok::semi, StopBeforeMatch);
3628+
}
3629+
} else {
3630+
ExprResult Res(ParseConstantExpression());
3631+
if (Res.isInvalid())
3632+
SkipUntil(tok::semi, StopBeforeMatch);
3633+
else
3634+
DeclaratorInfo.BitfieldSize = Res.get();
3635+
}
36273636
}
36283637

36293638
// If attributes exist after the declarator, parse them.
36303639
MaybeParseGNUAttributes(DeclaratorInfo.D);
36313640

36323641
// We're done with this declarator; invoke the callback.
3633-
FieldsCallback(DeclaratorInfo);
3642+
3643+
FieldsCallback(DeclaratorInfo, std::move(BoundsExprTokens));
36343644

36353645
// If we don't have a comma, it is either the end of the list (a ';')
36363646
// or an error, bail out.
@@ -3666,6 +3676,16 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
36663676

36673677
SmallVector<Decl *, 32> FieldDecls;
36683678

3679+
// Delay parsing/semantic processing of member bounds expressions until after the
3680+
// member list is parsed. For each member with a bounds declaration,
3681+
// keep a list of tokens for the bounds expression. Parsing/processing
3682+
// member bounds expressions is done in a scope that includes all
3683+
// the members in the member list.
3684+
typedef std::pair<FieldDecl *, std::unique_ptr<CachedTokens>> BoundsExprInfo;
3685+
// We are guessing that most structure declarations have 4 or fewer members
3686+
// with bounds expressions on them.
3687+
SmallVector<BoundsExprInfo, 4> deferredBoundsExpressions;
3688+
36693689
// While we still have something to read, read the declarations in the struct.
36703690
while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
36713691
Tok.isNot(tok::eof)) {
@@ -3703,14 +3723,17 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
37033723
}
37043724

37053725
if (!Tok.is(tok::at)) {
3706-
auto CFieldCallback = [&](ParsingFieldDeclarator &FD) {
3726+
auto CFieldCallback = [&](ParsingFieldDeclarator &FD,
3727+
std::unique_ptr<CachedTokens> BoundsExprTokens) {
37073728
// Install the declarator into the current TagDecl.
3708-
Decl *Field =
3709-
Actions.ActOnField(getCurScope(), TagDecl,
3710-
FD.D.getDeclSpec().getSourceRange().getBegin(),
3711-
FD.D, FD.BitfieldSize);
3729+
FieldDecl *Field =
3730+
Actions.ActOnField(getCurScope(), TagDecl,
3731+
FD.D.getDeclSpec().getSourceRange().getBegin(),
3732+
FD.D, FD.BitfieldSize);
37123733
FieldDecls.push_back(Field);
37133734
FD.complete(Field);
3735+
if (BoundsExprTokens != nullptr)
3736+
deferredBoundsExpressions.emplace_back(Field, std::move(BoundsExprTokens));
37143737
};
37153738

37163739
// Parse all the comma separated declarators.
@@ -3763,6 +3786,17 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
37633786
RecordLoc, TagDecl, FieldDecls,
37643787
T.getOpenLocation(), T.getCloseLocation(),
37653788
attrs.getList());
3789+
// Parse the deferred bounds expressions
3790+
for (auto &Pair : deferredBoundsExpressions) {
3791+
EnterMemberBoundsExprRAII MemberBoundsContext(Actions);
3792+
FieldDecl *FieldDecl = Pair.first;
3793+
std::unique_ptr<CachedTokens> Tokens = std::move(Pair.second);
3794+
ExprResult Bounds = DeferredParseBoundsExpression(std::move(Tokens));
3795+
if (Bounds.isInvalid())
3796+
Actions.ActOnInvalidBoundsExpr(FieldDecl);
3797+
else
3798+
Actions.ActOnBoundsExpr(FieldDecl, cast<BoundsExpr>(Bounds.get()));
3799+
}
37663800
StructScope.Exit();
37673801
Actions.ActOnTagFinishDefinition(getCurScope(), TagDecl,
37683802
T.getCloseLocation());
@@ -5925,8 +5959,9 @@ void Parser::ParseParameterDeclarationClause(
59255959
// keep a list of tokens for the bounds expression. Parsing/checking
59265960
// bounds expressions for parameters is done in a scope that includes all
59275961
// the parameters in the parameter list.
5928-
typedef std::pair<ParmVarDecl *, CachedTokens *> BoundsExprInfo;
5929-
// We are guessing that most functions take 4 or fewer parameters.
5962+
typedef std::pair<ParmVarDecl *, std::unique_ptr<CachedTokens>> BoundsExprInfo;
5963+
// We are guessing that most functions take 4 or fewer parameters with
5964+
// bounds expressions on them.
59305965
SmallVector<BoundsExprInfo, 4> deferredBoundsExpressions;
59315966
do {
59325967
// FIXME: Issue a diagnostic if we parsed an attribute-specifier-seq
@@ -6007,10 +6042,9 @@ void Parser::ParseParameterDeclarationClause(
60076042
// error message. This way the error messages from parsing of bounds
60086043
// expressions will be the same or very similar regardless of whether
60096044
// parsing is deferred or not.
6010-
// FIXME: Can we use a smart pointer for BoundsExprTokens?
6011-
CachedTokens *BoundsExprTokens = new CachedTokens;
6045+
std::unique_ptr<CachedTokens> BoundsExprTokens { new CachedTokens};
60126046
bool ParsingError = !ConsumeAndStoreBoundsExpression(*BoundsExprTokens);
6013-
deferredBoundsExpressions.emplace_back(Param, BoundsExprTokens);
6047+
deferredBoundsExpressions.emplace_back(Param, std::move(BoundsExprTokens));
60146048
if (ParsingError) {
60156049
SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch);
60166050
}
@@ -6108,11 +6142,10 @@ void Parser::ParseParameterDeclarationClause(
61086142
} while (TryConsumeToken(tok::comma));
61096143

61106144
// Now parse the deferred bounds expressions
6111-
for (const auto &Pair : deferredBoundsExpressions) {
6145+
for (auto &Pair : deferredBoundsExpressions) {
61126146
ParmVarDecl *Param = Pair.first;
6113-
CachedTokens *Tokens = Pair.second;
6114-
// DeferredParseBoundsExpression deletes Tokens
6115-
ExprResult Bounds = DeferredParseBoundsExpression(Tokens);
6147+
std::unique_ptr<CachedTokens> Tokens = std::move(Pair.second);
6148+
ExprResult Bounds = DeferredParseBoundsExpression(std::move(Tokens));
61166149
if (Bounds.isInvalid())
61176150
Actions.ActOnInvalidBoundsExpr(Param);
61186151
else

0 commit comments

Comments
 (0)