From ed68c5800e46859316b2cb9c564a6aabc7df1ca6 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 9 Oct 2026 15:33:57 +0100 Subject: [PATCH 1/2] In unsafe, const variables that need destruction need to be mut --- cpp2rust/converter/converter.cpp | 21 +++++++++++++++------ cpp2rust/converter/converter.h | 2 ++ tests/unit/destructor.cpp | 7 +++++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index b63c7fcb3..9be57f5de 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -480,7 +480,7 @@ bool Converter::NeedsMut(const clang::VarDecl *decl, clang::QualType type, auto *method_or_null = curr_function_ ? clang::dyn_cast(curr_function_) : nullptr; - return ((hoisted_decls_.contains(decl) || + return ((hoisted_decls_.contains(decl) || NeedsScopedDestructor(decl) || (!type.isConstQualified() && !type->isReferenceType())) && ((method_or_null == nullptr) || !method_or_null->isVirtual()) && !IsGlobalVar(decl) && name != "_"); @@ -627,8 +627,21 @@ bool Converter::VisitVarDecl(clang::VarDecl *decl) { return false; } -void Converter::EmitScopedDestructor(const clang::VarDecl *decl) { +bool Converter::NeedsScopedDestructor(const clang::VarDecl *decl) const { if (in_function_formals_ || !decl->isLocalVarDecl() || IsGlobalVar(decl)) { + return false; + } + auto type = decl->getType(); + if (auto lambda = AsLambdaClass(type); + lambda && LambdaNeedsDestruction(lambda)) { + return true; + } + return !type->isReferenceType() && !type->isArrayType() && + TypeNeedsDestruction(type); +} + +void Converter::EmitScopedDestructor(const clang::VarDecl *decl) { + if (!NeedsScopedDestructor(decl)) { return; } auto type = decl->getType(); @@ -640,10 +653,6 @@ void Converter::EmitScopedDestructor(const clang::VarDecl *decl) { GetNamedDeclAsString(decl))); return; } - if (type->isReferenceType() || type->isArrayType() || - !TypeNeedsDestruction(type)) { - return; - } auto name = GetNamedDeclAsString(decl); StrCat(token::kSemiColon, std::format("let _dtor_{0} = ScopedDestructorUnsafe::new(&raw mut " diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 179207599..b8c17de2d 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -150,6 +150,8 @@ class Converter : public clang::RecursiveASTVisitor { virtual std::string DestroyMembers(const clang::CXXRecordDecl *decl); + bool NeedsScopedDestructor(const clang::VarDecl *decl) const; + virtual void EmitScopedDestructor(const clang::VarDecl *decl); void EmitDeallocation(clang::CXXDeleteExpr *expr, diff --git a/tests/unit/destructor.cpp b/tests/unit/destructor.cpp index e1397ef48..dc08485fd 100644 --- a/tests/unit/destructor.cpp +++ b/tests/unit/destructor.cpp @@ -106,5 +106,12 @@ int main() { assert(order[1] == 2); assert(order[2] == 1); + int before = global; + { + const S cs; + (void)cs; + } + assert(global == before + 1); + return 0; } From 075b13a87106f4957f03981aed91af0d875a34d7 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 9 Oct 2026 19:13:22 +0100 Subject: [PATCH 2/2] Update tests --- tests/unit/out/refcount/destructor.rs | 7 +++++++ tests/unit/out/unsafe/destructor.rs | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/tests/unit/out/refcount/destructor.rs b/tests/unit/out/refcount/destructor.rs index 1eb4f7b62..cc7cbefb7 100644 --- a/tests/unit/out/refcount/destructor.rs +++ b/tests/unit/out/refcount/destructor.rs @@ -183,6 +183,13 @@ fn main_0() -> i32 { order_1.with(|rc| rc.borrow()[__idx]) }) == 1) ); + let mut before: i32 = global_0.with(|rc| *rc.borrow()); + { + let cs: Value = Rc::new(RefCell::new(::default())); + let _dtor_cs = ScopedDestructor::new(&cs, |__p| __p.destructor()); + &(*cs.borrow_mut()); + } + assert!((global_0.with(|rc| *rc.borrow()) == (before + 1))); return 0; } pub trait ArrayMemberImpl { diff --git a/tests/unit/out/unsafe/destructor.rs b/tests/unit/out/unsafe/destructor.rs index 960da03bb..a6eb6b3c9 100644 --- a/tests/unit/out/unsafe/destructor.rs +++ b/tests/unit/out/unsafe/destructor.rs @@ -198,6 +198,13 @@ unsafe fn main_0() -> i32 { assert!((((*std::cell::LazyCell::force_mut(&mut *&raw mut order_1))[(0) as usize]) == (3))); assert!((((*std::cell::LazyCell::force_mut(&mut *&raw mut order_1))[(1) as usize]) == (2))); assert!((((*std::cell::LazyCell::force_mut(&mut *&raw mut order_1))[(2) as usize]) == (1))); + let mut before: i32 = (*std::cell::LazyCell::force_mut(&mut *&raw mut global_0)); + { + let mut cs: S = ::default(); + let _dtor_cs = ScopedDestructorUnsafe::new(&raw mut cs, S::destructor); + &(cs); + } + assert!(((*std::cell::LazyCell::force_mut(&mut *&raw mut global_0)) == ((before) + (1)))); return 0; } pub unsafe fn __cpp2rust_init_globals() {