@inbounds for getindex not working

Here is the example code and output.

struct Minij{T<:Number} <: AbstractMatrix{T}
    n::Integer

    function Minij{T}(n::Integer) where {T<:Number}
        n >= 0 || throw(ArgumentError("$n < 0"))
        return new{T}(n)
    end
end

Minij(n::Integer) = Minij{Int}(n)

Base.size(A::Minij) = (A.n, A.n)

@inline Base.@propagate_inbounds function Base.getindex(A::Minij{T}, i::Integer, j::Integer) where {T}
    @boundscheck checkbounds(A, i, j)
    return T(min(i, j))
end

@inline function g(A, i)
    @boundscheck checkbounds(A, i)
    println("accessing ($A)[$i]")
end

a = Minij(3)

for i = 1:1
    g(1:2, 1)
    @inbounds g(1:2, -1)
    a[2, 2]
    @inbounds a[2, -1]
end
accessing (1:2)[1]
accessing (1:2)[-1]
ERROR: LoadError: BoundsError: attempt to access 3×3 Minij{Int64} at index [2, -1]
Stacktrace:
 [1] throw_boundserror(A::Minij{Int64}, I::Tuple{Int64, Int64})
   @ Base .\abstractarray.jl:737
 [2] checkbounds
   @ .\abstractarray.jl:702 [inlined]
 [3] getindex(A::Minij{Int64}, i::Int64, j::Int64)
   @ Main ...\perf.jl:17
 [4] top-level scope
   @ ...\perf.jl:32
in expression starting at D:\Projects\TypedMatrices\devtests\perf.jl:28

My question is: @inbounds works for function g, not for function getindex. It should removes the @boundscheck block in getindex funciton, but actually not.

Yes, @inbounds only works in a compiled context with type-stable values. It’s an optional optimization (that can be explicitly ignored), not a semantic guarantee.

I did a rundown of this in London long ago, and these are all still true, I think:

1 Like

Thanks! Your video is very helpful.

Here is the updated example code for anyone who has the same issue and found this topic:

function main()
    a = Minij(3)

    for i = 1:1
        g(1:2, 1)
        @inbounds g(1:2, -1)
        a[2, 2]
        @inbounds a[2, -1]
    end
end

main()

Things should be moved into a function to ensure type stability.

1 Like