Skip to content

Fix test failures across the whole CI matrix - #86

Merged
davidanthoff merged 1 commit into
mainfrom
fix-julia-1.12-compat
Aug 31, 2026
Merged

davidanthoff merged 1 commit into
mainfrom
fix-julia-1.12-compat

Conversation

@davidanthoff

Copy link
Copy Markdown
Member

Every run-tests leg of CI is currently red — 78/78 on the last PR run and on main. Three of the 13 test items fail. None of it is fallout from the test-item migration; these are long-standing bugs that the migration surfaced, plus one test that has been comparing floats for bit equality since Julia 1.0.

Which items fail depends on the Julia version:

Julia Failing items
1.0 – 1.5 Reduce
1.6 – 1.13-rc Reduce, Primitives, DataVector

What changed in Base, and when

Every version in the CI matrix was probed locally through its juliaup channel, so each fix targets a known boundary rather than a runtime feature check — no @static, no isdefined branches:

Julia Base._growbeg! ccall(:jl_array_grow_beg) Base._prepend! 1st arg Base.reverse(::AbstractVector, _, _)
1.0.5 – 1.5.4 works Any Any, Any
1.6.7 – 1.9.4 works Any Integer, Integer
1.10.12 works Vector Integer, Integer
1.11.9 – 1.13.0-rc3 symbol gone Vector Integer, Integer

The four source bugs

jl_array_grow_beg is gone from Julia 1.11 on. The three ccalls in datavaluevector.jl die with "could not load symbol". Replaced with Base._growbeg!, the Base wrapper that has always sat in front of that symbol — present with the same signature and identical behaviour on 1.0.5 through 1.13.0-rc3 (checked functionally at both ends, with Vector{Int} and Vector{Bool}). The same file already uses Base._growat! and Base._deleteat!.

Multi-value pushfirst! broke in Julia 1.10. pushfirst!(X, a, b, c) reaches us as prepend!(X, (a, b, c)), whose Base fallback narrowed from Any to Vector. Added a general-iterable prepend! beside the existing AbstractVector method; [x for x in items] always yields a Vector, so it cannot recurse.

reverse/reverse! became ambiguous in Julia 1.6, when Base typed its own positional arguments as Integer while ours stayed untyped. Typed ours to match — strictly more specific on 1.6+, and equally unambiguous against the pre-1.6 untyped method. Their docstrings also claimed (start, count) semantics while the bodies always forwarded Base's (start, stop); corrected.

promote_type(Any, DataValue{T}) overflowed the stack. Base's missing.jl defines promote_rule(T::Type{>:Union{Missing,Nothing}}, S::Type) = Any, so for (Any, DataValue{Any}) the two directions disagree — Base says Any, we say DataValue{Any} — and promote_result re-asks the same question forever. Added the mirror-image rule so both directions agree, keeping the DataValue{Any} answer. Every promotion case asserted in test_basederived.jl and test_core.jl is unchanged. This was latent anywhere Any met a DataValue in promotion, e.g. reduce(promote_type, ...) over column types in QueryOperators' pivot.

The test bug

Reduce compared a DataValueArray reduction against Base's for bit equality. Base's mapreduce_impl over a Vector{Float64} uses @simd and reassociates; accumulating DataValues cannot vectorise and stays strictly sequential, so at N = 2050 the two differ by exactly 1 ULP (5091.29121864207 vs 5091.291218642071). That has failed on every version since 1.0. Rather than relax the assertions, the data is now drawn from integers ≤ 1000, so every partial sum is exactly representable and reassociation cannot change the result — the four isequal/== comparisons stay exact. Keeping Float64 storage leaves the six DataValue{Float64}() expectations untouched.

Verification

13/13 items pass on 1.0.5, 1.6.7, 1.10.12, 1.11.9, 1.12.7 and 1.13.0-rc3 — both sides of every Base transition in the table above.

No Project.toml change: the version is already the unreleased 1.0.0-DEV, and julia = "1" still holds.

🤖 Generated with Claude Code

Every run-tests leg was red. Three of the 13 test items failed, for four
independent reasons, none of them caused by the test-item migration.

- `ccall(:jl_array_grow_beg, ...)`: the C symbol is gone from Julia 1.11 on.
  Use `Base._growbeg!`, which wraps it and is present, with the same signature
  and behaviour, on every version from 1.0.5 to 1.13.0-rc3.

- `pushfirst!(X, a, b, c)` reaches us as `prepend!(X, (a, b, c))`, and Base
  narrowed `_prepend!`'s first argument from `Any` to `Vector` in 1.10, so from
  1.10 on it threw a MethodError. Add a general-iterable `prepend!` fallback.

- `reverse`/`reverse!` left their positional arguments untyped, which became
  ambiguous with Base once 1.6 typed its own as `Integer`. Type ours to match.
  Their docstrings described (start, count) while the bodies always forwarded
  Base's (start, stop); correct the docs.

- `promote_type(Any, DataValue{T})` recursed until the stack overflowed: Base's
  `>:Union{Missing,Nothing}` rule answers `Any` while ours answers
  `DataValue{Any}`, so `promote_result` re-asked the same question forever. Add
  the mirror-image rule so both directions agree, keeping `DataValue{Any}`.

The Reduce item compared a DataValueArray reduction against Base's, which is
`@simd` and therefore reassociates; the two differ by 1 ULP at N = 2050. This
failed on every version including 1.0.5. Draw the test data from integers so
every partial sum is exact and the comparisons can stay exact too.

Verified: 13/13 items pass on 1.0.5, 1.6.7, 1.10.12, 1.11.9, 1.12.7 and
1.13.0-rc3 — each Base transition above is covered on both sides.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@davidanthoff
davidanthoff merged commit bdb5ec1 into main Aug 31, 2026
79 of 81 checks passed
@davidanthoff
davidanthoff deleted the fix-julia-1.12-compat branch August 31, 2026 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant