From f6b377f2e83f2d3ff389dc1834bcdba70c358a39 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 09:57:43 +0100 Subject: [PATCH 1/3] Track current break target using a stack --- cpp2rust/converter/converter.cpp | 9 ++++++--- cpp2rust/converter/converter.h | 22 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 359210aa..d8d40f05 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -990,6 +990,7 @@ bool Converter::VisitIfStmt(clang::IfStmt *stmt) { } bool Converter::VisitWhileStmt(clang::WhileStmt *stmt) { + PushBreakTarget push(break_target_, BreakTarget::Loop); StrCat("'loop_:"); StrCat(keyword::kWhile); ConvertCondition(stmt->getCond()); @@ -1002,6 +1003,7 @@ bool Converter::VisitWhileStmt(clang::WhileStmt *stmt) { } bool Converter::VisitDoStmt(clang::DoStmt *stmt) { + PushBreakTarget push(break_target_, BreakTarget::Loop); StrCat("'loop_:"); StrCat(keyword::kLoop, token::kOpenCurlyBracket); curr_for_inc_.emplace(nullptr); @@ -1016,6 +1018,7 @@ bool Converter::VisitDoStmt(clang::DoStmt *stmt) { } bool Converter::VisitForStmt(clang::ForStmt *stmt) { + PushBreakTarget push(break_target_, BreakTarget::Loop); Convert(stmt->getInit()); StrCat("'loop_:"); StrCat(keyword::kWhile); @@ -1055,6 +1058,7 @@ void Converter::ConvertLoopVariable(clang::VarDecl *decl, void Converter::ConvertForRangeBody(clang::CXXForRangeStmt *stmt, const clang::VarDecl *map_iter_decl) { + PushBreakTarget push(break_target_, BreakTarget::Loop); std::optional skip; if (map_iter_decl) skip.emplace(*this, map_iter_decl); @@ -1137,7 +1141,7 @@ bool Converter::VisitCXXForRangeStmtIndexBased(clang::CXXForRangeStmt *stmt, bool Converter::VisitBreakStmt([[maybe_unused]] clang::BreakStmt *stmt) { StrCat(keyword::kBreak); - if (break_with_explicit_label_) { + if (isSwitchBreak()) { StrCat("'switch"); } return false; @@ -2644,6 +2648,7 @@ bool Converter::VisitSwitchCase(clang::SwitchCase *stmt) { } bool Converter::VisitSwitchStmt(clang::SwitchStmt *stmt) { + PushBreakTarget push(break_target_, BreakTarget::Switch); StrCat("'switch: {"); StrCat(std::format("let __match_cond = {};", ToString(stmt->getCond()))); StrCat("match __match_cond"); @@ -2653,7 +2658,6 @@ bool Converter::VisitSwitchStmt(clang::SwitchStmt *stmt) { auto body = llvm::cast(stmt->getBody()); assert(body); - break_with_explicit_label_ = true; for (auto it = body->body_begin(), end = body->body_end(); it != end;) { if (auto switch_case = clang::dyn_cast(*it)) { if (clang::isa(switch_case)) { @@ -2676,7 +2680,6 @@ bool Converter::VisitSwitchStmt(clang::SwitchStmt *stmt) { if (!has_default_case) { StrCat(R"( _ => {})"); } - break_with_explicit_label_ = false; StrCat("}"); StrCat("}"); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 74e8e750..2c66fbaf 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -463,10 +463,30 @@ class Converter : public clang::RecursiveASTVisitor { clang::ASTContext &ctx_; clang::FunctionDecl *curr_function_ = nullptr; bool in_function_formals_ = false; - bool break_with_explicit_label_ = false; std::stack curr_for_inc_; std::stack curr_init_type_; + enum class BreakTarget { Loop, Switch }; + std::stack break_target_; + + bool isSwitchBreak() const { + return !break_target_.empty() && break_target_.top() == BreakTarget::Switch; + } + + class PushBreakTarget { + public: + PushBreakTarget(std::stack &stack, BreakTarget target) + : stack_(stack) { + stack_.push(target); + } + ~PushBreakTarget() { stack_.pop(); } + PushBreakTarget(const PushBreakTarget &) = delete; + PushBreakTarget &operator=(const PushBreakTarget &) = delete; + + private: + std::stack &stack_; + }; + std::unordered_set map_iter_decls_; struct ScopedMapIterDecl { From 7bc6f8833ada58078cf36a58fc56e219e2e0e6be Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 09:58:05 +0100 Subject: [PATCH 2/3] Update tests --- tests/unit/out/refcount/switch_for_in_switch_break.rs | 2 +- tests/unit/out/refcount/switch_for_switch_for_break.rs | 2 +- tests/unit/out/refcount/switch_nested.rs | 6 +++--- tests/unit/out/refcount/switch_while_in_switch_break.rs | 2 +- tests/unit/out/unsafe/switch_for_in_switch_break.rs | 2 +- tests/unit/out/unsafe/switch_for_switch_for_break.rs | 2 +- tests/unit/out/unsafe/switch_nested.rs | 6 +++--- tests/unit/out/unsafe/switch_while_in_switch_break.rs | 2 +- tests/unit/switch_for_in_switch_break.cpp | 1 - tests/unit/switch_for_switch_for_break.cpp | 1 - tests/unit/switch_nested.cpp | 1 - tests/unit/switch_while_in_switch_break.cpp | 1 - 12 files changed, 12 insertions(+), 16 deletions(-) diff --git a/tests/unit/out/refcount/switch_for_in_switch_break.rs b/tests/unit/out/refcount/switch_for_in_switch_break.rs index 689034ca..444b1a17 100644 --- a/tests/unit/out/refcount/switch_for_in_switch_break.rs +++ b/tests/unit/out/refcount/switch_for_in_switch_break.rs @@ -16,7 +16,7 @@ pub fn for_in_switch_break_0(n: i32) -> i32 { let i: Value = Rc::new(RefCell::new(0)); 'loop_: while ((*i.borrow()) < 10) { if ((*i.borrow()) == 3) { - break 'switch; + break; } (*r.borrow_mut()) += (*i.borrow()); (*i.borrow_mut()).prefix_inc(); diff --git a/tests/unit/out/refcount/switch_for_switch_for_break.rs b/tests/unit/out/refcount/switch_for_switch_for_break.rs index 49a797da..8063b20a 100644 --- a/tests/unit/out/refcount/switch_for_switch_for_break.rs +++ b/tests/unit/out/refcount/switch_for_switch_for_break.rs @@ -18,7 +18,7 @@ pub fn for_switch_for_break_0(n: i32) -> i32 { let j: Value = Rc::new(RefCell::new(0)); 'loop_: while ((*j.borrow()) < 10) { if ((*j.borrow()) == 2) { - break 'switch; + break; } (*r.borrow_mut()) += 1; (*j.borrow_mut()).prefix_inc(); diff --git a/tests/unit/out/refcount/switch_nested.rs b/tests/unit/out/refcount/switch_nested.rs index d9ef00c2..3f98290d 100644 --- a/tests/unit/out/refcount/switch_nested.rs +++ b/tests/unit/out/refcount/switch_nested.rs @@ -32,15 +32,15 @@ pub fn nested_0(a: i32, b: i32) -> i32 { } }; (*r.borrow_mut()) += 1; - break; + break 'switch; } v if v == 2 => { (*r.borrow_mut()) = 2; - break; + break 'switch; } _ => { (*r.borrow_mut()) = -1_i32; - break; + break 'switch; } } }; diff --git a/tests/unit/out/refcount/switch_while_in_switch_break.rs b/tests/unit/out/refcount/switch_while_in_switch_break.rs index 0bc32f78..8cbb0698 100644 --- a/tests/unit/out/refcount/switch_while_in_switch_break.rs +++ b/tests/unit/out/refcount/switch_while_in_switch_break.rs @@ -16,7 +16,7 @@ pub fn while_in_switch_break_0(n: i32) -> i32 { let i: Value = Rc::new(RefCell::new(0)); 'loop_: while ((*i.borrow()) < 10) { if ((*i.borrow()) == 4) { - break 'switch; + break; } (*r.borrow_mut()) += (*i.borrow()); (*i.borrow_mut()).prefix_inc(); diff --git a/tests/unit/out/unsafe/switch_for_in_switch_break.rs b/tests/unit/out/unsafe/switch_for_in_switch_break.rs index 391a491c..41ef40a0 100644 --- a/tests/unit/out/unsafe/switch_for_in_switch_break.rs +++ b/tests/unit/out/unsafe/switch_for_in_switch_break.rs @@ -15,7 +15,7 @@ pub unsafe fn for_in_switch_break_0(mut n: i32) -> i32 { let mut i: i32 = 0; 'loop_: while ((i) < (10)) { if ((i) == (3)) { - break 'switch; + break; } r += i; i.prefix_inc(); diff --git a/tests/unit/out/unsafe/switch_for_switch_for_break.rs b/tests/unit/out/unsafe/switch_for_switch_for_break.rs index 4eb72969..65c304b9 100644 --- a/tests/unit/out/unsafe/switch_for_switch_for_break.rs +++ b/tests/unit/out/unsafe/switch_for_switch_for_break.rs @@ -17,7 +17,7 @@ pub unsafe fn for_switch_for_break_0(mut n: i32) -> i32 { let mut j: i32 = 0; 'loop_: while ((j) < (10)) { if ((j) == (2)) { - break 'switch; + break; } r += 1; j.prefix_inc(); diff --git a/tests/unit/out/unsafe/switch_nested.rs b/tests/unit/out/unsafe/switch_nested.rs index aeee5de3..5630db70 100644 --- a/tests/unit/out/unsafe/switch_nested.rs +++ b/tests/unit/out/unsafe/switch_nested.rs @@ -30,15 +30,15 @@ pub unsafe fn nested_0(mut a: i32, mut b: i32) -> i32 { } }; r += 1; - break; + break 'switch; } v if v == 2 => { r = 2; - break; + break 'switch; } _ => { r = -1_i32; - break; + break 'switch; } } }; diff --git a/tests/unit/out/unsafe/switch_while_in_switch_break.rs b/tests/unit/out/unsafe/switch_while_in_switch_break.rs index 42900cb9..2acb971c 100644 --- a/tests/unit/out/unsafe/switch_while_in_switch_break.rs +++ b/tests/unit/out/unsafe/switch_while_in_switch_break.rs @@ -15,7 +15,7 @@ pub unsafe fn while_in_switch_break_0(mut n: i32) -> i32 { let mut i: i32 = 0; 'loop_: while ((i) < (10)) { if ((i) == (4)) { - break 'switch; + break; } r += i; i.prefix_inc(); diff --git a/tests/unit/switch_for_in_switch_break.cpp b/tests/unit/switch_for_in_switch_break.cpp index d15e0487..e7dda947 100644 --- a/tests/unit/switch_for_in_switch_break.cpp +++ b/tests/unit/switch_for_in_switch_break.cpp @@ -1,4 +1,3 @@ -// panic #include int for_in_switch_break(int n) { diff --git a/tests/unit/switch_for_switch_for_break.cpp b/tests/unit/switch_for_switch_for_break.cpp index 769b41ca..7a304a03 100644 --- a/tests/unit/switch_for_switch_for_break.cpp +++ b/tests/unit/switch_for_switch_for_break.cpp @@ -1,4 +1,3 @@ -// panic #include int for_switch_for_break(int n) { diff --git a/tests/unit/switch_nested.cpp b/tests/unit/switch_nested.cpp index 37f7f0bc..c7473597 100644 --- a/tests/unit/switch_nested.cpp +++ b/tests/unit/switch_nested.cpp @@ -1,4 +1,3 @@ -// no-compile #include int nested(int a, int b) { diff --git a/tests/unit/switch_while_in_switch_break.cpp b/tests/unit/switch_while_in_switch_break.cpp index 419f7ac5..897a7a90 100644 --- a/tests/unit/switch_while_in_switch_break.cpp +++ b/tests/unit/switch_while_in_switch_break.cpp @@ -1,4 +1,3 @@ -// panic #include int while_in_switch_break(int n) { From af549fe60dafca62c2e9eeae9e7bcc0e26a60b99 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 10:32:12 +0100 Subject: [PATCH 3/3] Fail if translation-fail test successfully compiles --- tests/lit/lit/formats/Cpp2RustTest.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/lit/lit/formats/Cpp2RustTest.py b/tests/lit/lit/formats/Cpp2RustTest.py index 934b267f..2daf1491 100644 --- a/tests/lit/lit/formats/Cpp2RustTest.py +++ b/tests/lit/lit/formats/Cpp2RustTest.py @@ -115,6 +115,9 @@ def fail(str, code = fail_code): return lit.Test.XFAIL, '' return fail('cpp2rust failed\n' + err) + if should_not_translate: + return fail('expected translation-fail but cpp2rust succeeded') + expected_file = self.getExpectedFile(filepath, model, fname) if not os.path.exists(expected_file) and not replace_expected: return fail('no expected file')