Skip to content

Commit

Permalink
Extend clang IR with bounds expressions and parse bounds expressions …
Browse files Browse the repository at this point in the history
…for parameters. (#8)

This change extends the clang IR to represent Checked C bounds expressions and optional bounds expressions for variable declarations. It also adds support for parsing bounds expressions and modifies parsing of function parameter lists to parse optional bounds expressions.

Bounds expressions are represented in the IR by adding a new abstract class BoundsExpr and subclassing it for count bounds expressions (count(e1) and byte_count(e1)), range bounds expressions (bounds(e1, e2)), and nullary bounds expressions(bounds(none)). AST printing, serialization, traversal, and tree transformations are extended handle the new expressions.

Bounds expressions are attached to variable declarations by adding an additional member to VarDecls. Many VarDecls will not have bounds expressions, so this adds extra space overhead to the representation of VarDecls. We can revisit this later if it becomes an issue.

To test the new bounds expressions, we add parsing of bounds expressions for function parameter lists and attach the parsed bounds expressions to the VarDecls for the parameters.

Bounds expressions for parameters need to be processed in a scope with all the parameters available. They are currently being processed in a scope that contains the parameters seen so far. This is a little complicated to implement in clang. You have to delay parsing of the bounds expressions. I will come back to this after getting basic parsing of bounds expressions working. I've opened issue #7 to track this.

Testing:
•This passes the current test baseline for this snapshot of clang:
•Wrote new feature tests of parsing of parameters with bounds declarations. There will be a separate pull request to the Github CheckedC repo for these tests.
•Passes the existing Checked C tests.
  Expected Passes    : 8942
  Expected Failures  : 21
  Unsupported Tests  : 206
  Unexpected Failures: 3

•We still need to test AST printing, serialization, traversal, and tree transformations. I've opened issues #4 , #3, #5, and #6 to track this.
  • Loading branch information
dtarditi authored Jun 30, 2016
1 parent 68a6baa commit 6d4d831
Show file tree
Hide file tree
Showing 21 changed files with 553 additions and 4 deletions.
25 changes: 25 additions & 0 deletions include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class CXXTemporary;
class CompoundStmt;
class DependentFunctionTemplateSpecializationInfo;
class Expr;
class BoundsExpr;
class FunctionTemplateDecl;
class FunctionTemplateSpecializationInfo;
class LabelStmt;
Expand Down Expand Up @@ -800,6 +801,15 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
/// C++ default argument.
mutable InitType Init;

// TODO: like the Init member above, it wastes space to have a pointer to a
// BoundsExpr in every VarDecl when many of them won't have bounds
// declarations. We could move the Init member and the Bounds to an
// "extension" object that is allocated on demand, at least not increasing
// the space usage of every single VarDecl.

/// \brief The bounds expression for the variable, if any.
BoundsExpr *Bounds;

private:
class VarDeclBitfields {
friend class VarDecl;
Expand Down Expand Up @@ -1120,6 +1130,21 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
return false;
}

/// \brief Return true if this variable has bounds declared for it.
bool hasBoundsExpr() const;

/// \brief The declared bounds for this variable. Null if no
/// bounds have been declared.
const BoundsExpr *getBoundsExpr() const {
return const_cast<VarDecl *>(this)->getBoundsExpr();
}

/// \brief The declared bounds for this variable. Null if no
/// bounds have been declared.
BoundsExpr *getBoundsExpr();

void setBoundsExpr(BoundsExpr *E);

/// getAnyInitializer - Get the initializer for this variable, no matter which
/// declaration it is attached to.
const Expr *getAnyInitializer() const {
Expand Down
114 changes: 114 additions & 0 deletions include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -4514,6 +4514,120 @@ class GenericSelectionExpr : public Expr {
friend class ASTStmtReader;
};

/// \brief Represents a Checked C bounds expression.
class BoundsExpr : public Expr {
private:
SourceLocation StartLoc, RParenLoc;

public:
BoundsExpr(StmtClass StmtClass, SourceLocation StartLoc, SourceLocation RParenLoc)
: Expr(StmtClass, QualType(), VK_RValue, OK_Ordinary, false,
false, false, false), StartLoc(StartLoc), RParenLoc(RParenLoc) {
}

explicit BoundsExpr(StmtClass StmtClass, EmptyShell Empty) :
Expr(StmtClass, Empty) {}

SourceLocation getStartLoc() { return StartLoc; }
void setStartLoc(SourceLocation Loc) { StartLoc = Loc; }
SourceLocation getRParenLoc() { return RParenLoc; }
void setRParenLoc(SourceLocation Loc) { RParenLoc = Loc; }

SourceLocation getLocStart() const LLVM_READONLY { return StartLoc; }
SourceLocation getLocEnd() const LLVM_READONLY { return RParenLoc; }
};

/// \brief Represents a Checked C nullary bounds expression.
class NullaryBoundsExpr : public BoundsExpr {
public:
enum Kind {
// Invalid bounds expressions are used to flag invalid bounds
// expressions and prevent spurious downstream error messages
// in bounds declaration checking.
Invalid = 0,
// bounds(none)
None = 1
};

public:
NullaryBoundsExpr(Kind Kind, SourceLocation StartLoc, SourceLocation RParenLoc)
: BoundsExpr(NullaryBoundsExprClass, StartLoc, RParenLoc) {
NullaryBoundsExprBits.Kind = Kind;
}

explicit NullaryBoundsExpr(EmptyShell Empty)
: BoundsExpr(NullaryBoundsExprClass, Empty) {}

Kind getKind() const { return (Kind) NullaryBoundsExprBits.Kind; }
void setKind(Kind Kind) { NullaryBoundsExprBits.Kind = Kind; }

// Iterators
child_range children() {
return child_range(child_iterator(), child_iterator());
}
};

/// \brief Represents a Checked C count bounds expression.
class CountBoundsExpr : public BoundsExpr {
public:
enum Kind {
ElementCount = 0,
ByteCount = 1,
};
private:
Stmt *CountExpr;

public:
CountBoundsExpr(Kind Kind, Expr *Count, SourceLocation StartLoc,
SourceLocation RParenLoc)
: BoundsExpr(CountBoundsExprClass, StartLoc, RParenLoc),
CountExpr(Count) {
CountBoundsExprBits.Kind = Kind;
}

explicit CountBoundsExpr(EmptyShell Empty)
: BoundsExpr(CountBoundsExprClass, Empty) {}

Kind getKind() const { return (Kind)CountBoundsExprBits.Kind; }
void setKind(Kind Kind) { CountBoundsExprBits.Kind = Kind; }

Expr *getCountExpr() const { return cast<Expr>(CountExpr); }
void setCountExpr(Expr *E) { CountExpr = E; }

// Iterators
child_range children() {
return child_range(&CountExpr, &CountExpr + 1);
}
};

/// \brief Represents a Checked C range bounds expression.
class RangeBoundsExpr : public BoundsExpr {
private:
enum { LOWER, UPPER, END_EXPR };
Stmt *SubExprs[END_EXPR];

public:
RangeBoundsExpr(Expr *Lower, Expr *Upper, SourceLocation StartLoc,
SourceLocation RParenLoc)
: BoundsExpr(RangeBoundsExprClass, StartLoc, RParenLoc) {
SubExprs[LOWER] = Lower;
SubExprs[UPPER] = Upper;
}

explicit RangeBoundsExpr(EmptyShell Empty)
: BoundsExpr(RangeBoundsExprClass, Empty) {}

Expr *getLowerExpr() const { return cast<Expr>(SubExprs[LOWER]); }
void setLowerExpr(Expr *E) { SubExprs[LOWER] = E; }
Expr *getUpperExpr() const { return cast<Expr>(SubExprs[UPPER]); }
void setUpperExpr(Expr *E) { SubExprs[UPPER] = E; }

// Iterators
child_range children() {
return child_range(&SubExprs[0], &SubExprs[0] + END_EXPR);
}
};

//===----------------------------------------------------------------------===//
// Clang Extensions
//===----------------------------------------------------------------------===//
Expand Down
3 changes: 3 additions & 0 deletions include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2348,6 +2348,9 @@ DEF_TRAVERSE_STMT(FunctionParmPackExpr, {})
DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {})
DEF_TRAVERSE_STMT(CXXFoldExpr, {})
DEF_TRAVERSE_STMT(AtomicExpr, {})
DEF_TRAVERSE_STMT(CountBoundsExpr, {})
DEF_TRAVERSE_STMT(NullaryBoundsExpr, {})
DEF_TRAVERSE_STMT(RangeBoundsExpr, {})

// For coroutines expressions, traverse either the operand
// as written or the implied calls, depending on what the
Expand Down
12 changes: 12 additions & 0 deletions include/clang/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,16 @@ class LLVM_ALIGNAS(LLVM_PTR_SIZE) Stmt {
unsigned NumArgs : 32 - 8 - 1 - NumExprBits;
};

class CountBoundsExprBitFields {
friend class CountBoundsExpr;
unsigned Kind : 1;
};

class NullaryBoundsExprBitFields {
friend class NullaryBoundsExpr;
unsigned Kind : 1;
};

union {
StmtBitfields StmtBits;
CompoundStmtBitfields CompoundStmtBits;
Expand All @@ -257,6 +267,8 @@ class LLVM_ALIGNAS(LLVM_PTR_SIZE) Stmt {
ObjCIndirectCopyRestoreExprBitfields ObjCIndirectCopyRestoreExprBits;
InitListExprBitfields InitListExprBits;
TypeTraitExprBitfields TypeTraitExprBits;
CountBoundsExprBitFields CountBoundsExprBits;
NullaryBoundsExprBitFields NullaryBoundsExprBits;
};

friend class ASTStmtReader;
Expand Down
6 changes: 5 additions & 1 deletion include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ def err_lambda_missing_parens : Error<
"attribute specifier|'constexpr'}0">;
def err_lambda_decl_specifier_repeated : Error<
"%select{'mutable'|'constexpr'}0 cannot appear multiple times in a lambda declarator">;
// C++1z lambda expressions
// C++1z lambda expressions
def err_expected_star_this_capture : Error<
"expected 'this' following '*' in lambda capture list">;

Expand Down Expand Up @@ -1008,4 +1008,8 @@ def err_for_co_await_not_range_for : Error<
"'co_await' modifier can only be applied to range-based for loop">;
}

// Checked C parsing errors
def err_expected_bounds_expr : Error<
"expected bounds expression">;

} // end of Parser diagnostics
6 changes: 6 additions & 0 deletions include/clang/Basic/StmtNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ def ObjCSubscriptRefExpr : DStmt<Expr>;
// Obj-C ARC Expressions.
def ObjCBridgedCastExpr : DStmt<ExplicitCastExpr>;

