From a86c09666ef3fbb71ce354cbd33f9e7cb4a3f810 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sun, 24 May 2026 04:52:52 +0000 Subject: [PATCH] to_vec: handle UnionAll the same as DataType (non-perturbable) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generic struct fallback for `to_vec` reconstructs the value through `T(field_vals...)`, which for `T = UnionAll` calls `UnionAll(fresh_TypeVar, original_body)`. Because the fresh TypeVar isn't referenced in `original_body`, the new UnionAll simplifies away and the result is the bare `DataType` body — not the original UnionAll. On Julia ≤ 1.13 the result happened to compare equal to the original (e.g. `Array{T,1} == Vector`), so the existing `test_to_vec(Vector)` passed; JuliaLang/julia#61876 treats free TypeVars as singleton identities, so that equality is intentionally false, and the `test_to_vec` round-trip fails. Add `UnionAll` to the list of non-perturbable types alongside `DataType`. Type values aren't meaningfully perturbed by a finite-difference step anyway, so returning `(Bool[], _ -> x)` is the appropriate behavior. The behavior of free TypeVars in subtyping was previously undefined (in effect a bug); JuliaLang/julia#61876 makes them well-defined singleton identities, which is what surfaces this latent issue. This change was AI-generated and should be reviewed carefully before merging. Co-authored-by: Claude Opus 4.7 --- src/to_vec.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/to_vec.jl b/src/to_vec.jl index 079fbd1..62a586b 100644 --- a/src/to_vec.jl +++ b/src/to_vec.jl @@ -280,7 +280,7 @@ function to_vec(d::Dict) end # non-perturbable types -for T in (:DataType, :CartesianIndex, :AbstractZero) +for T in (:DataType, :UnionAll, :CartesianIndex, :AbstractZero) T_from_vec = Symbol(T, :_from_vec) @eval function FiniteDifferences.to_vec(x::$T) function $T_from_vec(x_vec::Vector)