https://github.com/ChuanqiXu9 updated 
https://github.com/llvm/llvm-project/pull/221139

>From 9bf57b9bf05c4cf647008a09e3cb30056413cd1f Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <[email protected]>
Date: Fri, 4 Sep 2026 11:19:00 +0800
Subject: [PATCH 1/2] [C++26] [Contracts] Initial parser support

---
 clang/include/clang/Basic/BuiltinTraits.td    |   3 +-
 .../clang/Basic/DiagnosticParseKinds.td       |   4 +
 clang/include/clang/Basic/LangOptions.def     |   2 +
 clang/include/clang/Basic/TokenKinds.def      |   7 +
 clang/include/clang/Options/Options.td        |   8 +
 clang/include/clang/Parse/Parser.h            |  16 ++
 clang/include/clang/Sema/Sema.h               |   4 +
 clang/lib/Basic/IdentifierTable.cpp           |   6 +-
 clang/lib/Parse/ParseDecl.cpp                 |   8 +-
 clang/lib/Parse/ParseDeclCXX.cpp              | 170 ++++++++++++++++--
 clang/lib/Parse/ParseExprCXX.cpp              |   6 +-
 clang/lib/Parse/ParseStmt.cpp                 |  32 ++++
 clang/lib/Sema/SemaDeclCXX.cpp                |  15 ++
 .../Parser/cxx26-contracts-declarators.cpp    |  77 ++++++++
 clang/test/Parser/cxx26-contracts-keyword.cpp |  15 ++
 clang/test/Parser/cxx26-contracts-lambda.cpp  |  57 ++++++
 clang/test/Parser/cxx26-contracts-language.c  |  16 ++
 clang/test/Parser/cxx26-contracts-parser.cpp  |  59 ++++++
 .../test/Parser/cxx26-contracts-recovery.cpp  |  52 ++++++
 19 files changed, 532 insertions(+), 25 deletions(-)
 create mode 100644 clang/test/Parser/cxx26-contracts-declarators.cpp
 create mode 100644 clang/test/Parser/cxx26-contracts-keyword.cpp
 create mode 100644 clang/test/Parser/cxx26-contracts-lambda.cpp
 create mode 100644 clang/test/Parser/cxx26-contracts-language.c
 create mode 100644 clang/test/Parser/cxx26-contracts-parser.cpp
 create mode 100644 clang/test/Parser/cxx26-contracts-recovery.cpp

diff --git a/clang/include/clang/Basic/BuiltinTraits.td 
b/clang/include/clang/Basic/BuiltinTraits.td
index f80234dd21e14..c424ea42bc7cd 100644
--- a/clang/include/clang/Basic/BuiltinTraits.td
+++ b/clang/include/clang/Basic/BuiltinTraits.td
@@ -41,8 +41,9 @@ def KEYHLSL : TokenKey<0x8000000>;
 def KEYFIXEDPOINT : TokenKey<0x10000000>;
 def KEYDEFERTS : TokenKey<0x20000000>;
 def KEYNOHLSL : TokenKey<0x40000000>;
+def KEYCONTRACTS : TokenKey<0x80000000>;
 
