use std::collections::{BTreeMap, BTreeSet};
use super::value_wrap::format_escape_sites;
use super::CallableInfo;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct FrozenCallable {
pub(super) name: String,
pub(super) reason: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum FrozenCause {
ValueReference,
HostEntry,
ManifestHandler,
}
pub(super) struct ValueEscape<'a> {
pub(super) referenced_by_value: &'a BTreeSet<String>,
pub(super) escape_sites: &'a BTreeMap<String, Vec<(String, usize)>>,
pub(super) manifest_handlers: &'a BTreeSet<String>,
pub(super) frozen: &'a mut Vec<FrozenCallable>,
}
impl ValueEscape<'_> {
pub(super) fn record(&mut self, info: &CallableInfo) {
let Some(cause) = info.frozen_cause else {
return;
};
let sites = self
.escape_sites
.get(&info.name)
.map(Vec::as_slice)
.unwrap_or(&[]);
if self.frozen.iter().any(|entry| entry.name == info.name) {
return;
}
self.frozen
.push(FrozenCallable::new(&info.name, cause, sites));
}
}
impl FrozenCallable {
pub(super) fn new(name: &str, cause: FrozenCause, sites: &[(String, usize)]) -> Self {
let site_note = format_escape_sites(sites);
let reason = match cause {
FrozenCause::ValueReference => format!(
"its value is read as a first-class reference, so it is invoked at its declared arity through a call site the fixer cannot see{site_note}. It owns the ambient capability use, and a capability it cannot receive cannot be threaded into its body — pass the capability through an existing parameter, or wrap the reference as `{{ args -> {name}(harness, args) }}`"
),
FrozenCause::HostEntry => format!(
"it is declared `@host_entry`, so an embedding host supplies its arguments at the arity it declares{site_note}. It owns the ambient capability use, and a parameter the host was never asked to pass cannot be introduced — thread the capability through an existing parameter, or have the host pass it and drop `@host_entry` from `{name}`"
),
FrozenCause::ManifestHandler => format!(
"`harn.toml` registers it as a runtime handler, so the runtime supplies its capability argument — and it supplies the root `Harness`, which a narrowed or record carrier cannot receive{site_note}. Thread the capability through an existing parameter, declare the root handle yourself, or remove the `harn.toml` block that names `{name}`"
),
};
Self {
name: name.to_string(),
reason,
}
}
}