Retain only source backing guard after exact policy transfer

This commit is contained in:
2026-09-24 17:28:48 -07:00
parent d11b99bb4a
commit fa7a980418
4 changed files with 56 additions and 19 deletions
@@ -47,7 +47,7 @@ pub(super) struct ParsedMixedSyntax<'s> {
// phase adds syntax entries to this once, not tokens/positions again.
metadata: usize,
// Kept through consumed resolution/indexing; fixes the live backing owner.
carried: Option<carried::CarriedInput<'s>>,
carried: Option<carried::CarriedGuard<'s>>,
}
struct FileSyntax<'s> {
@@ -257,7 +257,7 @@ fn parse_impl<'s>(
bundle: &'s SourceBundle,
limits: ProjectLimits,
profile: Limits,
carried: Option<carried::CarriedInput<'s>>,
carried: Option<carried::CarriedGuard<'s>>,
) -> Result<ParsedMixedSyntax<'s>, Vec<SourceDiagnostic>> {
let entry_source = bundle.files.get(&bundle.entry).map_or("", String::as_str);
let at_entry = |error| located(&bundle.entry, entry_source, ordinary_error(error));
@@ -26,23 +26,44 @@ enum SnapshotBacking<'s> {
pub(super) struct CarriedInput<'s> {
backing: SnapshotBacking<'s>,
}
enum GuardBacking<'s> {
Borrowed,
Retained(&'s RetainedSnapshot),
}
/// Policy has transferred into Budget/ParsedMixedSyntax. Only backing ownership
/// remains here, avoiding a duplicate inline policy in every later witness.
pub(super) struct CarriedGuard<'s> {
backing: GuardBacking<'s>,
}
impl<'s> CarriedInput<'s> {
fn policy(&self) -> &SnapshotPolicy {
match &self.backing {
SnapshotBacking::Borrowed { policy, .. } => policy,
SnapshotBacking::Retained(owner) => &owner.policy,
}
}
fn bundle(&self) -> &'s SourceBundle {
match &self.backing {
SnapshotBacking::Borrowed { bundle, .. } => bundle,
SnapshotBacking::Retained(owner) => &owner.bundle,
fn into_parts(self) -> (&'s SourceBundle, SnapshotPolicy, CarriedGuard<'s>) {
match self.backing {
SnapshotBacking::Borrowed { bundle, policy } => (
bundle,
policy,
CarriedGuard {
backing: GuardBacking::Borrowed,
},
),
SnapshotBacking::Retained(owner) => (
&owner.bundle,
SnapshotPolicy {
limits: owner.policy.limits,
project_limits: owner.policy.project_limits,
},
CarriedGuard {
backing: GuardBacking::Retained(owner),
},
),
}
}
}
impl CarriedGuard<'_> {
pub(super) fn slots(&self) -> Slots {
match &self.backing {
SnapshotBacking::Borrowed { .. } => Slots::default(),
SnapshotBacking::Retained(owner) => owner.retained,
GuardBacking::Borrowed => Slots::default(),
GuardBacking::Retained(owner) => owner.retained,
}
}
pub(super) fn check(
@@ -63,9 +84,8 @@ impl<'s> CarriedInput<'s> {
/// Input already binds the actual bundle; callers cannot supply a replacement.
fn parse_carried(input: CarriedInput<'_>) -> Result<ParsedMixedSyntax<'_>, Vec<SourceDiagnostic>> {
let bundle = input.bundle();
let policy = input.policy();
parse_impl(bundle, policy.project_limits, policy.limits, Some(input))
let (bundle, policy, guard) = input.into_parts();
parse_impl(bundle, policy.project_limits, policy.limits, Some(guard))
}
#[cfg(test)]
@@ -139,7 +139,7 @@ fn real_retained_bundles_keep_exact_guard_work_and_once_only_resolved_baseline()
assert_eq!(parsed.budget.numeric_units(), 0);
assert!(std::ptr::eq(parsed.bundle, &owner.bundle));
assert!(
matches!(&parsed.carried.as_ref().unwrap().backing,SnapshotBacking::Retained(retained) if std::ptr::eq(*retained,&owner))
matches!(&parsed.carried.as_ref().unwrap().backing,GuardBacking::Retained(retained) if std::ptr::eq(*retained,&owner))
);
let resolved = project::resolve(parsed).unwrap();
assert_eq!(resolved.project(), &expected_project());
@@ -320,12 +320,19 @@ fn explicit_fault_injections_cover_occurrence_caps_and_unreachable_overflows() {
let budget = Budget::new(profile(100, 1024)).unwrap();
// Valid hard limits reject C first: direct injection reaches arithmetic.
assert_eq!(
held.input().check(&budget, 0, 1).unwrap_err().message,
held.input()
.into_parts()
.2
.check(&budget, 0, 1)
.unwrap_err()
.message,
"Mixed auxiliary storage size overflow."
);
held.retained = Slots::default();
assert_eq!(
held.input()
.into_parts()
.2
.check(&budget, usize::MAX, 1)
.unwrap_err()
.message,