-def KEYMAX : TokenKey<KEYNOHLSL.Value>;
+def KEYMAX : TokenKey<KEYCONTRACTS.Value>;
 def KEYALLCXX : TokenKey<!or(KEYCXX.Value, KEYCXX11.Value, KEYCXX20.Value)>;
 def KEYALL : TokenKey<!and(!or(KEYMAX.Value, !sub(KEYMAX.Value, 1)),
                            !xor(KEYNOMS18.Value, -1),
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td 
b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 4ca2ed38363c4..bc46b00e5f4c2 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -428,6 +428,10 @@ def warn_cxx98_compat_trailing_return_type : Warning<
   InGroup<CXX98Compat>, DefaultIgnore;
 def err_requires_clause_must_appear_after_trailing_return : Error<
   "trailing return type must appear before trailing requires clause">;
+def err_requires_clause_must_precede_contract_specifiers : Error<
+  "trailing requires clause must appear before contract specifiers">;
+def err_contract_specifier_not_function : Error<
+  "contract specifiers can only be applied to function declarations">;
 def err_requires_clause_on_declarator_not_declaring_a_function : Error<
   "trailing requires clause can only be used when declaring a function">;
 def err_requires_clause_inside_parens : Error<
diff --git a/clang/include/clang/Basic/LangOptions.def 
b/clang/include/clang/Basic/LangOptions.def
index ad993ce7e5d95..71ddfbe3badda 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -444,6 +444,8 @@ LANGOPT(PaddingOnUnsignedFixedPoint, 1, 0, NotCompatible,
 
 LANGOPT(OverflowBehaviorTypes, 1, 0, NotCompatible, "overflow behavior types")
 
+LANGOPT(Contracts, 1, 0, Benign, "C++ contracts support")
+
 ENUM_LANGOPT(RegisterStaticDestructors, RegisterStaticDestructorsKind, 2,
              RegisterStaticDestructorsKind::All, NotCompatible,
              "Register C++ static destructors")
diff --git a/clang/include/clang/Basic/TokenKinds.def 
b/clang/include/clang/Basic/TokenKinds.def
index dc9c7d8109467..70c6cae42d642 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
@@ -421,6 +424,9 @@ CXX11_KEYWORD(nullptr               , KEYC23)
 CXX11_KEYWORD(static_assert         , KEYMSCOMPAT|KEYC23)
 CXX11_KEYWORD(thread_local          , KEYC23)
 
+// C++26 Contracts keyword.
+CONTRACTS_KEYWORD(contract_assert)
+
 // C++20 / coroutines keywords
 COROUTINES_KEYWORD(co_await)
 COROUTINES_KEYWORD(co_return)
@@ -931,6 +937,7 @@ ANNOTATION(embed)
 #undef TYPE_TRAIT_1
 #undef TYPE_TRAIT
 #undef MODULES_KEYWORD
+#undef CONTRACTS_KEYWORD
 #undef CXX20_KEYWORD
 #undef CXX11_KEYWORD
 #undef KEYWORD
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 3b88dce9c822b..5c06c8ecbf3db 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -1833,6 +1833,14 @@ defm coroutines : BoolFOption<"coroutines",
           "Enable support for the C++ Coroutines">,
   NegFlag<SetFalse>>;
 
+defm contracts : BoolFOption<"contracts",
+  LangOpts<"Contracts">, DefaultFalse,
+  PosFlag<SetTrue, [], [ClangOption, CC1Option],
+          "Enable support for C++ Contracts">,
+  NegFlag<SetFalse, [], [ClangOption, CC1Option],
+          "Disable support for C++ Contracts">>,
+  ShouldParseIf<cplusplus.KeyPath>;
+
 defm coro_aligned_allocation : BoolFOption<"coro-aligned-allocation",
   LangOpts<"CoroAlignedAllocation">, DefaultFalse,
   PosFlag<SetTrue, [], [ClangOption, CC1Option],
diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index ae91153e34e3a..13b01f9cc1c1b 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2763,6 +2763,17 @@ class Parser : public CodeCompletionHandler {
       const Declarator &D, const DeclSpec &DS,
       std::optional<Sema::CXXThisScopeRAII> &ThisScope);
 
+  enum class ContractSpecifierKind { Pre, Post };
+  std::optional<ContractSpecifierKind> getContractSpecifierKind();
+  void ParseContractSpecifiers(Declarator &D);
+  /// Parse the function tails. e.g., requires clause and contracts.
+  ///
+  /// \param ParametersAlreadyInScope whether the paramers are arealdy in an
+  /// active function prototype scope. This can be true for lambda as lambda
+  /// would always build its function prototype scope.
+  void ParseFunctionDeclaratorTail(Declarator &D,
+                                   bool ParametersAlreadyInScope = false);
+
   /// ParseRefQualifier - Parses a member function ref-qualifier. Returns
   /// true if a ref-qualifier is found.
   bool ParseRefQualifier(bool &RefQualifierIsLValueRef,
@@ -7598,6 +7609,11 @@ class Parser : public CodeCompletionHandler {
   ///
   StmtResult ParseBreakStatement();
 
+  /// Parse a C++26 contract-assertion statement.
+  /// TODO: Currently the AST result of contracts is not support, its predicate
+  /// will be parsed and the corresponding AST result will be discarded.
+  StmtResult ParseContractAssertStatement();
+
   /// ParseReturnStatement
   /// \verbatim
   ///       jump-statement:
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 9b07b591b8c07..fff66a45fc2a2 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6501,6 +6501,10 @@ class Sema final : public SemaBase {
   void SetFunctionBodyKind(Decl *D, SourceLocation Loc, FnBodyKind BodyKind,
                            StringLiteral *DeletedMessage = nullptr);
   void ActOnStartTrailingRequiresClause(Scope *S, Declarator &D);
+  /// Create a result variable for postconditions and make it visible in the
+  /// predicate's scope.
+  VarDecl *ActOnPostConditionResultName(Scope *S, IdentifierInfo *ResultName,
+                                        SourceLocation ResultNameLoc);
   ExprResult ActOnFinishTrailingRequiresClause(ExprResult ConstraintExpr);
   ExprResult ActOnRequiresClause(ExprResult ConstraintExpr);
 
diff --git a/clang/lib/Basic/IdentifierTable.cpp 
b/clang/lib/Basic/IdentifierTable.cpp
index aa349fcc384ce..b4a22b3f597b9 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.CPlusPlus && LangOpts.Contracts ? KS_Enabled : KS_Unknown;
   case KEYCOROUTINES:
     return LangOpts.Coroutines ? KS_Enabled : KS_Unknown;
   case KEYMODULES:
@@ -199,8 +201,8 @@ KeywordStatus clang::getKeywordStatus(const LangOptions 
&LangOpts,
 }
 
 static bool IsKeywordInCpp(unsigned Flags) {
-  return (Flags & (KEYCXX | KEYCXX11 | KEYCXX20 | BOOLSUPPORT | WCHARSUPPORT |
-                   CHAR8SUPPORT)) != 0;
+  return (Flags & (KEYCXX | KEYCXX11 | KEYCXX20 | KEYCONTRACTS | BOOLSUPPORT |
+                   WCHARSUPPORT | CHAR8SUPPORT)) != 0;
 }
 
 static void MarkIdentifierAsKeywordInCpp(IdentifierTable &Table,
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 1d3789a10d9de..6e3fd2d5a4fc0 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2186,13 +2186,13 @@ Parser::DeclGroupPtrTy 
Parser::ParseDeclGroup(ParsingDeclSpec &DS,
     while (MaybeParseHLSLAnnotations(D))
       ;
 
-  if (Tok.is(tok::kw_requires)) {
+  if (Tok.is(tok::kw_requires) || getContractSpecifierKind()) {
     TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
     // With abbreviated function templates - we need to explicitly add depth to
     // account for the implicit template parameter list induced by the 
template.
     if (!TemplateInfo.TemplateParams && D.getInventedTemplateParameterList())
       ++CurTemplateDepthTracker;
-    ParseTrailingRequiresClauseWithScope(D);
+    ParseFunctionDeclaratorTail(D);
   }
 
   // Save late-parsed attributes for now; they need to be parsed in the
@@ -2441,8 +2441,8 @@ Parser::DeclGroupPtrTy 
Parser::ParseDeclGroup(ParsingDeclSpec &DS,
       //    init-declarator:
       //             declarator initializer[opt]
       //        declarator requires-clause
-      if (Tok.is(tok::kw_requires))
-        ParseTrailingRequiresClauseWithScope(D);
+      if (Tok.is(tok::kw_requires) || getContractSpecifierKind())
+        ParseFunctionDeclaratorTail(D);
       Decl *ThisDecl = ParseDeclarationAfterDeclarator(D, TemplateInfo);
       D.complete(ThisDecl);
       if (ThisDecl)
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index ed995398f597d..b60dd203635df 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -2563,8 +2563,9 @@ bool Parser::ParseCXXMemberDeclaratorBeforeInitializer(
     Declarator &DeclaratorInfo, VirtSpecifiers &VS, ExprResult &BitfieldSize,
     LateParsedAttrList &LateParsedAttrs) {
   // member-declarator:
-  //   declarator virt-specifier-seq[opt] pure-specifier[opt]
-  //   declarator requires-clause
+  //   declarator virt-specifier-seq[opt]
+  //     function-contract-specifier-seq[opt] pure-specifier[opt]
+  //   declarator requires-clause function-contract-specifier-seq[opt]
   //   declarator brace-or-equal-initializer[opt]
   //   identifier attribute-specifier-seq[opt] ':' constant-expression
   //       brace-or-equal-initializer[opt]
@@ -2591,21 +2592,30 @@ bool Parser::ParseCXXMemberDeclaratorBeforeInitializer(
     BitfieldSize = ParseConstantExpression();
     if (BitfieldSize.isInvalid())
       SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch);
-  } else if (Tok.is(tok::kw_requires)) {
-    TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
-    // With abbreviated function templates - we need to explicitly add depth to
-    // account for the implicit template parameter list induced by the 
template.
-    if (DeclaratorInfo.getTemplateParameterLists().empty() &&
-        DeclaratorInfo.getInventedTemplateParameterList())
-      ++CurTemplateDepthTracker;
-    ParseTrailingRequiresClauseWithScope(DeclaratorInfo);
   } else {
-    ParseOptionalCXX11VirtSpecifierSeq(
-        VS, getCurrentClass().IsInterface,
-        DeclaratorInfo.getDeclSpec().getFriendSpecLoc());
-    if (!VS.isUnset())
-      MaybeParseAndDiagnoseDeclSpecAfterCXX11VirtSpecifierSeq(DeclaratorInfo,
-                                                              VS);
+    // Requires clause can't follow 'override' but contracts can follow
+    // 'override'. Even if we don't support virtual functions with contractcs,
+    // we should diagnose at the sema stage instead of the parser stage.
+    if (Tok.isNot(tok::kw_requires)) {
+      ParseOptionalCXX11VirtSpecifierSeq(
+          VS, getCurrentClass().IsInterface,
+          DeclaratorInfo.getDeclSpec().getFriendSpecLoc());
+      if (!VS.isUnset())
+        MaybeParseAndDiagnoseDeclSpecAfterCXX11VirtSpecifierSeq(DeclaratorInfo,
+                                                                VS);
+    }
+
+    if (Tok.is(tok::kw_requires) || getContractSpecifierKind()) {
+      TemplateParameterDepthRAII CurTemplateDepthTracker(
+          TemplateParameterDepth);
+      // With abbreviated function templates - we need to explicitly add depth
+      // to account for the implicit template parameter list induced by the
+      // template.
+      if (DeclaratorInfo.getTemplateParameterLists().empty() &&
+          DeclaratorInfo.getInventedTemplateParameterList())
+        ++CurTemplateDepthTracker;
+      ParseFunctionDeclaratorTail(DeclaratorInfo);
+    }
   }
 
   // If a simple-asm-expr is present, parse it.
@@ -4244,6 +4254,134 @@ void Parser::ParseTrailingRequiresClause(Declarator &D) 
{
   }
 }
 
+std::optional<Parser::ContractSpecifierKind>
+Parser::getContractSpecifierKind() {
+  if (!getLangOpts().Contracts || Tok.isNot(tok::identifier))
+    return std::nullopt;
+
+  IdentifierInfo *II = Tok.getIdentifierInfo();
+  if (II->isStr("pre"))
+    return ContractSpecifierKind::Pre;
+  if (II->isStr("post"))
+    return ContractSpecifierKind::Post;
+  return std::nullopt;
+}
+
+void Parser::ParseContractSpecifiers(Declarator &D) {
+  assert(getLangOpts().Contracts && "contracts are not enabled");
+
+  const bool IsFunction = D.isDeclarationOfFunction() &&
+                          (D.isFunctionDeclarationContext() ||
+                           D.getContext() == DeclaratorContext::LambdaExpr);
+  if (!IsFunction)
+    Diag(Tok, diag::err_contract_specifier_not_function);
+
+  while (std::optional<ContractSpecifierKind> Kind =
+             getContractSpecifierKind()) {
+    const bool IsPost = *Kind == ContractSpecifierKind::Post;
+    ConsumeToken();
+
+    ParsedAttributes Attrs(AttrFactory);
+    MaybeParseCXX11Attributes(Attrs);
+
+    BalancedDelimiterTracker T(*this, tok::l_paren);
+    if (T.expectAndConsume(diag::err_expected_lparen_after,
+                           IsPost ? "post" : "pre"))
+      return;
+
+    // !IsFunction is diagnosed before, avoid confusing diagnostics.
+    if (!IsFunction) {
+      T.skipToEnd();
+      continue;
+    }
+
+    // FIXME: We should accept attribute for the result binding, e.g.,
+    // post(r [[attr]] : expr).
+    std::optional<ParseScope> ResultNameScope;
+    if (IsPost && Tok.is(tok::identifier) && NextToken().is(tok::colon)) {
+      IdentifierInfo *ResultName = Tok.getIdentifierInfo();
+      SourceLocation ResultNameLoc = ConsumeToken();
+      ConsumeToken();
+
+      ResultNameScope.emplace(this, Scope::DeclScope);
+      Actions.ActOnPostConditionResultName(getCurScope(), ResultName,
+                                           ResultNameLoc);
+    }
+
+    EnterExpressionEvaluationContext Evaluated(
+        Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
+    ExprResult Predicate = ParseConditionalExpression();
+    ResultNameScope.reset();
+    if (Predicate.isInvalid())
+      T.skipToEnd();
+    else
+      T.consumeClose();
+  }
+}
+
+// Parse the function tail where the require clause must appear first
+// and the contracts later.
+void Parser::ParseFunctionDeclaratorTail(Declarator &D,
+                                         bool ParametersAlreadyInScope) {
+  assert((Tok.is(tok::kw_requires) || getContractSpecifierKind()) &&
+         "expected a function declarator tail");
+
+  // TODO: We can consider merging ParseTrailingRequiresClauseWithScope into
+  // this function.
+  if (!ParametersAlreadyInScope && Tok.is(tok::kw_requires)) {
+    ParseTrailingRequiresClauseWithScope(D);
+    if (!getContractSpecifierKind())
+      return;
+  }
+
+  auto ParseTail = [&] {
+    bool ParametersInScope = ParametersAlreadyInScope;
+    if (Tok.is(tok::kw_requires)) {
+      ParseTrailingRequiresClause(D);
+      ParametersInScope = true;
+    }
+
+    while (getContractSpecifierKind()) {
+      if (!ParametersInScope) {
+        // FIXME: ActOnStartTrailingRequiresClause is not actually deal with
+        // require clause. It is actually adds the parameters to the scope
+        // chains. It needs a better name.
+        Actions.ActOnStartTrailingRequiresClause(getCurScope(), D);
+        ParametersInScope = true;
+      }
+
+      const DeclSpec *MethodDS = &D.getDeclSpec();
+      if (D.isFunctionDeclarator() && D.getFunctionTypeInfo().MethodQualifiers)
+        MethodDS = D.getFunctionTypeInfo().MethodQualifiers;
+      std::optional<Sema::CXXThisScopeRAII> ThisScope;
+      InitCXXThisScopeForDeclaratorIfRelevant(D, *MethodDS, ThisScope);
+      ParseContractSpecifiers(D);
+
+      // If we meet requires after contracts, emit a nice diagnostics and
+      // recovering compilation.
+      if (Tok.is(tok::kw_requires)) {
+        Diag(Tok, diag::err_requires_clause_must_precede_contract_specifiers);
+        ParseTrailingRequiresClause(D);
+      }
+    }
+  };
+
+  if (ParametersAlreadyInScope) {
+    ParseTail();
+    return;
+  }
+
+  CXXScopeSpec &SS = D.getCXXScopeSpec();
+  DeclaratorScopeObj DeclScopeObj(*this, SS);
+  if (SS.isValid() && Actions.ShouldEnterDeclaratorScope(getCurScope(), SS))
+    DeclScopeObj.EnterDeclaratorScope();
+
+  ParseScope ParamScope(this, Scope::DeclScope |
+                                  Scope::FunctionDeclarationScope |
+                                  Scope::FunctionPrototypeScope);
+  ParseTail();
+}
+
 Sema::ParsingClassState Parser::PushParsingClass(Decl *ClassDecl,
                                                  bool NonNestedClass,
                                                  bool IsInterface) {
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index ae741af7249cf..ad566c25a438d 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1349,6 +1349,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
                   tok::kw___private, tok::kw___global, tok::kw___local,
                   tok::kw___constant, tok::kw___generic, tok::kw_groupshared,
                   tok::kw_requires, tok::kw_noexcept) ||
+      getContractSpecifierKind().has_value() ||
       Tok.isRegularKeywordAttribute() ||
       (Tok.is(tok::l_square) && NextToken().is(tok::l_square));
 
@@ -1446,8 +1447,9 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
                       TrailingReturnType, TrailingReturnTypeLoc, &DS),
                   std::move(Attributes), DeclEndLoc);
 
-    if (HasParentheses && Tok.is(tok::kw_requires))
-      ParseTrailingRequiresClause(D);
+    if ((HasParentheses && Tok.is(tok::kw_requires)) ||
+        getContractSpecifierKind())
+      ParseFunctionDeclaratorTail(D, /*ParametersAlreadyInScope=*/true);
   }
 
   // Emit a warning if we see a CUDA host/device/global attribute
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 5e67cd551bff8..898e6bf3cf120 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -158,6 +158,11 @@ StmtResult 
Parser::ParseStatementOrDeclarationAfterAttributes(
         getCurScope(), SemaCodeCompletion::PCC_Statement);
     return StmtError();
 
+  case tok::kw_contract_assert:
+    Res = ParseContractAssertStatement();
+    SemiError = "contract_assert";
+    break;
+
   case tok::identifier:
   ParseIdentifier: {
     Token Next = NextToken();
@@ -2462,6 +2467,33 @@ StmtResult Parser::ParseBreakStatement() {
   return ParseBreakOrContinueStatement(/*IsContinue=*/false);
 }
 
+StmtResult Parser::ParseContractAssertStatement() {
+  assert(Tok.is(tok::kw_contract_assert) && "expected contract_assert");
+  SourceLocation ContractAssertLoc = ConsumeToken();
+
+  ParsedAttributes Attrs(AttrFactory);
+  MaybeParseCXX11Attributes(Attrs);
+
+  BalancedDelimiterTracker T(*this, tok::l_paren);
+  if (T.expectAndConsume(diag::err_expected_lparen_after, "contract_assert")) {
+    SkipUntil(tok::semi, StopBeforeMatch);
+    return Actions.ActOnNullStmt(ContractAssertLoc,
+                                 /*HasLeadingEmptyMacro=*/false);
+  }
+
+  EnterExpressionEvaluationContext Evaluated(
+      Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
+  ExprResult Predicate = ParseConditionalExpression();
+  if (Predicate.isInvalid())
+    T.skipToEnd();
+  else
+    T.consumeClose();
+
+  // TODO: Now we don't build AST node for contracts.
+  return Actions.ActOnNullStmt(ContractAssertLoc,
+                               /*HasLeadingEmptyMacro=*/false);
+}
+
 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/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 4457ec58902d0..3a33e77efb85b 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -4220,6 +4220,21 @@ void Sema::ActOnStartTrailingRequiresClause(Scope *S, 
Declarator &D) {
   }
 }
 
+VarDecl *Sema::ActOnPostConditionResultName(Scope *S,
+                                            IdentifierInfo *ResultName,
+                                            SourceLocation ResultNameLoc) {
+  // FIXME: We use a dependent type here to allow parsing the result name
+  // binding in post contracts. We should use the real result type when the
+  // support gets better Keeping this declaration out of the DeclContext makes
+  // it a predicate-local lookup aid rather than a program declaration.
+  VarDecl *ResultVar = VarDecl::Create(
+      Context, Context.getTranslationUnitDecl(), ResultNameLoc, ResultNameLoc,
+      ResultName, Context.DependentTy, /*TInfo=*/nullptr, SC_None);
+  ResultVar->setImplicit();
+  PushOnScopeChains(ResultVar, S, /*AddToContext=*/false);
+  return ResultVar;
+}
+
 ExprResult Sema::ActOnFinishTrailingRequiresClause(ExprResult ConstraintExpr) {
   return ActOnRequiresClause(ConstraintExpr);
 }
diff --git a/clang/test/Parser/cxx26-contracts-declarators.cpp 
b/clang/test/Parser/cxx26-contracts-declarators.cpp
new file mode 100644
index 0000000000000..2161fc0dcb394
--- /dev/null
+++ b/clang/test/Parser/cxx26-contracts-declarators.cpp
@@ -0,0 +1,77 @@
+// RUN: %clang_cc1 -std=c++2c -fcontracts -fsyntax-only -verify %s
+
+// This file exercises the declaration paths which can lead to a function
+// contract specifier. Contract predicates are parsed but not retained yet.
+
+template <typename>
+concept Always = true;
+
+int declaration(int value) pre(value > 0) post(result: value >= 0);
+
+int definition(int value) pre(value > 0) post(result: value >= 0) {
+  return value;
+}
+
+auto trailing_return(int value) -> int pre(value > 0) post(value >= 0) {
+  return value;
+}
+
+decltype(auto) reference_return(int &value) pre(value > 0) post(value > 0) {
+  return (value);
+}
+
+int with_defaults(int value = 1) noexcept pre(value > 0) {
+  return value;
+}
+
+template <typename T>
+auto constrained(T value) -> T requires Always<T> pre(value > T{}) {
+  return value;
+}
+
+int first(int value) pre(value > 0),
+    second(int value) post(result: value > 0);
+
+struct Widget {
+  int value;
+
+  Widget(int input) pre(input > 0);
+  ~Widget() pre(true);
+
+  static int static_member(int input) noexcept pre(input > 0) {
+    return input;
+  }
+
+  friend int inspect(const Widget &, int input) pre(input > 0);
+
+  explicit operator bool() const pre(this->value >= 0);
+  int operator()(int input) const & noexcept pre(input > 0) post(input >= 0);
+
+  virtual int pure(int input) const pre(input > 0) = 0;
+
+  template <typename T>
+  T member_template(T input) requires Always<T> pre(input > T{}) {
+    return input;
+  }
+};
+
+Widget::Widget(int input) pre(input > 0) : value(input) {}
+Widget::~Widget() pre(true) {}
+
+namespace API {
+struct Service {
+  int limit;
+  int call(int input) const;
+};
+} // namespace API
+
+int API::Service::call(int input) const
+    pre(this->limit >= 0 && input > 0) post(input > 0) {
+  return input;
+}
+
+int object pre(true); // expected-error {{contract specifiers can only be 
applied to function declarations}}
+int (*function_pointer)(int) pre(true); // expected-error {{contract 
specifiers can only be applied to function declarations}}
+extern int (&function_reference)(int) pre(true); // expected-error {{contract 
specifiers can only be applied to function declarations}}
+
+typedef int FunctionTypedef(int) pre(true); // expected-error {{contract 
specifiers can only be applied to function declarations}}
diff --git a/clang/test/Parser/cxx26-contracts-keyword.cpp 
b/clang/test/Parser/cxx26-contracts-keyword.cpp
new file mode 100644
index 0000000000000..a6f6b22503564
--- /dev/null
+++ b/clang/test/Parser/cxx26-contracts-keyword.cpp
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -std=c++2c -fcontracts -DENABLED=1 -fsyntax-only 
-verify=enabled %s
+// RUN: %clang_cc1 -std=c++2c -fno-contracts -DENABLED=0 -fsyntax-only 
-verify=disabled %s
+
+// disabled-no-diagnostics
+
+#ifdef __cplusplus
+void test() {
+#if ENABLED
+  int contract_assert; // enabled-error {{expected unqualified-id}}
+#else
+  int contract_assert;
+  ++contract_assert;
+#endif
+}
+#endif
diff --git a/clang/test/Parser/cxx26-contracts-lambda.cpp 
b/clang/test/Parser/cxx26-contracts-lambda.cpp
new file mode 100644
index 0000000000000..cb290c54319b0
--- /dev/null
+++ b/clang/test/Parser/cxx26-contracts-lambda.cpp
@@ -0,0 +1,57 @@
+// RUN: %clang_cc1 -std=c++2c -fcontracts -fsyntax-only -verify %s
+
+template <typename>
+concept LambdaConstraint = true;
+
+void lambda_contracts() {
+  auto no_parentheses = [] pre(true) { return 0; };
+  auto explicit_return = [](int value) -> int
+      pre(value > 0) post(result: result >= value) { return value; };
+  auto qualified = [](int value) mutable noexcept -> int
+      pre(value > 0) post(value >= 0) { return value; };
+  auto constexpr_lambda = [](int value) constexpr pre(value > 0) {
+    return value;
+  };
+  auto static_lambda = [](int value) static noexcept pre(value > 0) {
+    return value;
+  };
+  auto with_default = [](int value = 1) pre(value > 0) { return value; };
+
+  auto generic = []<typename T>(T value) pre(value > T{}) { return value; };
+  auto constrained = []<typename T>(T value)
+      requires LambdaConstraint<T>
+      pre(value > T{}) post(value >= T{}) { return value; };
+  auto doubly_constrained = []<typename T>
+      requires LambdaConstraint<T>
+      (T value) -> T requires LambdaConstraint<T>
+      pre(value > T{}) post(value >= T{}) { return value; };
+  auto non_type_parameter = []<int N>() pre(N > 0) { return N; };
+  auto parameter_pack = []<typename... T>(T... values)
+      pre(sizeof...(values) > 0) { return sizeof...(values); };
+
+  auto nested = [](int outer) pre(outer > 0) {
+    return [outer](int inner) pre(inner > outer) { return inner; };
+  };
+
+  (void)no_parentheses;
+  (void)explicit_return;
+  (void)qualified;
+  (void)constexpr_lambda;
+  (void)static_lambda;
+  (void)with_default;
+  (void)generic;
+  (void)constrained;
+  (void)doubly_constrained;
+  (void)non_type_parameter;
+  (void)parameter_pack;
+  (void)nested;
+}
+
+void lambda_order_recovery() {
+  auto wrong_order = []<typename T>(T value)
+      pre(value > T{}) requires LambdaConstraint<T> {
+    // expected-error@-1 {{trailing requires clause must appear before 
contract specifiers}}
+    return value;
+  };
+  (void)wrong_order;
+}
diff --git a/clang/test/Parser/cxx26-contracts-language.c 
b/clang/test/Parser/cxx26-contracts-language.c
new file mode 100644
index 0000000000000..2b80d4e79f2bc
--- /dev/null
+++ b/clang/test/Parser/cxx26-contracts-language.c
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -std=c17 -fcontracts -fsyntax-only -verify %s
+
+// Contracts are a C++ feature. Enabling the parser option in C mode must not
+// turn its spellings into keywords or contextual keywords.
+
+// expected-no-diagnostics
+
+int contract_assert;
+int pre;
+int post;
+
+void use_contract_spellings(void) {
+  ++contract_assert;
+  ++pre;
+  ++post;
+}
diff --git a/clang/test/Parser/cxx26-contracts-parser.cpp 
b/clang/test/Parser/cxx26-contracts-parser.cpp
new file mode 100644
index 0000000000000..7b08e702b9450
--- /dev/null
+++ b/clang/test/Parser/cxx26-contracts-parser.cpp
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -std=c++2c -fcontracts -fsyntax-only -verify %s
+
+// This test intentionally exercises parser-only support. Contract predicates
+// are parsed as ordinary expressions, but are not retained in the AST yet.
+
+int pre = 42;
+int post(int value) { return value; }
+
+int divide(int a, int b) pre(b != 0);
+int square(int x) post(result: result >= x);
+int clamp(int x) pre(x >= 0) pre(x <= 100)
+    post(result: x >= 0) post(other_result: x <= 100);
+
+struct OuterResult {};
+OuterResult shadowed_result;
+int result_shadowing(int x)
+    post(shadowed_result: shadowed_result >= x);
+
+template <typename T>
+concept C = true;
+
+template <typename T>
+int constrained(T value) requires C<T> pre(value > T{});
+
+struct S {
+  int member(int value) pre(value > 0) post(result: result > value);
+  virtual int virtual_member(int value) const final pre(value > 0);
+};
+
+int S::member(int value) pre(value > 0) post(result: result > value) {
+  contract_assert(value > 0);
+  contract_assert [[maybe_unused]] (value < 100);
+  return value;
+}
+
+void lambdas() {
+  auto a = [](int value) pre(value > 0) { return value; };
+  auto b = [] pre(true) { return 0; };
+  (void)a;
+  (void)b;
+}
+
+int attributed(int value)
+    pre [[maybe_unused]] (value > 0)
+    post [[maybe_unused]] (result: value > 0);
+
+int not_a_function pre(true); // expected-error {{contract specifiers can only 
be applied to function declarations}}
+
+template <typename T>
+int wrong_order(T value) pre(value > T{}) requires C<T>;
+// expected-error@-1 {{trailing requires clause must appear before contract 
specifiers}}
+
+void missing_assert_lparen() {
+  contract_assert true; // expected-error {{expected '(' after 
'contract_assert'}}
+}
+
+int missing_pre_lparen() pre true;
+// expected-error@-1 {{expected '(' after 'pre'}}
+// expected-error@-2 {{expected function body after function declarator}}
diff --git a/clang/test/Parser/cxx26-contracts-recovery.cpp 
b/clang/test/Parser/cxx26-contracts-recovery.cpp
new file mode 100644
index 0000000000000..9ffc2bc878f1c
--- /dev/null
+++ b/clang/test/Parser/cxx26-contracts-recovery.cpp
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -std=c++2c -fcontracts -fsyntax-only -verify %s
+
+template <typename>
+concept Recoverable = true;
+
+int empty_pre(int value) pre(); // expected-error {{expected expression}}
+int after_empty_pre(int value) pre(value > 0);
+
+int empty_post(int value) post(result:); // expected-error {{expected 
expression}}
+int after_empty_post(int value) post(value > 0);
+
+int missing_pre_close(int value) pre(value > 0; // expected-error {{expected 
')'}} expected-note {{to match this '('}}
+int after_missing_pre_close(int value) pre(value > 0);
+
+void assertion_recovery(int value) {
+  contract_assert(); // expected-error {{expected expression}}
+  int after_empty = value;
+
+  contract_assert value > 0; // expected-error {{expected '(' after 
'contract_assert'}}
+  int after_missing_open = after_empty;
+
+  contract_assert((value > 0) && ((value + 1) > 1));
+
+  contract_assert(value > 0; // expected-error {{expected ')'}} expected-note 
{{to match this '('}}
+  int after_missing_close = value;
+  (void)after_missing_close;
+  (void)after_missing_open;
+}
+
+template <typename T>
+int wrong_order_declaration(T value) pre(value > T{}) requires Recoverable<T>;
+// expected-error@-1 {{trailing requires clause must appear before contract 
specifiers}}
+
+template <typename T>
+int wrong_order_definition(T value) pre(value > T{}) requires Recoverable<T> {
+  // expected-error@-1 {{trailing requires clause must appear before contract 
specifiers}}
+  return value;
+}
+
+struct RecoveryMember {
+  template <typename T>
+  int wrong_order(T value) pre(value > T{}) requires Recoverable<T>;
+  // expected-error@-1 {{trailing requires clause must appear before contract 
specifiers}}
+
+  int after_error(int value) pre(value > 0);
+};
+
+int missing_pre_lparen(int value) pre value > 0;
+// expected-error@-1 {{expected '(' after 'pre'}}
+// expected-error@-2 {{expected function body after function declarator}}
+
+int after_missing_pre_lparen(int value) post(value > 0);

