Author: Sirraide
Date: 2025-12-11T05:54:09+01:00
New Revision: 71bfdd13040328bc83b520d09eee847fd2b7f82c

URL: 
https://github.com/llvm/llvm-project/commit/71bfdd13040328bc83b520d09eee847fd2b7f82c
DIFF: 
https://github.com/llvm/llvm-project/commit/71bfdd13040328bc83b520d09eee847fd2b7f82c.diff

LOG: [Clang] Add support for the C `_Defer` TS (#162848)

This implements WG14 N3734 
(https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3734.pdf),
aka `_Defer`; it is currently only supported in C if `-fdefer-ts` is passed.

Added: 
    clang/lib/Headers/stddefer.h
    clang/test/AST/ast-dump-defer-ts.c
    clang/test/AST/ast-print-defer-ts.c
    clang/test/CodeGen/defer-ts-musttail.c
    clang/test/CodeGen/defer-ts-nested-cleanups.c
    clang/test/CodeGen/defer-ts-seh.c
    clang/test/CodeGen/defer-ts.c
    clang/test/Lexer/defer-keyword.cpp
    clang/test/Parser/defer-ts.c
    clang/test/Parser/defer-ts.cpp
    clang/test/Preprocessor/defer-ts.c
    clang/test/Sema/defer-ts-seh.c
    clang/test/Sema/defer-ts-sjlj.c
    clang/test/Sema/defer-ts.c

Modified: 
    clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp
    clang/docs/ReleaseNotes.rst
    clang/include/clang/AST/RecursiveASTVisitor.h
    clang/include/clang/AST/Stmt.h
    clang/include/clang/Basic/DiagnosticParseKinds.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Basic/IdentifierTable.h
    clang/include/clang/Basic/LangOptions.def
    clang/include/clang/Basic/StmtNodes.td
    clang/include/clang/Basic/TokenKinds.def
    clang/include/clang/Options/Options.td
    clang/include/clang/Parse/Parser.h
    clang/include/clang/Sema/Sema.h
    clang/include/clang/Serialization/ASTBitCodes.h
    clang/lib/AST/Stmt.cpp
    clang/lib/AST/StmtPrinter.cpp
    clang/lib/AST/StmtProfile.cpp
    clang/lib/Basic/IdentifierTable.cpp
    clang/lib/CodeGen/CGStmt.cpp
    clang/lib/CodeGen/CodeGenFunction.h
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/lib/Frontend/InitPreprocessor.cpp
    clang/lib/Parse/ParseStmt.cpp
    clang/lib/Sema/JumpDiagnostics.cpp
    clang/lib/Sema/SemaExceptionSpec.cpp
    clang/lib/Sema/SemaExpr.cpp
    clang/lib/Sema/SemaStmt.cpp
    clang/lib/Sema/TreeTransform.h
    clang/lib/Serialization/ASTReaderStmt.cpp
    clang/lib/Serialization/ASTWriterStmt.cpp
    clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
    clang/tools/libclang/CXCursor.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp
index 4f33670a8500a..6618341296aaf 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp
@@ -237,6 +237,12 @@ static bool isIdenticalStmt(const ASTContext &Ctx, const 
Stmt *Stmt1,
       return false;
     return true;
   }
+  case Stmt::DeferStmtClass: {
+    const auto *DefStmt1 = cast<DeferStmt>(Stmt1);
+    const auto *DefStmt2 = cast<DeferStmt>(Stmt2);
+    return isIdenticalStmt(Ctx, DefStmt1->getBody(), DefStmt2->getBody(),
+                           IgnoreSideEffects);
+  }
   case Stmt::CompoundStmtClass: {
     const auto *CompStmt1 = cast<CompoundStmt>(Stmt1);
     const auto *CompStmt2 = cast<CompoundStmt>(Stmt2);

diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 005e858821804..899a4ee0dee0e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -208,6 +208,11 @@ Resolutions to C++ Defect Reports
 C Language Changes
 ------------------
 
+- Implemented the ``defer`` draft Technical Specification
+  (`WG14 N3734 
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3734.pdf>`_); it is enabled 
in C mode by
+  passing ``-fdefer-ts``. Note, the details of this feature are subject to 
change given that the Technical
+  Specification is not yet ratified.
+
 C2y Feature Support
 ^^^^^^^^^^^^^^^^^^^
 - No longer triggering ``-Wstatic-in-inline`` in C2y mode; use of a static

diff  --git a/clang/include/clang/AST/RecursiveASTVisitor.h 
b/clang/include/clang/AST/RecursiveASTVisitor.h
index 8f427427d71ed..c3ac310bf5402 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2561,6 +2561,7 @@ DEF_TRAVERSE_STMT(DefaultStmt, {})
 DEF_TRAVERSE_STMT(DoStmt, {})
 DEF_TRAVERSE_STMT(ForStmt, {})
 DEF_TRAVERSE_STMT(GotoStmt, {})
+DEF_TRAVERSE_STMT(DeferStmt, {})
 DEF_TRAVERSE_STMT(IfStmt, {})
 DEF_TRAVERSE_STMT(IndirectGotoStmt, {})
 DEF_TRAVERSE_STMT(LabelStmt, {})

diff  --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index e1cca34d2212c..d56de08eaf279 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -317,6 +317,16 @@ class alignas(void *) Stmt {
     SourceLocation KeywordLoc;
   };
 
+  class DeferStmtBitfields {
+    friend class DeferStmt;
+
+    LLVM_PREFERRED_TYPE(StmtBitfields)
+    unsigned : NumStmtBits;
+
+    /// The location of the "defer".
+    SourceLocation DeferLoc;
+  };
+
   //===--- Expression bitfields classes ---===//
 
   class ExprBitfields {
@@ -1318,6 +1328,7 @@ class alignas(void *) Stmt {
     LoopControlStmtBitfields LoopControlStmtBits;
     ReturnStmtBitfields ReturnStmtBits;
     SwitchCaseBitfields SwitchCaseBits;
+    DeferStmtBitfields DeferStmtBits;
 
     // Expressions
     ExprBitfields ExprBits;
@@ -3211,6 +3222,47 @@ class ReturnStmt final
   }
 };
 
+/// DeferStmt - This represents a deferred statement.
+class DeferStmt : public Stmt {
+  friend class ASTStmtReader;
+
+  /// The deferred statement.
+  Stmt *Body;
+
+  DeferStmt(EmptyShell Empty);
+  DeferStmt(SourceLocation DeferLoc, Stmt *Body);
+
+public:
+  static DeferStmt *CreateEmpty(ASTContext &Context, EmptyShell Empty);
+  static DeferStmt *Create(ASTContext &Context, SourceLocation DeferLoc,
+                           Stmt *Body);
+
+  SourceLocation getDeferLoc() const { return DeferStmtBits.DeferLoc; }
+  void setDeferLoc(SourceLocation DeferLoc) {
+    DeferStmtBits.DeferLoc = DeferLoc;
+  }
+
+  Stmt *getBody() { return Body; }
+  const Stmt *getBody() const { return Body; }
+  void setBody(Stmt *S) {
+    assert(S && "defer body must not be null");
+    Body = S;
+  }
+
+  SourceLocation getBeginLoc() const { return getDeferLoc(); }
+  SourceLocation getEndLoc() const { return Body->getEndLoc(); }
+
+  child_range children() { return child_range(&Body, &Body + 1); }
+
+  const_child_range children() const {
+    return const_child_range(&Body, &Body + 1);
+  }
+
+  static bool classof(const Stmt *S) {
+    return S->getStmtClass() == DeferStmtClass;
+  }
+};
+
 /// AsmStmt is the base class for GCCAsmStmt and MSAsmStmt.
 class AsmStmt : public Stmt {
 protected:

diff  --git a/clang/include/clang/Basic/DiagnosticParseKinds.td 
b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 9401377002223..442a90ec2472d 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -350,6 +350,8 @@ def err_address_of_label_outside_fn : Error<
   "use of address-of-label extension outside of a function body">;
 def err_asm_operand_wide_string_literal : Error<
   "cannot use %select{unicode|wide}0 string literal in 'asm'">;
+def err_defer_ts_labeled_stmt : Error<
+  "substatement of defer must not be a label">;
 
 def err_asm_expected_string : Error<
   "expected string literal %select{or parenthesized constant expression |}0in 
'asm'">;

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 28803829f387d..c79c208a07acd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6848,6 +6848,7 @@ def note_protected_by_objc_weak_init : Note<
   "jump bypasses initialization of __weak variable">;
 def note_protected_by_non_trivial_c_struct_init : Note<
   "jump bypasses initialization of variable of non-trivial C struct type">;
+def note_protected_by_defer_stmt : Note<"jump bypasses defer statement">;
 def note_enters_block_captures_cxx_obj : Note<
   "jump enters lifetime of block which captures a destructible C++ object">;
 def note_enters_block_captures_strong : Note<
@@ -6861,6 +6862,7 @@ def note_enters_compound_literal_scope : Note<
   "jump enters lifetime of a compound literal that is non-trivial to 
destruct">;
 def note_enters_statement_expression : Note<
   "jump enters a statement expression">;
+def note_enters_defer_stmt : Note<"jump enters a defer statement">;
 
 def note_exits_cleanup : Note<
   "jump exits scope of variable with __attribute__((cleanup))">;
@@ -6906,6 +6908,16 @@ def note_exits_block_captures_non_trivial_c_struct : 
Note<
   "to destroy">;
 def note_exits_compound_literal_scope : Note<
   "jump exits lifetime of a compound literal that is non-trivial to destruct">;
+def note_exits_defer_stmt : Note<"jump exits a defer statement">;
+def err_jump_out_of_defer_stmt : Error<
+  "cannot %enum_select<DeferJumpKind>{"
+  "%Break{break out of a}|"
+  "%Continue{continue loop outside of enclosing}|"
+  "%Return{return from a}|"
+  "%SEHLeave{__leave a}"
+  "}0 defer statement">;
+def err_defer_invalid_sjlj : Error<
+  "cannot use %0 inside a defer statement">;
 
 def err_func_returning_qualified_void : ExtWarn<
   "function cannot return qualified void type %0">,
@@ -11020,6 +11032,8 @@ def err_switch_explicit_conversion : Error<
 def err_switch_incomplete_class_type : Error<
   "switch condition has incomplete class type %0">;
 
+// TODO: It ought to be possible to refactor these to be a single warning that
+// uses %enum_select.
 def warn_empty_if_body : Warning<
   "if statement has empty body">, InGroup<EmptyBody>;
 def warn_empty_for_body : Warning<
@@ -11030,6 +11044,8 @@ def warn_empty_while_body : Warning<
   "while loop has empty body">, InGroup<EmptyBody>;
 def warn_empty_switch_body : Warning<
   "switch statement has empty body">, InGroup<EmptyBody>;
+def warn_empty_defer_body : Warning<
+  "defer statement has empty body">, InGroup<EmptyBody>;
 def note_empty_body_on_separate_line : Note<
   "put the semicolon on a separate line to silence this warning">;
 

diff  --git a/clang/include/clang/Basic/IdentifierTable.h 
b/clang/include/clang/Basic/IdentifierTable.h
index b27492d19a65b..043c184323876 100644
--- a/clang/include/clang/Basic/IdentifierTable.h
+++ b/clang/include/clang/Basic/IdentifierTable.h
@@ -77,7 +77,8 @@ enum TokenKey : unsigned {
   KEYNOZOS = 0x4000000,
   KEYHLSL = 0x8000000,
   KEYFIXEDPOINT = 0x10000000,
-  KEYMAX = KEYFIXEDPOINT, // The maximum key
+  KEYDEFERTS = 0x20000000,
+  KEYMAX = KEYDEFERTS, // The maximum key
   KEYALLCXX = KEYCXX | KEYCXX11 | KEYCXX20,
   KEYALL = (KEYMAX | (KEYMAX - 1)) & ~KEYNOMS18 & ~KEYNOOPENCL &
            ~KEYNOZOS // KEYNOMS18, KEYNOOPENCL, KEYNOZOS are excluded.

diff  --git a/clang/include/clang/Basic/LangOptions.def 
b/clang/include/clang/Basic/LangOptions.def
index e515c0cee79eb..093d2709e59f9 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -194,6 +194,7 @@ LANGOPT(NoSignedZero      , 1, 0, Benign, "Permit Floating 
Point optimization wi
 LANGOPT(AllowRecip        , 1, 0, Benign, "Permit Floating Point reciprocal")
 LANGOPT(ApproxFunc        , 1, 0, Benign, "Permit Floating Point 
approximation")
 LANGOPT(NamedLoops        , 1, 0, Benign, "Permit named break/continue")
+LANGOPT(DeferTS           , 1, 0, Benign, "C '_Defer' Technical Specification")
 
 ENUM_LANGOPT(ComplexRange, ComplexRangeKind, 3, CX_None, NotCompatible, 
"Enable use of range reduction for complex arithmetics.")
 

diff  --git a/clang/include/clang/Basic/StmtNodes.td 
b/clang/include/clang/Basic/StmtNodes.td
index bf3686bb372d5..2d740425a3cb0 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -17,6 +17,7 @@ def ForStmt : StmtNode<Stmt>;
 def GotoStmt : StmtNode<Stmt>;
 def IndirectGotoStmt : StmtNode<Stmt>;
 def ReturnStmt : StmtNode<Stmt>;
+def DeferStmt : StmtNode<Stmt>;
 def DeclStmt  : StmtNode<Stmt>;
 def SwitchCase : StmtNode<Stmt, 1>;
 def CaseStmt : StmtNode<SwitchCase>;

diff  --git a/clang/include/clang/Basic/TokenKinds.def 
b/clang/include/clang/Basic/TokenKinds.def
index 564d6010181cc..8240d395d3e8f 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -293,6 +293,7 @@ PUNCTUATOR(greatergreatergreater, ">>>")
 //   CHAR8SUPPORT - This is a keyword if 'char8_t' is a built-in type
 //   KEYFIXEDPOINT - This is a keyword according to the N1169 fixed point
 //                   extension.
+//   KEYDEFERTS - This is a keyword if the C '_Defer' TS is enabled
 //   KEYZOS - This is a keyword in C/C++ on z/OS
 //
 KEYWORD(auto                        , KEYALL)
@@ -441,6 +442,9 @@ KEYWORD(_Float16                    , KEYALL)
 C23_KEYWORD(typeof                  , KEYGNU)
 C23_KEYWORD(typeof_unqual           , 0)
 
+// '_Defer' TS
+KEYWORD(_Defer                      , KEYDEFERTS)
+
 // ISO/IEC JTC1 SC22 WG14 N1169 Extension
 KEYWORD(_Accum                      , KEYFIXEDPOINT)
 KEYWORD(_Fract                      , KEYFIXEDPOINT)

diff  --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index e55146f0c7823..e704d9e6275ec 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -1671,6 +1671,14 @@ defm named_loops
           PosFlag<SetTrue, [], [CC1Option], "Enable support for named loops">,
           NegFlag<SetFalse>>;
 
+// C '_Defer' TS
+defm defer_ts : BoolFOption<"defer-ts",
+  LangOpts<"DeferTS">, DefaultFalse,
+  PosFlag<SetTrue, [], [ClangOption, CC1Option],
+          "Enable support for the C '_Defer' Technical Specification">,
+  NegFlag<SetFalse>>,
+  ShouldParseIf<!strconcat("!", cplusplus.KeyPath)>;
+
 // 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 58eb1c0a7c114..47eedf216a44b 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -7500,6 +7500,16 @@ class Parser : public CodeCompletionHandler {
 
   StmtResult ParseBreakOrContinueStatement(bool IsContinue);
 
+  /// ParseDeferStatement
+  /// \verbatim
+  ///       defer-statement:
+  ///         '_Defer' deferred-block
+  ///
+  ///       deferred-block:
+  ///         unlabeled-statement
+  /// \endverbatim
+  StmtResult ParseDeferStatement(SourceLocation *TrailingElseLoc);
+
   StmtResult ParsePragmaLoopHint(StmtVector &Stmts, ParsedStmtContext StmtCtx,
                                  SourceLocation *TrailingElseLoc,
                                  ParsedAttributes &Attrs,

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index d14b5dc5ffaa4..97b6bb3d1b3a8 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10935,6 +10935,10 @@ class Sema final : public SemaBase {
   /// Stack of active SEH __finally scopes.  Can be empty.
   SmallVector<Scope *, 2> CurrentSEHFinally;
 
+  /// Stack of '_Defer' statements that are currently being parsed, as well
+  /// as the locations of their '_Defer' keywords. Can be empty.
+  SmallVector<std::pair<Scope *, SourceLocation>, 2> CurrentDefer;
+
   StmtResult ActOnExprStmt(ExprResult Arg, bool DiscardedValue = true);
   StmtResult ActOnExprStmtError();
 
@@ -11081,6 +11085,10 @@ class Sema final : public SemaBase {
   StmtResult ActOnBreakStmt(SourceLocation BreakLoc, Scope *CurScope,
                             LabelDecl *Label, SourceLocation LabelLoc);
 
+  void ActOnStartOfDeferStmt(SourceLocation DeferLoc, Scope *CurScope);
+  void ActOnDeferStmtError(Scope *CurScope);
+  StmtResult ActOnEndOfDeferStmt(Stmt *Body, Scope *CurScope);
+
   struct NamedReturnInfo {
     const VarDecl *Candidate;
 

diff  --git a/clang/include/clang/Serialization/ASTBitCodes.h 
b/clang/include/clang/Serialization/ASTBitCodes.h
index d7d429eacd67a..b48f02c601889 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -2061,6 +2061,7 @@ enum StmtCode {
   // HLSL Constructs
   EXPR_HLSL_OUT_ARG,
 
+  STMT_DEFER,
 };
 
 /// The kinds of designators that can occur in a

diff  --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp
index 11ece494490de..10aacd75a650a 100644
--- a/clang/lib/AST/Stmt.cpp
+++ b/clang/lib/AST/Stmt.cpp
@@ -1499,3 +1499,19 @@ const Stmt *LoopControlStmt::getNamedLoopOrSwitch() 
const {
     return nullptr;
   return getLabelDecl()->getStmt()->getInnermostLabeledStmt();
 }
+
+DeferStmt::DeferStmt(EmptyShell Empty) : Stmt(DeferStmtClass, Empty) {}
+DeferStmt::DeferStmt(SourceLocation DeferLoc, Stmt *Body)
+    : Stmt(DeferStmtClass) {
+  setDeferLoc(DeferLoc);
+  setBody(Body);
+}
+
+DeferStmt *DeferStmt::CreateEmpty(ASTContext &Context, EmptyShell Empty) {
+  return new (Context) DeferStmt(Empty);
+}
+
+DeferStmt *DeferStmt::Create(ASTContext &Context, SourceLocation DeferLoc,
+                             Stmt *Body) {
+  return new (Context) DeferStmt(DeferLoc, Body);
+}

diff  --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index ff8ca01ec5477..9bc5ee0c7f40e 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -491,6 +491,11 @@ void StmtPrinter::VisitBreakStmt(BreakStmt *Node) {
   if (Policy.IncludeNewlines) OS << NL;
 }
 
+void StmtPrinter::VisitDeferStmt(DeferStmt *Node) {
+  Indent() << "_Defer";
+  PrintControlledStmt(Node->getBody());
+}
+
 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 4a8c638c85331..b6395a17547f7 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -323,6 +323,8 @@ void StmtProfiler::VisitReturnStmt(const ReturnStmt *S) {
   VisitStmt(S);
 }
 
+void StmtProfiler::VisitDeferStmt(const DeferStmt *S) { VisitStmt(S); }
+
 void StmtProfiler::VisitGCCAsmStmt(const GCCAsmStmt *S) {
   VisitStmt(S);
   ID.AddBoolean(S->isVolatile());

diff  --git a/clang/lib/Basic/IdentifierTable.cpp 
b/clang/lib/Basic/IdentifierTable.cpp
index d1c959b9687c4..9b4019834c4be 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -164,6 +164,8 @@ static KeywordStatus getKeywordStatusHelper(const 
LangOptions &LangOpts,
     return KS_Unknown;
   case KEYFIXEDPOINT:
     return LangOpts.FixedPoint ? KS_Enabled : KS_Disabled;
+  case KEYDEFERTS:
+    return LangOpts.DeferTS ? KS_Enabled : KS_Disabled;
   default:
     llvm_unreachable("Unknown KeywordStatus flag");
   }

diff  --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 36be3295950b8..c050fd41ac0e9 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -114,6 +114,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S, 
ArrayRef<const Attr *> Attrs) {
   case Stmt::ContinueStmtClass:
   case Stmt::DefaultStmtClass:
   case Stmt::CaseStmtClass:
+  case Stmt::DeferStmtClass:
   case Stmt::SEHLeaveStmtClass:
   case Stmt::SYCLKernelCallStmtClass:
     llvm_unreachable("should have emitted these statements as simple");
@@ -539,6 +540,9 @@ bool CodeGenFunction::EmitSimpleStmt(const Stmt *S,
   case Stmt::CaseStmtClass:
     EmitCaseStmt(cast<CaseStmt>(*S), Attrs);
     break;
+  case Stmt::DeferStmtClass:
+    EmitDeferStmt(cast<DeferStmt>(*S));
+    break;
   case Stmt::SEHLeaveStmtClass:
     EmitSEHLeaveStmt(cast<SEHLeaveStmt>(*S));
     break;
@@ -2000,6 +2004,87 @@ void CodeGenFunction::EmitDefaultStmt(const DefaultStmt 
&S,
   EmitStmt(S.getSubStmt());
 }
 
+namespace {
+struct EmitDeferredStatement final : EHScopeStack::Cleanup {
+  const DeferStmt &Stmt;
+  EmitDeferredStatement(const DeferStmt *Stmt) : Stmt(*Stmt) {}
+
+  void Emit(CodeGenFunction &CGF, Flags) override {
+    // Take care that any cleanups pushed by the body of a '_Defer' statement
+    // don't clobber the current cleanup slot value.
+    //
+    // Assume we have a scope that pushes a cleanup; when that scope is exited,
+    // we need to run that cleanup; this is accomplished by emitting the 
cleanup
+    // into a separate block and then branching to that block at scope exit.
+    //
+    // Where this gets complicated is if we exit the scope in multiple 
diff erent
+    // ways; e.g. in a 'for' loop, we may exit the scope of its body by falling
+    // off the end (in which case we need to run the cleanup and then branch to
+    // the increment), or by 'break'ing out of the loop (in which case we need
+    // to run the cleanup and then branch to the loop exit block); in both 
cases
+    // we first branch to the cleanup block to run the cleanup, but the block 
we
+    // need to jump to *after* running the cleanup is 
diff erent.
+    //
+    // This is accomplished using a local integer variable called the 'cleanup
+    // slot': before branching to the cleanup block, we store a value into that
+    // slot. Then, in the cleanup block, after running the cleanup, we load the
+    // value of that variable and 'switch' on it to branch to the appropriate
+    // continuation block.
+    //
+    // The problem that arises once '_Defer' statements are involved is that 
the
+    // body of a '_Defer' is an arbitrary statement which itself can create 
more
+    // cleanups. This means we may end up overwriting the cleanup slot before 
we
+    // ever have a chance to 'switch' on it, which means that once we *do* get
+    // to the 'switch', we end up in whatever block the cleanup code happened 
to
+    // pick as the default 'switch' exit label!
+    //
+    // That is, what is normally supposed to happen is something like:
+    //
+    //   1. Store 'X' to cleanup slot.
+    //   2. Branch to cleanup block.
+    //   3. Execute cleanup.
+    //   4. Read value from cleanup slot.
+    //   5. Branch to the block associated with 'X'.
+    //
+    // But if we encounter a _Defer' statement that contains a cleanup, then
+    // what might instead happen is:
+    //
+    //   1. Store 'X' to cleanup slot.
+    //   2. Branch to cleanup block.
+    //   3. Execute cleanup; this ends up pushing another cleanup, so:
+    //       3a. Store 'Y' to cleanup slot.
+    //       3b. Run steps 2–5 recursively.
+    //   4. Read value from cleanup slot, which is now 'Y' instead of 'X'.
+    //   5. Branch to the block associated with 'Y'... which doesn't even
+    //      exist because the value 'Y' is only meaningful for the inner
+    //      cleanup. The result is we just branch 'somewhere random'.
+    //
+    // The rest of the cleanup code simply isn't prepared to handle this case
+    // because most other cleanups can't push more cleanups, and thus, emitting
+    // other cleanups generally cannot clobber the cleanup slot.
+    //
+    // To prevent this from happening, save the current cleanup slot value and
+    // restore it after emitting the '_Defer' statement.
+    llvm::Value *SavedCleanupDest = nullptr;
+    if (CGF.NormalCleanupDest.isValid())
+      SavedCleanupDest =
+          CGF.Builder.CreateLoad(CGF.NormalCleanupDest, "cleanup.dest.saved");
+
+    CGF.EmitStmt(Stmt.getBody());
+
+    if (SavedCleanupDest && CGF.HaveInsertPoint())
+      CGF.Builder.CreateStore(SavedCleanupDest, CGF.NormalCleanupDest);
+
+    // Cleanups must end with an insert point.
+    CGF.EnsureInsertPoint();
+  }
+};
+} // namespace
+
+void CodeGenFunction::EmitDeferStmt(const DeferStmt &S) {
+  EHStack.pushCleanup<EmitDeferredStatement>(NormalAndEHCleanup, &S);
+}
+
 /// CollectStatementsForCase - Given the body of a 'switch' statement and a
 /// constant value that is being switched on, see if we can dead code eliminate
 /// the body of the switch to a simple series of statements to emit.  
Basically,

diff  --git a/clang/lib/CodeGen/CodeGenFunction.h 
b/clang/lib/CodeGen/CodeGenFunction.h
index 664ee1547ccf1..10238ffd3971c 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3622,6 +3622,7 @@ class CodeGenFunction : public CodeGenTypeCache {
   void EmitDefaultStmt(const DefaultStmt &S, ArrayRef<const Attr *> Attrs);
   void EmitCaseStmt(const CaseStmt &S, ArrayRef<const Attr *> Attrs);
   void EmitCaseStmtRange(const CaseStmt &S, ArrayRef<const Attr *> Attrs);
+  void EmitDeferStmt(const DeferStmt &S);
   void EmitAsmStmt(const AsmStmt &S);
 
   const BreakContinue *GetDestForLoopControlStmt(const LoopControlStmt &S);

diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 542b70b3e9d4c..7119614634552 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7006,6 +7006,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction 
&JA,
       types::isCXX(InputType))
     CmdArgs.push_back("-fcoro-aligned-allocation");
 
+  if (Args.hasFlag(options::OPT_fdefer_ts, options::OPT_fno_defer_ts,
+                   /*Default=*/false))
+    CmdArgs.push_back("-fdefer-ts");
+
   Args.AddLastArg(CmdArgs, options::OPT_fdouble_square_bracket_attributes,
                   options::OPT_fno_double_square_bracket_attributes);
 

diff  --git a/clang/lib/Frontend/InitPreprocessor.cpp 
b/clang/lib/Frontend/InitPreprocessor.cpp
index fd464d68b5b42..8253fad9e5503 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -498,6 +498,11 @@ static void InitializeStandardPredefinedMacros(const 
TargetInfo &TI,
   Builder.defineMacro("__STDC_EMBED_EMPTY__",
                       llvm::itostr(static_cast<int>(EmbedResult::Empty)));
 
+  // We define this to '1' here to indicate that we only support '_Defer'
+  // as a keyword.
+  if (LangOpts.DeferTS)
+    Builder.defineMacro("__STDC_DEFER_TS25755__", "1");
+
   if (LangOpts.ObjC)
     Builder.defineMacro("__OBJC__");
 

diff  --git a/clang/lib/Headers/stddefer.h b/clang/lib/Headers/stddefer.h
new file mode 100644
index 0000000000000..162876ddfa395
--- /dev/null
+++ b/clang/lib/Headers/stddefer.h
@@ -0,0 +1,19 @@
+/*===---- stddefer.h - Standard header for 'defer' -------------------------===
+ *
+ * Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+ * See https://llvm.org/LICENSE.txt for license information.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ *
+ *===-----------------------------------------------------------------------===
+ */
+
+#ifndef __CLANG_STDDEFER_H
+#define __CLANG_STDDEFER_H
+
+/* Provide 'defer' if '_Defer' is supported. */
+#ifdef __STDC_DEFER_TS25755__
+#define __STDC_VERSION_STDDEFER_H__ 202602L
+#define defer _Defer
+#endif
+
+#endif /* __CLANG_STDDEFER_H */

diff  --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 7e73d89c2a18c..78ce4b76d29ae 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -28,6 +28,7 @@
 #include "clang/Sema/SemaOpenMP.h"
 #include "clang/Sema/TypoCorrection.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/ScopeExit.h"
 #include <optional>
 
 using namespace clang;
@@ -312,6 +313,8 @@ StmtResult 
Parser::ParseStatementOrDeclarationAfterAttributes(
     Res = ParseReturnStatement();
     SemiError = "co_return";
     break;
+  case tok::kw__Defer: // C defer TS: defer-statement
+    return ParseDeferStatement(TrailingElseLoc);
 
   case tok::kw_asm: {
     for (const ParsedAttr &AL : CXX11Attrs)
@@ -2370,6 +2373,29 @@ StmtResult Parser::ParseReturnStatement() {
   return Actions.ActOnReturnStmt(ReturnLoc, R.get(), getCurScope());
 }
 
+StmtResult Parser::ParseDeferStatement(SourceLocation *TrailingElseLoc) {
+  assert(Tok.is(tok::kw__Defer));
+  SourceLocation DeferLoc = ConsumeToken();
+
+  Actions.ActOnStartOfDeferStmt(DeferLoc, getCurScope());
+
+  auto OnError = llvm::make_scope_exit(
+      [&] { Actions.ActOnDeferStmtError(getCurScope()); });
+
+  StmtResult Res = ParseStatement(TrailingElseLoc);
+  if (!Res.isUsable())
+    return StmtError();
+
+  // The grammar specifically calls for an unlabeled-statement here.
+  if (auto *L = dyn_cast<LabelStmt>(Res.get())) {
+    Diag(L->getIdentLoc(), diag::err_defer_ts_labeled_stmt);
+    return StmtError();
+  }
+
+  OnError.release();
+  return Actions.ActOnEndOfDeferStmt(Res.get(), getCurScope());
+}
+
 StmtResult Parser::ParsePragmaLoopHint(StmtVector &Stmts,
                                        ParsedStmtContext StmtCtx,
                                        SourceLocation *TrailingElseLoc,

diff  --git a/clang/lib/Sema/JumpDiagnostics.cpp 
b/clang/lib/Sema/JumpDiagnostics.cpp
index 36704c3826dfd..36c9d9afb37f1 100644
--- a/clang/lib/Sema/JumpDiagnostics.cpp
+++ b/clang/lib/Sema/JumpDiagnostics.cpp
@@ -590,6 +590,27 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S,
     break;
   }
 
+  case Stmt::DeferStmtClass: {
+    auto *D = cast<DeferStmt>(S);
+
+    {
+      // Disallow jumps over defer statements.
+      unsigned NewParentScope = Scopes.size();
+      Scopes.emplace_back(ParentScope, diag::note_protected_by_defer_stmt, 0,
+                          D->getDeferLoc());
+      origParentScope = NewParentScope;
+    }
+
+    // Disallow jumps into or out of defer statements.
+    {
+      unsigned NewParentScope = Scopes.size();
+      Scopes.emplace_back(ParentScope, diag::note_enters_defer_stmt,
+                          diag::note_exits_defer_stmt, D->getDeferLoc());
+      BuildScopeInformation(D->getBody(), NewParentScope);
+    }
+    return;
+  }
+
   case Stmt::CaseStmtClass:
   case Stmt::DefaultStmtClass:
   case Stmt::LabelStmtClass:
@@ -972,7 +993,7 @@ void JumpScopeChecker::CheckJump(Stmt *From, Stmt *To, 
SourceLocation DiagLoc,
   // Common case: exactly the same scope, which is fine.
   if (FromScope == ToScope) return;
 
-  // Warn on gotos out of __finally blocks.
+  // Warn on gotos out of __finally blocks and defer statements.
   if (isa<GotoStmt>(From) || isa<IndirectGotoStmt>(From)) {
     // If FromScope > ToScope, FromScope is more nested and the jump goes to a
     // less nested scope.  Check if it crosses a __finally along the way.
@@ -990,6 +1011,10 @@ void JumpScopeChecker::CheckJump(Stmt *From, Stmt *To, 
SourceLocation DiagLoc,
         S.Diag(From->getBeginLoc(), diag::err_goto_into_protected_scope);
         S.Diag(Scopes[I].Loc, diag::note_acc_branch_out_of_compute_construct);
         return;
+      } else if (Scopes[I].OutDiag == diag::note_exits_defer_stmt) {
+        S.Diag(From->getBeginLoc(), diag::err_goto_into_protected_scope);
+        S.Diag(Scopes[I].Loc, diag::note_exits_defer_stmt);
+        return;
       }
     }
   }

diff  --git a/clang/lib/Sema/SemaExceptionSpec.cpp 
b/clang/lib/Sema/SemaExceptionSpec.cpp
index a0483c3027199..b5ff1dbd26d68 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1538,6 +1538,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Stmt::SEHTryStmtClass:
   case Stmt::SwitchStmtClass:
   case Stmt::WhileStmtClass:
+  case Stmt::DeferStmtClass:
     return canSubStmtsThrow(*this, S);
 
   case Stmt::DeclStmtClass: {

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index a8c2e39b49923..5836587a6ffa5 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6860,6 +6860,34 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, 
NamedDecl *NDecl,
   FunctionDecl *FDecl = dyn_cast_or_null<FunctionDecl>(NDecl);
   unsigned BuiltinID = (FDecl ? FDecl->getBuiltinID() : 0);
 
+  auto IsSJLJ = [&] {
+    switch (BuiltinID) {
+    case Builtin::BI__builtin_longjmp:
+    case Builtin::BI__builtin_setjmp:
+    case Builtin::BI__sigsetjmp:
+    case Builtin::BI_longjmp:
+    case Builtin::BI_setjmp:
+    case Builtin::BIlongjmp:
+    case Builtin::BIsetjmp:
+    case Builtin::BIsiglongjmp:
+    case Builtin::BIsigsetjmp:
+      return true;
+    default:
+      return false;
+    }
+  };
+
+  // Forbid any call to setjmp/longjmp and friends inside a '_Defer' statement.
+  if (!CurrentDefer.empty() && IsSJLJ()) {
+    // Note: If we ever start supporting '_Defer' in C++ we'll have to check
+    // for more than just blocks (e.g. lambdas, nested classes...).
+    Scope *DeferParent = CurrentDefer.back().first;
+    Scope *Block = CurScope->getBlockParent();
+    if (DeferParent->Contains(*CurScope) &&
+        (!Block || !DeferParent->Contains(*Block)))
+      Diag(Fn->getExprLoc(), diag::err_defer_invalid_sjlj) << FDecl;
+  }
+
   // Functions with 'interrupt' attribute cannot be called directly.
   if (FDecl) {
     if (FDecl->hasAttr<AnyX86InterruptAttr>()) {

diff  --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 6bb1a27d1800c..1b1643250d05e 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -3267,12 +3267,23 @@ Sema::ActOnIndirectGotoStmt(SourceLocation GotoLoc, 
SourceLocation StarLoc,
   return new (Context) IndirectGotoStmt(GotoLoc, StarLoc, E);
 }
 
-static void CheckJumpOutOfSEHFinally(Sema &S, SourceLocation Loc,
-                                     const Scope &DestScope) {
+static void CheckJumpOutOfSEHFinallyOrDefer(Sema &S, SourceLocation Loc,
+                                            const Scope &DestScope,
+                                            unsigned DeferJumpKind) {
   if (!S.CurrentSEHFinally.empty() &&
       DestScope.Contains(*S.CurrentSEHFinally.back())) {
     S.Diag(Loc, diag::warn_jump_out_of_seh_finally);
   }
+
+  if (!S.CurrentDefer.empty()) {
+    Scope *Parent = S.CurrentDefer.back().first;
+    assert(Parent);
+
+    // Note: We don't create a new scope for defer statements, so 'Parent'
+    // is actually the scope that contains the '_Defer'.
+    if (DestScope.Contains(*Parent) || &DestScope == Parent)
+      S.Diag(Loc, diag::err_jump_out_of_defer_stmt) << DeferJumpKind;
+  }
 }
 
 static Scope *FindLabeledBreakContinueScope(Sema &S, Scope *CurScope,
@@ -3346,7 +3357,8 @@ StmtResult Sema::ActOnContinueStmt(SourceLocation 
ContinueLoc, Scope *CurScope,
         Diag(ContinueLoc, diag::err_acc_branch_in_out_compute_construct)
         << /*branch*/ 0 << /*out of */ 0);
 
-  CheckJumpOutOfSEHFinally(*this, ContinueLoc, *S);
+  CheckJumpOutOfSEHFinallyOrDefer(*this, ContinueLoc, *S,
+                                  diag::DeferJumpKind::Continue);
 
   return new (Context) ContinueStmt(ContinueLoc, LabelLoc, Target);
 }
@@ -3387,7 +3399,8 @@ StmtResult Sema::ActOnBreakStmt(SourceLocation BreakLoc, 
Scope *CurScope,
         Diag(BreakLoc, diag::err_acc_branch_in_out_compute_construct)
         << /*branch*/ 0 << /*out of */ 0);
 
-  CheckJumpOutOfSEHFinally(*this, BreakLoc, *S);
+  CheckJumpOutOfSEHFinallyOrDefer(*this, BreakLoc, *S,
+                                  diag::DeferJumpKind::Break);
 
   return new (Context) BreakStmt(BreakLoc, LabelLoc, Target);
 }
@@ -3932,11 +3945,30 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr 
*RetValExp,
 
   CurScope->updateNRVOCandidate(VD);
 
-  CheckJumpOutOfSEHFinally(*this, ReturnLoc, *CurScope->getFnParent());
+  CheckJumpOutOfSEHFinallyOrDefer(*this, ReturnLoc, *CurScope->getFnParent(),
+                                  diag::DeferJumpKind::Return);
 
   return R;
 }
 
+void Sema::ActOnStartOfDeferStmt(SourceLocation DeferLoc, Scope *CurScope) {
+  CurrentDefer.emplace_back(CurScope, DeferLoc);
+}
+
+void Sema::ActOnDeferStmtError([[maybe_unused]] Scope *CurScope) {
+  assert(!CurrentDefer.empty() && CurrentDefer.back().first == CurScope);
+  CurrentDefer.pop_back();
+}
+
+StmtResult Sema::ActOnEndOfDeferStmt(Stmt *Body,
+                                     [[maybe_unused]] Scope *CurScope) {
+  assert(!CurrentDefer.empty() && CurrentDefer.back().first == CurScope);
+  SourceLocation DeferLoc = CurrentDefer.pop_back_val().second;
+  DiagnoseEmptyStmtBody(DeferLoc, Body, diag::warn_empty_defer_body);
+  setFunctionHasBranchProtectedScope();
+  return DeferStmt::Create(Context, DeferLoc, Body);
+}
+
 static bool CheckSimplerImplicitMovesMSVCWorkaround(const Sema &S,
                                                     const Expr *E) {
   if (!E || !S.getLangOpts().CPlusPlus23 || !S.getLangOpts().MSVCCompat)
@@ -4554,7 +4586,8 @@ Sema::ActOnSEHLeaveStmt(SourceLocation Loc, Scope 
*CurScope) {
     SEHTryParent = SEHTryParent->getParent();
   if (!SEHTryParent)
     return StmtError(Diag(Loc, diag::err_ms___leave_not_in___try));
-  CheckJumpOutOfSEHFinally(*this, Loc, *SEHTryParent);
+  CheckJumpOutOfSEHFinallyOrDefer(*this, Loc, *SEHTryParent,
+                                  diag::DeferJumpKind::SEHLeave);
 
   return new (Context) SEHLeaveStmt(Loc);
 }

diff  --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 8e5dbeb792348..d5b6fdd7dc405 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -8552,6 +8552,14 @@ TreeTransform<Derived>::TransformBreakStmt(BreakStmt *S) 
{
       BreakStmt(S->getKwLoc(), S->getLabelLoc(), cast<LabelDecl>(LD));
 }
 
+template <typename Derived>
+StmtResult TreeTransform<Derived>::TransformDeferStmt(DeferStmt *S) {
+  StmtResult Result = getDerived().TransformStmt(S->getBody());
+  if (!Result.isUsable())
+    return StmtError();
+  return DeferStmt::Create(getSema().Context, S->getDeferLoc(), Result.get());
+}
+
 template<typename Derived>
 StmtResult
 TreeTransform<Derived>::TransformReturnStmt(ReturnStmt *S) {

diff  --git a/clang/lib/Serialization/ASTReaderStmt.cpp 
b/clang/lib/Serialization/ASTReaderStmt.cpp
index eef97a8588f0b..495517ccb31f3 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -335,6 +335,12 @@ void ASTStmtReader::VisitContinueStmt(ContinueStmt *S) {
 
 void ASTStmtReader::VisitBreakStmt(BreakStmt *S) { VisitLoopControlStmt(S); }
 
+void ASTStmtReader::VisitDeferStmt(DeferStmt *S) {
+  VisitStmt(S);
+  S->setDeferLoc(readSourceLocation());
+  S->setBody(Record.readSubStmt());
+}
+
 void ASTStmtReader::VisitReturnStmt(ReturnStmt *S) {
   VisitStmt(S);
 
@@ -3146,6 +3152,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       S = new (Context) BreakStmt(Empty);
       break;
 
+    case STMT_DEFER:
+      S = DeferStmt::CreateEmpty(Context, Empty);
+      break;
+
     case STMT_RETURN:
       S = ReturnStmt::CreateEmpty(
           Context, /* HasNRVOCandidate=*/Record[ASTStmtReader::NumStmtFields]);

diff  --git a/clang/lib/Serialization/ASTWriterStmt.cpp 
b/clang/lib/Serialization/ASTWriterStmt.cpp
index acf345392aa1a..a457e627799c9 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -330,6 +330,13 @@ void ASTStmtWriter::VisitBreakStmt(BreakStmt *S) {
   Code = serialization::STMT_BREAK;
 }
 
+void ASTStmtWriter::VisitDeferStmt(DeferStmt *S) {
+  VisitStmt(S);
+  Record.AddSourceLocation(S->getDeferLoc());
+  Record.AddStmt(S->getBody());
+  Code = serialization::STMT_DEFER;
+}
+
 void ASTStmtWriter::VisitReturnStmt(ReturnStmt *S) {
   VisitStmt(S);
 

diff  --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index a759aee47b8ea..d3de632179e1d 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1874,6 +1874,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::NullStmtClass:
     case Stmt::SwitchStmtClass:
     case Stmt::WhileStmtClass:
+    case Stmt::DeferStmtClass:
     case Expr::MSDependentExistsStmtClass:
       llvm_unreachable("Stmt should not be in analyzer evaluation loop");
     case Stmt::ImplicitValueInitExprClass:

diff  --git a/clang/test/AST/ast-dump-defer-ts.c 
b/clang/test/AST/ast-dump-defer-ts.c
new file mode 100644
index 0000000000000..eba057f93c9c2
--- /dev/null
+++ b/clang/test/AST/ast-dump-defer-ts.c
@@ -0,0 +1,27 @@
+// Test without serialization:
+// RUN: %clang_cc1 -std=c23 -fdefer-ts -ast-dump %s -triple x86_64-linux-gnu \
+// RUN: | FileCheck %s
+//
+// Test with serialization:
+// RUN: %clang_cc1 -std=c23 -fdefer-ts -triple x86_64-linux-gnu -emit-pch -o 
%t %s
+// RUN: %clang_cc1 -std=c23 -fdefer-ts -triple x86_64-linux-gnu -include-pch 
%t -ast-dump-all /dev/null \
+// RUN: | FileCheck %s
+
+static inline void f() {
+  _Defer 3;
+  _Defer { 4; }
+  _Defer _Defer if (true) {}
+}
+
+// CHECK-LABEL: f 'void (void)' static inline
+// CHECK-NEXT:  `-CompoundStmt {{.*}} <col:24, line:14:1>
+// CHECK-NEXT:    |-DeferStmt {{.*}} <line:11:3, col:10>
+// CHECK-NEXT:    | `-IntegerLiteral {{.*}} <col:10> 'int' 3
+// CHECK-NEXT:    |-DeferStmt {{.*}} <line:12:3, col:15>
+// CHECK-NEXT:    | `-CompoundStmt {{.*}} <col:10, col:15>
+// CHECK-NEXT:    |   `-IntegerLiteral {{.*}} <col:12> 'int' 4
+// CHECK-NEXT:    `-DeferStmt {{.*}} <line:13:3, col:28>
+// CHECK-NEXT:      `-DeferStmt {{.*}} <col:10, col:28>
+// CHECK-NEXT:        `-IfStmt {{.*}} <col:17, col:28>
+// CHECK-NEXT:          |-CXXBoolLiteralExpr {{.*}} <col:21> 'bool' true
+// CHECK-NEXT:          `-CompoundStmt {{.*}} <col:27, col:28>

diff  --git a/clang/test/AST/ast-print-defer-ts.c 
b/clang/test/AST/ast-print-defer-ts.c
new file mode 100644
index 0000000000000..bcc217a597778
--- /dev/null
+++ b/clang/test/AST/ast-print-defer-ts.c
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -std=c23 -fdefer-ts -ast-print %s | FileCheck %s
+
+void g();
+
+// CHECK: void f
+void f() {
+    // CHECK-NEXT: _Defer
+    // CHECK-NEXT:     g();
+    // CHECK-NEXT: _Defer
+    // CHECK-NEXT:     _Defer
+    // CHECK-NEXT:         g();
+    // CHECK-NEXT: _Defer {
+    // CHECK-NEXT: }
+    // CHECK-NEXT: _Defer {
+    // CHECK-NEXT:     int x;
+    // CHECK-NEXT: }
+    // CHECK-NEXT: _Defer
+    // CHECK-NEXT:     if (1) {
+    // CHECK-NEXT:     }
+    _Defer
+        g();
+    _Defer
+        _Defer
+            g();
+    _Defer {
+    }
+    _Defer {
+        int x;
+    }
+    _Defer
+        if (1) {
+        }
+}

diff  --git a/clang/test/CodeGen/defer-ts-musttail.c 
b/clang/test/CodeGen/defer-ts-musttail.c
new file mode 100644
index 0000000000000..5622fecbb4fed
--- /dev/null
+++ b/clang/test/CodeGen/defer-ts-musttail.c
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -std=c23 -fdefer-ts -emit-llvm 
%s -o /dev/null -verify
+
+int bar() { return 12; }
+int foo() {
+  _Defer {};
+  [[clang::musttail]] return bar(); // expected-error {{cannot compile this 
tail call skipping over cleanups yet}}
+}

diff  --git a/clang/test/CodeGen/defer-ts-nested-cleanups.c 
b/clang/test/CodeGen/defer-ts-nested-cleanups.c
new file mode 100644
index 0000000000000..d831b4380b929
--- /dev/null
+++ b/clang/test/CodeGen/defer-ts-nested-cleanups.c
@@ -0,0 +1,179 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -std=c23 -fdefer-ts -emit-llvm 
%s -o - -O1 -disable-llvm-passes | FileCheck %s
+
+// Test that cleanups emitted in a '_Defer' don't clobber the cleanup slot; we
+// test this using lifetime intrinsics, which are emitted starting at -O1.
+
+void g();
+
+// CHECK-LABEL: define {{.*}} void @f1()
+// CHECK: entry:
+// CHECK-NEXT:   %i = alloca i32, align 4
+// CHECK-NEXT:   %cleanup.dest.slot = alloca i32, align 4
+// CHECK-NEXT:   %j = alloca i32, align 4
+// CHECK-NEXT:   call void @llvm.lifetime.start.p0(ptr %i)
+// CHECK-NEXT:   store i32 0, ptr %i, align 4
+// CHECK-NEXT:   br label %for.cond
+// CHECK: for.cond:
+// CHECK-NEXT:   %0 = load i32, ptr %i, align 4
+// CHECK-NEXT:   %cmp = icmp eq i32 %0, 1
+// CHECK-NEXT:   br i1 %cmp, label %if.then, label %if.end
+// CHECK: if.then:
+// CHECK-NEXT:   store i32 2, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup
+// CHECK: if.end:
+// CHECK-NEXT:   store i32 0, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup
+// CHECK: cleanup:
+// CHECK-NEXT:   %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot, align 
4
+// CHECK-NEXT:   call void @llvm.lifetime.start.p0(ptr %j)
+// CHECK-NEXT:   store i32 0, ptr %j, align 4
+// CHECK-NEXT:   br label %for.cond1
+// CHECK: for.cond1:
+// CHECK-NEXT:   %1 = load i32, ptr %j, align 4
+// CHECK-NEXT:   %cmp2 = icmp ne i32 %1, 1
+// CHECK-NEXT:   br i1 %cmp2, label %for.body, label %for.cond.cleanup
+// CHECK: for.cond.cleanup:
+// CHECK-NEXT:   store i32 5, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   call void @llvm.lifetime.end.p0(ptr %j)
+// CHECK-NEXT:   br label %for.end
+// CHECK: for.body:
+// CHECK-NEXT:   call void @g()
+// CHECK-NEXT:   br label %for.inc
+// CHECK: for.inc:
+// CHECK-NEXT:   %2 = load i32, ptr %j, align 4
+// CHECK-NEXT:   %inc = add nsw i32 %2, 1
+// CHECK-NEXT:   store i32 %inc, ptr %j, align 4
+// CHECK-NEXT:   br label %for.cond1
+// CHECK: for.end:
+// CHECK-NEXT:   store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   %cleanup.dest = load i32, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   switch i32 %cleanup.dest, label %cleanup6 [
+// CHECK-NEXT:     i32 0, label %cleanup.cont
+// CHECK-NEXT:   ]
+// CHECK: cleanup.cont:
+// CHECK-NEXT:   br label %for.inc4
+// CHECK: for.inc4:
+// CHECK-NEXT:   %3 = load i32, ptr %i, align 4
+// CHECK-NEXT:   %inc5 = add nsw i32 %3, 1
+// CHECK-NEXT:   store i32 %inc5, ptr %i, align 4
+// CHECK-NEXT:   br label %for.cond
+// CHECK: cleanup6:
+// CHECK-NEXT:   call void @llvm.lifetime.end.p0(ptr %i)
+// CHECK-NEXT:   br label %for.end7
+// CHECK: for.end7:
+// CHECK-NEXT:   ret void
+void f1() {
+  for (int i = 0;; i++) {
+    _Defer {
+      for (int j = 0; j != 1; j++) {
+        g();
+      }
+    }
+    if (i == 1) break;
+  }
+}
+
+// CHECK-LABEL: define {{.*}} void @f2()
+// CHECK: entry:
+// CHECK-NEXT:   %i = alloca i32, align 4
+// CHECK-NEXT:   %cleanup.dest.slot = alloca i32, align 4
+// CHECK-NEXT:   %j = alloca i32, align 4
+// CHECK-NEXT:   %k = alloca i32, align 4
+// CHECK-NEXT:   call void @llvm.lifetime.start.p0(ptr %i)
+// CHECK-NEXT:   store i32 0, ptr %i, align 4
+// CHECK-NEXT:   br label %for.cond
+// CHECK: for.cond:
+// CHECK-NEXT:   %0 = load i32, ptr %i, align 4
+// CHECK-NEXT:   %cmp = icmp eq i32 %0, 1
+// CHECK-NEXT:   br i1 %cmp, label %if.then, label %if.end
+// CHECK: if.then:
+// CHECK-NEXT:   store i32 2, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup
+// CHECK: if.end:
+// CHECK-NEXT:   store i32 0, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup
+// CHECK: cleanup:
+// CHECK-NEXT:   %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot, align 
4
+// CHECK-NEXT:   call void @llvm.lifetime.start.p0(ptr %j)
+// CHECK-NEXT:   store i32 0, ptr %j, align 4
+// CHECK-NEXT:   br label %for.cond1
+// CHECK: for.cond1:
+// CHECK-NEXT:   %1 = load i32, ptr %j, align 4
+// CHECK-NEXT:   %cmp2 = icmp eq i32 %1, 1
+// CHECK-NEXT:   br i1 %cmp2, label %if.then3, label %if.end4
+// CHECK: if.then3:
+// CHECK-NEXT:   store i32 5, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup5
+// CHECK: if.end4:
+// CHECK-NEXT:   store i32 0, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup5
+// CHECK: cleanup5:
+// CHECK-NEXT:   %cleanup.dest.saved6 = load i32, ptr %cleanup.dest.slot, 
align 4
+// CHECK-NEXT:   call void @llvm.lifetime.start.p0(ptr %k)
+// CHECK-NEXT:   store i32 0, ptr %k, align 4
+// CHECK-NEXT:   br label %for.cond7
+// CHECK: for.cond7:
+// CHECK-NEXT:   %2 = load i32, ptr %k, align 4
+// CHECK-NEXT:   %cmp8 = icmp ne i32 %2, 1
+// CHECK-NEXT:   br i1 %cmp8, label %for.body, label %for.cond.cleanup
+// CHECK: for.cond.cleanup:
+// CHECK-NEXT:   store i32 8, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   call void @llvm.lifetime.end.p0(ptr %k)
+// CHECK-NEXT:   br label %for.end
+// CHECK: for.body:
+// CHECK-NEXT:   call void @g()
+// CHECK-NEXT:   br label %for.inc
+// CHECK: for.inc:
+// CHECK-NEXT:   %3 = load i32, ptr %k, align 4
+// CHECK-NEXT:   %inc = add nsw i32 %3, 1
+// CHECK-NEXT:   store i32 %inc, ptr %k, align 4
+// CHECK-NEXT:   br label %for.cond7
+// CHECK: for.end:
+// CHECK-NEXT:   store i32 %cleanup.dest.saved6, ptr %cleanup.dest.slot, align 
4
+// CHECK-NEXT:   %cleanup.dest = load i32, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   switch i32 %cleanup.dest, label %cleanup12 [
+// CHECK-NEXT:     i32 0, label %cleanup.cont
+// CHECK-NEXT:   ]
+// CHECK: cleanup.cont:
+// CHECK-NEXT:   br label %for.inc10
+// CHECK: for.inc10:
+// CHECK-NEXT:   %4 = load i32, ptr %j, align 4
+// CHECK-NEXT:   %inc11 = add nsw i32 %4, 1
+// CHECK-NEXT:   store i32 %inc11, ptr %j, align 4
+// CHECK-NEXT:   br label %for.cond1
+// CHECK: cleanup12:
+// CHECK-NEXT:   call void @llvm.lifetime.end.p0(ptr %j)
+// CHECK-NEXT:   br label %for.end13
+// CHECK: for.end13:
+// CHECK-NEXT:   store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   %cleanup.dest14 = load i32, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   switch i32 %cleanup.dest14, label %cleanup18 [
+// CHECK-NEXT:     i32 0, label %cleanup.cont15
+// CHECK-NEXT:   ]
+// CHECK: cleanup.cont15:
+// CHECK-NEXT:   br label %for.inc16
+// CHECK: for.inc16:
+// CHECK-NEXT:   %5 = load i32, ptr %i, align 4
+// CHECK-NEXT:   %inc17 = add nsw i32 %5, 1
+// CHECK-NEXT:   store i32 %inc17, ptr %i, align 4
+// CHECK-NEXT:   br label %for.cond
+// CHECK: cleanup18:
+// CHECK-NEXT:   call void @llvm.lifetime.end.p0(ptr %i)
+// CHECK-NEXT:   br label %for.end19
+// CHECK: for.end19:
+// CHECK-NEXT:   ret void
+void f2() {
+  for (int i = 0;; i++) {
+    _Defer {
+      for (int j = 0;; j++) {
+        _Defer {
+          for (int k = 0; k != 1; k++) {
+            g();
+          }
+        }
+       if (j == 1) break;
+      }
+    }
+    if (i == 1) break;
+  }
+}

diff  --git a/clang/test/CodeGen/defer-ts-seh.c 
b/clang/test/CodeGen/defer-ts-seh.c
new file mode 100644
index 0000000000000..a91816f50d8d5
--- /dev/null
+++ b/clang/test/CodeGen/defer-ts-seh.c
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -triple x86_64-windows-msvc -std=c23 -fdefer-ts 
-fms-compatibility -emit-llvm %s -o - | FileCheck %s
+
+void g();
+void h();
+
+void f() {
+  __try {
+    _Defer h();
+    g();
+  } __finally {
+
+  }
+}
+
+// CHECK-LABEL: define {{.*}} void @f() {{.*}} personality ptr 
@__C_specific_handler
+// CHECK: entry:
+// CHECK:   invoke void @g() #4
+// CHECK:           to label %invoke.cont unwind label %ehcleanup
+// CHECK: invoke.cont:
+// CHECK:   invoke void @h() #4
+// CHECK:           to label %invoke.cont1 unwind label %ehcleanup3
+// CHECK: invoke.cont1:
+// CHECK:   %0 = call ptr @llvm.localaddress()
+// CHECK:   call void @"?fin$0@0@f@@"(i8 {{.*}} 0, ptr {{.*}} %0)
+// CHECK:   ret void
+// CHECK: ehcleanup:
+// CHECK:   %1 = cleanuppad within none []
+// CHECK:   invoke void @h() #4 [ "funclet"(token %1) ]
+// CHECK:           to label %invoke.cont2 unwind label %ehcleanup3
+// CHECK: invoke.cont2:
+// CHECK:   cleanupret from %1 unwind label %ehcleanup3
+// CHECK: ehcleanup3:
+// CHECK:   %2 = cleanuppad within none []
+// CHECK:   %3 = call ptr @llvm.localaddress()
+// CHECK:   call void @"?fin$0@0@f@@"(i8 {{.*}} 1, ptr {{.*}} %3) [ 
"funclet"(token %2) ]
+// CHECK:   cleanupret from %2 unwind to caller
+
+// CHECK-LABEL: define {{.*}} void @"?fin$0@0@f@@"(i8 {{.*}} 
%abnormal_termination, ptr {{.*}} %frame_pointer)
+// CHECK: entry:
+// CHECK:   %frame_pointer.addr = alloca ptr, align 8
+// CHECK:   %abnormal_termination.addr = alloca i8, align 1
+// CHECK:   store ptr %frame_pointer, ptr %frame_pointer.addr, align 8
+// CHECK:   store i8 %abnormal_termination, ptr %abnormal_termination.addr, 
align 1
+// CHECK:   ret void

diff  --git a/clang/test/CodeGen/defer-ts.c b/clang/test/CodeGen/defer-ts.c
new file mode 100644
index 0000000000000..79b09064d330c
--- /dev/null
+++ b/clang/test/CodeGen/defer-ts.c
@@ -0,0 +1,652 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -std=c23 -fdefer-ts -emit-llvm 
%s -o - | FileCheck %s
+
+#define defer _Defer
+
+void a();
+void b();
+void c();
+void x(int q);
+bool q(int q);
+[[noreturn]] void noreturn();
+
+// CHECK-LABEL: define {{.*}} void @f1()
+void f1() {
+  // CHECK: call void @c()
+  // CHECK: call void @b()
+  // CHECK: call void @a()
+  defer a();
+  defer b();
+  defer c();
+}
+
+// CHECK-LABEL: define {{.*}} void @f2()
+void f2() {
+  // CHECK: call void @x(i32 {{.*}} 1)
+  // CHECK: call void @x(i32 {{.*}} 2)
+  // CHECK: call void @x(i32 {{.*}} 3)
+  // CHECK: call void @x(i32 {{.*}} 4)
+  // CHECK: call void @x(i32 {{.*}} 5)
+  defer x(5);
+  {
+    defer x(4);
+    {
+      defer x(2);
+      defer x(1);
+    }
+    x(3);
+  }
+}
+
+// CHECK-LABEL: define {{.*}} void @f3(i1 {{.*}} %ret)
+void f3(bool ret) {
+  // CHECK: entry:
+  // CHECK:   %ret.addr = alloca i8, align 1
+  // CHECK:   %cleanup.dest.slot = alloca i32, align 4
+  // CHECK:   %storedv = zext i1 %ret to i8
+  // CHECK:   store i8 %storedv, ptr %ret.addr, align 1
+  // CHECK:   %0 = load i8, ptr %ret.addr, align 1
+  // CHECK:   %loadedv = trunc i8 %0 to i1
+  // CHECK:   br i1 %loadedv, label %if.then, label %if.end
+  // CHECK: if.then:
+  // CHECK:   store i32 1, ptr %cleanup.dest.slot, align 4
+  // CHECK:   br label %cleanup
+  // CHECK: if.end:
+  // CHECK:   %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot, align 4
+  // CHECK:   call void @x(i32 {{.*}} 1)
+  // CHECK:   store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot, align 4
+  // CHECK:   store i32 0, ptr %cleanup.dest.slot, align 4
+  // CHECK:   br label %cleanup
+  // CHECK: cleanup:
+  // CHECK:   %cleanup.dest.saved1 = load i32, ptr %cleanup.dest.slot, align 4
+  // CHECK:   call void @x(i32 {{.*}} 2)
+  // CHECK:   store i32 %cleanup.dest.saved1, ptr %cleanup.dest.slot, align 4
+  // CHECK:   %cleanup.dest = load i32, ptr %cleanup.dest.slot, align 4
+  // CHECK:   switch i32 %cleanup.dest, label %unreachable [
+  // CHECK:     i32 0, label %cleanup.cont
+  // CHECK:     i32 1, label %cleanup.cont
+  // CHECK:   ]
+  // CHECK: cleanup.cont:
+  // CHECK:   ret void
+  // CHECK: unreachable:
+  // CHECK:   unreachable
+  defer x(2);
+  if (ret) return;
+  defer x(1);
+}
+
+// CHECK-LABEL: define {{.*}} void @ts_g()
+void ts_g() {
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT:   ret void
+  // CHECK-NEXT: }
+  return;
+  defer x(42);
+}
+
+// CHECK-LABEL: define {{.*}} void @ts_h()
+void ts_h() {
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT:   br label %b
+  // CHECK-EMPTY:
+  goto b;
+  {
+    defer x(42);
+  }
+
+  // CHECK-NEXT: b:
+  // CHECK-NEXT:   ret void
+  // CHECK-NEXT: }
+  b:
+}
+
+// CHECK-LABEL: define {{.*}} void @ts_i()
+void ts_i() {
+  // CHECK: entry:
+  // CHECK:   %cleanup.dest.slot = alloca i32, align 4
+  // CHECK:   store i32 2, ptr %cleanup.dest.slot, align 4
+  // CHECK:   %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot, align 4
+  // CHECK:   call void @x(i32 {{.*}} 42)
+  // CHECK:   store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot, align 4
+  // CHECK:   %cleanup.dest = load i32, ptr %cleanup.dest.slot, align 4
+  // CHECK:   switch i32 %cleanup.dest, label %unreachable [
+  // CHECK:     i32 2, label %b
+  // CHECK:   ]
+  // CHECK: b:
+  // CHECK:   ret void
+  // CHECK: unreachable:
+  // CHECK:   unreachable
+  {
+    defer { x(42); }
+    goto b;
+  }
+  b:
+}
+
+
+// CHECK-LABEL: define {{.*}} void @ts_m()
+void ts_m() {
+  // CHECK: entry:
+  // CHECK:   br label %b
+  // CHECK: b:
+  // CHECK:   call void @x(i32 {{.*}} 1)
+  // CHECK:   ret void
+  goto b;
+  {
+    b:
+    defer x(1);
+  }
+}
+
+// CHECK-LABEL: define {{.*}} void @ts_p()
+void ts_p() {
+  // CHECK: entry:
+  // CHECK:   br label %b
+  // CHECK: b:
+  // CHECK:   ret void
+  {
+    goto b;
+    defer x(42);
+  }
+  b:
+}
+
+// CHECK-LABEL: define {{.*}} void @ts_r()
+void ts_r() {
+  // CHECK: entry:
+  // CHECK:   br label %b
+  // CHECK: b:
+  // CHECK:   call void @x(i32 {{.*}} 42)
+  // CHECK:   br label %b
+  {
+    b:
+    defer x(42);
+  }
+  goto b;
+}
+
+// CHECK-LABEL: define {{.*}} i32 @return_value()
+int return_value() {
+  // CHECK: entry:
+  // CHECK:   %r = alloca i32, align 4
+  // CHECK:   %p = alloca ptr, align 8
+  // CHECK:   store i32 4, ptr %r, align 4
+  // CHECK:   store ptr %r, ptr %p, align 8
+  // CHECK:   %0 = load ptr, ptr %p, align 8
+  // CHECK:   %1 = load i32, ptr %0, align 4
+  // CHECK:   %2 = load ptr, ptr %p, align 8
+  // CHECK:   store i32 5, ptr %2, align 4
+  // CHECK:   ret i32 %1
+  int r = 4;
+  int* p = &r;
+  defer { *p = 5; }
+  return *p;
+}
+
+void* malloc(__SIZE_TYPE__ size);
+void free(void* ptr);
+int use_buffer(__SIZE_TYPE__ size, void* ptr);
+
+// CHECK-LABEL: define {{.*}} i32 @malloc_free_example()
+int malloc_free_example() {
+  // CHECK: entry:
+  // CHECK:   %size = alloca i32, align 4
+  // CHECK:   %buf = alloca ptr, align 8
+  // CHECK:   store i32 20, ptr %size, align 4
+  // CHECK:   %call = call ptr @malloc(i64 {{.*}} 20)
+  // CHECK:   store ptr %call, ptr %buf, align 8
+  // CHECK:   %0 = load ptr, ptr %buf, align 8
+  // CHECK:   %call1 = call i32 @use_buffer(i64 {{.*}} 20, ptr {{.*}} %0)
+  // CHECK:   %1 = load ptr, ptr %buf, align 8
+  // CHECK:   call void @free(ptr {{.*}} %1)
+  // CHECK:   ret i32 %call1
+  const int size = 20;
+  void* buf = malloc(size);
+  defer { free(buf); }
+  return use_buffer(size, buf);
+}
+
+// CHECK-LABEL: define {{.*}} void @sequencing_1()
+void sequencing_1() {
+  // CHECK: entry:
+  // CHECK:   call void @x(i32 {{.*}} 1)
+  // CHECK:   call void @x(i32 {{.*}} 2)
+  // CHECK:   call void @x(i32 {{.*}} 3)
+  // CHECK:   ret void
+  {
+    defer {
+      x(3);
+    }
+    if (true)
+      defer x(1);
+    x(2);
+  }
+}
+
+// CHECK-LABEL: define {{.*}} void @sequencing_2()
+void sequencing_2() {
+  // CHECK: entry:
+  // CHECK:   %arr = alloca [3 x i32], align 4
+  // CHECK:   %i = alloca i32, align 4
+  // CHECK:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %arr, ptr align 4 
@__const.sequencing_2.arr, i64 12, i1 false)
+  // CHECK:   store i32 0, ptr %i, align 4
+  // CHECK:   br label %for.cond
+  // CHECK: for.cond:
+  // CHECK:   %0 = load i32, ptr %i, align 4
+  // CHECK:   %cmp = icmp ult i32 %0, 3
+  // CHECK:   br i1 %cmp, label %for.body, label %for.end
+  // CHECK: for.body:
+  // CHECK:   %1 = load i32, ptr %i, align 4
+  // CHECK:   %idxprom = zext i32 %1 to i64
+  // CHECK:   %arrayidx = getelementptr inbounds nuw [3 x i32], ptr %arr, i64 
0, i64 %idxprom
+  // CHECK:   %2 = load i32, ptr %arrayidx, align 4
+  // CHECK:   call void @x(i32 {{.*}} %2)
+  // CHECK:   br label %for.inc
+  // CHECK: for.inc:
+  // CHECK:   %3 = load i32, ptr %i, align 4
+  // CHECK:   %inc = add i32 %3, 1
+  // CHECK:   store i32 %inc, ptr %i, align 4
+  // CHECK:   br label %for.cond
+  // CHECK: for.end:
+  // CHECK:   call void @x(i32 {{.*}} 4)
+  // CHECK:   call void @x(i32 {{.*}} 5)
+  // CHECK:   ret void
+  {
+    int arr[] = {1, 2, 3};
+    defer {
+      x(5);
+    }
+    for (unsigned i = 0; i < 3; ++i)
+      defer x(arr[i]);
+    x(4);
+  }
+}
+
+// CHECK-LABEL: define {{.*}} void @sequencing_3()
+void sequencing_3() {
+  // CHECK: entry:
+  // CHECK:   %r = alloca i32, align 4
+  // CHECK:   store i32 0, ptr %r, align 4
+  // CHECK:   %0 = load i32, ptr %r, align 4
+  // CHECK:   %add = add nsw i32 %0, 1
+  // CHECK:   store i32 %add, ptr %r, align 4
+  // CHECK:   %1 = load i32, ptr %r, align 4
+  // CHECK:   %mul = mul nsw i32 %1, 2
+  // CHECK:   store i32 %mul, ptr %r, align 4
+  // CHECK:   %2 = load i32, ptr %r, align 4
+  // CHECK:   %add1 = add nsw i32 %2, 3
+  // CHECK:   store i32 %add1, ptr %r, align 4
+  // CHECK:   %3 = load i32, ptr %r, align 4
+  // CHECK:   %mul2 = mul nsw i32 %3, 4
+  // CHECK:   store i32 %mul2, ptr %r, align 4
+  // CHECK:   ret void
+  int r = 0;
+  {
+    defer {
+      defer r *= 4;
+      r *= 2;
+      defer {
+        r += 3;
+      }
+    }
+    defer r += 1;
+  }
+}
+
+// CHECK-LABEL: define {{.*}} void @defer_stmt(i32 {{.*}} %q)
+void defer_stmt(int q) {
+  // CHECK: entry:
+  // CHECK:   %q.addr = alloca i32, align 4
+  // CHECK:   store i32 %q, ptr %q.addr, align 4
+  // CHECK:   %0 = load i32, ptr %q.addr, align 4
+  // CHECK:   %cmp = icmp eq i32 %0, 3
+  // CHECK:   br i1 %cmp, label %if.then, label %if.end
+  // CHECK: if.then:
+  // CHECK:   call void @x(i32 {{.*}} 42)
+  // CHECK:   br label %if.end
+  // CHECK: if.end:
+  // CHECK:   ret void
+  defer if (q == 3) x(42);
+}
+
+// CHECK-LABEL: define {{.*}} void @defer_defer()
+void defer_defer() {
+  // CHECK: entry:
+  // CHECK:   call void @x(i32 {{.*}} 0)
+  // CHECK:   call void @x(i32 {{.*}} 1)
+  // CHECK:   call void @x(i32 {{.*}} 2)
+  // CHECK:   call void @x(i32 {{.*}} 3)
+  // CHECK:   call void @x(i32 {{.*}} 4)
+  // CHECK:   ret void
+  defer x(4);
+  defer defer x(3);
+  defer defer defer x(2);
+  defer defer defer defer x(1);
+  x(0);
+}
+
+// CHECK-LABEL: define {{.*}} i32 @vla(ptr {{.*}} %p, i32 {{.*}} %x)
+int vla(int* p, int x) {
+    // CHECK: entry:
+    // CHECK:   %retval = alloca i32, align 4
+    // CHECK:   %p.addr = alloca ptr, align 8
+    // CHECK:   %x.addr = alloca i32, align 4
+    // CHECK:   %cleanup.dest.slot = alloca i32, align 4
+    // CHECK:   %saved_stack = alloca ptr, align 8
+    // CHECK:   %__vla_expr0 = alloca i64, align 8
+    // CHECK:   %saved_stack3 = alloca ptr, align 8
+    // CHECK:   %__vla_expr1 = alloca i64, align 8
+    // CHECK:   store ptr %p, ptr %p.addr, align 8
+    // CHECK:   store i32 %x, ptr %x.addr, align 4
+    // CHECK:   %0 = load i32, ptr %x.addr, align 4
+    // CHECK:   %cmp = icmp slt i32 %0, 5
+    // CHECK:   br i1 %cmp, label %if.then, label %if.end
+    // CHECK: if.then:
+    // CHECK:   store i32 10, ptr %retval, align 4
+    // CHECK:   store i32 1, ptr %cleanup.dest.slot, align 4
+    // CHECK:   br label %cleanup
+    // CHECK: if.end:
+    // CHECK:   store i32 7, ptr %retval, align 4
+    // CHECK:   store i32 1, ptr %cleanup.dest.slot, align 4
+    // CHECK:   %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot, align 4
+    // CHECK:   %1 = load i32, ptr %x.addr, align 4
+    // CHECK:   %2 = zext i32 %1 to i64
+    // CHECK:   %3 = call ptr @llvm.stacksave.p0()
+    // CHECK:   store ptr %3, ptr %saved_stack, align 8
+    // CHECK:   %vla = alloca i32, i64 %2, align 16
+    // CHECK:   store i64 %2, ptr %__vla_expr0, align 8
+    // CHECK:   %arrayidx = getelementptr inbounds i32, ptr %vla, i64 2
+    // CHECK:   store i32 4, ptr %arrayidx, align 8
+    // CHECK:   %arrayidx1 = getelementptr inbounds i32, ptr %vla, i64 2
+    // CHECK:   %4 = load i32, ptr %arrayidx1, align 8
+    // CHECK:   %5 = load ptr, ptr %p.addr, align 8
+    // CHECK:   store i32 %4, ptr %5, align 4
+    // CHECK:   %6 = load ptr, ptr %saved_stack, align 8
+    // CHECK:   call void @llvm.stackrestore.p0(ptr %6)
+    // CHECK:   store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot, align 4
+    // CHECK:   br label %cleanup
+    // CHECK: cleanup:
+    // CHECK:   %cleanup.dest.saved2 = load i32, ptr %cleanup.dest.slot, align 
4
+    // CHECK:   %7 = load i32, ptr %x.addr, align 4
+    // CHECK:   %8 = zext i32 %7 to i64
+    // CHECK:   %9 = call ptr @llvm.stacksave.p0()
+    // CHECK:   store ptr %9, ptr %saved_stack3, align 8
+    // CHECK:   %vla4 = alloca i32, i64 %8, align 16
+    // CHECK:   store i64 %8, ptr %__vla_expr1, align 8
+    // CHECK:   %arrayidx5 = getelementptr inbounds i32, ptr %vla4, i64 2
+    // CHECK:   store i32 3, ptr %arrayidx5, align 8
+    // CHECK:   %arrayidx6 = getelementptr inbounds i32, ptr %vla4, i64 2
+    // CHECK:   %10 = load i32, ptr %arrayidx6, align 8
+    // CHECK:   %11 = load ptr, ptr %p.addr, align 8
+    // CHECK:   store i32 %10, ptr %11, align 4
+    // CHECK:   %12 = load ptr, ptr %saved_stack3, align 8
+    // CHECK:   call void @llvm.stackrestore.p0(ptr %12)
+    // CHECK:   store i32 %cleanup.dest.saved2, ptr %cleanup.dest.slot, align 4
+    // CHECK:   %13 = load i32, ptr %retval, align 4
+    // CHECK:   ret i32 %13
+    defer {
+        int a[x];
+        a[2] = 3;
+        *p = a[2];
+    }
+    if (x < 5) { return 10; }
+    defer {
+        int b[x];
+        b[2] = 4;
+        *p = b[2];
+    }
+    return 7;
+}
+
+[[noreturn]] void exit();
+[[noreturn]] void _Exit();
+[[noreturn]] void foobar();
+
+// CHECK-LABEL: define {{.*}} i32 @call_exit()
+int call_exit() {
+    // CHECK: entry:
+    // CHECK:   call void @exit()
+    // CHECK:   unreachable
+    defer x(1);
+    exit();
+}
+
+// CHECK-LABEL: define {{.*}} i32 @call__Exit()
+int call__Exit() {
+    // CHECK: entry:
+    // CHECK:   call void @_Exit()
+    // CHECK:   unreachable
+    defer x(1);
+    _Exit();
+}
+
+// CHECK-LABEL: define {{.*}} i32 @call_foobar()
+int call_foobar() {
+    // CHECK: entry:
+    // CHECK:   call void @foobar()
+    // CHECK:   unreachable
+    defer x(1);
+    foobar();
+}
+
+// CHECK-LABEL: define {{.*}} i32 @main()
+int main() {
+  // CHECK: entry:
+  // CHECK:   %retval = alloca i32, align 4
+  // CHECK:   store i32 0, ptr %retval, align 4
+  // CHECK:   store i32 5, ptr %retval, align 4
+  // CHECK:   call void @x(i32 {{.*}} 42)
+  // CHECK:   %0 = load i32, ptr %retval, align 4
+  // CHECK:   ret i32 %0
+  defer x(42);
+  return 5;
+}
+
+// CHECK-LABEL: define {{.*}} void @t()
+// CHECK: entry:
+// CHECK-NEXT:   %count = alloca i32, align 4
+// CHECK-NEXT:   %cleanup.dest.slot = alloca i32, align 4
+// CHECK-NEXT:   store i32 0, ptr %count, align 4
+// CHECK-NEXT:   br label %target
+// CHECK: target:
+// CHECK-NEXT:   %0 = load i32, ptr %count, align 4
+// CHECK-NEXT:   %inc = add nsw i32 %0, 1
+// CHECK-NEXT:   store i32 %inc, ptr %count, align 4
+// CHECK-NEXT:   %1 = load i32, ptr %count, align 4
+// CHECK-NEXT:   %cmp = icmp sle i32 %1, 2
+// CHECK-NEXT:   br i1 %cmp, label %if.then, label %if.end
+// CHECK: if.then:
+// CHECK-NEXT:   store i32 2, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup
+// CHECK: if.end:
+// CHECK-NEXT:   store i32 0, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup
+// CHECK: cleanup:
+// CHECK-NEXT:   %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot, align 
4
+// CHECK-NEXT:   call void @x(i32 {{.*}} 1)
+// CHECK-NEXT:   store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   %cleanup.dest = load i32, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   switch i32 %cleanup.dest, label %unreachable [
+// CHECK-NEXT:     i32 0, label %cleanup.cont
+// CHECK-NEXT:     i32 2, label %target
+// CHECK-NEXT:   ]
+// CHECK: cleanup.cont:
+// CHECK-NEXT:   call void @x(i32 {{.*}} 2)
+// CHECK-NEXT:   ret void
+// CHECK: unreachable:
+// CHECK-NEXT:   unreachable
+void t() {
+   int count = 0;
+
+   {
+     target:
+     _Defer { x(1); }
+     ++count;
+     if (count <= 2) {
+       goto target;
+     }
+   }
+
+   x(2);
+}
+
+// CHECK-LABEL: define {{.*}} void @stmt_expr()
+// CHECK: entry:
+// CHECK-NEXT:   %tmp = alloca i32, align 4
+// CHECK-NEXT:   call void @x(i32 {{.*}} 1)
+// CHECK-NEXT:   call void @x(i32 {{.*}} 2)
+// CHECK-NEXT:   call void @x(i32 {{.*}} 3)
+// CHECK-NEXT:   call void @x(i32 {{.*}} 4)
+// CHECK-NEXT:   store i32 6, ptr %tmp, align 4
+// CHECK-NEXT:   call void @x(i32 {{.*}} 5)
+// CHECK-NEXT:   %0 = load i32, ptr %tmp, align 4
+// CHECK-NEXT:   call void @x(i32 {{.*}} %0)
+// CHECK-NEXT:   ret void
+void stmt_expr() {
+  ({
+    _Defer x(4);
+    _Defer ({
+      _Defer x(3);
+      x(2);
+    });
+    x(1);
+  });
+
+  x(({
+    _Defer x(5);
+    6;
+  }));
+}
+
+// CHECK-LABEL: define {{.*}} void @cleanup_no_insert_point()
+// CHECK: entry:
+// CHECK-NEXT:   %cleanup.dest.slot = alloca i32, align 4
+// CHECK-NEXT:   br label %while.cond
+// CHECK: while.cond:
+// CHECK-NEXT:   %call = call {{.*}} i1 @q(i32 {{.*}} 1)
+// CHECK-NEXT:   br i1 %call, label %while.body, label %while.end
+// CHECK: while.body:
+// CHECK-NEXT:   %call1 = call {{.*}} i1 @q(i32 {{.*}} 2)
+// CHECK-NEXT:   br i1 %call1, label %if.then, label %if.end
+// CHECK: if.then:
+// CHECK-NEXT:   store i32 2, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup
+// CHECK: if.end:
+// CHECK-NEXT:   %call2 = call {{.*}} i1 @q(i32 {{.*}} 3)
+// CHECK-NEXT:   br i1 %call2, label %if.then3, label %if.end4
+// CHECK: if.then3:
+// CHECK-NEXT:   store i32 3, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup
+// CHECK: if.end4:
+// CHECK-NEXT:   store i32 0, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup
+// CHECK: cleanup:
+// CHECK-NEXT:   %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot, align 
4
+// CHECK-NEXT:   call void @noreturn()
+// CHECK-NEXT:   unreachable
+// CHECK: 0:
+// CHECK-NEXT:   %cleanup.dest = load i32, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   switch i32 %cleanup.dest, label %unreachable [
+// CHECK-NEXT:     i32 0, label %cleanup.cont
+// CHECK-NEXT:     i32 2, label %while.cond
+// CHECK-NEXT:     i32 3, label %while.end
+// CHECK-NEXT:   ]
+// CHECK: cleanup.cont:
+// CHECK-NEXT:   br label %while.cond
+// CHECK: while.end:
+// CHECK-NEXT:   ret void
+// CHECK: unreachable:
+// CHECK-NEXT:   unreachable
+void cleanup_no_insert_point() {
+  while (q(1)) {
+    _Defer {
+      noreturn();
+    };
+    if (q(2)) continue;
+    if (q(3)) break;
+  }
+}
+
+// CHECK-LABEL: define {{.*}} void @cleanup_nested()
+// CHECK: entry:
+// CHECK-NEXT:   %cleanup.dest.slot = alloca i32, align 4
+// CHECK-NEXT:   br label %while.cond
+// CHECK: while.cond:
+// CHECK-NEXT:   %call = call {{.*}} i1 @q(i32 {{.*}} 1)
+// CHECK-NEXT:   br i1 %call, label %while.body, label %while.end19
+// CHECK: while.body:
+// CHECK-NEXT:   %call1 = call {{.*}} i1 @q(i32 {{.*}} 6)
+// CHECK-NEXT:   br i1 %call1, label %if.then, label %if.end
+// CHECK: if.then:
+// CHECK-NEXT:   store i32 2, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup
+// CHECK: if.end:
+// CHECK-NEXT:   %call2 = call {{.*}} i1 @q(i32 {{.*}} 7)
+// CHECK-NEXT:   br i1 %call2, label %if.then3, label %if.end4
+// CHECK: if.then3:
+// CHECK-NEXT:   store i32 3, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup
+// CHECK: if.end4:
+// CHECK-NEXT:   store i32 0, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup
+// CHECK: cleanup:
+// CHECK-NEXT:   %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot, align 
4
+// CHECK-NEXT:   br label %while.cond5
+// CHECK: while.cond5:
+// CHECK-NEXT:   %call6 = call {{.*}} i1 @q(i32 {{.*}} 2)
+// CHECK-NEXT:   br i1 %call6, label %while.body7, label %while.end
+// CHECK: while.body7:
+// CHECK-NEXT:   %call8 = call {{.*}} i1 @q(i32 {{.*}} 4)
+// CHECK-NEXT:   br i1 %call8, label %if.then9, label %if.end10
+// CHECK: if.then9:
+// CHECK-NEXT:   store i32 4, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup14
+// CHECK: if.end10:
+// CHECK-NEXT:   %call11 = call {{.*}} i1 @q(i32 {{.*}} 5)
+// CHECK-NEXT:   br i1 %call11, label %if.then12, label %if.end13
+// CHECK: if.then12:
+// CHECK-NEXT:   store i32 5, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup14
+// CHECK: if.end13:
+// CHECK-NEXT:   store i32 0, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   br label %cleanup14
+// CHECK: cleanup14:
+// CHECK-NEXT:   %cleanup.dest.saved15 = load i32, ptr %cleanup.dest.slot, 
align 4
+// CHECK-NEXT:   %call16 = call {{.*}} i1 @q(i32 {{.*}} 3)
+// CHECK-NEXT:   store i32 %cleanup.dest.saved15, ptr %cleanup.dest.slot, 
align 4
+// CHECK-NEXT:   %cleanup.dest = load i32, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   switch i32 %cleanup.dest, label %unreachable [
+// CHECK-NEXT:     i32 0, label %cleanup.cont
+// CHECK-NEXT:     i32 4, label %while.cond5
+// CHECK-NEXT:     i32 5, label %while.end
+// CHECK-NEXT:   ]
+// CHECK: cleanup.cont:
+// CHECK-NEXT:   br label %while.cond5
+// CHECK: while.end:
+// CHECK-NEXT:   store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   %cleanup.dest17 = load i32, ptr %cleanup.dest.slot, align 4
+// CHECK-NEXT:   switch i32 %cleanup.dest17, label %unreachable [
+// CHECK-NEXT:     i32 0, label %cleanup.cont18
+// CHECK-NEXT:     i32 2, label %while.cond
+// CHECK-NEXT:     i32 3, label %while.end19
+// CHECK-NEXT:   ]
+// CHECK: cleanup.cont18:
+// CHECK-NEXT:   br label %while.cond
+// CHECK: while.end19:
+// CHECK-NEXT:   ret void
+// CHECK: unreachable:
+// CHECK-NEXT:   unreachable
+void cleanup_nested() {
+  while (q(1)) {
+    _Defer {
+      while (q(2)) {
+        _Defer {
+          q(3);
+        }
+        if (q(4)) continue;
+        if (q(5)) break;
+      }
+    };
+    if (q(6)) continue;
+    if (q(7)) break;
+  }
+}

diff  --git a/clang/test/Lexer/defer-keyword.cpp 
b/clang/test/Lexer/defer-keyword.cpp
new file mode 100644
index 0000000000000..929f2c58f974a
--- /dev/null
+++ b/clang/test/Lexer/defer-keyword.cpp
@@ -0,0 +1,5 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify -fdefer-ts %s
+
+// expected-no-diagnostics
+int _Defer;

diff  --git a/clang/test/Parser/defer-ts.c b/clang/test/Parser/defer-ts.c
new file mode 100644
index 0000000000000..118fe9ee3cc8f
--- /dev/null
+++ b/clang/test/Parser/defer-ts.c
@@ -0,0 +1,58 @@
+// RUN: %clang_cc1 -std=c11 -fsyntax-only -fdefer-ts -verify %s
+// RUN: %clang_cc1 -std=c23 -fsyntax-only -fdefer-ts -verify %s
+
+#define defer _Defer
+
+int g(void);
+int h(int x);
+
+void f1(void) {
+  defer 1; // expected-warning {{expression result unused}}
+  defer 1 + 1; // expected-warning {{expression result unused}}
+  defer "a"; // expected-warning {{expression result unused}}
+  defer "a" "b" "c"; // expected-warning {{expression result unused}}
+  defer defer 1; // expected-warning {{expression result unused}}
+  defer defer defer defer 1; // expected-warning {{expression result unused}}
+  defer (int) 4; // expected-warning {{expression result unused}}
+  defer g();
+
+  defer {}
+  defer { defer {} }
+  defer { defer {} defer {} }
+
+  defer if (g()) g();
+  defer while (g()) g();
+  defer for (int i = 0; i < 10; i++) h(i);
+  defer switch (g()) { case 1: g(); }
+
+  defer; // expected-warning {{defer statement has empty body}} expected-note 
{{put the semicolon on a separate line}}
+  defer
+    ;
+
+  defer a: g(); // expected-error {{substatement of defer must not be a label}}
+  defer b: {} // expected-error {{substatement of defer must not be a label}}
+  defer { c: g(); }
+
+  if (g()) defer g();
+  while (g()) defer g();
+  defer ({});
+  ({ defer g(); });
+
+  defer int x; // expected-error {{expected expression}}
+  defer void q() {} // expected-error {{expected expression}}
+}
+
+void f2(void) {
+  [[some, attributes]] defer g(); // expected-warning 2 {{unknown attribute}}
+  __attribute__((some_attribute)) defer g(); // expected-warning {{unknown 
attribute}}
+  [[some, attributes]] defer { g(); } // expected-warning 2 {{unknown 
attribute}}
+  __attribute__((some_attribute)) defer { g(); } // expected-warning {{unknown 
attribute}}
+}
+
+void f3(void) {
+  _Defer 1; // expected-warning {{expression result unused}}
+  _Defer {}
+  _Defer _Defer {}
+  _Defer { defer {} _Defer {} }
+  _Defer if (g()) g();
+}

diff  --git a/clang/test/Parser/defer-ts.cpp b/clang/test/Parser/defer-ts.cpp
new file mode 100644
index 0000000000000..fa25cac8575f6
--- /dev/null
+++ b/clang/test/Parser/defer-ts.cpp
@@ -0,0 +1,5 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fdefer-ts -verify %s
+
+void f() {
+  _Defer {} // expected-error {{use of undeclared identifier '_Defer'}}
+}

diff  --git a/clang/test/Preprocessor/defer-ts.c 
b/clang/test/Preprocessor/defer-ts.c
new file mode 100644
index 0000000000000..e4995ac9b23ea
--- /dev/null
+++ b/clang/test/Preprocessor/defer-ts.c
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -fsyntax-only -fdefer-ts -verify=enabled %s
+// RUN: %clang_cc1 -fsyntax-only -verify=disabled %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -fdefer-ts -verify=disabled %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -verify=disabled %s
+// enabled-no-diagnostics
+#if __STDC_DEFER_TS25755__ != 1
+// disabled-error@+1 {{Should have defined __STDC_DEFER_TS25755__ to 1}}
+#  error Should have defined __STDC_DEFER_TS25755__ to 1
+#endif

diff  --git a/clang/test/Sema/defer-ts-seh.c b/clang/test/Sema/defer-ts-seh.c
new file mode 100644
index 0000000000000..4b773ed3f09a0
--- /dev/null
+++ b/clang/test/Sema/defer-ts-seh.c
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -std=c23 -fdefer-ts -fms-compatibility -triple 
x86_64-windows-msvc -fsyntax-only -verify %s
+
+void f() {
+  __try {
+    _Defer {
+      __leave; // expected-error {{cannot __leave a defer statement}}
+    }
+  } __finally {}
+
+  __try {
+    _Defer {
+      __try {
+        __leave;
+      } __finally {}
+    }
+  } __finally {}
+}

diff  --git a/clang/test/Sema/defer-ts-sjlj.c b/clang/test/Sema/defer-ts-sjlj.c
new file mode 100644
index 0000000000000..49230fa721e0f
--- /dev/null
+++ b/clang/test/Sema/defer-ts-sjlj.c
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -triple x86_64-windows-msvc -std=gnu23 -fdefer-ts 
-fsyntax-only -fblocks -verify %s
+
+typedef void** jmp_buf;
+typedef void** sigjmp_buf;
+
+int setjmp(jmp_buf env);
+int _setjmp(jmp_buf env);
+int sigsetjmp(sigjmp_buf env, int savesigs);
+int __sigsetjmp(sigjmp_buf env, int savesigs);
+void longjmp(jmp_buf env, int val);
+void _longjmp(jmp_buf env, int val);
+void siglongjmp(sigjmp_buf env, int val);
+
+jmp_buf x;
+sigjmp_buf y;
+void f() {
+    _Defer {
+        __builtin_setjmp(x); // expected-error {{cannot use '__builtin_setjmp' 
inside a defer statement}}
+        __builtin_longjmp(x, 1); // expected-error {{cannot use 
'__builtin_longjmp' inside a defer statement}}
+        setjmp(x); // expected-error {{cannot use 'setjmp' inside a defer 
statement}}
+        _setjmp(x); // expected-error {{cannot use '_setjmp' inside a defer 
statement}}
+        sigsetjmp(y, 0); // expected-error {{cannot use 'sigsetjmp' inside a 
defer statement}}
+        __sigsetjmp(y, 0); // expected-error {{cannot use '__sigsetjmp' inside 
a defer statement}}
+        longjmp(x, 0); // expected-error {{cannot use 'longjmp' inside a defer 
statement}}
+        _longjmp(x, 0); // expected-error {{cannot use '_longjmp' inside a 
defer statement}}
+        siglongjmp(y, 0); // expected-error {{cannot use 'siglongjmp' inside a 
defer statement}}
+
+        (void) ^{
+            __builtin_setjmp(x);
+            __builtin_longjmp(x, 1);
+            setjmp(x);
+            _setjmp(x);
+            sigsetjmp(y, 0);
+            __sigsetjmp(y, 0);
+            longjmp(x, 0);
+            _longjmp(x, 0);
+            siglongjmp(y, 0);
+
+            _Defer {
+                __builtin_setjmp(x); // expected-error {{cannot use 
'__builtin_setjmp' inside a defer statement}}
+                __builtin_longjmp(x, 1); // expected-error {{cannot use 
'__builtin_longjmp' inside a defer statement}}
+                setjmp(x); // expected-error {{cannot use 'setjmp' inside a 
defer statement}}
+                _setjmp(x); // expected-error {{cannot use '_setjmp' inside a 
defer statement}}
+                sigsetjmp(y, 0); // expected-error {{cannot use 'sigsetjmp' 
inside a defer statement}}
+                __sigsetjmp(y, 0); // expected-error {{cannot use 
'__sigsetjmp' inside a defer statement}}
+                longjmp(x, 0); // expected-error {{cannot use 'longjmp' inside 
a defer statement}}
+                _longjmp(x, 0); // expected-error {{cannot use '_longjmp' 
inside a defer statement}}
+                siglongjmp(y, 0); // expected-error {{cannot use 'siglongjmp' 
inside a defer statement}}
+            }
+        };
+    }
+}

