-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[Clang] Add support for the C defer TS
#162848
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@llvm/pr-subscribers-clang-codegen Author: None (Sirraide) ChangesI 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:
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]
|
Following up on this, the author of the TS has modified their draft to require None of this has been seen by the full C committee yet and so it's subject to change. I recommend we only support |
Sgtm
Yeah, definitely. |
|
I’ve been busy w/ expansion statements lately, but I finally managed to track down that codegen bug: essentially, we were clobbering the cleanup slot while emitting cleanups inside the |
cor3ntin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Random unhinged thought: Can i put a defer statement inside a statement expression? in the initializer of a compound literal?
clang/lib/CodeGen/CGStmt.cpp
Outdated
| SaveAndRestore PreserveCleanupSlot{CGF.NormalCleanupDest, | ||
| RawAddress::invalid()}; | ||
| CGF.EmitStmt(Stmt.getBody()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@erichkeane can you have a look at that bit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CC @efriedma-quic @rjmccall for other codegen opinions as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I would appreciate it if someone more familiar w/ the cleanups code could confirm that this is enough because it’s rather complicated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is precedent in the code for this sort of construct. See PerformFinally in CGException.cpp: it loads the destination on entry, and stores it on exit. Not sure that's necessarily better, but I'd prefer to follow the existing pattern unless there's some compelling reason to diverge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh interesting, I didn’t think there was; I’ll take a look at that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I did that; that worked. It does mean we get a few more loads and stores even if the _Defer doesn’t use the cleanup slot at all, but the optimiser will just remove those so I don’t think it matters.
Yes, that's a good thing for us to be testing too though I would expect it to "just work".
No, |
clang/lib/CodeGen/CGStmt.cpp
Outdated
| SaveAndRestore PreserveCleanupSlot{CGF.NormalCleanupDest, | ||
| RawAddress::invalid()}; | ||
| CGF.EmitStmt(Stmt.getBody()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CC @efriedma-quic @rjmccall for other codegen opinions as well.
In a statement expression, sure, it’s a scope so that should just work. I’ll add a test for that. |
AaronBallman
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code generally looks good to me, but this still needs some dedicated review for CodeGen. I did find a testing bug though.
Co-authored-by: Aaron Ballman <[email protected]>
AaronBallman
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This LGTM, but I don't feel comfortable giving final sign-off because of the CodeGen changes. @rjmccall @efriedma-quic could you give the codegen changes a look? Thanks!
efriedma-quic
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
|
Alright, I’ll wait a bit longer in case anyone else has any comments, but if not then I’ll merge this later this week. |
cor3ntin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
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.