From 9b264497a4ecfefb9bcaf7ab061e0a535ad400e8 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 21 Apr 2026 11:25:54 +0100 Subject: [PATCH 01/24] Move visited_cases in class scope Rename visited_cases to visited_switch_cases_ and move it in class scope. VisitSwitchStmt always resets the set of visited switch cases. The old implementation was buggy because it saved reused SwitchCase pointers across translation units, making contain() return true for switches declared in other TUs. --- cpp2rust/converter/converter.cpp | 8 ++++---- cpp2rust/converter/converter.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 359210aa..12ffa24f 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -2617,13 +2617,11 @@ bool Converter::VisitImplicitValueInitExpr(clang::ImplicitValueInitExpr *expr) { return false; } -static std::unordered_set visited_cases; - bool Converter::VisitSwitchCase(clang::SwitchCase *stmt) { - if (visited_cases.contains(stmt)) { + if (visited_switch_cases_.contains(stmt)) { return false; } - visited_cases.insert(stmt); + visited_switch_cases_.insert(stmt); if (auto case_stmt = clang::dyn_cast(stmt)) { Convert(case_stmt->getLHS()); @@ -2653,6 +2651,8 @@ bool Converter::VisitSwitchStmt(clang::SwitchStmt *stmt) { auto body = llvm::cast(stmt->getBody()); assert(body); + visited_switch_cases_ = {}; + 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)) { diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 74e8e750..4d545b8a 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -466,6 +466,7 @@ class Converter : public clang::RecursiveASTVisitor { bool break_with_explicit_label_ = false; std::stack curr_for_inc_; std::stack curr_init_type_; + std::unordered_set visited_switch_cases_; std::unordered_set map_iter_decls_; From c33fbee4e1b3afbec09cc868837318f569a153ad Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 21 Apr 2026 14:54:29 +0100 Subject: [PATCH 02/24] Handle default as middle case C++ allows: switch (x) { default: ... case 1: ... } In rust, default needs to be always on the last position, otherwise, all values of x, even 1, will hit the default arm. Hence, the above C++ exmaple becomes: match x { 1 => ... _ => ... } As such, the algorithm for translating the cases becomes: for (case: GetTopLevelSwitchCases()) { if (ChainContainsDefault(case)) { defer the conversion for the end } Convert(case) Convert(GetSwitchArmBody(case)) } Convert(deferred default) ChainContainsDefault traverses the stacked case statements starting from a top level case. For example: case 2: case 3: default: ... is deferred for the end of the match arms and is translated as: _ => {} i.e., drop the case 2, case 3 from the output, only convert as if it was only default. --- cpp2rust/converter/converter.cpp | 123 ++++++++++++++++++++++--------- cpp2rust/converter/converter.h | 1 - 2 files changed, 88 insertions(+), 36 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 12ffa24f..34079202 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -2617,65 +2617,118 @@ bool Converter::VisitImplicitValueInitExpr(clang::ImplicitValueInitExpr *expr) { return false; } -bool Converter::VisitSwitchCase(clang::SwitchCase *stmt) { - if (visited_switch_cases_.contains(stmt)) { - return false; +static std::vector +GetTopLevelSwitchCases(clang::SwitchStmt *stmt) { + std::vector cases; + if (auto *body = llvm::dyn_cast(stmt->getBody())) { + for (auto *s : body->body()) { + if (auto *sc = clang::dyn_cast(s)) { + cases.push_back(sc); + } + } + } + return cases; +} + +static bool ChainContainsDefault(clang::SwitchCase *c) { + for (clang::Stmt *cur = c;;) { + if (clang::isa(cur)) { + return true; + } + auto *sc = clang::dyn_cast(cur); + if (!sc) { + return false; + } + cur = sc->getSubStmt(); } - visited_switch_cases_.insert(stmt); + return false; +} - if (auto case_stmt = clang::dyn_cast(stmt)) { - Convert(case_stmt->getLHS()); +static clang::Stmt *ChainLeafBody(clang::SwitchCase *c) { + clang::Stmt *cur = c->getSubStmt(); + while (auto *sc = clang::dyn_cast(cur)) { + cur = sc->getSubStmt(); } + return cur; +} - if (clang::isa(stmt->getSubStmt())) { - StrCat("|| v == "); - } else { - if (clang::isa(stmt)) { - StrCat(" => {"); - } else { - StrCat("_ => {"); +static std::vector GetSwitchArmBody(clang::CompoundStmt *body, + clang::SwitchCase *head) { + std::vector out; + out.push_back(ChainLeafBody(head)); + auto it = body->body_begin(), end = body->body_end(); + while (it != end && *it != head) { + ++it; + } + assert(it != end); + ++it; + while (it != end && !clang::isa(*it)) { + out.push_back(*it); + ++it; + } + return out; +} + +bool Converter::VisitSwitchCase(clang::SwitchCase *stmt) { + clang::Stmt *cur = stmt; + clang::SwitchCase *last = nullptr; + bool first = true; + + while (auto *sc = clang::dyn_cast(cur)) { + if (auto *case_stmt = clang::dyn_cast(sc)) { + if (!first) { + StrCat("|| v == "); + } + Convert(case_stmt->getLHS()); } + last = sc; + first = false; + cur = sc->getSubStmt(); } - Convert(stmt->getSubStmt()); + if (clang::isa(last)) { + StrCat(" => {"); + } else /* DefaultStmt */ { + StrCat("_ => {"); + } return false; } bool Converter::VisitSwitchStmt(clang::SwitchStmt *stmt) { + auto *body = clang::dyn_cast(stmt->getBody()); + assert(body); + StrCat("'switch: {"); StrCat(std::format("let __match_cond = {};", ToString(stmt->getCond()))); StrCat("match __match_cond"); StrCat("{"); - bool has_default_case = false; - auto body = llvm::cast(stmt->getBody()); - assert(body); - - visited_switch_cases_ = {}; - 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)) { - StrCat("v if v == "); - } else { - has_default_case = true; - } - VisitSwitchCase(switch_case); - ++it; - } - while (it != end && !clang::isa(*it)) { - Convert(*it); - ++it; + clang::SwitchCase *default_case = nullptr; + for (auto *sc : GetTopLevelSwitchCases(stmt)) { + if (ChainContainsDefault(sc)) { + default_case = sc; + continue; + } + StrCat("v if v == "); + VisitSwitchCase(sc); + for (auto *t : GetSwitchArmBody(body, sc)) { + Convert(t); } - StrCat("},"); } - if (!has_default_case) { + if (default_case) { + StrCat("_ => {"); + for (auto *t : GetSwitchArmBody(body, default_case)) { + Convert(t); + } + StrCat("},"); + } else { StrCat(R"( _ => {})"); } + break_with_explicit_label_ = false; StrCat("}"); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 4d545b8a..74e8e750 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -466,7 +466,6 @@ class Converter : public clang::RecursiveASTVisitor { bool break_with_explicit_label_ = false; std::stack curr_for_inc_; std::stack curr_init_type_; - std::unordered_set visited_switch_cases_; std::unordered_set map_iter_decls_; From 0c143fe069191f02992111b5bfb07a4876742f52 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 21 Apr 2026 15:08:12 +0100 Subject: [PATCH 03/24] Add switch tests --- tests/unit/out/refcount/switch.rs | 195 ++++++++++ tests/unit/out/unsafe/switch.rs | 192 ++++++++++ tests/unit/switch.cpp | 602 ++++++++++++++++++++++++++++++ 3 files changed, 989 insertions(+) create mode 100644 tests/unit/out/refcount/switch.rs create mode 100644 tests/unit/out/unsafe/switch.rs create mode 100644 tests/unit/switch.cpp diff --git a/tests/unit/out/refcount/switch.rs b/tests/unit/out/refcount/switch.rs new file mode 100644 index 00000000..a6e7b1d6 --- /dev/null +++ b/tests/unit/out/refcount/switch.rs @@ -0,0 +1,195 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn basic_0(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let r: Value = Rc::new(RefCell::new(0)); + 'switch: { + let __match_cond = (*x.borrow()); + match __match_cond { + v if v == 0 => { + (*r.borrow_mut()) = 10; + break 'switch; + } + v if v == 1 => { + (*r.borrow_mut()) = 20; + break 'switch; + } + v if v == 2 => { + (*r.borrow_mut()) = 30; + break 'switch; + } + _ => { + (*r.borrow_mut()) = 40; + break 'switch; + } + } + }; + return (*r.borrow()); +} +pub fn stacked_1(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let r: Value = Rc::new(RefCell::new(0)); + 'switch: { + let __match_cond = (*x.borrow()); + match __match_cond { + v if v == 1 || v == 2 || v == 3 => { + (*r.borrow_mut()) = 100; + break 'switch; + } + v if v == 4 || v == 5 => { + (*r.borrow_mut()) = 200; + break 'switch; + } + _ => { + (*r.borrow_mut()) = 300; + break 'switch; + } + } + }; + return (*r.borrow()); +} +pub fn no_default_2(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let r: Value = Rc::new(RefCell::new(-1_i32)); + 'switch: { + let __match_cond = (*x.borrow()); + match __match_cond { + v if v == 7 => { + (*r.borrow_mut()) = 1; + break 'switch; + } + v if v == 8 => { + (*r.borrow_mut()) = 2; + break 'switch; + } + _ => {} + } + }; + return (*r.borrow()); +} +pub fn only_default_3(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let r: Value = Rc::new(RefCell::new(0)); + 'switch: { + let __match_cond = (*x.borrow()); + match __match_cond { + _ => { + (*r.borrow_mut()) = 42; + break 'switch; + } + } + }; + return (*r.borrow()); +} +pub fn default_middle_4(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let r: Value = Rc::new(RefCell::new(0)); + 'switch: { + let __match_cond = (*x.borrow()); + match __match_cond { + v if v == 1 => { + (*r.borrow_mut()) = 1; + break 'switch; + } + v if v == 2 => { + (*r.borrow_mut()) = 2; + break 'switch; + } + _ => { + (*r.borrow_mut()) = 99; + break 'switch; + } + } + }; + return (*r.borrow()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (({ + let _x: i32 = 0; + basic_0(_x) + }) == 10) + ); + assert!( + (({ + let _x: i32 = 2; + basic_0(_x) + }) == 30) + ); + assert!( + (({ + let _x: i32 = 99; + basic_0(_x) + }) == 40) + ); + assert!( + (({ + let _x: i32 = 1; + stacked_1(_x) + }) == 100) + ); + assert!( + (({ + let _x: i32 = 3; + stacked_1(_x) + }) == 100) + ); + assert!( + (({ + let _x: i32 = 5; + stacked_1(_x) + }) == 200) + ); + assert!( + (({ + let _x: i32 = 9; + stacked_1(_x) + }) == 300) + ); + assert!( + (({ + let _x: i32 = 7; + no_default_2(_x) + }) == 1) + ); + assert!( + (({ + let _x: i32 = 42; + no_default_2(_x) + }) == -1_i32) + ); + assert!( + (({ + let _x: i32 = 1; + only_default_3(_x) + }) == 42) + ); + assert!( + (({ + let _x: i32 = 1; + default_middle_4(_x) + }) == 1) + ); + assert!( + (({ + let _x: i32 = 2; + default_middle_4(_x) + }) == 2) + ); + assert!( + (({ + let _x: i32 = 99; + default_middle_4(_x) + }) == 99) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/switch.rs b/tests/unit/out/unsafe/switch.rs new file mode 100644 index 00000000..37951eb1 --- /dev/null +++ b/tests/unit/out/unsafe/switch.rs @@ -0,0 +1,192 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn basic_0(mut x: i32) -> i32 { + let mut r: i32 = 0; + 'switch: { + let __match_cond = x; + match __match_cond { + v if v == 0 => { + r = 10; + break 'switch; + } + v if v == 1 => { + r = 20; + break 'switch; + } + v if v == 2 => { + r = 30; + break 'switch; + } + _ => { + r = 40; + break 'switch; + } + } + }; + return r; +} +pub unsafe fn stacked_1(mut x: i32) -> i32 { + let mut r: i32 = 0; + 'switch: { + let __match_cond = x; + match __match_cond { + v if v == 1 || v == 2 || v == 3 => { + r = 100; + break 'switch; + } + v if v == 4 || v == 5 => { + r = 200; + break 'switch; + } + _ => { + r = 300; + break 'switch; + } + } + }; + return r; +} +pub unsafe fn no_default_2(mut x: i32) -> i32 { + let mut r: i32 = -1_i32; + 'switch: { + let __match_cond = x; + match __match_cond { + v if v == 7 => { + r = 1; + break 'switch; + } + v if v == 8 => { + r = 2; + break 'switch; + } + _ => {} + } + }; + return r; +} +pub unsafe fn only_default_3(mut x: i32) -> i32 { + let mut r: i32 = 0; + 'switch: { + let __match_cond = x; + match __match_cond { + _ => { + r = 42; + break 'switch; + } + } + }; + return r; +} +pub unsafe fn default_middle_4(mut x: i32) -> i32 { + let mut r: i32 = 0; + 'switch: { + let __match_cond = x; + match __match_cond { + v if v == 1 => { + r = 1; + break 'switch; + } + v if v == 2 => { + r = 2; + break 'switch; + } + _ => { + r = 99; + break 'switch; + } + } + }; + return r; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + ((unsafe { + let _x: i32 = 0; + basic_0(_x) + }) == (10)) + ); + assert!( + ((unsafe { + let _x: i32 = 2; + basic_0(_x) + }) == (30)) + ); + assert!( + ((unsafe { + let _x: i32 = 99; + basic_0(_x) + }) == (40)) + ); + assert!( + ((unsafe { + let _x: i32 = 1; + stacked_1(_x) + }) == (100)) + ); + assert!( + ((unsafe { + let _x: i32 = 3; + stacked_1(_x) + }) == (100)) + ); + assert!( + ((unsafe { + let _x: i32 = 5; + stacked_1(_x) + }) == (200)) + ); + assert!( + ((unsafe { + let _x: i32 = 9; + stacked_1(_x) + }) == (300)) + ); + assert!( + ((unsafe { + let _x: i32 = 7; + no_default_2(_x) + }) == (1)) + ); + assert!( + ((unsafe { + let _x: i32 = 42; + no_default_2(_x) + }) == (-1_i32)) + ); + assert!( + ((unsafe { + let _x: i32 = 1; + only_default_3(_x) + }) == (42)) + ); + assert!( + ((unsafe { + let _x: i32 = 1; + default_middle_4(_x) + }) == (1)) + ); + assert!( + ((unsafe { + let _x: i32 = 2; + default_middle_4(_x) + }) == (2)) + ); + assert!( + ((unsafe { + let _x: i32 = 99; + default_middle_4(_x) + }) == (99)) + ); + return 0; +} diff --git a/tests/unit/switch.cpp b/tests/unit/switch.cpp new file mode 100644 index 00000000..6fea4b8d --- /dev/null +++ b/tests/unit/switch.cpp @@ -0,0 +1,602 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +int basic(int x) { + int r = 0; + switch (x) { + case 0: + r = 10; + break; + case 1: + r = 20; + break; + case 2: + r = 30; + break; + default: + r = 40; + break; + } + return r; +} + +int stacked(int x) { + int r = 0; + switch (x) { + case 1: + case 2: + case 3: + r = 100; + break; + case 4: + case 5: + r = 200; + break; + default: + r = 300; + break; + } + return r; +} + +int no_default(int x) { + int r = -1; + switch (x) { + case 7: + r = 1; + break; + case 8: + r = 2; + break; + } + return r; +} + +int only_default(int x) { + int r = 0; + switch (x) { + default: + r = 42; + break; + } + return r; +} + +int default_middle(int x) { + int r = 0; + switch (x) { + case 1: + r = 1; + break; + default: + r = 99; + break; + case 2: + r = 2; + break; + } + return r; +} + +int default_first(int x) { + int r = 0; + switch (x) { + default: + r = 7; + break; + case 1: + r = 1; + break; + case 2: + r = 2; + break; + } + return r; +} + +int empty_switch(int x) { + switch (x) { + } + return x; +} + +int switch_char(char c) { + switch (c) { + case 'a': + return 1; + case 'b': + return 2; + case '\n': + return 3; + case '\0': + return 4; + default: + return 0; + } +} + +enum Color { kRed, kGreen, kBlue }; + +int switch_enum(Color c) { + switch (c) { + case kRed: + return 10; + case kGreen: + return 20; + case kBlue: + return 30; + } + return -1; +} + +int compound_case_body(int x) { + int r = 0; + switch (x) { + case 1: { + int y = 10; + int z = 20; + r = y + z; + break; + } + case 2: { + int y = 100; + r = y - 1; + break; + } + default: + r = -1; + break; + } + return r; +} + +int nested(int a, int b) { + int r = 0; + switch (a) { + case 1: + switch (b) { + case 10: + r = 11; + break; + case 20: + r = 12; + break; + default: + r = 13; + break; + } + r += 1; + break; + case 2: + r = 2; + break; + default: + r = -1; + break; + } + return r; +} + +int switch_in_loop(int n) { + int r = 0; + for (int i = 0; i < n; ++i) { + switch (i % 3) { + case 0: + r += 1; + break; + case 1: + r += 2; + break; + default: + r += 3; + break; + } + r += 10; + } + return r; +} + +int stacked_block(int x) { + int r = 0; + switch (x) { + case 1: + case 2: + case 3: { + int y = x * 2; + r = y + 1; + break; + } + default: + r = 0; + break; + } + return r; +} + +int double_it(int v) { return v * 2; } + +int switch_on_call(int x) { + switch (double_it(x)) { + case 0: + return 100; + case 2: + return 200; + case 4: + return 400; + default: + return -1; + } +} + +int switch_on_assignment(int x) { + int y = 0; + int r = 0; + switch (y = x + 1) { + case 1: + r = 10; + break; + case 2: + r = 20; + break; + default: + r = y; + break; + } + return r; +} + +int mixed_literal_cases(int x) { + switch (x) { + case -1: + return 1; + case 0x10: + return 2; + case 0xFE80: + return 3; + case -0xFF: + return 4; + default: + return 0; + } +} + +int mixed_return_break(int x) { + int r = -1; + switch (x) { + case 0: + return 100; + case 1: + r = 10; + break; + case 2: + return 200; + default: + r = 99; + break; + } + return r; +} + +int empty_case_with_break(int x) { + int r = 5; + switch (x) { + case 1: + break; + case 2: + r = 2; + break; + default: + r = 9; + break; + } + return r; +} + +int fallthrough_one(int x) { + int r = 0; + switch (x) { + case 1: + r += 10; + [[fallthrough]]; + case 2: + r += 20; + break; + default: + r = -1; + break; + } + return r; +} + +int fallthrough_chain(int x) { + int r = 0; + switch (x) { + case 1: + r += 1; + [[fallthrough]]; + case 2: + r += 2; + [[fallthrough]]; + case 3: + r += 4; + [[fallthrough]]; + case 4: + r += 8; + break; + default: + r = -1; + break; + } + return r; +} + +int fallthrough_default(int x, int flag) { + int r = 0; + switch (x) { + case 7: + if (flag) { + r = 100; + break; + } + [[fallthrough]]; + default: + r = 42; + break; + } + return r; +} + +int fallthrough_into_block(int x) { + int r = 0; + switch (x) { + case 1: + r += 1; + [[fallthrough]]; + case 2: { + int tmp = r * 10; + r = tmp + 5; + break; + } + default: + r = -1; + break; + } + return r; +} + +int switch_complex_cond(int *p, int bias) { + switch (*p + bias) { + case 0: + return 1; + case 5: + return 2; + case 10: + return 3; + default: + return 0; + } +} + +int switch_in_dowhile(int n) { + int r = 0; + int i = 0; + do { + switch (i) { + case 0: + r += 1; + break; + case 1: + r += 10; + break; + default: + r += 100; + break; + } + ++i; + } while (i < n); + return r; +} + +int continue_inside_switch(int n) { + int r = 0; + for (int i = 0; i < n; ++i) { + switch (i) { + case 0: + case 2: + case 4: + continue; + default: + r += i; + break; + } + r += 1000; + } + return r; +} + +int case_then_default(int x) { + int r = 0; + switch (x) { + case 1: + default: + r = 10; + break; + case 2: + r = 20; + break; + } + return r; +} + +int default_then_case(int x) { + int r = 0; + switch (x) { + case 1: + r = 1; + break; + default: + case 2: + r = 77; + break; + case 3: + r = 3; + break; + } + return r; +} + +int cases_and_default_stacked(int x) { + int r = 0; + switch (x) { + case 1: + case 2: + default: + r = 42; + break; + case 3: + r = 3; + break; + } + return r; +} + +int stacked_with_inner_fallthrough(int x, int flag) { + int r = 0; + switch (x) { + case 1: + case 2: + case 3: + if (!flag) { + r = 50; + break; + } + [[fallthrough]]; + default: + r = 999; + break; + } + return r; +} + +int main() { + assert(basic(0) == 10); + assert(basic(2) == 30); + assert(basic(99) == 40); + + assert(stacked(1) == 100); + assert(stacked(3) == 100); + assert(stacked(5) == 200); + assert(stacked(9) == 300); + + assert(no_default(7) == 1); + assert(no_default(42) == -1); + + assert(only_default(1) == 42); + + assert(default_middle(1) == 1); + assert(default_middle(2) == 2); + assert(default_middle(99) == 99); + + assert(default_first(1) == 1); + assert(default_first(99) == 7); + + assert(empty_switch(5) == 5); + + assert(switch_char('a') == 1); + assert(switch_char('b') == 2); + assert(switch_char('\n') == 3); + assert(switch_char('\0') == 4); + assert(switch_char('z') == 0); + + assert(switch_enum(kRed) == 10); + assert(switch_enum(kGreen) == 20); + assert(switch_enum(kBlue) == 30); + + assert(compound_case_body(1) == 30); + assert(compound_case_body(2) == 99); + assert(compound_case_body(9) == -1); + + assert(nested(1, 10) == 12); + assert(nested(1, 99) == 14); + assert(nested(2, 0) == 2); + assert(nested(3, 3) == -1); + + assert(switch_in_loop(6) == 72); + + assert(stacked_block(2) == 5); + assert(stacked_block(9) == 0); + + assert(switch_on_call(0) == 100); + assert(switch_on_call(1) == 200); + assert(switch_on_call(2) == 400); + assert(switch_on_call(99) == -1); + + assert(switch_on_assignment(0) == 10); + assert(switch_on_assignment(1) == 20); + assert(switch_on_assignment(9) == 10); + + assert(mixed_literal_cases(-1) == 1); + assert(mixed_literal_cases(0x10) == 2); + assert(mixed_literal_cases(0xFE80) == 3); + assert(mixed_literal_cases(-0xFF) == 4); + assert(mixed_literal_cases(7) == 0); + + assert(mixed_return_break(0) == 100); + assert(mixed_return_break(1) == 10); + assert(mixed_return_break(2) == 200); + assert(mixed_return_break(99) == 99); + + assert(empty_case_with_break(1) == 5); + assert(empty_case_with_break(2) == 2); + assert(empty_case_with_break(9) == 9); + + assert(fallthrough_one(1) == 30); + assert(fallthrough_one(2) == 20); + assert(fallthrough_one(99) == -1); + + assert(fallthrough_chain(1) == 15); + assert(fallthrough_chain(2) == 14); + assert(fallthrough_chain(3) == 12); + assert(fallthrough_chain(4) == 8); + assert(fallthrough_chain(99) == -1); + + assert(fallthrough_default(7, 0) == 42); + assert(fallthrough_default(7, 1) == 100); + assert(fallthrough_default(99, 0) == 42); + + assert(fallthrough_into_block(1) == 15); + assert(fallthrough_into_block(2) == 5); + assert(fallthrough_into_block(99) == -1); + + int p_val = 5; + assert(switch_complex_cond(&p_val, 0) == 2); + assert(switch_complex_cond(&p_val, 5) == 3); + assert(switch_complex_cond(&p_val, -5) == 1); + assert(switch_complex_cond(&p_val, 99) == 0); + + assert(switch_in_dowhile(1) == 1); + assert(switch_in_dowhile(3) == 1 + 10 + 100); + + assert(continue_inside_switch(6) == (1 + 3 + 5) + 3 * 1000); + + assert(case_then_default(1) == 10); + assert(case_then_default(2) == 20); + assert(case_then_default(99) == 10); + + assert(default_then_case(1) == 1); + assert(default_then_case(2) == 77); + assert(default_then_case(3) == 3); + assert(default_then_case(99) == 77); + + assert(cases_and_default_stacked(1) == 42); + assert(cases_and_default_stacked(2) == 42); + assert(cases_and_default_stacked(3) == 3); + assert(cases_and_default_stacked(99) == 42); + + assert(stacked_with_inner_fallthrough(1, 0) == 50); + assert(stacked_with_inner_fallthrough(2, 1) == 999); + assert(stacked_with_inner_fallthrough(99, 0) == 999); + return 0; +} From 614e78e44cbda7fb81073bea66e34fcd915ae298 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 21 Apr 2026 15:37:41 +0100 Subject: [PATCH 04/24] Fix nested switches --- cpp2rust/converter/converter.cpp | 6 +++--- cpp2rust/converter/converter.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 34079202..6b1cdfc8 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1137,7 +1137,7 @@ bool Converter::VisitCXXForRangeStmtIndexBased(clang::CXXForRangeStmt *stmt, bool Converter::VisitBreakStmt([[maybe_unused]] clang::BreakStmt *stmt) { StrCat(keyword::kBreak); - if (break_with_explicit_label_) { + if (switch_depth_ > 0) { StrCat("'switch"); } return false; @@ -2703,7 +2703,7 @@ bool Converter::VisitSwitchStmt(clang::SwitchStmt *stmt) { StrCat("match __match_cond"); StrCat("{"); - break_with_explicit_label_ = true; + ++switch_depth_; clang::SwitchCase *default_case = nullptr; for (auto *sc : GetTopLevelSwitchCases(stmt)) { @@ -2729,7 +2729,7 @@ bool Converter::VisitSwitchStmt(clang::SwitchStmt *stmt) { StrCat(R"( _ => {})"); } - break_with_explicit_label_ = false; + --switch_depth_; StrCat("}"); StrCat("}"); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 74e8e750..1c753e0d 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -463,7 +463,7 @@ class Converter : public clang::RecursiveASTVisitor { clang::ASTContext &ctx_; clang::FunctionDecl *curr_function_ = nullptr; bool in_function_formals_ = false; - bool break_with_explicit_label_ = false; + int switch_depth_ = 0; std::stack curr_for_inc_; std::stack curr_init_type_; From c4880c0b9473c6ad2d7a780085cc48d389fb7b47 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 22 Apr 2026 16:31:25 +0100 Subject: [PATCH 05/24] Move helper switch/case functions in converter_lib --- cpp2rust/converter/converter.cpp | 52 ------------------- cpp2rust/converter/converter_lib.cpp | 74 ++++++++++++++++++++++++++++ cpp2rust/converter/converter_lib.h | 14 ++++++ 3 files changed, 88 insertions(+), 52 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 6b1cdfc8..6af01113 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -2617,58 +2617,6 @@ bool Converter::VisitImplicitValueInitExpr(clang::ImplicitValueInitExpr *expr) { return false; } -static std::vector -GetTopLevelSwitchCases(clang::SwitchStmt *stmt) { - std::vector cases; - if (auto *body = llvm::dyn_cast(stmt->getBody())) { - for (auto *s : body->body()) { - if (auto *sc = clang::dyn_cast(s)) { - cases.push_back(sc); - } - } - } - return cases; -} - -static bool ChainContainsDefault(clang::SwitchCase *c) { - for (clang::Stmt *cur = c;;) { - if (clang::isa(cur)) { - return true; - } - auto *sc = clang::dyn_cast(cur); - if (!sc) { - return false; - } - cur = sc->getSubStmt(); - } - return false; -} - -static clang::Stmt *ChainLeafBody(clang::SwitchCase *c) { - clang::Stmt *cur = c->getSubStmt(); - while (auto *sc = clang::dyn_cast(cur)) { - cur = sc->getSubStmt(); - } - return cur; -} - -static std::vector GetSwitchArmBody(clang::CompoundStmt *body, - clang::SwitchCase *head) { - std::vector out; - out.push_back(ChainLeafBody(head)); - auto it = body->body_begin(), end = body->body_end(); - while (it != end && *it != head) { - ++it; - } - assert(it != end); - ++it; - while (it != end && !clang::isa(*it)) { - out.push_back(*it); - ++it; - } - return out; -} - bool Converter::VisitSwitchCase(clang::SwitchCase *stmt) { clang::Stmt *cur = stmt; clang::SwitchCase *last = nullptr; diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index d493ad74..3019931e 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -660,4 +660,78 @@ clang::Expr *CreateConversionToBool(clang::Expr *expr, clang::ASTContext &ctx) { /*BasePath=*/nullptr, clang::VK_PRValue, clang::FPOptionsOverride()); } +std::vector +GetTopLevelSwitchCases(clang::SwitchStmt *stmt) { + std::vector cases; + if (auto *body = llvm::dyn_cast(stmt->getBody())) { + for (auto *s : body->body()) { + if (auto *sc = clang::dyn_cast(s)) { + cases.push_back(sc); + } + } + } + return cases; +} + +bool ChainContainsDefault(clang::SwitchCase *c) { + for (clang::Stmt *cur = c;;) { + if (clang::isa(cur)) { + return true; + } + auto *sc = clang::dyn_cast(cur); + if (!sc) { + return false; + } + cur = sc->getSubStmt(); + } + return false; +} + +clang::Stmt *ChainLeafBody(clang::SwitchCase *c) { + clang::Stmt *cur = c->getSubStmt(); + while (auto *sc = clang::dyn_cast(cur)) { + cur = sc->getSubStmt(); + } + return cur; +} + +std::vector GetSwitchArmBody(clang::CompoundStmt *body, + clang::SwitchCase *head) { + std::vector out; + out.push_back(ChainLeafBody(head)); + auto it = body->body_begin(), end = body->body_end(); + while (it != end && *it != head) { + ++it; + } + assert(it != end); + ++it; + while (it != end && !clang::isa(*it)) { + out.push_back(*it); + ++it; + } + return out; +} + +bool SwitchArmHasFallthrough(clang::Stmt *stmt) { + if (stmt) { + if (clang::isa(stmt) || + clang::isa(stmt)) { + return false; + } + } + return true; +} + +bool SwitchHasFallthrough(clang::SwitchStmt *stmt) { + if (auto *body = clang::dyn_cast(stmt->getBody())) { + for (auto top_level_case : GetTopLevelSwitchCases(stmt)) { + auto arm = GetSwitchArmBody(body, top_level_case); + if (arm.empty() || SwitchArmHasFallthrough(arm.back())) { + return true; + } + } + } + return false; +} + } // namespace cpp2rust diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index 36776336..8395a389 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -154,4 +154,18 @@ bool ContainsVAArgExpr(const clang::Stmt *stmt); clang::Expr *CreateConversionToBool(clang::Expr *expr, clang::ASTContext &ctx); +std::vector +GetTopLevelSwitchCases(clang::SwitchStmt *stmt); + +bool ChainContainsDefault(clang::SwitchCase *c); + +clang::Stmt *ChainLeafBody(clang::SwitchCase *c); + +std::vector GetSwitchArmBody(clang::CompoundStmt *body, + clang::SwitchCase *head); + +bool SwitchArmHasFallthrough(clang::Stmt *stmt); + +bool SwitchHasFallthrough(clang::SwitchStmt *stmt); + } // namespace cpp2rust From 2aa8cc8159569e98bb8cdcce7afce6b06c61bd10 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 22 Apr 2026 16:37:56 +0100 Subject: [PATCH 06/24] Emit switch! macro on switch stmt with fallthrough --- cpp2rust/converter/converter.cpp | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 6af01113..7d4227e5 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -2646,12 +2646,20 @@ bool Converter::VisitSwitchStmt(clang::SwitchStmt *stmt) { auto *body = clang::dyn_cast(stmt->getBody()); assert(body); - StrCat("'switch: {"); - StrCat(std::format("let __match_cond = {};", ToString(stmt->getCond()))); - StrCat("match __match_cond"); - StrCat("{"); + bool has_fallthrough = SwitchHasFallthrough(stmt); - ++switch_depth_; + if (has_fallthrough) { + StrCat("switch!(match ", ToString(stmt->getCond()), " {"); + } else { + StrCat("'switch: {"); + StrCat(std::format("let __match_cond = {};", ToString(stmt->getCond()))); + StrCat("match __match_cond"); + StrCat("{"); + } + + if (!has_fallthrough) { + ++switch_depth_; + } clang::SwitchCase *default_case = nullptr; for (auto *sc : GetTopLevelSwitchCases(stmt)) { @@ -2677,10 +2685,16 @@ bool Converter::VisitSwitchStmt(clang::SwitchStmt *stmt) { StrCat(R"( _ => {})"); } - --switch_depth_; + if (!has_fallthrough) { + --switch_depth_; + } - StrCat("}"); - StrCat("}"); + if (has_fallthrough) { + StrCat("})"); + } else { + StrCat("}"); + StrCat("}"); + } return false; } From 5455888233ff9163c83639384b77610b93c68140 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 22 Apr 2026 16:38:15 +0100 Subject: [PATCH 07/24] Update tests --- tests/unit/out/refcount/switch.rs | 1076 ++++++++++++++++++++++++++++- tests/unit/out/unsafe/switch.rs | 1043 +++++++++++++++++++++++++++- tests/unit/switch.cpp | 12 + 3 files changed, 2081 insertions(+), 50 deletions(-) diff --git a/tests/unit/out/refcount/switch.rs b/tests/unit/out/refcount/switch.rs index a6e7b1d6..01c2451c 100644 --- a/tests/unit/out/refcount/switch.rs +++ b/tests/unit/out/refcount/switch.rs @@ -109,6 +109,555 @@ pub fn default_middle_4(x: i32) -> i32 { }; return (*r.borrow()); } +pub fn default_first_5(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let r: Value = Rc::new(RefCell::new(0)); + 'switch: { + let __match_cond = (*x.borrow()); + match __match_cond { + v if v == 1 => { + (*r.borrow_mut()) = 1; + break 'switch; + } + v if v == 2 => { + (*r.borrow_mut()) = 2; + break 'switch; + } + _ => { + (*r.borrow_mut()) = 7; + break 'switch; + } + } + }; + return (*r.borrow()); +} +pub fn empty_switch_6(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + 'switch: { + let __match_cond = (*x.borrow()); + match __match_cond { + _ => {} + } + }; + return (*x.borrow()); +} +pub fn switch_char_7(c: u8) -> i32 { + let c: Value = Rc::new(RefCell::new(c)); + 'switch: { + let __match_cond = ((*c.borrow()) as i32); + match __match_cond { + v if v == (('a' as u8) as i32) => { + return 1; + } + v if v == (('b' as u8) as i32) => { + return 2; + } + v if v == (('\n' as u8) as i32) => { + return 3; + } + v if v == (('\0' as u8) as i32) => { + return 4; + } + _ => { + return 0; + } + } + }; + panic!("ub: non-void function does not return a value") +} +#[derive(Clone, Copy, PartialEq, Debug, Default)] +enum Color { + #[default] + kRed = 0, + kGreen = 1, + kBlue = 2, +} +pub fn switch_enum_8(c: Color) -> i32 { + let c: Value = Rc::new(RefCell::new(c)); + 'switch: { + let __match_cond = ((*c.borrow()) as i32); + match __match_cond { + v if v == (Color::kRed as i32) => { + return 10; + } + v if v == (Color::kGreen as i32) => { + return 20; + } + v if v == (Color::kBlue as i32) => { + return 30; + } + _ => {} + } + }; + return -1_i32; +} +pub fn compound_case_body_9(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let r: Value = Rc::new(RefCell::new(0)); + switch!(match (*x.borrow()) { + v if v == 1 => { + let y: Value = Rc::new(RefCell::new(10)); + let z: Value = Rc::new(RefCell::new(20)); + (*r.borrow_mut()) = ((*y.borrow()) + (*z.borrow())); + break; + } + v if v == 2 => { + let y: Value = Rc::new(RefCell::new(100)); + (*r.borrow_mut()) = ((*y.borrow()) - 1); + break; + } + _ => { + (*r.borrow_mut()) = -1_i32; + break; + } + }); + return (*r.borrow()); +} +pub fn nested_10(a: i32, b: i32) -> i32 { + let a: Value = Rc::new(RefCell::new(a)); + let b: Value = Rc::new(RefCell::new(b)); + let r: Value = Rc::new(RefCell::new(0)); + 'switch: { + let __match_cond = (*a.borrow()); + match __match_cond { + v if v == 1 => { + 'switch: { + let __match_cond = (*b.borrow()); + match __match_cond { + v if v == 10 => { + (*r.borrow_mut()) = 11; + break 'switch; + } + v if v == 20 => { + (*r.borrow_mut()) = 12; + break 'switch; + } + _ => { + (*r.borrow_mut()) = 13; + break 'switch; + } + } + }; + (*r.borrow_mut()) += 1; + break 'switch; + } + v if v == 2 => { + (*r.borrow_mut()) = 2; + break 'switch; + } + _ => { + (*r.borrow_mut()) = -1_i32; + break 'switch; + } + } + }; + return (*r.borrow()); +} +pub fn switch_in_loop_11(n: i32) -> i32 { + let n: Value = Rc::new(RefCell::new(n)); + let r: Value = Rc::new(RefCell::new(0)); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((*i.borrow()) < (*n.borrow())) { + 'switch: { + let __match_cond = ((*i.borrow()) % 3); + match __match_cond { + v if v == 0 => { + (*r.borrow_mut()) += 1; + break 'switch; + } + v if v == 1 => { + (*r.borrow_mut()) += 2; + break 'switch; + } + _ => { + (*r.borrow_mut()) += 3; + break 'switch; + } + } + }; + (*r.borrow_mut()) += 10; + (*i.borrow_mut()).prefix_inc(); + } + return (*r.borrow()); +} +pub fn stacked_block_12(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let r: Value = Rc::new(RefCell::new(0)); + switch!(match (*x.borrow()) { + v if v == 1 || v == 2 || v == 3 => { + let y: Value = Rc::new(RefCell::new(((*x.borrow()) * 2))); + (*r.borrow_mut()) = ((*y.borrow()) + 1); + break; + } + _ => { + (*r.borrow_mut()) = 0; + break; + } + }); + return (*r.borrow()); +} +pub fn double_it_13(v: i32) -> i32 { + let v: Value = Rc::new(RefCell::new(v)); + return ((*v.borrow()) * 2); +} +pub fn switch_on_call_14(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + 'switch: { + let __match_cond = ({ + let _v: i32 = (*x.borrow()); + double_it_13(_v) + }); + match __match_cond { + v if v == 0 => { + return 100; + } + v if v == 2 => { + return 200; + } + v if v == 4 => { + return 400; + } + _ => { + return -1_i32; + } + } + }; + panic!("ub: non-void function does not return a value") +} +pub fn switch_on_assignment_15(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let y: Value = Rc::new(RefCell::new(0)); + let r: Value = Rc::new(RefCell::new(0)); + 'switch: { + let __match_cond = { + (*y.borrow_mut()) = ((*x.borrow()) + 1); + (*y.borrow()) + }; + match __match_cond { + v if v == 1 => { + (*r.borrow_mut()) = 10; + break 'switch; + } + v if v == 2 => { + (*r.borrow_mut()) = 20; + break 'switch; + } + _ => { + (*r.borrow_mut()) = (*y.borrow()); + break 'switch; + } + } + }; + return (*r.borrow()); +} +pub fn mixed_literal_cases_16(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + 'switch: { + let __match_cond = (*x.borrow()); + match __match_cond { + v if v == -1_i32 => { + return 1; + } + v if v == 16 => { + return 2; + } + v if v == 65152 => { + return 3; + } + v if v == -255_i32 => { + return 4; + } + _ => { + return 0; + } + } + }; + panic!("ub: non-void function does not return a value") +} +pub fn mixed_return_break_17(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let r: Value = Rc::new(RefCell::new(-1_i32)); + 'switch: { + let __match_cond = (*x.borrow()); + match __match_cond { + v if v == 0 => { + return 100; + } + v if v == 1 => { + (*r.borrow_mut()) = 10; + break 'switch; + } + v if v == 2 => { + return 200; + } + _ => { + (*r.borrow_mut()) = 99; + break 'switch; + } + } + }; + return (*r.borrow()); +} +pub fn empty_case_with_break_18(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let r: Value = Rc::new(RefCell::new(5)); + 'switch: { + let __match_cond = (*x.borrow()); + match __match_cond { + v if v == 1 => { + break 'switch; + } + v if v == 2 => { + (*r.borrow_mut()) = 2; + break 'switch; + } + _ => { + (*r.borrow_mut()) = 9; + break 'switch; + } + } + }; + return (*r.borrow()); +} +pub fn fallthrough_one_19(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let r: Value = Rc::new(RefCell::new(0)); + switch!(match (*x.borrow()) { + v if v == 1 => { + (*r.borrow_mut()) += 10; + } + v if v == 2 => { + (*r.borrow_mut()) += 20; + break; + } + _ => { + (*r.borrow_mut()) = -1_i32; + break; + } + }); + return (*r.borrow()); +} +pub fn fallthrough_chain_20(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let r: Value = Rc::new(RefCell::new(0)); + switch!(match (*x.borrow()) { + v if v == 1 => { + (*r.borrow_mut()) += 1; + } + v if v == 2 => { + (*r.borrow_mut()) += 2; + } + v if v == 3 => { + (*r.borrow_mut()) += 4; + } + v if v == 4 => { + (*r.borrow_mut()) += 8; + break; + } + _ => { + (*r.borrow_mut()) = -1_i32; + break; + } + }); + return (*r.borrow()); +} +pub fn fallthrough_default_21(x: i32, flag: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let flag: Value = Rc::new(RefCell::new(flag)); + let r: Value = Rc::new(RefCell::new(0)); + switch!(match (*x.borrow()) { + v if v == 7 => { + if ((*flag.borrow()) != 0) { + (*r.borrow_mut()) = 100; + break; + }; + } + _ => { + (*r.borrow_mut()) = 42; + break; + } + }); + return (*r.borrow()); +} +pub fn fallthrough_into_block_22(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let r: Value = Rc::new(RefCell::new(0)); + switch!(match (*x.borrow()) { + v if v == 1 => { + (*r.borrow_mut()) += 1; + } + v if v == 2 => { + let tmp: Value = Rc::new(RefCell::new(((*r.borrow()) * 10))); + (*r.borrow_mut()) = ((*tmp.borrow()) + 5); + break; + } + _ => { + (*r.borrow_mut()) = -1_i32; + break; + } + }); + return (*r.borrow()); +} +pub fn switch_complex_cond_23(p: Ptr, bias: i32) -> i32 { + let p: Value> = Rc::new(RefCell::new(p)); + let bias: Value = Rc::new(RefCell::new(bias)); + 'switch: { + let __match_cond = { + let _lhs = ((*p.borrow()).read()); + _lhs + (*bias.borrow()) + }; + match __match_cond { + v if v == 0 => { + return 1; + } + v if v == 5 => { + return 2; + } + v if v == 10 => { + return 3; + } + _ => { + return 0; + } + } + }; + panic!("ub: non-void function does not return a value") +} +pub fn switch_in_dowhile_24(n: i32) -> i32 { + let n: Value = Rc::new(RefCell::new(n)); + let r: Value = Rc::new(RefCell::new(0)); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: loop { + 'switch: { + let __match_cond = (*i.borrow()); + match __match_cond { + v if v == 0 => { + (*r.borrow_mut()) += 1; + break 'switch; + } + v if v == 1 => { + (*r.borrow_mut()) += 10; + break 'switch; + } + _ => { + (*r.borrow_mut()) += 100; + break 'switch; + } + } + }; + (*i.borrow_mut()).prefix_inc(); + if !((*i.borrow()) < (*n.borrow())) { + break; + } + } + return (*r.borrow()); +} +pub fn continue_inside_switch_25(n: i32) -> i32 { + let n: Value = Rc::new(RefCell::new(n)); + let r: Value = Rc::new(RefCell::new(0)); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((*i.borrow()) < (*n.borrow())) { + switch!(match (*i.borrow()) { + v if v == 0 || v == 2 || v == 4 => { + (*i.borrow_mut()).prefix_inc(); + continue 'loop_; + } + _ => { + (*r.borrow_mut()) += (*i.borrow()); + break; + } + }); + (*r.borrow_mut()) += 1000; + (*i.borrow_mut()).prefix_inc(); + } + return (*r.borrow()); +} +pub fn case_then_default_26(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let r: Value = Rc::new(RefCell::new(0)); + 'switch: { + let __match_cond = (*x.borrow()); + match __match_cond { + v if v == 2 => { + (*r.borrow_mut()) = 20; + break 'switch; + } + _ => { + (*r.borrow_mut()) = 10; + break 'switch; + } + } + }; + return (*r.borrow()); +} +pub fn default_then_case_27(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let r: Value = Rc::new(RefCell::new(0)); + 'switch: { + let __match_cond = (*x.borrow()); + match __match_cond { + v if v == 1 => { + (*r.borrow_mut()) = 1; + break 'switch; + } + v if v == 3 => { + (*r.borrow_mut()) = 3; + break 'switch; + } + _ => { + (*r.borrow_mut()) = 77; + break 'switch; + } + } + }; + return (*r.borrow()); +} +pub fn cases_and_default_stacked_28(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let r: Value = Rc::new(RefCell::new(0)); + 'switch: { + let __match_cond = (*x.borrow()); + match __match_cond { + v if v == 3 => { + (*r.borrow_mut()) = 3; + break 'switch; + } + _ => { + (*r.borrow_mut()) = 42; + break 'switch; + } + } + }; + return (*r.borrow()); +} +pub fn stacked_with_inner_fallthrough_29(x: i32, flag: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let flag: Value = Rc::new(RefCell::new(flag)); + let r: Value = Rc::new(RefCell::new(0)); + switch!(match (*x.borrow()) { + v if v == 1 || v == 2 || v == 3 => { + if !((*flag.borrow()) != 0) { + (*r.borrow_mut()) = 50; + break; + }; + } + _ => { + (*r.borrow_mut()) = 999; + break; + } + }); + return (*r.borrow()); +} +pub fn borrow_in_condition_and_in_body_30(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + switch!(match (*x.borrow()) { + v if v == 0 => {} + _ => { + return ((*x.borrow()) + 1); + } + }); + panic!("ub: non-void function does not return a value") +} pub fn main() { std::process::exit(main_0()); } @@ -116,80 +665,557 @@ fn main_0() -> i32 { assert!( (({ let _x: i32 = 0; - basic_0(_x) - }) == 10) + basic_0(_x) + }) == 10) + ); + assert!( + (({ + let _x: i32 = 2; + basic_0(_x) + }) == 30) + ); + assert!( + (({ + let _x: i32 = 99; + basic_0(_x) + }) == 40) + ); + assert!( + (({ + let _x: i32 = 1; + stacked_1(_x) + }) == 100) + ); + assert!( + (({ + let _x: i32 = 3; + stacked_1(_x) + }) == 100) + ); + assert!( + (({ + let _x: i32 = 5; + stacked_1(_x) + }) == 200) + ); + assert!( + (({ + let _x: i32 = 9; + stacked_1(_x) + }) == 300) + ); + assert!( + (({ + let _x: i32 = 7; + no_default_2(_x) + }) == 1) + ); + assert!( + (({ + let _x: i32 = 42; + no_default_2(_x) + }) == -1_i32) + ); + assert!( + (({ + let _x: i32 = 1; + only_default_3(_x) + }) == 42) + ); + assert!( + (({ + let _x: i32 = 1; + default_middle_4(_x) + }) == 1) + ); + assert!( + (({ + let _x: i32 = 2; + default_middle_4(_x) + }) == 2) + ); + assert!( + (({ + let _x: i32 = 99; + default_middle_4(_x) + }) == 99) + ); + assert!( + (({ + let _x: i32 = 1; + default_first_5(_x) + }) == 1) + ); + assert!( + (({ + let _x: i32 = 99; + default_first_5(_x) + }) == 7) + ); + assert!( + (({ + let _x: i32 = 5; + empty_switch_6(_x) + }) == 5) + ); + assert!( + (({ + let _c: u8 = ('a' as u8); + switch_char_7(_c) + }) == 1) + ); + assert!( + (({ + let _c: u8 = ('b' as u8); + switch_char_7(_c) + }) == 2) + ); + assert!( + (({ + let _c: u8 = ('\n' as u8); + switch_char_7(_c) + }) == 3) + ); + assert!( + (({ + let _c: u8 = ('\0' as u8); + switch_char_7(_c) + }) == 4) + ); + assert!( + (({ + let _c: u8 = ('z' as u8); + switch_char_7(_c) + }) == 0) + ); + assert!( + (({ + let _c: Color = Color::kRed; + switch_enum_8(_c) + }) == 10) + ); + assert!( + (({ + let _c: Color = Color::kGreen; + switch_enum_8(_c) + }) == 20) + ); + assert!( + (({ + let _c: Color = Color::kBlue; + switch_enum_8(_c) + }) == 30) + ); + assert!( + (({ + let _x: i32 = 1; + compound_case_body_9(_x) + }) == 30) + ); + assert!( + (({ + let _x: i32 = 2; + compound_case_body_9(_x) + }) == 99) + ); + assert!( + (({ + let _x: i32 = 9; + compound_case_body_9(_x) + }) == -1_i32) + ); + assert!( + (({ + let _a: i32 = 1; + let _b: i32 = 10; + nested_10(_a, _b) + }) == 12) + ); + assert!( + (({ + let _a: i32 = 1; + let _b: i32 = 99; + nested_10(_a, _b) + }) == 14) + ); + assert!( + (({ + let _a: i32 = 2; + let _b: i32 = 0; + nested_10(_a, _b) + }) == 2) + ); + assert!( + (({ + let _a: i32 = 3; + let _b: i32 = 3; + nested_10(_a, _b) + }) == -1_i32) + ); + assert!( + (({ + let _n: i32 = 6; + switch_in_loop_11(_n) + }) == 72) + ); + assert!( + (({ + let _x: i32 = 2; + stacked_block_12(_x) + }) == 5) + ); + assert!( + (({ + let _x: i32 = 9; + stacked_block_12(_x) + }) == 0) + ); + assert!( + (({ + let _x: i32 = 0; + switch_on_call_14(_x) + }) == 100) + ); + assert!( + (({ + let _x: i32 = 1; + switch_on_call_14(_x) + }) == 200) ); assert!( (({ let _x: i32 = 2; - basic_0(_x) - }) == 30) + switch_on_call_14(_x) + }) == 400) ); assert!( (({ let _x: i32 = 99; - basic_0(_x) - }) == 40) + switch_on_call_14(_x) + }) == -1_i32) + ); + assert!( + (({ + let _x: i32 = 0; + switch_on_assignment_15(_x) + }) == 10) ); assert!( (({ let _x: i32 = 1; - stacked_1(_x) - }) == 100) + switch_on_assignment_15(_x) + }) == 20) ); assert!( (({ - let _x: i32 = 3; - stacked_1(_x) + let _x: i32 = 9; + switch_on_assignment_15(_x) + }) == 10) + ); + assert!( + (({ + let _x: i32 = -1_i32; + mixed_literal_cases_16(_x) + }) == 1) + ); + assert!( + (({ + let _x: i32 = 16; + mixed_literal_cases_16(_x) + }) == 2) + ); + assert!( + (({ + let _x: i32 = 65152; + mixed_literal_cases_16(_x) + }) == 3) + ); + assert!( + (({ + let _x: i32 = -255_i32; + mixed_literal_cases_16(_x) + }) == 4) + ); + assert!( + (({ + let _x: i32 = 7; + mixed_literal_cases_16(_x) + }) == 0) + ); + assert!( + (({ + let _x: i32 = 0; + mixed_return_break_17(_x) }) == 100) ); assert!( (({ - let _x: i32 = 5; - stacked_1(_x) + let _x: i32 = 1; + mixed_return_break_17(_x) + }) == 10) + ); + assert!( + (({ + let _x: i32 = 2; + mixed_return_break_17(_x) }) == 200) ); + assert!( + (({ + let _x: i32 = 99; + mixed_return_break_17(_x) + }) == 99) + ); + assert!( + (({ + let _x: i32 = 1; + empty_case_with_break_18(_x) + }) == 5) + ); + assert!( + (({ + let _x: i32 = 2; + empty_case_with_break_18(_x) + }) == 2) + ); assert!( (({ let _x: i32 = 9; - stacked_1(_x) - }) == 300) + empty_case_with_break_18(_x) + }) == 9) ); assert!( (({ - let _x: i32 = 7; - no_default_2(_x) - }) == 1) + let _x: i32 = 1; + fallthrough_one_19(_x) + }) == 30) ); assert!( (({ - let _x: i32 = 42; - no_default_2(_x) + let _x: i32 = 2; + fallthrough_one_19(_x) + }) == 20) + ); + assert!( + (({ + let _x: i32 = 99; + fallthrough_one_19(_x) }) == -1_i32) ); assert!( (({ let _x: i32 = 1; - only_default_3(_x) + fallthrough_chain_20(_x) + }) == 15) + ); + assert!( + (({ + let _x: i32 = 2; + fallthrough_chain_20(_x) + }) == 14) + ); + assert!( + (({ + let _x: i32 = 3; + fallthrough_chain_20(_x) + }) == 12) + ); + assert!( + (({ + let _x: i32 = 4; + fallthrough_chain_20(_x) + }) == 8) + ); + assert!( + (({ + let _x: i32 = 99; + fallthrough_chain_20(_x) + }) == -1_i32) + ); + assert!( + (({ + let _x: i32 = 7; + let _flag: i32 = 0; + fallthrough_default_21(_x, _flag) + }) == 42) + ); + assert!( + (({ + let _x: i32 = 7; + let _flag: i32 = 1; + fallthrough_default_21(_x, _flag) + }) == 100) + ); + assert!( + (({ + let _x: i32 = 99; + let _flag: i32 = 0; + fallthrough_default_21(_x, _flag) }) == 42) ); assert!( (({ let _x: i32 = 1; - default_middle_4(_x) - }) == 1) + fallthrough_into_block_22(_x) + }) == 15) ); assert!( (({ let _x: i32 = 2; - default_middle_4(_x) + fallthrough_into_block_22(_x) + }) == 5) + ); + assert!( + (({ + let _x: i32 = 99; + fallthrough_into_block_22(_x) + }) == -1_i32) + ); + let p_val: Value = Rc::new(RefCell::new(5)); + assert!( + (({ + let _p: Ptr = (p_val.as_pointer()); + let _bias: i32 = 0; + switch_complex_cond_23(_p, _bias) }) == 2) ); + assert!( + (({ + let _p: Ptr = (p_val.as_pointer()); + let _bias: i32 = 5; + switch_complex_cond_23(_p, _bias) + }) == 3) + ); + assert!( + (({ + let _p: Ptr = (p_val.as_pointer()); + let _bias: i32 = -5_i32; + switch_complex_cond_23(_p, _bias) + }) == 1) + ); + assert!( + (({ + let _p: Ptr = (p_val.as_pointer()); + let _bias: i32 = 99; + switch_complex_cond_23(_p, _bias) + }) == 0) + ); + assert!( + (({ + let _n: i32 = 1; + switch_in_dowhile_24(_n) + }) == 1) + ); + assert!( + (({ + let _n: i32 = 3; + switch_in_dowhile_24(_n) + }) == ((1 + 10) + 100)) + ); + assert!( + (({ + let _n: i32 = 6; + continue_inside_switch_25(_n) + }) == ((((1 + 3) + 5) as i32) + (3 * 1000))) + ); + assert!( + (({ + let _x: i32 = 1; + case_then_default_26(_x) + }) == 10) + ); + assert!( + (({ + let _x: i32 = 2; + case_then_default_26(_x) + }) == 20) + ); assert!( (({ let _x: i32 = 99; - default_middle_4(_x) - }) == 99) + case_then_default_26(_x) + }) == 10) + ); + assert!( + (({ + let _x: i32 = 1; + default_then_case_27(_x) + }) == 1) + ); + assert!( + (({ + let _x: i32 = 2; + default_then_case_27(_x) + }) == 77) + ); + assert!( + (({ + let _x: i32 = 3; + default_then_case_27(_x) + }) == 3) + ); + assert!( + (({ + let _x: i32 = 99; + default_then_case_27(_x) + }) == 77) + ); + assert!( + (({ + let _x: i32 = 1; + cases_and_default_stacked_28(_x) + }) == 42) + ); + assert!( + (({ + let _x: i32 = 2; + cases_and_default_stacked_28(_x) + }) == 42) + ); + assert!( + (({ + let _x: i32 = 3; + cases_and_default_stacked_28(_x) + }) == 3) + ); + assert!( + (({ + let _x: i32 = 99; + cases_and_default_stacked_28(_x) + }) == 42) + ); + assert!( + (({ + let _x: i32 = 1; + let _flag: i32 = 0; + stacked_with_inner_fallthrough_29(_x, _flag) + }) == 50) + ); + assert!( + (({ + let _x: i32 = 2; + let _flag: i32 = 1; + stacked_with_inner_fallthrough_29(_x, _flag) + }) == 999) + ); + assert!( + (({ + let _x: i32 = 99; + let _flag: i32 = 0; + stacked_with_inner_fallthrough_29(_x, _flag) + }) == 999) + ); + assert!( + (({ + let _x: i32 = 0; + borrow_in_condition_and_in_body_30(_x) + }) == 1) + ); + assert!( + (({ + let _x: i32 = 1; + borrow_in_condition_and_in_body_30(_x) + }) == 2) ); return 0; } diff --git a/tests/unit/out/unsafe/switch.rs b/tests/unit/out/unsafe/switch.rs index 37951eb1..0051ad99 100644 --- a/tests/unit/out/unsafe/switch.rs +++ b/tests/unit/out/unsafe/switch.rs @@ -104,6 +104,522 @@ pub unsafe fn default_middle_4(mut x: i32) -> i32 { }; return r; } +pub unsafe fn default_first_5(mut x: i32) -> i32 { + let mut r: i32 = 0; + 'switch: { + let __match_cond = x; + match __match_cond { + v if v == 1 => { + r = 1; + break 'switch; + } + v if v == 2 => { + r = 2; + break 'switch; + } + _ => { + r = 7; + break 'switch; + } + } + }; + return r; +} +pub unsafe fn empty_switch_6(mut x: i32) -> i32 { + 'switch: { + let __match_cond = x; + match __match_cond { + _ => {} + } + }; + return x; +} +pub unsafe fn switch_char_7(mut c: u8) -> i32 { + 'switch: { + let __match_cond = (c as i32); + match __match_cond { + v if v == (('a' as u8) as i32) => { + return 1; + } + v if v == (('b' as u8) as i32) => { + return 2; + } + v if v == (('\n' as u8) as i32) => { + return 3; + } + v if v == (('\0' as u8) as i32) => { + return 4; + } + _ => { + return 0; + } + } + }; + panic!("ub: non-void function does not return a value") +} +#[derive(Clone, Copy, PartialEq, Debug, Default)] +enum Color { + #[default] + kRed = 0, + kGreen = 1, + kBlue = 2, +} +pub unsafe fn switch_enum_8(mut c: Color) -> i32 { + 'switch: { + let __match_cond = (c as i32); + match __match_cond { + v if v == (Color::kRed as i32) => { + return 10; + } + v if v == (Color::kGreen as i32) => { + return 20; + } + v if v == (Color::kBlue as i32) => { + return 30; + } + _ => {} + } + }; + return -1_i32; +} +pub unsafe fn compound_case_body_9(mut x: i32) -> i32 { + let mut r: i32 = 0; + switch!(match x { + v if v == 1 => { + let mut y: i32 = 10; + let mut z: i32 = 20; + r = ((y) + (z)); + break; + } + v if v == 2 => { + let mut y: i32 = 100; + r = ((y) - (1)); + break; + } + _ => { + r = -1_i32; + break; + } + }); + return r; +} +pub unsafe fn nested_10(mut a: i32, mut b: i32) -> i32 { + let mut r: i32 = 0; + 'switch: { + let __match_cond = a; + match __match_cond { + v if v == 1 => { + 'switch: { + let __match_cond = b; + match __match_cond { + v if v == 10 => { + r = 11; + break 'switch; + } + v if v == 20 => { + r = 12; + break 'switch; + } + _ => { + r = 13; + break 'switch; + } + } + }; + r += 1; + break 'switch; + } + v if v == 2 => { + r = 2; + break 'switch; + } + _ => { + r = -1_i32; + break 'switch; + } + } + }; + return r; +} +pub unsafe fn switch_in_loop_11(mut n: i32) -> i32 { + let mut r: i32 = 0; + let mut i: i32 = 0; + 'loop_: while ((i) < (n)) { + 'switch: { + let __match_cond = ((i) % (3)); + match __match_cond { + v if v == 0 => { + r += 1; + break 'switch; + } + v if v == 1 => { + r += 2; + break 'switch; + } + _ => { + r += 3; + break 'switch; + } + } + }; + r += 10; + i.prefix_inc(); + } + return r; +} +pub unsafe fn stacked_block_12(mut x: i32) -> i32 { + let mut r: i32 = 0; + switch!(match x { + v if v == 1 || v == 2 || v == 3 => { + let mut y: i32 = ((x) * (2)); + r = ((y) + (1)); + break; + } + _ => { + r = 0; + break; + } + }); + return r; +} +pub unsafe fn double_it_13(mut v: i32) -> i32 { + return ((v) * (2)); +} +pub unsafe fn switch_on_call_14(mut x: i32) -> i32 { + 'switch: { + let __match_cond = (unsafe { + let _v: i32 = x; + double_it_13(_v) + }); + match __match_cond { + v if v == 0 => { + return 100; + } + v if v == 2 => { + return 200; + } + v if v == 4 => { + return 400; + } + _ => { + return -1_i32; + } + } + }; + panic!("ub: non-void function does not return a value") +} +pub unsafe fn switch_on_assignment_15(mut x: i32) -> i32 { + let mut y: i32 = 0; + let mut r: i32 = 0; + 'switch: { + let __match_cond = { + y = ((x) + (1)); + y + }; + match __match_cond { + v if v == 1 => { + r = 10; + break 'switch; + } + v if v == 2 => { + r = 20; + break 'switch; + } + _ => { + r = y; + break 'switch; + } + } + }; + return r; +} +pub unsafe fn mixed_literal_cases_16(mut x: i32) -> i32 { + 'switch: { + let __match_cond = x; + match __match_cond { + v if v == -1_i32 => { + return 1; + } + v if v == 16 => { + return 2; + } + v if v == 65152 => { + return 3; + } + v if v == -255_i32 => { + return 4; + } + _ => { + return 0; + } + } + }; + panic!("ub: non-void function does not return a value") +} +pub unsafe fn mixed_return_break_17(mut x: i32) -> i32 { + let mut r: i32 = -1_i32; + 'switch: { + let __match_cond = x; + match __match_cond { + v if v == 0 => { + return 100; + } + v if v == 1 => { + r = 10; + break 'switch; + } + v if v == 2 => { + return 200; + } + _ => { + r = 99; + break 'switch; + } + } + }; + return r; +} +pub unsafe fn empty_case_with_break_18(mut x: i32) -> i32 { + let mut r: i32 = 5; + 'switch: { + let __match_cond = x; + match __match_cond { + v if v == 1 => { + break 'switch; + } + v if v == 2 => { + r = 2; + break 'switch; + } + _ => { + r = 9; + break 'switch; + } + } + }; + return r; +} +pub unsafe fn fallthrough_one_19(mut x: i32) -> i32 { + let mut r: i32 = 0; + switch!(match x { + v if v == 1 => { + r += 10; + } + v if v == 2 => { + r += 20; + break; + } + _ => { + r = -1_i32; + break; + } + }); + return r; +} +pub unsafe fn fallthrough_chain_20(mut x: i32) -> i32 { + let mut r: i32 = 0; + switch!(match x { + v if v == 1 => { + r += 1; + } + v if v == 2 => { + r += 2; + } + v if v == 3 => { + r += 4; + } + v if v == 4 => { + r += 8; + break; + } + _ => { + r = -1_i32; + break; + } + }); + return r; +} +pub unsafe fn fallthrough_default_21(mut x: i32, mut flag: i32) -> i32 { + let mut r: i32 = 0; + switch!(match x { + v if v == 7 => { + if (flag != 0) { + r = 100; + break; + }; + } + _ => { + r = 42; + break; + } + }); + return r; +} +pub unsafe fn fallthrough_into_block_22(mut x: i32) -> i32 { + let mut r: i32 = 0; + switch!(match x { + v if v == 1 => { + r += 1; + } + v if v == 2 => { + let mut tmp: i32 = ((r) * (10)); + r = ((tmp) + (5)); + break; + } + _ => { + r = -1_i32; + break; + } + }); + return r; +} +pub unsafe fn switch_complex_cond_23(mut p: *mut i32, mut bias: i32) -> i32 { + 'switch: { + let __match_cond = ((*p) + (bias)); + match __match_cond { + v if v == 0 => { + return 1; + } + v if v == 5 => { + return 2; + } + v if v == 10 => { + return 3; + } + _ => { + return 0; + } + } + }; + panic!("ub: non-void function does not return a value") +} +pub unsafe fn switch_in_dowhile_24(mut n: i32) -> i32 { + let mut r: i32 = 0; + let mut i: i32 = 0; + 'loop_: loop { + 'switch: { + let __match_cond = i; + match __match_cond { + v if v == 0 => { + r += 1; + break 'switch; + } + v if v == 1 => { + r += 10; + break 'switch; + } + _ => { + r += 100; + break 'switch; + } + } + }; + i.prefix_inc(); + if !((i) < (n)) { + break; + } + } + return r; +} +pub unsafe fn continue_inside_switch_25(mut n: i32) -> i32 { + let mut r: i32 = 0; + let mut i: i32 = 0; + 'loop_: while ((i) < (n)) { + switch!(match i { + v if v == 0 || v == 2 || v == 4 => { + i.prefix_inc(); + continue 'loop_; + } + _ => { + r += i; + break; + } + }); + r += 1000; + i.prefix_inc(); + } + return r; +} +pub unsafe fn case_then_default_26(mut x: i32) -> i32 { + let mut r: i32 = 0; + 'switch: { + let __match_cond = x; + match __match_cond { + v if v == 2 => { + r = 20; + break 'switch; + } + _ => { + r = 10; + break 'switch; + } + } + }; + return r; +} +pub unsafe fn default_then_case_27(mut x: i32) -> i32 { + let mut r: i32 = 0; + 'switch: { + let __match_cond = x; + match __match_cond { + v if v == 1 => { + r = 1; + break 'switch; + } + v if v == 3 => { + r = 3; + break 'switch; + } + _ => { + r = 77; + break 'switch; + } + } + }; + return r; +} +pub unsafe fn cases_and_default_stacked_28(mut x: i32) -> i32 { + let mut r: i32 = 0; + 'switch: { + let __match_cond = x; + match __match_cond { + v if v == 3 => { + r = 3; + break 'switch; + } + _ => { + r = 42; + break 'switch; + } + } + }; + return r; +} +pub unsafe fn stacked_with_inner_fallthrough_29(mut x: i32, mut flag: i32) -> i32 { + let mut r: i32 = 0; + switch!(match x { + v if v == 1 || v == 2 || v == 3 => { + if !(flag != 0) { + r = 50; + break; + }; + } + _ => { + r = 999; + break; + } + }); + return r; +} +pub unsafe fn borrow_in_condition_and_in_body_30(mut x: i32) -> i32 { + switch!(match x { + v if v == 0 => {} + _ => { + return ((x) + (1)); + } + }); + panic!("ub: non-void function does not return a value") +} pub fn main() { unsafe { std::process::exit(main_0() as i32); @@ -113,80 +629,557 @@ unsafe fn main_0() -> i32 { assert!( ((unsafe { let _x: i32 = 0; - basic_0(_x) - }) == (10)) + basic_0(_x) + }) == (10)) + ); + assert!( + ((unsafe { + let _x: i32 = 2; + basic_0(_x) + }) == (30)) + ); + assert!( + ((unsafe { + let _x: i32 = 99; + basic_0(_x) + }) == (40)) + ); + assert!( + ((unsafe { + let _x: i32 = 1; + stacked_1(_x) + }) == (100)) + ); + assert!( + ((unsafe { + let _x: i32 = 3; + stacked_1(_x) + }) == (100)) + ); + assert!( + ((unsafe { + let _x: i32 = 5; + stacked_1(_x) + }) == (200)) + ); + assert!( + ((unsafe { + let _x: i32 = 9; + stacked_1(_x) + }) == (300)) + ); + assert!( + ((unsafe { + let _x: i32 = 7; + no_default_2(_x) + }) == (1)) + ); + assert!( + ((unsafe { + let _x: i32 = 42; + no_default_2(_x) + }) == (-1_i32)) + ); + assert!( + ((unsafe { + let _x: i32 = 1; + only_default_3(_x) + }) == (42)) + ); + assert!( + ((unsafe { + let _x: i32 = 1; + default_middle_4(_x) + }) == (1)) + ); + assert!( + ((unsafe { + let _x: i32 = 2; + default_middle_4(_x) + }) == (2)) + ); + assert!( + ((unsafe { + let _x: i32 = 99; + default_middle_4(_x) + }) == (99)) + ); + assert!( + ((unsafe { + let _x: i32 = 1; + default_first_5(_x) + }) == (1)) + ); + assert!( + ((unsafe { + let _x: i32 = 99; + default_first_5(_x) + }) == (7)) + ); + assert!( + ((unsafe { + let _x: i32 = 5; + empty_switch_6(_x) + }) == (5)) + ); + assert!( + ((unsafe { + let _c: u8 = ('a' as u8); + switch_char_7(_c) + }) == (1)) + ); + assert!( + ((unsafe { + let _c: u8 = ('b' as u8); + switch_char_7(_c) + }) == (2)) + ); + assert!( + ((unsafe { + let _c: u8 = ('\n' as u8); + switch_char_7(_c) + }) == (3)) + ); + assert!( + ((unsafe { + let _c: u8 = ('\0' as u8); + switch_char_7(_c) + }) == (4)) + ); + assert!( + ((unsafe { + let _c: u8 = ('z' as u8); + switch_char_7(_c) + }) == (0)) + ); + assert!( + ((unsafe { + let _c: Color = Color::kRed; + switch_enum_8(_c) + }) == (10)) + ); + assert!( + ((unsafe { + let _c: Color = Color::kGreen; + switch_enum_8(_c) + }) == (20)) + ); + assert!( + ((unsafe { + let _c: Color = Color::kBlue; + switch_enum_8(_c) + }) == (30)) + ); + assert!( + ((unsafe { + let _x: i32 = 1; + compound_case_body_9(_x) + }) == (30)) + ); + assert!( + ((unsafe { + let _x: i32 = 2; + compound_case_body_9(_x) + }) == (99)) + ); + assert!( + ((unsafe { + let _x: i32 = 9; + compound_case_body_9(_x) + }) == (-1_i32)) + ); + assert!( + ((unsafe { + let _a: i32 = 1; + let _b: i32 = 10; + nested_10(_a, _b) + }) == (12)) + ); + assert!( + ((unsafe { + let _a: i32 = 1; + let _b: i32 = 99; + nested_10(_a, _b) + }) == (14)) + ); + assert!( + ((unsafe { + let _a: i32 = 2; + let _b: i32 = 0; + nested_10(_a, _b) + }) == (2)) + ); + assert!( + ((unsafe { + let _a: i32 = 3; + let _b: i32 = 3; + nested_10(_a, _b) + }) == (-1_i32)) + ); + assert!( + ((unsafe { + let _n: i32 = 6; + switch_in_loop_11(_n) + }) == (72)) + ); + assert!( + ((unsafe { + let _x: i32 = 2; + stacked_block_12(_x) + }) == (5)) + ); + assert!( + ((unsafe { + let _x: i32 = 9; + stacked_block_12(_x) + }) == (0)) + ); + assert!( + ((unsafe { + let _x: i32 = 0; + switch_on_call_14(_x) + }) == (100)) + ); + assert!( + ((unsafe { + let _x: i32 = 1; + switch_on_call_14(_x) + }) == (200)) ); assert!( ((unsafe { let _x: i32 = 2; - basic_0(_x) - }) == (30)) + switch_on_call_14(_x) + }) == (400)) ); assert!( ((unsafe { let _x: i32 = 99; - basic_0(_x) - }) == (40)) + switch_on_call_14(_x) + }) == (-1_i32)) + ); + assert!( + ((unsafe { + let _x: i32 = 0; + switch_on_assignment_15(_x) + }) == (10)) ); assert!( ((unsafe { let _x: i32 = 1; - stacked_1(_x) - }) == (100)) + switch_on_assignment_15(_x) + }) == (20)) ); assert!( ((unsafe { - let _x: i32 = 3; - stacked_1(_x) + let _x: i32 = 9; + switch_on_assignment_15(_x) + }) == (10)) + ); + assert!( + ((unsafe { + let _x: i32 = -1_i32; + mixed_literal_cases_16(_x) + }) == (1)) + ); + assert!( + ((unsafe { + let _x: i32 = 16; + mixed_literal_cases_16(_x) + }) == (2)) + ); + assert!( + ((unsafe { + let _x: i32 = 65152; + mixed_literal_cases_16(_x) + }) == (3)) + ); + assert!( + ((unsafe { + let _x: i32 = -255_i32; + mixed_literal_cases_16(_x) + }) == (4)) + ); + assert!( + ((unsafe { + let _x: i32 = 7; + mixed_literal_cases_16(_x) + }) == (0)) + ); + assert!( + ((unsafe { + let _x: i32 = 0; + mixed_return_break_17(_x) }) == (100)) ); assert!( ((unsafe { - let _x: i32 = 5; - stacked_1(_x) + let _x: i32 = 1; + mixed_return_break_17(_x) + }) == (10)) + ); + assert!( + ((unsafe { + let _x: i32 = 2; + mixed_return_break_17(_x) }) == (200)) ); + assert!( + ((unsafe { + let _x: i32 = 99; + mixed_return_break_17(_x) + }) == (99)) + ); + assert!( + ((unsafe { + let _x: i32 = 1; + empty_case_with_break_18(_x) + }) == (5)) + ); + assert!( + ((unsafe { + let _x: i32 = 2; + empty_case_with_break_18(_x) + }) == (2)) + ); assert!( ((unsafe { let _x: i32 = 9; - stacked_1(_x) - }) == (300)) + empty_case_with_break_18(_x) + }) == (9)) ); assert!( ((unsafe { - let _x: i32 = 7; - no_default_2(_x) - }) == (1)) + let _x: i32 = 1; + fallthrough_one_19(_x) + }) == (30)) ); assert!( ((unsafe { - let _x: i32 = 42; - no_default_2(_x) + let _x: i32 = 2; + fallthrough_one_19(_x) + }) == (20)) + ); + assert!( + ((unsafe { + let _x: i32 = 99; + fallthrough_one_19(_x) }) == (-1_i32)) ); assert!( ((unsafe { let _x: i32 = 1; - only_default_3(_x) + fallthrough_chain_20(_x) + }) == (15)) + ); + assert!( + ((unsafe { + let _x: i32 = 2; + fallthrough_chain_20(_x) + }) == (14)) + ); + assert!( + ((unsafe { + let _x: i32 = 3; + fallthrough_chain_20(_x) + }) == (12)) + ); + assert!( + ((unsafe { + let _x: i32 = 4; + fallthrough_chain_20(_x) + }) == (8)) + ); + assert!( + ((unsafe { + let _x: i32 = 99; + fallthrough_chain_20(_x) + }) == (-1_i32)) + ); + assert!( + ((unsafe { + let _x: i32 = 7; + let _flag: i32 = 0; + fallthrough_default_21(_x, _flag) + }) == (42)) + ); + assert!( + ((unsafe { + let _x: i32 = 7; + let _flag: i32 = 1; + fallthrough_default_21(_x, _flag) + }) == (100)) + ); + assert!( + ((unsafe { + let _x: i32 = 99; + let _flag: i32 = 0; + fallthrough_default_21(_x, _flag) }) == (42)) ); assert!( ((unsafe { let _x: i32 = 1; - default_middle_4(_x) - }) == (1)) + fallthrough_into_block_22(_x) + }) == (15)) ); assert!( ((unsafe { let _x: i32 = 2; - default_middle_4(_x) + fallthrough_into_block_22(_x) + }) == (5)) + ); + assert!( + ((unsafe { + let _x: i32 = 99; + fallthrough_into_block_22(_x) + }) == (-1_i32)) + ); + let mut p_val: i32 = 5; + assert!( + ((unsafe { + let _p: *mut i32 = (&mut p_val as *mut i32); + let _bias: i32 = 0; + switch_complex_cond_23(_p, _bias) }) == (2)) ); + assert!( + ((unsafe { + let _p: *mut i32 = (&mut p_val as *mut i32); + let _bias: i32 = 5; + switch_complex_cond_23(_p, _bias) + }) == (3)) + ); + assert!( + ((unsafe { + let _p: *mut i32 = (&mut p_val as *mut i32); + let _bias: i32 = -5_i32; + switch_complex_cond_23(_p, _bias) + }) == (1)) + ); + assert!( + ((unsafe { + let _p: *mut i32 = (&mut p_val as *mut i32); + let _bias: i32 = 99; + switch_complex_cond_23(_p, _bias) + }) == (0)) + ); + assert!( + ((unsafe { + let _n: i32 = 1; + switch_in_dowhile_24(_n) + }) == (1)) + ); + assert!( + ((unsafe { + let _n: i32 = 3; + switch_in_dowhile_24(_n) + }) == (((1) + (10)) + (100))) + ); + assert!( + ((unsafe { + let _n: i32 = 6; + continue_inside_switch_25(_n) + }) == (((((1) + (3)) + (5)) as i32) + ((3) * (1000)))) + ); + assert!( + ((unsafe { + let _x: i32 = 1; + case_then_default_26(_x) + }) == (10)) + ); + assert!( + ((unsafe { + let _x: i32 = 2; + case_then_default_26(_x) + }) == (20)) + ); assert!( ((unsafe { let _x: i32 = 99; - default_middle_4(_x) - }) == (99)) + case_then_default_26(_x) + }) == (10)) + ); + assert!( + ((unsafe { + let _x: i32 = 1; + default_then_case_27(_x) + }) == (1)) + ); + assert!( + ((unsafe { + let _x: i32 = 2; + default_then_case_27(_x) + }) == (77)) + ); + assert!( + ((unsafe { + let _x: i32 = 3; + default_then_case_27(_x) + }) == (3)) + ); + assert!( + ((unsafe { + let _x: i32 = 99; + default_then_case_27(_x) + }) == (77)) + ); + assert!( + ((unsafe { + let _x: i32 = 1; + cases_and_default_stacked_28(_x) + }) == (42)) + ); + assert!( + ((unsafe { + let _x: i32 = 2; + cases_and_default_stacked_28(_x) + }) == (42)) + ); + assert!( + ((unsafe { + let _x: i32 = 3; + cases_and_default_stacked_28(_x) + }) == (3)) + ); + assert!( + ((unsafe { + let _x: i32 = 99; + cases_and_default_stacked_28(_x) + }) == (42)) + ); + assert!( + ((unsafe { + let _x: i32 = 1; + let _flag: i32 = 0; + stacked_with_inner_fallthrough_29(_x, _flag) + }) == (50)) + ); + assert!( + ((unsafe { + let _x: i32 = 2; + let _flag: i32 = 1; + stacked_with_inner_fallthrough_29(_x, _flag) + }) == (999)) + ); + assert!( + ((unsafe { + let _x: i32 = 99; + let _flag: i32 = 0; + stacked_with_inner_fallthrough_29(_x, _flag) + }) == (999)) + ); + assert!( + ((unsafe { + let _x: i32 = 0; + borrow_in_condition_and_in_body_30(_x) + }) == (1)) + ); + assert!( + ((unsafe { + let _x: i32 = 1; + borrow_in_condition_and_in_body_30(_x) + }) == (2)) ); return 0; } diff --git a/tests/unit/switch.cpp b/tests/unit/switch.cpp index 6fea4b8d..95edd1cd 100644 --- a/tests/unit/switch.cpp +++ b/tests/unit/switch.cpp @@ -480,6 +480,15 @@ int stacked_with_inner_fallthrough(int x, int flag) { return r; } +int borrow_in_condition_and_in_body(int x) { + switch (x) { + case 0: + [[fallthrough]]; + default: + return x + 1; + } +} + int main() { assert(basic(0) == 10); assert(basic(2) == 30); @@ -598,5 +607,8 @@ int main() { assert(stacked_with_inner_fallthrough(1, 0) == 50); assert(stacked_with_inner_fallthrough(2, 1) == 999); assert(stacked_with_inner_fallthrough(99, 0) == 999); + + assert(borrow_in_condition_and_in_body(0) == 1); + assert(borrow_in_condition_and_in_body(1) == 2); return 0; } From ac4bb8b97dce0e1bfcd2130f669b068f442d6204 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 22 Apr 2026 16:45:36 +0100 Subject: [PATCH 08/24] Add impl for switch and goto_block macros --- libcc2rs-macros/Cargo.toml | 12 + libcc2rs-macros/src/goto.rs | 54 +++++ libcc2rs-macros/src/lib.rs | 23 ++ libcc2rs-macros/src/state_machine.rs | 102 ++++++++ libcc2rs-macros/src/switch.rs | 65 ++++++ libcc2rs-macros/tests/control_flow.rs | 325 ++++++++++++++++++++++++++ libcc2rs/Cargo.toml | 3 + libcc2rs/src/lib.rs | 2 + 8 files changed, 586 insertions(+) create mode 100644 libcc2rs-macros/Cargo.toml create mode 100644 libcc2rs-macros/src/goto.rs create mode 100644 libcc2rs-macros/src/lib.rs create mode 100644 libcc2rs-macros/src/state_machine.rs create mode 100644 libcc2rs-macros/src/switch.rs create mode 100644 libcc2rs-macros/tests/control_flow.rs diff --git a/libcc2rs-macros/Cargo.toml b/libcc2rs-macros/Cargo.toml new file mode 100644 index 00000000..5f32d654 --- /dev/null +++ b/libcc2rs-macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "libcc2rs-macros" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1" +quote = "1" +syn = { version = "2", features = ["full", "visit-mut", "extra-traits"] } diff --git a/libcc2rs-macros/src/goto.rs b/libcc2rs-macros/src/goto.rs new file mode 100644 index 00000000..52730ce9 --- /dev/null +++ b/libcc2rs-macros/src/goto.rs @@ -0,0 +1,54 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +// goto_block! { +// '