Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
9ae1bd8
feat: allow using references in contract method's parameters
willemneal Aug 6, 2025
b5f535e
feat: add tests
willemneal Aug 6, 2025
a86e210
Merge branch 'main' into feat/ref_args
willemneal Aug 7, 2025
0ad4395
allow refs on fn args
leighmcculloch Aug 11, 2025
d71c5d9
Merge branch 'feat/ref_args' into ref-arg-support
leighmcculloch Aug 11, 2025
1f22342
undo unrelated changes
leighmcculloch Aug 11, 2025
96cd0cb
update doc
leighmcculloch Aug 11, 2025
435c5dc
fmt
leighmcculloch Aug 11, 2025
b8df882
fix mutability
leighmcculloch Aug 11, 2025
617fab5
fix mutability
leighmcculloch Aug 11, 2025
bdcab09
Merge branch 'main' into ref-arg-support
leighmcculloch Aug 18, 2025
bebfa83
Merge branch 'main' into ref-arg-support
leighmcculloch Sep 27, 2025
f73900a
Merge branch 'main' into ref-arg-support
leighmcculloch Sep 30, 2025
adac134
update test_empty3 version to 23.0.2
leighmcculloch Sep 30, 2025
c0cbdbe
update expanded
leighmcculloch Sep 30, 2025
40fd04b
Merge branch 'main' into ref-arg-support
leighmcculloch Oct 9, 2025
fc95876
refactor constructor to use ctor crate pattern
leighmcculloch Oct 13, 2025
3e93212
rename helper functions for clarity and consistency
leighmcculloch Oct 13, 2025
909a541
rename parameter from `i` to `p` in pat_unwrap_mut
leighmcculloch Oct 13, 2025
5f8941b
refactor fn_arg_make_ref to use if let instead of match
leighmcculloch Oct 13, 2025
498c1a7
remove unnecessary else branch in fn_arg_make_ref
leighmcculloch Oct 13, 2025
7569b2f
null
leighmcculloch Oct 13, 2025
da24d24
null
leighmcculloch Oct 13, 2025
13d7882
remove empty3 test contract and snapshots
leighmcculloch Oct 13, 2025
00f01df
remove test_empty3 test case and generated files
leighmcculloch Oct 13, 2025
2046b00
add lifetime parameter and mutable ref to get method
leighmcculloch Oct 13, 2025
c65f3e6
Merge branch 'main' into ref-arg-support
leighmcculloch Oct 15, 2025
37f0b06
Merge branch 'main' into ref-arg-support
leighmcculloch Oct 15, 2025
b3a6c99
Merge branch 'main' into ref-arg-support
leighmcculloch Oct 17, 2025
1d74fd7
Merge branch 'main' into ref-arg-support
leighmcculloch Oct 20, 2025
10ca49f
add test for method with reference arguments
leighmcculloch Oct 20, 2025
d5e76f8
replace empty method with exec in test generics
leighmcculloch Oct 21, 2025
04ab9d5
change contract method parameters from references to owned values
leighmcculloch Oct 21, 2025
4960ace
add test for mutable function arguments
leighmcculloch Oct 29, 2025
f269160
rename test function from test_hello to test_calc
leighmcculloch Oct 29, 2025
07b9193
rename test function from test_hello to test_calc
leighmcculloch Oct 29, 2025
88cebf8
strip mutability from generated function arguments
leighmcculloch Oct 29, 2025
432c270
refactor pat_unwrap_mut helper to strip mutability
leighmcculloch Oct 29, 2025
f066255
add test for mutable argument in contract function
leighmcculloch Oct 29, 2025
9cb08f5
Merge branch 'i1605-add-mut-arg-test' into ref-arg-support
leighmcculloch Oct 29, 2025
616dab7
disallow mutable references in contract function params
leighmcculloch Oct 29, 2025
0ca7ca0
remove commented-out mutable reference validation
leighmcculloch Oct 29, 2025
5fd0ce0
reorder contract methods in test
leighmcculloch Oct 29, 2025
28de27b
extract fn arg mutable reference validation logic
leighmcculloch Oct 29, 2025
858775a
Merge branch 'main' into ref-arg-support
leighmcculloch Oct 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion soroban-sdk-macros/src/derive_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{

fn is_muxed_address_type(arg: &FnArg) -> bool {
if let FnArg::Typed(pat_type) = arg {
if let Ok(ScSpecTypeDef::MuxedAddress) = map_type(&pat_type.ty, false, false) {
if let Ok(ScSpecTypeDef::MuxedAddress) = map_type(&pat_type.ty, true, false) {
return true;
}
}
Expand Down
9 changes: 8 additions & 1 deletion soroban-sdk-macros/src/derive_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub fn derive_pub_fn(
let allow_hash = ident == "__check_auth" && i == 0;

// Error if the type of the fn is not mappable.
if let Err(e) = map_type(&pat_ty.ty, false, allow_hash) {
if let Err(e) = map_type(&pat_ty.ty, true, allow_hash) {
errors.push(e);
}

Expand All @@ -82,7 +82,14 @@ pub fn derive_pub_fn(
ty: Box::new(Type::Verbatim(quote! { #crate_path::Val })),
});
let passthrough_call = quote! { #ident };

let call_prefix = match *pat_ty.ty {
Type::Reference(TypeReference { mutability: Some(_), .. }) => quote!(&mut),
Type::Reference(TypeReference { mutability: None, .. }) => quote!(&),
_ => quote!(),
};
let call = quote! {
#call_prefix
<_ as #crate_path::unwrap::UnwrapOptimized>::unwrap_optimized(
<_ as #crate_path::TryFromValForContractFn<#crate_path::Env, #crate_path::Val>>::try_from_val_for_contract_fn(
&env,
Expand Down
4 changes: 2 additions & 2 deletions soroban-sdk-macros/src/derive_spec_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub fn derive_fn_spec(
// signature_payload of type Bytes (32 size), to be a Hash.
let allow_hash = ident == "__check_auth" && i == 0;

match map_type(&pat_type.ty, false, allow_hash) {
match map_type(&pat_type.ty, true, allow_hash) {
Ok(type_) => {
let name = name.try_into().unwrap_or_else(|_| {
const MAX: u32 = 30;
Expand Down Expand Up @@ -118,7 +118,7 @@ pub fn derive_fn_spec(

// Prepare the output.
let spec_result = match output {
ReturnType::Type(_, ty) => vec![match map_type(ty, false, true) {
ReturnType::Type(_, ty) => vec![match map_type(ty, true, true) {
Ok(spec) => spec,
Err(e) => {
errors.push(e);
Expand Down
71 changes: 46 additions & 25 deletions soroban-sdk-macros/src/syn_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
token::Comma,
AngleBracketedGenericArguments, Attribute, GenericArgument, Path, PathArguments, PathSegment,
ReturnType, Token, TypePath,
AngleBracketedGenericArguments, Attribute, GenericArgument, PatIdent, Path, PathArguments,
PathSegment, ReturnType, Token, TypePath,
};
use syn::{
spanned::Spanned, token::And, Error, FnArg, Ident, ImplItem, ImplItemFn, ItemImpl, ItemTrait,
Expand Down Expand Up @@ -48,19 +48,46 @@ pub fn fn_arg_ident(arg: &FnArg) -> Result<Ident, Error> {
))
}

/// Returns a clone of the type from the FnArg.
/// Unwraps a reference, returning the type within the reference.
///
/// If the type is not a reference, returns the type as-is.
pub fn type_unwrap_ref(t: Type) -> Type {
match t {
Type::Reference(TypeReference { elem, .. }) => *elem,
_ => t,
}
}

/// Modifies a Pat removing any 'mut' on an Ident.
pub fn pat_unwrap_mut(i: Pat) -> Pat {
match i {
Pat::Ident(PatIdent {
attrs,
by_ref,
mutability: Some(_),
ident,
subpat,
}) => Pat::Ident(PatIdent {
attrs,
by_ref,
mutability: None,
ident,
subpat,
}),
_ => i,
}
}

/// Returns a clone of the type from the FnArg, converted into an immutable reference to the type
/// with the given lifetime.
pub fn fn_arg_ref_type(arg: &FnArg, lifetime: Option<&Lifetime>) -> Result<Type, Error> {
if let FnArg::Typed(pat_type) = arg {
if !matches!(*pat_type.ty, Type::Reference(_)) {
Ok(Type::Reference(TypeReference {
and_token: And::default(),
lifetime: lifetime.cloned(),
mutability: None,
elem: pat_type.ty.clone(),
}))
} else {
Ok((*pat_type.ty).clone())
}
Ok(Type::Reference(TypeReference {
and_token: And::default(),
lifetime: lifetime.cloned(),
mutability: None,
elem: Box::new(type_unwrap_ref(*pat_type.ty.clone())),
}))
} else {
Err(Error::new(
arg.span(),
Expand All @@ -69,25 +96,19 @@ pub fn fn_arg_ref_type(arg: &FnArg, lifetime: Option<&Lifetime>) -> Result<Type,
}
}

/// Returns a clone of FnArg with the type as a reference if the arg is a typed
/// arg and its type is not already a reference.
/// Returns a clone of FnArg, converted into an immutable reference with the given lifetime.
pub fn fn_arg_make_ref(arg: &FnArg, lifetime: Option<&Lifetime>) -> FnArg {
if let FnArg::Typed(pat_type) = arg {
if !matches!(*pat_type.ty, Type::Reference(_)) {
match arg {
FnArg::Typed(pat_type) => {
return FnArg::Typed(PatType {
attrs: pat_type.attrs.clone(),
pat: pat_type.pat.clone(),
pat: Box::new(pat_unwrap_mut(*pat_type.pat.clone())),
colon_token: pat_type.colon_token,
ty: Box::new(Type::Reference(TypeReference {
and_token: And::default(),
lifetime: lifetime.cloned(),
mutability: None,
elem: pat_type.ty.clone(),
})),
ty: Box::new(fn_arg_ref_type(arg, lifetime)),
});
}
_ => arg.clone(),
}
arg.clone()
}

pub fn fn_arg_make_into(arg: &FnArg) -> FnArg {
Expand Down
14 changes: 14 additions & 0 deletions soroban-sdk/src/tests/contract_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ impl Contract {
a + b
}

pub fn add_with_ref_arg(_e: &Env, a: i32, b: &i32) -> i32 {
a + b
}

pub fn add_with_mut_ref_arg(_e: &Env, a: i32, b: &mut i32) -> i32 {
*b += 2;
a + *b
}

pub fn add_with_mut_arg(_e: &Env, a: i32, mut b: i32) -> i32 {
b += 2;
a + b
}

pub fn add_with_unused_arg(_e: &Env, a: i32, _b: i32) -> i32 {
a + 2
}
Expand Down
20 changes: 10 additions & 10 deletions tests-expanded/test_contract_data_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,14 @@ impl soroban_sdk::testutils::ContractFunctionSet for Contract {
}
}
impl Contract {
pub fn put(e: Env, key: Symbol, val: Symbol) {
e.storage().persistent().set(&key, &val)
pub fn put(e: Env, key: &Symbol, val: &Symbol) {
e.storage().persistent().set(key, val)
}
pub fn get(e: Env, key: Symbol) -> Option<Symbol> {
e.storage().persistent().get(&key)
pub fn get(e: Env, key: &Symbol) -> Option<Symbol> {
e.storage().persistent().get(key)
}
pub fn del(e: Env, key: Symbol) {
e.storage().persistent().remove(&key)
pub fn del(e: Env, key: &Symbol) {
e.storage().persistent().remove(key)
}
}
#[doc(hidden)]
Expand Down Expand Up @@ -460,13 +460,13 @@ pub mod __Contract__put {
#[allow(deprecated)]
&<super::Contract>::put(
env.clone(),
<_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
&<_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
<_ as soroban_sdk::TryFromValForContractFn<
soroban_sdk::Env,
soroban_sdk::Val,
>>::try_from_val_for_contract_fn(&env, &arg_0),
),
<_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
&<_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
<_ as soroban_sdk::TryFromValForContractFn<
soroban_sdk::Env,
soroban_sdk::Val,
Expand Down Expand Up @@ -510,7 +510,7 @@ pub mod __Contract__get {
#[allow(deprecated)]
&<super::Contract>::get(
env.clone(),
<_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
&<_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
<_ as soroban_sdk::TryFromValForContractFn<
soroban_sdk::Env,
soroban_sdk::Val,
Expand Down Expand Up @@ -551,7 +551,7 @@ pub mod __Contract__del {
#[allow(deprecated)]
&<super::Contract>::del(
env.clone(),
<_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
&<_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
<_ as soroban_sdk::TryFromValForContractFn<
soroban_sdk::Env,
soroban_sdk::Val,
Expand Down
20 changes: 10 additions & 10 deletions tests-expanded/test_contract_data_wasm32v1-none.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ impl<'a> ContractClient<'a> {
}
}
impl Contract {
pub fn put(e: Env, key: Symbol, val: Symbol) {
e.storage().persistent().set(&key, &val)
pub fn put(e: Env, key: &Symbol, val: &Symbol) {
e.storage().persistent().set(key, val)
}
pub fn get(e: Env, key: Symbol) -> Option<Symbol> {
e.storage().persistent().get(&key)
pub fn get(e: Env, key: &Symbol) -> Option<Symbol> {
e.storage().persistent().get(key)
}
pub fn del(e: Env, key: Symbol) {
e.storage().persistent().remove(&key)
pub fn del(e: Env, key: &Symbol) {
e.storage().persistent().remove(key)
}
}
#[doc(hidden)]
Expand Down Expand Up @@ -222,13 +222,13 @@ pub mod __Contract__put {
#[allow(deprecated)]
&<super::Contract>::put(
env.clone(),
<_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
&<_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
<_ as soroban_sdk::TryFromValForContractFn<
soroban_sdk::Env,
soroban_sdk::Val,
>>::try_from_val_for_contract_fn(&env, &arg_0),
),
<_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
&<_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
<_ as soroban_sdk::TryFromValForContractFn<
soroban_sdk::Env,
soroban_sdk::Val,
Expand Down Expand Up @@ -259,7 +259,7 @@ pub mod __Contract__get {
#[allow(deprecated)]
&<super::Contract>::get(
env.clone(),
<_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
&<_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
<_ as soroban_sdk::TryFromValForContractFn<
soroban_sdk::Env,
soroban_sdk::Val,
Expand Down Expand Up @@ -287,7 +287,7 @@ pub mod __Contract__del {
#[allow(deprecated)]
&<super::Contract>::del(
env.clone(),
<_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
&<_ as soroban_sdk::unwrap::UnwrapOptimized>::unwrap_optimized(
<_ as soroban_sdk::TryFromValForContractFn<
soroban_sdk::Env,
soroban_sdk::Val,
Expand Down
Loading
Loading