Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
79 changes: 43 additions & 36 deletions meld-core/src/adapter/fact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32, &crate::resolver::ResolvedResourceOp> = 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<T>: callee handles conversion internally
Expand All @@ -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]<name> 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,
);
}
}
Expand Down
59 changes: 56 additions & 3 deletions meld-core/src/merger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Option<u32>>,

/// 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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions meld-core/src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
3 changes: 2 additions & 1 deletion meld-core/tests/wit_bindgen_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading