From 611547d40e65c32f1a8954960d110cda298a6183 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 1 May 2026 14:47:49 +0100 Subject: [PATCH 1/7] Add From trait for enums --- cpp2rust/converter/converter.cpp | 27 +++++++++++++++++++++++++++ cpp2rust/converter/converter.h | 2 ++ 2 files changed, 29 insertions(+) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 1318c6f1..1bf2e1ec 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1890,6 +1890,13 @@ bool Converter::VisitExplicitCastExpr(clang::ExplicitCastExpr *expr) { StrCat(')'); return false; } + if (type->isEnumeralType() && !sub_expr->getType()->isEnumeralType()) { + StrCat(std::format("{}::from", GetUnsafeTypeAsString(type))); + PushParen paren(*this); + Convert(sub_expr); + StrCat(keyword::kAs, "i32"); + return false; + } { PushParen paren(*this); Convert(sub_expr); @@ -2704,9 +2711,29 @@ bool Converter::VisitEnumDecl(clang::EnumDecl *decl) { std::string_view(init.data(), init.size()))); } StrCat("}"); + + AddFromImpl(decl); return false; } +void Converter::AddFromImpl(clang::EnumDecl *decl) { + auto name = GetRecordName(decl); + StrCat(std::format("impl From for {}", name)); + PushBrace impl(*this); + StrCat(std::format("fn from(n: i32) -> {}", name)); + PushBrace fn(*this); + StrCat("match n"); + PushBrace match(*this); + for (auto e : decl->enumerators()) { + llvm::SmallVector init; + e->getInitVal().toString(init, 10); + StrCat(std::format("{} => {}::{},", + std::string_view(init.data(), init.size()), name, + std::string_view(e->getName()))); + } + StrCat(std::format("_ => panic!(\"invalid {} value: {{}}\", n),", name)); +} + bool Converter::VisitCXXDefaultArgExpr(clang::CXXDefaultArgExpr *expr) { if (expr->getType()->isPointerType()) { StrCat(keyword_default_); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 139b025a..60c631b2 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -293,6 +293,8 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool VisitEnumDecl(clang::EnumDecl *decl); + virtual void AddFromImpl(clang::EnumDecl *decl); + virtual bool VisitCXXDefaultArgExpr(clang::CXXDefaultArgExpr *expr); virtual bool VisitLambdaExpr(clang::LambdaExpr *expr); From 3c7f08e2aea07919176aa929a7b46ddeba34119c Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 1 May 2026 14:48:20 +0100 Subject: [PATCH 2/7] Add enum-int interop test --- tests/unit/enum_int_interop.cpp | 117 ++++++++++++++++++++++++++++++++ tests/unit/enum_int_interop_c.c | 117 ++++++++++++++++++++++++++++++++ 2 files changed, 234 insertions(+) create mode 100644 tests/unit/enum_int_interop.cpp create mode 100644 tests/unit/enum_int_interop_c.c diff --git a/tests/unit/enum_int_interop.cpp b/tests/unit/enum_int_interop.cpp new file mode 100644 index 00000000..02aeee10 --- /dev/null +++ b/tests/unit/enum_int_interop.cpp @@ -0,0 +1,117 @@ +#include + +enum Color { RED, GREEN, BLUE }; + +enum Option { + OPT_NONE = 0, + OPT_A = 10, + OPT_B = 20, + OPT_C = 30, +}; + +typedef enum { + TAG_ZERO = 0, + TAG_ONE = 1, + TAG_TWO = 2, +} Tag; + +int as_int(Color c) { return c; } + +int classify_option(int option) { + switch (option) { + case OPT_NONE: + return -1; + case OPT_A: + return 1; + case OPT_B: + return 2; + case OPT_C: + return 3; + default: + return 0; + } +} + +Color make_color(int n) { return (Color)n; } + +int main() { + Color c = RED; + + assert(c == RED); + assert(c == 0); + assert(c != 1); + + if (c == GREEN) { + return 1; + } + + switch (c) { + case 0: + break; + case 1: + return 1; + case 2: + return 2; + default: + return 99; + } + + int x = c; + assert(x == 0); + + int y = c + 1; + assert(y == 1); + + c = (Color)2; + assert(c == BLUE); + assert(c == 2); + + c = make_color(1); + assert(c == GREEN); + + Color cmp = (Color)(c + 1); + assert(cmp == BLUE); + + Option o = OPT_A; + assert(o == OPT_A); + assert(o == 10); + + int oi = o; + assert(oi == 10); + + o = (Option)20; + assert(o == OPT_B); + + int rc = classify_option(o); + assert(rc == 2); + + rc = classify_option(20); + assert(rc == 2); + + rc = classify_option(OPT_C); + assert(rc == 3); + + Tag t = TAG_ONE; + assert(t == 1); + assert(t == TAG_ONE); + + int ti = t; + assert(ti == 1); + + t = (Tag)2; + assert(t == TAG_TWO); + + switch (t) { + case TAG_ZERO: + return 90; + case 1: + return 91; + case 2: + break; + } + + int extra = (int)RED + (int)GREEN + (int)BLUE; + assert(extra == 0 + 1 + 2); + + return 0; +} diff --git a/tests/unit/enum_int_interop_c.c b/tests/unit/enum_int_interop_c.c new file mode 100644 index 00000000..ed22035a --- /dev/null +++ b/tests/unit/enum_int_interop_c.c @@ -0,0 +1,117 @@ +#include + +enum Color { RED, GREEN, BLUE }; + +enum Option { + OPT_NONE = 0, + OPT_A = 10, + OPT_B = 20, + OPT_C = 30, +}; + +typedef enum { + TAG_ZERO = 0, + TAG_ONE = 1, + TAG_TWO = 2, +} Tag; + +int as_int(enum Color c) { return c; } + +int classify_option(int option) { + switch (option) { + case OPT_NONE: + return -1; + case OPT_A: + return 1; + case OPT_B: + return 2; + case OPT_C: + return 3; + default: + return 0; + } +} + +enum Color make_color(int n) { return (enum Color)n; } + +int main() { + enum Color c = RED; + + assert(c == RED); + assert(c == 0); + assert(c != 1); + + if (c == GREEN) { + return 1; + } + + switch (c) { + case 0: + break; + case 1: + return 1; + case 2: + return 2; + default: + return 99; + } + + int x = c; + assert(x == 0); + + int y = c + 1; + assert(y == 1); + + c = (enum Color)2; + assert(c == BLUE); + assert(c == 2); + + c = make_color(1); + assert(c == GREEN); + + enum Color cmp = (enum Color)(c + 1); + assert(cmp == BLUE); + + enum Option o = OPT_A; + assert(o == OPT_A); + assert(o == 10); + + int oi = o; + assert(oi == 10); + + o = (enum Option)20; + assert(o == OPT_B); + + int rc = classify_option(o); + assert(rc == 2); + + rc = classify_option(20); + assert(rc == 2); + + rc = classify_option(OPT_C); + assert(rc == 3); + + Tag t = TAG_ONE; + assert(t == 1); + assert(t == TAG_ONE); + + int ti = t; + assert(ti == 1); + + t = (Tag)2; + assert(t == TAG_TWO); + + switch (t) { + case TAG_ZERO: + return 90; + case 1: + return 91; + case 2: + break; + } + + int extra = (int)RED + (int)GREEN + (int)BLUE; + assert(extra == 0 + 1 + 2); + + return 0; +} From 5e3e4872346f35710dd09ff61b5689c0662e168d Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 1 May 2026 16:09:51 +0100 Subject: [PATCH 3/7] Handle enums which have type int --- cpp2rust/converter/converter.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 1bf2e1ec..061eb9ae 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1846,6 +1846,13 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { Convert(sub_expr); break; } + if (type->isEnumeralType() && !sub_expr->getType()->isEnumeralType()) { + StrCat(std::format("{}::from", GetUnsafeTypeAsString(type))); + PushParen paren(*this); + Convert(sub_expr); + StrCat(keyword::kAs, "i32"); + break; + } { PushParen outer(*this); if (clang::isa(sub_expr)) { @@ -2207,10 +2214,14 @@ std::string Converter::ConvertDeclRefExpr(clang::DeclRefExpr *expr) { } if (auto enum_constant = clang::dyn_cast(decl)) { - return std::format("{}::{}", - GetRecordName(clang::dyn_cast( - enum_constant->getDeclContext())), - std::string_view(enum_constant->getName())); + auto qualified = std::format("{}::{}", + GetRecordName(clang::dyn_cast( + enum_constant->getDeclContext())), + std::string_view(enum_constant->getName())); + if (!expr->getType()->isEnumeralType()) { + return std::format("({} as i32)", qualified); + } + return qualified; } if (IsGlobalVar(expr)) { From f56a035d21af9fedfccf5af60c81f029fd52858a Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 1 May 2026 16:15:16 +0100 Subject: [PATCH 4/7] Update tests --- tests/unit/out/refcount/anonymous_enum.rs | 45 +++++ tests/unit/out/refcount/anonymous_enum_c.rs | 67 +++++- tests/unit/out/refcount/c_struct.rs | 16 +- tests/unit/out/refcount/enum_int_interop.rs | 189 +++++++++++++++++ tests/unit/out/refcount/enum_int_interop_c.rs | 191 ++++++++++++++++++ tests/unit/out/refcount/switch_char.rs | 10 + tests/unit/out/refcount/switch_enum.rs | 10 + tests/unit/out/unsafe/anonymous_enum.rs | 45 +++++ tests/unit/out/unsafe/anonymous_enum_c.rs | 67 +++++- tests/unit/out/unsafe/c_struct.rs | 16 +- tests/unit/out/unsafe/enum_int_interop.rs | 182 +++++++++++++++++ tests/unit/out/unsafe/enum_int_interop_c.rs | 182 +++++++++++++++++ tests/unit/out/unsafe/switch_char.rs | 10 + tests/unit/out/unsafe/switch_enum.rs | 10 + .../unit/out/unsafe/union_tagged_many_arms.rs | 22 +- tests/unit/out/unsafe/union_tagged_simple.rs | 15 +- .../out/unsafe/union_tagged_struct_arms.rs | 16 +- .../out/unsafe/union_void_ptr_sized_deref.rs | 22 +- 18 files changed, 1070 insertions(+), 45 deletions(-) create mode 100644 tests/unit/out/refcount/enum_int_interop.rs create mode 100644 tests/unit/out/refcount/enum_int_interop_c.rs create mode 100644 tests/unit/out/unsafe/enum_int_interop.rs create mode 100644 tests/unit/out/unsafe/enum_int_interop_c.rs diff --git a/tests/unit/out/refcount/anonymous_enum.rs b/tests/unit/out/refcount/anonymous_enum.rs index 47dcf325..fd913572 100644 --- a/tests/unit/out/refcount/anonymous_enum.rs +++ b/tests/unit/out/refcount/anonymous_enum.rs @@ -12,12 +12,30 @@ enum anon_enum_3 { FIRST_A = 0, FIRST_B = 1, } +impl From for anon_enum_3 { + fn from(n: i32) -> anon_enum_3 { + match n { + 0 => anon_enum_3::FIRST_A, + 1 => anon_enum_3::FIRST_B, + _ => panic!("invalid anon_enum_3 value: {}", n), + } + } +} #[derive(Clone, Copy, PartialEq, Debug, Default)] enum anon_enum_11 { #[default] SECOND_A = 0, SECOND_B = 1, } +impl From for anon_enum_11 { + fn from(n: i32) -> anon_enum_11 { + match n { + 0 => anon_enum_11::SECOND_A, + 1 => anon_enum_11::SECOND_B, + _ => panic!("invalid anon_enum_11 value: {}", n), + } + } +} #[derive(Default)] pub struct S { pub a: Value, @@ -37,12 +55,30 @@ enum TdEnum { TD_A = 0, TD_B = 1, } +impl From for TdEnum { + fn from(n: i32) -> TdEnum { + match n { + 0 => TdEnum::TD_A, + 1 => TdEnum::TD_B, + _ => panic!("invalid TdEnum value: {}", n), + } + } +} #[derive(Clone, Copy, PartialEq, Debug, Default)] enum anon_enum_24 { #[default] FIELD_A = 0, FIELD_B = 1, } +impl From for anon_enum_24 { + fn from(n: i32) -> anon_enum_24 { + match n { + 0 => anon_enum_24::FIELD_A, + 1 => anon_enum_24::FIELD_B, + _ => panic!("invalid anon_enum_24 value: {}", n), + } + } +} #[derive(Default)] pub struct WithAnonField { pub a: Value, @@ -67,6 +103,15 @@ fn main_0() -> i32 { #[default] THIRD_A = 0, THIRD_B = 1, + } + impl From for anon_enum_31 { + fn from(n: i32) -> anon_enum_31 { + match n { + 0 => anon_enum_31::THIRD_A, + 1 => anon_enum_31::THIRD_B, + _ => panic!("invalid anon_enum_31 value: {}", n), + } + } }; assert!(((anon_enum_3::FIRST_A as i32) != (anon_enum_3::FIRST_B as i32))); assert!(((anon_enum_11::SECOND_A as i32) != (anon_enum_11::SECOND_B as i32))); diff --git a/tests/unit/out/refcount/anonymous_enum_c.rs b/tests/unit/out/refcount/anonymous_enum_c.rs index 0b0c762c..8596c3fa 100644 --- a/tests/unit/out/refcount/anonymous_enum_c.rs +++ b/tests/unit/out/refcount/anonymous_enum_c.rs @@ -12,12 +12,30 @@ enum anon_enum_3 { FIRST_A = 0, FIRST_B = 1, } +impl From for anon_enum_3 { + fn from(n: i32) -> anon_enum_3 { + match n { + 0 => anon_enum_3::FIRST_A, + 1 => anon_enum_3::FIRST_B, + _ => panic!("invalid anon_enum_3 value: {}", n), + } + } +} #[derive(Clone, Copy, PartialEq, Debug, Default)] enum anon_enum_11 { #[default] SECOND_A = 0, SECOND_B = 1, } +impl From for anon_enum_11 { + fn from(n: i32) -> anon_enum_11 { + match n { + 0 => anon_enum_11::SECOND_A, + 1 => anon_enum_11::SECOND_B, + _ => panic!("invalid anon_enum_11 value: {}", n), + } + } +} #[derive(Default)] pub struct S { pub a: Value, @@ -29,12 +47,30 @@ enum TdEnum { TD_A = 0, TD_B = 1, } +impl From for TdEnum { + fn from(n: i32) -> TdEnum { + match n { + 0 => TdEnum::TD_A, + 1 => TdEnum::TD_B, + _ => panic!("invalid TdEnum value: {}", n), + } + } +} #[derive(Clone, Copy, PartialEq, Debug, Default)] enum anon_enum_24 { #[default] FIELD_A = 0, FIELD_B = 1, } +impl From for anon_enum_24 { + fn from(n: i32) -> anon_enum_24 { + match n { + 0 => anon_enum_24::FIELD_A, + 1 => anon_enum_24::FIELD_B, + _ => panic!("invalid anon_enum_24 value: {}", n), + } + } +} #[derive(Default)] pub struct WithAnonField { pub a: Value, @@ -50,18 +86,27 @@ fn main_0() -> i32 { #[default] THIRD_A = 0, THIRD_B = 1, + } + impl From for anon_enum_31 { + fn from(n: i32) -> anon_enum_31 { + match n { + 0 => anon_enum_31::THIRD_A, + 1 => anon_enum_31::THIRD_B, + _ => panic!("invalid anon_enum_31 value: {}", n), + } + } }; - assert!((anon_enum_3::FIRST_A != anon_enum_3::FIRST_B)); - assert!((anon_enum_11::SECOND_A != anon_enum_11::SECOND_B)); - assert!((anon_enum_31::THIRD_A != anon_enum_31::THIRD_B)); - let td: Value = Rc::new(RefCell::new((TdEnum::TD_A as TdEnum))); - assert!((((*td.borrow()) as u32) == (TdEnum::TD_A as u32))); - (*td.borrow_mut()) = (TdEnum::TD_B as TdEnum); - assert!((((*td.borrow()) as u32) == (TdEnum::TD_B as u32))); + assert!(((anon_enum_3::FIRST_A as i32) != (anon_enum_3::FIRST_B as i32))); + assert!(((anon_enum_11::SECOND_A as i32) != (anon_enum_11::SECOND_B as i32))); + assert!(((anon_enum_31::THIRD_A as i32) != (anon_enum_31::THIRD_B as i32))); + let td: Value = Rc::new(RefCell::new(TdEnum::from((TdEnum::TD_A as i32) as i32))); + assert!((((*td.borrow()) as u32) == ((TdEnum::TD_A as i32) as u32))); + (*td.borrow_mut()) = TdEnum::from((TdEnum::TD_B as i32) as i32); + assert!((((*td.borrow()) as u32) == ((TdEnum::TD_B as i32) as u32))); let w: Value = >::default(); - (*(*w.borrow()).field.borrow_mut()) = (anon_enum_24::FIELD_A as anon_enum_24); - assert!((((*(*w.borrow()).field.borrow()) as u32) == (anon_enum_24::FIELD_A as u32))); - (*(*w.borrow()).field.borrow_mut()) = (anon_enum_24::FIELD_B as anon_enum_24); - assert!((((*(*w.borrow()).field.borrow()) as u32) == (anon_enum_24::FIELD_B as u32))); + (*(*w.borrow()).field.borrow_mut()) = anon_enum_24::from((anon_enum_24::FIELD_A as i32) as i32); + assert!((((*(*w.borrow()).field.borrow()) as u32) == ((anon_enum_24::FIELD_A as i32) as u32))); + (*(*w.borrow()).field.borrow_mut()) = anon_enum_24::from((anon_enum_24::FIELD_B as i32) as i32); + assert!((((*(*w.borrow()).field.borrow()) as u32) == ((anon_enum_24::FIELD_B as i32) as u32))); return 0; } diff --git a/tests/unit/out/refcount/c_struct.rs b/tests/unit/out/refcount/c_struct.rs index 2343bb35..042b810b 100644 --- a/tests/unit/out/refcount/c_struct.rs +++ b/tests/unit/out/refcount/c_struct.rs @@ -31,6 +31,16 @@ enum Color { GREEN = 1, BLUE = 2, } +impl From for Color { + fn from(n: i32) -> Color { + match n { + 0 => Color::RED, + 1 => Color::GREEN, + 2 => Color::BLUE, + _ => panic!("invalid Color value: {}", n), + } + } +} #[derive(Default)] pub struct Inner { pub a: Value, @@ -85,15 +95,15 @@ fn main_0() -> i32 { a: Rc::new(RefCell::new(5)), b: Rc::new(RefCell::new(6)), })), - color: Rc::new(RefCell::new((Color::GREEN as Color))), + color: Rc::new(RefCell::new(Color::from((Color::GREEN as i32) as i32))), count: Rc::new(RefCell::new(42)), })); assert!(((*(*(*c.borrow()).inner.borrow()).a.borrow()) == 5)); assert!(((*(*(*c.borrow()).inner.borrow()).b.borrow()) == 6)); - assert!((((*(*c.borrow()).color.borrow()) as u32) == (Color::GREEN as u32))); + assert!((((*(*c.borrow()).color.borrow()) as u32) == ((Color::GREEN as i32) as u32))); assert!(((*(*c.borrow()).count.borrow()) == 42)); let c2: Value = >::default(); - (*(*c2.borrow()).color.borrow_mut()) = (Color::BLUE as Color); + (*(*c2.borrow()).color.borrow_mut()) = Color::from((Color::BLUE as i32) as i32); assert!((((*(*c2.borrow()).color.borrow()) as u32) == 2_u32)); return 0; } diff --git a/tests/unit/out/refcount/enum_int_interop.rs b/tests/unit/out/refcount/enum_int_interop.rs new file mode 100644 index 00000000..aa7714e0 --- /dev/null +++ b/tests/unit/out/refcount/enum_int_interop.rs @@ -0,0 +1,189 @@ +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}; +#[derive(Clone, Copy, PartialEq, Debug, Default)] +enum Color { + #[default] + RED = 0, + GREEN = 1, + BLUE = 2, +} +impl From for Color { + fn from(n: i32) -> Color { + match n { + 0 => Color::RED, + 1 => Color::GREEN, + 2 => Color::BLUE, + _ => panic!("invalid Color value: {}", n), + } + } +} +#[derive(Clone, Copy, PartialEq, Debug, Default)] +enum Option { + #[default] + OPT_NONE = 0, + OPT_A = 10, + OPT_B = 20, + OPT_C = 30, +} +impl From for Option { + fn from(n: i32) -> Option { + match n { + 0 => Option::OPT_NONE, + 10 => Option::OPT_A, + 20 => Option::OPT_B, + 30 => Option::OPT_C, + _ => panic!("invalid Option value: {}", n), + } + } +} +#[derive(Clone, Copy, PartialEq, Debug, Default)] +enum Tag { + #[default] + TAG_ZERO = 0, + TAG_ONE = 1, + TAG_TWO = 2, +} +impl From for Tag { + fn from(n: i32) -> Tag { + match n { + 0 => Tag::TAG_ZERO, + 1 => Tag::TAG_ONE, + 2 => Tag::TAG_TWO, + _ => panic!("invalid Tag value: {}", n), + } + } +} +pub fn as_int_0(c: Color) -> i32 { + let c: Value = Rc::new(RefCell::new(c)); + return ((*c.borrow()) as i32).clone(); +} +pub fn classify_option_1(option: i32) -> i32 { + let option: Value = Rc::new(RefCell::new(option)); + 'switch: { + let __match_cond = (*option.borrow()); + match __match_cond { + v if v == (Option::OPT_NONE as i32) => { + return -1_i32; + } + v if v == (Option::OPT_A as i32) => { + return 1; + } + v if v == (Option::OPT_B as i32) => { + return 2; + } + v if v == (Option::OPT_C as i32) => { + return 3; + } + _ => { + return 0; + } + } + }; + panic!("ub: non-void function does not return a value") +} +pub fn make_color_2(n: i32) -> Color { + let n: Value = Rc::new(RefCell::new(n)); + return Color::from((*n.borrow()) as i32); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let c: Value = Rc::new(RefCell::new(Color::RED)); + assert!((((*c.borrow()) as i32) == (Color::RED as i32))); + assert!((((*c.borrow()) as i32) == 0)); + assert!((((*c.borrow()) as i32) != 1)); + if (((*c.borrow()) as i32) == (Color::GREEN as i32)) { + return 1; + } + 'switch: { + let __match_cond = ((*c.borrow()) as i32); + match __match_cond { + v if v == 0 => { + break 'switch; + } + v if v == 1 => { + return 1; + } + v if v == 2 => { + return 2; + } + _ => { + return 99; + } + } + }; + let x: Value = Rc::new(RefCell::new(((*c.borrow()) as i32).clone())); + assert!(((*x.borrow()) == 0)); + let y: Value = Rc::new(RefCell::new((((*c.borrow()) as i32) + 1))); + assert!(((*y.borrow()) == 1)); + (*c.borrow_mut()) = Color::from(2 as i32); + assert!((((*c.borrow()) as i32) == (Color::BLUE as i32))); + assert!((((*c.borrow()) as i32) == 2)); + (*c.borrow_mut()) = ({ + let _n: i32 = 1; + make_color_2(_n) + }); + assert!((((*c.borrow()) as i32) == (Color::GREEN as i32))); + let cmp: Value = Rc::new(RefCell::new(Color::from( + ((((*c.borrow()) as i32) + 1) as i32) as i32, + ))); + assert!((((*cmp.borrow()) as i32) == (Color::BLUE as i32))); + let o: Value