From 30df58c57da3fb82e6332dabc3957f1a6a1f1f22 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 21 Oct 2026 21:09:08 +0100 Subject: [PATCH 1/6] Delete implicit members whose definition does not compile --- cpp2rust/converter/converter_lib.cpp | 27 ++++++++++++++++++++++----- tests/unit/copy_move_deleted.cpp | 9 +++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 00ef3ca26..067a33940 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -1636,27 +1636,42 @@ void DefineImplicitMembers(clang::Sema &sema, clang::CXXRecordDecl *decl) { tu_scope.setEntity(sema.Context.getTranslationUnitDecl()); auto *saved_tu_scope = std::exchange(sema.TUScope, &tu_scope); sema.ForceDeclarationOfImplicitMembers(decl); + auto define = [&](clang::FunctionDecl *fn, auto &&definer) { + RuleDecls::Trial trial(sema); + definer(); + if (trial.failed()) { + fn->setDeletedAsWritten(true); + } + }; for (auto ctor : decl->ctors()) { if (ctor->isCopyConstructor() && ctor->isImplicit() && !ctor->doesThisDeclarationHaveABody() && !ctor->isDeleted()) { - sema.DefineImplicitCopyConstructor(decl->getLocation(), ctor); + define(ctor, [&] { + sema.DefineImplicitCopyConstructor(decl->getLocation(), ctor); + }); } if (ctor->isMoveConstructor() && !ctor->isUserProvided() && !ctor->doesThisDeclarationHaveABody() && !ctor->isDeleted() && !HasDefaultedCopyConstructor(decl)) { - sema.DefineImplicitMoveConstructor(decl->getLocation(), ctor); + define(ctor, [&] { + sema.DefineImplicitMoveConstructor(decl->getLocation(), ctor); + }); } } for (auto *method : decl->methods()) { if (method->isMoveAssignmentOperator() && !method->isUserProvided() && !method->doesThisDeclarationHaveABody() && !method->isDeleted() && !HasDefaultedCopyAssignment(decl)) { - sema.DefineImplicitMoveAssignment(decl->getLocation(), method); + define(method, [&] { + sema.DefineImplicitMoveAssignment(decl->getLocation(), method); + }); } if (method->isCopyAssignmentOperator() && !method->isUserProvided() && !method->doesThisDeclarationHaveABody() && !method->isDeleted() && NeedsDefaultedCopyAssignment(decl)) { - sema.DefineImplicitCopyAssignment(decl->getLocation(), method); + define(method, [&] { + sema.DefineImplicitCopyAssignment(decl->getLocation(), method); + }); } } auto define_defaulted_comparison = [&](clang::FunctionDecl *fn) { @@ -1669,7 +1684,9 @@ void DefineImplicitMembers(clang::Sema &sema, clang::CXXRecordDecl *decl) { #else auto kind = sema.getDefaultedComparisonKind(fn); #endif - sema.DefineDefaultedComparison(decl->getLocation(), fn, kind); + define(fn, [&] { + sema.DefineDefaultedComparison(decl->getLocation(), fn, kind); + }); }; for (auto *method : decl->methods()) { define_defaulted_comparison(method); diff --git a/tests/unit/copy_move_deleted.cpp b/tests/unit/copy_move_deleted.cpp index 4f7eabed1..ee5327210 100644 --- a/tests/unit/copy_move_deleted.cpp +++ b/tests/unit/copy_move_deleted.cpp @@ -1,5 +1,6 @@ #include #include +#include struct NoCopy { int v; @@ -43,6 +44,10 @@ struct Container { int tag; }; +struct VecHolder { + std::vector items; +}; + static void bump(NoCopy *p) { p->v++; } static void bump_ref(Immovable &r) { r.v++; } @@ -71,5 +76,9 @@ int main() { Container c{NoCopy(6), 7}; Container d(std::move(c)); assert(d.inner.v == 6 && d.tag == 7 && c.inner.v == 0); + + VecHolder h; + VecHolder h2(std::move(h)); + assert(h2.items.size() == 0); return 0; } From 38cea204d4b0b2ccc5f02f115e5b433c86624bab Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 21 Oct 2026 21:31:19 +0100 Subject: [PATCH 2/6] Instantiate what implicit members use inside their trial --- cpp2rust/converter/converter_lib.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 067a33940..b3190464b 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -1638,7 +1638,12 @@ void DefineImplicitMembers(clang::Sema &sema, clang::CXXRecordDecl *decl) { sema.ForceDeclarationOfImplicitMembers(decl); auto define = [&](clang::FunctionDecl *fn, auto &&definer) { RuleDecls::Trial trial(sema); - definer(); + { + clang::Sema::GlobalEagerInstantiationScope instantiations( + sema, /*Enabled=*/true, /*AtEndOfTU=*/false); + definer(); + instantiations.perform(); + } if (trial.failed()) { fn->setDeletedAsWritten(true); } From 1aeb113fa8f4fe212a7446c8de4e0f11d1d88649 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 21 Oct 2026 21:35:03 +0100 Subject: [PATCH 3/6] Do not clone records whose implicit copy constructor was deleted --- cpp2rust/converter/converter_lib.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index b3190464b..123dc424c 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -692,7 +692,10 @@ bool HasCallableCopyConstructor(const clang::RecordDecl *decl) { ctor->getDefinition(); }); } - if (cxx->defaultedCopyConstructorIsDeleted()) { + if (cxx->defaultedCopyConstructorIsDeleted() || + std::ranges::any_of(cxx->ctors(), [](const auto *ctor) { + return ctor->isCopyConstructor() && ctor->isDeleted(); + })) { return false; } auto &ctx = cxx->getASTContext(); From 4f4ecc62fe37640caab8771104a3fcd676a7369e Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 21 Oct 2026 21:47:55 +0100 Subject: [PATCH 4/6] Catch errors in instantiations the implicit members need --- cpp2rust/converter/converter_lib.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 123dc424c..0c8f29670 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -1641,13 +1641,14 @@ void DefineImplicitMembers(clang::Sema &sema, clang::CXXRecordDecl *decl) { sema.ForceDeclarationOfImplicitMembers(decl); auto define = [&](clang::FunctionDecl *fn, auto &&definer) { RuleDecls::Trial trial(sema); + clang::DiagnosticErrorTrap errors(sema.getDiagnostics()); { clang::Sema::GlobalEagerInstantiationScope instantiations( sema, /*Enabled=*/true, /*AtEndOfTU=*/false); definer(); instantiations.perform(); } - if (trial.failed()) { + if (trial.failed() || errors.hasErrorOccurred()) { fn->setDeletedAsWritten(true); } }; From 4fc5607531157f114907eb70c6cdeef3f8782baf Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 21 Oct 2026 21:59:59 +0100 Subject: [PATCH 5/6] Update tests --- tests/unit/out/refcount/copy_move_deleted.rs | 17 +++++++++++++++++ tests/unit/out/unsafe/copy_move_deleted.rs | 16 ++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/tests/unit/out/refcount/copy_move_deleted.rs b/tests/unit/out/refcount/copy_move_deleted.rs index 10d02ffcf..7ba64f151 100644 --- a/tests/unit/out/refcount/copy_move_deleted.rs +++ b/tests/unit/out/refcount/copy_move_deleted.rs @@ -80,6 +80,20 @@ impl Container { } } } +#[derive(Record, ByteRepr, FnPtrArg, MoveCtor, Default)] +#[byte_size(24)] +pub struct VecHolder { + #[offset(0)] + #[byte_size(24)] + pub items: Vec, +} +impl VecHolder { + pub fn move_from(_a0: Ptr) -> Self { + Self { + items: field!(_a0, items).with_mut(std::mem::take), + } + } +} pub fn bump_0(mut p: Ptr) { field!(p, v).with_mut(|__v| __v.postfix_inc()); } @@ -115,6 +129,9 @@ fn main_0() -> i32 { })); let mut d: Container = Container::move_from({ c.as_pointer() }); assert!(((d.inner.v == 6) && (d.tag == 7)) && ({ (*c.borrow()).inner.v } == 0)); + let h: Value = Rc::new(RefCell::new(::default())); + let mut h2: VecHolder = VecHolder::move_from({ h.as_pointer() }); + assert!((h2.items.len() == 0_usize)); return 0; } pub trait ContainerImpl { diff --git a/tests/unit/out/unsafe/copy_move_deleted.rs b/tests/unit/out/unsafe/copy_move_deleted.rs index 8387b6370..1be051764 100644 --- a/tests/unit/out/unsafe/copy_move_deleted.rs +++ b/tests/unit/out/unsafe/copy_move_deleted.rs @@ -92,6 +92,19 @@ impl Container { return &mut (*(self as *mut Container)); } } +#[repr(C)] +#[derive(FnPtrArg, MoveCtorUnsafe, Default)] +pub struct VecHolder { + pub items: Vec, +} +impl VecHolder { + pub unsafe fn move_from(_a0: *mut VecHolder) -> Self { + let mut this = Self { + items: std::mem::take(&mut (*_a0).items), + }; + this + } +} pub unsafe fn bump_0(mut p: *mut NoCopy) { (*p).v.postfix_inc(); } @@ -129,6 +142,9 @@ unsafe fn main_0() -> i32 { }; let mut d: Container = Container::move_from({ &mut c }); assert!((((d.inner.v) == (6)) && ((d.tag) == (7))) && ((c.inner.v) == (0))); + let mut h: VecHolder = ::default(); + let mut h2: VecHolder = VecHolder::move_from({ &mut h }); + assert!(((h2.items.len()) == (0_usize))); return 0; } pub unsafe fn __cpp2rust_init_globals() {} From 774a4164ed4e46e8136496a9cc566e37adb6e7de Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 21 Oct 2026 22:32:46 +0100 Subject: [PATCH 6/6] Trigger CI