Skip to content
Prev Previous commit
Next Next commit
generic return type based on const eval
  • Loading branch information
SkymanOne committed Dec 21, 2022
commit d58e571a0489f7938cd517351949e3fa1f1c8a54
55 changes: 29 additions & 26 deletions crates/ink/codegen/src/generator/chain_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,60 +74,63 @@ impl ChainExtension<'_> {

let handle_status = method.handle_status();

let error_code_handling = if handle_status {
let handle_status_token = if handle_status {
quote_spanned!(span=>
.handle_error_code::<#error_code>()
true
)
} else {
quote_spanned!(span=>
.ignore_error_code()
false
)
};

let returned_type = if handle_status {
let error_code_handling = if handle_status {
quote_spanned!(span=>
::core::result::Result<#output_type, #error_code>
.handle_error_code::<#error_code>()
)
} else {
quote_spanned!(span=>
#output_type
.ignore_error_code()
)
};

let return_type = quote_spanned!(span =>
<::ink::ValueReturned as ::ink::Output<{ ::ink::is_result_type!(#output_type) }, #handle_status_token, #output_type, #error_code>>::ReturnType
);

// we not only need to check if handle status is set to true to enable this type bound
let where_output_impls_from_error_code = Some(quote_spanned!(span=>
<#output_type as ::ink::IsResultType>::Err: ::core::convert::From<#error_code>,
<#return_type as ::ink::IsResultType>::Err: ::core::convert::From<#error_code>,
)).filter(|_| handle_status);

quote_spanned!(span=>
const #ident : #ident = if ::ink::is_result_type!(#output_type) {
#( #attrs )*
#[inline]
pub fn #ident(self, #inputs) -> #returned_type
pub fn #ident(self, #inputs) -> #return_type
where
#output_type: ::ink::IsResultType,
#where_output_impls_from_error_code
{
::ink::env::chain_extension::ChainExtensionMethod::build(#func_id)
.input::<#compound_input_type>()
.output_result::<
<#output_type as ::ink::IsResultType>::Ok,
<#output_type as ::ink::IsResultType>::Err,
>()
#error_code_handling
.call(&#compound_input_bindings)
}
} else {
#( #attrs )*
#[inline]
pub fn #ident(self, #inputs) -> #output_type
{
::ink::env::chain_extension::ChainExtensionMethod::build(#func_id)
let is_result = ::ink::is_result_type!(#output_type);
if is_result {
println!("Return type is of Result");
::ink::env::chain_extension::ChainExtensionMethod::build(#func_id)
.input::<#compound_input_type>()
//TODO: rewrite as a single method
.output_result::<
<#output_type as ::ink::IsResultType>::Ok,
<#output_type as ::ink::IsResultType>::Err,
>()
#error_code_handling
.call(&#compound_input_bindings)
} else {
println!("Return type is NOT of Result");
::ink::env::chain_extension::ChainExtensionMethod::build(#func_id)
.input::<#compound_input_type>()
.output::<#output_type>()
#error_code_handling
.call(&#compound_input_bindings)
}
}
};
)
}
}
Expand Down
32 changes: 29 additions & 3 deletions crates/ink/src/chain_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,46 @@ pub trait ChainExtension {
/// Used to check at compile time if the chain extension method return type
/// is a `Result` type using the type system instead of the syntactic structure.
#[doc(hidden)]
pub trait IsResultType: private::Sealed {
pub trait IsResultType: private::IsResultSealed {
/// The `T` type of the `Result<T, E>`.
type Ok;
/// The `E` type of the `Result<T, E>`.
type Err;
}

impl<T, E> private::Sealed for Result<T, E> {}
impl<T, E> private::IsResultSealed for Result<T, E> {}
impl<T, E> IsResultType for Result<T, E> {
type Ok = T;
type Err = E;
}

pub trait Output<const IS_RESULT: bool, const HANDLE_STATUS: bool, T, E>:
private::OutputSealed
{
type ReturnType;
}

pub struct ValueReturned;
impl private::OutputSealed for ValueReturned {}

impl<T, E> Output<false, false, T, E> for ValueReturned {
type ReturnType = T;
}

impl<T, E> Output<false, true, T, E> for ValueReturned {
type ReturnType = core::result::Result<T, E>;
}

impl<T, E> Output<true, false, T, E> for ValueReturned {
type ReturnType = T;
}

impl<T, E> Output<true, true, T, E> for ValueReturned {
type ReturnType = T;
}

mod private {
/// Seals the `IsResultType` trait so that it cannot be implemented outside this module.
pub trait Sealed {}
pub trait IsResultSealed {}
pub trait OutputSealed {}
}
2 changes: 2 additions & 0 deletions crates/ink/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ pub use self::{
chain_extension::{
ChainExtensionInstance,
IsResultType,
Output,
ValueReturned,
},
contract_ref::ToAccountId,
env_access::EnvAccess,
Expand Down