From a55600192b2f5051533e8bb7895578444941d053 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 7 Oct 2026 20:13:40 +0100 Subject: [PATCH 1/6] Add test for non-bool returning comparison operators --- tests/unit/operator_comparison_nonbool.cpp | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 tests/unit/operator_comparison_nonbool.cpp diff --git a/tests/unit/operator_comparison_nonbool.cpp b/tests/unit/operator_comparison_nonbool.cpp new file mode 100644 index 000000000..9425f7aab --- /dev/null +++ b/tests/unit/operator_comparison_nonbool.cpp @@ -0,0 +1,57 @@ +#include +#include + +struct X { + int v; +}; + +int operator==(X a, X b) { return a.v == b.v ? 2 : 0; } +int operator!=(X a, X b) { return a.v != b.v ? 3 : 0; } +int operator<(X a, X b) { return a.v < b.v ? 4 : 0; } +int operator>(X a, X b) { return a.v > b.v ? 5 : 0; } +int operator<=(X a, X b) { return a.v <= b.v ? 6 : 0; } +int operator>=(X a, X b) { return a.v >= b.v ? 7 : 0; } + +struct Result { + int r; +}; + +struct Custom { + int v; + Result operator<=>(const Custom &o) const { return Result{v - o.v}; } +}; + +struct Mixed { + int v; + Result operator<=>(const Mixed &o) const { return Result{v - o.v}; } + int operator<(const Mixed &o) const { return v < o.v ? 8 : 0; } + int operator==(const Mixed &o) const { return v == o.v ? 9 : 0; } +}; + +int main() { + X a{1}, b{2}, c{1}; + assert((a == c) == 2); + assert((a == b) == 0); + assert((a != b) == 3); + assert((a < b) == 4); + assert((b > a) == 5); + assert((a <= c) == 6); + assert((a >= c) == 7); + + X xs[] = {{3}, {1}, {2}}; + std::sort(xs, xs + 3); + assert(xs[0].v == 1 && xs[1].v == 2 && xs[2].v == 3); + + Custom p{5}, q{2}; + Result r = p <=> q; + assert(r.r == 3); + + Mixed m{4}, n{4}; + assert((m == n) == 9); + assert((m <=> n).r == 0); + Mixed ms[] = {{6}, {4}, {5}}; + std::sort(ms, ms + 3); + assert(ms[0].v == 4 && ms[1].v == 5 && ms[2].v == 6); + assert((ms[0] < ms[1]) == 8); + return 0; +} From 25f452c5495677ed15e2af910971337da6fcab41 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 7 Oct 2026 21:07:21 +0100 Subject: [PATCH 2/6] Expand operator_comparison_nonbool --- tests/unit/operator_comparison_nonbool.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/unit/operator_comparison_nonbool.cpp b/tests/unit/operator_comparison_nonbool.cpp index 9425f7aab..fe3623e10 100644 --- a/tests/unit/operator_comparison_nonbool.cpp +++ b/tests/unit/operator_comparison_nonbool.cpp @@ -28,6 +28,19 @@ struct Mixed { int operator==(const Mixed &o) const { return v == o.v ? 9 : 0; } }; +struct MyBool { + bool value; + explicit MyBool(bool v) : value(v) {} + operator bool() const { return value; } +}; + +struct Boolish { + int v; +}; + +MyBool operator==(Boolish a, Boolish b) { return MyBool(a.v == b.v); } +MyBool operator<(Boolish a, Boolish b) { return MyBool(a.v < b.v); } + int main() { X a{1}, b{2}, c{1}; assert((a == c) == 2); @@ -53,5 +66,14 @@ int main() { std::sort(ms, ms + 3); assert(ms[0].v == 4 && ms[1].v == 5 && ms[2].v == 6); assert((ms[0] < ms[1]) == 8); + + Boolish b1{1}, b2{2}; + assert(b1 == Boolish{1}); + assert(!(b2 < b1)); + MyBool lt = b1 < b2; + assert(lt.value); + Boolish bs[] = {{3}, {1}, {2}}; + std::sort(bs, bs + 3); + assert(bs[0].v == 1 && bs[1].v == 2 && bs[2].v == 3); return 0; } From 2dbbc5f10752f9e3d828fdb811998d26ed2ed720 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 8 Oct 2026 13:11:54 +0100 Subject: [PATCH 3/6] Update tests --- tests/unit/operator_comparison_nonbool.cpp | 1 + .../refcount/operator_comparison_nonbool.rs | 355 ++++++++++++++++++ .../out/unsafe/operator_comparison_nonbool.rs | 303 +++++++++++++++ 3 files changed, 659 insertions(+) create mode 100644 tests/unit/out/refcount/operator_comparison_nonbool.rs create mode 100644 tests/unit/out/unsafe/operator_comparison_nonbool.rs diff --git a/tests/unit/operator_comparison_nonbool.cpp b/tests/unit/operator_comparison_nonbool.cpp index fe3623e10..4b2861c05 100644 --- a/tests/unit/operator_comparison_nonbool.cpp +++ b/tests/unit/operator_comparison_nonbool.cpp @@ -1,3 +1,4 @@ +// ADDITIONAL_COMPILE_FLAGS: -std=c++20 #include #include diff --git a/tests/unit/out/refcount/operator_comparison_nonbool.rs b/tests/unit/out/refcount/operator_comparison_nonbool.rs new file mode 100644 index 000000000..a491a003b --- /dev/null +++ b/tests/unit/out/refcount/operator_comparison_nonbool.rs @@ -0,0 +1,355 @@ +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, Record, ByteRepr, VaArg, FnPtrArg, Default)] +#[byte_size(4)] +pub struct X { + #[offset(0)] + pub v: i32, +} +impl std::cmp::Ord for X { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + { + if operator_lt_0(self.clone(), other.clone()) { + std::cmp::Ordering::Less + } else if operator_lt_0(other.clone(), self.clone()) { + std::cmp::Ordering::Greater + } else { + std::cmp::Ordering::Equal + } + } + } +} +impl std::cmp::PartialOrd for X { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl std::cmp::PartialEq for X { + fn eq(&self, other: &Self) -> bool { + { operator_eq_1(self.clone(), other.clone()) } + } +} +impl std::cmp::Eq for X {} +pub fn operator_eq_1(mut a: X, mut b: X) -> i32 { + return if (a.v == b.v) { 2 } else { 0 }; +} +pub fn operator_ne_2(mut a: X, mut b: X) -> i32 { + return if (a.v != b.v) { 3 } else { 0 }; +} +pub fn operator_lt_0(mut a: X, mut b: X) -> i32 { + return if (a.v < b.v) { 4 } else { 0 }; +} +pub fn operator_gt_3(mut a: X, mut b: X) -> i32 { + return if (a.v > b.v) { 5 } else { 0 }; +} +pub fn operator_le_4(mut a: X, mut b: X) -> i32 { + return if (a.v <= b.v) { 6 } else { 0 }; +} +pub fn operator_ge_5(mut a: X, mut b: X) -> i32 { + return if (a.v >= b.v) { 7 } else { 0 }; +} +#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg, Default)] +#[byte_size(4)] +pub struct Result { + #[offset(0)] + pub r: i32, +} +#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg, Default)] +#[byte_size(4)] +pub struct Custom { + #[offset(0)] + pub v: i32, +} +impl std::cmp::Ord for Custom { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + { + CustomImpl::operator_cmp( + &Rc::new(RefCell::new(Custom { v: self.v.clone() })).as_pointer(), + Rc::new(RefCell::new(Custom { v: other.v.clone() })).as_pointer(), + ) + } + } +} +impl std::cmp::PartialOrd for Custom { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl std::cmp::PartialEq for Custom { + fn eq(&self, other: &Self) -> bool { + { + CustomImpl::operator_cmp( + &Rc::new(RefCell::new(Custom { v: self.v.clone() })).as_pointer(), + Rc::new(RefCell::new(Custom { v: other.v.clone() })).as_pointer(), + ) == std::cmp::Ordering::Equal + } + } +} +impl std::cmp::Eq for Custom {} +#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg, Default)] +#[byte_size(4)] +pub struct Mixed { + #[offset(0)] + pub v: i32, +} +impl std::cmp::Ord for Mixed { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + { + MixedImpl::operator_cmp( + &Rc::new(RefCell::new(Mixed { v: self.v.clone() })).as_pointer(), + Rc::new(RefCell::new(Mixed { v: other.v.clone() })).as_pointer(), + ) + } + } +} +impl std::cmp::PartialOrd for Mixed { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl std::cmp::PartialEq for Mixed { + fn eq(&self, other: &Self) -> bool { + { + MixedImpl::operator_eq( + &Rc::new(RefCell::new(Mixed { v: self.v.clone() })).as_pointer(), + Rc::new(RefCell::new(Mixed { v: other.v.clone() })).as_pointer(), + ) + } + } +} +impl std::cmp::Eq for Mixed {} +#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg, Default)] +#[byte_size(1)] +pub struct MyBool { + #[offset(0)] + pub value: bool, +} +impl MyBool { + pub fn new(mut v: bool) -> Self { + Self { value: v } + } +} +#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg, Default)] +#[byte_size(4)] +pub struct Boolish { + #[offset(0)] + pub v: i32, +} +impl std::cmp::Ord for Boolish { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + { + if operator_lt_6(self.clone(), other.clone()) { + std::cmp::Ordering::Less + } else if operator_lt_6(other.clone(), self.clone()) { + std::cmp::Ordering::Greater + } else { + std::cmp::Ordering::Equal + } + } + } +} +impl std::cmp::PartialOrd for Boolish { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl std::cmp::PartialEq for Boolish { + fn eq(&self, other: &Self) -> bool { + { operator_eq_7(self.clone(), other.clone()) } + } +} +impl std::cmp::Eq for Boolish {} +pub fn operator_eq_7(mut a: Boolish, mut b: Boolish) -> MyBool { + return MyBool::new({ (a.v == b.v) }); +} +pub fn operator_lt_6(mut a: Boolish, mut b: Boolish) -> MyBool { + return MyBool::new({ (a.v < b.v) }); +} +pub fn main() { + __cpp2rust_init_globals(); + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let mut a: X = X { v: 1 }; + let mut b: X = X { v: 2 }; + let mut c: X = X { v: 1 }; + assert!( + (({ + let _a: X = (a).clone(); + operator_eq_1(_a, (c).clone()) + }) == 2) + ); + assert!( + (({ + let _a: X = (a).clone(); + operator_eq_1(_a, (b).clone()) + }) == 0) + ); + assert!( + (({ + let _a: X = (a).clone(); + operator_ne_2(_a, (b).clone()) + }) == 3) + ); + assert!( + (({ + let _a: X = (a).clone(); + operator_lt_0(_a, (b).clone()) + }) == 4) + ); + assert!( + (({ + let _a: X = (b).clone(); + operator_gt_3(_a, (a).clone()) + }) == 5) + ); + assert!( + (({ + let _a: X = (a).clone(); + operator_le_4(_a, (c).clone()) + }) == 6) + ); + assert!( + (({ + let _a: X = (a).clone(); + operator_ge_5(_a, (c).clone()) + }) == 7) + ); + let xs: Value> = Rc::new(RefCell::new(Box::new([X { v: 3 }, X { v: 1 }, X { v: 2 }]))); + (xs.as_pointer() as Ptr).sort( + (xs.as_pointer() as Ptr) + .offset((3) as isize) + .get_offset(), + ); + assert!( + (({ (*xs.borrow())[(0) as usize].v } == 1) && ({ (*xs.borrow())[(1) as usize].v } == 2)) + && ({ (*xs.borrow())[(2) as usize].v } == 3) + ); + let p: Value = Rc::new(RefCell::new(Custom { v: 5 })); + let q: Value = Rc::new(RefCell::new(Custom { v: 2 })); + let mut r: Result = ({ CustomImpl::operator_cmp(&p.as_pointer(), q.as_pointer()) }); + assert!((r.r == 3)); + let m: Value = Rc::new(RefCell::new(Mixed { v: 4 })); + let n: Value = Rc::new(RefCell::new(Mixed { v: 4 })); + assert!((({ MixedImpl::operator_eq(&m.as_pointer(), n.as_pointer(),) }) == 9)); + assert!(({ ({ MixedImpl::operator_cmp(&m.as_pointer(), n.as_pointer(),) }).r } == 0)); + let ms: Value> = Rc::new(RefCell::new(Box::new([ + Mixed { v: 6 }, + Mixed { v: 4 }, + Mixed { v: 5 }, + ]))); + (ms.as_pointer() as Ptr).sort( + (ms.as_pointer() as Ptr) + .offset((3) as isize) + .get_offset(), + ); + assert!( + (({ (*ms.borrow())[(0) as usize].v } == 4) && ({ (*ms.borrow())[(1) as usize].v } == 5)) + && ({ (*ms.borrow())[(2) as usize].v } == 6) + ); + assert!( + (({ + let _o: Ptr = (ms.as_pointer() as Ptr).offset(1); + MixedImpl::operator_lt(&(ms.as_pointer() as Ptr).offset(0), _o) + }) == 8) + ); + let mut b1: Boolish = Boolish { v: 1 }; + let mut b2: Boolish = Boolish { v: 2 }; + assert!( + ({ + MyBoolImpl::to_bool( + &Rc::new(RefCell::new( + ({ + let _a: Boolish = (b1).clone(); + operator_eq_7(_a, Boolish { v: 1 }) + }), + )) + .as_pointer(), + ) + }) + ); + assert!( + !({ + MyBoolImpl::to_bool( + &Rc::new(RefCell::new( + ({ + let _a: Boolish = (b2).clone(); + operator_lt_6(_a, (b1).clone()) + }), + )) + .as_pointer(), + ) + }) + ); + let mut lt: MyBool = ({ + let _a: Boolish = (b1).clone(); + operator_lt_6(_a, (b2).clone()) + }); + assert!(lt.value); + let bs: Value> = Rc::new(RefCell::new(Box::new([ + Boolish { v: 3 }, + Boolish { v: 1 }, + Boolish { v: 2 }, + ]))); + (bs.as_pointer() as Ptr).sort( + (bs.as_pointer() as Ptr) + .offset((3) as isize) + .get_offset(), + ); + assert!( + (({ (*bs.borrow())[(0) as usize].v } == 1) && ({ (*bs.borrow())[(1) as usize].v } == 2)) + && ({ (*bs.borrow())[(2) as usize].v } == 3) + ); + return 0; +} +pub trait CustomImpl { + fn operator_cmp(&self, o: Ptr) -> Result; +} +impl CustomImpl for Ptr { + fn operator_cmp(&self, o: Ptr) -> Result { + return Result { + r: ({ (*self).with(|__s| __s.v) } - { o.with(|__s| __s.v) }), + }; + } +} +pub trait MixedImpl { + fn operator_cmp(&self, o: Ptr) -> Result; + fn operator_lt(&self, o: Ptr) -> i32; + fn operator_eq(&self, o: Ptr) -> i32; +} +impl MixedImpl for Ptr { + fn operator_cmp(&self, o: Ptr) -> Result { + return Result { + r: ({ (*self).with(|__s| __s.v) } - { o.with(|__s| __s.v) }), + }; + } + fn operator_lt(&self, o: Ptr) -> i32 { + return if ({ (*self).with(|__s| __s.v) } < { o.with(|__s| __s.v) }) { + 8 + } else { + 0 + }; + } + fn operator_eq(&self, o: Ptr) -> i32 { + return if ({ (*self).with(|__s| __s.v) } == { o.with(|__s| __s.v) }) { + 9 + } else { + 0 + }; + } +} +pub trait MyBoolImpl { + fn to_bool(&self) -> bool; +} +impl MyBoolImpl for Ptr { + fn to_bool(&self) -> bool { + return (*self).with(|__s| __s.value); + } +} +pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/unsafe/operator_comparison_nonbool.rs b/tests/unit/out/unsafe/operator_comparison_nonbool.rs new file mode 100644 index 000000000..230b006d3 --- /dev/null +++ b/tests/unit/out/unsafe/operator_comparison_nonbool.rs @@ -0,0 +1,303 @@ +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; +#[repr(C)] +#[derive(Copy, Clone, VaArg, FnPtrArg, Default)] +pub struct X { + pub v: i32, +} +impl std::cmp::Ord for X { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + unsafe { + if operator_lt_0(self.clone(), other.clone()) { + std::cmp::Ordering::Less + } else if operator_lt_0(other.clone(), self.clone()) { + std::cmp::Ordering::Greater + } else { + std::cmp::Ordering::Equal + } + } + } +} +impl std::cmp::PartialOrd for X { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl std::cmp::PartialEq for X { + fn eq(&self, other: &Self) -> bool { + unsafe { operator_eq_1(self.clone(), other.clone()) } + } +} +impl std::cmp::Eq for X {} +pub unsafe fn operator_eq_1(mut a: X, mut b: X) -> i32 { + return if ((a.v) == (b.v)) { 2 } else { 0 }; +} +pub unsafe fn operator_ne_2(mut a: X, mut b: X) -> i32 { + return if ((a.v) != (b.v)) { 3 } else { 0 }; +} +pub unsafe fn operator_lt_0(mut a: X, mut b: X) -> i32 { + return if ((a.v) < (b.v)) { 4 } else { 0 }; +} +pub unsafe fn operator_gt_3(mut a: X, mut b: X) -> i32 { + return if ((a.v) > (b.v)) { 5 } else { 0 }; +} +pub unsafe fn operator_le_4(mut a: X, mut b: X) -> i32 { + return if ((a.v) <= (b.v)) { 6 } else { 0 }; +} +pub unsafe fn operator_ge_5(mut a: X, mut b: X) -> i32 { + return if ((a.v) >= (b.v)) { 7 } else { 0 }; +} +#[repr(C)] +#[derive(Copy, Clone, VaArg, FnPtrArg, Default)] +pub struct Result { + pub r: i32, +} +#[repr(C)] +#[derive(Copy, Clone, VaArg, FnPtrArg, Default)] +pub struct Custom { + pub v: i32, +} +impl Custom { + pub unsafe fn operator_cmp(&self, o: *const Custom) -> Result { + return Result { + r: ((self.v) - ((*o).v)), + }; + } +} +impl std::cmp::Ord for Custom { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + unsafe { Custom::operator_cmp(self, other as *const Custom) } + } +} +impl std::cmp::PartialOrd for Custom { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl std::cmp::PartialEq for Custom { + fn eq(&self, other: &Self) -> bool { + unsafe { Custom::operator_cmp(self, other as *const Custom) == std::cmp::Ordering::Equal } + } +} +impl std::cmp::Eq for Custom {} +#[repr(C)] +#[derive(Copy, Clone, VaArg, FnPtrArg, Default)] +pub struct Mixed { + pub v: i32, +} +impl Mixed { + pub unsafe fn operator_cmp(&self, o: *const Mixed) -> Result { + return Result { + r: ((self.v) - ((*o).v)), + }; + } + pub unsafe fn operator_lt(&self, o: *const Mixed) -> i32 { + return if ((self.v) < ((*o).v)) { 8 } else { 0 }; + } + pub unsafe fn operator_eq(&self, o: *const Mixed) -> i32 { + return if ((self.v) == ((*o).v)) { 9 } else { 0 }; + } +} +impl std::cmp::Ord for Mixed { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + unsafe { Mixed::operator_cmp(self, other as *const Mixed) } + } +} +impl std::cmp::PartialOrd for Mixed { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl std::cmp::PartialEq for Mixed { + fn eq(&self, other: &Self) -> bool { + unsafe { Mixed::operator_eq(self, other as *const Mixed) } + } +} +impl std::cmp::Eq for Mixed {} +#[repr(C)] +#[derive(Copy, Clone, VaArg, FnPtrArg, Default)] +pub struct MyBool { + pub value: bool, +} +impl MyBool { + pub unsafe fn new(mut v: bool) -> Self { + let mut this = Self { value: v }; + this + } + pub unsafe fn to_bool(&self) -> bool { + return self.value; + } +} +#[repr(C)] +#[derive(Copy, Clone, VaArg, FnPtrArg, Default)] +pub struct Boolish { + pub v: i32, +} +impl std::cmp::Ord for Boolish { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + unsafe { + if operator_lt_6(self.clone(), other.clone()) { + std::cmp::Ordering::Less + } else if operator_lt_6(other.clone(), self.clone()) { + std::cmp::Ordering::Greater + } else { + std::cmp::Ordering::Equal + } + } + } +} +impl std::cmp::PartialOrd for Boolish { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl std::cmp::PartialEq for Boolish { + fn eq(&self, other: &Self) -> bool { + unsafe { operator_eq_7(self.clone(), other.clone()) } + } +} +impl std::cmp::Eq for Boolish {} +pub unsafe fn operator_eq_7(mut a: Boolish, mut b: Boolish) -> MyBool { + return MyBool::new({ ((a.v) == (b.v)) }); +} +pub unsafe fn operator_lt_6(mut a: Boolish, mut b: Boolish) -> MyBool { + return MyBool::new({ ((a.v) < (b.v)) }); +} +pub fn main() { + unsafe { + __cpp2rust_init_globals(); + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut a: X = X { v: 1 }; + let mut b: X = X { v: 2 }; + let mut c: X = X { v: 1 }; + assert!( + ((unsafe { + let _a: X = a; + operator_eq_1(_a, c) + }) == (2)) + ); + assert!( + ((unsafe { + let _a: X = a; + operator_eq_1(_a, b) + }) == (0)) + ); + assert!( + ((unsafe { + let _a: X = a; + operator_ne_2(_a, b) + }) == (3)) + ); + assert!( + ((unsafe { + let _a: X = a; + operator_lt_0(_a, b) + }) == (4)) + ); + assert!( + ((unsafe { + let _a: X = b; + operator_gt_3(_a, a) + }) == (5)) + ); + assert!( + ((unsafe { + let _a: X = a; + operator_le_4(_a, c) + }) == (6)) + ); + assert!( + ((unsafe { + let _a: X = a; + operator_ge_5(_a, c) + }) == (7)) + ); + let mut xs: [X; 3] = [X { v: 3 }, X { v: 1 }, X { v: 2 }]; + { + let len = xs + .as_mut_ptr() + .offset((3) as isize) + .offset_from(xs.as_mut_ptr()) as usize; + ::std::slice::from_raw_parts_mut(xs.as_mut_ptr(), len).sort() + }; + assert!( + (((xs[(0) as usize].v) == (1)) && ((xs[(1) as usize].v) == (2))) + && ((xs[(2) as usize].v) == (3)) + ); + let mut p: Custom = Custom { v: 5 }; + let mut q: Custom = Custom { v: 2 }; + let mut r: Result = (unsafe { Custom::operator_cmp(&p, &q) }); + assert!(((r.r) == (3))); + let mut m: Mixed = Mixed { v: 4 }; + let mut n: Mixed = Mixed { v: 4 }; + assert!(((unsafe { Mixed::operator_eq(&m, &n,) }) == (9))); + assert!((((unsafe { Mixed::operator_cmp(&m, &n,) }).r) == (0))); + let mut ms: [Mixed; 3] = [Mixed { v: 6 }, Mixed { v: 4 }, Mixed { v: 5 }]; + { + let len = ms + .as_mut_ptr() + .offset((3) as isize) + .offset_from(ms.as_mut_ptr()) as usize; + ::std::slice::from_raw_parts_mut(ms.as_mut_ptr(), len).sort() + }; + assert!( + (((ms[(0) as usize].v) == (4)) && ((ms[(1) as usize].v) == (5))) + && ((ms[(2) as usize].v) == (6)) + ); + assert!( + ((unsafe { + let _o: *const Mixed = &ms[(1) as usize]; + Mixed::operator_lt(&ms[(0) as usize], _o) + }) == (8)) + ); + let mut b1: Boolish = Boolish { v: 1 }; + let mut b2: Boolish = Boolish { v: 2 }; + assert!( + (unsafe { + MyBool::to_bool( + &(unsafe { + let _a: Boolish = b1; + operator_eq_7(_a, Boolish { v: 1 }) + }), + ) + }) + ); + assert!( + !(unsafe { + MyBool::to_bool( + &(unsafe { + let _a: Boolish = b2; + operator_lt_6(_a, b1) + }), + ) + }) + ); + let mut lt: MyBool = (unsafe { + let _a: Boolish = b1; + operator_lt_6(_a, b2) + }); + assert!(lt.value); + let mut bs: [Boolish; 3] = [Boolish { v: 3 }, Boolish { v: 1 }, Boolish { v: 2 }]; + { + let len = bs + .as_mut_ptr() + .offset((3) as isize) + .offset_from(bs.as_mut_ptr()) as usize; + ::std::slice::from_raw_parts_mut(bs.as_mut_ptr(), len).sort() + }; + assert!( + (((bs[(0) as usize].v) == (1)) && ((bs[(1) as usize].v) == (2))) + && ((bs[(2) as usize].v) == (3)) + ); + return 0; +} +pub unsafe fn __cpp2rust_init_globals() {} From d4c422f39d173a0c5b25552725bb08640b1133b1 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 8 Oct 2026 13:20:20 +0100 Subject: [PATCH 4/6] Skip Ord trait for non-ordering-returning spaceship operators --- cpp2rust/converter/converter.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 779cfdc0b..86f0ed085 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -4697,6 +4697,10 @@ void Converter::AddOrdTrait(const clang::CXXRecordDecl *decl) { fn->getDescribedFunctionTemplate() || !IsSameTypeComparison(fn, decl)) { return; } + if (fn->getOverloadedOperator() == clang::OO_Spaceship && + !ctx_.CompCategories.lookupInfoForType(fn->getReturnType())) { + return; + } switch (fn->getOverloadedOperator()) { case clang::OO_EqualEqual: eq = fn; From 864405b6633f4e7e1c83bb117f612a2105e85911 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 8 Oct 2026 13:20:45 +0100 Subject: [PATCH 5/6] Convert non-bool comparison return to bool --- cpp2rust/converter/converter.cpp | 36 +++++++++++++++++++++++++++----- cpp2rust/converter/converter.h | 4 ++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 86f0ed085..424d6a1c3 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -4643,6 +4643,32 @@ std::string Converter::GetComparisonCall(const clang::FunctionDecl *op, arg(0, lhs), arg(1, rhs)); } +std::string Converter::GetComparisonBool(const clang::FunctionDecl *op, + const clang::CXXRecordDecl *decl, + std::string_view lhs, + std::string_view rhs) { + auto call = GetComparisonCall(op, decl, lhs, rhs); + auto ret = op->getReturnType(); + if (ret->isBooleanType()) { + return call; + } + if (ret->isIntegerType()) { + return std::format("(({}) != 0)", call); + } + const auto *record = ret->getAsCXXRecordDecl(); + assert(record && "unsupported comparison operator return type"); + for (const auto *found : record->getVisibleConversionFunctions()) { + const auto *conversion = clang::dyn_cast(found); + if (conversion && conversion->getConversionType()->isBooleanType()) { + return std::format("{{ let __cmp = &{}; {}::{}({}) }}", call, + GetUFCSName(conversion), GetMethodName(conversion), + GetComparisonReceiver(conversion, record, "__cmp")); + } + } + assert(0 && "comparison operator result has no conversion to bool"); + return call; +} + std::string Converter::GetComparisonReferenceArg(const clang::CXXRecordDecl *decl, std::string_view value) { @@ -4670,19 +4696,19 @@ void Converter::ConvertOrdAndPartialOrdTraits(const clang::CXXRecordDecl *decl, cmp_body = std::format("if {} {{ std::cmp::Ordering::Less }} else if {} {{ " "std::cmp::Ordering::Greater }} else {{ " "std::cmp::Ordering::Equal }}", - GetComparisonCall(lt, decl, "self", "other"), - GetComparisonCall(lt, decl, "other", "self")); + GetComparisonBool(lt, decl, "self", "other"), + GetComparisonBool(lt, decl, "other", "self")); } if (eq) { - eq_body = GetComparisonCall(eq, decl, "self", "other"); + eq_body = GetComparisonBool(eq, decl, "self", "other"); } else if (cmp) { eq_body = std::format("{} == std::cmp::Ordering::Equal", GetComparisonCall(cmp, decl, "self", "other")); } else { eq_body = std::format("!({}) && !({})", - GetComparisonCall(lt, decl, "self", "other"), - GetComparisonCall(lt, decl, "other", "self")); + GetComparisonBool(lt, decl, "self", "other"), + GetComparisonBool(lt, decl, "other", "self")); } ConvertOrdAndPartialOrdTraitsBase(cmp_body, eq_body, GetRecordName(decl)); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 683ce155e..ff7200358 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -676,6 +676,10 @@ class Converter : public clang::RecursiveASTVisitor { const clang::CXXRecordDecl *decl, std::string_view lhs, std::string_view rhs); + std::string GetComparisonBool(const clang::FunctionDecl *op, + const clang::CXXRecordDecl *decl, + std::string_view lhs, std::string_view rhs); + virtual std::string GetComparisonReferenceArg(const clang::CXXRecordDecl *decl, std::string_view value); From cd3ac046f77f6d24d24f664933a7d7ecc54493b1 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 8 Oct 2026 13:20:59 +0100 Subject: [PATCH 6/6] Update tests --- .../refcount/operator_comparison_nonbool.rs | 83 +++++++++++-------- .../out/unsafe/operator_comparison_nonbool.rs | 51 ++++++------ 2 files changed, 74 insertions(+), 60 deletions(-) diff --git a/tests/unit/out/refcount/operator_comparison_nonbool.rs b/tests/unit/out/refcount/operator_comparison_nonbool.rs index a491a003b..bd227ef7b 100644 --- a/tests/unit/out/refcount/operator_comparison_nonbool.rs +++ b/tests/unit/out/refcount/operator_comparison_nonbool.rs @@ -15,9 +15,9 @@ pub struct X { impl std::cmp::Ord for X { fn cmp(&self, other: &Self) -> std::cmp::Ordering { { - if operator_lt_0(self.clone(), other.clone()) { + if ((operator_lt_0(self.clone(), other.clone())) != 0) { std::cmp::Ordering::Less - } else if operator_lt_0(other.clone(), self.clone()) { + } else if ((operator_lt_0(other.clone(), self.clone())) != 0) { std::cmp::Ordering::Greater } else { std::cmp::Ordering::Equal @@ -32,7 +32,7 @@ impl std::cmp::PartialOrd for X { } impl std::cmp::PartialEq for X { fn eq(&self, other: &Self) -> bool { - { operator_eq_1(self.clone(), other.clone()) } + { ((operator_eq_1(self.clone(), other.clone())) != 0) } } } impl std::cmp::Eq for X {} @@ -66,32 +66,6 @@ pub struct Custom { #[offset(0)] pub v: i32, } -impl std::cmp::Ord for Custom { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - { - CustomImpl::operator_cmp( - &Rc::new(RefCell::new(Custom { v: self.v.clone() })).as_pointer(), - Rc::new(RefCell::new(Custom { v: other.v.clone() })).as_pointer(), - ) - } - } -} -impl std::cmp::PartialOrd for Custom { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl std::cmp::PartialEq for Custom { - fn eq(&self, other: &Self) -> bool { - { - CustomImpl::operator_cmp( - &Rc::new(RefCell::new(Custom { v: self.v.clone() })).as_pointer(), - Rc::new(RefCell::new(Custom { v: other.v.clone() })).as_pointer(), - ) == std::cmp::Ordering::Equal - } - } -} -impl std::cmp::Eq for Custom {} #[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg, Default)] #[byte_size(4)] pub struct Mixed { @@ -101,10 +75,21 @@ pub struct Mixed { impl std::cmp::Ord for Mixed { fn cmp(&self, other: &Self) -> std::cmp::Ordering { { - MixedImpl::operator_cmp( + if ((MixedImpl::operator_lt( &Rc::new(RefCell::new(Mixed { v: self.v.clone() })).as_pointer(), Rc::new(RefCell::new(Mixed { v: other.v.clone() })).as_pointer(), - ) + )) != 0) + { + std::cmp::Ordering::Less + } else if ((MixedImpl::operator_lt( + &Rc::new(RefCell::new(Mixed { v: other.v.clone() })).as_pointer(), + Rc::new(RefCell::new(Mixed { v: self.v.clone() })).as_pointer(), + )) != 0) + { + std::cmp::Ordering::Greater + } else { + std::cmp::Ordering::Equal + } } } } @@ -116,10 +101,10 @@ impl std::cmp::PartialOrd for Mixed { impl std::cmp::PartialEq for Mixed { fn eq(&self, other: &Self) -> bool { { - MixedImpl::operator_eq( + ((MixedImpl::operator_eq( &Rc::new(RefCell::new(Mixed { v: self.v.clone() })).as_pointer(), Rc::new(RefCell::new(Mixed { v: other.v.clone() })).as_pointer(), - ) + )) != 0) } } } @@ -144,9 +129,25 @@ pub struct Boolish { impl std::cmp::Ord for Boolish { fn cmp(&self, other: &Self) -> std::cmp::Ordering { { - if operator_lt_6(self.clone(), other.clone()) { + if { + let __cmp = &operator_lt_6(self.clone(), other.clone()); + MyBoolImpl::to_bool( + &Rc::new(RefCell::new(MyBool { + value: __cmp.value.clone(), + })) + .as_pointer(), + ) + } { std::cmp::Ordering::Less - } else if operator_lt_6(other.clone(), self.clone()) { + } else if { + let __cmp = &operator_lt_6(other.clone(), self.clone()); + MyBoolImpl::to_bool( + &Rc::new(RefCell::new(MyBool { + value: __cmp.value.clone(), + })) + .as_pointer(), + ) + } { std::cmp::Ordering::Greater } else { std::cmp::Ordering::Equal @@ -161,7 +162,17 @@ impl std::cmp::PartialOrd for Boolish { } impl std::cmp::PartialEq for Boolish { fn eq(&self, other: &Self) -> bool { - { operator_eq_7(self.clone(), other.clone()) } + { + { + let __cmp = &operator_eq_7(self.clone(), other.clone()); + MyBoolImpl::to_bool( + &Rc::new(RefCell::new(MyBool { + value: __cmp.value.clone(), + })) + .as_pointer(), + ) + } + } } } impl std::cmp::Eq for Boolish {} diff --git a/tests/unit/out/unsafe/operator_comparison_nonbool.rs b/tests/unit/out/unsafe/operator_comparison_nonbool.rs index 230b006d3..d4b9150e8 100644 --- a/tests/unit/out/unsafe/operator_comparison_nonbool.rs +++ b/tests/unit/out/unsafe/operator_comparison_nonbool.rs @@ -14,9 +14,9 @@ pub struct X { impl std::cmp::Ord for X { fn cmp(&self, other: &Self) -> std::cmp::Ordering { unsafe { - if operator_lt_0(self.clone(), other.clone()) { + if ((operator_lt_0(self.clone(), other.clone())) != 0) { std::cmp::Ordering::Less - } else if operator_lt_0(other.clone(), self.clone()) { + } else if ((operator_lt_0(other.clone(), self.clone())) != 0) { std::cmp::Ordering::Greater } else { std::cmp::Ordering::Equal @@ -31,7 +31,7 @@ impl std::cmp::PartialOrd for X { } impl std::cmp::PartialEq for X { fn eq(&self, other: &Self) -> bool { - unsafe { operator_eq_1(self.clone(), other.clone()) } + unsafe { ((operator_eq_1(self.clone(), other.clone())) != 0) } } } impl std::cmp::Eq for X {} @@ -70,22 +70,6 @@ impl Custom { }; } } -impl std::cmp::Ord for Custom { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - unsafe { Custom::operator_cmp(self, other as *const Custom) } - } -} -impl std::cmp::PartialOrd for Custom { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl std::cmp::PartialEq for Custom { - fn eq(&self, other: &Self) -> bool { - unsafe { Custom::operator_cmp(self, other as *const Custom) == std::cmp::Ordering::Equal } - } -} -impl std::cmp::Eq for Custom {} #[repr(C)] #[derive(Copy, Clone, VaArg, FnPtrArg, Default)] pub struct Mixed { @@ -106,7 +90,15 @@ impl Mixed { } impl std::cmp::Ord for Mixed { fn cmp(&self, other: &Self) -> std::cmp::Ordering { - unsafe { Mixed::operator_cmp(self, other as *const Mixed) } + unsafe { + if ((Mixed::operator_lt(self, other as *const Mixed)) != 0) { + std::cmp::Ordering::Less + } else if ((Mixed::operator_lt(other, self as *const Mixed)) != 0) { + std::cmp::Ordering::Greater + } else { + std::cmp::Ordering::Equal + } + } } } impl std::cmp::PartialOrd for Mixed { @@ -116,7 +108,7 @@ impl std::cmp::PartialOrd for Mixed { } impl std::cmp::PartialEq for Mixed { fn eq(&self, other: &Self) -> bool { - unsafe { Mixed::operator_eq(self, other as *const Mixed) } + unsafe { ((Mixed::operator_eq(self, other as *const Mixed)) != 0) } } } impl std::cmp::Eq for Mixed {} @@ -142,9 +134,15 @@ pub struct Boolish { impl std::cmp::Ord for Boolish { fn cmp(&self, other: &Self) -> std::cmp::Ordering { unsafe { - if operator_lt_6(self.clone(), other.clone()) { + if { + let __cmp = &operator_lt_6(self.clone(), other.clone()); + MyBool::to_bool(__cmp) + } { std::cmp::Ordering::Less - } else if operator_lt_6(other.clone(), self.clone()) { + } else if { + let __cmp = &operator_lt_6(other.clone(), self.clone()); + MyBool::to_bool(__cmp) + } { std::cmp::Ordering::Greater } else { std::cmp::Ordering::Equal @@ -159,7 +157,12 @@ impl std::cmp::PartialOrd for Boolish { } impl std::cmp::PartialEq for Boolish { fn eq(&self, other: &Self) -> bool { - unsafe { operator_eq_7(self.clone(), other.clone()) } + unsafe { + { + let __cmp = &operator_eq_7(self.clone(), other.clone()); + MyBool::to_bool(__cmp) + } + } } } impl std::cmp::Eq for Boolish {}