>From d5e0dbaee60bcf9a4e256dd42f91b4bf4fb001f8 Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <[email protected]>
Date: Fri, 4 Sep 2026 17:06:28 +0800
Subject: [PATCH 2/2] Address comments

---
 clang/include/clang/Basic/BuiltinTraits.td    |   7 +-
 .../include/clang/Basic/DiagnosticLexKinds.td |   2 +
 .../clang/Basic/DiagnosticParseKinds.td       |   4 +
 clang/include/clang/Basic/TokenKinds.def      |  13 +-
 clang/include/clang/Options/Options.td        |   4 +-
 clang/include/clang/Parse/Parser.h            |  17 ++-
 clang/include/clang/Sema/Sema.h               |   3 +-
 clang/lib/Basic/IdentifierTable.cpp           |  11 +-
 clang/lib/Parse/ParseDecl.cpp                 |   4 +-
 clang/lib/Parse/ParseDeclCXX.cpp              | 120 +++++++-----------
 clang/lib/Parse/ParseExprCXX.cpp              |   3 +-
 clang/lib/Parse/ParseStmt.cpp                 |   2 +
 clang/lib/Parse/Parser.cpp                    |   2 +
 clang/lib/Sema/SemaDeclCXX.cpp                |   3 +-
 clang/test/Lexer/keywords_test.cpp            |  13 +-
 clang/test/Parser/cxx26-contracts-keyword.cpp |  24 ++--
 16 files changed, 124 insertions(+), 108 deletions(-)

