diff --git a/meld-core/src/adapter/fact.rs b/meld-core/src/adapter/fact.rs index 231d36e..c6280fc 100644 --- a/meld-core/src/adapter/fact.rs +++ b/meld-core/src/adapter/fact.rs @@ -220,14 +220,6 @@ impl FactStyleGenerator { // // Results are never converted — own results have resource.new called // by the callee's core function, and borrows cannot appear in results. - // Build caller resource params index by flat_idx for quick lookup - let caller_ops: std::collections::HashMap = site - .requirements - .caller_resource_params - .iter() - .map(|op| (op.flat_idx, op)) - .collect(); - for op in &site.requirements.resource_params { if op.is_owned { continue; // own: callee handles conversion internally @@ -249,51 +241,66 @@ impl FactStyleGenerator { } } else { // 3-component case: callee doesn't define the resource. - // Use caller's [resource-rep] to convert handle → rep, - // then callee's [resource-new] to create handle in callee's table. - // - // Find the caller's [resource-rep] by matching the resource name - // from the callee's import field (e.g., "float" from "[resource-rep]float"). + // Use caller's [resource-rep] + callee's [resource-new]. let resource_name = op .import_field .strip_prefix("[resource-rep]") .unwrap_or(&op.import_field); - // Look for a [resource-rep] import from a DIFFERENT module than callee's - let caller_rep = resource_rep_imports - .iter() - .find(|((module, field), _)| { - field.ends_with(resource_name) - && field.starts_with("[resource-rep]") - && *module != op.import_module + // Primary: per-component map from MergedModule + let caller_rep_func = merged + .resource_rep_by_component + .get(&(site.from_component, resource_name.to_string())) + .copied() + .or_else(|| { + // Fallback: find any [resource-rep] for this resource name + // that isn't the callee's (different component index). + merged + .resource_rep_by_component + .iter() + .find(|((comp, rn), _)| { + rn == resource_name && *comp != site.to_component + }) + .map(|(_, &idx)| idx) }) .or_else(|| { - // Fallback: try caller_resource_params if available - caller_ops.get(&op.flat_idx).and_then(|caller_op| { - resource_rep_imports.get_key_value(&( - caller_op.import_module.clone(), - caller_op.import_field.clone(), - )) - }) + // Last resort: flat map with different module heuristic + resource_rep_imports + .iter() + .find(|((module, field), _)| { + field.ends_with(resource_name) + && field.starts_with("[resource-rep]") + && *module != op.import_module + }) + .map(|(_, &idx)| idx) + }); + + let callee_new_func = merged + .resource_new_by_component + .get(&(site.to_component, resource_name.to_string())) + .copied() + .or_else(|| { + let new_field = op.import_field.replace("[resource-rep]", "[resource-new]"); + resource_new_imports + .get(&(op.import_module.clone(), new_field)) + .copied() }); - if let Some((_, &rep_func)) = caller_rep { - let new_field = op.import_field.replace("[resource-rep]", "[resource-new]"); - let new_func = resource_new_imports - .get(&(op.import_module.clone(), new_field)) - .copied(); + if let Some(rep_func) = caller_rep_func { options .resource_rep_calls .push(super::ResourceBorrowTransfer { param_idx: op.flat_idx, rep_func, - new_func, + new_func: callee_new_func, }); } else { - log::debug!( - "No caller resource rep found for {} at flat_idx {}", + log::warn!( + "3-component borrow: no caller [resource-rep] for '{}' \ + (from_comp={}, to_comp={})", resource_name, - op.flat_idx + site.from_component, + site.to_component, ); } } diff --git a/meld-core/src/merger.rs b/meld-core/src/merger.rs index 102b50a..04c6cc5 100644 --- a/meld-core/src/merger.rs +++ b/meld-core/src/merger.rs @@ -121,6 +121,14 @@ pub struct MergedModule { /// of the component's cabi_realloc. Used by component_wrap to select the /// correct CanonicalOption::Realloc(N) per import. pub import_realloc_indices: Vec>, + + /// Maps (component_idx, resource_name) → merged function index for [resource-rep]. + /// Used by adapter generation to find the correct component's [resource-rep] + /// in multi-component chains where multiple components have the same resource. + pub resource_rep_by_component: HashMap<(usize, String), u32>, + + /// Maps (component_idx, resource_name) → merged function index for [resource-new]. + pub resource_new_by_component: HashMap<(usize, String), u32>, } /// Function type in merged module @@ -446,6 +454,8 @@ impl Merger { import_counts, import_memory_indices: Vec::new(), import_realloc_indices: Vec::new(), + resource_rep_by_component: HashMap::new(), + resource_new_by_component: HashMap::new(), }; // Process components in topological order @@ -1356,9 +1366,37 @@ impl Merger { unresolved.field_name.clone(), )); if !is_type_mismatch && !emitted_func.insert(dedup_key.clone()) { - // Duplicate with matching type — skip emitting. Position - // was already handled by compute_unresolved_import_assignments - // using the same dedup_key → same position logic. + // Duplicate with matching type — skip emitting. + // Still record per-component resource tracking: find the + // func index already assigned to this resource name. + let eff_field = &dedup_key.1; + if let Some(rn) = eff_field.strip_prefix("[resource-rep]") { + if let Some(&idx) = + merged.resource_rep_by_component.values().find(|&&idx| { + merged.imports.get(idx as usize).is_some_and(|imp| { + imp.name.starts_with("[resource-rep]") + && imp.name.ends_with(rn) + }) + }) + { + merged + .resource_rep_by_component + .insert((unresolved.component_idx, rn.to_string()), idx); + } + } else if let Some(rn) = eff_field.strip_prefix("[resource-new]") { + if let Some(&idx) = + merged.resource_new_by_component.values().find(|&&idx| { + merged.imports.get(idx as usize).is_some_and(|imp| { + imp.name.starts_with("[resource-new]") + && imp.name.ends_with(rn) + }) + }) + { + merged + .resource_new_by_component + .insert((unresolved.component_idx, rn.to_string()), idx); + } + } continue; } @@ -1414,6 +1452,19 @@ impl Merger { name, entity_type: EntityType::Function(new_type_idx), }); + + // Track per-component resource import indices. + let merged_func_idx = func_position - 1; + let eff_field = &dedup_key.1; + if let Some(rn) = eff_field.strip_prefix("[resource-rep]") { + merged + .resource_rep_by_component + .insert((unresolved.component_idx, rn.to_string()), merged_func_idx); + } else if let Some(rn) = eff_field.strip_prefix("[resource-new]") { + merged + .resource_new_by_component + .insert((unresolved.component_idx, rn.to_string()), merged_func_idx); + } } ImportKind::Table(t) => { if !emitted_table.insert(dedup_key.clone()) { @@ -2455,6 +2506,8 @@ mod tests { import_counts: ImportCounts::default(), import_memory_indices: Vec::new(), import_realloc_indices: Vec::new(), + resource_rep_by_component: HashMap::new(), + resource_new_by_component: HashMap::new(), }; // Simulate multi-memory merging for module A (comp 0, mod 0) diff --git a/meld-core/src/resolver.rs b/meld-core/src/resolver.rs index 44cc410..a7c603a 100644 --- a/meld-core/src/resolver.rs +++ b/meld-core/src/resolver.rs @@ -1053,6 +1053,13 @@ impl Resolver { site.to_component, )); } + // For 3-component chains: synthesize callee's [resource-new]. + for op in &site.requirements.resource_params { + if !op.is_owned && !op.callee_defines_resource { + let new_field = op.import_field.replace("[resource-rep]", "[resource-new]"); + needed.push((op.import_module.clone(), new_field, site.to_component)); + } + } } if needed.is_empty() { diff --git a/meld-core/tests/wit_bindgen_runtime.rs b/meld-core/tests/wit_bindgen_runtime.rs index e5bb12a..cf19cf0 100644 --- a/meld-core/tests/wit_bindgen_runtime.rs +++ b/meld-core/tests/wit_bindgen_runtime.rs @@ -650,7 +650,8 @@ fuse_only_test!( test_fuse_wit_bindgen_resource_aggregates, "resource_aggregates" ); -// resource_floats: 3-component borrow forwarding needs caller→callee handle transfer +// resource_floats: adapter produces correct rep→new_handle chain, but P2 wrapper +// resource type mismatch between [resource-new] and [resource-rep] for intermediate fuse_only_test!(test_fuse_wit_bindgen_resource_floats, "resource_floats"); fuse_only_test!( test_fuse_wit_bindgen_resource_borrow_in_record,