https://github.com/Expertcoderz created 
https://github.com/llvm/llvm-project/pull/228655

[Reopened from #226754 after a Git accident.]

This PR fixes the issue described in #184060: having a labeled `continue` (or 
`break`) statement in a statement expression within the condition of a `switch` 
statement would cause Clang to crash:

```c
void foo() {
l1: for (;;) {
    switch (({ continue l1; 1; })) {}
  }
}
```

The issue seems to be caused by a null pointer dereference in 
`clang/lib/AST/Stmt.cpp`. I've checked the generated IR after applying the fix 
to ensure that the `break`/`continue` statements are working as expected. Also 
added regression tests.

>From 13a9873c60b9e047514509b0f3b2671af89077df Mon Sep 17 00:00:00 2001
From: Expertcoderz <[email protected]>
Date: Sun, 27 Sep 2026 04:36:04 +0000
Subject: [PATCH 1/5] [clang][AST] Fix crash on labeled break/continue within
 switch condition

---
 clang/docs/ReleaseNotes.md                  |  3 ++
 clang/lib/AST/Stmt.cpp                      |  4 +-
 clang/test/CodeGen/labeled-break-continue.c | 50 +++++++++++++++++++++
 3 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 929892fd34f7fcd..99645faae1afb48 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -822,6 +822,9 @@ features cannot lower the translation-unit ABI level;
 - Added missed information to the AST node representing the member function
   when calling a explicit object member function. (#GH218829)
 
+- Fixed a crash when encountering C2y labeled `break`/`continue` statements
+  in a statement expression within a `switch` conditon.
+
 #### Miscellaneous Bug Fixes
 
 #### Miscellaneous Clang Crashes Fixed
diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp
index 15d0e6435aaf3ad..cad8ebb9d854e2e 100644
--- a/clang/lib/AST/Stmt.cpp
+++ b/clang/lib/AST/Stmt.cpp
@@ -1535,7 +1535,9 @@ const Stmt *LabelStmt::getInnermostLabeledStmt() const {
 const Stmt *LoopControlStmt::getNamedLoopOrSwitch() const {
   if (!hasLabelTarget())
     return nullptr;
-  return getLabelDecl()->getStmt()->getInnermostLabeledStmt();
+
+  LabelStmt *Label = getLabelDecl()->getStmt();
+  return Label ? Label->getInnermostLabeledStmt() : nullptr;
 }
 
 DeferStmt::DeferStmt(EmptyShell Empty) : Stmt(DeferStmtClass, Empty) {}
diff --git a/clang/test/CodeGen/labeled-break-continue.c 
b/clang/test/CodeGen/labeled-break-continue.c
index f307a1bd79ab8c6..3d050a5cc6e44d6 100644
--- a/clang/test/CodeGen/labeled-break-continue.c
+++ b/clang/test/CodeGen/labeled-break-continue.c
@@ -279,3 +279,53 @@ void f7() {
     }
   }
 }
+
+/// https://github.com/llvm/llvm-project/issues/184060
+// CHECK-LABEL: define {{.*}} void @f8()
+// CHECK: entry:
+// CHECK:   %tmp = alloca i32, align 4
+// CHECK:   br label %l1
+// CHECK: l1:
+// CHECK:   br label %for.cond
+// CHECK: for.cond:
+// CHECK:   br label %for.end
+// CHECK: 0:
+// CHECK:   store i32 1, ptr %tmp, align 4
+// CHECK:   %1 = load i32, ptr %tmp, align 4
+// CHECK:   switch i32 %1, label %sw.epilog [
+// CHECK:   ]
+// CHECK: sw.epilog:
+// CHECK:   call {{.*}} i1 @g1()
+// CHECK:   br label %for.cond
+// CHECK: for.end:
+// CHECK:   ret void
+void f8() {
+l1: for (;;) {
+    switch (({ break l1; 1; })) {}
+    g1();
+  }
+}
+
+/// https://github.com/llvm/llvm-project/issues/184060
+// CHECK-LABEL: define {{.*}} void @f9()
+// CHECK: entry:
+// CHECK:   %tmp = alloca i32, align 4
+// CHECK:   br label %l1
+// CHECK: l1:
+// CHECK:   br label %for.cond
+// CHECK: for.cond:
+// CHECK:   br label %for.cond
+// CHECK: 0:
+// CHECK:   store i32 1, ptr %tmp, align 4
+// CHECK:   %1 = load i32, ptr %tmp, align 4
+// CHECK:   switch i32 %1, label %sw.epilog [
+// CHECK:   ]
+// CHECK: sw.epilog:
+// CHECK:   call {{.*}} i1 @g1()
+// CHECK:   br label %for.cond
+void f9() {
+l1: for (;;) {
+    switch (({ continue l1; 1; })) {}
+    g1();
+  }
+}

>From 5f8f3640d4f20c14d0e25f34858f478a7ca3f34f Mon Sep 17 00:00:00 2001
From: Expertcoderz <[email protected]>
Date: Sun, 27 Sep 2026 07:13:17 +0000
Subject: [PATCH 2/5] Apply typo fix from @kazutakahirata

