[clang] [clang-tools-extra] [Clang] Add support for the C `defer` TS (PR #162848)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 10 07:11:43 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-codegen
Author: None (Sirraide)
<details>
<summary>Changes</summary>
I was talking to @<!-- -->AaronBallman about this, and we decided it would make sense to open a PR for this at this point, even if we ultimately decide to defer merging it to a later point in time.
---
Patch is 57.12 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/162848.diff
40 Files Affected:
- (modified) clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp (+6)
- (modified) clang/include/clang/AST/RecursiveASTVisitor.h (+1)
- (modified) clang/include/clang/AST/Stmt.h (+51)
- (modified) clang/include/clang/Basic/DiagnosticParseKinds.td (+3)
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+15)
- (modified) clang/include/clang/Basic/LangOptions.def (+1)
- (modified) clang/include/clang/Basic/StmtNodes.td (+1)
- (modified) clang/include/clang/Basic/TokenKinds.def (+4)
- (modified) clang/include/clang/Driver/Options.td (+8)
- (modified) clang/include/clang/Parse/Parser.h (+10)
- (modified) clang/include/clang/Sema/Sema.h (+8)
- (modified) clang/include/clang/Serialization/ASTBitCodes.h (+1)
- (modified) clang/lib/AST/StmtPrinter.cpp (+5)
- (modified) clang/lib/AST/StmtProfile.cpp (+2)
- (modified) clang/lib/Basic/IdentifierTable.cpp (+4-1)
- (modified) clang/lib/CodeGen/CGStmt.cpp (+19)
- (modified) clang/lib/CodeGen/CodeGenFunction.h (+1)
- (modified) clang/lib/Driver/ToolChains/Clang.cpp (+3)
- (modified) clang/lib/Frontend/InitPreprocessor.cpp (+3)
- (modified) clang/lib/Parse/ParseStmt.cpp (+32)
- (modified) clang/lib/Sema/JumpDiagnostics.cpp (+26-1)
- (modified) clang/lib/Sema/SemaExceptionSpec.cpp (+1)
- (modified) clang/lib/Sema/SemaExpr.cpp (+29)
- (modified) clang/lib/Sema/SemaStmt.cpp (+38-6)
- (modified) clang/lib/Sema/TreeTransform.h (+8)
- (modified) clang/lib/Serialization/ASTReaderStmt.cpp (+10)
- (modified) clang/lib/Serialization/ASTWriterStmt.cpp (+7)
- (modified) clang/lib/StaticAnalyzer/Core/ExprEngine.cpp (+1)
- (added) clang/test/AST/ast-dump-defer-ts.c (+27)
- (added) clang/test/AST/ast-print-defer-ts.c (+33)
- (added) clang/test/CodeGen/defer-ts-seh.c (+44)
- (added) clang/test/CodeGen/defer-ts.c (+427)
- (added) clang/test/Lexer/defer-keyword.cpp (+5)
- (added) clang/test/Parser/defer-ts.c (+41)
- (added) clang/test/Parser/defer-ts.cpp (+9)
- (added) clang/test/Preprocessor/defer-ts.c (+4)
- (added) clang/test/Sema/defer-ts-seh.c (+17)
- (added) clang/test/Sema/defer-ts-sjlj.c (+52)
- (added) clang/test/Sema/defer-ts.c (+157)
- (modified) clang/tools/libclang/CXCursor.cpp (+5)
``````````diff
diff --git a/clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp
index 07bb08166a006..f1b4682c397ab 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp
@@ -241,6 +241,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/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 1d1b7f183f75a..a7a89e8338af5 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2560,6 +2560,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 76942f1a84f9a..219a99bee8432 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;
@@ -3232,6 +3243,46 @@ class ReturnStmt final
}
};
+/// DeferStmt - This represents a deferred statement.
+class DeferStmt : public Stmt {
+ friend class ASTStmtReader;
+
+ /// The deferred statement.
+ Stmt *Body;
+
+public:
+ DeferStmt(SourceLocation DeferLoc, Stmt *Body) : Stmt(DeferStmtClass) {
+ setDeferLoc(DeferLoc);
+ setBody(Body);
+ }
+
+ explicit DeferStmt(EmptyShell Empty) : Stmt(DeferStmtClass, Empty) {}
+
+ SourceLocation getDeferLoc() const { return DeferStmtBits.DeferLoc; }
+ void setDeferLoc(SourceLocation DeferLoc) {
+ DeferStmtBits.DeferLoc = DeferLoc;
+ }
+
+ 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 4d9e123eb4ef1..5fd7bdb08fe14 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -347,6 +347,9 @@ 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<"body of 'defer' statement cannot start with a label">;
+def err_defer_unsupported : Error<"'defer' statements are only supported in C">;
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 bd896524321d1..6b7bd117b990c 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6793,6 +6793,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<
@@ -6806,6 +6807,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))">;
@@ -6851,6 +6853,15 @@ 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">,
@@ -10926,6 +10937,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<
@@ -10936,6 +10949,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/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 84f5ab3443a59..3ba281a68fcb3 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 dd1a24405fae7..04f8f2ee1687e 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 9d1a23d1af218..436b1a3756dc1 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/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 16e1c396fedbe..28429c3aff83e 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1653,6 +1653,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>>;
+
// 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 30edd303e1824..52d8a0238cb2a 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -7501,6 +7501,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 d017d1f829015..1634ccf97f603 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10915,6 +10915,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();
@@ -11061,6 +11065,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 441047d64f48c..b287539681ded 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -2060,6 +2060,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/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 2c9c3581a2962..843110bc93f59 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 37c4d43ec0b2f..10794d9dcba26 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 4a2b77cd16bfc..79f9eb696d9e8 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -110,7 +110,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.
@@ -215,6 +216,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 aeff73d525c10..1e909b6dfafc4 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");
@@ -536,6 +537,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;
@@ -1997,6 +2001,21 @@ 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 flags) override {
+ CGF.EmitStmt(Stmt.getBody());
+ }
+};
+} // 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 727487b46054f..5e032eae37f11 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3606,6 +3606,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 f67454ee517bd..169127a71e76e 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7066,6 +7066,9 @@ 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, 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 edf0a091e087c..3c4b2fbfb8760 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -529,6 +529,9 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI,
Builder.defineMacro("__STDC_EMBED_EMPTY__",
llvm::itostr(static_cast<int>(EmbedResult::Empty)));
+ if (LangOpts.DeferTS)
+ Builder.defineMacro("__STDC_DEFER_TS25755__", "1");
+
if (LangOpts.ObjC)
Builder.defineMacro("__OBJC__");
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 2e7af1219547e..c18c3067b217b 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,10 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
Res = ParseReturnStatement();
SemiError = "co_return";
break;
+ case tok::kw_defer: // C defer TS: defer-statement
+ ProhibitAttributes(GNUAttrs);
+ ProhibitAttributes(CXX11Attrs);
+ return ParseDeferStatement(TrailingElseLoc);
case tok::kw_asm: {
for (const ParsedAttr &AL : CXX11Attrs)
@@ -2376,6 +2381,33 @@ 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();
+
+ // Diagnose this *after* parsing the body for better synchronisation.
+ if (getLangOpts().CPlusPlus) {
+ Diag(DeferLoc, diag::err_defer_unsupported);
+ 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 Jum...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/162848
More information about the cfe-commits
mailing list