diff --git a/crates/bolt-lang/attribute/system/src/lib.rs b/crates/bolt-lang/attribute/system/src/lib.rs index 32d2c128..ef49a41f 100644 --- a/crates/bolt-lang/attribute/system/src/lib.rs +++ b/crates/bolt-lang/attribute/system/src/lib.rs @@ -123,6 +123,8 @@ impl VisitMut for SystemTransform { // Modify the return type of the system function to Result,*> fn visit_item_fn_mut(&mut self, item_fn: &mut ItemFn) { if item_fn.sig.ident == "execute" { + // Ensure execute has lifetimes and a fully-qualified Context + Self::inject_lifetimes_and_context(item_fn); // Modify the return type to Result> if necessary if let ReturnType::Type(_, type_box) = &item_fn.sig.output { if let Type::Path(type_path) = &**type_box { @@ -186,6 +188,84 @@ impl VisitMut for SystemTransform { } impl SystemTransform { + fn inject_lifetimes_and_context(item_fn: &mut ItemFn) { + // Add lifetimes <'a, 'b, 'c, 'info> if missing + let lifetime_idents = ["a", "b", "c", "info"]; + for name in lifetime_idents.iter() { + let exists = item_fn.sig.generics.params.iter().any(|p| match p { + syn::GenericParam::Lifetime(l) => l.lifetime.ident == *name, + _ => false, + }); + if !exists { + let lifetime: syn::Lifetime = + syn::parse_str(&format!("'{}", name)).expect("valid lifetime"); + let gp: syn::GenericParam = syn::parse_quote!(#lifetime); + item_fn.sig.generics.params.push(gp); + } + } + + // Update the first argument type from Context to Context<'a, 'b, 'c, 'info, Components<'info>> + if let Some(FnArg::Typed(pat_type)) = item_fn.sig.inputs.first_mut() { + if let Type::Path(type_path) = pat_type.ty.as_mut() { + if let Some(last_segment) = type_path.path.segments.last_mut() { + if last_segment.ident == "Context" { + // Extract Components path from existing generic args (if any) + let mut components_ty_opt: Option = None; + if let PathArguments::AngleBracketed(args) = &last_segment.arguments { + for ga in args.args.iter() { + if let GenericArgument::Type(t) = ga { + components_ty_opt = Some(t.clone()); + break; + } + } + } + + // If not found, leave early + if let Some(components_ty) = components_ty_opt { + // Ensure Components<'info> + let components_with_info: Type = match components_ty { + Type::Path(mut tp) => { + let seg = tp.path.segments.last_mut().unwrap(); + match &mut seg.arguments { + PathArguments::AngleBracketed(ab) => { + if ab.args.is_empty() { + ab.args.push(GenericArgument::Lifetime( + syn::parse_quote!('info), + )); + } + } + _ => { + seg.arguments = PathArguments::AngleBracketed( + syn::AngleBracketedGenericArguments { + colon2_token: None, + lt_token: Default::default(), + args: std::iter::once( + GenericArgument::Lifetime( + syn::parse_quote!('info), + ), + ) + .collect(), + gt_token: Default::default(), + }, + ); + } + } + Type::Path(tp) + } + other => other, + }; + + // Build new Context<'a, 'b, 'c, 'info, Components<'info>> type + let new_ty: Type = syn::parse_quote! { + Context<'a, 'b, 'c, 'info, #components_with_info> + }; + pat_type.ty = Box::new(new_ty); + } + } + } + } + } + } fn add_variadic_execute_function(content: &mut Vec) { content.push(syn::parse2(quote! { pub fn bolt_execute<'a, 'b, 'info>(ctx: Context<'a, 'b, 'info, 'info, VariadicBoltComponents<'info>>, args: Vec) -> Result>> { diff --git a/examples/escrow-funding/src/lib.rs b/examples/escrow-funding/src/lib.rs index ea8c6de1..135d4da5 100644 --- a/examples/escrow-funding/src/lib.rs +++ b/examples/escrow-funding/src/lib.rs @@ -6,10 +6,7 @@ declare_id!("4Um2d8SvyfWyLLtfu2iJMFhM77DdjjyQusEy7K3VhPkd"); #[system] pub mod escrow_funding { - pub fn execute<'info>( - ctx: Context<'_, '_, '_, 'info, Components<'info>>, - args: Args, - ) -> Result> { + pub fn execute(ctx: Context, args: Args) -> Result { let receiver = ctx.accounts.receiver.to_account_info(); let sender = ctx.sender()?.clone(); let system_program = ctx.system_program()?.clone(); diff --git a/examples/system-simple-movement/Cargo.toml b/examples/system-simple-movement/Cargo.toml index 5a48ad84..35234a1d 100644 --- a/examples/system-simple-movement/Cargo.toml +++ b/examples/system-simple-movement/Cargo.toml @@ -26,4 +26,4 @@ custom-panic = [] [dependencies] serde.workspace = true bolt-lang.workspace = true -bolt-types = { version = "0.2.4", path = "../../crates/types" } +bolt-types = { version = "0.2.5", path = "../../crates/types" }