diff  --git a/clang/test/Sema/defer-ts.c b/clang/test/Sema/defer-ts.c
new file mode 100644
index 0000000000000..95c68fa213eaa
--- /dev/null
+++ b/clang/test/Sema/defer-ts.c
@@ -0,0 +1,172 @@
+// RUN: %clang_cc1 -std=c23 -fdefer-ts -fsyntax-only -verify %s
+
+#define defer _Defer
+
+void a();
+
+void f1() {
+  defer {
+    goto l1;
+    l1:
+  }
+
+  defer {
+    l2:
+    goto l2;
+  }
+}
+
+void f2() {
+  goto l1; // expected-error {{cannot jump from this goto statement to its 
label}}
+  defer { // expected-note {{jump enters a defer statement}}
+    l1:
+  }
+
+  goto l2; // expected-error {{cannot jump from this goto statement to its 
label}}
+  defer {} // expected-note {{jump bypasses defer statement}}
+  l2:
+}
+
+void f3() {
+  x:
+  defer { // expected-note {{jump exits a defer statement}}
+    goto x; // expected-error {{cannot jump from this goto statement to its 
label}}
+  }
+}
+
+void f4() {
+  defer { // expected-note {{jump exits a defer statement}}
+    goto y; // expected-error {{cannot jump from this goto statement to its 
label}}
+  }
+  y:
+}
+
+void f5() {
+  defer { // expected-note {{jump enters a defer statement}}
+    l2:
+  }
+  goto l2; // expected-error {{cannot jump from this goto statement to its 
label}}
+}
+
+void f6() {
+  goto b; // expected-error {{cannot jump from this goto statement to its 
label}}
+  {
+    defer {} // expected-note {{jump bypasses defer statement}}
+    b:
+  }
+
+  {
+    defer {} // expected-note {{jump bypasses defer statement}}
+    b2:
+  }
+  goto b2; // expected-error {{cannot jump from this goto statement to its 
label}}
+}
+
+void f7() {
+  defer { // expected-note {{jump bypasses defer statement}}
+    goto cross1; // expected-error {{cannot jump from this goto statement to 
its label}}
+    cross2:
+  }
+  defer { // expected-note {{jump exits a defer statement}} expected-note 
{{jump enters a defer statement}}
+    goto cross2; // expected-error {{cannot jump from this goto statement to 
its label}}
+    cross1:
+  }
+}
+
+void f8() {
+  defer {
+    return; // expected-error {{cannot return from a defer statement}}
+  }
+
+  {
+    defer {
+      return; // expected-error {{cannot return from a defer statement}}
+    }
+  }
+
+  switch (1) {
+    case 1: defer {
+      break; // expected-error {{cannot break out of a defer statement}}
+    }
+  }
+
+  for (;;) {
+    defer {
+      break; // expected-error {{cannot break out of a defer statement}}
+    }
+  }
+
+  for (;;) {
+    defer {
+      continue; // expected-error {{cannot continue loop outside of enclosing 
defer statement}}
+    }
+  }
+
+  switch (1) {
+    defer {} // expected-note {{jump bypasses defer statement}}
+  default: // expected-error {{cannot jump from switch statement to this case 
label}}
+    defer {}
+    break;
+  }
+
+  switch (1) {
+    case 1: {
+      defer { // expected-note {{jump enters a defer statement}}
+        case 2: {} // expected-error {{cannot jump from switch statement to 
this case label}}
+      }
+    }
+  }
+
+  switch (1) {
+    case 1: defer {
+      switch (2) { case 2: break; }
+    }
+  }
+
+  for (;;) {
+    defer { for (;;) break; }
+  }
+
+  for (;;) {
+    defer { for (;;) continue; }
+  }
+}
+
+void f9() {
+  {
+    defer {}
+    goto l1;
+  }
+  l1:
+
+  {
+    goto l2;
+    defer {}
+  }
+  l2:
+
+  {
+    { defer {} }
+    goto l3;
+  }
+  l3:
+
+  {
+    defer {}
+    { goto l4; }
+  }
+  l4:
+}
+
+void f10(int i) {
+  switch (i) {
+    defer case 12: break; // expected-error {{cannot break out of a defer 
statement}} \
+                             expected-error {{cannot jump from switch 
statement to this case label}} \
+                             expected-note {{jump enters a defer statement}} \
+                             expected-note {{jump bypasses defer statement}}
+
+    defer default: break; // expected-error {{cannot break out of a defer 
statement}} \
+                             expected-error {{cannot jump from switch 
statement to this case label}} \
+                             expected-note {{jump enters a defer statement}}
+  }
+}

diff  --git a/clang/tools/libclang/CXCursor.cpp 
b/clang/tools/libclang/CXCursor.cpp
index 0a43d73063c1f..c49ca567049c7 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -224,6 +224,11 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl 
*Parent,
     K = CXCursor_ReturnStmt;
     break;
 
+  // Not exposed for now because '_Defer' is currently just a TS.
+  case Stmt::DeferStmtClass:
+    K = CXCursor_UnexposedStmt;
+    break;
+
   case Stmt::GCCAsmStmtClass:
     K = CXCursor_GCCAsmStmt;
     break;


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

Reply via email to