Skip to content

Commit 9d60155

Browse files
Merge branch 'master' of github.com:microsoft/checkedc-clang
2 parents b89dbe1 + cb1e150 commit 9d60155

File tree

10 files changed

+315
-2
lines changed

10 files changed

+315
-2
lines changed

clang/include/clang/AST/Expr.h

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7132,6 +7132,74 @@ class RecoveryExpr final : public Expr,
71327132
friend class ASTStmtWriter;
71337133
};
71347134

7135+
/// \brief Represents a Checked C where clause fact.
7136+
class WhereClauseFact {
7137+
public:
7138+
enum class FactKind { BoundsDeclFact, EqualityOpFact };
7139+
FactKind Kind;
7140+
7141+
private:
7142+
// For a BoundsDeclFact, Loc gives the location of the variable whose bounds
7143+
// are being redeclared in the where clause. For example, "_Where p :
7144+
// bounds(p, p + 1)". ^
7145+
// For an EqualityOpFact, Loc gives the location of the start of the equality
7146+
// expression. For example, "_Where a < 1".
7147+
// ^
7148+
// TODO: Implement richer location information by capturing the start and end
7149+
// locations of where clause facts.
7150+
SourceLocation Loc;
7151+
7152+
public:
7153+
WhereClauseFact(FactKind Kind, SourceLocation Loc)
7154+
: Kind(Kind), Loc(Loc) {}
7155+
};
7156+
7157+
/// \brief Represents a Checked C where clause bounds decl fact.
7158+
class BoundsDeclFact : public WhereClauseFact {
7159+
public:
7160+
VarDecl *Var;
7161+
BoundsExpr *Bounds;
7162+
7163+
BoundsDeclFact(VarDecl *Var, BoundsExpr *Bounds, SourceLocation Loc)
7164+
: WhereClauseFact(FactKind::BoundsDeclFact, Loc),
7165+
Var(Var), Bounds(Bounds) {}
7166+
7167+
static bool classof(const WhereClauseFact *Fact) {
7168+
return Fact->Kind == FactKind::BoundsDeclFact;
7169+
}
7170+
};
7171+
7172+
/// \brief Represents a Checked C relational operator fact.
7173+
class EqualityOpFact : public WhereClauseFact {
7174+
public:
7175+
BinaryOperator *EqualityOp;
7176+
7177+
EqualityOpFact(BinaryOperator *EqualityOp, SourceLocation Loc)
7178+
: WhereClauseFact(FactKind::EqualityOpFact, Loc),
7179+
EqualityOp(EqualityOp) {}
7180+
7181+
static bool classof(const WhereClauseFact *Fact) {
7182+
return Fact->Kind == FactKind::EqualityOpFact;
7183+
}
7184+
};
7185+
7186+
using FactListTy = llvm::SmallVector<WhereClauseFact *, 2>;
7187+
7188+
/// \brief Represents a Checked C where clause.
7189+
class WhereClause {
7190+
private:
7191+
SourceLocation Loc;
7192+
FactListTy Facts;
7193+
7194+
public:
7195+
WhereClause(SourceLocation Loc) : Loc(Loc) {}
7196+
7197+
void addFact(WhereClauseFact *Fact) { Facts.push_back(Fact); }
7198+
bool isInvalid() const { return Facts.size() == 0; }
7199+
FactListTy getFacts() { return Facts; }
7200+
static bool classof(const WhereClause *) { return true; }
7201+
};
7202+
71357203
} // end namespace clang
71367204

71377205
#endif // LLVM_CLANG_AST_EXPR_H

clang/include/clang/AST/Stmt.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class SourceManager;
5959
class StringLiteral;
6060
class Token;
6161
class VarDecl;
62+
class WhereClause;
6263

