Skip to content

Commit

Permalink
Support boxed dyn trait in return position
Browse files Browse the repository at this point in the history
  • Loading branch information
avl committed Oct 12, 2024
1 parent 84cb199 commit 8673864
Show file tree
Hide file tree
Showing 11 changed files with 243 additions and 21 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ See the docs for more information, including schema-versioning: https://docs.rs/

# Changelog

## 0.17.12

Make savefile-abi support Result containing boxed dyn traits in method call return position.
I.e, this signature is now possible with savefile-abi:
```
fn return_boxed_closure_result(&self) -> Result<Box<dyn Fn() -> u32>,()>;
```
This is a special case, dyn traits are still not possible in arbitrary positions. This particular
case is important in order to be able to have fallible functions returning boxed dyn traits.

## 0.17.11

Support Sync + Send-bounds for dyn Fn parameters, in savefile-abi. Also, reduce
Expand Down
6 changes: 3 additions & 3 deletions savefile-abi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "savefile-abi"
version = "0.17.11"
version = "0.17.12"
edition = "2021"
authors = ["Anders Musikka <[email protected]>"]
documentation = "https://docs.rs/savefile-abi/"
Expand All @@ -17,8 +17,8 @@ keywords = ["dylib", "dlopen", "ffi"]
license = "MIT/Apache-2.0"

[dependencies]
savefile = { path="../savefile", version = "=0.17.11" }
savefile-derive = { path="../savefile-derive", version = "=0.17.11" }
savefile = { path="../savefile", version = "=0.17.12" }
savefile-derive = { path="../savefile-derive", version = "=0.17.12" }
byteorder = "1.4"
libloading = "0.8"

