llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-codegen

Author: Chuanqi Xu (ChuanqiXu9)

<details>
<summary>Changes</summary>

AI assisted. Disclaimer: the commit message is fully hand-written. And also I 
made the whole design.

---

A draft for the whole support can be found at 
https://github.com/ChuanqiXu9/llvm-project/commits/Contracts_v2/ . People who 
want to try or take a look earlier can take a look at them. And also you can 
send issues directly there. 

For easier review, I prefer to send them one by one. So that I don't need to 
update every patch every time.

I divided the whole  implementation into the following parts. Note that the 
following is just a draft for you to have a feeling. The parts other than part 
1 is not sent as a PR now. We can change them always if we have other ideas. 

I plan to devide the implementation into the following parts. Note that the 
following is just an idea. It may change during the process.

- Part 1 (The current PR): AST Node structure design, Parser, Serialization and 
basic Sema interface.
- Part 2: Decide how to construct contracts violation handler.
- Part 3: Support Code Generation. Then we can use contracts in an "undefined" 
way more or less. 
- Part 4: Driver part. `-fcontracts` and `-fcontract-mode=`. I don't plan to 
enable contracts with `-std=c++26` for a while as   it is not completed and 
still experiments in implementations. So the users (I prefer to call them 
tester in the early days) who want to try earlier have to use `-fcontracts` and 
`-fcontract-mode=` so that they know it is not stablized.
- Part 5: Support constructors/destructors and "this" access.
- Part 6: More checks: e.g., ODR violation handling, coroutines checks, virtual 
function checks..
- Part 7: libc++'s &lt;contracts&gt; header.

My overall feeling for the implementation experience is, comparing to my 
experience in coroutines and modules, the problems of contracts majorly live in 
the design space. In the implementation space, I feel contracts is more 
straight forward  to me than coroutines or modules. 

And also, I feel implementing contracts will be easier if we have a clean 
design. So that the problems of contracts now is the lack of implementation 
experience and **user experience**. So I prefer to implement this and then more 
users can use this. A lot of C++ users needs compile to implement features to 
use these features.

Also for user experience, I'd like to invite more internal users to use 
contracts as we did for coroutines and modules. We eat our dog foods.

---

For the patch it self, the entry point to review the patch may be 
`clang/include/clang/AST/Decl.h`. The design choice here is to make contracts 
as two linked list for functions, one for precondition contracts and one for 
postcondition contracts. As I think in most cases the number of contracts won't 
be large in practice.

---

Patch is 45.24 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/205739.diff


33 Files Affected:

- (modified) clang/include/clang/AST/ASTNodeTraverser.h (+11) 
- (modified) clang/include/clang/AST/Decl.h (+82) 
- (modified) clang/include/clang/AST/RecursiveASTVisitor.h (+10) 
- (modified) clang/include/clang/AST/Stmt.h (+49) 
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+4) 
- (modified) clang/include/clang/Basic/IdentifierTable.h (+2-1) 
- (modified) clang/include/clang/Basic/LangOptions.def (+5) 
- (modified) clang/include/clang/Basic/LangOptions.h (+6) 
- (modified) clang/include/clang/Basic/StmtNodes.td (+1) 
- (modified) clang/include/clang/Basic/TokenKinds.def (+8) 
- (modified) clang/include/clang/Options/Options.td (+14) 
- (modified) clang/include/clang/Parse/Parser.h (+12) 
- (modified) clang/include/clang/Sema/DeclSpec.h (+27) 
- (modified) clang/include/clang/Sema/Sema.h (+21) 
- (modified) clang/include/clang/Serialization/ASTBitCodes.h (+2) 
- (modified) clang/lib/AST/DeclPrinter.cpp (+14) 
- (modified) clang/lib/AST/StmtPrinter.cpp (+8) 
- (modified) clang/lib/AST/StmtProfile.cpp (+4) 
- (modified) clang/lib/AST/TextNodeDumper.cpp (+3) 
- (modified) clang/lib/Basic/IdentifierTable.cpp (+2) 
- (modified) clang/lib/CodeGen/CGStmt.cpp (+6) 
- (modified) clang/lib/Parse/ParseDecl.cpp (+5) 
- (modified) clang/lib/Parse/ParseDeclCXX.cpp (+103) 
- (modified) clang/lib/Parse/ParseStmt.cpp (+31) 
- (modified) clang/lib/Sema/SemaDecl.cpp (+88) 
- (modified) clang/lib/Sema/TreeTransform.h (+13) 
- (modified) clang/lib/Serialization/ASTReaderDecl.cpp (+37) 
- (modified) clang/lib/Serialization/ASTReaderStmt.cpp (+12) 
- (modified) clang/lib/Serialization/ASTWriterDecl.cpp (+27) 
- (modified) clang/lib/Serialization/ASTWriterStmt.cpp (+9) 
- (added) clang/test/AST/cxx26-contracts-ast-dump.cpp (+31) 
- (added) clang/test/Modules/contracts.cppm (+52) 
- (added) clang/test/Parser/cxx26-contracts.cpp (+32) 


