From f7c4b2ec83a78c15c99de40f4c53bbca2925d5d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller-Widmann?= Date: Fri, 30 Oct 2026 01:12:29 +0100 Subject: [PATCH 1/4] Check for zero tuple type --- src/projection.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/projection.jl b/src/projection.jl index ddc797bd5..2577dfa7b 100644 --- a/src/projection.jl +++ b/src/projection.jl @@ -339,7 +339,7 @@ function (project::ProjectTo{<:Tangent{<:Tuple}})(dx::Tuple) end # Here map will fail if the lengths don't match, but gives a much less helpful error: dy = map((f, x) -> f(x), project.elements, dx) - if all(d -> d isa AbstractZero, dy) + if dy isa Tuple{Vararg{AbstractZero}} return NoTangent() else return project_type(project)(dy...) @@ -392,7 +392,7 @@ function (project::ProjectTo{<:Tangent{<:Tuple}})(dx::AbstractArray) end dy = reshape(dx, axes(project.elements)) # allows for dx::OffsetArray dz = ntuple(i -> project.elements[i](dy[i]), length(project.elements)) - if all(d -> d isa AbstractZero, dy) + if dz isa Tuple{Vararg{AbstractZero}} return NoTangent() else return project_type(project)(dz...) From 0cc3cd8467e15035178989ca9cfd99fbde5369a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller-Widmann?= Date: Fri, 30 Oct 2026 09:36:03 +0100 Subject: [PATCH 2/4] Test tuple projector collapse and inference Co-Authored-By: Claude Opus 5.5 --- test/projection.jl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/projection.jl b/test/projection.jl index dee6ef146..848d85e98 100644 --- a/test/projection.jl +++ b/test/projection.jl @@ -173,6 +173,17 @@ struct NoSuperType end pt3 = ProjectTo(([1, 2, 3], false, :gamma)) # partly non-differentiable @test pt3((1:3, 4, 5)) == Tangent{Tuple{Vector{Int}, Bool, Symbol}}([1.0, 2.0, 3.0], NoTangent(), NoTangent()) @test ProjectTo((true, [false])) isa ProjectTo{NoTangent} + + pt2 = ProjectTo((1.0, 2.0)) + @test @inferred(pt2([3, 4])) == Tangent{Tuple{Float64,Float64}}(3.0, 4.0) + @test @inferred(pt2((ZeroTangent(), 1))) == Tangent{Tuple{Float64,Float64}}(ZeroTangent(), 1.0) + @test @inferred(pt2((ZeroTangent(), NoTangent()))) === NoTangent() + @test pt2([ZeroTangent(), NoTangent()]) === NoTangent() + + ptb = ProjectTo((true, 1.0)) + @test @inferred(ptb((2.0, ZeroTangent()))) === NoTangent() + @test ptb(Any[2.0, ZeroTangent()]) === NoTangent() # collapse all-zero after projection + @test ptb([2.0, 3.0]) == Tangent{Tuple{Bool,Float64}}(NoTangent(), 3.0) end @testset "Base: NamedTuple" begin From 28c545105a3211727360c4fc8c4661d88ac95b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller-Widmann?= Date: Fri, 30 Oct 2026 09:39:54 +0100 Subject: [PATCH 3/4] Fix format Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- test/projection.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/projection.jl b/test/projection.jl index 848d85e98..e05541cae 100644 --- a/test/projection.jl +++ b/test/projection.jl @@ -176,7 +176,8 @@ struct NoSuperType end pt2 = ProjectTo((1.0, 2.0)) @test @inferred(pt2([3, 4])) == Tangent{Tuple{Float64,Float64}}(3.0, 4.0) - @test @inferred(pt2((ZeroTangent(), 1))) == Tangent{Tuple{Float64,Float64}}(ZeroTangent(), 1.0) + @test @inferred(pt2((ZeroTangent(), 1))) == + Tangent{Tuple{Float64,Float64}}(ZeroTangent(), 1.0) @test @inferred(pt2((ZeroTangent(), NoTangent()))) === NoTangent() @test pt2([ZeroTangent(), NoTangent()]) === NoTangent() From 11caa3032517b6f99f97cddd479ef141db3d02e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller-Widmann?= Date: Fri, 30 Oct 2026 17:02:53 +0100 Subject: [PATCH 4/4] Bump version Co-Authored-By: Claude Opus 5.5 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 30a618092..5df2a56ea 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "ChainRulesCore" uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.26.7" +version = "1.26.8" [deps] LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"