From 7aba731a6225a1bdff67bc3b28213d0cdb6c19b2 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 7 Oct 2026 21:34:34 +0200 Subject: [PATCH 1/2] Check a single logical mask's shape on Julia 1.10 On 1.10, Base passes a single index to the three-argument to_indices with one linear range as the indices, so _check_mask never checked a mask against a multidimensional array. A mask of the wrong shape whose true entries all fell within the array then returned values instead of throwing a BoundsError. Define the two-argument method on every version, as 1.11 already did, and keep the three-argument override only for the trailing host mask that 1.10 would make a LogicalIndex. The test now uses masks that select only in-bounds elements, so it fails deterministically instead of in about a quarter of the runs. --- src/host/indexing.jl | 14 +++++--------- test/testsuite/findall.jl | 4 ++-- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/host/indexing.jl b/src/host/indexing.jl index 2a1b21e6..1279d427 100644 --- a/src/host/indexing.jl +++ b/src/host/indexing.jl @@ -250,17 +250,13 @@ _findall_items(A) = ndims(A) == 0 ? LinearIndices(A) : keys(A) # Those no longer carry the mask's shape, so a single mask is checked against the array first, as # Base does; a mask mixed with other indices is not (as before). Base.to_index(::AnyGPUArray, I::AbstractArray{Bool}) = findall(I) -@static if VERSION >= v"1.11.0-DEV.1157" - Base.to_indices(A::AnyGPUArray, I::Tuple{AbstractArray{Bool}}) = - (checkbounds(A, I[1]); (Base.to_index(A, I[1]),)) -else - # (also reached for the last of several indices, whose `inds` are then not all of `A`'s) - _check_mask(A, inds, mask) = length(inds) == ndims(A) ? checkbounds(A, mask) : nothing +Base.to_indices(A::AnyGPUArray, I::Tuple{AbstractArray{Bool}}) = + (checkbounds(A, I[1]); (Base.to_index(A, I[1]),)) +@static if VERSION < v"1.11.0-DEV.1157" + # Base turns a trailing mask into a `LogicalIndex`, bypassing `to_index` Base.to_indices(A::AnyGPUArray, inds, I::Tuple{Union{Array{Bool,N}, BitArray{N}}}) where {N} = - (_check_mask(A, inds, I[1]); (Base.to_index(A, I[1]),)) - Base.to_indices(A::AnyGPUArray, inds, I::Tuple{AbstractArray{Bool}}) = - (_check_mask(A, inds, I[1]); (Base.to_index(A, I[1]),)) + (Base.to_index(A, I[1]),) end # ... except that a mask of the array's shape selects the values themselves, in one pass function Base.getindex(A::AbstractGPUArray, mask::AnyGPUArray{Bool}) diff --git a/test/testsuite/findall.jl b/test/testsuite/findall.jl index e6d35143..5408de3a 100644 --- a/test/testsuite/findall.jl +++ b/test/testsuite/findall.jl @@ -50,8 +50,8 @@ end @test compare_exact((A, m) -> A[m], AT, a, m) end @test compare_exact((A, m) -> view(A, 1:5, :)[m], AT, rand(Float32, 10, 4), rand(Bool, 5, 4)) - # ... and a mask that does not fit the array - for (a, m) in ((rand(Float32, 3), rand(Bool, 2)), (rand(Float32, 2, 3), rand(Bool, 3, 2))) + # ... and a mask that does not fit the array, though every index it selects is in bounds + for (a, m) in ((rand(Float32, 3), Bool[1, 0]), (rand(Float32, 2, 3), Bool[1 0; 1 0; 0 0])) @test compare_exact((A, m) -> A[m], AT, a, m) @test compare_exact(A -> A[m], AT, a) # (a host mask) @test compare_exact(A -> A[view(m, :)], AT, a) # (a host view as the mask) From dbba9036213489b28d3c27449f711cab2422b2cf Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 7 Oct 2026 21:34:34 +0200 Subject: [PATCH 2/2] Bump version to 12.0.2 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index ba5fe8e4..f6d86a0f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "GPUArrays" uuid = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7" -version = "12.0.1" +version = "12.0.2" [workspace] projects = ["lib/GPUArraysCore", "lib/JLArrays", "test", "docs"]