From 874a78b510f26648763b0ffd54866c6937513bd9 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 14 Oct 2026 09:42:44 +0100 Subject: [PATCH 1/9] Update tests --- tests/unit/lambda_as_field.cpp | 1 - tests/unit/lambda_decltype_type.cpp | 1 - tests/unit/lambda_immediate_init.cpp | 9 +- tests/unit/lambda_static_local.cpp | 1 - tests/unit/lambda_to_fn_ptr.cpp | 1 - tests/unit/out/refcount/lambda_as_field.rs | 245 ++++++++++++++++++ .../unit/out/refcount/lambda_decltype_type.rs | 160 ++++++++++++ .../out/refcount/lambda_immediate_init.rs | 94 +++++++ tests/unit/out/unsafe/lambda_as_field.rs | 223 ++++++++++++++++ tests/unit/out/unsafe/lambda_decltype_type.rs | 117 +++++++++ .../unit/out/unsafe/lambda_immediate_init.rs | 92 +++++++ 11 files changed, 936 insertions(+), 8 deletions(-) create mode 100644 tests/unit/out/refcount/lambda_as_field.rs create mode 100644 tests/unit/out/refcount/lambda_decltype_type.rs create mode 100644 tests/unit/out/refcount/lambda_immediate_init.rs create mode 100644 tests/unit/out/unsafe/lambda_as_field.rs create mode 100644 tests/unit/out/unsafe/lambda_decltype_type.rs create mode 100644 tests/unit/out/unsafe/lambda_immediate_init.rs diff --git a/tests/unit/lambda_as_field.cpp b/tests/unit/lambda_as_field.cpp index 6d7d294c4..a0a37ab14 100644 --- a/tests/unit/lambda_as_field.cpp +++ b/tests/unit/lambda_as_field.cpp @@ -1,5 +1,4 @@ // ADDITIONAL_COMPILE_FLAGS: -std=c++23 -// no-compile #include #include diff --git a/tests/unit/lambda_decltype_type.cpp b/tests/unit/lambda_decltype_type.cpp index 22768e5a0..45b03f791 100644 --- a/tests/unit/lambda_decltype_type.cpp +++ b/tests/unit/lambda_decltype_type.cpp @@ -1,5 +1,4 @@ // ADDITIONAL_COMPILE_FLAGS: -std=c++23 -// no-compile #include template struct Sorted { diff --git a/tests/unit/lambda_immediate_init.cpp b/tests/unit/lambda_immediate_init.cpp index 2d445bee8..c02c2b11c 100644 --- a/tests/unit/lambda_immediate_init.cpp +++ b/tests/unit/lambda_immediate_init.cpp @@ -1,12 +1,11 @@ -// no-compile: refcount -// translation-fail: unsafe #include int pick(int x = [] { return 237; }()) { return x; } struct S { int j = 10; - int i = [this]() { return j * 2; }(); + // Below will only work after: https://github.com/Cpp2Rust/cpp2rust/issues/248 + //int i = [this]() { return j * 2; }(); int k = [] { return 3; }() + 1; }; @@ -23,11 +22,13 @@ int main() { assert(pick(1) == 1); S s; - assert(s.i == 20); + // assert(s.i == 20); assert(s.k == 4); + /* S t{5}; assert(t.i == 10); + */ assert(g == 10); diff --git a/tests/unit/lambda_static_local.cpp b/tests/unit/lambda_static_local.cpp index 0deeb2a6b..3d1ec9025 100644 --- a/tests/unit/lambda_static_local.cpp +++ b/tests/unit/lambda_static_local.cpp @@ -1,5 +1,4 @@ // ADDITIONAL_COMPILE_FLAGS: -std=c++23 -// translation-fail #include static int a; diff --git a/tests/unit/lambda_to_fn_ptr.cpp b/tests/unit/lambda_to_fn_ptr.cpp index c0cda2aea..4f305cabb 100644 --- a/tests/unit/lambda_to_fn_ptr.cpp +++ b/tests/unit/lambda_to_fn_ptr.cpp @@ -1,5 +1,4 @@ // ADDITIONAL_COMPILE_FLAGS: -std=c++23 -// translation-fail #include #include diff --git a/tests/unit/out/refcount/lambda_as_field.rs b/tests/unit/out/refcount/lambda_as_field.rs new file mode 100644 index 000000000..e2087334d --- /dev/null +++ b/tests/unit/out/refcount/lambda_as_field.rs @@ -0,0 +1,245 @@ +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, Destructor)] +#[byte_size(1)] +pub struct Guard__lambda_at_lambda_as_field_cpp______ { + #[offset(0)] + #[byte_size(1)] + pub f: FnPtr, +} +impl Default for Guard__lambda_at_lambda_as_field_cpp______ { + fn default() -> Self { + { + Guard__lambda_at_lambda_as_field_cpp______ { + f: FnPtr::::new(|| { + (*count_0.with(Value::clone).borrow_mut()).postfix_inc(); + }), + } + } + } +} +#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg)] +#[byte_size(8)] +pub struct Holder__lambda_at_lambda_as_field_cpp______ { + #[offset(0)] + #[byte_size(1)] + pub f: FnPtr i32>, + #[offset(4)] + pub calls: i32, +} +impl Default for Holder__lambda_at_lambda_as_field_cpp______ { + fn default() -> Self { + { + Holder__lambda_at_lambda_as_field_cpp______ { + f: FnPtr:: i32>::new(|mut x: i32| -> i32 { + { + return (x + 1); + } + }), + calls: 0_i32, + } + } + } +} +#[derive(Record, ByteRepr, FnPtrArg, MoveCtor)] +#[byte_size(8)] +pub struct Moving__lambda_at_lambda_as_field_cpp______ { + #[offset(0)] + #[byte_size(1)] + pub f: FnPtr i32>, + #[offset(4)] + pub moves: i32, +} +impl Moving__lambda_at_lambda_as_field_cpp______ { + pub fn new(f: FnPtr i32>) -> Self { + let f: Value i32>> = Rc::new(RefCell::new(f)); + Self { + f: (*f.borrow()).copy_from(), + moves: 0, + } + } + pub fn move_from(o: Ptr) -> Self { + Self { + f: (*o.upgrade().deref()).f.move_from(), + moves: (o.with(|__s| __s.moves) + 1), + } + } +} +impl Default for Moving__lambda_at_lambda_as_field_cpp______ { + fn default() -> Self { + { + Moving__lambda_at_lambda_as_field_cpp______ { + f: FnPtr:: i32>::new(|mut x: i32| -> i32 { + { + return (x * 2); + } + }), + moves: 0, + } + } + } +} +pub fn wrap_1(fn_: FnPtr i32>) -> FnPtr i32> { + let fn_: Value i32>> = Rc::new(RefCell::new(fn_)); + return lambda!( + { + let fn_: Value i32>> = + Rc::new(RefCell::new((*fn_.borrow()).copy_from())); + }, + |mut x: i32| -> i32 { + return (({ (*fn_.borrow()).call(x) }) + 1); + } + ); +} +pub fn wrap_2(fn_: FnPtr i32>) -> FnPtr i32> { + let fn_: Value i32>> = Rc::new(RefCell::new(fn_)); + return lambda!( + { + let fn_: Value i32>> = + Rc::new(RefCell::new((*fn_.borrow()).copy_from())); + }, + |mut x: i32| -> i32 { + return (({ (*fn_.borrow()).call(x) }) + 1); + } + ); +} +thread_local!( + pub static count_0: Value = Rc::new(RefCell::new(0)); +); +pub fn main() { + __cpp2rust_init_globals(); + std::process::exit(main_0()); +} +fn main_0() -> i32 { + { + let g: Value = + Rc::new(RefCell::new(Guard__lambda_at_lambda_as_field_cpp______ { + f: FnPtr::::new(|| { + (*count_0.with(Value::clone).borrow_mut()).postfix_inc(); + }), + })); + let _dtor_g = ScopedDestructor::new(&g, |__p| __p.destructor()); + assert!((count_0.with(|rc| *rc.borrow()) == 0)); + } + assert!((count_0.with(|rc| *rc.borrow()) == 1)); + let n: Value = + Rc::new(RefCell::new(Holder__lambda_at_lambda_as_field_cpp______ { + f: FnPtr:: i32>::new(|mut x: i32| -> i32 { + { + return (x + 1); + } + }), + calls: 0, + })); + assert!( + (({ Holder__lambda_at_lambda_as_field_cpp______Impl::call(&n.as_pointer(), 2,) }) == 3) + ); + assert!(({ (*n.borrow()).calls } == 1)); + let cleaned: Value = Rc::new(RefCell::new(0)); + { + let g: Value = + Rc::new(RefCell::new(Guard__lambda_at_lambda_as_field_cpp______ { + f: lambda!( + { + let cleaned: Ptr = cleaned.as_pointer(); + }, + || { + cleaned.with_mut(|__v| __v.postfix_inc()); + } + ), + })); + let _dtor_g = ScopedDestructor::new(&g, |__p| __p.destructor()); + assert!(((*cleaned.borrow()) == 0)); + } + assert!(((*cleaned.borrow()) == 1)); + let factor: Value = Rc::new(RefCell::new(3)); + let h: Value = + Rc::new(RefCell::new(Holder__lambda_at_lambda_as_field_cpp______ { + f: lambda!( + { + let factor: Value = Rc::new(RefCell::new((*factor.borrow()))); + }, + |mut x: i32| -> i32 { + return (x * (*factor.borrow())); + } + ), + calls: 0, + })); + (*factor.borrow_mut()) = 100; + assert!( + (({ Holder__lambda_at_lambda_as_field_cpp______Impl::call(&h.as_pointer(), 2,) }) == 6) + ); + assert!( + (({ Holder__lambda_at_lambda_as_field_cpp______Impl::call(&h.as_pointer(), 5,) }) == 15) + ); + assert!(({ (*h.borrow()).calls } == 2)); + let w: Value i32>> = Rc::new(RefCell::new( + ({ + wrap_1(lambda!( + { + let factor: Value = Rc::new(RefCell::new((*factor.borrow()))); + }, + |mut x: i32| -> i32 { + return (x * (*factor.borrow())); + } + )) + }), + )); + (*factor.borrow_mut()) = 7; + assert!((({ (*w.borrow()).call(2,) }) == 201)); + let ww: Value i32>> = + Rc::new(RefCell::new(({ wrap_2((*w.borrow()).copy_from()) }))); + assert!((({ (*ww.borrow()).call(2,) }) == 202)); + let m: Value = Rc::new(RefCell::new( + Moving__lambda_at_lambda_as_field_cpp______::new({ + FnPtr:: i32>::new(|mut x: i32| -> i32 { + { + return (x * 2); + } + }) + }), + )); + let m2: Value = Rc::new(RefCell::new( + Moving__lambda_at_lambda_as_field_cpp______::move_from({ m.as_pointer() }), + )); + assert!((({ (*m2.borrow()).f.call(3,) }) == 6)); + assert!(({ (*m2.borrow()).moves } == 1)); + return 0; +} +pub trait Guard__lambda_at_lambda_as_field_cpp______Impl { + fn destructor(&self); +} +impl Guard__lambda_at_lambda_as_field_cpp______Impl + for Ptr +{ + fn destructor(&self) { + ({ (*(*self).upgrade().deref()).f.call() }); + } + fn destructor(&self) { + ({ (*(*self).upgrade().deref()).f.call() }); + } +} +pub trait Holder__lambda_at_lambda_as_field_cpp______Impl { + fn call(&self, x: i32) -> i32; +} +impl Holder__lambda_at_lambda_as_field_cpp______Impl + for Ptr +{ + fn call(&self, mut x: i32) -> i32 { + field!((*self), calls).with_mut(|__v| __v.postfix_inc()); + return ({ (*(*self).upgrade().deref()).f.call(x) }); + } + fn call(&self, mut x: i32) -> i32 { + field!((*self), calls).with_mut(|__v| __v.postfix_inc()); + return ({ (*(*self).upgrade().deref()).f.call(x) }); + } +} +pub fn __cpp2rust_init_globals() { + let _ = count_0.with(|_| ()); +} diff --git a/tests/unit/out/refcount/lambda_decltype_type.rs b/tests/unit/out/refcount/lambda_decltype_type.rs new file mode 100644 index 000000000..72b906d4a --- /dev/null +++ b/tests/unit/out/refcount/lambda_decltype_type.rs @@ -0,0 +1,160 @@ +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(DeepClone, Record, ByteRepr, VaArg, FnPtrArg)] +#[byte_size(24)] +pub struct Sorted__lambda_at_lambda_decltype_type_cpp______ { + #[offset(0)] + #[byte_size(16)] + pub items: Value>, + #[offset(16)] + pub size: i32, + #[offset(20)] + #[byte_size(1)] + pub cmp: FnPtr bool>, +} +impl Default for Sorted__lambda_at_lambda_decltype_type_cpp______ { + fn default() -> Self { + { + Sorted__lambda_at_lambda_decltype_type_cpp______ { + items: Rc::new(RefCell::new((0..4).map(|_| 0_i32).collect::>())), + size: 0, + cmp: FnPtr:: bool>::new(|mut a: i32, mut b: i32| -> bool { + { + return (a > b); + } + }), + } + } + } +} +pub fn main() { + __cpp2rust_init_globals(); + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let greater: Value bool>> = + Rc::new(RefCell::new(FnPtr:: bool>::new( + |mut a: i32, mut b: i32| -> bool { + { + return (a > b); + } + }, + ))); + let desc: Value = Rc::new(RefCell::new( + ::default(), + )); + ({ Sorted__lambda_at_lambda_decltype_type_cpp______Impl::insert(&desc.as_pointer(), 2) }); + ({ Sorted__lambda_at_lambda_decltype_type_cpp______Impl::insert(&desc.as_pointer(), 7) }); + ({ Sorted__lambda_at_lambda_decltype_type_cpp______Impl::insert(&desc.as_pointer(), 4) }); + assert!( + ((elem!( + (array_field_ptr!(desc.as_pointer(), items) as Ptr::), + 0 + ) + .read()) + == 7) + ); + assert!( + ((elem!( + (array_field_ptr!(desc.as_pointer(), items) as Ptr::), + 1 + ) + .read()) + == 4) + ); + assert!( + ((elem!( + (array_field_ptr!(desc.as_pointer(), items) as Ptr::), + 2 + ) + .read()) + == 2) + ); + let asc: Value = Rc::new(RefCell::new( + ::default(), + )); + ({ Sorted__lambda_at_lambda_decltype_type_cpp______Impl::insert(&asc.as_pointer(), 2) }); + ({ Sorted__lambda_at_lambda_decltype_type_cpp______Impl::insert(&asc.as_pointer(), 7) }); + ({ Sorted__lambda_at_lambda_decltype_type_cpp______Impl::insert(&asc.as_pointer(), 4) }); + assert!(((elem!((array_field_ptr!(asc.as_pointer(), items) as Ptr::), 0).read()) == 2)); + assert!(((elem!((array_field_ptr!(asc.as_pointer(), items) as Ptr::), 1).read()) == 4)); + assert!(((elem!((array_field_ptr!(asc.as_pointer(), items) as Ptr::), 2).read()) == 7)); + let fresh: Value bool>> = + Rc::new(RefCell::new(FnPtr:: bool>::new( + |mut a: i32, mut b: i32| -> bool { + { + return (a > b); + } + }, + ))); + assert!((({ (*fresh.borrow()).call(3, 1,) }) as bool)); + assert!(!({ (*fresh.borrow()).call(1, 3,) })); + let assigned: Value bool>> = + Rc::new(RefCell::new(FnPtr:: bool>::new( + |mut a: i32, mut b: i32| -> bool { + { + return (a > b); + } + }, + ))); + (*assigned.borrow_mut()) = (*greater.borrow()).clone(); + assert!((({ (*assigned.borrow()).call(5, 4,) }) as bool)); + let less: Value bool>> = + Rc::new(RefCell::new(FnPtr:: bool>::new( + |mut a: i32, mut b: i32| -> bool { + { + return (a < b); + } + }, + ))); + assert!((({ (*less.borrow()).call(1, 3,) }) as bool)); + return 0; +} +pub trait Sorted__lambda_at_lambda_decltype_type_cpp______Impl { + fn insert(&self, v: i32); +} +impl Sorted__lambda_at_lambda_decltype_type_cpp______Impl + for Ptr +{ + fn insert(&self, mut v: i32) { + let mut i: i32 = (*self).with(|__s| __s.size); + 'loop_: while (i > 0) + && ({ + let _b: i32 = + (elem!((array_field_ptr!((*self), items) as Ptr::), (i - 1)).read()); + (*(*self).upgrade().deref()).cmp.call(v, _b) + }) + { + elem!((array_field_ptr!((*self), items) as Ptr::), i).write({ + (elem!((array_field_ptr!((*self), items) as Ptr::), (i - 1)).read()) + }); + i.postfix_dec(); + } + elem!((array_field_ptr!((*self), items) as Ptr::), i).write(v); + field!((*self), size).with_mut(|__v| __v.postfix_inc()); + } + fn insert(&self, mut v: i32) { + let mut i: i32 = (*self).with(|__s| __s.size); + 'loop_: while (i > 0) + && ({ + let _b: i32 = + (elem!((array_field_ptr!((*self), items) as Ptr::), (i - 1)).read()); + (*(*self).upgrade().deref()).cmp.call(v, _b) + }) + { + elem!((array_field_ptr!((*self), items) as Ptr::), i).write({ + (elem!((array_field_ptr!((*self), items) as Ptr::), (i - 1)).read()) + }); + i.postfix_dec(); + } + elem!((array_field_ptr!((*self), items) as Ptr::), i).write(v); + field!((*self), size).with_mut(|__v| __v.postfix_inc()); + } +} +pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/refcount/lambda_immediate_init.rs b/tests/unit/out/refcount/lambda_immediate_init.rs new file mode 100644 index 000000000..0cc937595 --- /dev/null +++ b/tests/unit/out/refcount/lambda_immediate_init.rs @@ -0,0 +1,94 @@ +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 pick_0(x: Option) -> i32 { + let mut x: i32 = x.unwrap_or_else(|| { + ({ + FnPtr:: i32>::new(|| -> i32 { + { + return 237; + } + }) + .call() + }) + }); + return x; +} +#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg)] +#[byte_size(8)] +pub struct S { + #[offset(0)] + pub j: i32, + #[offset(4)] + pub k: i32, +} +impl Default for S { + fn default() -> Self { + { + S { + j: 10, + k: (({ + FnPtr:: i32>::new(|| -> i32 { + { + return 3; + } + }) + .call() + }) + 1), + } + } + } +} +thread_local!( + pub static g_1: Value = Rc::new(RefCell::new( + ({ + FnPtr:: i32>::new(|| -> i32 { + { + let mut s: i32 = 0; + let mut i: i32 = 1; + 'loop_: while (i <= 4) { + s += i; + i.postfix_inc(); + } + return s; + } + }) + .call() + }), + )); +); +pub fn main() { + __cpp2rust_init_globals(); + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!((({ pick_0(None,) }) == 237)); + assert!((({ pick_0(Some(1),) }) == 1)); + let mut s: S = ::default(); + assert!((s.k == 4)); + assert!((g_1.with(|rc| *rc.borrow()) == 10)); + let a: Value = Rc::new(RefCell::new(2)); + let mut c: i32 = ({ + lambda!( + { + let a: Ptr = a.as_pointer(); + }, + || -> i32 { + a.with_mut(|__v| __v.postfix_inc()); + return ((a.read()) * 10); + } + ) + .call() + }); + assert!((c == 30)); + assert!(((*a.borrow()) == 3)); + return 0; +} +pub fn __cpp2rust_init_globals() { + let _ = g_1.with(|_| ()); +} diff --git a/tests/unit/out/unsafe/lambda_as_field.rs b/tests/unit/out/unsafe/lambda_as_field.rs new file mode 100644 index 000000000..d6e589f5e --- /dev/null +++ b/tests/unit/out/unsafe/lambda_as_field.rs @@ -0,0 +1,223 @@ +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(Clone, VaArg, FnPtrArg, DestructorUnsafe)] +pub struct Guard__lambda_at_lambda_as_field_cpp______ { + pub f: FnPtr, +} +impl Guard__lambda_at_lambda_as_field_cpp______ { + pub unsafe fn destructor(&mut self) { + (unsafe { self.f.call() }); + } +} +impl Default for Guard__lambda_at_lambda_as_field_cpp______ { + fn default() -> Self { + unsafe { + Guard__lambda_at_lambda_as_field_cpp______ { + f: FnPtr::::new(|| unsafe { + (*std::cell::LazyCell::force_mut(&mut *&raw mut count_0)).postfix_inc(); + }), + } + } + } +} +impl Guard__lambda_at_lambda_as_field_cpp______ { + pub unsafe fn destructor(&mut self) { + (unsafe { self.f.call() }); + } +} +#[repr(C)] +#[derive(Clone, VaArg, FnPtrArg)] +pub struct Holder__lambda_at_lambda_as_field_cpp______ { + pub f: FnPtr i32>, + pub calls: i32, +} +impl Holder__lambda_at_lambda_as_field_cpp______ { + pub unsafe fn call(&mut self, mut x: i32) -> i32 { + self.calls.postfix_inc(); + return (unsafe { self.f.call(x) }); + } +} +impl Default for Holder__lambda_at_lambda_as_field_cpp______ { + fn default() -> Self { + unsafe { + Holder__lambda_at_lambda_as_field_cpp______ { + f: FnPtr:: i32>::new(|mut x: i32| -> i32 { + unsafe { + return ((x) + (1)); + } + }), + calls: 0_i32, + } + } + } +} +impl Holder__lambda_at_lambda_as_field_cpp______ { + pub unsafe fn call(&mut self, mut x: i32) -> i32 { + self.calls.postfix_inc(); + return (unsafe { self.f.call(x) }); + } +} +#[repr(C)] +#[derive(FnPtrArg, MoveCtorUnsafe)] +pub struct Moving__lambda_at_lambda_as_field_cpp______ { + pub f: FnPtr i32>, + pub moves: i32, +} +impl Moving__lambda_at_lambda_as_field_cpp______ { + pub unsafe fn new(mut f: FnPtr i32>) -> Self { + let mut this = Self { + f: f.copy_from(), + moves: 0, + }; + this + } + pub unsafe fn move_from(o: *mut Moving__lambda_at_lambda_as_field_cpp______) -> Self { + let mut this = Self { + f: (*o).f.move_from(), + moves: (((*o).moves) + (1)), + }; + this + } +} +impl Default for Moving__lambda_at_lambda_as_field_cpp______ { + fn default() -> Self { + unsafe { + Moving__lambda_at_lambda_as_field_cpp______ { + f: FnPtr:: i32>::new(|mut x: i32| -> i32 { + unsafe { + return ((x) * (2)); + } + }), + moves: 0, + } + } + } +} +pub unsafe fn wrap_1(mut fn_: FnPtr i32>) -> FnPtr i32> { + return lambda_unsafe!( + { + let fn_: FnPtr i32> = fn_.copy_from(); + }, + |mut x: i32| -> i32 { + return ((unsafe { fn_.call(x) }) + (1)); + } + ); +} +pub unsafe fn wrap_2(mut fn_: FnPtr i32>) -> FnPtr i32> { + return lambda_unsafe!( + { + let fn_: FnPtr i32> = fn_.copy_from(); + }, + |mut x: i32| -> i32 { + return ((unsafe { fn_.call(x) }) + (1)); + } + ); +} +pub static mut count_0: std::cell::LazyCell = std::cell::LazyCell::new(|| unsafe { 0 }); +pub fn main() { + unsafe { + __cpp2rust_init_globals(); + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + { + let mut g: Guard__lambda_at_lambda_as_field_cpp______ = + Guard__lambda_at_lambda_as_field_cpp______ { + f: FnPtr::::new(|| unsafe { + (*std::cell::LazyCell::force_mut(&mut *&raw mut count_0)).postfix_inc(); + }), + }; + let _dtor_g = ScopedDestructorUnsafe::new( + &raw mut g, + Guard__lambda_at_lambda_as_field_cpp______::destructor, + ); + assert!(((*std::cell::LazyCell::force_mut(&mut *&raw mut count_0)) == (0))); + } + assert!(((*std::cell::LazyCell::force_mut(&mut *&raw mut count_0)) == (1))); + let mut n: Holder__lambda_at_lambda_as_field_cpp______ = + Holder__lambda_at_lambda_as_field_cpp______ { + f: FnPtr:: i32>::new(|mut x: i32| -> i32 { + unsafe { + return ((x) + (1)); + } + }), + calls: 0, + }; + assert!(((unsafe { Holder__lambda_at_lambda_as_field_cpp______::call(&mut n, 2,) }) == (3))); + assert!(((n.calls) == (1))); + let mut cleaned: i32 = 0; + { + let mut g: Guard__lambda_at_lambda_as_field_cpp______ = + Guard__lambda_at_lambda_as_field_cpp______ { + f: lambda_unsafe!( + { + let cleaned: *mut i32 = &mut cleaned; + }, + || { + (*cleaned).postfix_inc(); + } + ), + }; + let _dtor_g = ScopedDestructorUnsafe::new( + &raw mut g, + Guard__lambda_at_lambda_as_field_cpp______::destructor, + ); + assert!(((cleaned) == (0))); + } + assert!(((cleaned) == (1))); + let mut factor: i32 = 3; + let mut h: Holder__lambda_at_lambda_as_field_cpp______ = + Holder__lambda_at_lambda_as_field_cpp______ { + f: lambda_unsafe!( + { + let factor: i32 = factor; + }, + |mut x: i32| -> i32 { + return ((x) * (factor)); + } + ), + calls: 0, + }; + factor = 100; + assert!(((unsafe { Holder__lambda_at_lambda_as_field_cpp______::call(&mut h, 2,) }) == (6))); + assert!(((unsafe { Holder__lambda_at_lambda_as_field_cpp______::call(&mut h, 5,) }) == (15))); + assert!(((h.calls) == (2))); + let mut w: FnPtr i32> = (unsafe { + wrap_1(lambda_unsafe!( + { + let factor: i32 = factor; + }, + |mut x: i32| -> i32 { + return ((x) * (factor)); + } + )) + }); + factor = 7; + assert!(((unsafe { w.call(2,) }) == (201))); + let mut ww: FnPtr i32> = (unsafe { wrap_2(w.copy_from()) }); + assert!(((unsafe { ww.call(2,) }) == (202))); + let mut m: Moving__lambda_at_lambda_as_field_cpp______ = + Moving__lambda_at_lambda_as_field_cpp______::new({ + FnPtr:: i32>::new(|mut x: i32| -> i32 { + unsafe { + return ((x) * (2)); + } + }) + }); + let mut m2: Moving__lambda_at_lambda_as_field_cpp______ = + Moving__lambda_at_lambda_as_field_cpp______::move_from({ &mut m }); + assert!(((unsafe { m2.f.call(3,) }) == (6))); + assert!(((m2.moves) == (1))); + return 0; +} +pub unsafe fn __cpp2rust_init_globals() { + std::cell::LazyCell::force(&*&raw const count_0); +} diff --git a/tests/unit/out/unsafe/lambda_decltype_type.rs b/tests/unit/out/unsafe/lambda_decltype_type.rs new file mode 100644 index 000000000..90fe68c77 --- /dev/null +++ b/tests/unit/out/unsafe/lambda_decltype_type.rs @@ -0,0 +1,117 @@ +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(Clone, VaArg, FnPtrArg)] +pub struct Sorted__lambda_at_lambda_decltype_type_cpp______ { + pub items: [i32; 4], + pub size: i32, + pub cmp: FnPtr bool>, +} +impl Sorted__lambda_at_lambda_decltype_type_cpp______ { + pub unsafe fn insert(&mut self, mut v: i32) { + let mut i: i32 = self.size; + 'loop_: while ((i) > (0)) + && (unsafe { + let _b: i32 = self.items[((i) - (1)) as usize]; + self.cmp.call(v, _b) + }) + { + self.items[(i) as usize] = self.items[((i) - (1)) as usize]; + i.postfix_dec(); + } + self.items[(i) as usize] = v; + self.size.postfix_inc(); + } +} +impl Default for Sorted__lambda_at_lambda_decltype_type_cpp______ { + fn default() -> Self { + unsafe { + Sorted__lambda_at_lambda_decltype_type_cpp______ { + items: [0_i32; 4], + size: 0, + cmp: FnPtr:: bool>::new(|mut a: i32, mut b: i32| -> bool { + unsafe { + return ((a) > (b)); + } + }), + } + } + } +} +impl Sorted__lambda_at_lambda_decltype_type_cpp______ { + pub unsafe fn insert(&mut self, mut v: i32) { + let mut i: i32 = self.size; + 'loop_: while ((i) > (0)) + && (unsafe { + let _b: i32 = self.items[((i) - (1)) as usize]; + self.cmp.call(v, _b) + }) + { + self.items[(i) as usize] = self.items[((i) - (1)) as usize]; + i.postfix_dec(); + } + self.items[(i) as usize] = v; + self.size.postfix_inc(); + } +} +pub fn main() { + unsafe { + __cpp2rust_init_globals(); + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut greater: FnPtr bool> = + FnPtr:: bool>::new(|mut a: i32, mut b: i32| -> bool { + unsafe { + return ((a) > (b)); + } + }); + let mut desc: Sorted__lambda_at_lambda_decltype_type_cpp______ = + ::default(); + (unsafe { Sorted__lambda_at_lambda_decltype_type_cpp______::insert(&mut desc, 2) }); + (unsafe { Sorted__lambda_at_lambda_decltype_type_cpp______::insert(&mut desc, 7) }); + (unsafe { Sorted__lambda_at_lambda_decltype_type_cpp______::insert(&mut desc, 4) }); + assert!(((desc.items[(0) as usize]) == (7))); + assert!(((desc.items[(1) as usize]) == (4))); + assert!(((desc.items[(2) as usize]) == (2))); + let mut asc: Sorted__lambda_at_lambda_decltype_type_cpp______ = + ::default(); + (unsafe { Sorted__lambda_at_lambda_decltype_type_cpp______::insert(&mut asc, 2) }); + (unsafe { Sorted__lambda_at_lambda_decltype_type_cpp______::insert(&mut asc, 7) }); + (unsafe { Sorted__lambda_at_lambda_decltype_type_cpp______::insert(&mut asc, 4) }); + assert!(((asc.items[(0) as usize]) == (2))); + assert!(((asc.items[(1) as usize]) == (4))); + assert!(((asc.items[(2) as usize]) == (7))); + let mut fresh: FnPtr bool> = + FnPtr:: bool>::new(|mut a: i32, mut b: i32| -> bool { + unsafe { + return ((a) > (b)); + } + }); + assert!(((unsafe { fresh.call(3, 1,) }) as bool)); + assert!(!(unsafe { fresh.call(1, 3,) })); + let mut assigned: FnPtr bool> = + FnPtr:: bool>::new(|mut a: i32, mut b: i32| -> bool { + unsafe { + return ((a) > (b)); + } + }); + assigned = (greater).clone(); + assert!(((unsafe { assigned.call(5, 4,) }) as bool)); + let mut less: FnPtr bool> = + FnPtr:: bool>::new(|mut a: i32, mut b: i32| -> bool { + unsafe { + return ((a) < (b)); + } + }); + assert!(((unsafe { less.call(1, 3,) }) as bool)); + return 0; +} +pub unsafe fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/unsafe/lambda_immediate_init.rs b/tests/unit/out/unsafe/lambda_immediate_init.rs new file mode 100644 index 000000000..c9f388249 --- /dev/null +++ b/tests/unit/out/unsafe/lambda_immediate_init.rs @@ -0,0 +1,92 @@ +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 pick_0(mut x: Option) -> i32 { + let mut x: i32 = x.unwrap_or_else(|| unsafe { + (unsafe { + FnPtr:: i32>::new(|| -> i32 { + unsafe { + return 237; + } + }) + .call() + }) + }); + return x; +} +#[repr(C)] +#[derive(Copy, Clone, VaArg, FnPtrArg)] +pub struct S { + pub j: i32, + pub k: i32, +} +impl Default for S { + fn default() -> Self { + unsafe { + S { + j: 10, + k: ((unsafe { + FnPtr:: i32>::new(|| -> i32 { + unsafe { + return 3; + } + }) + .call() + }) + (1)), + } + } + } +} +pub static mut g_1: std::cell::LazyCell = std::cell::LazyCell::new(|| unsafe { + (unsafe { + FnPtr:: i32>::new(|| -> i32 { + unsafe { + let mut s: i32 = 0; + let mut i: i32 = 1; + 'loop_: while ((i) <= (4)) { + s += i; + i.postfix_inc(); + } + return s; + } + }) + .call() + }) +}); +pub fn main() { + unsafe { + __cpp2rust_init_globals(); + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!(((unsafe { pick_0(None,) }) == (237))); + assert!(((unsafe { pick_0(Some(1),) }) == (1))); + let mut s: S = ::default(); + assert!(((s.k) == (4))); + assert!(((*std::cell::LazyCell::force_mut(&mut *&raw mut g_1)) == (10))); + let mut a: i32 = 2; + let c: i32 = (unsafe { + lambda_unsafe!( + { + let a: *mut i32 = &mut a; + }, + || -> i32 { + (*a).postfix_inc(); + return ((*a) * (10)); + } + ) + .call() + }); + assert!(((c) == (30))); + assert!(((a) == (3))); + return 0; +} +pub unsafe fn __cpp2rust_init_globals() { + std::cell::LazyCell::force(&*&raw const g_1); +} From 637eb9478bd8258984e920a2ac17207d7628237d Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 14 Oct 2026 09:50:55 +0100 Subject: [PATCH 2/9] Fix IsUserDefinedDecl for redeclarations --- cpp2rust/converter/converter_lib.cpp | 18 ++++++++++++++++++ cpp2rust/converter/rules/rules_loader.cpp | 10 ++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index c11ea4977..957547077 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -296,6 +296,24 @@ std::string GetNumAsString(llvm::APFloat value) { bool IsUserDefinedDecl(const clang::Decl *decl) { const auto &ctx = decl->getASTContext(); const auto &src_mgr = ctx.getSourceManager(); + if (auto fn = llvm::dyn_cast(decl)) { + if (const clang::FunctionDecl *definition = nullptr; + fn->isDefined(definition)) { + decl = definition; + } else if (llvm::any_of(fn->redecls(), [&](auto redecl) { + return src_mgr.isInSystemHeader(redecl->getLocation()); + })) { + return false; + } + } else if (auto var = llvm::dyn_cast(decl)) { + if (auto definition = var->getDefinition()) { + decl = definition; + } else if (llvm::any_of(var->redecls(), [&](auto redecl) { + return src_mgr.isInSystemHeader(redecl->getLocation()); + })) { + return false; + } + } auto src_loc = decl->getLocation(); if (auto *spec = clang::dyn_cast(decl); diff --git a/cpp2rust/converter/rules/rules_loader.cpp b/cpp2rust/converter/rules/rules_loader.cpp index 529b09601..29d9a85b1 100644 --- a/cpp2rust/converter/rules/rules_loader.cpp +++ b/cpp2rust/converter/rules/rules_loader.cpp @@ -389,10 +389,7 @@ std::string ConstructorKey(const std::string &class_key) { } std::string FunctionKey(const clang::FunctionDecl *decl) { - if (const clang::FunctionDecl *definition = nullptr; - decl->isDefined(definition) && - !decl->getASTContext().getSourceManager().isInSystemHeader( - definition->getLocation())) { + if (decl->isDefined() && IsUserDefinedDecl(decl)) { return {}; } if (auto method = llvm::dyn_cast(decl)) { @@ -409,6 +406,11 @@ std::string DeclKey(const clang::NamedDecl *decl) { if (auto fn = decl->getAsFunction()) { return FunctionKey(fn); } + if (auto var = llvm::dyn_cast(decl); + var && var->hasDefinition() != clang::VarDecl::DeclarationOnly && + IsUserDefinedDecl(var)) { + return {}; + } if (auto record = llvm::dyn_cast(decl->getDeclContext())) { return MemberKey(ClassKey(record), MemberName(decl->getDeclName())); } From c6421d31e278ccc34846737b8582a62f3b48da89 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 14 Oct 2026 10:24:17 +0100 Subject: [PATCH 3/9] Print lambda in GetNamedDeclAsString --- cpp2rust/converter/converter_lib.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 957547077..01041b29a 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -1000,9 +1000,11 @@ std::string GetNamedDeclAsString(const clang::NamedDecl *decl) { target = record; } } + auto *record = clang::dyn_cast(target); return std::format( - "anon_{}", type_mapping.try_emplace(GetID(target), type_mapping.size()) - .first->second); + "{}_{}", record && record->isLambda() ? "lambda" : "anon", + type_mapping.try_emplace(GetID(target), type_mapping.size()) + .first->second); } std::optional id; From e2daca04667dc869e963ec292703cd2e26d4e92f Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 14 Oct 2026 10:29:03 +0100 Subject: [PATCH 4/9] Add no-compile back --- tests/unit/lambda_as_field.cpp | 2 ++ tests/unit/lambda_decltype_type.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/unit/lambda_as_field.cpp b/tests/unit/lambda_as_field.cpp index a0a37ab14..48ddeda96 100644 --- a/tests/unit/lambda_as_field.cpp +++ b/tests/unit/lambda_as_field.cpp @@ -1,4 +1,6 @@ // ADDITIONAL_COMPILE_FLAGS: -std=c++23 +// no-compile +// Blocked by https://github.com/Cpp2Rust/cpp2rust/issues/435 #include #include diff --git a/tests/unit/lambda_decltype_type.cpp b/tests/unit/lambda_decltype_type.cpp index 45b03f791..05b946a6d 100644 --- a/tests/unit/lambda_decltype_type.cpp +++ b/tests/unit/lambda_decltype_type.cpp @@ -1,4 +1,6 @@ // ADDITIONAL_COMPILE_FLAGS: -std=c++23 +// no-compile +// Blocked by https://github.com/Cpp2Rust/cpp2rust/issues/435 #include template struct Sorted { From fa5a4633aba594b38a30ff020a7107585fb5a52e Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 14 Oct 2026 10:36:08 +0100 Subject: [PATCH 5/9] Translate lambda TPOs --- cpp2rust/converter/converter.cpp | 7 ++ tests/unit/out/refcount/lambda_to_fn_ptr.rs | 81 ++++++++++++++++++ tests/unit/out/unsafe/lambda_to_fn_ptr.rs | 92 +++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 tests/unit/out/refcount/lambda_to_fn_ptr.rs create mode 100644 tests/unit/out/unsafe/lambda_to_fn_ptr.rs diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index aa13f6639..24526398d 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -3232,6 +3232,13 @@ std::string Converter::ConvertDeclRef(clang::Expr *expr, } bool Converter::VisitDeclRefExpr(clang::DeclRefExpr *expr) { + if (auto param = + clang::dyn_cast(expr->getDecl()); + param && AsLambdaClass(param->getType())) { + StrCat(GetDefaultAsString(param->getType())); + computed_expr_type_ = ComputedExprType::FreshValue; + return false; + } if (auto init = AsLambdaUncapturedConstant(curr_function_, expr)) { Convert(init); return false; diff --git a/tests/unit/out/refcount/lambda_to_fn_ptr.rs b/tests/unit/out/refcount/lambda_to_fn_ptr.rs new file mode 100644 index 000000000..1f0439974 --- /dev/null +++ b/tests/unit/out/refcount/lambda_to_fn_ptr.rs @@ -0,0 +1,81 @@ +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 apply_0(mut x: i32, mut fn_: FnPtr i32>) -> i32 { + return ({ fn_.call(x) }); +} +pub fn call_nttp_1(mut x: i32) -> i32 { + return ({ (FnPtr:: i32>::new(int_operator___int__const::__invoke))(x) }); +} +pub fn call_nttp_1(mut x: i32) -> i32 { + return ({ (FnPtr:: i32>::new(int_operator___int__const::__invoke))(x) }); +} +pub fn call_nttp_2(mut x: i32) -> i32 { + return ({ + FnPtr:: i32>::new(|mut x: i32| -> i32 { + { + return (x + 100); + } + }) + .call(x) + }); +} +pub fn main() { + __cpp2rust_init_globals(); + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let mut fresh: FnPtr i32> = FnPtr:: i32>::new(|mut x: i32| -> i32 { + { + return -x; + } + }); + assert!((({ fresh.call(5,) }) == -5_i32)); + let twice: Value i32>> = Rc::new(RefCell::new(FnPtr:: i32>::new( + |mut x: i32| -> i32 { + { + return (x * 2); + } + }, + ))); + let mut named: FnPtr i32> = (*twice.borrow()).clone(); + assert!((({ named.call(5,) }) == 10)); + assert!((({ apply_0(5, (*twice.borrow()).clone(),) }) == 10)); + named = (fresh).clone(); + assert!((({ named.call(3,) }) == -3_i32)); + let mut p: FnPtr i32> = FnPtr:: i32>::new(|mut x: i32| -> i32 { + { + return (x * 3); + } + }); + assert!((({ p.call(2,) }) == 6)); + assert!((({ call_nttp_1(5,) }) == 4)); + assert!((({ call_nttp_1(5,) }) == 50)); + assert!((({ call_nttp_2(5,) }) == 105)); + let sum: Value i32>> = Rc::new(RefCell::new(FnPtr:: i32>::new( + |n: i32| -> i32 { + { + let __args: Value> = Rc::new(RefCell::new(__args.into())); + let n: Value = Rc::new(RefCell::new(n)); + let ap: Value> = Rc::new(RefCell::new(Ptr::::default())); + (*ap.borrow_mut()) = __args.as_pointer(); + let s: Value = Rc::new(RefCell::new(0)); + let mut i: i32 = 0; + 'loop_: while (i < (*n.borrow())) { + (*s.borrow_mut()) += (*ap.borrow_mut()).arg::(); + i.postfix_inc(); + } + return (*s.borrow()); + } + }, + ))); + assert!((({ (*sum.borrow()).call(2, &[(5).into(), (5).into(),]) }) == 10)); + assert!((({ (*sum.borrow()).call(3, &[(1).into(), (2).into(), (3).into(),]) }) == 6)); + return 0; +} +pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/unsafe/lambda_to_fn_ptr.rs b/tests/unit/out/unsafe/lambda_to_fn_ptr.rs new file mode 100644 index 000000000..edf8472e8 --- /dev/null +++ b/tests/unit/out/unsafe/lambda_to_fn_ptr.rs @@ -0,0 +1,92 @@ +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 apply_0(mut x: i32, mut fn_: Option i32>) -> i32 { + return (unsafe { (fn_).unwrap()(x) }); +} +pub unsafe fn call_nttp_1(mut x: i32) -> i32 { + return (unsafe { (Some(int_operator___int__const::__invoke))(x) }); +} +pub unsafe fn call_nttp_1(mut x: i32) -> i32 { + return (unsafe { (Some(int_operator___int__const::__invoke))(x) }); +} +pub unsafe fn call_nttp_2(mut x: i32) -> i32 { + return (unsafe { + FnPtr:: i32>::new(|mut x: i32| -> i32 { + unsafe { + return ((x) + (100)); + } + }) + .call(x) + }); +} +pub fn main() { + unsafe { + __cpp2rust_init_globals(); + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut fresh: Option i32> = Some(|mut x: i32| -> i32 { + unsafe { + return -x; + } + }); + assert!(((unsafe { (fresh).unwrap()(5,) }) == (-5_i32))); + let mut twice: FnPtr i32> = FnPtr:: i32>::new(|mut x: i32| -> i32 { + unsafe { + return ((x) * (2)); + } + }); + let mut named: Option i32> = Some(|mut x: i32| -> i32 { + unsafe { + return ((x) * (2)); + } + }); + assert!(((unsafe { (named).unwrap()(5,) }) == (10))); + assert!( + ((unsafe { + apply_0( + 5, + Some(|mut x: i32| -> i32 { + unsafe { + return ((x) * (2)); + } + }), + ) + }) == (10)) + ); + named = fresh; + assert!(((unsafe { (named).unwrap()(3,) }) == (-3_i32))); + let mut p: Option i32> = Some(|mut x: i32| -> i32 { + unsafe { + return ((x) * (3)); + } + }); + assert!(((unsafe { (p).unwrap()(2,) }) == (6))); + assert!(((unsafe { call_nttp_1(5,) }) == (4))); + assert!(((unsafe { call_nttp_1(5,) }) == (50))); + assert!(((unsafe { call_nttp_2(5,) }) == (105))); + let mut sum: FnPtr i32> = FnPtr:: i32>::new(|mut n: i32| -> i32 { + unsafe { + let mut ap: *const VaArg = ::std::ptr::null(); + ap = __args.as_ptr(); + let mut s: i32 = 0; + let mut i: i32 = 0; + 'loop_: while ((i) < (n)) { + s += ap.arg::(); + i.postfix_inc(); + } + return s; + } + }); + assert!(((unsafe { sum.call(2, &[(5).into(), (5).into(),]) }) == (10))); + assert!(((unsafe { sum.call(3, &[(1).into(), (2).into(), (3).into(),]) }) == (6))); + return 0; +} +pub unsafe fn __cpp2rust_init_globals() {} From b05d9e2da1e6fbe58c61cb48a140e6aa1dd4675e Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 14 Oct 2026 10:57:17 +0100 Subject: [PATCH 6/9] Use same ASTNameGenerator for the duratoin of the Converter --- cpp2rust/converter/converter.h | 16 ++++++++++++++-- cpp2rust/converter/converter_lib.cpp | 21 ++++++++++++++++----- cpp2rust/converter/converter_lib.h | 5 +++++ tests/unit/out/refcount/lambda_to_fn_ptr.rs | 8 ++++---- tests/unit/out/unsafe/lambda_to_fn_ptr.rs | 8 ++++---- 5 files changed, 43 insertions(+), 15 deletions(-) diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index bd3773230..c699019c2 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -4,11 +4,13 @@ // Distributed under the MIT license that can be found in the LICENSE file. #include +#include #include #include #include #include +#include #include #include #include @@ -32,9 +34,18 @@ class Converter : public clang::RecursiveASTVisitor { const char *keyword_unsafe = "unsafe", const char *keyword_mut = keyword::kMut) : rs_code_(&rs_code), ctx_(ctx), keyword_unsafe_(keyword_unsafe), - keyword_mut_(keyword_mut) {} + keyword_mut_(keyword_mut) { + if (!GetNameGenerator()) { + name_generator_ = std::make_unique(ctx); + SetNameGenerator(name_generator_.get()); + } + } - virtual ~Converter() = default; + virtual ~Converter() { + if (name_generator_) { + SetNameGenerator(nullptr); + } + } Converter(const Converter &) = delete; Converter &operator=(const Converter &) = delete; @@ -784,6 +795,7 @@ class Converter : public clang::RecursiveASTVisitor { std::string *rs_code_; clang::ASTContext &ctx_; + std::unique_ptr name_generator_; clang::FunctionDecl *curr_function_ = nullptr; bool in_function_formals_ = false; enum class MethodTarget : uint8_t { diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 01041b29a..f13c0e320 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -840,12 +840,25 @@ static std::string GetParamSignature(const clang::Decl *decl) { return args; } +static clang::ASTNameGenerator *name_generator = nullptr; + +void SetNameGenerator(clang::ASTNameGenerator *generator) { + name_generator = generator; +} + +clang::ASTNameGenerator *GetNameGenerator() { return name_generator; } + +static std::string GetMangledName(const clang::Decl *decl) { + assert(name_generator); + return name_generator->getName(decl); +} + static std::string GetLexicalSpecializationID(const clang::Decl *decl) { auto &ctx = decl->getASTContext(); std::string id; if (const auto *var = clang::dyn_cast(decl)) { - id += clang::ASTNameGenerator(var->getASTContext()).getName(var); + id += GetMangledName(var); } if (const auto *self = clang::dyn_cast(decl)) { @@ -864,7 +877,7 @@ static std::string GetLexicalSpecializationID(const clang::Decl *decl) { } if (const auto *fn = clang::dyn_cast(dc); fn && fn->getTemplateSpecializationArgs()) { - id += clang::ASTNameGenerator(fn->getASTContext()).getName(fn); + id += GetMangledName(fn); } } return id; @@ -937,9 +950,7 @@ std::string DisambiguateAnonymousTag(const clang::TagDecl *tag) { static std::unordered_map type_mapping; static size_t GetDeclId(const clang::NamedDecl *decl, bool internal) { - std::string key = - clang::ASTNameGenerator(decl->getASTContext()).getName(decl) + - GetParamSignature(decl); + std::string key = GetMangledName(decl) + GetParamSignature(decl); if (internal) { key += GetLocationID(decl); } diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index 384455d50..fac0eebce 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -181,6 +182,10 @@ unsigned GetColumnNumber(const clang::Decl *decl); unsigned GetArraySize(clang::QualType array_type); +void SetNameGenerator(clang::ASTNameGenerator *generator); + +clang::ASTNameGenerator *GetNameGenerator(); + std::string GetID(const clang::Decl *decl); std::string GetMethodID(const clang::CXXMethodDecl *decl); diff --git a/tests/unit/out/refcount/lambda_to_fn_ptr.rs b/tests/unit/out/refcount/lambda_to_fn_ptr.rs index 1f0439974..2466af0c9 100644 --- a/tests/unit/out/refcount/lambda_to_fn_ptr.rs +++ b/tests/unit/out/refcount/lambda_to_fn_ptr.rs @@ -12,10 +12,10 @@ pub fn apply_0(mut x: i32, mut fn_: FnPtr i32>) -> i32 { pub fn call_nttp_1(mut x: i32) -> i32 { return ({ (FnPtr:: i32>::new(int_operator___int__const::__invoke))(x) }); } -pub fn call_nttp_1(mut x: i32) -> i32 { +pub fn call_nttp_2(mut x: i32) -> i32 { return ({ (FnPtr:: i32>::new(int_operator___int__const::__invoke))(x) }); } -pub fn call_nttp_2(mut x: i32) -> i32 { +pub fn call_nttp_3(mut x: i32) -> i32 { return ({ FnPtr:: i32>::new(|mut x: i32| -> i32 { { @@ -55,8 +55,8 @@ fn main_0() -> i32 { }); assert!((({ p.call(2,) }) == 6)); assert!((({ call_nttp_1(5,) }) == 4)); - assert!((({ call_nttp_1(5,) }) == 50)); - assert!((({ call_nttp_2(5,) }) == 105)); + assert!((({ call_nttp_2(5,) }) == 50)); + assert!((({ call_nttp_3(5,) }) == 105)); let sum: Value i32>> = Rc::new(RefCell::new(FnPtr:: i32>::new( |n: i32| -> i32 { { diff --git a/tests/unit/out/unsafe/lambda_to_fn_ptr.rs b/tests/unit/out/unsafe/lambda_to_fn_ptr.rs index edf8472e8..4d6179611 100644 --- a/tests/unit/out/unsafe/lambda_to_fn_ptr.rs +++ b/tests/unit/out/unsafe/lambda_to_fn_ptr.rs @@ -12,10 +12,10 @@ pub unsafe fn apply_0(mut x: i32, mut fn_: Option i32>) -> i32 pub unsafe fn call_nttp_1(mut x: i32) -> i32 { return (unsafe { (Some(int_operator___int__const::__invoke))(x) }); } -pub unsafe fn call_nttp_1(mut x: i32) -> i32 { +pub unsafe fn call_nttp_2(mut x: i32) -> i32 { return (unsafe { (Some(int_operator___int__const::__invoke))(x) }); } -pub unsafe fn call_nttp_2(mut x: i32) -> i32 { +pub unsafe fn call_nttp_3(mut x: i32) -> i32 { return (unsafe { FnPtr:: i32>::new(|mut x: i32| -> i32 { unsafe { @@ -70,8 +70,8 @@ unsafe fn main_0() -> i32 { }); assert!(((unsafe { (p).unwrap()(2,) }) == (6))); assert!(((unsafe { call_nttp_1(5,) }) == (4))); - assert!(((unsafe { call_nttp_1(5,) }) == (50))); - assert!(((unsafe { call_nttp_2(5,) }) == (105))); + assert!(((unsafe { call_nttp_2(5,) }) == (50))); + assert!(((unsafe { call_nttp_3(5,) }) == (105))); let mut sum: FnPtr i32> = FnPtr:: i32>::new(|mut n: i32| -> i32 { unsafe { let mut ap: *const VaArg = ::std::ptr::null(); From b18f36d33c44707a91e00654cf857b6ce9ef7c5c Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 14 Oct 2026 11:04:12 +0100 Subject: [PATCH 7/9] Translate static invoke for lambdas --- cpp2rust/converter/converter.cpp | 17 ++++++++++++++++- .../converter/models/converter_refcount.cpp | 6 ++++++ tests/unit/out/refcount/lambda_to_fn_ptr.rs | 18 ++++++++++++++++-- tests/unit/out/unsafe/lambda_to_fn_ptr.rs | 18 ++++++++++++++++-- 4 files changed, 54 insertions(+), 5 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 24526398d..2ab7b0f3f 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -4,6 +4,7 @@ #include "converter/converter.h" #include +#include #include #include #include @@ -2004,6 +2005,16 @@ std::string Converter::GetFunctionRefName(const clang::FunctionDecl *fn_decl) { void Converter::ConvertFunctionToFunctionPointer( const clang::FunctionDecl *fn_decl) { + if (auto *method = clang::dyn_cast(fn_decl); + method && method->isLambdaStaticInvoker()) { + assert(!method->getParent()->isGenericLambda()); + StrCat("Some"); + PushParen paren(*this); + ConvertLambdaClosure(method->getParent(), + method->getParent()->getLambdaCallOperator()); + computed_expr_type_ = ComputedExprType::FreshPointer; + return; + } StrCat(std::format("Some({})", GetFunctionRefName(fn_decl))); computed_expr_type_ = ComputedExprType::FreshPointer; } @@ -2055,7 +2066,11 @@ Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) { unsigned num_named_params = function ? function->getNumParams() : proto->getNumParams(); info.is_variadic = function ? function->isVariadic() : proto->isVariadic(); - info.is_fn_ptr_call = !function || AsLambdaClass(callee->getType()); + info.is_fn_ptr_call = + !function || AsLambdaClass(callee->getType()) || + clang::isa( + clang::IgnoreExprNodes(callee, clang::IgnoreParensSingleStep, + clang::IgnoreImplicitCastsSingleStep)); info.is_libc_passthrough = RuleRegistry::IsLibcPassthrough(ctx_, expr); for (unsigned i = 0; i < num_named_params && i < num_args; ++i) { diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 5edffdeb3..396fc454b 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -1567,6 +1567,12 @@ void ConverterRefCount::ConvertLambdaToFunctionPointer( void ConverterRefCount::ConvertFunctionToFunctionPointer( const clang::FunctionDecl *fn_decl) { + if (auto *method = clang::dyn_cast(fn_decl); + method && method->isLambdaStaticInvoker()) { + ConvertCapturelessLambda(method->getParent()); + computed_expr_type_ = ComputedExprType::FreshPointer; + return; + } StrCat(std::format("FnPtr::<{}>::new({})", ConvertFunctionPointerType( fn_decl->getType()->getAs()), diff --git a/tests/unit/out/refcount/lambda_to_fn_ptr.rs b/tests/unit/out/refcount/lambda_to_fn_ptr.rs index 2466af0c9..9d97227ed 100644 --- a/tests/unit/out/refcount/lambda_to_fn_ptr.rs +++ b/tests/unit/out/refcount/lambda_to_fn_ptr.rs @@ -10,10 +10,24 @@ pub fn apply_0(mut x: i32, mut fn_: FnPtr i32>) -> i32 { return ({ fn_.call(x) }); } pub fn call_nttp_1(mut x: i32) -> i32 { - return ({ (FnPtr:: i32>::new(int_operator___int__const::__invoke))(x) }); + return ({ + (FnPtr:: i32>::new(|mut x: i32| -> i32 { + { + return (x - 1); + } + })) + .call(x) + }); } pub fn call_nttp_2(mut x: i32) -> i32 { - return ({ (FnPtr:: i32>::new(int_operator___int__const::__invoke))(x) }); + return ({ + (FnPtr:: i32>::new(|mut x: i32| -> i32 { + { + return (x * 10); + } + })) + .call(x) + }); } pub fn call_nttp_3(mut x: i32) -> i32 { return ({ diff --git a/tests/unit/out/unsafe/lambda_to_fn_ptr.rs b/tests/unit/out/unsafe/lambda_to_fn_ptr.rs index 4d6179611..5409b9447 100644 --- a/tests/unit/out/unsafe/lambda_to_fn_ptr.rs +++ b/tests/unit/out/unsafe/lambda_to_fn_ptr.rs @@ -10,10 +10,24 @@ pub unsafe fn apply_0(mut x: i32, mut fn_: Option i32>) -> i32 return (unsafe { (fn_).unwrap()(x) }); } pub unsafe fn call_nttp_1(mut x: i32) -> i32 { - return (unsafe { (Some(int_operator___int__const::__invoke))(x) }); + return (unsafe { + (Some(|mut x: i32| -> i32 { + unsafe { + return ((x) - (1)); + } + })) + .unwrap()(x) + }); } pub unsafe fn call_nttp_2(mut x: i32) -> i32 { - return (unsafe { (Some(int_operator___int__const::__invoke))(x) }); + return (unsafe { + (Some(|mut x: i32| -> i32 { + unsafe { + return ((x) * (10)); + } + })) + .unwrap()(x) + }); } pub unsafe fn call_nttp_3(mut x: i32) -> i32 { return (unsafe { From 728b9ae1fe165c2b2f3043a04d667407aee0d3cd Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 14 Oct 2026 11:12:09 +0100 Subject: [PATCH 8/9] Fix default lambdas with static locals --- cpp2rust/converter/converter.cpp | 7 +- cpp2rust/converter/converter_lib.cpp | 15 --- cpp2rust/converter/converter_lib.h | 2 - .../unit/out/refcount/lambda_static_local.rs | 91 +++++++++++++++++++ tests/unit/out/unsafe/lambda_static_local.rs | 78 ++++++++++++++++ 5 files changed, 172 insertions(+), 21 deletions(-) create mode 100644 tests/unit/out/refcount/lambda_static_local.rs create mode 100644 tests/unit/out/unsafe/lambda_static_local.rs diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 2ab7b0f3f..8536cb8d8 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -515,6 +515,9 @@ bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) { ENSURE(decl_ids_.insert(GetID(decl)).second); global_inits_.push_back(ForceGlobalInit(decl)); } else if (decl->isStaticLocal()) { + if (!globals_.insert(name).second) { + return false; + } StrCat(keyword::kStatic, keyword_mut_); } else if (decl->isLocalVarDecl()) { StrCat(keyword::kLet); @@ -4281,10 +4284,6 @@ std::string Converter::GetArrayDefaultAsString(clang::QualType qual_type) { std::string Converter::GetDefaultAsString(clang::QualType qual_type) { if (auto lambda = AsLambdaClass(qual_type); lambda && lambda->captures().empty()) { - if (HasStaticLocal(lambda->getLambdaCallOperator()->getBody())) { - llvm::report_fatal_error("default-constructed lambda with a static local " - "is not supported yet"); - } Buffer buf(*this); ConvertCapturelessLambda(lambda); return std::move(buf).str(); diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index f13c0e320..7af37ced6 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -1377,21 +1377,6 @@ clang::QualType GetDeclRefType(const clang::FunctionDecl *fn, return field ? field->getType() : decl->getType(); } -bool HasStaticLocal(const clang::Stmt *stmt) { - if (!stmt) { - return false; - } - if (auto decl_stmt = clang::dyn_cast(stmt)) { - for (auto decl : decl_stmt->decls()) { - if (auto var = clang::dyn_cast(decl); - var && var->isStaticLocal()) { - return true; - } - } - } - return llvm::any_of(stmt->children(), HasStaticLocal); -} - bool IsUserOperatorCall(const clang::CXXOperatorCallExpr *expr) { const auto *callee = expr->getDirectCallee(); if (!callee || AsLambdaOperatorCall(callee)) { diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index fac0eebce..12741d570 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -252,8 +252,6 @@ clang::QualType GetDeclRefType(const clang::FunctionDecl *fn, const clang::Expr *expr, const clang::ValueDecl *decl); -bool HasStaticLocal(const clang::Stmt *stmt); - bool IsSameTypeComparison(const clang::FunctionDecl *fn, const clang::CXXRecordDecl *record); diff --git a/tests/unit/out/refcount/lambda_static_local.rs b/tests/unit/out/refcount/lambda_static_local.rs new file mode 100644 index 000000000..d6e6856d5 --- /dev/null +++ b/tests/unit/out/refcount/lambda_static_local.rs @@ -0,0 +1,91 @@ +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}; +thread_local!( + pub static a_0: Value = Rc::new(RefCell::new(0_i32)); +); +pub fn main() { + __cpp2rust_init_globals(); + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let next: Value i32>> = + Rc::new(RefCell::new(FnPtr:: i32>::new(|| -> i32 { + { + return (*n_1.with(Value::clone).borrow_mut()).prefix_inc(); + } + }))); + assert!((({ (*next.borrow()).call() }) == 1)); + assert!((({ (*next.borrow()).call() }) == 2)); + let copy: Value i32>> = Rc::new(RefCell::new((*next.borrow()).copy_from())); + assert!((({ (*copy.borrow()).call() }) == 3)); + assert!((({ (*next.borrow()).call() }) == 4)); + let fresh: Value i32>> = + Rc::new(RefCell::new(FnPtr:: i32>::new(|| -> i32 { + { + return (*n_1.with(Value::clone).borrow_mut()).prefix_inc(); + } + }))); + assert!((({ (*fresh.borrow()).call() }) == 5)); + assert!((({ (*next.borrow()).call() }) == 6)); + let per_type: Value> = Rc::new(RefCell::new(lambda!( + Generic, + {}, + |mut x: i32| -> i32 { + (*calls_2.with(Value::clone).borrow_mut()).postfix_inc(); + return calls_2.with(|rc| *rc.borrow()); + }, + |mut x: f64| -> i32 { + (*calls_3.with(Value::clone).borrow_mut()).postfix_inc(); + return calls_3.with(|rc| *rc.borrow()); + } + ))); + assert!((({ (*per_type.borrow()).spec:: i32>(0).call(1,) }) == 1)); + assert!((({ (*per_type.borrow()).spec:: i32>(0).call(2,) }) == 2)); + assert!((({ (*per_type.borrow()).spec:: i32>(1).call(1_f64,) }) == 1)); + assert!((({ (*per_type.borrow()).spec:: i32>(0).call(3,) }) == 3)); + let read: Value i32>> = Rc::new(RefCell::new(FnPtr:: i32>::new( + |mut _a0: i32| -> i32 { + { + return a_0.with(|rc| *rc.borrow()); + } + }, + ))); + assert!((({ (*read.borrow()).call(0,) }) == 0)); + a_0.with(|rc| *rc.borrow_mut() = 1); + assert!((({ (*read.borrow()).call(0,) }) == 1)); + let local: Value i32>> = + Rc::new(RefCell::new(FnPtr:: i32>::new(|| -> i32 { + { + let mut p: P_4 = P_4 { x: 2, y: 3 }; + return (p.x * p.y); + } + }))); + assert!((({ (*local.borrow()).call() }) == 6)); + return 0; +} +thread_local!( + static n_1: Value = Rc::new(RefCell::new(0)); +); +thread_local!( + static calls_2: Value = Rc::new(RefCell::new(0)); +); +thread_local!( + static calls_3: Value = Rc::new(RefCell::new(0)); +); +#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg, Default)] +#[byte_size(8)] +pub struct P_4 { + #[offset(0)] + pub x: i32, + #[offset(4)] + pub y: i32, +} +pub fn __cpp2rust_init_globals() { + let _ = a_0.with(|_| ()); +} diff --git a/tests/unit/out/unsafe/lambda_static_local.rs b/tests/unit/out/unsafe/lambda_static_local.rs new file mode 100644 index 000000000..71753b4e0 --- /dev/null +++ b/tests/unit/out/unsafe/lambda_static_local.rs @@ -0,0 +1,78 @@ +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 static mut a_0: std::cell::LazyCell = std::cell::LazyCell::new(|| unsafe { 0_i32 }); +pub fn main() { + unsafe { + __cpp2rust_init_globals(); + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut next: FnPtr i32> = FnPtr:: i32>::new(|| -> i32 { + unsafe { + return (*std::cell::LazyCell::force_mut(&mut *&raw mut n_1)).prefix_inc(); + } + }); + assert!(((unsafe { next.call() }) == (1))); + assert!(((unsafe { next.call() }) == (2))); + let mut copy: FnPtr i32> = next.copy_from(); + assert!(((unsafe { copy.call() }) == (3))); + assert!(((unsafe { next.call() }) == (4))); + let mut fresh: FnPtr i32> = FnPtr:: i32>::new(|| -> i32 { + unsafe { + return (*std::cell::LazyCell::force_mut(&mut *&raw mut n_1)).prefix_inc(); + } + }); + assert!(((unsafe { fresh.call() }) == (5))); + assert!(((unsafe { next.call() }) == (6))); + let mut per_type: FnPtr = lambda_unsafe!( + Generic, + {}, + |mut x: i32| -> i32 { + (*std::cell::LazyCell::force_mut(&mut *&raw mut calls_2)).postfix_inc(); + return (*std::cell::LazyCell::force_mut(&mut *&raw mut calls_2)); + }, + |mut x: f64| -> i32 { + (*std::cell::LazyCell::force_mut(&mut *&raw mut calls_3)).postfix_inc(); + return (*std::cell::LazyCell::force_mut(&mut *&raw mut calls_3)); + } + ); + assert!(((unsafe { per_type.spec:: i32>(0).call(1,) }) == (1))); + assert!(((unsafe { per_type.spec:: i32>(0).call(2,) }) == (2))); + assert!(((unsafe { per_type.spec:: i32>(1).call(1_f64,) }) == (1))); + assert!(((unsafe { per_type.spec:: i32>(0).call(3,) }) == (3))); + let mut read: FnPtr i32> = FnPtr:: i32>::new(|mut _a0: i32| -> i32 { + unsafe { + return (*std::cell::LazyCell::force_mut(&mut *&raw mut a_0)); + } + }); + assert!(((unsafe { read.call(0,) }) == (0))); + (*std::cell::LazyCell::force_mut(&mut *&raw mut a_0)) = 1; + assert!(((unsafe { read.call(0,) }) == (1))); + let mut local: FnPtr i32> = FnPtr:: i32>::new(|| -> i32 { + unsafe { + let mut p: P_4 = P_4 { x: 2, y: 3 }; + return ((p.x) * (p.y)); + } + }); + assert!(((unsafe { local.call() }) == (6))); + return 0; +} +static mut n_1: std::cell::LazyCell = std::cell::LazyCell::new(|| unsafe { 0 }); +static mut calls_2: std::cell::LazyCell = std::cell::LazyCell::new(|| unsafe { 0 }); +static mut calls_3: std::cell::LazyCell = std::cell::LazyCell::new(|| unsafe { 0 }); +#[repr(C)] +#[derive(Copy, Clone, VaArg, FnPtrArg, Default)] +pub struct P_4 { + pub x: i32, + pub y: i32, +} +pub unsafe fn __cpp2rust_init_globals() { + std::cell::LazyCell::force(&*&raw const a_0); +} From 86bf45d5fcf32d00bf27883f64fd8d010305cb2f Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 14 Oct 2026 12:40:57 +0100 Subject: [PATCH 9/9] Update tests --- tests/unit/lambda_immediate_init.cpp | 2 +- tests/unit/lambda_to_fn_ptr.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/lambda_immediate_init.cpp b/tests/unit/lambda_immediate_init.cpp index c02c2b11c..f2f67a651 100644 --- a/tests/unit/lambda_immediate_init.cpp +++ b/tests/unit/lambda_immediate_init.cpp @@ -5,7 +5,7 @@ int pick(int x = [] { return 237; }()) { return x; } struct S { int j = 10; // Below will only work after: https://github.com/Cpp2Rust/cpp2rust/issues/248 - //int i = [this]() { return j * 2; }(); + // int i = [this]() { return j * 2; }(); int k = [] { return 3; }() + 1; }; diff --git a/tests/unit/lambda_to_fn_ptr.cpp b/tests/unit/lambda_to_fn_ptr.cpp index 4f305cabb..a82b1aeff 100644 --- a/tests/unit/lambda_to_fn_ptr.cpp +++ b/tests/unit/lambda_to_fn_ptr.cpp @@ -1,4 +1,5 @@ // ADDITIONAL_COMPILE_FLAGS: -std=c++23 +// no-compile #include #include