Specialize findlast for integer AbstractUnitRanges and StepRanges (#54902)
For monotonic ranges, `findfirst` and `findlast` with `==(val)` as the
predicate should be identical, as each value appears only once in the
range. Since `findfirst` is specialized for some ranges, we may define
`findlast` as well analogously.
On v"1.12.0-DEV.770"
```julia
julia> @btime findlast(==(1), $(Ref(1:1_000))[])
1.186 μs (0 allocations: 0 bytes)
1
```
This PR
```julia
julia> @btime findlast(==(1), $(Ref(1:1_000))[])
3.171 ns (0 allocations: 0 bytes)
1
```
I've also specialized `findfirst(iszero, r::AbstractRange)` to make this
be equivalent to `findfirst(==(0), ::AbstractRange)` for numerical
ranges. Similarly, for `isone`. These now take the fast path as well.
Thirdly, I've added some `convert` calls to address issues like
```julia
julia> r = Int128(1):Int128(1):Int128(4);
julia> findfirst(==(Int128(2)), r) |> typeof
Int128
julia> keytype(r)
Int64
```
This PR ensures that the return type always corresponds to `keytype`,
which is what the docstring promises.
This PR also fixes
```julia
julia> findfirst(==(0), UnitRange(-0.5, 0.5))
ERROR: InexactError: Int64(0.5)
Stacktrace:
[1] Int64
@ ./float.jl:994 [inlined]
[2] findfirst(p::Base.Fix2{typeof(==), Int64}, r::UnitRange{Float64})
@ Base ./array.jl:2397
[3] top-level scope
@ REPL[1]:1
```
which now returns `nothing`, as expected.