• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #38002

06 Feb 2025 06:14AM UTC coverage: 20.322% (-2.4%) from 22.722%
#38002

push

local

web-flow
bpart: Fully switch to partitioned semantics (#57253)

This is the final PR in the binding partitions series (modulo bugs and
tweaks), i.e. it closes #54654 and thus closes #40399, which was the
original design sketch.

This thus activates the full designed semantics for binding partitions,
in particular allowing safe replacement of const bindings. It in
particular allows struct redefinitions. This thus closes
timholy/Revise.jl#18 and also closes #38584.

The biggest semantic change here is probably that this gets rid of the
notion of "resolvedness" of a binding. Previously, a lot of the behavior
of our implementation depended on when bindings were "resolved", which
could happen at basically an arbitrary point (in the compiler, in REPL
completion, in a different thread), making a lot of the semantics around
bindings ill- or at least implementation-defined. There are several
related issues in the bugtracker, so this closes #14055 closes #44604
closes #46354 closes #30277

It is also the last step to close #24569.
It also supports bindings for undef->defined transitions and thus closes
#53958 closes #54733 - however, this is not activated yet for
performance reasons and may need some further optimization.

Since resolvedness no longer exists, we need to replace it with some
hopefully more well-defined semantics. I will describe the semantics
below, but before I do I will make two notes:

1. There are a number of cases where these semantics will behave
slightly differently than the old semantics absent some other task going
around resolving random bindings.
2. The new behavior (except for the replacement stuff) was generally
permissible under the old semantics if the bindings happened to be
resolved at the right time.

With all that said, there are essentially three "strengths" of bindings:

1. Implicit Bindings: Anything implicitly obtained from `using Mod`, "no
binding", plus slightly more exotic corner cases around conflicts

2. Weakly declared bindin... (continued)

11 of 111 new or added lines in 7 files covered. (9.91%)

1273 existing lines in 68 files now uncovered.

9908 of 48755 relevant lines covered (20.32%)

105126.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

16.3
/base/tuple.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
# Document NTuple here where we have everything needed for the doc system
4
"""
5
    NTuple{N, T}
6

7
A compact way of representing the type for a tuple of length `N` where all elements are of type `T`.
8

9
# Examples
10
```jldoctest
11
julia> isa((1, 2, 3, 4, 5, 6), NTuple{6, Int})
12
true
13
```
14

15
See also [`ntuple`](@ref).
16
"""
17
NTuple
18

19
# convenience function for extracting N from a Tuple (if defined)
20
# else return `nothing` for anything else given (such as Vararg or other non-sized Union)
21
_counttuple(::Type{<:NTuple{N,Any}}) where {N} = N
×
22
_counttuple(::Type) = nothing
×
23

24
## indexing ##
25

26
length(@nospecialize t::Tuple) = nfields(t)
328,856✔
27
firstindex(@nospecialize t::Tuple) = 1
×
28
lastindex(@nospecialize t::Tuple) = length(t)
×
29
size(@nospecialize(t::Tuple), d::Integer) = (d == 1) ? length(t) : throw(ArgumentError("invalid tuple dimension $d"))
×
30
axes(@nospecialize t::Tuple) = (OneTo(length(t)),)
×
31
getindex(@nospecialize(t::Tuple), i::Int) = getfield(t, i, @_boundscheck)
150,941,423✔
32
getindex(@nospecialize(t::Tuple), i::Integer) = getfield(t, convert(Int, i), @_boundscheck)
×
33
__safe_getindex(@nospecialize(t::Tuple), i::Int) = (@_nothrow_noub_meta; getfield(t, i, false))
24,191✔
34
getindex(t::Tuple, r::AbstractArray{<:Any,1}) = (eltype(t)[t[ri] for ri in r]...,)
×
35
getindex(t::Tuple, b::AbstractArray{Bool,1}) = length(b) == length(t) ? getindex(t, findall(b)) : throw(BoundsError(t, b))
×
36
getindex(t::Tuple, c::Colon) = t
×
37

38
get(t::Tuple, i::Integer, default) = i in 1:length(t) ? getindex(t, i) : default
×
39
get(f::Callable, t::Tuple, i::Integer) = i in 1:length(t) ? getindex(t, i) : f()
×
40

41
# returns new tuple; N.B.: becomes no-op if `i` is out-of-bounds
42

43
"""
44
    setindex(t::Tuple, v, i::Integer)
45

46
Creates a new tuple similar to `t` with the value at index `i` set to `v`.
47
Throws a `BoundsError` when out of bounds.
48

49
# Examples
50
```jldoctest
51
julia> Base.setindex((1, 2, 6), 2, 3) == (1, 2, 2)
52
true
53
```
54
"""
55
function setindex(x::Tuple, v, i::Integer)
×
56
    @boundscheck 1 <= i <= length(x) || throw(BoundsError(x, i))
×
57
    @inline
×
58
    _setindex(v, i, x...)
×
59
end
60

61
function _setindex(v, i::Integer, args::Vararg{Any,N}) where {N}
×
62
    @inline
×
63
    return ntuple(j -> ifelse(j == i, v, args[j]), Val{N}())::NTuple{N, Any}
×
64
end
65

66

67
## iterating ##
68

69
function iterate(@nospecialize(t::Tuple), i::Int=1)
117✔
70
    @inline
369,774✔
71
    @_nothrow_meta
369,774✔
72
    return (1 <= i <= length(t)) ? (t[i], i + 1) : nothing
14,148,999✔
73
end
74

75
keys(@nospecialize t::Tuple) = OneTo(length(t))
×
76

77
"""
78
    prevind(A, i)
79

80
Return the index before `i` in `A`. The returned index is often equivalent to
81
`i - 1` for an integer `i`. This function can be useful for generic code.
82

83
!!! warning
84
    The returned index might be out of bounds. Consider using
85
    [`checkbounds`](@ref).
86

87
See also: [`nextind`](@ref).
88

89
# Examples
90
```jldoctest
91
julia> x = [1 2; 3 4]
92
2×2 Matrix{Int64}:
93
 1  2
94
 3  4
95

96
julia> prevind(x, 4) # valid result
97
3
98

99
julia> prevind(x, 1) # invalid result
100
0
101

102
julia> prevind(x, CartesianIndex(2, 2)) # valid result
103
CartesianIndex(1, 2)
104

105
julia> prevind(x, CartesianIndex(1, 1)) # invalid result
106
CartesianIndex(2, 0)
107
```
108
"""
109
function prevind end
110

111
"""
112
    nextind(A, i)
113

114
Return the index after `i` in `A`. The returned index is often equivalent to
115
`i + 1` for an integer `i`. This function can be useful for generic code.
116

117
!!! warning
118
    The returned index might be out of bounds. Consider using
119
    [`checkbounds`](@ref).
120

121
See also: [`prevind`](@ref).
122

123
# Examples
124
```jldoctest
125
julia> x = [1 2; 3 4]
126
2×2 Matrix{Int64}:
127
 1  2
128
 3  4
129

130
julia> nextind(x, 1) # valid result
131
2
132

133
julia> nextind(x, 4) # invalid result
134
5
135

136
julia> nextind(x, CartesianIndex(1, 1)) # valid result
137
CartesianIndex(2, 1)
138

139
julia> nextind(x, CartesianIndex(2, 2)) # invalid result
140
CartesianIndex(1, 3)
141
```
142
"""
143
function nextind end
144

145
prevind(@nospecialize(t::Tuple), i::Integer) = Int(i)-1
×
146
nextind(@nospecialize(t::Tuple), i::Integer) = Int(i)+1
×
147

148
function keys(t::Tuple, t2::Tuple...)
×
149
    @inline
×
150
    OneTo(_maxlength(t, t2...))
×
151
end
152
_maxlength(t::Tuple) = length(t)
×
153
function _maxlength(t::Tuple, t2::Tuple, t3::Tuple...)
×
154
    @inline
×
155
    max(length(t), _maxlength(t2, t3...))
×
156
end
157

158
# this allows partial evaluation of bounded sequences of next() calls on tuples,
159
# while reducing to plain next() for arbitrary iterables.
160
indexed_iterate(t::Tuple, i::Int, state=1) = (@inline; (getfield(t, i), i+1))
32,809,268✔
161
indexed_iterate(a::Array, i::Int, state=1) = (@inline; (a[i], i+1))
1,610✔
162
function indexed_iterate(I, i)
1✔
163
    x = iterate(I)
42✔
164
    x === nothing && throw(BoundsError(I, i))
21✔
165
    x
1✔
166
end
167
function indexed_iterate(I, i, state)
1✔
168
    x = iterate(I, state)
44✔
169
    x === nothing && throw(BoundsError(I, i))
23✔
170
    x
3✔
171
end
172

173
"""
174
    Base.rest(collection[, itr_state])
175

176
Generic function for taking the tail of `collection`, starting from a specific iteration
177
state `itr_state`. Return a `Tuple`, if `collection` itself is a `Tuple`, a subtype of
178
`AbstractVector`, if `collection` is an `AbstractArray`, a subtype of `AbstractString`
179
if `collection` is an `AbstractString`, and an arbitrary iterator, falling back to
180
`Iterators.rest(collection[, itr_state])`, otherwise.
181

182
Can be overloaded for user-defined collection types to customize the behavior of [slurping
183
in assignments](@ref destructuring-assignment) in final position, like `a, b... = collection`.
184

185
!!! compat "Julia 1.6"
186
    `Base.rest` requires at least Julia 1.6.
187

188
See also: [`first`](@ref first), [`Iterators.rest`](@ref), [`Base.split_rest`](@ref).
189

190
# Examples
191
```jldoctest
192
julia> a = [1 2; 3 4]
193
2×2 Matrix{Int64}:
194
 1  2
195
 3  4
196

197
julia> first, state = iterate(a)
198
(1, 2)
199

200
julia> first, Base.rest(a, state)
201
(1, [3, 2, 4])
202
```
203
"""
204
function rest end
205
rest(t::Tuple) = t
×
206
rest(t::Tuple, i::Int) = ntuple(x -> getfield(t, x+i-1), length(t)-i+1)
×
207
rest(a::Array, i::Int=1) = a[i:end]
1✔
208
rest(a::Core.SimpleVector, i::Int=1) = a[i:end]
×
209
rest(itr, state...) = Iterators.rest(itr, state...)
×
210

211
"""
212
    Base.split_rest(collection, n::Int[, itr_state]) -> (rest_but_n, last_n)
213

214
Generic function for splitting the tail of `collection`, starting from a specific iteration
215
state `itr_state`. Returns a tuple of two new collections. The first one contains all
216
elements of the tail but the `n` last ones, which make up the second collection.
217

218
The type of the first collection generally follows that of [`Base.rest`](@ref), except that
219
the fallback case is not lazy, but is collected eagerly into a vector.
220

221
Can be overloaded for user-defined collection types to customize the behavior of [slurping
222
in assignments](@ref destructuring-assignment) in non-final position, like `a, b..., c = collection`.
223

224
!!! compat "Julia 1.9"
225
    `Base.split_rest` requires at least Julia 1.9.
226

227
See also: [`Base.rest`](@ref).
228

229
# Examples
230
```jldoctest
231
julia> a = [1 2; 3 4]
232
2×2 Matrix{Int64}:
233
 1  2
234
 3  4
235

236
julia> first, state = iterate(a)
237
(1, 2)
238

239
julia> first, Base.split_rest(a, 1, state)
240
(1, ([3, 2], [4]))
241
```
242
"""
243
function split_rest end
244
function split_rest(itr, n::Int, state...)
×
245
    if IteratorSize(itr) == IsInfinite()
×
246
        throw(ArgumentError("Cannot split an infinite iterator in the middle."))
×
247
    end
248
    return _split_rest(rest(itr, state...), n)
×
249
end
250
_split_rest(itr, n::Int) = _split_rest(collect(itr), n)
×
251
function _check_length_split_rest(len, n)
×
252
    len < n && throw(ArgumentError(
×
253
        "The iterator only contains $len elements, but at least $n were requested."
254
    ))
255
end
256
function _split_rest(a::Union{AbstractArray, Core.SimpleVector}, n::Int)
×
257
    _check_length_split_rest(length(a), n)
×
258
    return a[begin:end-n], a[end-n+1:end]
×
259
end
260

261
@eval split_rest(t::Tuple, n::Int, i=1) = ($(Expr(:meta, :aggressive_constprop)); (t[i:end-n], t[end-n+1:end]))
×
262

263
# Use dispatch to avoid a branch in first
264
first(::Tuple{}) = throw(ArgumentError("tuple must be non-empty"))
×
265
first(t::Tuple) = t[1]
80✔
266

267
# eltype
268

269
eltype(::Type{Tuple{}}) = Bottom
×
270
# the <: here makes the runtime a bit more complicated (needing to check isdefined), but really helps inference
271
eltype(t::Type{<:Tuple{Vararg{E}}}) where {E} = @isdefined(E) ? (E isa Type ? E : Union{}) : _compute_eltype(t)
13✔
272
eltype(t::Type{<:Tuple}) = _compute_eltype(t)
1✔
273
function _compute_eltype(@nospecialize t)
×
274
    @_total_meta
×
275
    has_free_typevars(t) && return Any
×
276
    t´ = unwrap_unionall(t)
×
277
    # Given t = Tuple{Vararg{S}} where S<:Real, the various
278
    # unwrapping/wrapping/va-handling here will return Real
279
    if t´ isa Union
×
280
        return promote_typejoin(_compute_eltype(rewrap_unionall(t´.a, t)),
×
281
                                _compute_eltype(rewrap_unionall(t´.b, t)))
282
    end
283
    p = (t´::DataType).parameters
×
284
    length(p) == 0 && return Union{}
×
285
    elt = rewrap_unionall(unwrapva(p[1]), t)
×
286
    elt isa Type || return Union{} # Tuple{2} is legal as a Type, but the eltype is Union{} since it is uninhabited
×
287
    r = elt
×
288
    for i in 2:length(p)
×
289
        r === Any && return r # if we've already reached Any, it can't widen any more
×
290
        elt = rewrap_unionall(unwrapva(p[i]), t)
×
291
        elt isa Type || return Union{} # Tuple{2} is legal as a Type, but the eltype is Union{} since it is uninhabited
×
292
        r = promote_typejoin(elt, r)
×
293
    end
×
294
    return r
×
295
end
296

297
# We'd like to be able to infer eltype(::Tuple), which needs to be able to
298
# look at these four methods:
299
#
300
# julia> methods(Base.eltype, Tuple{Type{<:Tuple}})
301
# 4 methods for generic function "eltype" from Base:
302
# [1] eltype(::Type{Union{}})
303
#  @ abstractarray.jl:234
304
# [2] eltype(::Type{Tuple{}})
305
#  @ tuple.jl:199
306
# [3] eltype(t::Type{<:Tuple{Vararg{E}}}) where E
307
#  @ tuple.jl:200
308
# [4] eltype(t::Type{<:Tuple})
309
#  @ tuple.jl:209
310
typeof(function eltype end).name.max_methods = UInt8(4)
311

312
# key/val types
313
keytype(@nospecialize t::Tuple) = keytype(typeof(t))
×
314
keytype(@nospecialize T::Type{<:Tuple}) = Int
×
315

316
valtype(@nospecialize t::Tuple) = valtype(typeof(t))
×
317
valtype(@nospecialize T::Type{<:Tuple}) = eltype(T)
×
318

319
# version of tail that doesn't throw on empty tuples (used in array indexing)
320
safe_tail(t::Tuple) = tail(t)
×
321
safe_tail(t::Tuple{}) = ()
×
322

323
# front (the converse of tail: it skips the last entry)
324

325
"""
326
    front(x::Tuple)::Tuple
327

328
Return a `Tuple` consisting of all but the last component of `x`.
329

330
See also: [`first`](@ref), [`tail`](@ref Base.tail).
331

332
# Examples
333
```jldoctest
334
julia> Base.front((1,2,3))
335
(1, 2)
336

337
julia> Base.front(())
338
ERROR: ArgumentError: Cannot call front on an empty tuple.
339
```
340
"""
341
function front(t::Tuple)
342
    @inline
×
343
    _front(t...)
×
344
end
345
_front() = throw(ArgumentError("Cannot call front on an empty tuple."))
×
346
_front(v) = ()
×
347
function _front(v, t...)
348
    @inline
×
349
    (v, _front(t...)...)
×
350
end
351

352
## mapping ##
353

354
# 1 argument function
355
map(f, t::Tuple{})              = ()
×
356
map(f, t::Tuple{Any,})          = (@inline; (f(t[1]),))
4,399,374✔
357
map(f, t::Tuple{Any, Any})      = (@inline; (f(t[1]), f(t[2])))
817,572✔
358
map(f, t::Tuple{Any, Any, Any}) = (@inline; (f(t[1]), f(t[2]), f(t[3])))
1✔
359
map(f, t::Tuple)                = (@inline; (f(t[1]), map(f,tail(t))...))
×
360
# stop inlining after some number of arguments to avoid code blowup
361
const Any32{N} = Tuple{Any,Any,Any,Any,Any,Any,Any,Any,
362
                       Any,Any,Any,Any,Any,Any,Any,Any,
363
                       Any,Any,Any,Any,Any,Any,Any,Any,
364
                       Any,Any,Any,Any,Any,Any,Any,Any,
365
                       Vararg{Any,N}}
366
const All32{T,N} = Tuple{T,T,T,T,T,T,T,T,
367
                         T,T,T,T,T,T,T,T,
368
                         T,T,T,T,T,T,T,T,
369
                         T,T,T,T,T,T,T,T,
370
                         Vararg{T,N}}
371
function map(f, t::Any32)
×
372
    n = length(t)
×
373
    A = Vector{Any}(undef, n)
×
374
    for i=1:n
×
375
        A[i] = f(t[i])
×
376
    end
×
377
    (A...,)
×
378
end
379
# 2 argument function
380
map(f, t::Tuple{},        s::Tuple{})        = ()
×
381
map(f, t::Tuple,          s::Tuple{})        = ()
×
382
map(f, t::Tuple{},        s::Tuple)          = ()
×
383
map(f, t::Tuple{Any,},    s::Tuple{Any,})    = (@inline; (f(t[1],s[1]),))
5,455✔
384
map(f, t::Tuple{Any,Any}, s::Tuple{Any,Any}) = (@inline; (f(t[1],s[1]), f(t[2],s[2])))
×
385
function map(f, t::Tuple, s::Tuple)
×
386
    @inline
×
387
    (f(t[1],s[1]), map(f, tail(t), tail(s))...)
×
388
end
389
function map(f, t::Any32, s::Any32)
×
390
    n = min(length(t), length(s))
×
391
    A = Vector{Any}(undef, n)
×
392
    for i = 1:n
×
393
        A[i] = f(t[i], s[i])
×
394
    end
×
395
    (A...,)
×
396
end
397
# n argument function
398
heads(ts::Tuple...) = map(t -> t[1], ts)
×
399
tails(ts::Tuple...) = map(tail, ts)
×
400
map(f, ::Tuple{}, ::Tuple{}...) = ()
×
401
anyempty(x::Tuple{}, xs...) = true
×
402
anyempty(x::Tuple, xs...) = anyempty(xs...)
×
403
anyempty() = false
×
404
function map(f, t1::Tuple, t2::Tuple, ts::Tuple...)
×
405
    @inline
×
406
    anyempty(t1, t2, ts...) && return ()
×
407
    (f(heads(t1, t2, ts...)...), map(f, tails(t1, t2, ts...)...)...)
×
408
end
409
function map(f, t1::Any32, t2::Any32, ts::Any32...)
×
410
    n = min(length(t1), length(t2), minimum(length, ts))
×
411
    A = Vector{Any}(undef, n)
×
412
    for i = 1:n
×
413
        A[i] = f(t1[i], t2[i], map(t -> t[i], ts)...)
×
414
    end
×
415
    (A...,)
×
416
end
417

418
# type-stable padding
419
fill_to_length(t::NTuple{N,Any}, val, ::Val{N}) where {N} = t
×
420
fill_to_length(t::Tuple{}, val, ::Val{1}) = (val,)
×
421
fill_to_length(t::Tuple{Any}, val, ::Val{2}) = (t..., val)
×
422
fill_to_length(t::Tuple{}, val, ::Val{2}) = (val, val)
×
423
#function fill_to_length(t::Tuple, val, ::Val{N}) where {N}
424
#    @inline
425
#    return (t..., ntuple(i -> val, N - length(t))...)
426
#end
427

428
# constructing from an iterator
429

430
function tuple_type_tail(T::Type)
×
431
    @_foldable_meta # TODO: this method is wrong (and not :foldable)
×
432
    if isa(T, UnionAll)
×
433
        return UnionAll(T.var, tuple_type_tail(T.body))
×
434
    elseif isa(T, Union)
×
435
        return Union{tuple_type_tail(T.a), tuple_type_tail(T.b)}
×
436
    else
437
        T.name === Tuple.name || throw(MethodError(tuple_type_tail, (T,)))
×
438
        if isvatuple(T) && length(T.parameters) == 1
×
439
            va = unwrap_unionall(T.parameters[1])::Core.TypeofVararg
×
440
            (isdefined(va, :N) && isa(va.N, Int)) || return T
×
441
            return Tuple{Vararg{va.T, va.N-1}}
×
442
        end
443
        return Tuple{argtail(T.parameters...)...}
×
444
    end
445
end
446

447
(::Type{T})(x::Tuple) where {T<:Tuple} = x isa T ? x : convert(T, x)  # still use `convert` for tuples
×
448

449
Tuple(x::Ref) = tuple(getindex(x))  # faster than iterator for one element
×
450
Tuple(x::Array{T,0}) where {T} = tuple(getindex(x))
×
451

452
(::Type{T})(itr) where {T<:Tuple} = _totuple(T, itr)
5✔
453

454
_totuple(::Type{Tuple{}}, itr, s...) = ()
×
455

456
function _totuple_err(@nospecialize T)
×
457
    @noinline
×
458
    throw(ArgumentError(LazyString("too few elements for tuple type ", T)))
×
459
end
460

461
function _totuple(::Type{T}, itr, s::Vararg{Any,N}) where {T,N}
×
462
    @inline
×
463
    y = iterate(itr, s...)
×
464
    y === nothing && _totuple_err(T)
×
465
    T1 = fieldtype(T, 1)
×
466
    y1 = y[1]
×
467
    t1 = y1 isa T1 ? y1 : convert(T1, y1)::T1
×
468
    # inference may give up in recursive calls, so annotate here to force accurate return type to be propagated
469
    rT = tuple_type_tail(T)
×
470
    ts = _totuple(rT, itr, y[2])::rT
×
471
    return (t1, ts...)::T
×
472
end
473

474
# use iterative algorithm for long tuples
475
function _totuple(T::Type{All32{E,N}}, itr) where {E,N}
×
476
    len = N+32
×
477
    elts = collect(E, Iterators.take(itr,len))
×
478
    if length(elts) != len
×
479
        _totuple_err(T)
×
480
    end
481
    (elts...,)
×
482
end
483

484
_totuple(::Type{Tuple{Vararg{E}}}, itr, s...) where {E} = (collect(E, Iterators.rest(itr,s...))...,)
×
485

486
_totuple(::Type{Tuple}, itr, s...) = (collect(Iterators.rest(itr,s...))...,)
×
487

488
# for types that `apply` knows about, just splatting is faster than collecting first
489
_totuple(::Type{Tuple}, itr::Array) = (itr...,)
5✔
490
_totuple(::Type{Tuple}, itr::SimpleVector) = (itr...,)
×
491
_totuple(::Type{Tuple}, itr::NamedTuple) = (itr...,)
×
492
_totuple(::Type{Tuple}, p::Pair) = (p.first, p.second)
×
493
_totuple(::Type{Tuple}, x::Number) = (x,) # to make Tuple(x) inferable
×
494

495
## find ##
496

497
_findfirst_rec(f, i::Int, ::Tuple{}) = nothing
×
498
_findfirst_rec(f, i::Int, t::Tuple) = (@inline; f(first(t)) ? i : _findfirst_rec(f, i+1, tail(t)))
×
499
function _findfirst_loop(f::Function, t)
×
500
    for i in eachindex(t)
×
501
        f(t[i]) && return i
×
502
    end
×
503
    return nothing
×
504
end
505
findfirst(f::Function, t::Tuple) = length(t) < 32 ? _findfirst_rec(f, 1, t) : _findfirst_loop(f, t)
×
506

507
findlast(f::Function, t::Tuple) = length(t) < 32 ? _findlast_rec(f, t) : _findlast_loop(f, t)
×
508
function _findlast_rec(f::Function, x::Tuple)
×
509
    r = findfirst(f, reverse(x))
×
510
    return isnothing(r) ? r : length(x) - r + 1
×
511
end
512
function _findlast_loop(f::Function, t)
×
513
    for i in reverse(1:length(t))
×
514
        f(t[i]) && return i
×
515
    end
×
516
    return nothing
×
517
end
518

519
## filter ##
520

521
filter_rec(f, xs::Tuple) = afoldl((ys, x) -> f(x) ? (ys..., x) : ys, (), xs...)
311✔
522

523
# use Array for long tuples
524
filter(f, t::Tuple) = length(t) < 32 ? filter_rec(f, t) : Tuple(filter(f, collect(t)))
311✔
525

526
## comparison ##
527

528
isequal(t1::Tuple, t2::Tuple) = length(t1) == length(t2) && _isequal(t1, t2)
×
529
_isequal(::Tuple{}, ::Tuple{}) = true
×
530
function _isequal(t1::Tuple{Any,Vararg{Any}}, t2::Tuple{Any,Vararg{Any}})
531
    return isequal(t1[1], t2[1]) && _isequal(tail(t1), tail(t2))
×
532
end
533
function _isequal(t1::Any32, t2::Any32)
×
534
    for i in eachindex(t1, t2)
×
535
        if !isequal(t1[i], t2[i])
×
536
            return false
×
537
        end
538
    end
×
539
    return true
×
540
end
541

542
==(t1::Tuple, t2::Tuple) = (length(t1) == length(t2)) && _eq(t1, t2)
369,875✔
543
_eq(t1::Tuple{}, t2::Tuple{}) = true
×
544
_eq_missing(t1::Tuple{}, t2::Tuple{}) = missing
×
545
function _eq(t1::Tuple, t2::Tuple)
546
    eq = t1[1] == t2[1]
337,961✔
547
    if eq === false
337,961✔
548
        return false
132,685✔
549
    elseif ismissing(eq)
54✔
550
        return _eq_missing(tail(t1), tail(t2))
×
551
    else
552
        return _eq(tail(t1), tail(t2))
205,276✔
553
    end
554
end
555
function _eq_missing(t1::Tuple, t2::Tuple)
×
556
    eq = t1[1] == t2[1]
×
557
    if eq === false
×
558
        return false
×
559
    else
560
        return _eq_missing(tail(t1), tail(t2))
×
561
    end
562
end
563
function _eq(t1::Any32, t2::Any32)
×
564
    anymissing = false
×
565
    for i in eachindex(t1, t2)
×
566
        eq = (t1[i] == t2[i])
×
567
        if ismissing(eq)
×
568
            anymissing = true
×
569
        elseif !eq
×
570
           return false
×
571
       end
572
    end
×
573
    return anymissing ? missing : true
×
574
end
575

576
const tuplehash_seed = UInt === UInt64 ? 0x77cfa1eef01bca90 : 0xf01bca90
577
hash(::Tuple{}, h::UInt) = h + tuplehash_seed
14,559✔
578
hash(t::Tuple, h::UInt) = hash(t[1], hash(tail(t), h))
197,955✔
579
function hash(t::Any32, h::UInt)
×
580
    out = h + tuplehash_seed
×
581
    for i = length(t):-1:1
×
582
        out = hash(t[i], out)
×
583
    end
×
584
    return out
×
585
end
586

587
<(::Tuple{}, ::Tuple{}) = false
×
588
<(::Tuple{}, ::Tuple) = true
×
589
<(::Tuple, ::Tuple{}) = false
×
590
function <(t1::Tuple, t2::Tuple)
591
    a, b = t1[1], t2[1]
1✔
592
    eq = (a == b)
312,636✔
593
    if ismissing(eq)
×
594
        return missing
×
595
    elseif !eq
312,636✔
596
        return a < b
195,486✔
597
    end
598
    return tail(t1) < tail(t2)
117,150✔
599
end
600
function <(t1::Any32, t2::Any32)
×
601
    n1, n2 = length(t1), length(t2)
×
602
    for i = 1:min(n1, n2)
×
603
        a, b = t1[i], t2[i]
×
604
        eq = (a == b)
×
605
        if ismissing(eq)
×
606
            return missing
×
607
        elseif !eq
×
608
           return a < b
×
609
        end
610
    end
×
611
    return n1 < n2
×
612
end
613

614
isless(::Tuple{}, ::Tuple{}) = false
×
615
isless(::Tuple{}, ::Tuple) = true
×
616
isless(::Tuple, ::Tuple{}) = false
×
617

618
"""
619
    isless(t1::Tuple, t2::Tuple)
620

621
Return `true` when `t1` is less than `t2` in lexicographic order.
622
"""
623
function isless(t1::Tuple, t2::Tuple)
×
624
    a, b = t1[1], t2[1]
×
625
    isless(a, b) || (isequal(a, b) && isless(tail(t1), tail(t2)))
2,329✔
626
end
627
function isless(t1::Any32, t2::Any32)
×
628
    n1, n2 = length(t1), length(t2)
×
629
    for i = 1:min(n1, n2)
×
630
        a, b = t1[i], t2[i]
×
631
        if !isequal(a, b)
×
632
            return isless(a, b)
×
633
        end
634
    end
×
635
    return n1 < n2
×
636
end
637

638
## functions ##
639

640
isempty(x::Tuple{}) = true
×
641
isempty(@nospecialize x::Tuple) = false
35✔
642

643
revargs() = ()
×
644
revargs(x, r...) = (revargs(r...)..., x)
×
645

646
reverse(t::Tuple) = revargs(t...)
×
647

648
## specialized reduction ##
649

650
prod(x::Tuple{}) = 1
×
651
# This is consistent with the regular prod because there is no need for size promotion
652
# if all elements in the tuple are of system size.
653
# It is defined here separately in order to support bootstrap, because it's needed earlier
654
# than the general prod definition is available.
655
prod(x::Tuple{Int, Vararg{Int}}) = *(x...)
259✔
656

657
all(x::Tuple{}) = true
×
658
all(x::Tuple{Bool}) = x[1]
×
659
all(x::Tuple{Bool, Bool}) = x[1]&x[2]
×
660
all(x::Tuple{Bool, Bool, Bool}) = x[1]&x[2]&x[3]
×
661
all(x::Tuple{Any}) = x[1] || return false
×
662
all(f, x::Tuple{}) = true
×
663
all(f, x::Tuple{Any}) = all((f(x[1]),))
9✔
664

665
any(x::Tuple{}) = false
×
666
any(x::Tuple{Bool}) = x[1]
×
667
any(x::Tuple{Bool, Bool}) = x[1]|x[2]
×
668
any(x::Tuple{Bool, Bool, Bool}) = x[1]|x[2]|x[3]
×
669

670
# a version of `in` esp. for NamedTuple, to make it pure, and not compiled for each tuple length
671
function sym_in(x::Symbol, itr::Tuple{Vararg{Symbol}})
602,893✔
672
    @noinline
×
673
    @_total_meta
×
674
    for y in itr
602,893✔
675
        y === x && return true
4,659,018✔
676
    end
4,455,229✔
677
    return false
199,552✔
678
end
679
in(x::Symbol, itr::Tuple{Vararg{Symbol}}) = sym_in(x, itr)
1,237,732✔
680

681

682
"""
683
    empty(x::Tuple)
684

685
Return an empty tuple, `()`.
686
"""
687
empty(@nospecialize x::Tuple) = ()
×
688

UNCOV
689
foreach(f, itr::Tuple) = foldl((_, x) -> (f(x); nothing), itr, init=nothing)
×
690
foreach(f, itr::Tuple, itrs::Tuple...) = foldl((_, xs) -> (f(xs...); nothing), zip(itr, itrs...), init=nothing)
×
691

692
circshift((@nospecialize t::Union{Tuple{},Tuple{Any}}), @nospecialize _::Integer) = t
×
693
circshift(t::Tuple{Any,Any}, shift::Integer) = iseven(shift) ? t : reverse(t)
×
694
function circshift(x::Tuple{Any,Any,Any,Vararg{Any,N}}, shift::Integer) where {N}
×
695
    @inline
×
696
    len = N + 3
×
697
    j = mod1(shift, len)
×
698
    ntuple(k -> getindex(x, k-j+ifelse(k>j,0,len)), Val(len))::Tuple
×
699
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc