diff --git a/meld-core/src/adapter/fact.rs b/meld-core/src/adapter/fact.rs index c6280fc..515c477 100644 --- a/meld-core/src/adapter/fact.rs +++ b/meld-core/src/adapter/fact.rs @@ -77,6 +77,22 @@ fn emit_resource_borrow_phase0(func: &mut Function, transfers: &[super::Resource } } +/// Emit Phase R: convert own results via [resource-new] for 3-component chains. +/// Results are in locals starting at `result_base`. For each result that needs +/// conversion, load from local, call resource.new, store back. +fn emit_resource_new_results( + func: &mut Function, + transfers: &[super::ResourceOwnResultTransfer], + result_base: u32, +) { + for t in transfers { + let local_idx = result_base + t.position; + func.instruction(&Instruction::LocalGet(local_idx)); + func.instruction(&Instruction::Call(t.new_func)); + func.instruction(&Instruction::LocalSet(local_idx)); + } +} + /// FACT-style adapter generator pub struct FactStyleGenerator { #[allow(dead_code)] @@ -218,8 +234,6 @@ impl FactStyleGenerator { // function calls from_handle/resource.rep internally, so the adapter // must NOT convert them (that would cause double conversion). // - // Results are never converted — own results have resource.new called - // by the callee's core function, and borrows cannot appear in results. for op in &site.requirements.resource_params { if op.is_owned { continue; // own: callee handles conversion internally @@ -306,6 +320,37 @@ impl FactStyleGenerator { } } + // Resolve own results that need [resource-new] conversion. + // Only for 3-component chains where callee doesn't define the resource. + // When the callee defines it, the P2 wrapper handles lift/lower. + for op in &site.requirements.resource_results { + if !op.is_owned || op.callee_defines_resource { + continue; + } + let resource_name = op + .import_field + .strip_prefix("[resource-new]") + .unwrap_or(&op.import_field); + let new_func = merged + .resource_new_by_component + .get(&(site.from_component, resource_name.to_string())) + .copied() + .or_else(|| { + resource_new_imports + .get(&(op.import_module.clone(), op.import_field.clone())) + .copied() + }); + if let Some(new_func) = new_func { + options + .resource_new_calls + .push(super::ResourceOwnResultTransfer { + position: op.flat_idx, + byte_offset: op.byte_offset, + new_func, + }); + } + } + options } @@ -332,11 +377,13 @@ impl FactStyleGenerator { let has_post_return = options.callee_post_return.is_some(); let has_resource_ops = !options.resource_rep_calls.is_empty(); + let has_result_resource_ops = !options.resource_new_calls.is_empty(); + let needs_result_locals = (has_post_return || has_result_resource_ops) && result_count > 0; - if has_resource_ops || (has_post_return && result_count > 0) { + if has_resource_ops || has_result_resource_ops || (has_post_return && result_count > 0) { let mut locals: Vec<(u32, wasm_encoder::ValType)> = Vec::new(); let result_base = param_count as u32; - if has_post_return && result_count > 0 { + if needs_result_locals { locals.extend(result_types.iter().map(|t| (1u32, *t))); } let mut func = Function::new(locals); @@ -349,16 +396,22 @@ impl FactStyleGenerator { } func.instruction(&Instruction::Call(target_func)); - if has_post_return && result_count > 0 { + if needs_result_locals { // Save results to locals (pop in reverse order) for i in (0..result_count).rev() { func.instruction(&Instruction::LocalSet(result_base + i as u32)); } + + // Phase R: Convert own results via resource.new + emit_resource_new_results(&mut func, &options.resource_new_calls, result_base); + // Call post-return with saved results - for i in 0..result_count { - func.instruction(&Instruction::LocalGet(result_base + i as u32)); + if has_post_return { + for i in 0..result_count { + func.instruction(&Instruction::LocalGet(result_base + i as u32)); + } + func.instruction(&Instruction::Call(options.callee_post_return.unwrap())); } - func.instruction(&Instruction::Call(options.callee_post_return.unwrap())); // Push saved results back onto stack for i in 0..result_count { func.instruction(&Instruction::LocalGet(result_base + i as u32)); diff --git a/meld-core/src/adapter/mod.rs b/meld-core/src/adapter/mod.rs index 62a7b5a..d6a2f7d 100644 --- a/meld-core/src/adapter/mod.rs +++ b/meld-core/src/adapter/mod.rs @@ -138,6 +138,8 @@ pub struct AdapterOptions { /// /// `own` params are never converted (callee calls from_handle internally). pub resource_rep_calls: Vec, + /// Resource own results needing rep→handle conversion (3-component chains). + pub resource_new_calls: Vec, } /// Describes how to transfer a `borrow` handle across an adapter boundary. @@ -153,6 +155,17 @@ pub struct ResourceBorrowTransfer { pub new_func: Option, } +/// Describes how to convert an `own` result via `[resource-new]`. +#[derive(Debug, Clone)] +pub struct ResourceOwnResultTransfer { + /// Flat result index (non-retptr path) + pub position: u32, + /// Byte offset in return area (retptr path) + pub byte_offset: u32, + /// Merged function index of `[resource-new]` + pub new_func: u32, +} + impl Default for AdapterOptions { fn default() -> Self { Self { @@ -165,6 +178,7 @@ impl Default for AdapterOptions { returns_pointer_pair: false, callee_post_return: None, resource_rep_calls: Vec::new(), + resource_new_calls: Vec::new(), } } }