``````````diff
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h 
b/clang/include/clang/AST/ASTNodeTraverser.h
index d184e03355077..20e39aa93bf8d 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -569,6 +569,17 @@ class ASTNodeTraverser
 
     if (D->doesThisDeclarationHaveABody())
       Visit(D->getBody());
+
+    // C++26 Contracts: dump pre/post-condition annotations.
+    for (auto *C = D->getPreConditions(); C; C = C->getNext())
+      getNodeDelegate().AddChild("pre", [=] { Visit(C->getPredicate()); });
+    for (auto *C = D->getPostConditions(); C; C = C->getNext()) {
+      getNodeDelegate().AddChild("post", [=] {
+        if (auto *RV = C->getResultVar())
+          Visit(RV);
+        Visit(C->getPredicate());
+      });
+    }
   }
 
   void VisitFieldDecl(const FieldDecl *D) {
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 620206612f98f..46404ec8300c1 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -2003,6 +2003,74 @@ enum class MultiVersionKind {
   TargetVersion
 };
 
+/// Represents a single C++26 contract annotation (P2900R14).
+///
+/// This is the primary template for pre-conditions (IsPostCondition=false).
+/// Nodes are ASTContext-allocated and form a singly-linked list on
+/// FunctionDecl.
+///
+/// \code
+///   int f(int x) pre(x > 0) pre(x < 100);
+///   //           ^~~~~~~~~~~ ^~~~~~~~~~~~
+///   //           two PreContractAnnotation nodes linked together
+/// \endcode
+template <bool IsPostCondition> class ContractAnnotationBase {
+  Expr *Predicate;
+  ContractAnnotationBase *Next = nullptr;
+  SourceLocation KwLoc;
+  SourceLocation LParenLoc, RParenLoc;
+
+public:
+  ContractAnnotationBase(Expr *Pred, SourceLocation KwLoc, SourceLocation LP,
+                         SourceLocation RP)
+      : Predicate(Pred), KwLoc(KwLoc), LParenLoc(LP), RParenLoc(RP) {}
+
+  Expr *getPredicate() const { return Predicate; }
+  void setPredicate(Expr *P) { Predicate = P; }
+  ContractAnnotationBase *getNext() const { return Next; }
+  void setNext(ContractAnnotationBase *N) { Next = N; }
+  SourceLocation getKeywordLoc() const { return KwLoc; }
+  SourceLocation getLParenLoc() const { return LParenLoc; }
+  SourceLocation getRParenLoc() const { return RParenLoc; }
+};
+
+/// Specialization for post-conditions, which additionally store an optional
+/// result variable for the return value name in \c post(name: expr).
+///
+/// \code
+///   int f(int x) post(r: r >= 0);
+///   //           ^~~~~~~~~~~~~~~~
+///   //           PostContractAnnotation with ResultVar for 'r'
+/// \endcode
+template <> class ContractAnnotationBase<true> {
+  Expr *Predicate;
+  ContractAnnotationBase *Next = nullptr;
+  SourceLocation KwLoc;
+  SourceLocation LParenLoc, RParenLoc;
+  /// The implicit VarDecl for the return value name in post(name: expr).
+  /// nullptr when no result name is specified, e.g. post(expr).
+  VarDecl *ResultVar;
+
+public:
+  ContractAnnotationBase(Expr *Pred, SourceLocation KwLoc, SourceLocation LP,
+                         SourceLocation RP, VarDecl *RV = nullptr)
+      : Predicate(Pred), KwLoc(KwLoc), LParenLoc(LP), RParenLoc(RP),
+        ResultVar(RV) {}
+
+  Expr *getPredicate() const { return Predicate; }
+  void setPredicate(Expr *P) { Predicate = P; }
+  ContractAnnotationBase *getNext() const { return Next; }
+  void setNext(ContractAnnotationBase *N) { Next = N; }
+  VarDecl *getResultVar() const { return ResultVar; }
+  void setResultVar(VarDecl *RV) { ResultVar = RV; }
+  SourceLocation getKeywordLoc() const { return KwLoc; }
+  SourceLocation getLParenLoc() const { return LParenLoc; }
+  SourceLocation getRParenLoc() const { return RParenLoc; }
+};
+
+using PreContractAnnotation = ContractAnnotationBase<false>;
+using PostContractAnnotation = ContractAnnotationBase<true>;
+
 /// Represents a function declaration or definition.
 ///
 /// Since a given function can be declared several times in a program,
@@ -2122,6 +2190,11 @@ class FunctionDecl : public DeclaratorDecl,
   /// the DeclaratorDecl base class.
   DeclarationNameLoc DNLoc;
 
+  /// C++26 contract annotations (P2900R14). Each is a singly-linked list
+  /// of ASTContext-allocated nodes, nullptr when no contracts are present.
+  PreContractAnnotation *PreConditions = nullptr;
+  PostContractAnnotation *PostConditions = nullptr;
+
   /// Specify that this function declaration is actually a function
   /// template specialization.
   ///
@@ -2565,6 +2638,15 @@ class FunctionDecl : public DeclaratorDecl,
 
   void setDeletedAsWritten(bool D = true, StringLiteral *Message = nullptr);
 
+  /// \name C++26 Contracts (P2900R14)
+  /// @{
+  bool hasContracts() const { return PreConditions || PostConditions; }
+  PreContractAnnotation *getPreConditions() const { return PreConditions; }
+  PostContractAnnotation *getPostConditions() const { return PostConditions; }
+  void setPreConditions(PreContractAnnotation *C) { PreConditions = C; }
+  void setPostConditions(PostContractAnnotation *C) { PostConditions = C; }
+  /// @}
+
   /// Determines whether this function is "main", which is the
   /// entry point into an executable program.
   bool isMain() const;
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h 
b/clang/include/clang/AST/RecursiveASTVisitor.h
index febdf715698d9..82cada83f9116 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2387,6 +2387,15 @@ bool 
RecursiveASTVisitor<Derived>::TraverseFunctionHelper(FunctionDecl *D) {
         const_cast<Expr *>(TrailingRequiresClause.ConstraintExpr)));
   }
 