// Checked C Bounds Expressions.
def BoundsExpr : DStmt<Expr, 1>;
def NullaryBoundsExpr : DStmt<BoundsExpr>;
def CountBoundsExpr : DStmt<BoundsExpr>;
def RangeBoundsExpr : DStmt<BoundsExpr>;

// CUDA Expressions.
def CUDAKernelCallExpr : DStmt<CallExpr>;

Expand Down
24 changes: 24 additions & 0 deletions include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,25 @@ class Parser : public CodeCompletionHandler {
mutable IdentifierInfo *Ident_final;
mutable IdentifierInfo *Ident_override;

/// Checked C contextual keywords

/// These keywords are for bounds expressions. They are contextual to avoid
/// collisions with existing identifiers in programs. Some keywords like "count"
/// and "any" are likely to collide. Others are unlikely to collide, but we make
/// them contextual for consistency.

/// \brief Identifier for "bounds".
IdentifierInfo *Ident_bounds;

/// \brief Identifier for "byte_count".
IdentifierInfo *Ident_byte_count;

/// \brief Identifier for "count".
IdentifierInfo *Ident_count;

/// \brief Identifier for "none".
IdentifierInfo *Ident_none;

// C++ type trait keywords that can be reverted to identifiers and still be
// used as type traits.
llvm::SmallDenseMap<IdentifierInfo *, tok::TokenKind> RevertibleTypeTraits;
Expand Down Expand Up @@ -1610,6 +1629,11 @@ class Parser : public CodeCompletionHandler {
ExprResult ParseBraceInitializer();
ExprResult ParseInitializerWithPotentialDesignator();

//===--------------------------------------------------------------------===//
// Checked C Expressions

ExprResult ParseBoundsExpression();

//===--------------------------------------------------------------------===//
// clang Expressions

Expand Down
16 changes: 15 additions & 1 deletion include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -1695,7 +1695,7 @@ class Sema {
bool IsExplicitSpecialization);
void CheckMain(FunctionDecl *FD, const DeclSpec &D);
void CheckMSVCRTEntryPoint(FunctionDecl *FD);
Decl *ActOnParamDeclarator(Scope *S, Declarator &D);
ParmVarDecl *ActOnParamDeclarator(Scope *S, Declarator &D);
ParmVarDecl *BuildParmVarDeclForTypedef(DeclContext *DC,
SourceLocation Loc,
QualType T);
Expand Down Expand Up @@ -4103,6 +4103,20 @@ class Sema {
ExprResult ActOnBlockStmtExpr(SourceLocation CaretLoc, Stmt *Body,
Scope *CurScope);

//===---------------------------- Checked C Extension ----------------------===//

ExprResult ActOnNullaryBoundsExpr(SourceLocation BoundKWLoc,
NullaryBoundsExpr::Kind Kind,
SourceLocation RParenLoc);
ExprResult ActOnCountBoundsExpr(SourceLocation BoundsKWLoc,
CountBoundsExpr::Kind Kind, Expr *CountExpr,
SourceLocation RParenLoc);
ExprResult ActOnRangeBoundsExpr(SourceLocation BoundsKWLoc, Expr *LowerBound,
Expr *UpperBound, SourceLocation RParenLoc);

void ActOnBoundsExpr(VarDecl *D, BoundsExpr *Expr);
void ActOnInvalidBoundsExpr(VarDecl *D);

//===---------------------------- Clang Extensions ----------------------===//

/// __builtin_convertvector(...)
Expand Down
5 changes: 5 additions & 0 deletions include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1421,6 +1421,11 @@ namespace clang {
// CUDA
EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr

// Checked C
EXPR_COUNT_BOUNDS_EXPR, // CountBoundsExpr
EXPR_NULLARY_BOUNDS_EXPR, // NullaryBoundsExpr
EXPR_RANGE_BOUNDS_EXPR, // RangeBoundsExpr

// OpenCL
EXPR_ASTYPE, // AsTypeExpr

Expand Down
18 changes: 18 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1795,6 +1795,7 @@ VarDecl::VarDecl(Kind DK, ASTContext &C, DeclContext *DC,
"NonParmVarDeclBitfields too large!");
AllBits = 0;
VarDeclBits.SClass = SC;
Bounds = nullptr;
// Everything else is implicitly initialized to false.
}

Expand Down Expand Up @@ -2009,6 +2010,20 @@ VarDecl *VarDecl::getDefinition(ASTContext &C) {
return nullptr;
}

// Checked C bounds information

bool VarDecl::hasBoundsExpr() const {
return Bounds != nullptr;
}

BoundsExpr *VarDecl::getBoundsExpr() {
return Bounds;
}

void VarDecl::setBoundsExpr(BoundsExpr *E) {
Bounds = E;
}

VarDecl::DefinitionKind VarDecl::hasDefinition(ASTContext &C) const {
DefinitionKind Kind = DeclarationOnly;

Expand All @@ -2022,6 +2037,9 @@ VarDecl::DefinitionKind VarDecl::hasDefinition(ASTContext &C) const {
return Kind;
}




const Expr *VarDecl::getAnyInitializer(const VarDecl *&D) const {
for (auto I : redecls()) {
if (auto Expr = I->getInit()) {
Expand Down
6 changes: 6 additions & 0 deletions lib/AST/DeclPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,12 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) {
? D->getTypeSourceInfo()->getType()
: D->getASTContext().getUnqualifiedObjCPointerType(D->getType());
printDeclType(T, D->getName());
if (D->hasBoundsExpr()) {
Out << " : ";
Expr *BoundsExpr = D->getBoundsExpr();
BoundsExpr->printPretty(Out, nullptr, Policy, Indentation);
}

Expr *Init = D->getInit();
if (!Policy.SuppressInitializers && Init) {
bool ImplicitInit = false;
Expand Down
Loading

0 comments on commit 6d4d831

Please sign in to comment.