diff --git a/clang/include/clang/Basic/BuiltinTraits.td 
b/clang/include/clang/Basic/BuiltinTraits.td
index c424ea42bc7cd..0eb64f38310f7 100644
--- a/clang/include/clang/Basic/BuiltinTraits.td
+++ b/clang/include/clang/Basic/BuiltinTraits.td
@@ -41,10 +41,11 @@ def KEYHLSL : TokenKey<0x8000000>;
 def KEYFIXEDPOINT : TokenKey<0x10000000>;
 def KEYDEFERTS : TokenKey<0x20000000>;
 def KEYNOHLSL : TokenKey<0x40000000>;
-def KEYCONTRACTS : TokenKey<0x80000000>;
+def KEYCXX26 : TokenKey<0x80000000>;
 
-def KEYMAX : TokenKey<KEYCONTRACTS.Value>;
-def KEYALLCXX : TokenKey<!or(KEYCXX.Value, KEYCXX11.Value, KEYCXX20.Value)>;
+def KEYMAX : TokenKey<KEYCXX26.Value>;
+def KEYALLCXX : TokenKey<!or(KEYCXX.Value, KEYCXX11.Value, KEYCXX20.Value,
+                             KEYCXX26.Value)>;
 def KEYALL : TokenKey<!and(!or(KEYMAX.Value, !sub(KEYMAX.Value, 1)),
                            !xor(KEYNOMS18.Value, -1),
                            !xor(KEYNOOPENCL.Value, -1),
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td 
b/clang/include/clang/Basic/DiagnosticLexKinds.td
index c7f4856b03a1e..9e3f7ea842bfe 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -85,6 +85,8 @@ def warn_cxx11_keyword : Warning<"'%0' is a keyword in 
C++11">,
   InGroup<CXX11Compat>, DefaultIgnore;
 def warn_cxx20_keyword : Warning<"'%0' is a keyword in C++20">,
   InGroup<CXX20Compat>, DefaultIgnore;
+def warn_cxx26_keyword : Warning<"'%0' is a keyword in C++26">,
+  InGroup<CXX26Compat>, DefaultIgnore;
 def warn_c99_keyword : Warning<"'%0' is a keyword in C99">,
   InGroup<C99Compat>, DefaultIgnore;
 def warn_c23_keyword : Warning<"'%0' is a keyword in C23">,
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td 
b/clang/include/clang/Basic/DiagnosticParseKinds.td
index bc46b00e5f4c2..09ef2e013a3e5 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -12,6 +12,10 @@
 
 let Component = "Parse" in {
 let CategoryName = "Parse Issue" in {
+def err_contracts_require_cxx26 : Error<"contracts are a C++26 feature">;
+def err_contracts_disabled : Error<
+  "contracts support is disabled; pass '-fcontracts' to enable it">;
+
 // C23 compatibility with C17 and earlier.
 defm c_label_at_end_of_compound_statement : C23Compat<
   "label at end of compound statement is", /*ext_warn*/true>;
diff --git a/clang/include/clang/Basic/TokenKinds.def 
b/clang/include/clang/Basic/TokenKinds.def
index 70c6cae42d642..22aed14ff6369 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -28,15 +28,15 @@
 #ifndef CXX20_KEYWORD
 #define CXX20_KEYWORD(X,Y) KEYWORD(X,KEYCXX20|(Y))
 #endif
+#ifndef CXX26_KEYWORD
+#define CXX26_KEYWORD(X,Y) KEYWORD(X,KEYCXX26|(Y))
+#endif
 #ifndef C99_KEYWORD
 #define C99_KEYWORD(X,Y) KEYWORD(X,KEYC99|(Y))
 #endif
 #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
@@ -281,6 +281,7 @@ PUNCTUATOR(greatergreatergreater, ">>>")
 //   KEYNOCXX - This is a keyword in every non-C++ dialect.
 //   KEYCXX11 - This is a C++ keyword introduced to C++ in C++11
 //   KEYCXX20 - This is a C++ keyword introduced to C++ in C++20
+//   KEYCXX26 - This is a C++ keyword introduced to C++ in C++26
 //   KEYMODULES - This is a keyword if the C++ extensions for modules
 //                are enabled.
 //   KEYGNU   - This is a keyword if GNU extensions are enabled
@@ -424,8 +425,8 @@ CXX11_KEYWORD(nullptr               , KEYC23)
 CXX11_KEYWORD(static_assert         , KEYMSCOMPAT|KEYC23)
 CXX11_KEYWORD(thread_local          , KEYC23)
 
-// C++26 Contracts keyword.
-CONTRACTS_KEYWORD(contract_assert)
+// C++26 keyword.
+CXX26_KEYWORD(contract_assert         , 0)
 
 // C++20 / coroutines keywords
 COROUTINES_KEYWORD(co_await)
@@ -937,7 +938,7 @@ ANNOTATION(embed)
 #undef TYPE_TRAIT_1
 #undef TYPE_TRAIT
 #undef MODULES_KEYWORD
-#undef CONTRACTS_KEYWORD
+#undef CXX26_KEYWORD
 #undef CXX20_KEYWORD
 #undef CXX11_KEYWORD
 #undef KEYWORD
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 5c06c8ecbf3db..ff7714edd116a 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -1835,9 +1835,9 @@ defm coroutines : BoolFOption<"coroutines",
 
 defm contracts : BoolFOption<"contracts",
   LangOpts<"Contracts">, DefaultFalse,
-  PosFlag<SetTrue, [], [ClangOption, CC1Option],
+  PosFlag<SetTrue, [], [CC1Option],
           "Enable support for C++ Contracts">,
-  NegFlag<SetFalse, [], [ClangOption, CC1Option],
+  NegFlag<SetFalse, [], [CC1Option],
           "Disable support for C++ Contracts">>,
   ShouldParseIf<cplusplus.KeyPath>;
 
diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index 13b01f9cc1c1b..429e7789e9696 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2766,13 +2766,13 @@ class Parser : public CodeCompletionHandler {
   enum class ContractSpecifierKind { Pre, Post };
   std::optional<ContractSpecifierKind> getContractSpecifierKind();
   void ParseContractSpecifiers(Declarator &D);
-  /// Parse the function tails. e.g., requires clause and contracts.
+  /// Parse the trailing requires-clause and function contract specifiers.
   ///
-  /// \param ParametersAlreadyInScope whether the paramers are arealdy in an
-  /// active function prototype scope. This can be true for lambda as lambda
-  /// would always build its function prototype scope.
-  void ParseFunctionDeclaratorTail(Declarator &D,
-                                   bool ParametersAlreadyInScope = false);
+  /// \param ParametersAlreadyInScope whether the parameters are already in an
+  /// active function prototype scope. This can be true for lambdas, which
+  /// always build their function prototype scope.
+  void ParseFunctionContractSpecifiersAndConstraints(
+      Declarator &D, bool ParametersAlreadyInScope = false);
 
   /// ParseRefQualifier - Parses a member function ref-qualifier. Returns
   /// true if a ref-qualifier is found.
@@ -2898,6 +2898,10 @@ class Parser : public CodeCompletionHandler {
   mutable IdentifierInfo *Ident_GNU_final;
   mutable IdentifierInfo *Ident_override;
 
+  /// C++26 contextual keywords.
+  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,7 +3007,6 @@ class Parser : public CodeCompletionHandler {
                                      bool MayBeFollowedByDirectInit);
 
   /// Parse a requires-clause as part of a function declaration.
-  void ParseTrailingRequiresClauseWithScope(Declarator &D);
   void ParseTrailingRequiresClause(Declarator &D);
 
   void ParseMicrosoftIfExistsClassDeclaration(DeclSpec::TST TagType,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index fff66a45fc2a2..0aa0f682283df 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6500,7 +6500,8 @@ class Sema final : public SemaBase {
 
   void SetFunctionBodyKind(Decl *D, SourceLocation Loc, FnBodyKind BodyKind,
                            StringLiteral *DeletedMessage = nullptr);
-  void ActOnStartTrailingRequiresClause(Scope *S, Declarator &D);
+  void ActOnStartTrailingRequiresClauseOrContractSpecifier(Scope *S,
+                                                           Declarator &D);
   /// Create a result variable for postconditions and make it visible in the
   /// predicate's scope.
   VarDecl *ActOnPostConditionResultName(Scope *S, IdentifierInfo *ResultName,
diff --git a/clang/lib/Basic/IdentifierTable.cpp 
b/clang/lib/Basic/IdentifierTable.cpp
index b4a22b3f597b9..08d1265323072 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -108,6 +108,10 @@ static KeywordStatus getKeywordStatusHelper(const 
LangOptions &LangOpts,
     if (LangOpts.CPlusPlus20)
       return KS_Enabled;
     return LangOpts.CPlusPlus ? KS_Future : KS_Unknown;
+  case KEYCXX26:
+    if (LangOpts.CPlusPlus26)
+      return KS_Enabled;
+    return LangOpts.CPlusPlus ? KS_Future : KS_Unknown;
   case KEYGNU:
     return LangOpts.GNUKeywords ? KS_Extension : KS_Unknown;
   case KEYMS:
@@ -137,8 +141,6 @@ 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.CPlusPlus && LangOpts.Contracts ? KS_Enabled : KS_Unknown;
   case KEYCOROUTINES:
     return LangOpts.Coroutines ? KS_Enabled : KS_Unknown;
   case KEYMODULES:
@@ -201,7 +203,7 @@ KeywordStatus clang::getKeywordStatus(const LangOptions 
&LangOpts,
 }
 
 static bool IsKeywordInCpp(unsigned Flags) {
-  return (Flags & (KEYCXX | KEYCXX11 | KEYCXX20 | KEYCONTRACTS | BOOLSUPPORT |
+  return (Flags & (KEYCXX | KEYCXX11 | KEYCXX20 | KEYCXX26 | BOOLSUPPORT |
                    WCHARSUPPORT | CHAR8SUPPORT)) != 0;
 }
 
@@ -849,6 +851,9 @@ IdentifierTable::getFutureCompatDiagKind(const 
IdentifierInfo &II,
     if (((Flags & KEYCXX20) == KEYCXX20) ||
         ((Flags & CHAR8SUPPORT) == CHAR8SUPPORT))
       return diag::warn_cxx20_keyword;
+
+    if ((Flags & KEYCXX26) == KEYCXX26)
+      return diag::warn_cxx26_keyword;
   } else {
     if ((Flags & KEYC99) == KEYC99)
       return diag::warn_c99_keyword;
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 6e3fd2d5a4fc0..270d0e98c1f11 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2192,7 +2192,7 @@ Parser::DeclGroupPtrTy 
Parser::ParseDeclGroup(ParsingDeclSpec &DS,
     // account for the implicit template parameter list induced by the 
template.
     if (!TemplateInfo.TemplateParams && D.getInventedTemplateParameterList())
       ++CurTemplateDepthTracker;
-    ParseFunctionDeclaratorTail(D);
+    ParseFunctionContractSpecifiersAndConstraints(D);
   }
 
   // Save late-parsed attributes for now; they need to be parsed in the
@@ -2442,7 +2442,7 @@ Parser::DeclGroupPtrTy 
Parser::ParseDeclGroup(ParsingDeclSpec &DS,
       //             declarator initializer[opt]
       //        declarator requires-clause
       if (Tok.is(tok::kw_requires) || getContractSpecifierKind())
-        ParseFunctionDeclaratorTail(D);
+        ParseFunctionContractSpecifiersAndConstraints(D);
       Decl *ThisDecl = ParseDeclarationAfterDeclarator(D, TemplateInfo);
       D.complete(ThisDecl);
       if (ThisDecl)
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index b60dd203635df..87b33761abc0d 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -2614,7 +2614,7 @@ bool Parser::ParseCXXMemberDeclaratorBeforeInitializer(
       if (DeclaratorInfo.getTemplateParameterLists().empty() &&
           DeclaratorInfo.getInventedTemplateParameterList())
         ++CurTemplateDepthTracker;
-      ParseFunctionDeclaratorTail(DeclaratorInfo);
+      ParseFunctionContractSpecifiersAndConstraints(DeclaratorInfo);
     }
   }
 
@@ -4172,49 +4172,17 @@ TypeResult Parser::ParseTrailingReturnType(SourceRange 
&Range,
                                    : DeclaratorContext::TrailingReturn);
 }
 
-void Parser::ParseTrailingRequiresClauseWithScope(Declarator &D) {
-  assert(Tok.is(tok::kw_requires) && "expected requires");
-
-  // C++23 [basic.scope.namespace]p1:
-  //   For each non-friend redeclaration or specialization whose target scope
-  //   is or is contained by the scope, the portion after the declarator-id,
-  //   class-head-name, or enum-head-name is also included in the scope.
-  // C++23 [basic.scope.class]p1:
-  //   For each non-friend redeclaration or specialization whose target scope
-  //   is or is contained by the scope, the portion after the declarator-id,
-  //   class-head-name, or enum-head-name is also included in the scope.
-  //
-  // FIXME: We should really be calling ParseTrailingRequiresClause in
-  // ParseDirectDeclarator, when we are already in the declarator scope.
-  // This would also correctly suppress access checks for specializations
-  // and explicit instantiations, which we currently do not do.
-  CXXScopeSpec &SS = D.getCXXScopeSpec();
-  DeclaratorScopeObj DeclScopeObj(*this, SS);
-  if (SS.isValid() && Actions.ShouldEnterDeclaratorScope(getCurScope(), SS))
-    DeclScopeObj.EnterDeclaratorScope();
-
-  ParseScope ParamScope(this, Scope::DeclScope |
-                                  Scope::FunctionDeclarationScope |
-                                  Scope::FunctionPrototypeScope);
-
-  ParseTrailingRequiresClause(D);
-}
-
 void Parser::ParseTrailingRequiresClause(Declarator &D) {
+  // The caller need to set up the scope for the parameters and init 'this'
+  // scope fro declarator if relevant.
+
   assert(Tok.is(tok::kw_requires) && "expected requires");
   assert(
       getCurScope()->isFunctionPrototypeScope() &&
       "trailing requires-clause must be parsed in a function prototype scope");
 
   SourceLocation RequiresKWLoc = ConsumeToken();
-
-  ExprResult TrailingRequiresClause;
-  Actions.ActOnStartTrailingRequiresClause(getCurScope(), D);
-
-  std::optional<Sema::CXXThisScopeRAII> ThisScope;
-  InitCXXThisScopeForDeclaratorIfRelevant(D, D.getDeclSpec(), ThisScope);
-
-  TrailingRequiresClause =
+  ExprResult TrailingRequiresClause =
       ParseConstraintLogicalOrExpression(/*IsTrailingRequiresClause=*/true);
 
   TrailingRequiresClause =
@@ -4256,19 +4224,29 @@ void Parser::ParseTrailingRequiresClause(Declarator &D) 
{
 
 std::optional<Parser::ContractSpecifierKind>
 Parser::getContractSpecifierKind() {
-  if (!getLangOpts().Contracts || Tok.isNot(tok::identifier))
+  if (!getLangOpts().CPlusPlus || Tok.isNot(tok::identifier))
     return std::nullopt;
 
-  IdentifierInfo *II = Tok.getIdentifierInfo();
-  if (II->isStr("pre"))
+  if (!Ident_pre) {
+    Ident_pre = &PP.getIdentifierTable().get("pre");
+    Ident_post = &PP.getIdentifierTable().get("post");
+  }
+
+  const IdentifierInfo *II = Tok.getIdentifierInfo();
+  if (II == Ident_pre)
     return ContractSpecifierKind::Pre;
-  if (II->isStr("post"))
+  if (II == Ident_post)
     return ContractSpecifierKind::Post;
   return std::nullopt;
 }
 
 void Parser::ParseContractSpecifiers(Declarator &D) {
-  assert(getLangOpts().Contracts && "contracts are not enabled");
+  assert(getContractSpecifierKind() && "expected a contract specifier");
+
+  if (!getLangOpts().CPlusPlus26)
+    Diag(Tok, diag::err_contracts_require_cxx26);
+  else if (!getLangOpts().Contracts)
+    Diag(Tok, diag::err_contracts_disabled);
 
   const bool IsFunction = D.isDeclarationOfFunction() &&
                           (D.isFunctionDeclarationContext() ||
@@ -4319,42 +4297,27 @@ void Parser::ParseContractSpecifiers(Declarator &D) {
   }
 }
 
-// Parse the function tail where the require clause must appear first
-// and the contracts later.
-void Parser::ParseFunctionDeclaratorTail(Declarator &D,
-                                         bool ParametersAlreadyInScope) {
+void Parser::ParseFunctionContractSpecifiersAndConstraints(
+    Declarator &D, bool ParametersAlreadyInScope) {
   assert((Tok.is(tok::kw_requires) || getContractSpecifierKind()) &&
-         "expected a function declarator tail");
+         "expected a trailing requires-clause or contract specifier");
 
-  // TODO: We can consider merging ParseTrailingRequiresClauseWithScope into
-  // this function.
-  if (!ParametersAlreadyInScope && Tok.is(tok::kw_requires)) {
-    ParseTrailingRequiresClauseWithScope(D);
-    if (!getContractSpecifierKind())
-      return;
-  }
+  auto ParseSpecifiersAndConstraints = [&] {
+    if (!ParametersAlreadyInScope)
+      
Actions.ActOnStartTrailingRequiresClauseOrContractSpecifier(getCurScope(),
+                                                                  D);
+
+    const DeclSpec *MethodDS = &D.getDeclSpec();
+    if (D.isFunctionDeclarator() && D.getFunctionTypeInfo().MethodQualifiers)
+      MethodDS = D.getFunctionTypeInfo().MethodQualifiers;
+    std::optional<Sema::CXXThisScopeRAII> ThisScope;
+    InitCXXThisScopeForDeclaratorIfRelevant(D, *MethodDS, ThisScope);
 
-  auto ParseTail = [&] {
-    bool ParametersInScope = ParametersAlreadyInScope;
     if (Tok.is(tok::kw_requires)) {
       ParseTrailingRequiresClause(D);
-      ParametersInScope = true;
     }
 
     while (getContractSpecifierKind()) {
-      if (!ParametersInScope) {
-        // FIXME: ActOnStartTrailingRequiresClause is not actually deal with
-        // require clause. It is actually adds the parameters to the scope
-        // chains. It needs a better name.
-        Actions.ActOnStartTrailingRequiresClause(getCurScope(), D);
-        ParametersInScope = true;
-      }
-
-      const DeclSpec *MethodDS = &D.getDeclSpec();
-      if (D.isFunctionDeclarator() && D.getFunctionTypeInfo().MethodQualifiers)
-        MethodDS = D.getFunctionTypeInfo().MethodQualifiers;
-      std::optional<Sema::CXXThisScopeRAII> ThisScope;
-      InitCXXThisScopeForDeclaratorIfRelevant(D, *MethodDS, ThisScope);
       ParseContractSpecifiers(D);
 
       // If we meet requires after contracts, emit a nice diagnostics and
@@ -4367,10 +4330,23 @@ void Parser::ParseFunctionDeclaratorTail(Declarator &D,
   };
 
   if (ParametersAlreadyInScope) {
-    ParseTail();
+    ParseSpecifiersAndConstraints();
     return;
   }
 
+  // C++23 [basic.scope.namespace]p1:
+  //   For each non-friend redeclaration or specialization whose target scope
+  //   is or is contained by the scope, the portion after the declarator-id,
+  //   class-head-name, or enum-head-name is also included in the scope.
+  // C++23 [basic.scope.class]p1:
+  //   For each non-friend redeclaration or specialization whose target scope
+  //   is or is contained by the scope, the portion after the declarator-id,
+  //   class-head-name, or enum-head-name is also included in the scope.
+  //
+  // FIXME: We should really parse these in ParseDirectDeclarator, when we are
+  // already in the declarator scope. This would also correctly suppress access
+  // checks for specializations and explicit instantiations, which we currently
+  // do not do.
   CXXScopeSpec &SS = D.getCXXScopeSpec();
   DeclaratorScopeObj DeclScopeObj(*this, SS);
   if (SS.isValid() && Actions.ShouldEnterDeclaratorScope(getCurScope(), SS))
@@ -4379,7 +4355,7 @@ void Parser::ParseFunctionDeclaratorTail(Declarator &D,
   ParseScope ParamScope(this, Scope::DeclScope |
                                   Scope::FunctionDeclarationScope |
                                   Scope::FunctionPrototypeScope);
-  ParseTail();
+  ParseSpecifiersAndConstraints();
 }
 
 Sema::ParsingClassState Parser::PushParsingClass(Decl *ClassDecl,
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index ad566c25a438d..f80fdd6256ccb 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1449,7 +1449,8 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
 
     if ((HasParentheses && Tok.is(tok::kw_requires)) ||
         getContractSpecifierKind())
-      ParseFunctionDeclaratorTail(D, /*ParametersAlreadyInScope=*/true);
+      ParseFunctionContractSpecifiersAndConstraints(
+          D, /*ParametersAlreadyInScope=*/true);
   }
 
   // Emit a warning if we see a CUDA host/device/global attribute
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 898e6bf3cf120..feb228c1af984 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2469,6 +2469,8 @@ StmtResult Parser::ParseBreakStatement() {
 
 StmtResult Parser::ParseContractAssertStatement() {
   assert(Tok.is(tok::kw_contract_assert) && "expected contract_assert");
+  if (!getLangOpts().Contracts)
+    Diag(Tok, diag::err_contracts_disabled);
   SourceLocation ContractAssertLoc = ConsumeToken();
 
   ParsedAttributes Attrs(AttrFactory);
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index af40d59e51bd4..ea733932f037b 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -521,6 +521,8 @@ void Parser::Initialize() {
   Ident_sealed = nullptr;
   Ident_abstract = nullptr;
   Ident_override = nullptr;
+  Ident_pre = nullptr;
+  Ident_post = nullptr;
   Ident_GNU_final = nullptr;
 
   Ident_super = &PP.getIdentifierTable().get("super");
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 3a33e77efb85b..631f47859cfe1 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -4206,7 +4206,8 @@ void Sema::ActOnStartCXXInClassMemberInitializer() {
   PushFunctionScope();
 }
 
-void Sema::ActOnStartTrailingRequiresClause(Scope *S, Declarator &D) {
+void Sema::ActOnStartTrailingRequiresClauseOrContractSpecifier(Scope *S,
+                                                               Declarator &D) {
   if (!D.isFunctionDeclarator())
     return;
   auto &FTI = D.getFunctionTypeInfo();
diff --git a/clang/test/Lexer/keywords_test.cpp 
b/clang/test/Lexer/keywords_test.cpp
index 62a2aef8ff66c..987baa46610c6 100644
--- a/clang/test/Lexer/keywords_test.cpp
+++ b/clang/test/Lexer/keywords_test.cpp
@@ -1,6 +1,7 @@
 // RUN: %clang_cc1 -std=c++03 -fsyntax-only %s
 // RUN: %clang_cc1 -std=c++11 -DCXX11 -fsyntax-only %s
 // RUN: %clang_cc1 -std=c++20 -DCXX11 -DCXX20 -fsyntax-only %s
+// RUN: %clang_cc1 -std=c++26 -DCXX11 -DCXX20 -DCXX26 -fsyntax-only %s
 // RUN: %clang_cc1 -std=c++03 -fdeclspec -DDECLSPEC -fsyntax-only %s
 // RUN: %clang_cc1 -std=c++03 -fms-extensions -DDECLSPEC -fsyntax-only %s
 // RUN: %clang_cc1 -std=c++03 -fborland-extensions -DDECLSPEC -fsyntax-only %s
@@ -15,7 +16,7 @@
 // RUN: %clang -std=c++03 -target i686-windows-msvc -DMS -fno-declspec 
-fsyntax-only %s
 // RUN: %clang -std=c++03 -target x86_64-scei-ps4 -fno-declspec -fsyntax-only 
%s
 
-// RUN: %clang_cc1 -std=c++98 -DFutureKeyword -fsyntax-only -Wc++11-compat 
-Wc++20-compat -verify=cxx98 %s
+// RUN: %clang_cc1 -std=c++98 -DFutureKeyword -fsyntax-only -Wc++11-compat 
-Wc++20-compat -Wc++2c-compat -verify=cxx98 %s
 
 #define IS_KEYWORD(NAME) _Static_assert(!__is_identifier(NAME), #NAME)
 #define NOT_KEYWORD(NAME) _Static_assert(__is_identifier(NAME), #NAME)
@@ -27,6 +28,12 @@
 #define CXX20_KEYWORD(NAME)  NOT_KEYWORD(NAME)
 #endif
 
+#if defined(CXX26)
+#define CXX26_KEYWORD(NAME)  IS_KEYWORD(NAME)
+#else
+#define CXX26_KEYWORD(NAME)  NOT_KEYWORD(NAME)
+#endif
+
 #ifdef DECLSPEC
 #define DECLSPEC_KEYWORD(NAME)  IS_KEYWORD(NAME)
 #else
@@ -71,6 +78,9 @@ CXX20_KEYWORD(co_await);
 CXX20_KEYWORD(co_return);
 CXX20_KEYWORD(co_yield);
 
+// C++26 keywords
+CXX26_KEYWORD(contract_assert);
+
 // __declspec extension
 DECLSPEC_KEYWORD(__declspec);
 
@@ -100,5 +110,6 @@ int constinit; // cxx98-warning {{'constinit' is a keyword 
in C++20}}
 int consteval; // cxx98-warning {{'consteval' is a keyword in C++20}}
 int requires; // cxx98-warning {{'requires' is a keyword in C++20}}
 int concept; // cxx98-warning {{'concept' is a keyword in C++20}}
+int contract_assert; // cxx98-warning {{'contract_assert' is a keyword in 
C++26}}
 
 #endif
diff --git a/clang/test/Parser/cxx26-contracts-keyword.cpp 
b/clang/test/Parser/cxx26-contracts-keyword.cpp
index a6f6b22503564..d7539ae2de408 100644
--- a/clang/test/Parser/cxx26-contracts-keyword.cpp
+++ b/clang/test/Parser/cxx26-contracts-keyword.cpp
@@ -1,15 +1,21 @@
-// RUN: %clang_cc1 -std=c++2c -fcontracts -DENABLED=1 -fsyntax-only 
-verify=enabled %s
-// RUN: %clang_cc1 -std=c++2c -fno-contracts -DENABLED=0 -fsyntax-only 
-verify=disabled %s
-
-// disabled-no-diagnostics
+// RUN: %clang_cc1 -std=c++2c -fcontracts -DCXX26 -fsyntax-only 
-verify=enabled,expected %s
+// RUN: %clang_cc1 -std=c++2c -fno-contracts -DCXX26 -fsyntax-only 
-verify=disabled,expected %s
+// RUN: %clang_cc1 -std=c++23 -fcontracts -fsyntax-only 
-verify=precxx26,expected %s
 
 #ifdef __cplusplus
+#ifdef CXX26
 void test() {
-#if ENABLED
-  int contract_assert; // enabled-error {{expected unqualified-id}}
+  int contract_assert; // expected-error {{expected unqualified-id}}
+}
+
+void assertion() {
+  contract_assert(true); // disabled-error {{contracts support is disabled; 
pass '-fcontracts' to enable it}}
+}
+
+int specifier(int value) pre(value > 0);
+// disabled-error@-1 {{contracts support is disabled; pass '-fcontracts' to 
enable it}}
 #else
-  int contract_assert;
-  ++contract_assert;
+int old_standard(int value) pre(value > 0);
+// precxx26-error@-1 {{contracts are a C++26 feature}}
 #endif
-}
 #endif

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

Reply via email to