diff --git a/Makefile.toml b/Makefile.toml index c61d8d679..414cf9b4b 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -229,8 +229,8 @@ dependencies = [ "gen-feature-c", "gen-feature-c2", "gen-feature-js", - "gen-feature-dotnet", "gen-feature-dart", + "gen-feature-dotnet", ] [tasks.gen-examples] diff --git a/core/src/ast/types.rs b/core/src/ast/types.rs index 74d50aa97..ce852cc5e 100644 --- a/core/src/ast/types.rs +++ b/core/src/ast/types.rs @@ -383,8 +383,9 @@ pub enum TypeName { Writeable, /// A `&DiplomatStr` type. StrReference(Lifetime, StringEncoding), - /// A `&[T]` type, where `T` is a primitive. - PrimitiveSlice(Lifetime, Mutability, PrimitiveType), + /// A `&[T]` or `Box<[T]>` type, where `T` is a primitive. + /// Owned slices don't have a lifetime or mutability. + PrimitiveSlice(Option<(Lifetime, Mutability)>, PrimitiveType), /// The `()` type. Unit, /// The `Self` type. @@ -514,7 +515,7 @@ impl TypeName { )) .unwrap() } - TypeName::PrimitiveSlice(lifetime, mutability, name) => { + TypeName::PrimitiveSlice(Some((lifetime, mutability)), name) => { let primitive_name = PRIMITIVE_TO_STRING.get(name).unwrap(); let formatted_str = format!( "{}[{}]", @@ -523,6 +524,11 @@ impl TypeName { ); syn::parse_str(&formatted_str).unwrap() } + TypeName::PrimitiveSlice(None, name) => syn::parse_str(&format!( + "Box<[{}]>", + PRIMITIVE_TO_STRING.get(name).unwrap() + )) + .unwrap(), TypeName::Unit => syn::parse_quote! { () }, @@ -539,7 +545,7 @@ impl TypeName { /// - If the type is a path equal to [`diplomat_runtime::DiplomatResult`], returns a [`TypeName::DiplomatResult`] with the type parameters recursively converted /// - If the type is a path equal to [`diplomat_runtime::DiplomatWriteable`], returns a [`TypeName::Writeable`] /// - If the type is a reference to `DiplomatStr`, returns a [`TypeName::StrReference`] - /// - If the type is a reference to a slice of a Rust primitive, returns a [`TypeName::PrimitiveSlice`] + /// - If the type is a reference (owned or borrowed) to a slice of a Rust primitive, returns a [`TypeName::PrimitiveSlice`] /// - If the type is a reference (`&` or `&mut`), returns a [`TypeName::Reference`] with the referenced type recursively converted /// - Otherwise, assume that the reference is to a [`CustomType`] in either the current module or another one, returns a [`TypeName::Named`] pub fn from_syn(ty: &syn::Type, self_path_type: Option) -> TypeName { @@ -566,7 +572,10 @@ impl TypeName { .get_ident() .and_then(|i| STRING_TO_PRIMITIVE.get(i.to_string().as_str())) { - return TypeName::PrimitiveSlice(lifetime, mutability, *primitive); + return TypeName::PrimitiveSlice( + Some((lifetime, mutability)), + *primitive, + ); } } } @@ -586,7 +595,15 @@ impl TypeName { } else if p.path.segments.len() == 1 && p.path.segments[0].ident == "Box" { if let PathArguments::AngleBracketed(type_args) = &p.path.segments[0].arguments { - if let GenericArgument::Type(tpe) = &type_args.args[0] { + if let GenericArgument::Type(syn::Type::Slice(slice)) = &type_args.args[0] { + if let TypeName::Primitive(p) = + TypeName::from_syn(&*slice.elem, self_path_type) + { + TypeName::PrimitiveSlice(None, p) + } else { + panic!("Owned slices only support primitives.") + } + } else if let GenericArgument::Type(tpe) = &type_args.args[0] { TypeName::Box(Box::new(TypeName::from_syn(tpe, self_path_type))) } else { panic!("Expected first type argument for Box to be a type") @@ -679,7 +696,9 @@ impl TypeName { err.visit_lifetimes(visit) } TypeName::StrReference(lt, ..) => visit(lt, LifetimeOrigin::StrReference), - TypeName::PrimitiveSlice(lt, ..) => visit(lt, LifetimeOrigin::PrimitiveSlice), + TypeName::PrimitiveSlice(Some((lt, _)), ..) => { + visit(lt, LifetimeOrigin::PrimitiveSlice) + } _ => ControlFlow::Continue(()), } } @@ -888,7 +907,7 @@ impl TypeName { err.check_lifetime_elision(full_type, in_path, env, errors); } TypeName::StrReference(Lifetime::Anonymous, ..) - | TypeName::PrimitiveSlice(Lifetime::Anonymous, ..) => { + | TypeName::PrimitiveSlice(Some((Lifetime::Anonymous, _)), ..) => { errors.push(ValidityError::LifetimeElisionInReturn { full_type: full_type.clone(), sub_type: self.clone(), @@ -951,10 +970,11 @@ impl fmt::Display for TypeName { ReferenceDisplay(lifetime, &Mutability::Immutable) ) } - TypeName::PrimitiveSlice(lifetime, mutability, typ) => { + TypeName::PrimitiveSlice(Some((lifetime, mutability)), typ) => { write!(f, "{}[{typ}]", ReferenceDisplay(lifetime, mutability)) } TypeName::Unit => "()".fmt(f), + TypeName::PrimitiveSlice(None, typ) => write!(f, "Box<[{typ}]>"), } } } diff --git a/core/src/hir/lowering.rs b/core/src/hir/lowering.rs index a7431e30c..ba5c337df 100644 --- a/core/src/hir/lowering.rs +++ b/core/src/hir/lowering.rs @@ -470,8 +470,12 @@ impl<'ast, 'errors> LoweringContext<'ast, 'errors> { ltl?.lower_lifetime(lifetime), *encoding, ))), - ast::TypeName::PrimitiveSlice(lifetime, mutability, prim) => { - let borrow = Borrow::new(ltl?.lower_lifetime(lifetime), *mutability); + ast::TypeName::PrimitiveSlice(lm, prim) => { + let borrow = if let Some((lifetime, mutability)) = lm { + Some(Borrow::new(ltl?.lower_lifetime(lifetime), *mutability)) + } else { + None + }; let prim = PrimitiveType::from_ast(*prim); Some(Type::Slice(Slice::Primitive(borrow, prim))) @@ -663,8 +667,12 @@ impl<'ast, 'errors> LoweringContext<'ast, 'errors> { ltl?.lower_lifetime(lifetime), *encoding, ))), - ast::TypeName::PrimitiveSlice(lifetime, mutability, prim) => { - let borrow = Borrow::new(ltl?.lower_lifetime(lifetime), *mutability); + ast::TypeName::PrimitiveSlice(lm, prim) => { + let borrow = if let Some((lifetime, mutability)) = lm { + Some(Borrow::new(ltl?.lower_lifetime(lifetime), *mutability)) + } else { + None + }; let prim = PrimitiveType::from_ast(*prim); Some(OutType::Slice(Slice::Primitive(borrow, prim))) diff --git a/core/src/hir/methods.rs b/core/src/hir/methods.rs index 648f76879..6c5674b92 100644 --- a/core/src/hir/methods.rs +++ b/core/src/hir/methods.rs @@ -374,10 +374,11 @@ impl<'m> BorrowingFieldVisitor<'m> { method_lifetimes: &MethodLifetimes, leaves: &mut SmallVec<[BorrowingFieldVisitorLeaf; 8]>, ) { - let method_lifetime = slice - .lifetime() - .flat_map_nonstatic(|lt| lt.as_method_lifetime(method_lifetimes)); - leaves.push(BorrowingFieldVisitorLeaf::Slice(parent, method_lifetime)); + if let Some(lifetime) = slice.lifetime() { + let method_lifetime = + lifetime.flat_map_nonstatic(|lt| lt.as_method_lifetime(method_lifetimes)); + leaves.push(BorrowingFieldVisitorLeaf::Slice(parent, method_lifetime)); + } } /// Add a struct as a parent and recurse down leaves during construction of a diff --git a/core/src/hir/types.rs b/core/src/hir/types.rs index 0bb6eb42a..56d1e2908 100644 --- a/core/src/hir/types.rs +++ b/core/src/hir/types.rs @@ -37,8 +37,8 @@ pub enum Slice { /// A string slice, e.g. `&DiplomatStr`. Str(MaybeStatic, StringEncoding), - /// A primitive slice, e.g. `&mut [u8]`. - Primitive(Borrow, PrimitiveType), + /// A primitive slice, e.g. `&mut [u8]` or `Box<[usize]> + Primitive(Option, PrimitiveType), } // For now, the lifetime in not optional. This is because when you have references @@ -88,10 +88,11 @@ impl SelfType { impl Slice { /// Returns the [`TypeLifetime`] contained in either the `Str` or `Primitive` /// variant. - pub fn lifetime(&self) -> &MaybeStatic { + pub fn lifetime(&self) -> Option<&MaybeStatic> { match self { - Slice::Str(lifetime, ..) => lifetime, - Slice::Primitive(reference, ..) => &reference.lifetime, + Slice::Str(lifetime, ..) => Some(lifetime), + Slice::Primitive(Some(reference), ..) => Some(&reference.lifetime), + Slice::Primitive(..) => None, } } } diff --git a/example/c/include/diplomat_runtime.h b/example/c/include/diplomat_runtime.h index de0f9c76f..9cc0abedb 100644 --- a/example/c/include/diplomat_runtime.h +++ b/example/c/include/diplomat_runtime.h @@ -40,26 +40,30 @@ typedef struct DiplomatStringView { size_t len; } DiplomatStringView; -#define MAKE_SLICE_VIEW(name, c_ty) \ +#define MAKE_SLICES(name, c_ty) \ typedef struct Diplomat##name##View { \ const c_ty* data; \ size_t len; \ - } Diplomat##name##View; + } Diplomat##name##View; \ + typedef struct Diplomat##name##Array { \ + const c_ty* data; \ + size_t len; \ + } Diplomat##name##Array; -MAKE_SLICE_VIEW(I8, int8_t) -MAKE_SLICE_VIEW(U8, uint8_t) -MAKE_SLICE_VIEW(I16, int16_t) -MAKE_SLICE_VIEW(U16, uint16_t) -MAKE_SLICE_VIEW(I32, int32_t) -MAKE_SLICE_VIEW(U32, uint32_t) -MAKE_SLICE_VIEW(I64, int64_t) -MAKE_SLICE_VIEW(U64, uint64_t) -MAKE_SLICE_VIEW(Isize, intptr_t) -MAKE_SLICE_VIEW(Usize, size_t) -MAKE_SLICE_VIEW(F32, float) -MAKE_SLICE_VIEW(F64, double) -MAKE_SLICE_VIEW(Bool, bool) -MAKE_SLICE_VIEW(Char, char32_t) +MAKE_SLICES(I8, int8_t) +MAKE_SLICES(U8, uint8_t) +MAKE_SLICES(I16, int16_t) +MAKE_SLICES(U16, uint16_t) +MAKE_SLICES(I32, int32_t) +MAKE_SLICES(U32, uint32_t) +MAKE_SLICES(I64, int64_t) +MAKE_SLICES(U64, uint64_t) +MAKE_SLICES(Isize, intptr_t) +MAKE_SLICES(Usize, size_t) +MAKE_SLICES(F32, float) +MAKE_SLICES(F64, double) +MAKE_SLICES(Bool, bool) +MAKE_SLICES(Char, char32_t) #ifdef __cplusplus diff --git a/example/c2/include/diplomat_runtime.h b/example/c2/include/diplomat_runtime.h index de0f9c76f..9cc0abedb 100644 --- a/example/c2/include/diplomat_runtime.h +++ b/example/c2/include/diplomat_runtime.h @@ -40,26 +40,30 @@ typedef struct DiplomatStringView { size_t len; } DiplomatStringView; -#define MAKE_SLICE_VIEW(name, c_ty) \ +#define MAKE_SLICES(name, c_ty) \ typedef struct Diplomat##name##View { \ const c_ty* data; \ size_t len; \ - } Diplomat##name##View; + } Diplomat##name##View; \ + typedef struct Diplomat##name##Array { \ + const c_ty* data; \ + size_t len; \ + } Diplomat##name##Array; -MAKE_SLICE_VIEW(I8, int8_t) -MAKE_SLICE_VIEW(U8, uint8_t) -MAKE_SLICE_VIEW(I16, int16_t) -MAKE_SLICE_VIEW(U16, uint16_t) -MAKE_SLICE_VIEW(I32, int32_t) -MAKE_SLICE_VIEW(U32, uint32_t) -MAKE_SLICE_VIEW(I64, int64_t) -MAKE_SLICE_VIEW(U64, uint64_t) -MAKE_SLICE_VIEW(Isize, intptr_t) -MAKE_SLICE_VIEW(Usize, size_t) -MAKE_SLICE_VIEW(F32, float) -MAKE_SLICE_VIEW(F64, double) -MAKE_SLICE_VIEW(Bool, bool) -MAKE_SLICE_VIEW(Char, char32_t) +MAKE_SLICES(I8, int8_t) +MAKE_SLICES(U8, uint8_t) +MAKE_SLICES(I16, int16_t) +MAKE_SLICES(U16, uint16_t) +MAKE_SLICES(I32, int32_t) +MAKE_SLICES(U32, uint32_t) +MAKE_SLICES(I64, int64_t) +MAKE_SLICES(U64, uint64_t) +MAKE_SLICES(Isize, intptr_t) +MAKE_SLICES(Usize, size_t) +MAKE_SLICES(F32, float) +MAKE_SLICES(F64, double) +MAKE_SLICES(Bool, bool) +MAKE_SLICES(Char, char32_t) #ifdef __cplusplus diff --git a/example/cpp/include/diplomat_runtime.h b/example/cpp/include/diplomat_runtime.h index de0f9c76f..9cc0abedb 100644 --- a/example/cpp/include/diplomat_runtime.h +++ b/example/cpp/include/diplomat_runtime.h @@ -40,26 +40,30 @@ typedef struct DiplomatStringView { size_t len; } DiplomatStringView; -#define MAKE_SLICE_VIEW(name, c_ty) \ +#define MAKE_SLICES(name, c_ty) \ typedef struct Diplomat##name##View { \ const c_ty* data; \ size_t len; \ - } Diplomat##name##View; + } Diplomat##name##View; \ + typedef struct Diplomat##name##Array { \ + const c_ty* data; \ + size_t len; \ + } Diplomat##name##Array; -MAKE_SLICE_VIEW(I8, int8_t) -MAKE_SLICE_VIEW(U8, uint8_t) -MAKE_SLICE_VIEW(I16, int16_t) -MAKE_SLICE_VIEW(U16, uint16_t) -MAKE_SLICE_VIEW(I32, int32_t) -MAKE_SLICE_VIEW(U32, uint32_t) -MAKE_SLICE_VIEW(I64, int64_t) -MAKE_SLICE_VIEW(U64, uint64_t) -MAKE_SLICE_VIEW(Isize, intptr_t) -MAKE_SLICE_VIEW(Usize, size_t) -MAKE_SLICE_VIEW(F32, float) -MAKE_SLICE_VIEW(F64, double) -MAKE_SLICE_VIEW(Bool, bool) -MAKE_SLICE_VIEW(Char, char32_t) +MAKE_SLICES(I8, int8_t) +MAKE_SLICES(U8, uint8_t) +MAKE_SLICES(I16, int16_t) +MAKE_SLICES(U16, uint16_t) +MAKE_SLICES(I32, int32_t) +MAKE_SLICES(U32, uint32_t) +MAKE_SLICES(I64, int64_t) +MAKE_SLICES(U64, uint64_t) +MAKE_SLICES(Isize, intptr_t) +MAKE_SLICES(Usize, size_t) +MAKE_SLICES(F32, float) +MAKE_SLICES(F64, double) +MAKE_SLICES(Bool, bool) +MAKE_SLICES(Char, char32_t) #ifdef __cplusplus diff --git a/example/cpp2/include/diplomat_runtime.h b/example/cpp2/include/diplomat_runtime.h index de0f9c76f..9cc0abedb 100644 --- a/example/cpp2/include/diplomat_runtime.h +++ b/example/cpp2/include/diplomat_runtime.h @@ -40,26 +40,30 @@ typedef struct DiplomatStringView { size_t len; } DiplomatStringView; -#define MAKE_SLICE_VIEW(name, c_ty) \ +#define MAKE_SLICES(name, c_ty) \ typedef struct Diplomat##name##View { \ const c_ty* data; \ size_t len; \ - } Diplomat##name##View; + } Diplomat##name##View; \ + typedef struct Diplomat##name##Array { \ + const c_ty* data; \ + size_t len; \ + } Diplomat##name##Array; -MAKE_SLICE_VIEW(I8, int8_t) -MAKE_SLICE_VIEW(U8, uint8_t) -MAKE_SLICE_VIEW(I16, int16_t) -MAKE_SLICE_VIEW(U16, uint16_t) -MAKE_SLICE_VIEW(I32, int32_t) -MAKE_SLICE_VIEW(U32, uint32_t) -MAKE_SLICE_VIEW(I64, int64_t) -MAKE_SLICE_VIEW(U64, uint64_t) -MAKE_SLICE_VIEW(Isize, intptr_t) -MAKE_SLICE_VIEW(Usize, size_t) -MAKE_SLICE_VIEW(F32, float) -MAKE_SLICE_VIEW(F64, double) -MAKE_SLICE_VIEW(Bool, bool) -MAKE_SLICE_VIEW(Char, char32_t) +MAKE_SLICES(I8, int8_t) +MAKE_SLICES(U8, uint8_t) +MAKE_SLICES(I16, int16_t) +MAKE_SLICES(U16, uint16_t) +MAKE_SLICES(I32, int32_t) +MAKE_SLICES(U32, uint32_t) +MAKE_SLICES(I64, int64_t) +MAKE_SLICES(U64, uint64_t) +MAKE_SLICES(Isize, intptr_t) +MAKE_SLICES(Usize, size_t) +MAKE_SLICES(F32, float) +MAKE_SLICES(F64, double) +MAKE_SLICES(Bool, bool) +MAKE_SLICES(Char, char32_t) #ifdef __cplusplus diff --git a/example/dart/lib/lib.g.dart b/example/dart/lib/lib.g.dart index 228fcfa22..0a16e6ffb 100644 --- a/example/dart/lib/lib.g.dart +++ b/example/dart/lib/lib.g.dart @@ -36,6 +36,21 @@ void init(String path) => _capi = ffi.DynamicLibrary.open(path).lookup; final _callocFree = Finalizer(ffi2.calloc.free); +final class _RustAlloc implements ffi.Allocator { + @override + ffi.Pointer allocate(int byteCount, {int? alignment}) { + return _diplomat_alloc(byteCount, alignment ?? 1).cast(); + } + static final _diplomat_alloc = + _capi>('diplomat_alloc').asFunction(); + + @override + void free(ffi.Pointer pointer) { + throw "Internal error: should not deallocate in Rust memory"; + } +} + + final class _ResultOpaqueVoidUnion extends ffi.Union { external ffi.Pointer ok; } diff --git a/feature_tests/c/include/Float64Vec.h b/feature_tests/c/include/Float64Vec.h index baea1b933..31067bdb8 100644 --- a/feature_tests/c/include/Float64Vec.h +++ b/feature_tests/c/include/Float64Vec.h @@ -21,6 +21,12 @@ extern "C" { Float64Vec* Float64Vec_new(const double* v_data, size_t v_len); +Float64Vec* Float64Vec_new_from_owned(double* v_data, size_t v_len); + +DiplomatF64Array Float64Vec_as_boxed_slice(const Float64Vec* self); + +DiplomatF64View Float64Vec_as_slice(const Float64Vec* self); + void Float64Vec_fill_slice(const Float64Vec* self, double* v_data, size_t v_len); void Float64Vec_set_value(Float64Vec* self, const double* new_slice_data, size_t new_slice_len); diff --git a/feature_tests/c/include/diplomat_runtime.h b/feature_tests/c/include/diplomat_runtime.h index de0f9c76f..9cc0abedb 100644 --- a/feature_tests/c/include/diplomat_runtime.h +++ b/feature_tests/c/include/diplomat_runtime.h @@ -40,26 +40,30 @@ typedef struct DiplomatStringView { size_t len; } DiplomatStringView; -#define MAKE_SLICE_VIEW(name, c_ty) \ +#define MAKE_SLICES(name, c_ty) \ typedef struct Diplomat##name##View { \ const c_ty* data; \ size_t len; \ - } Diplomat##name##View; + } Diplomat##name##View; \ + typedef struct Diplomat##name##Array { \ + const c_ty* data; \ + size_t len; \ + } Diplomat##name##Array; -MAKE_SLICE_VIEW(I8, int8_t) -MAKE_SLICE_VIEW(U8, uint8_t) -MAKE_SLICE_VIEW(I16, int16_t) -MAKE_SLICE_VIEW(U16, uint16_t) -MAKE_SLICE_VIEW(I32, int32_t) -MAKE_SLICE_VIEW(U32, uint32_t) -MAKE_SLICE_VIEW(I64, int64_t) -MAKE_SLICE_VIEW(U64, uint64_t) -MAKE_SLICE_VIEW(Isize, intptr_t) -MAKE_SLICE_VIEW(Usize, size_t) -MAKE_SLICE_VIEW(F32, float) -MAKE_SLICE_VIEW(F64, double) -MAKE_SLICE_VIEW(Bool, bool) -MAKE_SLICE_VIEW(Char, char32_t) +MAKE_SLICES(I8, int8_t) +MAKE_SLICES(U8, uint8_t) +MAKE_SLICES(I16, int16_t) +MAKE_SLICES(U16, uint16_t) +MAKE_SLICES(I32, int32_t) +MAKE_SLICES(U32, uint32_t) +MAKE_SLICES(I64, int64_t) +MAKE_SLICES(U64, uint64_t) +MAKE_SLICES(Isize, intptr_t) +MAKE_SLICES(Usize, size_t) +MAKE_SLICES(F32, float) +MAKE_SLICES(F64, double) +MAKE_SLICES(Bool, bool) +MAKE_SLICES(Char, char32_t) #ifdef __cplusplus diff --git a/feature_tests/c2/include/Float64Vec.h b/feature_tests/c2/include/Float64Vec.h index 55fddddc4..1fb2ca2d8 100644 --- a/feature_tests/c2/include/Float64Vec.h +++ b/feature_tests/c2/include/Float64Vec.h @@ -17,6 +17,12 @@ extern "C" { Float64Vec* Float64Vec_new(const double* v_data, size_t v_len); +Float64Vec* Float64Vec_new_from_owned(double* v_data, size_t v_len); + +struct { const double* data; size_t len; } Float64Vec_as_boxed_slice(const Float64Vec* self); + +struct { const double* data; size_t len; } Float64Vec_as_slice(const Float64Vec* self); + void Float64Vec_fill_slice(const Float64Vec* self, double* v_data, size_t v_len); void Float64Vec_set_value(Float64Vec* self, const double* new_slice_data, size_t new_slice_len); diff --git a/feature_tests/c2/include/diplomat_runtime.h b/feature_tests/c2/include/diplomat_runtime.h index de0f9c76f..9cc0abedb 100644 --- a/feature_tests/c2/include/diplomat_runtime.h +++ b/feature_tests/c2/include/diplomat_runtime.h @@ -40,26 +40,30 @@ typedef struct DiplomatStringView { size_t len; } DiplomatStringView; -#define MAKE_SLICE_VIEW(name, c_ty) \ +#define MAKE_SLICES(name, c_ty) \ typedef struct Diplomat##name##View { \ const c_ty* data; \ size_t len; \ - } Diplomat##name##View; + } Diplomat##name##View; \ + typedef struct Diplomat##name##Array { \ + const c_ty* data; \ + size_t len; \ + } Diplomat##name##Array; -MAKE_SLICE_VIEW(I8, int8_t) -MAKE_SLICE_VIEW(U8, uint8_t) -MAKE_SLICE_VIEW(I16, int16_t) -MAKE_SLICE_VIEW(U16, uint16_t) -MAKE_SLICE_VIEW(I32, int32_t) -MAKE_SLICE_VIEW(U32, uint32_t) -MAKE_SLICE_VIEW(I64, int64_t) -MAKE_SLICE_VIEW(U64, uint64_t) -MAKE_SLICE_VIEW(Isize, intptr_t) -MAKE_SLICE_VIEW(Usize, size_t) -MAKE_SLICE_VIEW(F32, float) -MAKE_SLICE_VIEW(F64, double) -MAKE_SLICE_VIEW(Bool, bool) -MAKE_SLICE_VIEW(Char, char32_t) +MAKE_SLICES(I8, int8_t) +MAKE_SLICES(U8, uint8_t) +MAKE_SLICES(I16, int16_t) +MAKE_SLICES(U16, uint16_t) +MAKE_SLICES(I32, int32_t) +MAKE_SLICES(U32, uint32_t) +MAKE_SLICES(I64, int64_t) +MAKE_SLICES(U64, uint64_t) +MAKE_SLICES(Isize, intptr_t) +MAKE_SLICES(Usize, size_t) +MAKE_SLICES(F32, float) +MAKE_SLICES(F64, double) +MAKE_SLICES(Bool, bool) +MAKE_SLICES(Char, char32_t) #ifdef __cplusplus diff --git a/feature_tests/cpp/docs/source/slices_ffi.rst b/feature_tests/cpp/docs/source/slices_ffi.rst index 62f0d90b7..36ef4b8d5 100644 --- a/feature_tests/cpp/docs/source/slices_ffi.rst +++ b/feature_tests/cpp/docs/source/slices_ffi.rst @@ -6,7 +6,18 @@ .. cpp:function:: static Float64Vec new_(const diplomat::span v) - .. cpp:function:: void fill_slice(diplomat::span v) const + .. cpp:function:: static Float64Vec new_from_owned(const diplomat::span v) + + + .. cpp:function:: const diplomat::span as_boxed_slice() const + + + .. cpp:function:: const diplomat::span as_slice() const + + Lifetimes: ``this`` must live at least as long as the output. + + + .. cpp:function:: void fill_slice(const diplomat::span v) const .. cpp:function:: void set_value(const diplomat::span new_slice) diff --git a/feature_tests/cpp/include/Float64Vec.h b/feature_tests/cpp/include/Float64Vec.h index baea1b933..31067bdb8 100644 --- a/feature_tests/cpp/include/Float64Vec.h +++ b/feature_tests/cpp/include/Float64Vec.h @@ -21,6 +21,12 @@ extern "C" { Float64Vec* Float64Vec_new(const double* v_data, size_t v_len); +Float64Vec* Float64Vec_new_from_owned(double* v_data, size_t v_len); + +DiplomatF64Array Float64Vec_as_boxed_slice(const Float64Vec* self); + +DiplomatF64View Float64Vec_as_slice(const Float64Vec* self); + void Float64Vec_fill_slice(const Float64Vec* self, double* v_data, size_t v_len); void Float64Vec_set_value(Float64Vec* self, const double* new_slice_data, size_t new_slice_len); diff --git a/feature_tests/cpp/include/Float64Vec.hpp b/feature_tests/cpp/include/Float64Vec.hpp index de094e6be..3857b7b1d 100644 --- a/feature_tests/cpp/include/Float64Vec.hpp +++ b/feature_tests/cpp/include/Float64Vec.hpp @@ -24,7 +24,14 @@ struct Float64VecDeleter { class Float64Vec { public: static Float64Vec new_(const diplomat::span v); - void fill_slice(diplomat::span v) const; + static Float64Vec new_from_owned(const diplomat::span v); + const diplomat::span as_boxed_slice() const; + + /** + * Lifetimes: `this` must live at least as long as the output. + */ + const diplomat::span as_slice() const; + void fill_slice(const diplomat::span v) const; void set_value(const diplomat::span new_slice); inline const capi::Float64Vec* AsFFI() const { return this->inner.get(); } inline capi::Float64Vec* AsFFIMut() { return this->inner.get(); } @@ -40,7 +47,20 @@ class Float64Vec { inline Float64Vec Float64Vec::new_(const diplomat::span v) { return Float64Vec(capi::Float64Vec_new(v.data(), v.size())); } -inline void Float64Vec::fill_slice(diplomat::span v) const { +inline Float64Vec Float64Vec::new_from_owned(const diplomat::span v) { + return Float64Vec(capi::Float64Vec_new_from_owned(v.data(), v.size())); +} +inline const diplomat::span Float64Vec::as_boxed_slice() const { + capi::DiplomatF64View diplomat_slice_raw_out_value = capi::Float64Vec_as_boxed_slice(this->inner.get()); + diplomat::span slice(diplomat_slice_raw_out_value.data, diplomat_slice_raw_out_value.len); + return slice; +} +inline const diplomat::span Float64Vec::as_slice() const { + capi::DiplomatF64View diplomat_slice_raw_out_value = capi::Float64Vec_as_slice(this->inner.get()); + diplomat::span slice(diplomat_slice_raw_out_value.data, diplomat_slice_raw_out_value.len); + return slice; +} +inline void Float64Vec::fill_slice(const diplomat::span v) const { capi::Float64Vec_fill_slice(this->inner.get(), v.data(), v.size()); } inline void Float64Vec::set_value(const diplomat::span new_slice) { diff --git a/feature_tests/cpp/include/diplomat_runtime.h b/feature_tests/cpp/include/diplomat_runtime.h index de0f9c76f..9cc0abedb 100644 --- a/feature_tests/cpp/include/diplomat_runtime.h +++ b/feature_tests/cpp/include/diplomat_runtime.h @@ -40,26 +40,30 @@ typedef struct DiplomatStringView { size_t len; } DiplomatStringView; -#define MAKE_SLICE_VIEW(name, c_ty) \ +#define MAKE_SLICES(name, c_ty) \ typedef struct Diplomat##name##View { \ const c_ty* data; \ size_t len; \ - } Diplomat##name##View; + } Diplomat##name##View; \ + typedef struct Diplomat##name##Array { \ + const c_ty* data; \ + size_t len; \ + } Diplomat##name##Array; -MAKE_SLICE_VIEW(I8, int8_t) -MAKE_SLICE_VIEW(U8, uint8_t) -MAKE_SLICE_VIEW(I16, int16_t) -MAKE_SLICE_VIEW(U16, uint16_t) -MAKE_SLICE_VIEW(I32, int32_t) -MAKE_SLICE_VIEW(U32, uint32_t) -MAKE_SLICE_VIEW(I64, int64_t) -MAKE_SLICE_VIEW(U64, uint64_t) -MAKE_SLICE_VIEW(Isize, intptr_t) -MAKE_SLICE_VIEW(Usize, size_t) -MAKE_SLICE_VIEW(F32, float) -MAKE_SLICE_VIEW(F64, double) -MAKE_SLICE_VIEW(Bool, bool) -MAKE_SLICE_VIEW(Char, char32_t) +MAKE_SLICES(I8, int8_t) +MAKE_SLICES(U8, uint8_t) +MAKE_SLICES(I16, int16_t) +MAKE_SLICES(U16, uint16_t) +MAKE_SLICES(I32, int32_t) +MAKE_SLICES(U32, uint32_t) +MAKE_SLICES(I64, int64_t) +MAKE_SLICES(U64, uint64_t) +MAKE_SLICES(Isize, intptr_t) +MAKE_SLICES(Usize, size_t) +MAKE_SLICES(F32, float) +MAKE_SLICES(F64, double) +MAKE_SLICES(Bool, bool) +MAKE_SLICES(Char, char32_t) #ifdef __cplusplus diff --git a/feature_tests/cpp2/include/Float64Vec.d.hpp b/feature_tests/cpp2/include/Float64Vec.d.hpp index 5c83738f2..86ac564cd 100644 --- a/feature_tests/cpp2/include/Float64Vec.d.hpp +++ b/feature_tests/cpp2/include/Float64Vec.d.hpp @@ -16,6 +16,12 @@ class Float64Vec { inline static std::unique_ptr new_(diplomat::span v); + inline static std::unique_ptr new_from_owned(diplomat::span v); + + inline diplomat::span as_boxed_slice() const; + + inline diplomat::span as_slice() const; + inline void fill_slice(diplomat::span v) const; inline void set_value(diplomat::span new_slice); diff --git a/feature_tests/cpp2/include/Float64Vec.h b/feature_tests/cpp2/include/Float64Vec.h index 55fddddc4..1fb2ca2d8 100644 --- a/feature_tests/cpp2/include/Float64Vec.h +++ b/feature_tests/cpp2/include/Float64Vec.h @@ -17,6 +17,12 @@ extern "C" { Float64Vec* Float64Vec_new(const double* v_data, size_t v_len); +Float64Vec* Float64Vec_new_from_owned(double* v_data, size_t v_len); + +struct { const double* data; size_t len; } Float64Vec_as_boxed_slice(const Float64Vec* self); + +struct { const double* data; size_t len; } Float64Vec_as_slice(const Float64Vec* self); + void Float64Vec_fill_slice(const Float64Vec* self, double* v_data, size_t v_len); void Float64Vec_set_value(Float64Vec* self, const double* new_slice_data, size_t new_slice_len); diff --git a/feature_tests/cpp2/include/Float64Vec.hpp b/feature_tests/cpp2/include/Float64Vec.hpp index c98d9330f..206095ff8 100644 --- a/feature_tests/cpp2/include/Float64Vec.hpp +++ b/feature_tests/cpp2/include/Float64Vec.hpp @@ -19,6 +19,22 @@ inline std::unique_ptr Float64Vec::new_(diplomat::span return std::unique_ptr(Float64Vec::FromFFI(result)); } +inline std::unique_ptr Float64Vec::new_from_owned(diplomat::span v) { + auto result = capi::Float64Vec_new_from_owned(v.data(), + v.size()); + return std::unique_ptr(Float64Vec::FromFFI(result)); +} + +inline diplomat::span Float64Vec::as_boxed_slice() const { + auto result = capi::Float64Vec_as_boxed_slice(this->AsFFI()); + return diplomat::span(result_data, result_size); +} + +inline diplomat::span Float64Vec::as_slice() const { + auto result = capi::Float64Vec_as_slice(this->AsFFI()); + return diplomat::span(result_data, result_size); +} + inline void Float64Vec::fill_slice(diplomat::span v) const { capi::Float64Vec_fill_slice(this->AsFFI(), v.data(), diff --git a/feature_tests/cpp2/include/diplomat_runtime.h b/feature_tests/cpp2/include/diplomat_runtime.h index de0f9c76f..9cc0abedb 100644 --- a/feature_tests/cpp2/include/diplomat_runtime.h +++ b/feature_tests/cpp2/include/diplomat_runtime.h @@ -40,26 +40,30 @@ typedef struct DiplomatStringView { size_t len; } DiplomatStringView; -#define MAKE_SLICE_VIEW(name, c_ty) \ +#define MAKE_SLICES(name, c_ty) \ typedef struct Diplomat##name##View { \ const c_ty* data; \ size_t len; \ - } Diplomat##name##View; + } Diplomat##name##View; \ + typedef struct Diplomat##name##Array { \ + const c_ty* data; \ + size_t len; \ + } Diplomat##name##Array; -MAKE_SLICE_VIEW(I8, int8_t) -MAKE_SLICE_VIEW(U8, uint8_t) -MAKE_SLICE_VIEW(I16, int16_t) -MAKE_SLICE_VIEW(U16, uint16_t) -MAKE_SLICE_VIEW(I32, int32_t) -MAKE_SLICE_VIEW(U32, uint32_t) -MAKE_SLICE_VIEW(I64, int64_t) -MAKE_SLICE_VIEW(U64, uint64_t) -MAKE_SLICE_VIEW(Isize, intptr_t) -MAKE_SLICE_VIEW(Usize, size_t) -MAKE_SLICE_VIEW(F32, float) -MAKE_SLICE_VIEW(F64, double) -MAKE_SLICE_VIEW(Bool, bool) -MAKE_SLICE_VIEW(Char, char32_t) +MAKE_SLICES(I8, int8_t) +MAKE_SLICES(U8, uint8_t) +MAKE_SLICES(I16, int16_t) +MAKE_SLICES(U16, uint16_t) +MAKE_SLICES(I32, int32_t) +MAKE_SLICES(U32, uint32_t) +MAKE_SLICES(I64, int64_t) +MAKE_SLICES(U64, uint64_t) +MAKE_SLICES(Isize, intptr_t) +MAKE_SLICES(Usize, size_t) +MAKE_SLICES(F32, float) +MAKE_SLICES(F64, double) +MAKE_SLICES(Bool, bool) +MAKE_SLICES(Char, char32_t) #ifdef __cplusplus diff --git a/feature_tests/dart/lib/BorrowedFields.g.dart b/feature_tests/dart/lib/BorrowedFields.g.dart index 4c2d98c95..e3ff89918 100644 --- a/feature_tests/dart/lib/BorrowedFields.g.dart +++ b/feature_tests/dart/lib/BorrowedFields.g.dart @@ -25,7 +25,7 @@ final class BorrowedFields { String get a => _underlying.a._asDart; set a(String a) { - final alloc = ffi2.calloc; + final alloc = RustAlloc(); alloc.free(_underlying.a._bytes); final aSlice = _SliceFfiUtf16._fromDart(a, alloc); _underlying.a = aSlice; @@ -33,7 +33,7 @@ final class BorrowedFields { String get b => _underlying.b._asDart; set b(String b) { - final alloc = ffi2.calloc; + final alloc = RustAlloc(); alloc.free(_underlying.b._bytes); final bSlice = _SliceFfi2Utf8._fromDart(b, alloc); _underlying.b = bSlice; diff --git a/feature_tests/dart/lib/BorrowedFieldsReturning.g.dart b/feature_tests/dart/lib/BorrowedFieldsReturning.g.dart index 0d39c977b..a873c69c6 100644 --- a/feature_tests/dart/lib/BorrowedFieldsReturning.g.dart +++ b/feature_tests/dart/lib/BorrowedFieldsReturning.g.dart @@ -24,7 +24,7 @@ final class BorrowedFieldsReturning { String get bytes => _underlying.bytes._asDart; set bytes(String bytes) { - final alloc = ffi2.calloc; + final alloc = RustAlloc(); alloc.free(_underlying.bytes._bytes); final bytesSlice = _SliceFfi2Utf8._fromDart(bytes, alloc); _underlying.bytes = bytesSlice; diff --git a/feature_tests/dart/lib/Float64Vec.g.dart b/feature_tests/dart/lib/Float64Vec.g.dart index 0f52e0033..f00908ed1 100644 --- a/feature_tests/dart/lib/Float64Vec.g.dart +++ b/feature_tests/dart/lib/Float64Vec.g.dart @@ -15,18 +15,36 @@ final class Float64Vec implements ffi.Finalizable { static final _finalizer = ffi.NativeFinalizer(_capi('Float64Vec_destroy')); factory Float64Vec(Float64List v) { - final alloc = ffi2.Arena(); - final vSlice = _SliceFfiDouble._fromDart(v, alloc); - final result = _Float64Vec_new(vSlice._bytes, vSlice._length); - alloc.releaseAll(); + final vSlice = _SliceFfiDouble._fromDart(v, _RustAlloc()); + final result = _Float64Vec_new_from_owned(vSlice._bytes, vSlice._length); return Float64Vec._(result); } // ignore: non_constant_identifier_names - static final _Float64Vec_new = - _capi Function(ffi.Pointer, ffi.Size)>>('Float64Vec_new') + static final _Float64Vec_new_from_owned = + _capi Function(ffi.Pointer, ffi.Size)>>('Float64Vec_new_from_owned') .asFunction Function(ffi.Pointer, int)>(isLeaf: true); + Float64List get asBoxedSlice { + final result = _Float64Vec_as_boxed_slice(_underlying); + return result._asDart; + } + + // ignore: non_constant_identifier_names + static final _Float64Vec_as_boxed_slice = + _capi)>>('Float64Vec_as_boxed_slice') + .asFunction<_SliceFfiDouble Function(ffi.Pointer)>(isLeaf: true); + + Float64List get asSlice { + final result = _Float64Vec_as_slice(_underlying); + return result._asDart; + } + + // ignore: non_constant_identifier_names + static final _Float64Vec_as_slice = + _capi)>>('Float64Vec_as_slice') + .asFunction<_SliceFfiDouble Function(ffi.Pointer)>(isLeaf: true); + void fillSlice(Float64List v) { final alloc = ffi2.Arena(); final vSlice = _SliceFfiDouble._fromDart(v, alloc); diff --git a/feature_tests/dart/lib/lib.g.dart b/feature_tests/dart/lib/lib.g.dart index fc8b5167e..fab1b93c4 100644 --- a/feature_tests/dart/lib/lib.g.dart +++ b/feature_tests/dart/lib/lib.g.dart @@ -55,6 +55,21 @@ void init(String path) => _capi = ffi.DynamicLibrary.open(path).lookup; final _callocFree = Finalizer(ffi2.calloc.free); +final class _RustAlloc implements ffi.Allocator { + @override + ffi.Pointer allocate(int byteCount, {int? alignment}) { + return _diplomat_alloc(byteCount, alignment ?? 1).cast(); + } + static final _diplomat_alloc = + _capi>('diplomat_alloc').asFunction(); + + @override + void free(ffi.Pointer pointer) { + throw "Internal error: should not deallocate in Rust memory"; + } +} + + final class _ResultInt32OpaqueUnion extends ffi.Union { @ffi.Int32() external int ok; diff --git a/feature_tests/dotnet/Lib/Generated/Float64Vec.cs b/feature_tests/dotnet/Lib/Generated/Float64Vec.cs index 2ff2158e5..0e5d1e231 100644 --- a/feature_tests/dotnet/Lib/Generated/Float64Vec.cs +++ b/feature_tests/dotnet/Lib/Generated/Float64Vec.cs @@ -45,6 +45,48 @@ public static Float64Vec New(double[] v) } } + /// + /// A Float64Vec allocated on Rust side. + /// + public static Float64Vec NewFromOwned(double[] v) + { + unsafe + { + nuint vLength = (nuint)v.Length; + fixed (double* vPtr = v) + { + Raw.Float64Vec* retVal = Raw.Float64Vec.NewFromOwned(vPtr, vLength); + return new Float64Vec(retVal); + } + } + } + + public double[] AsBoxedSlice() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("Float64Vec"); + } + Raw.double[] retVal = Raw.Float64Vec.AsBoxedSlice(_inner); + return expected named type name, found `Box<[f64]>`; + } + } + + public double[] AsSlice() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("Float64Vec"); + } + Raw.double[] retVal = Raw.Float64Vec.AsSlice(_inner); + return expected named type name, found `&'a [f64]`; + } + } + public void FillSlice(double[] v) { unsafe diff --git a/feature_tests/dotnet/Lib/Generated/RawFloat64Vec.cs b/feature_tests/dotnet/Lib/Generated/RawFloat64Vec.cs index 0870a6507..c2060ba10 100644 --- a/feature_tests/dotnet/Lib/Generated/RawFloat64Vec.cs +++ b/feature_tests/dotnet/Lib/Generated/RawFloat64Vec.cs @@ -19,6 +19,15 @@ public partial struct Float64Vec [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Float64Vec_new", ExactSpelling = true)] public static unsafe extern Float64Vec* New(double* v, nuint vSz); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Float64Vec_new_from_owned", ExactSpelling = true)] + public static unsafe extern Float64Vec* NewFromOwned(double* v, nuint vSz); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Float64Vec_as_boxed_slice", ExactSpelling = true)] + public static unsafe extern double[] AsBoxedSlice(Float64Vec* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Float64Vec_as_slice", ExactSpelling = true)] + public static unsafe extern double[] AsSlice(Float64Vec* self); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Float64Vec_fill_slice", ExactSpelling = true)] public static unsafe extern void FillSlice(Float64Vec* self, double* v, nuint vSz); diff --git a/feature_tests/js/api/Float64Vec.d.ts b/feature_tests/js/api/Float64Vec.d.ts index 15fd599af..1b585bcaa 100644 --- a/feature_tests/js/api/Float64Vec.d.ts +++ b/feature_tests/js/api/Float64Vec.d.ts @@ -7,6 +7,18 @@ export class Float64Vec { */ static new(v: Float64Array): Float64Vec; + /** + */ + static new_from_owned(v: Float64Array): Float64Vec; + + /** + */ + as_boxed_slice(): Float64Array; + + /** + */ + as_slice(): Float64Array; + /** */ fill_slice(v: Float64Array): void; diff --git a/feature_tests/js/api/Float64Vec.js b/feature_tests/js/api/Float64Vec.js index c08874e3a..c3ca40109 100644 --- a/feature_tests/js/api/Float64Vec.js +++ b/feature_tests/js/api/Float64Vec.js @@ -22,6 +22,31 @@ export class Float64Vec { return diplomat_out; } + static new_from_owned(arg_v) { + const buf_arg_v = diplomatRuntime.DiplomatBuf.slice(wasm, arg_v, 8); + return new Float64Vec(wasm.Float64Vec_new_from_owned(buf_arg_v.ptr, buf_arg_v.size), true, []); + } + + as_boxed_slice() { + return (() => { + const diplomat_receive_buffer = wasm.diplomat_alloc(8, 4); + wasm.Float64Vec_as_boxed_slice(diplomat_receive_buffer, this.underlying); + const [ptr, size] = new Uint32Array(wasm.memory.buffer, diplomat_receive_buffer, 2); + wasm.diplomat_free(diplomat_receive_buffer, 8, 4); + return new Float64Array(wasm.memory.buffer, ptr, size); + })(); + } + + as_slice() { + return (() => { + const diplomat_receive_buffer = wasm.diplomat_alloc(8, 4); + wasm.Float64Vec_as_slice(diplomat_receive_buffer, this.underlying); + const [ptr, size] = new Uint32Array(wasm.memory.buffer, diplomat_receive_buffer, 2); + wasm.diplomat_free(diplomat_receive_buffer, 8, 4); + return new Float64Array(wasm.memory.buffer, ptr, size); + })(); + } + fill_slice(arg_v) { const buf_arg_v = diplomatRuntime.DiplomatBuf.slice(wasm, arg_v, 8); wasm.Float64Vec_fill_slice(this.underlying, buf_arg_v.ptr, buf_arg_v.size); diff --git a/feature_tests/js/docs/source/slices_ffi.rst b/feature_tests/js/docs/source/slices_ffi.rst index 76ea741ca..5d430e399 100644 --- a/feature_tests/js/docs/source/slices_ffi.rst +++ b/feature_tests/js/docs/source/slices_ffi.rst @@ -7,6 +7,14 @@ - Note: ``v`` should be an ArrayBuffer or TypedArray corresponding to the slice type expected by Rust. + .. js:function:: new_from_owned(v) + - Note: ``v`` should be an ArrayBuffer or TypedArray corresponding to the slice type expected by Rust. + + + .. js:method:: as_boxed_slice() + + .. js:method:: as_slice() + .. js:method:: fill_slice(v) - Note: ``v`` should be an ArrayBuffer or TypedArray corresponding to the slice type expected by Rust. diff --git a/feature_tests/src/lib.rs b/feature_tests/src/lib.rs index 05fe3faf4..3b637ce66 100644 --- a/feature_tests/src/lib.rs +++ b/feature_tests/src/lib.rs @@ -3,6 +3,8 @@ // We're not trying to write good code here, just tests #![allow(clippy::style)] +extern crate alloc; + pub mod attrs; pub mod imports; pub mod lifetimes; diff --git a/feature_tests/src/slices.rs b/feature_tests/src/slices.rs index ecbd4e473..b0bf7ba6f 100644 --- a/feature_tests/src/slices.rs +++ b/feature_tests/src/slices.rs @@ -25,10 +25,24 @@ mod ffi { struct Float64Vec(Vec); impl Float64Vec { + #[diplomat::attr(dart, disable)] pub fn new(v: &[f64]) -> Box { Box::new(Self(v.to_vec())) } + #[diplomat::attr(dart, rename = "new")] + pub fn new_from_owned(v: Box<[f64]>) -> Box { + Box::new(Self(v.into())) + } + + pub fn as_boxed_slice(&self) -> Box<[f64]> { + self.0.clone().into() + } + + pub fn as_slice<'a>(&'a self) -> &'a [f64] { + &self.0 + } + pub fn fill_slice(&self, v: &mut [f64]) { v.copy_from_slice(&self.0) } diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 7c42ad00a..6a2ecb695 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -45,7 +45,7 @@ fn gen_params_at_boundary(param: &ast::Param, expanded_params: &mut Vec) colon_token: syn::token::Colon(Span::call_site()), ty: Box::new( parse2({ - if let ast::TypeName::PrimitiveSlice(_, ast::Mutability::Mutable, _) = + if let ast::TypeName::PrimitiveSlice(Some((_, ast::Mutability::Mutable)) | None, _) = ¶m.ty { quote! { *mut #data_type } @@ -99,14 +99,18 @@ fn gen_params_invocation(param: &ast::Param, expanded_params: &mut Vec) { Ident::new(&format!("{}_diplomat_data", param.name), Span::call_site()); let len_ident = Ident::new(&format!("{}_diplomat_len", param.name), Span::call_site()); - let tokens = if let ast::TypeName::PrimitiveSlice(_, mutability, _) = ¶m.ty { - match mutability { - ast::Mutability::Mutable => quote! { + let tokens = if let ast::TypeName::PrimitiveSlice(lm, _) = ¶m.ty + { + match lm { + Some((_, ast::Mutability::Mutable)) => quote! { unsafe { core::slice::from_raw_parts_mut(#data_ident, #len_ident) } }, - ast::Mutability::Immutable => quote! { + Some((_, ast::Mutability::Immutable)) => quote! { unsafe { core::slice::from_raw_parts(#data_ident, #len_ident) } }, + None => quote! { + unsafe { alloc::boxed::Box::from_raw(core::ptr::slice_from_raw_parts_mut(#data_ident, #len_ident)) } + } } } else { quote! {unsafe { core::slice::from_raw_parts(#data_ident, #len_ident) } } @@ -521,6 +525,25 @@ mod tests { )); } + #[test] + fn method_taking_owned_slice() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + struct Foo {} + + impl Foo { + pub fn fill_slice(s: Box<[u16]>) { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + #[test] fn mod_with_enum() { insta::assert_display_snapshot!(rustfmt_code( diff --git a/macro/src/snapshots/diplomat__tests__method_taking_owned_slice.snap b/macro/src/snapshots/diplomat__tests__method_taking_owned_slice.snap new file mode 100644 index 000000000..f0106565c --- /dev/null +++ b/macro/src/snapshots/diplomat__tests__method_taking_owned_slice.snap @@ -0,0 +1,26 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n pub fn fill_slice(s : Box < [u16] >) { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + impl Foo { + pub fn fill_slice(s: Box<[u16]>) { + unimplemented!() + } + } + use diplomat_runtime::*; + #[no_mangle] + extern "C" fn Foo_fill_slice(s_diplomat_data: *mut u16, s_diplomat_len: usize) { + Foo::fill_slice(unsafe { + alloc::boxed::Box::from_raw(core::ptr::slice_from_raw_parts_mut( + s_diplomat_data, + s_diplomat_len, + )) + }) + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box) {} +} + diff --git a/tool/src/c/runtime.h b/tool/src/c/runtime.h index de0f9c76f..9cc0abedb 100644 --- a/tool/src/c/runtime.h +++ b/tool/src/c/runtime.h @@ -40,26 +40,30 @@ typedef struct DiplomatStringView { size_t len; } DiplomatStringView; -#define MAKE_SLICE_VIEW(name, c_ty) \ +#define MAKE_SLICES(name, c_ty) \ typedef struct Diplomat##name##View { \ const c_ty* data; \ size_t len; \ - } Diplomat##name##View; + } Diplomat##name##View; \ + typedef struct Diplomat##name##Array { \ + const c_ty* data; \ + size_t len; \ + } Diplomat##name##Array; -MAKE_SLICE_VIEW(I8, int8_t) -MAKE_SLICE_VIEW(U8, uint8_t) -MAKE_SLICE_VIEW(I16, int16_t) -MAKE_SLICE_VIEW(U16, uint16_t) -MAKE_SLICE_VIEW(I32, int32_t) -MAKE_SLICE_VIEW(U32, uint32_t) -MAKE_SLICE_VIEW(I64, int64_t) -MAKE_SLICE_VIEW(U64, uint64_t) -MAKE_SLICE_VIEW(Isize, intptr_t) -MAKE_SLICE_VIEW(Usize, size_t) -MAKE_SLICE_VIEW(F32, float) -MAKE_SLICE_VIEW(F64, double) -MAKE_SLICE_VIEW(Bool, bool) -MAKE_SLICE_VIEW(Char, char32_t) +MAKE_SLICES(I8, int8_t) +MAKE_SLICES(U8, uint8_t) +MAKE_SLICES(I16, int16_t) +MAKE_SLICES(U16, uint16_t) +MAKE_SLICES(I32, int32_t) +MAKE_SLICES(U32, uint32_t) +MAKE_SLICES(I64, int64_t) +MAKE_SLICES(U64, uint64_t) +MAKE_SLICES(Isize, intptr_t) +MAKE_SLICES(Usize, size_t) +MAKE_SLICES(F32, float) +MAKE_SLICES(F64, double) +MAKE_SLICES(Bool, bool) +MAKE_SLICES(Char, char32_t) #ifdef __cplusplus diff --git a/tool/src/c/structs.rs b/tool/src/c/structs.rs index 8bd25f230..743117cfd 100644 --- a/tool/src/c/structs.rs +++ b/tool/src/c/structs.rs @@ -88,11 +88,11 @@ pub fn gen_method( ¶m.ty { write!(out, "const uint16_t* {0}_data, size_t {0}_len", param.name)?; - } else if let ast::TypeName::PrimitiveSlice(_, mutability, prim) = ¶m.ty { + } else if let ast::TypeName::PrimitiveSlice(lm, prim) = ¶m.ty { write!( out, "{0}{1}* {2}_data, size_t {2}_len", - if mutability.is_immutable() { + if matches!(lm, Some((_, ast::Mutability::Immutable))) { "const " } else { "" diff --git a/tool/src/c/types.rs b/tool/src/c/types.rs index cac28b6b6..c080ff384 100644 --- a/tool/src/c/types.rs +++ b/tool/src/c/types.rs @@ -59,13 +59,14 @@ pub fn gen_type( ast::TypeName::StrReference(_, ast::StringEncoding::UnvalidatedUtf16) => { write!(out, "DiplomatU16View")? } - ast::TypeName::PrimitiveSlice(_lt, mutability, prim) => { - if mutability.is_mutable() { - panic!("Mutable slices in structs not supported"); - } + ast::TypeName::PrimitiveSlice(lm, prim) => { let mut prim = prim.to_string(); prim.get_mut(0..1).unwrap().make_ascii_uppercase(); - write!(out, "Diplomat{prim}View")?; + match lm { + Some((_, ast::Mutability::Mutable)) => panic!("Mutable borrowed slices in structs not supported"), + Some((_, ast::Mutability::Immutable)) => write!(out, "Diplomat{prim}View")?, + None => write!(out, "Diplomat{prim}Array")? + } } ast::TypeName::Unit => write!(out, "void")?, &_ => unreachable!("unknown AST/HIR variant"), @@ -108,12 +109,12 @@ pub fn name_for_type(typ: &ast::TypeName) -> ast::Ident { ast::TypeName::StrReference(_, ast::StringEncoding::UnvalidatedUtf16) => { ast::Ident::from("str_ref16") } - ast::TypeName::PrimitiveSlice(_lt, ast::Mutability::Mutable, prim) => { - ast::Ident::from(format!("ref_mut_prim_slice_{}", c_type_for_prim(prim))) - } - ast::TypeName::PrimitiveSlice(_lt, ast::Mutability::Immutable, prim) => { + ast::TypeName::PrimitiveSlice(Some((_, ast::Mutability::Immutable)), prim) => { ast::Ident::from(format!("ref_prim_slice_{}", c_type_for_prim(prim))) } + ast::TypeName::PrimitiveSlice(_, prim) => { + ast::Ident::from(format!("ref_mut_prim_slice_{}", c_type_for_prim(prim))) + } ast::TypeName::Unit => ast::Ident::from("void"), &_ => unreachable!("unknown AST/HIR variant"), } diff --git a/tool/src/c2/formatter.rs b/tool/src/c2/formatter.rs index ade7a10f6..e99ff7db1 100644 --- a/tool/src/c2/formatter.rs +++ b/tool/src/c2/formatter.rs @@ -112,7 +112,10 @@ impl<'tcx> CFormatter<'tcx> { Type::Slice(hir::Slice::Str(_, StringEncoding::UnvalidatedUtf8)) => "str_ref8".into(), Type::Slice(hir::Slice::Str(_, StringEncoding::UnvalidatedUtf16)) => "str_ref16".into(), Type::Slice(hir::Slice::Primitive(borrow, p)) => { - let constness = borrow.mutability.if_mut_else("", "const_"); + let constness = borrow + .map(|b| b.mutability) + .unwrap_or(hir::Mutability::Mutable) + .if_mut_else("", "const_"); let prim = self.fmt_primitive_as_c(*p); format!("ref_{constness}prim_slice_{prim}").into() } diff --git a/tool/src/c2/ty.rs b/tool/src/c2/ty.rs index b8115ce0b..3118ae240 100644 --- a/tool/src/c2/ty.rs +++ b/tool/src/c2/ty.rs @@ -253,7 +253,10 @@ impl<'ccx, 'tcx: 'ccx, 'header> TyGenContext<'ccx, 'tcx, 'header> { } Type::Slice(hir::Slice::Primitive(b, p)) if !is_struct => { let prim = self.cx.formatter.fmt_primitive_as_c(*p); - let ptr_type = self.cx.formatter.fmt_ptr(&prim, b.mutability); + let ptr_type = self.cx.formatter.fmt_ptr( + &prim, + b.map(|b| b.mutability).unwrap_or(hir::Mutability::Mutable), + ); vec![ ( format!("{ptr_type}").into(), diff --git a/tool/src/cpp/conversions.rs b/tool/src/cpp/conversions.rs index 9ea3e5696..b571a31d1 100644 --- a/tool/src/cpp/conversions.rs +++ b/tool/src/cpp/conversions.rs @@ -226,7 +226,7 @@ pub fn gen_rust_to_cpp( .unwrap(); "slice".into() } - ast::TypeName::PrimitiveSlice(_lt, mutability, prim) => { + ast::TypeName::PrimitiveSlice(Some((_lt, mutability)), prim) => { assert!(mutability.is_immutable()); let raw_value_id = format!("diplomat_slice_raw_{path}"); let mut prim_caps = prim.to_string(); @@ -242,6 +242,22 @@ pub fn gen_rust_to_cpp( .unwrap(); "slice".into() } + ast::TypeName::PrimitiveSlice(None, prim) => { + let raw_value_id = format!("diplomat_slice_raw_{path}"); + let mut prim_caps = prim.to_string(); + prim_caps.get_mut(0..1).unwrap().make_ascii_uppercase(); + let span = &library_config.span.expr; + let prim = crate::c::types::c_type_for_prim(prim); + writeln!(out, "capi::Diplomat{prim_caps}View {raw_value_id} = {cpp};").unwrap(); + + // TODO: how to indicate ownership of the span? + writeln!( + out, + "{span} slice({raw_value_id}.data, {raw_value_id}.len);" + ) + .unwrap(); + "slice".into() + } ast::TypeName::Unit => cpp.to_string(), &_ => unreachable!("unknown AST/HIR variant"), } diff --git a/tool/src/cpp/types.rs b/tool/src/cpp/types.rs index abf5a6ab6..bb8b73309 100644 --- a/tool/src/cpp/types.rs +++ b/tool/src/cpp/types.rs @@ -164,19 +164,19 @@ fn gen_type_inner( write!(out, "const {}", library_config.span.expr)?; } - ast::TypeName::PrimitiveSlice(_, ast::Mutability::Mutable, prim) => { + ast::TypeName::PrimitiveSlice(Some((_, ast::Mutability::Immutable)), prim) => { write!( out, - "{}", + "const {}", library_config.span.expr, crate::c::types::c_type_for_prim(prim) )?; } - ast::TypeName::PrimitiveSlice(_, ast::Mutability::Immutable, prim) => { + ast::TypeName::PrimitiveSlice(_, prim) => { write!( out, - "const {}", + "const {}<{}>", library_config.span.expr, crate::c::types::c_type_for_prim(prim) )?; diff --git a/tool/src/cpp2/ty.rs b/tool/src/cpp2/ty.rs index 8fc240d31..123134448 100644 --- a/tool/src/cpp2/ty.rs +++ b/tool/src/cpp2/ty.rs @@ -462,7 +462,7 @@ impl<'ccx, 'tcx: 'ccx, 'header> TyGenContext<'ccx, 'tcx, 'header> { } Type::Slice(hir::Slice::Primitive(b, p)) => { let ret = self.cx.formatter.fmt_primitive_as_c(p); - let ret = self.cx.formatter.fmt_borrowed_slice(&ret, b.mutability); + let ret = self.cx.formatter.fmt_borrowed_slice(&ret, b.map(|b| b.mutability).unwrap_or(hir::Mutability::Mutable)); ret.into_owned().into() } _ => unreachable!("unknown AST/HIR variant"), @@ -657,7 +657,7 @@ impl<'ccx, 'tcx: 'ccx, 'header> TyGenContext<'ccx, 'tcx, 'header> { let span = self .cx .formatter - .fmt_borrowed_slice(&prim_name, b.mutability); + .fmt_borrowed_slice(&prim_name, b.map(|b| b.mutability).unwrap_or(hir::Mutability::Mutable)); format!("{span}({var_name}_data, {var_name}_size)").into() } _ => unreachable!("unknown AST/HIR variant"), diff --git a/tool/src/dart/mod.rs b/tool/src/dart/mod.rs index f7e53e157..12b1ef2e1 100644 --- a/tool/src/dart/mod.rs +++ b/tool/src/dart/mod.rs @@ -239,6 +239,7 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { &field.ty, name.clone(), &mut set_slice_conversions, + &mut false, ); FieldInfo { @@ -301,6 +302,7 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { } let mut slice_conversions = Vec::new(); + let mut needs_arena = false; for param in method.params.iter() { param_decls_dart.push(format!( @@ -315,6 +317,7 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { ¶m.ty, self.formatter.fmt_param_name(param.name.as_str()), &mut slice_conversions, + &mut needs_arena ); if matches!(param.ty, hir::Type::Slice(..)) { @@ -459,6 +462,7 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { dart_to_ffi_params, dart_return_expression, slice_conversions, + needs_arena, }) } @@ -594,6 +598,7 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { ty: &Type

, dart_name: Cow<'cx, str>, slice_conversions: &mut Vec>, + needs_arena: &mut bool, ) -> Cow<'cx, str> { match *ty { Type::Primitive(..) => dart_name.clone(), @@ -607,12 +612,19 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { Type::Opaque(..) | Type::Struct(..) | Type::Enum(..) => { format!("{dart_name}._underlying").into() } - Type::Slice(s) => { + Type::Slice(slice) => { let name = format!("{dart_name}Slice"); + let alloc = match slice { + hir::Slice::Primitive(None, _) => "_RustAlloc()", // yield ownership + _ => { + *needs_arena = true; + "alloc" // use the arena for a temporary allocation + } + }; slice_conversions.push( format!( - "final {name} = {}._fromDart({dart_name}, alloc);", - &self.gen_slice(&s) + "final {name} = {}._fromDart({dart_name}, {alloc});", + &self.gen_slice(&slice) ) .into(), ); @@ -942,6 +954,7 @@ struct MethodInfo<'a> { ffi_cast_return_ty: Cow<'a, str>, slice_conversions: Vec>, + needs_arena: bool, /// Dart conversion code for each parameter of the C function dart_to_ffi_params: Vec>, diff --git a/tool/src/dotnet/conversions.rs b/tool/src/dotnet/conversions.rs index 288db82cc..a3f9b00ab 100644 --- a/tool/src/dotnet/conversions.rs +++ b/tool/src/dotnet/conversions.rs @@ -80,7 +80,7 @@ pub fn to_idiomatic_object( &_ => unreachable!("unknown AST/HIR variant"), } } - other => panic!("expected named type name, found `{}`", other), + other => write!(out, "expected named type name, found `{}`", other), } } } diff --git a/tool/src/dotnet/types.rs b/tool/src/dotnet/types.rs index 5feb38b3c..a425412b6 100644 --- a/tool/src/dotnet/types.rs +++ b/tool/src/dotnet/types.rs @@ -118,12 +118,12 @@ pub fn name_for_type(typ: &ast::TypeName) -> ast::Ident { ast::TypeName::StrReference(_, ast::StringEncoding::UnvalidatedUtf16) => { ast::Ident::from("RefMutPrimSliceU16") } - ast::TypeName::PrimitiveSlice(_, ast::Mutability::Mutable, prim) => ast::Ident::from( - format!("RefMutPrimSlice{}", prim.to_string().to_upper_camel_case()), - ), - ast::TypeName::PrimitiveSlice(_, ast::Mutability::Immutable, prim) => ast::Ident::from( + ast::TypeName::PrimitiveSlice(Some((_, ast::Mutability::Immutable)), prim) => ast::Ident::from( format!("RefPrimSlice{}", prim.to_string().to_upper_camel_case()), ), + ast::TypeName::PrimitiveSlice(_, prim) => ast::Ident::from( + format!("RefMutPrimSlice{}", prim.to_string().to_upper_camel_case()), + ), ast::TypeName::Unit => ast::Ident::from("Void"), &_ => unreachable!("unknown AST/HIR variant"), } diff --git a/tool/src/js/conversions.rs b/tool/src/js/conversions.rs index 8f69aba9f..83de6124c 100644 --- a/tool/src/js/conversions.rs +++ b/tool/src/js/conversions.rs @@ -114,7 +114,7 @@ pub fn gen_value_js_to_rust<'env>( entries: &mut BTreeMap<&'env ast::NamedLifetime, Vec>>, ) { match typ { - ast::TypeName::StrReference(lifetime, ..) | ast::TypeName::PrimitiveSlice(lifetime, ..) => { + ast::TypeName::StrReference(..) | ast::TypeName::PrimitiveSlice(..) => { let param_name_buf = Argument::DiplomatBuf(param_name.clone()); // TODO: turn `gen_value_js_to_rust` into a struct and add a // `display_slice` method so we can use the `SliceKind` type here to @@ -140,15 +140,26 @@ pub fn gen_value_js_to_rust<'env>( invocation_params.push(format!("{param_name_buf}.ptr")); invocation_params.push(format!("{param_name_buf}.size")); - if let Some(named) = lifetime + let lifetime = match typ { + ast::TypeName::StrReference(lifetime, ..) => Some(lifetime), + ast::TypeName::PrimitiveSlice(Some((lifetime, _)), ..) => Some(lifetime), + ast::TypeName::PrimitiveSlice(None, ..) => None, + _ => unreachable!(), + }; + + if let Some(lifetime) = lifetime { + if let Some(named) = lifetime .as_named() .and_then(|current| borrowed_current_to_root.get(current)) - { - entries.entry(named).or_default().push(param_name_buf); - } else if lifetime == &ast::Lifetime::Static { - post_logic.push(format!("{param_name_buf}.leak();")); + { + entries.entry(named).or_default().push(param_name_buf); + } else if lifetime == &ast::Lifetime::Static { + post_logic.push(format!("{param_name_buf}.leak();")); + } else { + post_logic.push(format!("{param_name_buf}.free();")); + } } else { - post_logic.push(format!("{param_name_buf}.free();")); + // ownership is transferred to Rust, no need to leak, free, or GC } } ast::TypeName::Primitive(ast::PrimitiveType::char) => { diff --git a/tool/templates/dart/init.dart b/tool/templates/dart/init.dart index ddd1b84c6..e518fc928 100644 --- a/tool/templates/dart/init.dart +++ b/tool/templates/dart/init.dart @@ -19,4 +19,18 @@ typedef RuneList = Uint32List; late final ffi.Pointer Function(String) _capi; void init(String path) => _capi = ffi.DynamicLibrary.open(path).lookup; -final _callocFree = Finalizer(ffi2.calloc.free); \ No newline at end of file +final _callocFree = Finalizer(ffi2.calloc.free); + +final class _RustAlloc implements ffi.Allocator { + @override + ffi.Pointer allocate(int byteCount, {int? alignment}) { + return _diplomat_alloc(byteCount, alignment ?? 1).cast(); + } + static final _diplomat_alloc = + _capi>('diplomat_alloc').asFunction(); + + @override + void free(ffi.Pointer pointer) { + throw "Internal error: should not deallocate in Rust memory"; + } +} diff --git a/tool/templates/dart/method.dart.jinja b/tool/templates/dart/method.dart.jinja index f72577fc4..4a995c6a1 100644 --- a/tool/templates/dart/method.dart.jinja +++ b/tool/templates/dart/method.dart.jinja @@ -4,7 +4,7 @@ {{ m.declaration -}} {%- if !m.declaration.starts_with("static final") %} { {%- for slice_conversion in m.slice_conversions %} - {%- if loop.first %} + {%- if loop.first && m.needs_arena %} final alloc = ffi2.Arena(); {%- endif %} {{ slice_conversion }} @@ -21,7 +21,7 @@ {{ param }} {%- endfor -%} ); - {%- if !m.slice_conversions.is_empty() %} + {%- if m.needs_arena %} alloc.releaseAll(); {%- endif %} {%- match m.dart_return_expression %} diff --git a/tool/templates/dart/struct.dart.jinja b/tool/templates/dart/struct.dart.jinja index 7ff68cabe..faea8fcae 100644 --- a/tool/templates/dart/struct.dart.jinja +++ b/tool/templates/dart/struct.dart.jinja @@ -30,7 +30,7 @@ final class {{type_name}} { {{field.dart_type_name}} get {{field.name}} => {{ field.get_expression }}; set {{field.name}}({{field.dart_type_name}} {{field.name}}) { {%- if !field.set_slice_conversions.is_empty() %} - final alloc = ffi2.calloc; + final alloc = RustAlloc(); alloc.free(_underlying.{{field.name}}._bytes); {# TODO: What if the field is a struct containing a pointer? #} {%- endif %}