Skip to content

Commit 2b822f8

Browse files
committed
Add custom deleter support to UniquePtr
Add the possibility to use shared trivial types as deleters for objects managed by std::unique_ptr / UniquePtr.
1 parent 5ceca34 commit 2b822f8

File tree

17 files changed

+561
-118
lines changed

17 files changed

+561
-118
lines changed

book/src/binding/uniqueptr.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ the link for documentation of the Rust API.
88

99
### Restrictions:
1010

11-
Only `std::unique_ptr<T, std::default_delete<T>>` is currently supported. Custom
12-
deleters may be supported in the future.
11+
If a custom deleter is used, it needs to be a type that is
12+
[shared between C++ and Rust](../shared.md) so that the instance of UniquePtr can still
13+
be passed by value in Rust code.
1314

1415
UniquePtr\<T\> does not support T being an opaque Rust type. You should use a
1516
Box\<T\> (C++ [rust::Box\<T\>](box.md)) instead for transferring ownership of

gen/src/write.rs

Lines changed: 84 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -823,8 +823,7 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
823823
write_type(out, &arg.ty);
824824
write!(out, "::from_raw({})", arg.name.cxx);
825825
} else if let Type::UniquePtr(_) = &arg.ty {
826-
write_type(out, &arg.ty);
827-
write!(out, "({})", arg.name.cxx);
826+
write!(out, "::std::move(*{})", arg.name.cxx);
828827
} else if arg.ty == RustString {
829828
out.builtin.unsafe_bitcopy = true;
830829
write!(
@@ -846,7 +845,6 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
846845
write!(out, ")");
847846
match &efn.ret {
848847
Some(Type::RustBox(_)) => write!(out, ".into_raw()"),
849-
Some(Type::UniquePtr(_)) => write!(out, ".release()"),
850848
Some(Type::Str(_) | Type::SliceRef(_)) if !indirect_return => write!(out, ")"),
851849
_ => {}
852850
}
@@ -1092,7 +1090,6 @@ fn write_rust_function_shim_impl(
10921090
write!(out, "{}", arg.name.cxx);
10931091
match &arg.ty {
10941092
Type::RustBox(_) => write!(out, ".into_raw()"),
1095-
Type::UniquePtr(_) => write!(out, ".release()"),
10961093
ty if ty != RustString && out.types.needs_indirect_abi(ty) => write!(out, "$.value"),
10971094
_ => {}
10981095
}
@@ -1155,10 +1152,19 @@ fn indirect_return(sig: &Signature, types: &Types) -> bool {
11551152

11561153
fn write_indirect_return_type(out: &mut OutFile, ty: &Type) {
11571154
match ty {
1158-
Type::RustBox(ty) | Type::UniquePtr(ty) => {
1155+
Type::RustBox(ty) => {
11591156
write_type_space(out, &ty.inner);
11601157
write!(out, "*");
11611158
}
1159+
Type::UniquePtr(ty) => {
1160+
write!(out, "::std::unique_ptr<");
1161+
write_type(out, &ty.first);
1162+
if let Some(deleter) = &ty.second {
1163+
write!(out, ", ");
1164+
write_type(out, deleter);
1165+
}
1166+
write!(out, "> ");
1167+
}
11621168
Type::Ref(ty) => {
11631169
write_type_space(out, &ty.inner);
11641170
if !ty.mutable {
@@ -1181,7 +1187,7 @@ fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) {
11811187

11821188
fn write_extern_return_type_space(out: &mut OutFile, ty: &Option<Type>) {
11831189
match ty {
1184-
Some(Type::RustBox(ty) | Type::UniquePtr(ty)) => {
1190+
Some(Type::RustBox(ty)) => {
11851191
write_type_space(out, &ty.inner);
11861192
write!(out, "*");
11871193
}
@@ -1203,7 +1209,7 @@ fn write_extern_return_type_space(out: &mut OutFile, ty: &Option<Type>) {
12031209

12041210
fn write_extern_arg(out: &mut OutFile, arg: &Var) {
12051211
match &arg.ty {
1206-
Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) => {
1212+
Type::RustBox(ty) | Type::CxxVector(ty) => {
12071213
write_type_space(out, &ty.inner);
12081214
write!(out, "*");
12091215
}
@@ -1237,7 +1243,11 @@ fn write_type(out: &mut OutFile, ty: &Type) {
12371243
}
12381244
Type::UniquePtr(ptr) => {
12391245
write!(out, "::std::unique_ptr<");
1240-
write_type(out, &ptr.inner);
1246+
write_type(out, &ptr.first);
1247+
if let Some(deleter) = &ptr.second {
1248+
write!(out, ", ");
1249+
write_type(out, deleter);
1250+
}
12411251
write!(out, ">");
12421252
}
12431253
Type::SharedPtr(ptr) => {
@@ -1350,7 +1360,7 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) {
13501360

13511361
#[derive(Copy, Clone)]
13521362
enum UniquePtr<'a> {
1353-
Ident(&'a Ident),
1363+
Ident(&'a Ident, Option<&'a Ident>),
13541364
CxxVector(&'a Ident),
13551365
}
13561366

@@ -1367,7 +1377,7 @@ impl ToTypename for Ident {
13671377
impl<'a> ToTypename for UniquePtr<'a> {
13681378
fn to_typename(&self, types: &Types) -> String {
13691379
match self {
1370-
UniquePtr::Ident(ident) => ident.to_typename(types),
1380+
UniquePtr::Ident(ident, _) => ident.to_typename(types),
13711381
UniquePtr::CxxVector(element) => {
13721382
format!("::std::vector<{}>", element.to_typename(types))
13731383
}
@@ -1388,7 +1398,7 @@ impl ToMangled for Ident {
13881398
impl<'a> ToMangled for UniquePtr<'a> {
13891399
fn to_mangled(&self, types: &Types) -> Symbol {
13901400
match self {
1391-
UniquePtr::Ident(ident) => ident.to_mangled(types),
1401+
UniquePtr::Ident(ident, _) => ident.to_mangled(types),
13921402
UniquePtr::CxxVector(element) => {
13931403
symbol::join(&[&"std", &"vector", &element.to_mangled(types)])
13941404
}
@@ -1409,7 +1419,7 @@ fn write_generic_instantiations(out: &mut OutFile) {
14091419
match *impl_key {
14101420
ImplKey::RustBox(ident) => write_rust_box_extern(out, ident),
14111421
ImplKey::RustVec(ident) => write_rust_vec_extern(out, ident),
1412-
ImplKey::UniquePtr(ident) => write_unique_ptr(out, ident),
1422+
ImplKey::UniquePtr(ident, deleter) => write_unique_ptr(out, ident, deleter),
14131423
ImplKey::SharedPtr(ident) => write_shared_ptr(out, ident),
14141424
ImplKey::WeakPtr(ident) => write_weak_ptr(out, ident),
14151425
ImplKey::CxxVector(ident) => write_cxx_vector(out, ident),
@@ -1621,8 +1631,8 @@ fn write_rust_vec_impl(out: &mut OutFile, key: NamedImplKey) {
16211631
writeln!(out, "}}");
16221632
}
16231633

1624-
fn write_unique_ptr(out: &mut OutFile, key: NamedImplKey) {
1625-
let ty = UniquePtr::Ident(key.rust);
1634+
fn write_unique_ptr(out: &mut OutFile, key: NamedImplKey, deleter: Option<&Ident>) {
1635+
let ty = UniquePtr::Ident(key.rust, deleter);
16261636
write_unique_ptr_common(out, ty);
16271637
}
16281638

@@ -1632,18 +1642,34 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) {
16321642
out.include.utility = true;
16331643
let inner = ty.to_typename(out.types);
16341644
let instance = ty.to_mangled(out.types);
1645+
let (cxx_type_string, mangled_prefix, deleter_name) =
1646+
if let UniquePtr::Ident(_, Some(deleter)) = &ty {
1647+
let deleter_instance = deleter.to_mangled(out.types);
1648+
let deleter_inner = deleter.to_typename(out.types);
1649+
(
1650+
format!("::std::unique_ptr<{}, {}>", inner, &deleter_inner),
1651+
format!("cxxbridge1$unique_ptr${}${}$", instance, deleter_instance),
1652+
Some(deleter_inner),
1653+
)
1654+
} else {
1655+
(
1656+
format!("::std::unique_ptr<{}>", inner),
1657+
format!("cxxbridge1$unique_ptr${}$", instance),
1658+
None,
1659+
)
1660+
};
16351661

16361662
let can_construct_from_value = match ty {
16371663
// Some aliases are to opaque types; some are to trivial types. We can't
16381664
// know at code generation time, so we generate both C++ and Rust side
16391665
// bindings for a "new" method anyway. But the Rust code can't be called
16401666
// for Opaque types because the 'new' method is not implemented.
1641-
UniquePtr::Ident(ident) => out.types.is_maybe_trivial(ident),
1667+
UniquePtr::Ident(ident, _) => out.types.is_maybe_trivial(ident),
16421668
UniquePtr::CxxVector(_) => false,
16431669
};
16441670

16451671
let conditional_delete = match ty {
1646-
UniquePtr::Ident(ident) => {
1672+
UniquePtr::Ident(ident, _) => {
16471673
!out.types.structs.contains_key(ident) && !out.types.enums.contains_key(ident)
16481674
}
16491675
UniquePtr::CxxVector(_) => false,
@@ -1652,7 +1678,7 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) {
16521678
if conditional_delete {
16531679
out.builtin.is_complete = true;
16541680
let definition = match ty {
1655-
UniquePtr::Ident(ty) => &out.types.resolve(ty).name.cxx,
1681+
UniquePtr::Ident(ty, _) => &out.types.resolve(ty).name.cxx,
16561682
UniquePtr::CxxVector(_) => unreachable!(),
16571683
};
16581684
writeln!(
@@ -1661,22 +1687,36 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) {
16611687
inner, definition,
16621688
);
16631689
}
1664-
writeln!(
1665-
out,
1666-
"static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");",
1667-
inner,
1668-
);
1669-
writeln!(
1670-
out,
1671-
"static_assert(alignof(::std::unique_ptr<{}>) == alignof(void *), \"\");",
1672-
inner,
1673-
);
1690+
if let Some(deleter_name) = &deleter_name {
1691+
writeln!(
1692+
out,
1693+
"static_assert(sizeof(::std::unique_ptr<{inner}, {deleter_name}>) >= (sizeof(void *) + \
1694+
sizeof({deleter_name})), \"Implausible size of unique_ptr with deleter {deleter_name}\");",
1695+
);
1696+
out.include.type_traits = true;
1697+
writeln!(
1698+
out,
1699+
"static_assert(std::is_trivially_move_constructible<{deleter_name}>::value && std::is_trivially_destructible<{deleter_name}>::value, \
1700+
\"Only trivial types are supported as deleter for unique_ptr\");",
1701+
)
1702+
} else {
1703+
writeln!(
1704+
out,
1705+
"static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");",
1706+
inner,
1707+
);
1708+
writeln!(
1709+
out,
1710+
"static_assert(alignof(::std::unique_ptr<{}>) == alignof(void *), \"\");",
1711+
inner,
1712+
);
1713+
}
16741714

16751715
begin_function_definition(out);
16761716
writeln!(
16771717
out,
1678-
"void cxxbridge1$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{",
1679-
instance, inner,
1718+
"void {}null(::std::unique_ptr<{}> *ptr) noexcept {{",
1719+
mangled_prefix, inner,
16801720
);
16811721
writeln!(out, " ::new (ptr) ::std::unique_ptr<{}>();", inner);
16821722
writeln!(out, "}}");
@@ -1686,8 +1726,8 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) {
16861726
begin_function_definition(out);
16871727
writeln!(
16881728
out,
1689-
"{} *cxxbridge1$unique_ptr${}$uninit(::std::unique_ptr<{}> *ptr) noexcept {{",
1690-
inner, instance, inner,
1729+
"{} *{}uninit(::std::unique_ptr<{}> *ptr) noexcept {{",
1730+
inner, mangled_prefix, inner,
16911731
);
16921732
writeln!(
16931733
out,
@@ -1702,36 +1742,41 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: UniquePtr) {
17021742
begin_function_definition(out);
17031743
writeln!(
17041744
out,
1705-
"void cxxbridge1$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{",
1706-
instance, inner, inner,
1745+
"void {}raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{",
1746+
mangled_prefix, inner, inner,
17071747
);
17081748
writeln!(out, " ::new (ptr) ::std::unique_ptr<{}>(raw);", inner);
17091749
writeln!(out, "}}");
17101750

17111751
begin_function_definition(out);
17121752
writeln!(
17131753
out,
1714-
"{} const *cxxbridge1$unique_ptr${}$get(::std::unique_ptr<{}> const &ptr) noexcept {{",
1715-
inner, instance, inner,
1754+
"{} const *{}get(::std::unique_ptr<{}> const &ptr) noexcept {{",
1755+
inner, mangled_prefix, inner,
17161756
);
17171757
writeln!(out, " return ptr.get();");
17181758
writeln!(out, "}}");
17191759

17201760
begin_function_definition(out);
17211761
writeln!(
17221762
out,
1723-
"{} *cxxbridge1$unique_ptr${}$release(::std::unique_ptr<{}> &ptr) noexcept {{",
1724-
inner, instance, inner,
1763+
"{} *{}release(::std::unique_ptr<{}> &ptr) noexcept {{",
1764+
inner, mangled_prefix, inner,
17251765
);
17261766
writeln!(out, " return ptr.release();");
17271767
writeln!(out, "}}");
17281768

17291769
begin_function_definition(out);
17301770
writeln!(
17311771
out,
1732-
"void cxxbridge1$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{",
1733-
instance, inner,
1772+
"void {}drop({} *ptr) noexcept {{",
1773+
mangled_prefix, &cxx_type_string,
17341774
);
1775+
if deleter_name.is_some() {
1776+
// Check whether the deleter is really the first member as expected by the Rust type
1777+
out.include.cassert = true;
1778+
writeln!(out, " assert(reinterpret_cast<void*>(&ptr->get_deleter()) == reinterpret_cast<void*>(ptr));");
1779+
}
17351780
if conditional_delete {
17361781
out.builtin.deleter_if = true;
17371782
writeln!(

0 commit comments

Comments
 (0)