Intersect all ranges in and chains - #50
Merged
Merged
Conversation
Range.allows_any?/2, Version.allows_any?/2 and Union.allows_any?/2 returned true for an empty constraint, so a positive term with an empty constraint related as overlapping instead of disjoint to any assignment. When a dependency's constraint was empty and the depender had another version to backtrack to, unit propagation derived the same term from the no-versions incompatibility forever.
ericmj
marked this pull request as ready for review
August 26, 2026 21:31
Requirements with more than two and terms, two bounds in the same direction, == inside an and, or two pessimistic ranges either crashed with FunctionClauseError or were rejected, even though Version.parse_requirement/1 accepts them and hexpm allows publishing them. Fold each and group through Constraint.intersect/2 instead of the special-cased to_range/4 clauses. An empty intersection raises UnsatisfiableRequirementError naming the two disjoint parts. Closes #49.
ericmj
force-pushed
the
intersect-and-chains
branch
from
August 26, 2026 21:34
324b8d8 to
a8bc4fa
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
HexSolver.Requirementonly handled two-termandgroups and special-cased their operator combinations, so requirements thatVersion.parse_requirement/1accepts and hexpm lets through on publish either crashed withFunctionClauseError(>= 1.0.0 and < 2.0.0 and > 1.5.0,>= 1.0.0 and > 1.5.0,== 1.0.0 and >= 1.0.0,~> 1.0 and >= 2.0.0) or were rejected (~> 1.0.0 and ~> 1.0).delex/2now folds everyandgroup throughConstraint.intersect/2, which replaces allto_range/4clauses. Semantics match core: a property checksConstraint.allows?/2againstVersion.match?/2for random one to four term chains over a version grid.An empty intersection raises the new
HexSolver.UnsatisfiableRequirementError, whose message names the two disjoint parts:requirement "~> 1.0 and >= 2.0.0" is unsatisfiable because "~> 1.0" and ">= 2.0.0" are disjoint.parse_constraint/1keeps returning:error.The first commit fixes a separate solver bug found while checking what an empty constraint does downstream. When a dependency's constraint is
%Empty{}(reachable with a published< 0.0.0-0requirement since #44) and the depender has another version to backtrack to,HexSolver.run/4never returned:allows_any?/2returnedtrueagainst an empty constraint, so the no-versions incompatibility related as overlapping and unit propagation derivednot bar emptyindefinitely. TheRange,VersionandUnionclauses now returnfalse.Empty.allows_any?/2is unchanged on purpose: flipping it too makes the dependency incompatibility conflict with itself and the failure message degrades todepends on both "bar empty" and "bar empty".Closes #49.