https://github.com/ChuanqiXu9 created 
https://github.com/llvm/llvm-project/pull/205739

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 <contracts> 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.

>From 77fe54a388d1e8e86ee449ccefad86e7151db2a4 Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <[email protected]>
Date: Thu, 25 Jun 2026 14:07:22 +0800
Subject: [PATCH] [C++26] [Contracts] Support Parser, AST and AST Serialization
 (1/n)

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

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/DeclPrinter.cpp
index 95591d3a09431..c2f0fada7f15b 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -849,6 +849,20 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
           prettyPrintAttributes(D, AttrPosAsWritten::Right))
     Out << ' ' << *Attrs;
 
+  // C++26 Contracts: print pre/post-condition specifiers.
+  for (auto *C = D->getPreConditions(); C; C = C->getNext()) {
+    Out << " pre(";
+    C->getPredicate()->printPretty(Out, nullptr, Policy);
+    Out << ")";
+  }
+  for (auto *C = D->getPostConditions(); C; C = C->getNext()) {
+    Out << " post(";
+    if (auto *RV = C->getResultVar())
+      Out << RV->getName() << ": ";
+    C->getPredicate()->printPretty(Out, nullptr, Policy);
+    Out << ")";
+  }
+
   if (D->isPureVirtual())
     Out << " = 0";
   else if (D->isDeletedAsWritten()) {
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 6c3294573e9d4..f94bd95295c1d 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -496,6 +496,14 @@ void StmtPrinter::VisitDeferStmt(DeferStmt *Node) {
   PrintControlledStmt(Node->getBody());
 }
 
+void StmtPrinter::VisitContractAssertStmt(ContractAssertStmt *Node) {
+  Indent() << "contract_assert(";
+  PrintExpr(Node->getCondition());
+  OS << ");";
+  if (Policy.IncludeNewlines)
+    OS << NL;
+}
+
 void StmtPrinter::VisitReturnStmt(ReturnStmt *Node) {
   Indent() << "return";
   if (Node->getRetValue()) {
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index eb25e5260fd1a..c2bbf744e62c0 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -319,6 +319,10 @@ void StmtProfiler::VisitBreakStmt(const BreakStmt *S) {
   VisitStmt(S);
 }
 
+void StmtProfiler::VisitContractAssertStmt(const ContractAssertStmt *S) {
+  VisitStmt(S);
+}
+
 void StmtProfiler::VisitReturnStmt(const ReturnStmt *S) {
   VisitStmt(S);
 }
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 2b1c0cac25b6d..fa1754b1cebc1 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -2461,6 +2461,9 @@ void TextNodeDumper::VisitFunctionDecl(const FunctionDecl 
*D) {
   if (!isa<CXXDeductionGuideDecl>(D) && !D->getDescribedTemplate()) {
     dumpFormalLinkage(D);
   }
+
+  if (D->hasContracts())
+    OS << " contracts";
 }
 
 void TextNodeDumper::VisitCXXDeductionGuideDecl(
diff --git a/clang/lib/Basic/IdentifierTable.cpp 
b/clang/lib/Basic/IdentifierTable.cpp
index a2e9316e4e372..77bc3e3d6781c 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -137,6 +137,8 @@ static KeywordStatus getKeywordStatusHelper(const 
LangOptions &LangOpts,
     return LangOpts.ObjC ? KS_Enabled : KS_Unknown;
   case KEYZVECTOR:
     return LangOpts.ZVector ? KS_Enabled : KS_Unknown;
+  case KEYCONTRACTS:
+    return LangOpts.Contracts ? KS_Enabled : KS_Unknown;
   case KEYCOROUTINES:
     return LangOpts.Coroutines ? KS_Enabled : KS_Unknown;
   case KEYMODULES:
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 30756180ebafa..1706309dad2b4 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -160,6 +160,12 @@ void CodeGenFunction::EmitStmt(const Stmt *S, 
ArrayRef<const Attr *> Attrs) {
   case Stmt::ForStmtClass:     EmitForStmt(cast<ForStmt>(*S), Attrs);     
break;
 
   case Stmt::ReturnStmtClass:  EmitReturnStmt(cast<ReturnStmt>(*S));      
break;
+  case Stmt::ContractAssertStmtClass:
+    // TODO: implement full contract checking in a following CodeGen patch.
+    // For now, emit the predicate expression for its side effects so that
+    // code like contract_assert(expensive_check()) still calls the function.
+    EmitIgnoredExpr(cast<ContractAssertStmt>(*S).getCondition());
+    break;
 
   case Stmt::SwitchStmtClass:  EmitSwitchStmt(cast<SwitchStmt>(*S));      
break;
   case Stmt::GCCAsmStmtClass:  // Intentional fall-through.
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 405dddf7991b4..9599c6629c2b4 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -7456,6 +7456,11 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
     }
   }
 
+  // Parse C++26 contract specifiers (pre/post).
+  ParseContractSpecifiers(D, TrailingReturnType.isUsable()
+                                 ? TrailingReturnType.get()
+                                 : ParsedType());
+
   // Collect non-parameter declarations from the prototype if this is a 
function
   // declaration. They will be moved into the scope of the function. Only do
   // this in C and not C++, where the decls will continue to live in the
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 893989bd2398f..c1abb8b669745 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4188,6 +4188,109 @@ void Parser::ParseTrailingRequiresClause(Declarator &D) 
{
   }
 }
 
+/// Parse a sequence of C++26 contract specifiers (P2900R14).
+///
+/// Called from ParseFunctionDeclarator after the trailing return type,
+/// while still inside the function prototype scope (parameters are visible).
+///
+///   function-contract-specifier-seq:
+///     function-contract-specifier
+///     function-contract-specifier-seq function-contract-specifier
+///
+///   function-contract-specifier:
+///     'pre' '(' conditional-expression ')'
+///     'post' '(' result-name-introducer[opt] conditional-expression ')'
+///
+///   result-name-introducer:
+///     identifier ':'
+///
+/// 'pre' and 'post' are context-sensitive keywords (identifiers that are
+/// only special in this position). Parsed specifiers are stored in the
+/// Declarator and later converted to ContractAnnotation nodes on
+/// FunctionDecl by Sema::ActOnFunctionContractSpecifiers.
+///
+/// \param TrailingReturnType The trailing return type (if any), needed to
+///        determine the type of the result variable in post(name: expr).
+///        For non-trailing return types (e.g. `int f()`), pass ParsedType().
+void Parser::ParseContractSpecifiers(Declarator &D,
+                                     ParsedType TrailingReturnType) {
+  if (!getLangOpts().Contracts)
+    return;
+
+  // Lazily initialize context-sensitive keyword identifiers.
+  if (!Ident_pre) {
+    Ident_pre = &PP.getIdentifierTable().get("pre");
+    Ident_post = &PP.getIdentifierTable().get("post");
+  }
+
+  // Quick check: bail out early if the next token isn't 'pre' or 'post'
+  // followed by '(' to avoid unnecessary work for the common case.
+  if (!Tok.is(tok::identifier))
+    return;
+  IdentifierInfo *II = Tok.getIdentifierInfo();
+  if (II != Ident_pre && II != Ident_post)
+    return;
+  if (!NextToken().is(tok::l_paren))
+    return;
+
+  // Parse a sequence of contract specifiers.
+  while (Tok.is(tok::identifier)) {
+    II = Tok.getIdentifierInfo();
+    if (II != Ident_pre && II != Ident_post)
+      break;
+    if (!NextToken().is(tok::l_paren))
+      break;
+
+    bool IsPre = (II == Ident_pre);
+    SourceLocation KwLoc = ConsumeToken();
+
+    BalancedDelimiterTracker T(*this, tok::l_paren);
+    T.consumeOpen();
+
+    // For post-conditions, check for 'identifier :' result-name-introducer.
+    IdentifierInfo *ResultName = nullptr;
+    SourceLocation ResultNameLoc;
+    VarDecl *ResultVar = nullptr;
+
+    // Each post-condition's result name gets its own scope so that
+    // multiple post(r: ...) on the same function don't conflict.
+    std::optional<ParseScope> ResultNameScope;
+    if (!IsPre && Tok.is(tok::identifier) && NextToken().is(tok::colon)) {
+      ResultNameScope.emplace(this, Scope::DeclScope);
+      ResultName = Tok.getIdentifierInfo();
+      ResultNameLoc = ConsumeToken();
+      ConsumeToken(); // consume ':'
+      // Create an implicit VarDecl for the result name and push it into scope
+      // so the predicate expression can reference it.
+      ResultVar = Actions.ActOnPostConditionResultName(
+          getCurScope(), D, ResultName, ResultNameLoc, TrailingReturnType);
+    }
+
+    ExprResult Predicate = ParseConditionalExpression();
+
+    // Close the result name scope before consuming ')' so that the
+    // result name is not visible outside this contract specifier.
+    ResultNameScope.reset();
+    T.consumeClose();
+
+    if (Predicate.isInvalid())
+      continue;
+
+    // Store the parsed contract specifier in the Declarator.
+    Declarator::ContractSpecInfo Info;
+    Info.CKind = IsPre ? Declarator::ContractSpecInfo::Pre
+                       : Declarator::ContractSpecInfo::Post;
+    Info.Predicate = Predicate.get();
+    Info.ResultName = ResultName;
+    Info.ResultVar = ResultVar;
+    Info.KwLoc = KwLoc;
+    Info.LParenLoc = T.getOpenLocation();
+    Info.RParenLoc = T.getCloseLocation();
+    Info.ResultNameLoc = ResultNameLoc;
+    D.addContractSpecifier(std::move(Info));
+  }
+}
+
 Sema::ParsingClassState Parser::PushParsingClass(Decl *ClassDecl,
                                                  bool NonNestedClass,
                                                  bool IsInterface) {
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 37f142e059930..fea6f4622b161 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -158,6 +158,12 @@ StmtResult 
Parser::ParseStatementOrDeclarationAfterAttributes(
         getCurScope(), SemaCodeCompletion::PCC_Statement);
     return StmtError();
 
+  // C++26 contract_assert statement (P2900R14).
+  case tok::kw_contract_assert:
+    Res = ParseContractAssertStatement();
+    SemiError = "contract_assert";
+    break;
+
   case tok::identifier:
   ParseIdentifier: {
     Token Next = NextToken();
@@ -2325,6 +2331,31 @@ StmtResult Parser::ParseBreakStatement() {
   return ParseBreakOrContinueStatement(/*IsContinue=*/false);
 }
 
+/// Parse a C++26 contract_assert statement (P2900R14).
+///
+///   contract_assert '(' conditional-expression ')' ';'
+///
+/// 'contract_assert' is a context-sensitive keyword (an identifier that is
+/// only special at statement level). The caller has already verified that
+/// the current token is the 'contract_assert' identifier followed by '('.
+StmtResult Parser::ParseContractAssertStatement() {
+  SourceLocation ContractAssertLoc =
+      ConsumeToken(); // consume 'contract_assert'
+
+  BalancedDelimiterTracker T(*this, tok::l_paren);
+  T.consumeOpen();
+
+  ExprResult Predicate = ParseConditionalExpression();
+
+  T.consumeClose();
+
+  if (Predicate.isInvalid())
+    return StmtError();
+
+  return Actions.ActOnContractAssert(ContractAssertLoc, Predicate.get(),
+                                     T.getOpenLocation(), 
T.getCloseLocation());
+}
+
 StmtResult Parser::ParseReturnStatement() {
   assert((Tok.is(tok::kw_return) || Tok.is(tok::kw_co_return)) &&
          "Not a return stmt!");
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index cddcf3a010279..f6b2b9bf8368b 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -11291,6 +11291,9 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, 
DeclContext *DC,
   if (getLangOpts().OpenACC)
     OpenACC().ActOnFunctionDeclarator(NewFD);
 
+  if (D.hasContractSpecifiers())
+    ActOnFunctionContractSpecifiers(NewFD, D);
+
   return NewFD;
 }
 
@@ -21330,3 +21333,88 @@ bool Sema::isRedefinitionAllowedFor(NamedDecl *D, 
NamedDecl **Suggested,
   // D is defined in the global module of other module units.
   return D->isInAnotherModuleUnit() || !Visible;
 }
+
+/// Create an implicit VarDecl for the result name in post(name: expr).
+///
+/// Called during parsing of a post-condition specifier, before the predicate
+/// expression is parsed. The VarDecl is pushed into the current scope so
+/// that the predicate can reference it (e.g., `r` in `post(r: r >= 0)`).
+///
+/// The result variable has the function's return type, const-qualified and
+/// with references stripped. This is called from within
+/// ParseFunctionDeclarator, before the function chunk is added to the
+/// Declarator, so we determine the return type from either the trailing return
+/// type or the DeclSpec base type.
+VarDecl *Sema::ActOnPostConditionResultName(Scope *S, Declarator &D,
+                                            IdentifierInfo *ResultName,
+                                            SourceLocation ResultNameLoc,
+                                            ParsedType TrailingReturnType) {
+  QualType RetTy;
+  if (!TrailingReturnType.get().isNull()) {
+    RetTy = GetTypeFromParser(TrailingReturnType);
+  } else {
+    // The function chunk hasn't been added to the Declarator yet, so
+    // GetTypeForDeclarator returns the DeclSpec base type (the return type).
+    TypeSourceInfo *TSI = GetTypeForDeclarator(D);
+    RetTy = TSI->getType();
+  }
+  assert(!RetTy.isNull() && "return type should never be null here");
+
+  if (RetTy->isVoidType()) {
+    Diag(ResultNameLoc, diag::err_post_condition_result_name_void_return);
+    return nullptr;
+  }
+
+  RetTy = RetTy.getNonReferenceType().withConst();
+
+  auto *ResultVar = VarDecl::Create(
+      Context, CurContext, ResultNameLoc, ResultNameLoc, ResultName, RetTy,
+      Context.getTrivialTypeSourceInfo(RetTy, ResultNameLoc), SC_None);
+  ResultVar->setImplicit();
+  // AddToContext=false: the VarDecl is only visible during parsing of the
+  // predicate expression, not added to any DeclContext permanently.
+  PushOnScopeChains(ResultVar, S, /*AddToContext=*/false);
+  return ResultVar;
+}
+
+/// Convert parsed contract specifiers from the Declarator into
+/// ContractAnnotation linked lists and attach them to the FunctionDecl.
+///
+/// Called from ActOnFunctionDeclarator after the FunctionDecl is created.
+/// Builds two singly-linked lists (pre-conditions and post-conditions)
+/// in declaration order.
+void Sema::ActOnFunctionContractSpecifiers(FunctionDecl *FD,
+                                           const Declarator &D) {
+  PreContractAnnotation *PreHead = nullptr, *PreTail = nullptr;
+  PostContractAnnotation *PostHead = nullptr, *PostTail = nullptr;
+
+  for (const auto &CS : D.getContractSpecifiers()) {
+    if (CS.CKind == Declarator::ContractSpecInfo::Pre) {
+      auto *Ann = new (Context) PreContractAnnotation(
+          CS.Predicate, CS.KwLoc, CS.LParenLoc, CS.RParenLoc);
+      if (PreTail)
+        PreTail->setNext(Ann);
+      else
+        PreHead = Ann;
+      PreTail = Ann;
+    } else {
+      auto *Ann = new (Context) PostContractAnnotation(
+          CS.Predicate, CS.KwLoc, CS.LParenLoc, CS.RParenLoc, CS.ResultVar);
+      if (PostTail)
+        PostTail->setNext(Ann);
+      else
+        PostHead = Ann;
+      PostTail = Ann;
+    }
+  }
+
+  FD->setPreConditions(PreHead);
+  FD->setPostConditions(PostHead);
+}
+
+StmtResult Sema::ActOnContractAssert(SourceLocation ContractAssertLoc,
+                                     Expr *Predicate, SourceLocation LParenLoc,
+                                     SourceLocation RParenLoc) {
+  return new (Context)
+      ContractAssertStmt(Predicate, ContractAssertLoc, LParenLoc, RParenLoc);
+}
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 77c8f17439a1a..3510708184317 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -8640,6 +8640,19 @@ TreeTransform<Derived>::TransformBreakStmt(BreakStmt *S) 
{
       BreakStmt(S->getKwLoc(), S->getLabelLoc(), cast<LabelDecl>(LD));
 }
 
+template <typename Derived>
+StmtResult
+TreeTransform<Derived>::TransformContractAssertStmt(ContractAssertStmt *S) {
+  ExprResult Cond = getDerived().TransformExpr(S->getCondition());
+  if (Cond.isInvalid())
+    return StmtError();
+  if (!getDerived().AlwaysRebuild() && Cond.get() == S->getCondition())
+    return S;
+  return new (getSema().Context)
+      ContractAssertStmt(Cond.get(), S->getContractAssertLoc(),
+                         S->getLParenLoc(), S->getRParenLoc());
+}
+
 template <typename Derived>
 StmtResult TreeTransform<Derived>::TransformDeferStmt(DeferStmt *S) {
   StmtResult Result = getDerived().TransformStmt(S->getBody());
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp 
b/clang/lib/Serialization/ASTReaderDecl.cpp
index fb291a4b0f2c5..3c175ef535206 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -1143,6 +1143,43 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
     Params.push_back(readDeclAs<ParmVarDecl>());
   FD->setParams(Reader.getContext(), Params);
 
+  // C++26 Contracts (P2900R14)
+  {
+    ASTContext &C = Reader.getContext();
+    PreContractAnnotation *PreHead = nullptr, *PreTail = nullptr;
+    unsigned NumPre = Record.readInt();
+    for (unsigned I = 0; I != NumPre; ++I) {
+      Expr *Pred = Record.readExpr();
+      SourceLocation KwLoc = readSourceLocation();
+      SourceLocation LP = readSourceLocation();
+      SourceLocation RP = readSourceLocation();
+      auto *Ann = new (C) PreContractAnnotation(Pred, KwLoc, LP, RP);
+      if (PreTail)
+        PreTail->setNext(Ann);
+      else
+        PreHead = Ann;
+      PreTail = Ann;
+    }
+    FD->setPreConditions(PreHead);
+
+    PostContractAnnotation *PostHead = nullptr, *PostTail = nullptr;
+    unsigned NumPost = Record.readInt();
+    for (unsigned I = 0; I != NumPost; ++I) {
+      Expr *Pred = Record.readExpr();
+      SourceLocation KwLoc = readSourceLocation();
+      SourceLocation LP = readSourceLocation();
+      SourceLocation RP = readSourceLocation();
+      VarDecl *RV = readDeclAs<VarDecl>();
+      auto *Ann = new (C) PostContractAnnotation(Pred, KwLoc, LP, RP, RV);
+      if (PostTail)
+        PostTail->setNext(Ann);
+      else
+        PostHead = Ann;
+      PostTail = Ann;
+    }
+    FD->setPostConditions(PostHead);
+  }
+
   // If the declaration is a SYCL kernel entry point function as indicated by
   // the presence of a sycl_kernel_entry_point attribute, register it so that
   // associated metadata is recreated.
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp 
b/clang/lib/Serialization/ASTReaderStmt.cpp
index 02ccf8d4d41c2..71eb8fd1baf53 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -339,6 +339,14 @@ void ASTStmtReader::VisitContinueStmt(ContinueStmt *S) {
 
 void ASTStmtReader::VisitBreakStmt(BreakStmt *S) { VisitLoopControlStmt(S); }
 
+void ASTStmtReader::VisitContractAssertStmt(ContractAssertStmt *S) {
+  VisitStmt(S);
+  S->setCondition(Record.readSubExpr());
+  S->setContractAssertLoc(readSourceLocation());
+  S->setLParenLoc(readSourceLocation());
+  S->setRParenLoc(readSourceLocation());
+}
+
 void ASTStmtReader::VisitDeferStmt(DeferStmt *S) {
   VisitStmt(S);
   S->setDeferLoc(readSourceLocation());
@@ -3203,6 +3211,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       S = new (Context) BreakStmt(Empty);
       break;
 
+    case STMT_CONTRACT_ASSERT:
+      S = new (Context) ContractAssertStmt(Empty);
+      break;
+
     case STMT_DEFER:
       S = DeferStmt::CreateEmpty(Context, Empty);
       break;
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp 
b/clang/lib/Serialization/ASTWriterDecl.cpp
index 7f5005aa666c7..f6af5a676aa74 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -926,6 +926,33 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
   Record.push_back(D->param_size());
   for (auto *P : D->parameters())
     Record.AddDeclRef(P);
+
+  // C++26 Contracts (P2900R14)
+  {
+    unsigned NumPre = 0;
+    for (auto *C = D->getPreConditions(); C; C = C->getNext())
+      ++NumPre;
+    Record.push_back(NumPre);
+    for (auto *C = D->getPreConditions(); C; C = C->getNext()) {
+      Record.AddStmt(C->getPredicate());
+      Record.AddSourceLocation(C->getKeywordLoc());
+      Record.AddSourceLocation(C->getLParenLoc());
+      Record.AddSourceLocation(C->getRParenLoc());
+    }
+
+    unsigned NumPost = 0;
+    for (auto *C = D->getPostConditions(); C; C = C->getNext())
+      ++NumPost;
+    Record.push_back(NumPost);
+    for (auto *C = D->getPostConditions(); C; C = C->getNext()) {
+      Record.AddStmt(C->getPredicate());
+      Record.AddSourceLocation(C->getKeywordLoc());
+      Record.AddSourceLocation(C->getLParenLoc());
+      Record.AddSourceLocation(C->getRParenLoc());
+      Record.AddDeclRef(C->getResultVar());
+    }
+  }
+
   Code = serialization::DECL_FUNCTION;
 }
 
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp 
b/clang/lib/Serialization/ASTWriterStmt.cpp
index 4e9eadd730a56..04b7f92b9c583 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -331,6 +331,15 @@ void ASTStmtWriter::VisitBreakStmt(BreakStmt *S) {
   Code = serialization::STMT_BREAK;
 }
 
+void ASTStmtWriter::VisitContractAssertStmt(ContractAssertStmt *S) {
+  VisitStmt(S);
+  Record.AddStmt(S->getCondition());
+  Record.AddSourceLocation(S->getContractAssertLoc());
+  Record.AddSourceLocation(S->getLParenLoc());
+  Record.AddSourceLocation(S->getRParenLoc());
+  Code = serialization::STMT_CONTRACT_ASSERT;
+}
+
 void ASTStmtWriter::VisitDeferStmt(DeferStmt *S) {
   VisitStmt(S);
   Record.AddSourceLocation(S->getDeferLoc());
diff --git a/clang/test/AST/cxx26-contracts-ast-dump.cpp 
b/clang/test/AST/cxx26-contracts-ast-dump.cpp
new file mode 100644
index 0000000000000..6af708d1a4e3f
--- /dev/null
+++ b/clang/test/AST/cxx26-contracts-ast-dump.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -std=c++2c -fcontracts -ast-dump %s | FileCheck %s
+
+int divide(int a, int b) pre(b != 0);
+// CHECK: FunctionDecl {{.*}} divide 'int (int, int)' {{.*}}contracts
+// CHECK:   pre:
+// CHECK:     BinaryOperator {{.*}} 'bool' '!='
+
+int square(int x) post(r: r >= 0);
+// CHECK: FunctionDecl {{.*}} square 'int (int)' {{.*}}contracts
+// CHECK:   post:
+// CHECK:     VarDecl {{.*}} implicit used r 'const int'
+// CHECK:     BinaryOperator {{.*}} 'bool' '>='
+// CHECK:       DeclRefExpr {{.*}} 'const int' lvalue Var {{.*}} 'r' 'const 
int'
+
+int abs_val(int x) pre(x >= 0) pre(x < 1000) post(r: r >= 0);
+// CHECK: FunctionDecl {{.*}} abs_val 'int (int)' {{.*}}contracts
+// CHECK:   pre:
+// CHECK:     BinaryOperator {{.*}} 'bool' '>='
+// CHECK:   pre:
+// CHECK:     BinaryOperator {{.*}} 'bool' '<'
+// CHECK:   post:
+// CHECK:     VarDecl {{.*}} implicit used r 'const int'
+// CHECK:     BinaryOperator {{.*}} 'bool' '>='
+
+void f(int x) {
+  contract_assert(x > 0);
+}
+// CHECK: FunctionDecl {{.*}} f 'void (int)'
+// CHECK:   CompoundStmt
+// CHECK:     ContractAssertStmt
+// CHECK:       BinaryOperator {{.*}} 'bool' '>'
diff --git a/clang/test/Modules/contracts.cppm 
b/clang/test/Modules/contracts.cppm
new file mode 100644
index 0000000000000..772200da81a3d
--- /dev/null
+++ b/clang/test/Modules/contracts.cppm
@@ -0,0 +1,52 @@
+// Tests that contract annotations survive module 
serialization/deserialization.
+//
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++2c -fcontracts %t/A.cppm -emit-module-interface -o 
%t/A.pcm
+// RUN: %clang_cc1 -std=c++2c -fcontracts %t/Use.cpp -fprebuilt-module-path=%t 
\
+// RUN:   -fsyntax-only -ast-dump-all 2>&1 | FileCheck %t/Use.cpp
+
+// Test again with reduced BMI.
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++2c -fcontracts %t/A.cppm 
-emit-reduced-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++2c -fcontracts %t/Use.cpp -fprebuilt-module-path=%t 
\
+// RUN:   -fsyntax-only -ast-dump-all 2>&1 | FileCheck %t/Use.cpp
+
+//--- A.cppm
+export module A;
+
+export int divide(int a, int b) pre(b != 0);
+
+export int clamp(int x, int lo, int hi)
+    pre(lo <= hi)
+    post(x >= lo);
+
+export int square(int x) post(r: r >= 0);
+
+//--- Use.cpp
+import A;
+
+int test() {
+  return divide(10, 2) + clamp(5, 0, 10) + square(3);
+}
+
+// CHECK: FunctionDecl {{.*}} divide 'int (int, int)' {{.*}}contracts
+// CHECK:   pre:
+// CHECK:     BinaryOperator {{.*}} '!='
+
+// CHECK: FunctionDecl {{.*}} clamp 'int (int, int, int)' {{.*}}contracts
+// CHECK:   pre:
+// CHECK:     BinaryOperator {{.*}} '<='
+// CHECK:   post:
+// CHECK:     BinaryOperator {{.*}} '>='
+
+// CHECK: FunctionDecl {{.*}} square 'int (int)' {{.*}}contracts
+// CHECK:   post:
+// CHECK:     VarDecl {{.*}} implicit {{.*}} r 'const int'
+// CHECK:     BinaryOperator {{.*}} '>='
+// CHECK:       DeclRefExpr {{.*}} 'const int' lvalue Var {{.*}} 'r' 'const 
int'
diff --git a/clang/test/Parser/cxx26-contracts.cpp 
b/clang/test/Parser/cxx26-contracts.cpp
new file mode 100644
index 0000000000000..4be473931a240
--- /dev/null
+++ b/clang/test/Parser/cxx26-contracts.cpp
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -std=c++2c -fcontracts -fsyntax-only -verify %s
+
+int divide(int a, int b) pre(b != 0);
+
+int square(int x) post(r: r >= 0);
+
+int abs_val(int x) pre(x >= -1000) pre(x <= 1000) post(r: r >= 0);
+
+int safe_div(int a, int b) pre(b != 0) post(r: r * b == a);
+
+// Multiple post with result names
+int clamp(int x) post(r: r >= 0) post(r: r <= 100);
+
+// post without result name
+int identity(int x) post(x >= 0);
+
+void f(int x) {
+  contract_assert(x > 0);
+}
+
+// pre and post are not keywords - they can still be used as identifiers.
+int pre = 42;
+int post(int x) { return x; }
+void use() {
+  pre = 10;
+  post(5);
+}
+
+// post(name: expr) on void-returning function is an error.
+void g() post(r: r > 0); // expected-error {{post-condition result name on 
function returning void}} \
+                          // expected-error {{use of undeclared identifier 
'r'}}
+

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

Reply via email to