+  // Visit C++26 contract annotations (P2900R14), if any.
+  for (auto *C = D->getPreConditions(); C; C = C->getNext())
+    TRY_TO(TraverseStmt(C->getPredicate()));
+  for (auto *C = D->getPostConditions(); C; C = C->getNext()) {
+    if (auto *RV = C->getResultVar())
+      TRY_TO(TraverseDecl(RV));
+    TRY_TO(TraverseStmt(C->getPredicate()));
+  }
+
   if (CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(D)) {
     // Constructor initializers.
     for (auto *I : Ctor->inits()) {
@@ -2589,6 +2598,7 @@ DEF_TRAVERSE_STMT(DefaultStmt, {})
 DEF_TRAVERSE_STMT(DoStmt, {})
 DEF_TRAVERSE_STMT(ForStmt, {})
 DEF_TRAVERSE_STMT(GotoStmt, {})
+DEF_TRAVERSE_STMT(ContractAssertStmt, {})
 DEF_TRAVERSE_STMT(DeferStmt, {})
 DEF_TRAVERSE_STMT(IfStmt, {})
 DEF_TRAVERSE_STMT(IndirectGotoStmt, {})
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index f07ba9205661b..ac8d8f4a411c1 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -4160,6 +4160,55 @@ class CapturedStmt : public Stmt {
   const_child_range children() const;
 };
 
+/// Represents a C++26 contract_assert statement (P2900R14).
+///
+/// \code
+///   void f(int x) {
+///     contract_assert(x > 0);
+///   }
+/// \endcode
+class ContractAssertStmt : public Stmt {
+  Expr *Condition;
+  SourceLocation ContractAssertLoc;
+  SourceLocation LParenLoc, RParenLoc;
+
+public:
+  ContractAssertStmt(Expr *Condition, SourceLocation ContractAssertLoc,
+                     SourceLocation LParenLoc, SourceLocation RParenLoc)
+      : Stmt(ContractAssertStmtClass), Condition(Condition),
+        ContractAssertLoc(ContractAssertLoc), LParenLoc(LParenLoc),
+        RParenLoc(RParenLoc) {}
+
+  explicit ContractAssertStmt(EmptyShell Empty)
+      : Stmt(ContractAssertStmtClass, Empty) {}
+
+  Expr *getCondition() const { return Condition; }
+  void setCondition(Expr *E) { Condition = E; }
+
+  SourceLocation getContractAssertLoc() const { return ContractAssertLoc; }
+  void setContractAssertLoc(SourceLocation Loc) { ContractAssertLoc = Loc; }
+  SourceLocation getLParenLoc() const { return LParenLoc; }
+  void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; }
+  SourceLocation getRParenLoc() const { return RParenLoc; }
+  void setRParenLoc(SourceLocation Loc) { RParenLoc = Loc; }
+
+  SourceLocation getBeginLoc() const { return ContractAssertLoc; }
+  SourceLocation getEndLoc() const { return RParenLoc; }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == ContractAssertStmtClass;
+  }
+
+  child_range children() {
+    Stmt **Begin = reinterpret_cast<Stmt **>(&Condition);
+    return child_range(Begin, Begin + 1);
+  }
+  const_child_range children() const {
+    Stmt *const *Begin = reinterpret_cast<Stmt *const *>(&Condition);
+    return const_child_range(Begin, Begin + 1);
+  }
+};
+
 } // namespace clang
 
 #endif // LLVM_CLANG_AST_STMT_H
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 077aace321264..8bf8f36d7f410 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1764,6 +1764,10 @@ def note_expr_evaluates_to : Note<
   "expression evaluates to '%0 %1 %2'">;
 
 