Expand Down
2 changes: 1 addition & 1 deletion savefile-derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "savefile-derive"
version = "0.17.11"
version = "0.17.12"
authors = ["Anders Musikka <[email protected]>"]
repository = "https://github.com/avl/savefile"
rust-version = "1.74"
Expand Down
5 changes: 3 additions & 2 deletions savefile-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ pub fn savefile_abi_exportable(
let uses = quote_spanned! { defspan =>
extern crate savefile;
extern crate savefile_abi;
use savefile::prelude::{Packed, Schema, SchemaPrimitive, WithSchema, WithSchemaContext, get_schema, Serializer, Serialize, Deserializer, Deserialize, SavefileError, deserialize_slice_as_vec, ReadBytesExt,LittleEndian,AbiMethodArgument, AbiMethod, AbiMethodInfo,AbiTraitDefinition};
use savefile::prelude::{Packed, Schema, SchemaPrimitive, WithSchema, WithSchemaContext, get_schema, get_result_schema, Serializer, Serialize, Deserializer, Deserialize, SavefileError, deserialize_slice_as_vec, ReadBytesExt,LittleEndian,AbiMethodArgument, AbiMethod, AbiMethodInfo,AbiTraitDefinition};
use savefile_abi::{parse_return_value_impl,abi_result_receiver,abi_boxed_trait_receiver, FlexBuffer, AbiExportable, TraitObject, PackagedTraitObject, Owning, AbiErrorMsg, RawAbiCallResult, AbiConnection, AbiConnectionMethod, AbiProtocol, abi_entry_light};
use std::collections::HashMap;
use std::mem::MaybeUninit;
Expand Down Expand Up @@ -508,7 +508,7 @@ pub fn savefile_abi_exportable(
#version
}

fn call(trait_object: TraitObject, method_number: u16, effective_version:u32, compatibility_mask: u64, data: &[u8], abi_result: *mut (), receiver: unsafe extern "C" fn(outcome: *const RawAbiCallResult, result_receiver: *mut ()/*Result<T,SaveFileError>>*/)) -> Result<(),SavefileError> {
fn call(trait_object: TraitObject, method_number: u16, effective_version:u32, compatibility_mask: u64, data: &[u8], abi_result: *mut (), __savefile_internal_receiver: unsafe extern "C" fn(outcome: *const RawAbiCallResult, result_receiver: *mut ()/*Result<T,SaveFileError>>*/)) -> Result<(),SavefileError> {

let mut cursor = Cursor::new(data);

Expand Down Expand Up @@ -539,6 +539,7 @@ pub fn savefile_abi_exportable(
let extra_definitions: Vec<_> = extra_definitions.values().map(|(_, x)| x).collect();
let expanded = quote! {
#[allow(clippy::double_comparisons)]
#[allow(unused_variables)]
#[allow(clippy::needless_late_init)]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[allow(non_upper_case_globals)]
Expand Down
158 changes: 152 additions & 6 deletions savefile-derive/src/savefile_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,14 @@ fn emit_closure_helpers(
return names;
}

#[derive(Debug)]
pub(crate) enum ArgType {
PlainData(Type),
Reference(Box<ArgType>, bool /*ismut (only traits objects can be mut here)*/),
Reference(Box<ArgType>, bool /*ismut (only trait objects can be mut here)*/),
Str(bool /*static*/),
Boxed(Box<ArgType>),
Slice(Box<ArgType>),
Result(Box<ArgType>, Box<ArgType>),
Trait(Ident, bool /*ismut self*/),
Fn(
TokenStream, /*full closure definition (e.g "Fn(u32)->u16")*/
Expand Down Expand Up @@ -207,6 +209,14 @@ pub(crate) fn parse_box_type(
true,
is_mut_ref,
) {
ArgType::Result(_,_) => {
abort!(
first_gen_arg.span(),
"{}. Savefile does not support boxed results. Try boxing the contents of the result instead. I.e, instead of Box<Result<A,B>>, try Result<Box<A>,Box<B>>. Type encountered: {}",
location,
typ.to_token_stream()
)
}
ArgType::Boxed(_) => {
abort!(
first_gen_arg.span(),
Expand Down Expand Up @@ -335,7 +345,7 @@ fn parse_type(
if is_mut_ref {
abort!(
typ.span(),
"{}: Mutable refernces are not supported by savefile-abi, except for FnMut-trait objects. {}",
"{}: Mutable references are not supported by savefile-abi, except for FnMut-trait objects. {}",
location,
typ.to_token_stream()
);
Expand Down Expand Up @@ -400,7 +410,7 @@ fn parse_type(
if bound.ident == "FnOnce" {
abort!(
bound.ident.span(),
"{}, FnOnce is not supported. Maybe you can use FnMut instead?",
"{}, FnOnce is presently not supported by savefile-abi. Maybe you can use FnMut instead?",
location,
);
}
Expand Down Expand Up @@ -470,6 +480,62 @@ fn parse_type(
is_reference,
is_mut_ref,
);
} else if last_seg.ident == "Result" && is_return_value {
if path.path.segments.len() != 1 {
abort!(path.path.segments.span(), "Savefile does not support types named 'Result', unless they are the standard type Result, and it must be specified as 'Result', without any namespace");
}
if is_reference {
abort!(last_seg.ident.span(), "Savefile does not presently support reference to Result in return position. Consider removing the '&'.");
}

match &last_seg.arguments {
PathArguments::Parenthesized(_) |
PathArguments::None => {
abort!(last_seg.arguments.span(), "Savefile does not support types named 'Result', unless they are the standard type Result, and it must be specified as 'Result<A,B>', with two type parameters within angle-brackets. Found not type arguments.");
}
PathArguments::AngleBracketed(params) => {
let argvec: Vec<_> = params.args.iter().collect();
if argvec.len() != 2 {
abort!(last_seg.arguments.span(), "Savefile does not support types named 'Result', unless they are the standard type Result, and it must be specified as 'Result<A,B>', with two type parameters within angle-brackets. Got {} type arguments", argvec.len());
}
let mut argtypes = vec![];
for arg in argvec {
match arg {
GenericArgument::Type(argtyp) => {
argtypes.push(Box::new(
parse_type(
version,
arg_name,
argtyp,
method_name,
is_return_value,
&mut *name_generator,
extra_definitions,
is_reference,
is_mut_ref,
)));
}
GenericArgument::Lifetime(_) => {
abort!(arg.span(), "Savefile does not support lifetime specifications.");
}
GenericArgument::Const(_) => {
abort!(arg.span(), "Savefile does not support const in this location.");
}
GenericArgument::Binding(_) => {
abort!(arg.span(), "Savefile does not support the syntax expressed here.");
}
GenericArgument::Constraint(_) => {
abort!(arg.span(), "Savefile does not support constraints at this position.");
}
}
}
let mut i = argtypes.into_iter();
let oktype = i.next().unwrap();

let errtype = i.next().unwrap();
return ArgType::Result(oktype, errtype);
}
}
} else {
rawtype = typ;
}
Expand Down Expand Up @@ -528,7 +594,7 @@ impl ArgType {
version: u32,
arg_index: Option<usize>,
arg_orig_name: &str,
arg_name: &TokenStream,
arg_name: &TokenStream, //always just 'arg_orig_name'
nesting_level: u32,
take_ownership: bool,
extra_definitions: &mut HashMap<FnWrapperKey, (ClosureWrapperNames, TokenStream)>,
Expand Down Expand Up @@ -583,6 +649,7 @@ impl ArgType {
ArgType::Str(_) => None,
ArgType::Reference(..) => None,
ArgType::Slice(_) => None,
ArgType::Result(_,_) => None,
};

let (mutsymbol, read_raw_ptr) = if *is_mut {
Expand Down Expand Up @@ -888,6 +955,85 @@ impl ArgType {
known_size_align_of_pointer1: None,
}
}
ArgType::Result(ok_type, err_type) => {
let ok_instruction = ok_type.get_instruction(
version,
arg_index,
"okval",
&quote!( okval ),
nesting_level + 1,
true,
extra_definitions,
);
let err_instruction = err_type.get_instruction(
version,
arg_index,
"errval",
&quote!( errval ),
nesting_level + 1,
true,
extra_definitions,
);

let TypeInstruction {
callee_trampoline_variable_deserializer1: ok_callee_trampoline_variable_deserializer1,
caller_arg_serializer1: ok_caller_arg_serializer1,
deserialized_type: ok_deserialized_type,
callee_trampoline_temp_variable_declaration1: ok_callee_trampoline_temp_variable_declaration1,
caller_arg_serializer_temp1: ok_caller_arg_serializer_temp1,
schema: ok_schema,
..
} = ok_instruction;

let TypeInstruction {
callee_trampoline_variable_deserializer1: err_callee_trampoline_variable_deserializer1,
caller_arg_serializer1: err_caller_arg_serializer1,
deserialized_type: err_deserialized_type,
callee_trampoline_temp_variable_declaration1: err_callee_trampoline_temp_variable_declaration1,
caller_arg_serializer_temp1: err_caller_arg_serializer_temp1,
schema: err_schema,
..
} = err_instruction;

TypeInstruction {
deserialized_type: quote! { Result<#ok_deserialized_type, #err_deserialized_type> },
callee_trampoline_temp_variable_declaration1: quote! {
#ok_callee_trampoline_temp_variable_declaration1;
#err_callee_trampoline_temp_variable_declaration1;
},
callee_trampoline_variable_deserializer1: quote! {
if deserializer.read_bool()? {
Ok(#ok_callee_trampoline_variable_deserializer1)
} else {
Err(#err_callee_trampoline_variable_deserializer1)
}
},
caller_arg_serializer_temp1: quote! {
#ok_caller_arg_serializer_temp1;
#err_caller_arg_serializer_temp1;
},
caller_arg_serializer1: quote! {
match #arg_name {
Ok(okval) => {
serializer.write_bool(true)?;
#ok_caller_arg_serializer1
},
Err(errval) => {
serializer.write_bool(false)?;
#err_caller_arg_serializer1
}
}
},
schema: quote!(
get_result_schema(#ok_schema, #err_schema)
),
arg_type1: quote!(
Result<#ok_deserialized_type, #err_deserialized_type>
),
known_size_align1: None,
known_size_align_of_pointer1: None,
}
}
}
}
}
Expand Down Expand Up @@ -1188,12 +1334,12 @@ pub(super) fn generate_method_definitions(
{
Ok(()) => {
let outcome = RawAbiCallResult::Success {data: #data_as_ptr, len: #data_length};
unsafe { receiver(&outcome as *const _, abi_result) }
unsafe { __savefile_internal_receiver(&outcome as *const _, abi_result) }
}
Err(err) => {
let err_str = format!("{:?}", err);
let outcome = RawAbiCallResult::AbiError(AbiErrorMsg{error_msg_utf8: err_str.as_ptr(), len: err_str.len()});
unsafe { receiver(&outcome as *const _, abi_result) }
unsafe { __savefile_internal_receiver(&outcome as *const _, abi_result) }
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion savefile-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ nightly=["savefile/nightly"]

[dependencies]
savefile = { path = "../savefile", features = ["size_sanity_checks", "encryption", "compression","bit-set","bit-vec","rustc-hash","serde_derive", "quickcheck", "nalgebra"]}
savefile-derive = { path = "../savefile-derive", version = "=0.17.11" }
savefile-derive = { path = "../savefile-derive", version = "=0.17.12" }
savefile-abi = { path = "../savefile-abi" }
bit-vec = "0.8"
arrayvec="0.7"
Expand Down
Loading

0 comments on commit 8673864

Please sign in to comment.