6364
//===----------------------------------------------------------------------===//
6465
// AST classes for statements.
@@ -1368,15 +1369,18 @@ class DeclStmt : public Stmt {
13681369
/// NullStmt - This is the null statement ";": C99 6.8.3p3.
13691370
///
13701371
class NullStmt : public Stmt {
1372+
private:
1373+
WhereClause *WClause;
13711374
public:
13721375
NullStmt(SourceLocation L, bool hasLeadingEmptyMacro = false)
1373-
: Stmt(NullStmtClass) {
1376+
: Stmt(NullStmtClass), WClause(nullptr) {
13741377
NullStmtBits.HasLeadingEmptyMacro = hasLeadingEmptyMacro;
13751378
setSemiLoc(L);
13761379
}
13771380

13781381
/// Build an empty null statement.
1379-
explicit NullStmt(EmptyShell Empty) : Stmt(NullStmtClass, Empty) {}
1382+
explicit NullStmt(EmptyShell Empty) : Stmt(NullStmtClass, Empty),
1383+
WClause(nullptr) {}
13801384

13811385
SourceLocation getSemiLoc() const { return NullStmtBits.SemiLoc; }
13821386
void setSemiLoc(SourceLocation L) { NullStmtBits.SemiLoc = L; }
@@ -1399,6 +1403,9 @@ class NullStmt : public Stmt {
13991403
const_child_range children() const {
14001404
return const_child_range(const_child_iterator(), const_child_iterator());
14011405
}
1406+
1407+
void setWhereClause(WhereClause *WC) { WClause = WC; }
1408+
WhereClause *getWhereClause() const { return WClause; }
14021409
};
14031410

14041411
// The kind of Checked C checking to do in a scope.

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10909,6 +10909,9 @@ def err_probability_out_of_range : Error<
1090910909
"probability argument to __builtin_expect_with_probability is outside the "
1091010910
"range [0.0, 1.0]">;
1091110911

10912+
def err_expected_comparison_op_in_equality_expr : Error<
10913+
"expected comparison operator in equality expression">;
10914+
1091210915
// Checked C diagnostic messages
1091310916
let CategoryName = "Checked C" in {
1091410917

clang/include/clang/Basic/TokenKinds.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,7 @@ KEYWORD(_Bounds_only , KEYCHECKEDC)
707707
KEYWORD(_Exists , KEYCHECKEDC)
708708
KEYWORD(_Pack , KEYCHECKEDC)
709709
KEYWORD(_Unpack , KEYCHECKEDC)
710+
KEYWORD(_And , KEYCHECKEDC)
710711

711712
// Borland Extensions which should be disabled in strict conformance mode.
712713
ALIAS("_pascal" , __pascal , KEYBORLAND)

clang/include/clang/Parse/Parser.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2099,6 +2099,12 @@ class Parser : public CodeCompletionHandler {
20992099
/// Parse a pack expression of the form '_Pack(expr, existential_type, substitution_type)'.
21002100
ExprResult ParsePackExpression();
21012101

2102+
/// Parse a Checked C where clause.
2103+
WhereClause *ParseWhereClause();
2104+
2105+
/// Parse a Checked C where clause fact.
2106+
WhereClauseFact *ParseWhereClauseFact();
2107+
21022108
//===--------------------------------------------------------------------===//
21032109
// clang Expressions
21042110

clang/include/clang/Sema/Sema.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4457,6 +4457,17 @@ class Sema final {
44574457
void disable() { Active = false; }
44584458
};
44594459

4460+
// Checked C: Perform semantic analysis on a where clause.
4461+
WhereClause *ActOnWhereClause(SourceLocation WhereLoc);
4462+
4463+
// Checked C: Perform semantic analysis on a where clause bounds decl fact.
4464+
BoundsDeclFact *ActOnBoundsDeclFact(IdentifierInfo *Id, Expr *E,
4465+
Scope *CurScope, SourceLocation IdLoc,
4466+
SourceLocation BoundsLoc);
4467+
4468+
// Checked C: Perform semantic analysis on a where clause equality-op fact.
4469+
EqualityOpFact *ActOnEqualityOpFact(Expr *E, SourceLocation ExprLoc);
4470+
44604471
StmtResult ActOnDeclStmt(DeclGroupPtrTy Decl,
44614472
SourceLocation StartLoc,
44624473
SourceLocation EndLoc);

clang/lib/Parse/ParseStmt.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,26 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
264264
return Actions.ActOnNullStmt(ConsumeToken(), HasLeadingEmptyMacro);
265265
}
266266

267+
// Parse Checked C _Where token.
268+
case tok::kw__Where: {
269+
WhereClause *WClause = ParseWhereClause();
270+
if (!WClause)
271+
return StmtError();
272+
273+
StmtResult StmtRes = Actions.ActOnNullStmt(SourceLocation());
274+
if (StmtRes.isInvalid() || !isa<NullStmt>(StmtRes.get()))
275+
return StmtError();
276+
277+
auto *NS = dyn_cast<NullStmt>(StmtRes.get());
278+
NS->setWhereClause(WClause);
279+
280+
// The where clause should end with a semicolon.
281+
if (ExpectAndConsume(tok::semi))
282+
return StmtError();
283+
284+
return StmtRes;
285+
}
286+
267287
case tok::kw_if: // C99 6.8.4.1: if-statement
268288
return ParseIfStatement(TrailingElseLoc);
269289
case tok::kw_switch: // C99 6.8.4.2: switch-statement
@@ -2588,3 +2608,65 @@ bool Parser::ParseOpenCLUnrollHintAttribute(ParsedAttributes &Attrs) {
25882608
}
25892609
return true;
25902610
}
2611+
2612+
WhereClauseFact *Parser::ParseWhereClauseFact() {
2613+
// TODO: Handle bounds expression surrounded by parentheses, like:
2614+
// _Where ((((p : bounds(p, p + 1))))).
2615+
// Equality expressions surrounded by parentheses are already handled by
2616+
// invoking IgnoreValuePreservingCasts in ActOnEqualityOpFact.
2617+
2618+
if (Tok.is(tok::identifier) && NextToken().is(tok::colon)) {
2619+
IdentifierInfo *VarName = Tok.getIdentifierInfo();
2620+
2621+
// Consume the identifier.
2622+
SourceLocation IdLoc = ConsumeToken();
2623+
// Consume the ':' token.
2624+
ConsumeToken();
2625+
2626+
// Get the location of the start of bounds expr.
2627+
SourceLocation BoundsLoc = Tok.getLocation();
2628+
2629+
// Parse a bounds decl expression.
2630+
ExprResult BoundsRes(ParseBoundsExpression());
2631+
if (BoundsRes.isInvalid())
2632+
return nullptr;
2633+
return Actions.ActOnBoundsDeclFact(VarName, BoundsRes.get(),
2634+
getCurScope(), IdLoc, BoundsLoc);
2635+
}
2636+
2637+
// Parse an equality expression.
2638+
SourceLocation ExprLoc = Tok.getLocation();
2639+
ExprResult ExprRes = Actions.CorrectDelayedTyposInExpr(ParseExpression());
2640+
if (ExprRes.isInvalid())
2641+
return nullptr;
2642+
return Actions.ActOnEqualityOpFact(ExprRes.get(), ExprLoc);
2643+
}
2644+
2645+
WhereClause *Parser::ParseWhereClause() {
2646+
SourceLocation WhereLoc = Tok.getLocation();
2647+
2648+
// Consume the "_Where" token.
2649+
if (ExpectAndConsume(tok::kw__Where)) {
2650+
SkipUntil(tok::semi, StopBeforeMatch);
2651+
return nullptr;
2652+
}
2653+
2654+
WhereClause *WClause = Actions.ActOnWhereClause(WhereLoc);
2655+
if (!WClause)
2656+
return nullptr;
2657+
2658+
// Parse each where clause fact. We want to issue diagnostics for as many
2659+
// parsing errors a possible. So we do not break on the first error.
2660+
bool IsError = false;
2661+
do {
2662+
WhereClauseFact *Fact = ParseWhereClauseFact();
2663+
if (!Fact)
2664+
IsError = true;
2665+
else
2666+
WClause->addFact(Fact);
2667+
} while (TryConsumeToken(tok::kw__And));
2668+
2669+
if (IsError)
2670+
return nullptr;
2671+
return WClause;
2672+
}

clang/lib/Sema/SemaStmt.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4504,3 +4504,68 @@ StmtResult Sema::ActOnCapturedRegionEnd(Stmt *S) {
45044504

45054505
return Res;
45064506
}
4507+
4508+
WhereClause *Sema::ActOnWhereClause(SourceLocation WhereLoc) {
4509+
return new (Context) WhereClause(WhereLoc);
4510+
}
4511+
4512+
BoundsDeclFact
4513+
*Sema::ActOnBoundsDeclFact(IdentifierInfo *Id, Expr *E,
4514+
Scope *CurScope,
4515+
SourceLocation IdLoc,
4516+
SourceLocation BoundsLoc) {
4517+
BoundsExpr *Bounds = dyn_cast<BoundsExpr>(E);
4518+
if (!Bounds) {
4519+
Diag(BoundsLoc, diag::err_expected_bounds_expr_for_member);
4520+
return nullptr;
4521+
}
4522+
4523+
LookupResult Lookup(*this, Id, IdLoc, Sema::LookupOrdinaryName);
4524+
LookupParsedName(Lookup, CurScope, nullptr, true);
4525+
if (Lookup.empty()) {
4526+
Diag(IdLoc, diag::err_undeclared_var_use) << Id->getName();
4527+
return nullptr;
4528+
}
4529+
4530+
VarDecl *VD = Lookup.getAsSingle<VarDecl>();
4531+
if (!VD) {
4532+
Diag(IdLoc, diag::err_undeclared_var_use) << Id->getName();
4533+
return nullptr;
4534+
}
4535+
4536+
return new (Context) BoundsDeclFact(VD, Bounds, IdLoc);
4537+
}
4538+
4539+
EqualityOpFact *Sema::ActOnEqualityOpFact(Expr *E, SourceLocation ExprLoc) {
4540+
// We define an equality-op fact in terms of equality-expressions as defined
4541+
// in section 6.5.9 of the C11 spec. Equality-op facts have an added
4542+
// constraint that the equality-expressions should be non-modifying
4543+
// expressions.
4544+
4545+
// Here, we are checking whether E is an equality expression defined as:
4546+
// equality-expression:
4547+
// relational-expression
4548+
// equality-expression == equality-expression
4549+
// equality-expression != equality-expression
4550+
4551+
Lexicographic Lex(Context, nullptr);
4552+
Expr *TmpE = Lex.IgnoreValuePreservingOperations(Context, E);
4553+
4554+
// TODO: Handle equality-op facts joined by logical operators, like
4555+
// _Where equality-op-fact && equality-op-fact || equality-op-fact.
4556+
4557+
auto *BO = dyn_cast_or_null<BinaryOperator>(TmpE);
4558+
4559+
// isComparisonOp checks for equality and relational operators.
4560+
if (!BO || !BO->isComparisonOp()) {
4561+
Diag(ExprLoc, diag::err_expected_comparison_op_in_equality_expr);
4562+
return nullptr;
4563+
}
4564+
4565+
// CheckIsNonModifying issues a diagnostic if its argument is not
4566+
// non-modifying.
4567+
if (!CheckIsNonModifying(BO->getLHS()) ||
4568+
!CheckIsNonModifying(BO->getRHS()))
4569+
return nullptr;
4570+
return new (Context) EqualityOpFact(BO, ExprLoc);
4571+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify %s
2+
3+
// Test parsing errors for incorrect where clause.
4+
5+
int f();
6+
7+
void invalid_cases(_Nt_array_ptr<char> p, int a, int b) {
8+
_Where ; // expected-error {{expected expression}}
9+
_Where ;;;;; // expected-error {{expected expression}}
10+
_Where _Where; // expected-error {{expected expression}} expected-error {{expected expression}}
11+
_Where _And; // expected-error {{expected expression}} expected-error {{expected expression}}
12+
_Where _And _And _And; // expected-error {{expected expression}} expected-error {{expected expression}} expected-error {{expected expression}} expected-error {{expected expression}}
13+
_Where a; // expected-error {{expected comparison operator in equality expression}}
14+
_Where a _Where a _Where a; // expected-error {{expected comparison operator in equality expression}} expected-error {{expected comparison operator in equality expression}} expected-error {{expected comparison operator in equality expression}}
15+
_Where a _And; // expected-error {{expected comparison operator in equality expression}} expected-error {{expected expression}}
16+
_Where x; // expected-error {{use of undeclared identifier 'x'}}
17+
_Where p : ; // expected-error {{expected bounds expression}}
18+
_Where p : count(x); // expected-error {{use of undeclared identifier 'x'}}
19+
_Where q : count(0); // expected-error {{use of undeclared identifier q}}
20+
_Where (); // expected-error {{expected expression}}
21+
_Where a = 0; // expected-error {{expected comparison operator in equality expression}}
22+
_Where a == 1 _And a; // expected-error {{expected comparison operator in equality expression}}
23+
_Where a _And a == 1; // expected-error {{expected comparison operator in equality expression}}
24+
_Where a < 0 _And x; // expected-error {{use of undeclared identifier 'x'}}
25+
_Where 1; // expected-error {{expected comparison operator in equality expression}}
26+
_Where x _And p : count(0); // expected-error {{use of undeclared identifier 'x'}}
27+
_Where p : count(0) _And x; // expected-error {{use of undeclared identifier 'x'}}
28+
_Where a == 1 _And p : Count(0); // expected-error {{expected bounds expression}}
29+
_Where p : Bounds(p, p + 1) _And a == 1; // expected-error {{expected bounds expression}}
30+
where a == 1; // expected-error {{use of undeclared identifier 'where'}}
31+
_Where a == 1 and a == 2; // expected-error {{expected ';'}} expected-error {{use of undeclared identifier 'and'}}
32+
_Where a == 1 // expected-error {{expected ';'}}
33+
_Where f(); // expected-error {{expected comparison operator in equality expression}}
34+
_Where f() == 1; // expected-error {{call expression not allowed in expression}}
35+
_Where 1 != f(); // expected-error {{call expression not allowed in expression}}
36+
_Where a++ < 1; // expected-error {{increment expression not allowed in expression}}
37+
_Where a _And p : _And q : count(0) _And x _And a = 1 _And f() < 0 _And; // expected-error {{expected comparison operator in equality expression}} expected-error {{expected bounds expression}} expected-error {{use of undeclared identifier q}} expected-error {{use of undeclared identifier 'x'}} expected-error {{expected comparison operator in equality expression}} expected-error {{call expression not allowed in expression}} expected-error {{expected expression}}
38+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify %s
2+
3+
// Test valid where clause cases.
4+
5+
// expected-no-diagnostics
6+
7+
void valid_cases(_Nt_array_ptr<char> p, _Nt_array_ptr<char> q,
8+
int a, int b) {
9+
_Where a < 0;
10+
_Where a > 0;
11+
_Where a <= 0;
12+
_Where a >= 0;
13+
_Where a == 0;
14+
_Where a != 0;
15+
_Where 1 < 2;
16+
_Where 1 == 2;
17+
_Where a + b < a - b;
18+
_Where a + b < 1 + 2;
19+
_Where 1 + 2 > a + b;
20+
_Where a + 1 == b + 2;
21+
_Where a < 0 _And b > 0;
22+
_Where p : bounds(p, p + 1);
23+
_Where p : bounds(q, q + a);
24+
_Where p : count(0);
25+
_Where p : bounds(p, p + 1) _And q : bounds(q, q + 1);
26+
_Where a < 0 _And p : count(0) _And b > 0 _And q : bounds(q, q + 1);
27+
_Where a > 0 _And a > 1 _And a > 2 _And b < 0 _And b < 1 _And b < 2;
28+
_Where p : count(a) _And p : count(a + 1) _And p : count(a + 2);
29+
_Where q : count(0) _And q : bounds(q, q + 1) _And q : count(a);
30+
_Where (((((a == 0)))));
31+
_Where (a == 0) _And ((a == 0)) _And (((a == 0)));
32+
}

0 commit comments

Comments
 (0)