https://github.com/tbaederr created 
https://github.com/llvm/llvm-project/pull/156856

None

>From 9c3e449ddf0aaa392547024041a8605b635cd988 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com>
Date: Thu, 4 Sep 2025 12:31:24 +0200
Subject: [PATCH] [clang][bytecode] Implement C23 named loops

---
 clang/lib/AST/ByteCode/Compiler.cpp           | 169 ++++++++++++------
 clang/lib/AST/ByteCode/Compiler.h             |  29 +--
 .../labeled-break-continue-constexpr.cpp      |   1 +
 3 files changed, 131 insertions(+), 68 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 56552f3969216..d97f8ad2add73 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -114,31 +114,25 @@ template <class Emitter> class LoopScope final {
 public:
   using LabelTy = typename Compiler<Emitter>::LabelTy;
   using OptLabelTy = typename Compiler<Emitter>::OptLabelTy;
+  using LabelInfo = typename Compiler<Emitter>::LabelInfo;
 
-  LoopScope(Compiler<Emitter> *Ctx, LabelTy BreakLabel, LabelTy ContinueLabel)
-      : Ctx(Ctx), OldBreakLabel(Ctx->BreakLabel),
-        OldContinueLabel(Ctx->ContinueLabel),
-        OldBreakVarScope(Ctx->BreakVarScope),
-        OldContinueVarScope(Ctx->ContinueVarScope) {
-    this->Ctx->BreakLabel = BreakLabel;
-    this->Ctx->ContinueLabel = ContinueLabel;
-    this->Ctx->BreakVarScope = this->Ctx->VarScope;
-    this->Ctx->ContinueVarScope = this->Ctx->VarScope;
-  }
+  LoopScope(Compiler<Emitter> *Ctx, const Stmt *Name, LabelTy BreakLabel,
+            LabelTy ContinueLabel)
+      : Ctx(Ctx) {
+#ifndef NDEBUG
+    for (const LabelInfo &LI : Ctx->LabelInfoStack)
+      assert(LI.Name != Name);
+#endif
 
-  ~LoopScope() {
-    this->Ctx->BreakLabel = OldBreakLabel;
-    this->Ctx->ContinueLabel = OldContinueLabel;
-    this->Ctx->ContinueVarScope = OldContinueVarScope;
-    this->Ctx->BreakVarScope = OldBreakVarScope;
+    this->Ctx->LabelInfoStack.emplace_back(Name, BreakLabel, ContinueLabel,
+                                           /*DefaultLabel=*/std::nullopt,
+                                           Ctx->VarScope);
   }
 
+  ~LoopScope() { this->Ctx->LabelInfoStack.pop_back(); }
+
 private:
   Compiler<Emitter> *Ctx;
-  OptLabelTy OldBreakLabel;
-  OptLabelTy OldContinueLabel;
-  VariableScope<Emitter> *OldBreakVarScope;
-  VariableScope<Emitter> *OldContinueVarScope;
 };
 
 // Sets the context for a switch scope, mapping labels.
@@ -147,32 +141,30 @@ template <class Emitter> class SwitchScope final {
   using LabelTy = typename Compiler<Emitter>::LabelTy;
   using OptLabelTy = typename Compiler<Emitter>::OptLabelTy;
   using CaseMap = typename Compiler<Emitter>::CaseMap;
+  using LabelInfo = typename Compiler<Emitter>::LabelInfo;
+
+  SwitchScope(Compiler<Emitter> *Ctx, const Stmt *Name, CaseMap &&CaseLabels,
+              LabelTy BreakLabel, OptLabelTy DefaultLabel)
+      : Ctx(Ctx), OldCaseLabels(std::move(this->Ctx->CaseLabels)) {
+#ifndef NDEBUG
+    for (const LabelInfo &LI : Ctx->LabelInfoStack)
+      assert(LI.Name != Name);
+#endif
 
-  SwitchScope(Compiler<Emitter> *Ctx, CaseMap &&CaseLabels, LabelTy BreakLabel,
-              OptLabelTy DefaultLabel)
-      : Ctx(Ctx), OldBreakLabel(Ctx->BreakLabel),
-        OldDefaultLabel(this->Ctx->DefaultLabel),
-        OldCaseLabels(std::move(this->Ctx->CaseLabels)),
-        OldLabelVarScope(Ctx->BreakVarScope) {
-    this->Ctx->BreakLabel = BreakLabel;
-    this->Ctx->DefaultLabel = DefaultLabel;
     this->Ctx->CaseLabels = std::move(CaseLabels);
-    this->Ctx->BreakVarScope = this->Ctx->VarScope;
+    this->Ctx->LabelInfoStack.emplace_back(Name, BreakLabel,
+                                           /*ContinueLabel=*/std::nullopt,
+                                           DefaultLabel, Ctx->VarScope);
   }
 
   ~SwitchScope() {
-    this->Ctx->BreakLabel = OldBreakLabel;
-    this->Ctx->DefaultLabel = OldDefaultLabel;
     this->Ctx->CaseLabels = std::move(OldCaseLabels);
-    this->Ctx->BreakVarScope = OldLabelVarScope;
+    this->Ctx->LabelInfoStack.pop_back();
   }
 
 private:
   Compiler<Emitter> *Ctx;
-  OptLabelTy OldBreakLabel;
-  OptLabelTy OldDefaultLabel;
   CaseMap OldCaseLabels;
-  VariableScope<Emitter> *OldLabelVarScope;
 };
 
 template <class Emitter> class StmtExprScope final {
@@ -5682,7 +5674,8 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt 
*S) {
 
   LabelTy CondLabel = this->getLabel(); // Label before the condition.
   LabelTy EndLabel = this->getLabel();  // Label after the loop.
-  LoopScope<Emitter> LS(this, EndLabel, CondLabel);
+  LocalScope<Emitter> WholeLoopScope(this);
+  LoopScope<Emitter> LS(this, S, EndLabel, CondLabel);
 
   this->fallthrough(CondLabel);
   this->emitLabel(CondLabel);
@@ -5712,8 +5705,7 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt 
*S) {
     return false;
   this->fallthrough(EndLabel);
   this->emitLabel(EndLabel);
-
-  return true;
+  return WholeLoopScope.destroyLocals();
 }
 
 template <class Emitter> bool Compiler<Emitter>::visitDoStmt(const DoStmt *S) {
@@ -5723,7 +5715,8 @@ template <class Emitter> bool 
Compiler<Emitter>::visitDoStmt(const DoStmt *S) {
   LabelTy StartLabel = this->getLabel();
   LabelTy EndLabel = this->getLabel();
   LabelTy CondLabel = this->getLabel();
-  LoopScope<Emitter> LS(this, EndLabel, CondLabel);
+  LocalScope<Emitter> WholeLoopScope(this);
+  LoopScope<Emitter> LS(this, S, EndLabel, CondLabel);
 
   this->fallthrough(StartLabel);
   this->emitLabel(StartLabel);
@@ -5745,7 +5738,7 @@ template <class Emitter> bool 
Compiler<Emitter>::visitDoStmt(const DoStmt *S) {
 
   this->fallthrough(EndLabel);
   this->emitLabel(EndLabel);
-  return true;
+  return WholeLoopScope.destroyLocals();
 }
 
 template <class Emitter>
@@ -5759,19 +5752,21 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
   LabelTy EndLabel = this->getLabel();
   LabelTy CondLabel = this->getLabel();
   LabelTy IncLabel = this->getLabel();
-  LoopScope<Emitter> LS(this, EndLabel, IncLabel);
 
+  LocalScope<Emitter> WholeLoopScope(this);
   if (Init && !this->visitStmt(Init))
     return false;
 
+  // Start of the loop body {
   this->fallthrough(CondLabel);
   this->emitLabel(CondLabel);
 
-  // Start of loop body.
   LocalScope<Emitter> CondScope(this);
-  if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt())
+  LoopScope<Emitter> LS(this, S, EndLabel, IncLabel);
+  if (const DeclStmt *CondDecl = S->getConditionVariableDeclStmt()) {
     if (!visitDeclStmt(CondDecl))
       return false;
+  }
 
   if (Cond) {
     if (!this->visitBool(Cond))
@@ -5794,12 +5789,12 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
     return false;
   if (!this->jump(CondLabel))
     return false;
-  // End of loop body.
+  // } End of loop body.
 
   this->emitLabel(EndLabel);
   // If we jumped out of the loop above, we still need to clean up the 
condition
   // scope.
-  return CondScope.destroyLocals();
+  return CondScope.destroyLocals() && WholeLoopScope.destroyLocals();
 }
 
 template <class Emitter>
@@ -5815,7 +5810,8 @@ bool Compiler<Emitter>::visitCXXForRangeStmt(const 
CXXForRangeStmt *S) {
   LabelTy EndLabel = this->getLabel();
   LabelTy CondLabel = this->getLabel();
   LabelTy IncLabel = this->getLabel();
-  LoopScope<Emitter> LS(this, EndLabel, IncLabel);
+  LocalScope<Emitter> WholeLoopScope(this);
+  LoopScope<Emitter> LS(this, S, EndLabel, IncLabel);
 
   // Emit declarations needed in the loop.
   if (Init && !this->visitStmt(Init))
@@ -5854,29 +5850,78 @@ bool Compiler<Emitter>::visitCXXForRangeStmt(const 
CXXForRangeStmt *S) {
 
   this->fallthrough(EndLabel);
   this->emitLabel(EndLabel);
-  return true;
+  return WholeLoopScope.destroyLocals();
 }
 
 template <class Emitter>
 bool Compiler<Emitter>::visitBreakStmt(const BreakStmt *S) {
-  if (!BreakLabel)
+  if (LabelInfoStack.empty())
     return false;
 
-  for (VariableScope<Emitter> *C = VarScope; C != BreakVarScope;
+  OptLabelTy TargetLabel = std::nullopt;
+  const Stmt *TargetLoop = S->getNamedLoopOrSwitch();
+  const VariableScope<Emitter> *BreakScope = nullptr;
+
+  if (!TargetLoop) {
+    for (const auto &LI : llvm::reverse(LabelInfoStack)) {
+      if (LI.BreakLabel) {
+        TargetLabel = *LI.BreakLabel;
+        BreakScope = LI.BreakOrContinueScope;
+        break;
+      }
+    }
+  } else {
+    for (auto LI : LabelInfoStack) {
+      if (LI.Name == TargetLoop) {
+        TargetLabel = *LI.BreakLabel;
+        BreakScope = LI.BreakOrContinueScope;
+        break;
+      }
+    }
+  }
+
+  assert(TargetLabel);
+
+  for (VariableScope<Emitter> *C = this->VarScope; C != BreakScope;
        C = C->getParent())
     C->emitDestruction();
-  return this->jump(*BreakLabel);
+
+  return this->jump(*TargetLabel);
 }
 
 template <class Emitter>
 bool Compiler<Emitter>::visitContinueStmt(const ContinueStmt *S) {
-  if (!ContinueLabel)
+  if (LabelInfoStack.empty())
     return false;
 
-  for (VariableScope<Emitter> *C = VarScope;
-       C && C->getParent() != ContinueVarScope; C = C->getParent())
+  OptLabelTy TargetLabel = std::nullopt;
+  const Stmt *TargetLoop = S->getNamedLoopOrSwitch();
+  const VariableScope<Emitter> *ContinueScope = nullptr;
+
+  if (!TargetLoop) {
+    for (const auto &LI : llvm::reverse(LabelInfoStack)) {
+      if (LI.ContinueLabel) {
+        TargetLabel = *LI.ContinueLabel;
+        ContinueScope = LI.BreakOrContinueScope;
+        break;
+      }
+    }
+  } else {
+    for (auto LI : LabelInfoStack) {
+      if (LI.Name == TargetLoop) {
+        TargetLabel = *LI.ContinueLabel;
+        ContinueScope = LI.BreakOrContinueScope;
+        break;
+      }
+    }
+  }
+  assert(TargetLabel);
+
+  for (VariableScope<Emitter> *C = VarScope; C != ContinueScope;
+       C = C->getParent())
     C->emitDestruction();
-  return this->jump(*ContinueLabel);
+
+  return this->jump(*TargetLabel);
 }
 
 template <class Emitter>
@@ -5889,7 +5934,7 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt 
*S) {
   LocalScope<Emitter> LS(this);
 
   LabelTy EndLabel = this->getLabel();
-  OptLabelTy DefaultLabel = std::nullopt;
+  UnsignedOrNone DefaultLabel = std::nullopt;
   unsigned CondVar =
       this->allocateLocalPrimitive(Cond, CondT, /*IsConst=*/true);
 
@@ -5950,7 +5995,8 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt 
*S) {
       return false;
   }
 
-  SwitchScope<Emitter> SS(this, std::move(CaseLabels), EndLabel, DefaultLabel);
+  SwitchScope<Emitter> SS(this, S, std::move(CaseLabels), EndLabel,
+                          DefaultLabel);
   if (!this->visitStmt(S->getBody()))
     return false;
   this->emitLabel(EndLabel);
@@ -5966,7 +6012,18 @@ bool Compiler<Emitter>::visitCaseStmt(const CaseStmt *S) 
{
 
 template <class Emitter>
 bool Compiler<Emitter>::visitDefaultStmt(const DefaultStmt *S) {
-  this->emitLabel(*DefaultLabel);
+  if (LabelInfoStack.empty())
+    return false;
+
+  LabelTy DefaultLabel;
+  for (const LabelInfo &LI : llvm::reverse(LabelInfoStack)) {
+    if (LI.DefaultLabel) {
+      DefaultLabel = *LI.DefaultLabel;
+      break;
+    }
+  }
+
+  this->emitLabel(DefaultLabel);
   return this->visitStmt(S->getSubStmt());
 }
 
diff --git a/clang/lib/AST/ByteCode/Compiler.h 
b/clang/lib/AST/ByteCode/Compiler.h
index 475faee4d3fde..63e0fdada0393 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -112,9 +112,23 @@ class Compiler : public 
ConstStmtVisitor<Compiler<Emitter>, bool>,
   // Aliases for types defined in the emitter.
   using LabelTy = typename Emitter::LabelTy;
   using AddrTy = typename Emitter::AddrTy;
-  using OptLabelTy = std::optional<LabelTy>;
+  using OptLabelTy = UnsignedOrNone;
   using CaseMap = llvm::DenseMap<const SwitchCase *, LabelTy>;
 
+  struct LabelInfo {
+    const Stmt *Name;
+    const VariableScope<Emitter> *BreakOrContinueScope;
+    OptLabelTy BreakLabel;
+    OptLabelTy ContinueLabel;
+    OptLabelTy DefaultLabel;
+    LabelInfo(const Stmt *Name, OptLabelTy BreakLabel, OptLabelTy 
ContinueLabel,
+              OptLabelTy DefaultLabel,
+              const VariableScope<Emitter> *BreakOrContinueScope)
+        : Name(Name), BreakOrContinueScope(BreakOrContinueScope),
+          BreakLabel(BreakLabel), ContinueLabel(ContinueLabel),
+          DefaultLabel(DefaultLabel) {}
+  };
+
   /// Current compilation context.
   Context &Ctx;
   /// Program to link to.
@@ -443,17 +457,8 @@ class Compiler : public 
ConstStmtVisitor<Compiler<Emitter>, bool>,
 
   /// Switch case mapping.
   CaseMap CaseLabels;
-
-  /// Scope to cleanup until when we see a break statement.
-  VariableScope<Emitter> *BreakVarScope = nullptr;
-  /// Point to break to.
-  OptLabelTy BreakLabel;
-  /// Scope to cleanup until when we see a continue statement.
-  VariableScope<Emitter> *ContinueVarScope = nullptr;
-  /// Point to continue to.
-  OptLabelTy ContinueLabel;
-  /// Default case label.
-  OptLabelTy DefaultLabel;
+  /// Stack of label information for loops and switch statements.
+  llvm::SmallVector<LabelInfo> LabelInfoStack;
 
   const FunctionDecl *CompilingFunction = nullptr;
 };
diff --git a/clang/test/SemaCXX/labeled-break-continue-constexpr.cpp 
b/clang/test/SemaCXX/labeled-break-continue-constexpr.cpp
index bec6c582a1f0d..d1b57adca91c1 100644
--- a/clang/test/SemaCXX/labeled-break-continue-constexpr.cpp
+++ b/clang/test/SemaCXX/labeled-break-continue-constexpr.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -fnamed-loops -std=c++23 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fnamed-loops -std=c++23 -fsyntax-only -verify %s 
-fexperimental-new-constant-interpreter
 // expected-no-diagnostics
 
 struct Tracker {

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to