From 177b894d7d77a5297bee3b2487ef18a0cae7a596 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 3 May 2026 21:50:54 +0900 Subject: fix(resolver): expand root branch-alias and self.version replace links Two related parity gaps surfaced by the `circular-dependency` fixture: 1. The root's `extra.branch-alias` entry was never materialized in the pool, and root-level `replace`/`provide`/`conflict` constraints written as `self.version` were forwarded verbatim. Mirror Composer's `RootAliasPackage`: resolve `self.version` against the root's declared version for the base entry, then add an extra alias entry (carrying the base links plus a duplicate link per `self.version` original retagged at the alias's version) when the root's version matches an `extra.branch-alias` key. 2. `Pool::matches_package` returned on the first link to a target name even when its constraint did not match the query, hiding any later link to the same target. With the alias above, that masked the second `replace` link tagged at the alias version. Keep iterating when target matches but constraint does not, so a later link can still satisfy. --- crates/mozart-sat-resolver/src/pool.rs | 50 ++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 23 deletions(-) (limited to 'crates/mozart-sat-resolver/src') diff --git a/crates/mozart-sat-resolver/src/pool.rs b/crates/mozart-sat-resolver/src/pool.rs index 2c52791..8a63c05 100644 --- a/crates/mozart-sat-resolver/src/pool.rs +++ b/crates/mozart-sat-resolver/src/pool.rs @@ -299,36 +299,40 @@ impl Pool { }; } - // Check provides + // Check provides. A package may declare more than one provide link + // for the same target (e.g. an `AliasPackage` carries the base's link + // and an extra link tagged at the alias's own version), so keep + // iterating once a target name matches but the constraint doesn't — + // a later link may still satisfy. for link in &candidate.provides { - if link.target == name { - return match constraint { - None => true, - Some(vc) => { - // The provide link has its own constraint; check if they intersect - if let Ok(provide_vc) = VersionConstraint::parse(&link.constraint) { - constraints_intersect(vc, &provide_vc) - } else { - false - } + if link.target != name { + continue; + } + match constraint { + None => return true, + Some(vc) => { + if let Ok(provide_vc) = VersionConstraint::parse(&link.constraint) + && constraints_intersect(vc, &provide_vc) + { + return true; } - }; + } } } - // Check replaces for link in &candidate.replaces { - if link.target == name { - return match constraint { - None => true, - Some(vc) => { - if let Ok(replace_vc) = VersionConstraint::parse(&link.constraint) { - constraints_intersect(vc, &replace_vc) - } else { - false - } + if link.target != name { + continue; + } + match constraint { + None => return true, + Some(vc) => { + if let Ok(replace_vc) = VersionConstraint::parse(&link.constraint) + && constraints_intersect(vc, &replace_vc) + { + return true; } - }; + } } } -- cgit v1.3.1