Co-authored-by: Kazu Hirata <[email protected]>
---
 clang/docs/ReleaseNotes.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 99645faae1afb48..2c5d6dbfb4b052e 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -823,7 +823,7 @@ features cannot lower the translation-unit ABI level;
   when calling a explicit object member function. (#GH218829)
 
 - Fixed a crash when encountering C2y labeled `break`/`continue` statements
-  in a statement expression within a `switch` conditon.
+  in a statement expression within a `switch` condition.
 
 #### Miscellaneous Bug Fixes
 

>From 81a8f14edb6e6bbccdacb8c2f85a35dcb4dd5e4f Mon Sep 17 00:00:00 2001
From: Expertcoderz <[email protected]>
Date: Tue, 29 Sep 2026 00:06:34 +0000
Subject: [PATCH 3/5] Add constant evaluation tests for labeled break/continue

---
 clang/test/SemaCXX/labeled-break-continue.cpp | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/clang/test/SemaCXX/labeled-break-continue.cpp 
b/clang/test/SemaCXX/labeled-break-continue.cpp
index 3d34211ed745a99..d600ec5ff3cafda 100644
--- a/clang/test/SemaCXX/labeled-break-continue.cpp
+++ b/clang/test/SemaCXX/labeled-break-continue.cpp
@@ -49,3 +49,21 @@ void f3() {
     };
   }
 }
+
+void f4() {
+  l1: for (;;) {
+    constexpr int x = ({ // expected-error {{constexpr variable 'x' must be 
initialized by a constant expression}}
+      break l1; // expected-note {{not supported in a constant expression}}
+      1;
+    });
+  }
+}
+
+void f5() {
+  l1: for (;;) {
+    constexpr int x = ({ // expected-error {{constexpr variable 'x' must be 
initialized by a constant expression}}
+      continue l1; // expected-note {{not supported in a constant expression}}
+      1;
+    });
+  }
+}

>From 6739068e91b5d53c1e5b024967f7587b015b8c74 Mon Sep 17 00:00:00 2001
From: Expertcoderz <[email protected]>
Date: Sat, 3 Oct 2026 02:47:14 +0000
Subject: [PATCH 4/5] Move hasLabelTarget() check out of
 LoopControlStmt::getNamedLoopOrSwitch()

---
 clang/lib/AST/ByteCode/Compiler.cpp | 6 ++++--
 clang/lib/AST/ExprConstant.cpp      | 3 ++-
 clang/lib/AST/Stmt.cpp              | 4 +---
 clang/lib/CodeGen/CGStmt.cpp        | 2 +-
 4 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 01daace5b3c83e7..df37b705d495f48 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -7184,7 +7184,8 @@ bool Compiler<Emitter>::visitBreakStmt(const BreakStmt 
*S) {
     return false;
 
   OptLabelTy TargetLabel = std::nullopt;
-  const Stmt *TargetLoop = S->getNamedLoopOrSwitch();
+  const Stmt *TargetLoop =
+      S->hasLabelTarget() ? S->getNamedLoopOrSwitch() : nullptr;
   const VariableScope<Emitter> *BreakScope = nullptr;
 
   if (!TargetLoop) {
@@ -7224,7 +7225,8 @@ bool Compiler<Emitter>::visitContinueStmt(const 
ContinueStmt *S) {
     return false;
 
   OptLabelTy TargetLabel = std::nullopt;
-  const Stmt *TargetLoop = S->getNamedLoopOrSwitch();
+  const Stmt *TargetLoop =
+      S->hasLabelTarget() ? S->getNamedLoopOrSwitch() : nullptr;
   const VariableScope<Emitter> *ContinueScope = nullptr;
 
   if (!TargetLoop) {
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 3f295f35d1361a6..46c408c794f9607 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -6385,7 +6385,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, 
EvalInfo &Info,
   case Stmt::ContinueStmtClass:
   case Stmt::BreakStmtClass: {
     auto *B = cast<LoopControlStmt>(S);
-    Info.BreakContinueStack.push_back(B->getNamedLoopOrSwitch());
+    Info.BreakContinueStack.push_back(
+        B->hasLabelTarget() ? B->getNamedLoopOrSwitch() : nullptr);
     return isa<ContinueStmt>(S) ? ESR_Continue : ESR_Break;
   }
 
diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp
index cad8ebb9d854e2e..a8c17676d56f334 100644
--- a/clang/lib/AST/Stmt.cpp
+++ b/clang/lib/AST/Stmt.cpp
@@ -1533,9 +1533,7 @@ const Stmt *LabelStmt::getInnermostLabeledStmt() const {
 }
 
 const Stmt *LoopControlStmt::getNamedLoopOrSwitch() const {
-  if (!hasLabelTarget())
-    return nullptr;
-
+  assert(hasLabelTarget());
   LabelStmt *Label = getLabelDecl()->getStmt();
   return Label ? Label->getInnermostLabeledStmt() : nullptr;
 }
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 03b6e84a1c136cd..617929e1e613d86 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -1703,7 +1703,7 @@ auto CodeGenFunction::GetDestForLoopControlStmt(const 
LoopControlStmt &S)
     return &BreakContinueStack.back();
 
   const Stmt *LoopOrSwitch = S.getNamedLoopOrSwitch();
-  assert(LoopOrSwitch && "break/continue target not set?");
+  assert(LoopOrSwitch && "break/continue target label not available?");
   for (const BreakContinue &BC : llvm::reverse(BreakContinueStack))
     if (BC.LoopOrSwitch == LoopOrSwitch)
       return &BC;

>From 42a778088d3fedab9187f40d97adac16c41185cf Mon Sep 17 00:00:00 2001
From: Expertcoderz <[email protected]>
Date: Sat, 3 Oct 2026 02:48:18 +0000
Subject: [PATCH 5/5] Add comment on return value of
 LoopControlStmt::getNamedLoopOrSwitch()

---
 clang/include/clang/AST/Stmt.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 5d27ded64082daf..34bca3706f0a9ff 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -3109,7 +3109,8 @@ class LoopControlStmt : public Stmt {
   void setLabelDecl(LabelDecl *S) { TargetLabel = S; }
 
   /// If this is a named break/continue, get the loop or switch statement
-  /// that this targets.
+  /// that this targets. May return null if the target LabelStmt has not
+  /// yet been created.
   const Stmt *getNamedLoopOrSwitch() const;
 
   // Iterators

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

Reply via email to