diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 04c4b46c9f9b..05256ed36051 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -36,6 +36,7 @@ class CXXTemporary; class CompoundStmt; class DependentFunctionTemplateSpecializationInfo; class Expr; +class BoundsExpr; class FunctionTemplateDecl; class FunctionTemplateSpecializationInfo; class LabelStmt; @@ -800,6 +801,15 @@ class VarDecl : public DeclaratorDecl, public Redeclarable { /// 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; @@ -1120,6 +1130,21 @@ class VarDecl : public DeclaratorDecl, public Redeclarable { 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(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 { diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 6b14c60e9513..6ca7072c3538 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -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(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(SubExprs[LOWER]); } + void setLowerExpr(Expr *E) { SubExprs[LOWER] = E; } + Expr *getUpperExpr() const { return cast(SubExprs[UPPER]); } + void setUpperExpr(Expr *E) { SubExprs[UPPER] = E; } + + // Iterators + child_range children() { + return child_range(&SubExprs[0], &SubExprs[0] + END_EXPR); + } +}; + //===----------------------------------------------------------------------===// // Clang Extensions //===----------------------------------------------------------------------===// diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index 6d867270e050..96db26e2287f 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -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 diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h index b6ed6c547cc4..4e16b073d503 100644 --- a/include/clang/AST/Stmt.h +++ b/include/clang/AST/Stmt.h @@ -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; @@ -257,6 +267,8 @@ class LLVM_ALIGNAS(LLVM_PTR_SIZE) Stmt { ObjCIndirectCopyRestoreExprBitfields ObjCIndirectCopyRestoreExprBits; InitListExprBitfields InitListExprBits; TypeTraitExprBitfields TypeTraitExprBits; + CountBoundsExprBitFields CountBoundsExprBits; + NullaryBoundsExprBitFields NullaryBoundsExprBits; }; friend class ASTStmtReader; diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index dfbcdee28cdf..d72ed1802c37 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -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">; @@ -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 diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td index 67fddbdffd6d..4e1b78a1b761 100644 --- a/include/clang/Basic/StmtNodes.td +++ b/include/clang/Basic/StmtNodes.td @@ -168,6 +168,12 @@ def ObjCSubscriptRefExpr : DStmt; // Obj-C ARC Expressions. def ObjCBridgedCastExpr : DStmt; +// Checked C Bounds Expressions. +def BoundsExpr : DStmt; +def NullaryBoundsExpr : DStmt; +def CountBoundsExpr : DStmt; +def RangeBoundsExpr : DStmt; + // CUDA Expressions. def CUDAKernelCallExpr : DStmt; diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 7f0c3a7b896d..3184e11fb70f 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -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 RevertibleTypeTraits; @@ -1610,6 +1629,11 @@ class Parser : public CodeCompletionHandler { ExprResult ParseBraceInitializer(); ExprResult ParseInitializerWithPotentialDesignator(); + //===--------------------------------------------------------------------===// + // Checked C Expressions + + ExprResult ParseBoundsExpression(); + //===--------------------------------------------------------------------===// // clang Expressions diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 87b5c054e20d..609de62ac47c 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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); @@ -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(...) diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index ce6b53f735e3..1dff15233db1 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -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 diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index abaec2d7e10a..211ac716e978 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -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. } @@ -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; @@ -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()) { diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index 9d76f65b62a8..b990a07a49ce 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -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; diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index cc3837409269..479af0db4cfd 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -1735,6 +1735,40 @@ void StmtPrinter::VisitAtomicExpr(AtomicExpr *Node) { OS << ")"; } +// Checked C extension + +void StmtPrinter::VisitCountBoundsExpr(CountBoundsExpr *Node) { + switch (Node->getKind()) { + case CountBoundsExpr::Kind::ByteCount: + OS << "byte_count("; + case CountBoundsExpr::Kind::ElementCount: + OS << "count("; + } + PrintExpr(Node->getCountExpr()); + OS << ")"; +} + +void StmtPrinter::VisitNullaryBoundsExpr(NullaryBoundsExpr *Node) { + switch (Node->getKind()) { + case NullaryBoundsExpr::Invalid: { + OS << "bounds(invalid)"; + break; + } + case NullaryBoundsExpr::None: { + OS << "bounds(none)"; + break; + } + } +} + +void StmtPrinter::VisitRangeBoundsExpr(RangeBoundsExpr *Node) { + OS << "bounds("; + PrintExpr(Node->getLowerExpr()); + OS << ", "; + PrintExpr(Node->getUpperExpr()); + OS << ")"; +} + // C++ void StmtPrinter::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *Node) { const char *OpStrings[NUM_OVERLOADED_OPERATORS] = { diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index 30e094c68ed7..be6d05885a6c 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -921,6 +921,20 @@ void StmtProfiler::VisitPseudoObjectExpr(const PseudoObjectExpr *S) { Visit(OVE->getSourceExpr()); } +void StmtProfiler::VisitCountBoundsExpr(const CountBoundsExpr *S) { + VisitExpr(S); + ID.AddInteger(S->getKind()); +} + +void StmtProfiler::VisitNullaryBoundsExpr(const NullaryBoundsExpr *S) { + VisitExpr(S); + ID.AddInteger(S->getKind()); +} + +void StmtProfiler::VisitRangeBoundsExpr(const RangeBoundsExpr *S) { + VisitExpr(S); +} + void StmtProfiler::VisitAtomicExpr(const AtomicExpr *S) { VisitExpr(S); ID.AddInteger(S->getOp()); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index c96c1b310a06..703818bc72da 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -5942,7 +5942,18 @@ void Parser::ParseParameterDeclarationClause( // Inform the actions module about the parameter declarator, so it gets // added to the current scope. - Decl *Param = Actions.ActOnParamDeclarator(getCurScope(), ParmDeclarator); + ParmVarDecl *Param = Actions.ActOnParamDeclarator(getCurScope(), ParmDeclarator); + + // Parse the bounds expression, if any. + if (getLangOpts().CheckedC && Tok.is(tok::colon)) { + ConsumeToken(); + ExprResult Bounds = ParseBoundsExpression(); + if (Bounds.isInvalid()) { + SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch); + Actions.ActOnInvalidBoundsExpr(Param); + } else Actions.ActOnBoundsExpr(Param, cast(Bounds.get())); + } + // Parse the default argument, if any. We parse the default // arguments in all dialects; the semantic analysis in // ActOnParamDefaultArgument will reject the default argument in diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 12b80ba84f0a..66f89bbd8864 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -2744,6 +2744,82 @@ void Parser::ParseBlockId(SourceLocation CaretLoc) { Actions.ActOnBlockArguments(CaretLoc, DeclaratorInfo, getCurScope()); } +ExprResult Parser::ParseBoundsExpression() { + if (Tok.getKind() != tok::identifier) { + // This can't be a contextual keyword that begins a bounds expression, + // so stop now. + Diag(Tok, diag::err_expected_bounds_expr); + return ExprError(); + } + + IdentifierInfo *Ident = Tok.getIdentifierInfo(); + SourceLocation BoundsKWLoc = Tok.getLocation(); + ConsumeToken(); + + // Parse the body of a bounds expression. Set Result to ExprError() if + // something goes wrong. + BalancedDelimiterTracker PT(*this, tok::l_paren); + if (PT.expectAndConsume(diag::err_expected_lparen_after, Ident->getNameStart())) + return ExprError(); + + ExprResult Result; + if (Ident == Ident_byte_count || Ident == Ident_count) { + // Parse byte_count(e1) or count(e1) + Result = Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()); + CountBoundsExpr::Kind CountKind = Ident == Ident_count ? + CountBoundsExpr::Kind::ElementCount : CountBoundsExpr::Kind::ByteCount; + if (Result.isInvalid()) + Result = ExprError(); + else + Result = Actions.ActOnCountBoundsExpr(BoundsKWLoc, CountKind, + Result.get(), + Tok.getLocation()); + } else if (Ident == Ident_bounds) { + // Parse bounds(none) or bounds(e1, e2) + bool FoundNullaryOperator = false; + + // Start with "none" + if (Tok.getKind() == tok::identifier) { + IdentifierInfo *NextIdent = Tok.getIdentifierInfo(); + if (NextIdent == Ident_none) { + FoundNullaryOperator = true; + ConsumeToken(); + Result = Actions.ActOnNullaryBoundsExpr(BoundsKWLoc, + NullaryBoundsExpr::Kind::None, + Tok.getLocation()); + } + } + + if (!FoundNullaryOperator) { + // Look for e1 "," e2 + ExprResult LowerBound = + Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()); + if (LowerBound.isInvalid()) + Result = ExprError(); + else if (ExpectAndConsume(tok::comma)) + Result = ExprError(); + else { + ExprResult UpperBound = + Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()); + if (UpperBound.isInvalid()) + Result = ExprError(); + else + Result = Actions.ActOnRangeBoundsExpr(BoundsKWLoc, LowerBound.get(), + UpperBound.get(), + Tok.getLocation()); + } + } // if (!FoundNullaryOperator) + } else { + // The identifier is not a valid contextual keyword for the start of a + // a bounds expression. + Diag(Tok, diag::err_expected_bounds_expr); + Result = ExprError(); + } + + PT.consumeClose(); + return Result; +} + /// ParseBlockLiteralExpression - Parse a block literal, which roughly looks /// like ^(int x){ return x+1; } /// diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 9ed2d72fcd9d..332cff1544dc 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -494,6 +494,18 @@ void Parser::Initialize() { Ident_strict = nullptr; Ident_replacement = nullptr; + if (getLangOpts().CheckedC) { + Ident_bounds = &PP.getIdentifierTable().get("bounds"); + Ident_byte_count = &PP.getIdentifierTable().get("byte_count"); + Ident_count = &PP.getIdentifierTable().get("count"); + Ident_none = &PP.getIdentifierTable().get("none"); + } else { + Ident_bounds = nullptr; + Ident_byte_count = nullptr; + Ident_count = nullptr; + Ident_none = nullptr; + } + Ident__except = nullptr; Ident__exception_code = Ident__exception_info = nullptr; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 774af59e4976..1965265130bd 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -10580,7 +10580,7 @@ void Sema::ActOnDocumentableDecls(ArrayRef Group) { /// ActOnParamDeclarator - Called from Parser::ParseFunctionDeclarator() /// to introduce parameters into function prototype scope. -Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D) { +ParmVarDecl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D) { const DeclSpec &DS = D.getDeclSpec(); // Verify C99 6.7.5.3p2: The only SCS allowed is 'register'. @@ -10868,6 +10868,27 @@ void Sema::ActOnFinishKNRParamDeclarations(Scope *S, Declarator &D, } } +/// ActOnBoundsExpr: attach a bounds expression to a parameter declaration. +void Sema::ActOnBoundsExpr(VarDecl *D, BoundsExpr *Expr) { + if (!D || !Expr) + return; + + D->setBoundsExpr(Expr); +} + +void Sema::ActOnInvalidBoundsExpr(VarDecl *D) { + if (!D) + return; + + ExprResult Result = + ActOnNullaryBoundsExpr(SourceLocation(), + NullaryBoundsExpr::Kind::Invalid, + SourceLocation()); + + if (!Result.isInvalid()) + D->setBoundsExpr(cast(Result.get())); +} + Decl * Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Declarator &D, MultiTemplateParamsArg TemplateParameterLists, diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 5347ffc2af8d..2c6514883601 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -11898,6 +11898,31 @@ ExprResult Sema::ActOnChooseExpr(SourceLocation BuiltinLoc, CondIsTrue, resType->isDependentType(), ValueDependent); } +//===----------------------------------------------------------------------===// +// Checked C Extension. +//===----------------------------------------------------------------------===// + +ExprResult Sema::ActOnNullaryBoundsExpr(SourceLocation BoundsKWLoc, + NullaryBoundsExpr::Kind Kind, + SourceLocation RParenLoc) { + return new (Context) NullaryBoundsExpr(Kind, BoundsKWLoc, RParenLoc); +} + +ExprResult Sema::ActOnCountBoundsExpr(SourceLocation BoundsKWLoc, + CountBoundsExpr::Kind Kind, + Expr *CountExpr, + SourceLocation RParenLoc) { + return new (Context) CountBoundsExpr(Kind, CountExpr, BoundsKWLoc, RParenLoc); +} + +ExprResult Sema::ActOnRangeBoundsExpr(SourceLocation BoundsKWLoc, + Expr *LowerBound, + Expr *UpperBound, + SourceLocation RParenLoc) { + return new (Context) RangeBoundsExpr(LowerBound, UpperBound, BoundsKWLoc, + RParenLoc); +} + //===----------------------------------------------------------------------===// // Clang Extensions. //===----------------------------------------------------------------------===// diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 15221c11ce8d..2cff9615ccbd 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -2288,6 +2288,25 @@ class TreeTransform { ControllingExpr, Types, Exprs); } + ExprResult RebuildCountBoundsExpr(SourceLocation StartLoc, + CountBoundsExpr::Kind Kind, + Expr * CountExpr, + SourceLocation RParenLoc) { + return getSema().ActOnCountBoundsExpr(StartLoc, Kind, CountExpr, RParenLoc); + } + + ExprResult RebuildNullaryBoundsExpr(SourceLocation StartLoc, + NullaryBoundsExpr::Kind Kind, + SourceLocation RParenLoc) { + return getSema().ActOnNullaryBoundsExpr(StartLoc, Kind, RParenLoc); + } + + ExprResult RebuildRangeBoundsExpr(SourceLocation StartLoc, + Expr *Lower, Expr *Upper, + SourceLocation RParenLoc) { + return getSema().ActOnRangeBoundsExpr(StartLoc, Lower, Upper, RParenLoc); + } + /// \brief Build a new overloaded operator call expression. /// /// By default, performs semantic analysis to build the new expression. @@ -11277,6 +11296,51 @@ TreeTransform::TransformAtomicExpr(AtomicExpr *E) { RetTy, E->getOp(), E->getRParenLoc()); } +template +ExprResult +TreeTransform::TransformCountBoundsExpr(CountBoundsExpr *E) { + ExprResult CountExpr = getDerived().TransformExpr(E->getCountExpr()); + if (CountExpr.isInvalid()) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && + CountExpr.get() == E->getCountExpr()) + return E; + + return getDerived().RebuildCountBoundsExpr(E->getStartLoc(), + E->getKind(), + CountExpr.get(), + E->getRParenLoc()); +} + +template +ExprResult +TreeTransform::TransformNullaryBoundsExpr(NullaryBoundsExpr *E) { + return E; +} + +template +ExprResult +TreeTransform::TransformRangeBoundsExpr(RangeBoundsExpr *E) { + ExprResult LowerExpr = getDerived().TransformExpr(E->getLowerExpr()); + if (LowerExpr.isInvalid()) + return ExprError(); + + ExprResult UpperExpr = getDerived().TransformExpr(E->getUpperExpr()); + if (UpperExpr.isInvalid()) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && + LowerExpr.get() == E->getLowerExpr() && + UpperExpr.get() == E->getUpperExpr()) + return E; + + return getDerived().RebuildRangeBoundsExpr(E->getStartLoc(), + LowerExpr.get(), + UpperExpr.get(), + E->getRParenLoc()); +} + //===----------------------------------------------------------------------===// // Type reconstruction //===----------------------------------------------------------------------===// diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index 2de0b58fa4a3..a2611adfde83 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -953,6 +953,29 @@ void ASTStmtReader::VisitAtomicExpr(AtomicExpr *E) { E->RParenLoc = ReadSourceLocation(Record, Idx); } +void ASTStmtReader::VisitCountBoundsExpr(CountBoundsExpr *E) { + VisitExpr(E); + E->setKind((CountBoundsExpr::Kind)Record[Idx++]); + E->setCountExpr(Reader.ReadSubExpr()); + E->setStartLoc(ReadSourceLocation(Record, Idx)); + E->setRParenLoc(ReadSourceLocation(Record, Idx)); +} + +void ASTStmtReader::VisitNullaryBoundsExpr(NullaryBoundsExpr *E) { + VisitExpr(E); + E->setKind((NullaryBoundsExpr::Kind)Record[Idx++]); + E->setStartLoc(ReadSourceLocation(Record, Idx)); + E->setRParenLoc(ReadSourceLocation(Record, Idx)); +} + +void ASTStmtReader::VisitRangeBoundsExpr(RangeBoundsExpr *E) { + VisitExpr(E); + E->setLowerExpr(Reader.ReadSubExpr()); + E->setUpperExpr(Reader.ReadSubExpr()); + E->setStartLoc(ReadSourceLocation(Record, Idx)); + E->setRParenLoc(ReadSourceLocation(Record, Idx)); +} + //===----------------------------------------------------------------------===// // Objective-C Expressions and Statements @@ -3432,6 +3455,18 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { case EXPR_ATOMIC: S = new (Context) AtomicExpr(Empty); break; + + case EXPR_COUNT_BOUNDS_EXPR: + S = new (Context) CountBoundsExpr(Empty); + break; + + case EXPR_NULLARY_BOUNDS_EXPR: + S = new (Context) NullaryBoundsExpr(Empty); + break; + + case EXPR_RANGE_BOUNDS_EXPR: + S = new (Context) RangeBoundsExpr(Empty); + break; case EXPR_LAMBDA: { unsigned NumCaptures = Record[ASTStmtReader::NumExprFields]; diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index 39d6361303fa..d8394b5e04ab 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -909,6 +909,32 @@ void ASTStmtWriter::VisitAtomicExpr(AtomicExpr *E) { Code = serialization::EXPR_ATOMIC; } +void ASTStmtWriter::VisitCountBoundsExpr(CountBoundsExpr *E) { + VisitExpr(E); + Record.push_back(E->getKind()); + VisitExpr(E->getCountExpr()); + Record.AddSourceLocation(E->getStartLoc()); + Record.AddSourceLocation(E->getRParenLoc()); + Code = serialization::EXPR_COUNT_BOUNDS_EXPR; +} + +void ASTStmtWriter::VisitNullaryBoundsExpr(NullaryBoundsExpr *E) { + VisitExpr(E); + Record.push_back(E->getKind()); + Record.AddSourceLocation(E->getStartLoc()); + Record.AddSourceLocation(E->getRParenLoc()); + Code = serialization::EXPR_NULLARY_BOUNDS_EXPR; +} + +void ASTStmtWriter::VisitRangeBoundsExpr(RangeBoundsExpr *E) { + VisitExpr(E); + VisitExpr(E->getLowerExpr()); + VisitExpr(E->getUpperExpr()); + Record.AddSourceLocation(E->getStartLoc()); + Record.AddSourceLocation(E->getRParenLoc()); + Code = serialization::EXPR_RANGE_BOUNDS_EXPR; +} + //===----------------------------------------------------------------------===// // Objective-C Expressions and Statements. //===----------------------------------------------------------------------===//