From eab80001e74307baedae57e2160f6ad1dd492b40 Mon Sep 17 00:00:00 2001 From: nseaSeb Date: Fri, 10 Jul 2026 10:03:50 +0200 Subject: [PATCH] Fix Range.disjoint?/2 for single-element ranges with a negative step `member?/2`, `size/1` and `to_list/1` all treat a single-element range such as `1..1//-2` as the set `[1]`, and `Range.split/2` produces such ranges from library code (e.g. `Range.split(5..1//-2, 2)` yields `1..1//-2`). Yet `disjoint?/2` reported two of them as sharing no element: Range.disjoint?(1..1//-2, 1..1//-2) #=> true (should be false) `normalize/3` only reversed ranges when `first > last`, leaving a negative step in place for single-element ranges (`first == last`). The arithmetic-progression intersection in `disjoint?/2` assumes an increasing progression, so the leftover negative step produced a wrong result. The `step == -1` case was masked by the `abs(step) == 1` shortcut, so only `step <= -2` was affected. Assisted-by: Claude Code:claude-opus-4-8 --- lib/elixir/lib/range.ex | 4 ++++ lib/elixir/test/elixir/range_test.exs | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/lib/elixir/lib/range.ex b/lib/elixir/lib/range.ex index 1ed62a0c59d..58886331864 100644 --- a/lib/elixir/lib/range.ex +++ b/lib/elixir/lib/range.ex @@ -517,6 +517,10 @@ defmodule Range do defp normalize(first, last, step) when first > last, do: {first - abs(div(first - last, step) * step), first, -step} + # A single-element range holds the same element regardless of the step, so + # make the step positive to keep the progression in disjoint?/2 increasing. + defp normalize(first, last, step) when first == last, do: {first, last, abs(step)} + defp normalize(first, last, step), do: {first, last, step} @doc false diff --git a/lib/elixir/test/elixir/range_test.exs b/lib/elixir/test/elixir/range_test.exs index 16009818b55..508d2d29178 100644 --- a/lib/elixir/test/elixir/range_test.exs +++ b/lib/elixir/test/elixir/range_test.exs @@ -103,6 +103,11 @@ defmodule RangeTest do assert_overlap(-7..-5, -5..-1) assert Range.disjoint?(1..1, 1..1) == false + + # Single-element ranges still contain their element regardless of the step + assert Range.disjoint?(1..1//-2, 1..1//-2) == false + assert Range.disjoint?(3..3//-3, 1..5) == false + assert Range.disjoint?(1..5, 3..3//-3) == false end end