From 5d0eb4f09ec99fa2a632e6c1fee21fcab629d32a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller-Widmann?= Date: Fri, 16 Oct 2026 13:49:24 +0200 Subject: [PATCH] Make ProjectTo{AbstractArray} inferrable for wrappers with OneTo axes If dx and the projector have the same number of OneTo axes but they are not identical, the sizes differ and the size check would throw anyway. Doing this check on types lets inference skip the type-unstable reshape, which returns a ReshapedArray for wrappers like Diagonal, UpperTriangular or Symmetric. Fixes #407 Co-Authored-By: Claude Opus 5.5 --- src/projection.jl | 4 ++++ test/projection.jl | 15 ++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/projection.jl b/src/projection.jl index ba1696fd5..1e846f014 100644 --- a/src/projection.jl +++ b/src/projection.jl @@ -223,6 +223,10 @@ function (project::ProjectTo{AbstractArray})(dx::AbstractArray{S,M}) where {S,M} # like dx = ones(4,1), where x = ones(4), but throw an error on dx = ones(1,4) etc. dy = if axes(dx) === project.axes dx + elseif project.axes isa Tuple{Vararg{Base.OneTo}} && axes(dx) isa typeof(project.axes) + # Same number of `OneTo` axes that are not identical: the sizes differ. Resolved from + # the types alone, so inference does not see the type-unstable `reshape` below. + throw(_projection_mismatch(project.axes, size(dx))) else for d in 1:max(M, length(project.axes)) if size(dx, d) != length(get(project.axes, d, 1)) diff --git a/test/projection.jl b/test/projection.jl index 3d1e150bf..2445b79bd 100644 --- a/test/projection.jl +++ b/test/projection.jl @@ -457,11 +457,16 @@ struct NoSuperType end @test 0 == @ballocated $pdiag(dx) setup = (dx = Diagonal(rand(10^3))) psymm = ProjectTo(Symmetric(rand(10^3, 10^3))) - # The Union{Symmetric{…,Matrix}, Symmetric{…,ReshapedArray}} return value is only - # unboxed since JuliaLang/julia#55045 (sret_union ABI for pointer-ful types) - @test 0 == @ballocated($psymm(dx), setup = (dx = Symmetric(rand(10^3, 10^3)))) broken = ( - VERSION < v"1.14.0-DEV.1348" - ) + @test 0 == @ballocated $psymm(dx) setup = (dx = Symmetric(rand(10^3, 10^3))) + end + + @testset "#407" begin + @test @inferred(ProjectTo(rand(3, 3))(Diagonal(rand(3)))) isa Diagonal{Float64} + pupp = ProjectTo(UpperTriangular(rand(3, 3))) + @test @inferred(pupp(UpperTriangular(rand(3, 3)))) isa UpperTriangular{Float64} + psymm = ProjectTo(Symmetric(rand(3, 3))) + @test @inferred(psymm(Symmetric(rand(3, 3)))) isa Symmetric{Float64} + @test_throws DimensionMismatch ProjectTo(rand(3, 3))(Diagonal(rand(2))) end @testset "#685" begin