+// C++26 Contracts (P2900R14)
+def err_post_condition_result_name_void_return : Error<
+  "post-condition result name on function returning void">;
+
 def subst_user_defined_msg : TextSubstitution<
   "%select{the message|the expression}0 in "
   "%select{a static assertion|this asm operand}0">;
diff --git a/clang/include/clang/Basic/IdentifierTable.h 
b/clang/include/clang/Basic/IdentifierTable.h
index 41420ece94bf4..698e4da1d011a 100644
--- a/clang/include/clang/Basic/IdentifierTable.h
+++ b/clang/include/clang/Basic/IdentifierTable.h
@@ -79,7 +79,8 @@ enum TokenKey : unsigned {
   KEYFIXEDPOINT = 0x10000000,
   KEYDEFERTS = 0x20000000,
   KEYNOHLSL = 0x40000000,
-  KEYMAX = KEYNOHLSL, // The maximum key
+  KEYCONTRACTS = 0x80000000,
+  KEYMAX = KEYCONTRACTS, // The maximum key
   KEYALLCXX = KEYCXX | KEYCXX11 | KEYCXX20,
   KEYALL = (KEYMAX | (KEYMAX - 1)) & ~KEYNOMS18 & ~KEYNOOPENCL & ~KEYNOZOS &
            ~KEYNOHLSL // KEYNOMS18, KEYNOOPENCL, KEYNOZOS, KEYNOHLSL excluded.
diff --git a/clang/include/clang/Basic/LangOptions.def 
b/clang/include/clang/Basic/LangOptions.def
index 4a3e3b7c04822..f64d65fcabef6 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -438,6 +438,11 @@ LANGOPT(PaddingOnUnsignedFixedPoint, 1, 0, NotCompatible,
 
 LANGOPT(OverflowBehaviorTypes, 1, 0, NotCompatible, "overflow behavior types")
 
+LANGOPT(Contracts, 1, 0, Benign, "C++ contracts support")
+ENUM_LANGOPT(ContractViolationMode, ContractViolationModeKind, 2,
+             ContractViolationModeKind::Enforce, Benign,
+             "C++ contract violation handling mode")
+
 ENUM_LANGOPT(RegisterStaticDestructors, RegisterStaticDestructorsKind, 2,
              RegisterStaticDestructorsKind::All, NotCompatible,
              "Register C++ static destructors")
diff --git a/clang/include/clang/Basic/LangOptions.h 
b/clang/include/clang/Basic/LangOptions.h
index 9af036156b1ad..3176c89bd3f18 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -431,6 +431,12 @@ class LangOptionsBase {
     CX_None
   };
 
+  enum class ContractViolationModeKind {
+    Enforce,
+    Observe,
+    Ignore,
+  };
+
   /// Controls which variables have static destructors registered.
   enum class RegisterStaticDestructorsKind {
     /// Register static destructors for all variables.
diff --git a/clang/include/clang/Basic/StmtNodes.td 
b/clang/include/clang/Basic/StmtNodes.td
index e166894ea024b..cbe628c267972 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -19,6 +19,7 @@ def IndirectGotoStmt : StmtNode<Stmt>;
 def ReturnStmt : StmtNode<Stmt>;
 def DeferStmt : StmtNode<Stmt>;
 def DeclStmt  : StmtNode<Stmt>;
+def ContractAssertStmt : StmtNode<Stmt>;
 def SwitchCase : StmtNode<Stmt, 1>;
 def CaseStmt : StmtNode<SwitchCase>;
 def DefaultStmt : StmtNode<SwitchCase>;
diff --git a/clang/include/clang/Basic/TokenKinds.def 
b/clang/include/clang/Basic/TokenKinds.def
index f07d8ebb75035..be278ecd52e56 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -34,6 +34,9 @@
 #ifndef C23_KEYWORD
 #define C23_KEYWORD(X,Y) KEYWORD(X,KEYC23|(Y))
 #endif
+#ifndef CONTRACTS_KEYWORD
+#define CONTRACTS_KEYWORD(X) KEYWORD(X,KEYCONTRACTS)
+#endif
 #ifndef COROUTINES_KEYWORD
 #define COROUTINES_KEYWORD(X) CXX20_KEYWORD(X,KEYCOROUTINES)
 #endif
@@ -293,6 +296,7 @@ PUNCTUATOR(greatergreatergreater, ">>>")
 //   KEYZVECTOR - This is a keyword for the System z vector extensions,
 //                which are heavily based on AltiVec
 //   KEYBORLAND - This is a keyword if Borland extensions are enabled
+//   KEYCONTRACTS - This is a keyword if support for C++ contracts is enabled
 //   KEYCOROUTINES - This is a keyword if support for C++ coroutines is enabled
 //   BOOLSUPPORT - This is a keyword if 'bool' is a built-in type
 //   HALFSUPPORT - This is a keyword if 'half' is a built-in type
@@ -428,6 +432,9 @@ CXX11_KEYWORD(nullptr               , KEYC23)
 CXX11_KEYWORD(static_assert         , KEYMSCOMPAT|KEYC23)
 CXX11_KEYWORD(thread_local          , KEYC23)
 
+// C++26 / contracts keywords
+CONTRACTS_KEYWORD(contract_assert)
+
 // C++20 / coroutines keywords
 COROUTINES_KEYWORD(co_await)
 COROUTINES_KEYWORD(co_return)
@@ -1075,6 +1082,7 @@ ANNOTATION(embed)
 #undef TYPE_TRAIT_2
 #undef TYPE_TRAIT_1
 #undef TYPE_TRAIT
+#undef CONTRACTS_KEYWORD
 #undef MODULES_KEYWORD
 #undef CXX20_KEYWORD
 #undef CXX11_KEYWORD
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 8451a3698ef17..fe010832c9c89 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -1716,6 +1716,20 @@ defm defer_ts : BoolFOption<"defer-ts",
   NegFlag<SetFalse>>,
   ShouldParseIf<!strconcat("!", cplusplus.KeyPath)>;
 
+// C++ Contracts
+defm contracts : BoolFOption<"contracts",
+  LangOpts<"Contracts">, DefaultFalse,
+  PosFlag<SetTrue, [], [ClangOption, CC1Option],
+          "Enable support for C++ Contracts">,
+  NegFlag<SetFalse>>;
+def fcontract_mode_EQ : Joined<["-"], "fcontract-mode=">, Group<f_Group>,
+  HelpText<"Set behavior when a C++ contract is violated">,
+  Values<"enforce,observe,ignore">,
+  NormalizedValues<["Enforce", "Observe", "Ignore"]>,
+  NormalizedValuesScope<"LangOptions::ContractViolationModeKind">,
+  MarshallingInfoEnum<LangOpts<"ContractViolationMode">, "Enforce">,
+  Visibility<[ClangOption, CC1Option]>;
+
 // C++ Coroutines
 defm coroutines : BoolFOption<"coroutines",
   LangOpts<"Coroutines">, Default<cpp20.KeyPath>,
diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index c6c492b4980af..fd5391d68655b 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2895,6 +2895,11 @@ class Parser : public CodeCompletionHandler {
   mutable IdentifierInfo *Ident_GNU_final;
   mutable IdentifierInfo *Ident_override;
 
+  /// C++26 Contracts contextual keywords (pre/post are context-sensitive;
+  /// contract_assert is a proper keyword via TokenKinds.def).
+  mutable IdentifierInfo *Ident_pre;
+  mutable IdentifierInfo *Ident_post;
+
   /// Representation of a class that has been parsed, including
   /// any member function declarations or definitions that need to be
   /// parsed after the corresponding top-level class is complete.
@@ -3003,6 +3008,11 @@ class Parser : public CodeCompletionHandler {
   void ParseTrailingRequiresClauseWithScope(Declarator &D);
   void ParseTrailingRequiresClause(Declarator &D);
 
+  /// Parse C++26 contract specifiers: pre(expr) and post(name: expr).
+  /// \param TrailingReturnType The trailing return type if present, used to
+  ///        determine the type of the result variable in post(name: expr).
+  void ParseContractSpecifiers(Declarator &D, ParsedType TrailingReturnType);
+
   void ParseMicrosoftIfExistsClassDeclaration(DeclSpec::TST TagType,
                                               ParsedAttributes &AccessAttrs,
                                               AccessSpecifier &CurAS);
@@ -7589,6 +7599,8 @@ class Parser : public CodeCompletionHandler {
   ///         'co_return' braced-init-list ';'
   /// \endverbatim
   StmtResult ParseReturnStatement();
+  /// Parse a C++26 contract_assert(expr) statement.
+  StmtResult ParseContractAssertStatement();
 
   StmtResult ParseBreakOrContinueStatement(bool IsContinue);
 
diff --git a/clang/include/clang/Sema/DeclSpec.h 
b/clang/include/clang/Sema/DeclSpec.h
index b3c459821c79c..0ce636e43307e 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -2050,6 +2050,24 @@ class Declarator {
 
   friend struct DeclaratorChunk;
 
+public:
+  /// Parsed contract specifier from a function declarator (pre/post).
+  /// Temporary storage during parsing; converted to ContractAnnotation
+  /// nodes on FunctionDecl by Sema::ActOnFunctionContractSpecifiers.
+  struct ContractSpecInfo {
+    enum Kind { Pre, Post };
+    Kind CKind;
+    Expr *Predicate;
+    /// For post(name: expr), the identifier for the result name.
+    IdentifierInfo *ResultName = nullptr;
+    /// The implicit VarDecl created for the result name during parsing.
+    VarDecl *ResultVar = nullptr;
+    SourceLocation KwLoc, LParenLoc, RParenLoc, ResultNameLoc;
+  };
+
+private:
+  SmallVector<ContractSpecInfo, 2> ContractSpecifiers;
+
 public:
   /// `DS` and `DeclarationAttrs` must outlive the `Declarator`. In particular,
   /// take care not to pass temporary objects for these parameters.
@@ -2173,6 +2191,7 @@ class Declarator {
     CommaLoc = SourceLocation();
     EllipsisLoc = SourceLocation();
     PackIndexingExpr = nullptr;
+    ContractSpecifiers.clear();
   }
 
   /// mayOmitIdentifier - Return true if the identifier is either optional or
@@ -2688,6 +2707,14 @@ class Declarator {
     return TrailingRequiresClause != nullptr;
   }
 
+  void addContractSpecifier(ContractSpecInfo &&Info) {
+    ContractSpecifiers.push_back(std::move(Info));
+  }
+  ArrayRef<ContractSpecInfo> getContractSpecifiers() const {
+    return ContractSpecifiers;
+  }
+  bool hasContractSpecifiers() const { return !ContractSpecifiers.empty(); }
+
   /// Sets the template parameter lists that preceded the declarator.
   void setTemplateParameterLists(ArrayRef<TemplateParameterList *> TPLs) {
     TemplateParameterLists = TPLs;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index b8d760e7e0975..d4866b193e07b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6060,6 +6060,27 @@ class Sema final : public SemaBase {
                         StringEvaluationContext EvalContext,
                         bool ErrorOnInvalidMessage);
 
+  /// \name C++26 Contracts (P2900R14)
+  /// @{
+
+  /// Build contract annotation nodes from parsed specifiers and attach them
+  /// to the FunctionDecl.
+  void ActOnFunctionContractSpecifiers(FunctionDecl *FD, const Declarator &D);
+
+  /// Create an implicit VarDecl for the result name in post(name: expr) and
+  /// push it into the current scope so the predicate expression can reference
+  /// it.
+  VarDecl *ActOnPostConditionResultName(Scope *S, Declarator &D,
+                                        IdentifierInfo *ResultName,
+                                        SourceLocation ResultNameLoc,
+                                        ParsedType TrailingReturnType);
+
+  /// Build a ContractAssertStmt from a parsed contract_assert(expr) statement.
+  StmtResult ActOnContractAssert(SourceLocation ContractAssertLoc,
+                                 Expr *Predicate, SourceLocation LParenLoc,
+                                 SourceLocation RParenLoc);
+  /// @}
+
   Decl *ActOnStaticAssertDeclaration(SourceLocation StaticAssertLoc,
                                      Expr *AssertExpr, Expr *AssertMessageExpr,
                                      SourceLocation RParenLoc);
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h 
b/clang/include/clang/Serialization/ASTBitCodes.h
index 412f87e9da43e..026caff5ce510 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -2085,6 +2085,8 @@ enum StmtCode {
   EXPR_HLSL_OUT_ARG,
 
   STMT_DEFER,
+
+  STMT_CONTRACT_ASSERT,
 };
 
 /// The kinds of designators that can occur in a
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/De...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/205739
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to