Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backports for julia 1.9.0-beta3 #48075

Merged
merged 40 commits into from
Jan 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1365281
Update the libuv checksums (#47763)
DilumAluthge Dec 1, 2022
c22bdcc
Don't double-count inference time (#48033)
pchintalapudi Dec 29, 2022
0ede754
[CompilerSupportLibraries_jll] Add libssp for more platforms (#48027)
giordano Dec 29, 2022
106f5be
Adjust assertion for pkgimage & code-coverage (#48036)
vchuravy Dec 30, 2022
05c8182
use the correct env variable name to set default openblas num threads…
KristofferC Jan 1, 2023
14d3caf
update Pkg
KristofferC Jan 2, 2023
13c1435
use invokelatest to prevent invalidations in TOML
KristofferC Jan 2, 2023
149aa57
bump Pkg
KristofferC Jan 3, 2023
78bc9e0
Julia 1.9 should use the Pkg.jl `release-1.9` branch (#48105)
DilumAluthge Jan 5, 2023
08d4d81
Switch back to LLVM's IR linker (#48106)
pchintalapudi Jan 5, 2023
8e88b1b
Avoid a couple of InexactErrors in the IdDict code. (#48116)
maleadt Jan 5, 2023
6c5a4a5
🤖 [backports-release-1.9] Bump the Pkg stdlib from d3883ad7b to 5ae86…
DilumAluthgeBot Jan 9, 2023
bf8d4ed
Merge pull request #48158 from JuliaLang/avi/inbounds
aviatesk Jan 10, 2023
46cd080
Avoid allocations in reduction over adjoints (#48120)
dkarrasch Jan 5, 2023
a064027
Don't perform extra inference during incremental image creation (#48054)
Keno Jan 5, 2023
6c0c8b3
Deprecate AMD's LWP extension (#48131)
vchuravy Jan 5, 2023
cb13bad
Fix invalidations in `finish_show_ir` (#48134)
timholy Jan 5, 2023
fd788ad
Make QuickerSort efficient for non-homogonous eltype (#47973)
LilithHafner Jan 5, 2023
3296f9b
Math tests: if fma is not available, relax some tests from exact equa…
DilumAluthge Jan 6, 2023
fdf89d8
Make cache mismatch log more informative (#48168)
IanButterworth Jan 8, 2023
acb7e09
Extend method root to support more than 16bit roots (#48185)
vchuravy Jan 9, 2023
5547468
ensure jl_compilation_sig does not narrow Vararg (#48152)
vtjnash Jan 9, 2023
d5fbff1
add a suffix to a new cache files in case of failure of renaming it t…
KristofferC Jan 9, 2023
fed0c20
🤖 [backports-release-1.9] Bump the Pkg stdlib from 5ae866151 to 747c1…
DilumAluthgeBot Jan 13, 2023
8d14fc3
Stop using `rand(lo:hi)` for QuickerSort pivot selection (#48241)
LilithHafner Jan 12, 2023
979fd53
move some badly typed logging calls behind an invokelatest (#48254)
KristofferC Jan 12, 2023
32cae57
also cache `identify_package` and `locate_package` during package loa…
KristofferC Jan 13, 2023
ba490ff
docstring for `@time_imports`: explain what is shown (it's not cumula…
tfiers Jan 13, 2023
cbc63ac
rename QuickerSort to ScratchQuickSort (#48160)
LilithHafner Jan 13, 2023
68f4230
Don't deprecate splat (#48038)
knuesel Jan 13, 2023
f5fbc2d
[backports-release-1.9] add pkgimages to NEWS (#48286)
IanButterworth Jan 16, 2023
5d67bdc
🤖 [master] Bump the Tar stdlib from 6bfc114 to ff55460 (#48268)
DilumAluthgeBot Jan 13, 2023
f053566
Add inline to cache flags (#48179)
vchuravy Jan 14, 2023
c615cdd
bump Pkg to latest v1.9
KristofferC Jan 16, 2023
c168996
Make LLVM Profiling robust for multithreaded programs (#47778)
bachdavi Jan 11, 2023
36d9822
Merge pull request #48029 from N5N3/inter-fix3
N5N3 Jan 4, 2023
698aafe
Make sure `reachable_var` not falls into infinite recusion. (#48135)
N5N3 Jan 7, 2023
6694375
Rework :inbounds effects tainting (#48246)
Keno Jan 12, 2023
829c5bf
Make DemoteFloat16 a conditional pass (#43327)
gbaraldi Nov 21, 2022
05d3871
Fix small nits in multiversioning (#47675)
gbaraldi Jan 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ Language changes
Compiler/Runtime improvements
-----------------------------

* Time to first execution (TTFX, sometimes called time to first plot) is greatly reduced. Package precompilation now
saves native code into a "pkgimage", meaning that code generated during the precompilation process will not
require compilation after package load. Use of pkgimages can be disabled via `--pkgimages=no` ([#44527]) ([#47184]).
* The known quadratic behavior of type inference is now fixed and inference uses less memory in general.
Certain edge cases with auto-generated long functions (e.g. ModelingToolkit.jl with partial
differential equations and large causal models) should see significant compile-time improvements ([#45276], [#45404]).
Expand Down
4 changes: 4 additions & 0 deletions base/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,10 @@ for m in methods(include)
delete_method(m)
end

# This method is here only to be overwritten during the test suite to test
# various sysimg related invalidation scenarios.
a_method_to_overwrite_in_test() = inferencebarrier(1)

# These functions are duplicated in client.jl/include(::String) for
# nicer stacktraces. Modifications here have to be backported there
include(mod::Module, _path::AbstractString) = _include(identity, mod, _path)
Expand Down
68 changes: 49 additions & 19 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -781,9 +781,18 @@ end
# - false: eligible for semi-concrete evaluation
# - nothing: not eligible for either of it
function concrete_eval_eligible(interp::AbstractInterpreter,
@nospecialize(f), result::MethodCallResult, arginfo::ArgInfo)
@nospecialize(f), result::MethodCallResult, arginfo::ArgInfo, sv::InferenceState)
# disable all concrete-evaluation if this function call is tainted by some overlayed
# method since currently there is no direct way to execute overlayed methods
if inbounds_option() === :off
# Disable concrete evaluation in `--check-bounds=no` mode, since we cannot be sure
# that inferred effects are accurate.
return nothing
elseif !result.effects.noinbounds && stmt_taints_inbounds_consistency(sv)
# If the current statement is @inbounds or we propagate inbounds, the call's consistency
# is tainted and not consteval eligible.
return nothing
end
isoverlayed(method_table(interp)) && !is_nonoverlayed(result.effects) && return nothing
if f !== nothing && result.edge !== nothing && is_foldable(result.effects)
if is_all_const_arg(arginfo, #=start=#2)
Expand Down Expand Up @@ -824,7 +833,7 @@ end
function concrete_eval_call(interp::AbstractInterpreter,
@nospecialize(f), result::MethodCallResult, arginfo::ArgInfo, si::StmtInfo,
sv::InferenceState, invokecall::Union{Nothing,InvokeCall}=nothing)
eligible = concrete_eval_eligible(interp, f, result, arginfo)
eligible = concrete_eval_eligible(interp, f, result, arginfo, sv)
eligible === nothing && return false
if eligible
args = collect_const_args(arginfo, #=start=#2)
Expand Down Expand Up @@ -2026,22 +2035,32 @@ function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, vtypes::V
nothing
end

function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, sv::Union{InferenceState, IRCode})
function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable, Nothing}, sv::Union{InferenceState, IRCode})
rt = Any
head = e.head
if head === :static_parameter
n = e.args[1]::Int
t = Any
if 1 <= n <= length(sv.sptypes)
t = sv.sptypes[n]
rt = sv.sptypes[n]
end
return t
elseif head === :boundscheck
return Bool
if isa(sv, InferenceState)
flag = sv.src.ssaflags[sv.currpc]
# If there is no particular @inbounds for this function, then we only taint `noinbounds`,
# which will subsequently taint consistency if this function is called from another
# function that uses `@inbounds`. However, if this :boundscheck is itself within an
# `@inbounds` region, its value depends on `--check-bounds`, so we need to taint
# consistency here also.
merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; noinbounds=false,
consistent = (flag & IR_FLAG_INBOUNDS) != 0 ? ALWAYS_FALSE : ALWAYS_TRUE))
end
rt = Bool
elseif head === :inbounds
@assert false && "Expected this to have been moved into flags"
elseif head === :the_exception
merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; consistent=ALWAYS_FALSE))
return Any
end
return Any
return rt
end

function abstract_eval_special_value(interp::AbstractInterpreter, @nospecialize(e), vtypes::Union{VarTable, Nothing}, sv::Union{InferenceState, IRCode})
Expand Down Expand Up @@ -2071,7 +2090,7 @@ end

function abstract_eval_value(interp::AbstractInterpreter, @nospecialize(e), vtypes::Union{VarTable, Nothing}, sv::Union{InferenceState, IRCode})
if isa(e, Expr)
return abstract_eval_value_expr(interp, e, sv)
return abstract_eval_value_expr(interp, e, vtypes, sv)
else
typ = abstract_eval_special_value(interp, e, vtypes, sv)
return collect_limitations!(typ, sv)
Expand Down Expand Up @@ -2112,7 +2131,6 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
arginfo = ArgInfo(ea, argtypes)
si = StmtInfo(isa(sv, IRCode) ? true : !call_result_unused(sv, sv.currpc))
(; rt, effects, info) = abstract_call(interp, arginfo, si, sv)
merge_effects!(interp, sv, effects)
if isa(sv, InferenceState)
sv.stmt_info[sv.currpc] = info
end
Expand Down Expand Up @@ -2176,7 +2194,6 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
nothrow = false
end
effects = Effects(EFFECTS_TOTAL; consistent, nothrow)
merge_effects!(interp, sv, effects)
elseif ehead === :splatnew
t, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv))
nothrow = false # TODO: More precision
Expand All @@ -2195,7 +2212,6 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
end
consistent = !ismutabletype(t) ? ALWAYS_TRUE : CONSISTENT_IF_NOTRETURNED
effects = Effects(EFFECTS_TOTAL; consistent, nothrow)
merge_effects!(interp, sv, effects)
elseif ehead === :new_opaque_closure
t = Union{}
effects = Effects() # TODO
Expand Down Expand Up @@ -2223,20 +2239,16 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
elseif ehead === :foreigncall
(;rt, effects) = abstract_eval_foreigncall(interp, e, vtypes, sv, mi)
t = rt
merge_effects!(interp, sv, effects)
elseif ehead === :cfunction
effects = EFFECTS_UNKNOWN
merge_effects!(interp, sv, effects)
t = e.args[1]
isa(t, Type) || (t = Any)
abstract_eval_cfunction(interp, e, vtypes, sv)
elseif ehead === :method
t = (length(e.args) == 1) ? Any : Nothing
effects = EFFECTS_UNKNOWN
merge_effects!(interp, sv, effects)
elseif ehead === :copyast
effects = EFFECTS_UNKNOWN
merge_effects!(interp, sv, effects)
t = abstract_eval_value(interp, e.args[1], vtypes, sv)
if t isa Const && t.val isa Expr
# `copyast` makes copies of Exprs
Expand All @@ -2247,6 +2259,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
elseif ehead === :isdefined
sym = e.args[1]
t = Bool
effects = EFFECTS_TOTAL
if isa(sym, SlotNumber)
vtyp = vtypes[slot_id(sym)]
if vtyp.typ === Bottom
Expand Down Expand Up @@ -2275,9 +2288,9 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
@label always_throw
t = Bottom
effects = EFFECTS_THROWS
merge_effects!(interp, sv, effects)
else
t = abstract_eval_value_expr(interp, e, sv)
t = abstract_eval_value_expr(interp, e, vtypes, sv)
effects = EFFECTS_TOTAL
end
return RTEffects(t, effects)
end
Expand Down Expand Up @@ -2319,6 +2332,11 @@ function abstract_eval_phi(interp::AbstractInterpreter, phi::PhiNode, vtypes::Un
return rt
end

function stmt_taints_inbounds_consistency(sv::InferenceState)
flag = sv.src.ssaflags[sv.currpc]
return sv.src.propagate_inbounds || (flag & IR_FLAG_INBOUNDS) != 0
end

function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), vtypes::VarTable, sv::InferenceState)
if !isa(e, Expr)
if isa(e, PhiNode)
Expand All @@ -2327,6 +2345,18 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e),
return abstract_eval_special_value(interp, e, vtypes, sv)
end
(;rt, effects) = abstract_eval_statement_expr(interp, e, vtypes, sv, nothing)
if !effects.noinbounds
flag = sv.src.ssaflags[sv.currpc]
if !sv.src.propagate_inbounds
# The callee read our inbounds flag, but unless we propagate inbounds,
# we ourselves don't read our parent's inbounds.
effects = Effects(effects; noinbounds=true)
end
if (flag & IR_FLAG_INBOUNDS) != 0
effects = Effects(effects; consistent=ALWAYS_FALSE)
end
end
merge_effects!(interp, sv, effects)
e = e::Expr
@assert !isa(rt, TypeVar) "unhandled TypeVar"
rt = maybe_singleton_const(rt)
Expand Down
20 changes: 11 additions & 9 deletions base/compiler/effects.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ following meanings:
This state corresponds to LLVM's `inaccessiblemem_or_argmemonly` function attribute.
- `nonoverlayed::Bool`: indicates that any methods that may be called within this method
are not defined in an [overlayed method table](@ref OverlayMethodTable).
- `noinbounds::Bool`: indicates this method can't be `:consistent` because of bounds checking.
This effect is currently only set on `InferenceState` construction and used to taint
`:consistent`-cy before caching. We may want to track it with more accuracy in the future.
- `noinbounds::Bool`: If set, indicates that this method does not read the parent's :inbounds
state. In particular, it does not have any reached :boundscheck exprs, not propagates inbounds
to any children that do.

Note that the representations above are just internal implementation details and thus likely
to change in the future. See [`Base.@assume_effects`](@ref) for more detailed explanation
Expand Down Expand Up @@ -98,10 +98,10 @@ const EFFECT_FREE_IF_INACCESSIBLEMEMONLY = 0x01 << 1
# :inaccessiblememonly bits
const INACCESSIBLEMEM_OR_ARGMEMONLY = 0x01 << 1

const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, true)
const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, true)
const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, true) # unknown mostly, but it's not overlayed at least (e.g. it's not a call)
const EFFECTS_UNKNOWN′ = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, false) # unknown really
const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, true, true)
const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, true, true)
const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, true, true) # unknown mostly, but it's not overlayed at least (e.g. it's not a call)
const EFFECTS_UNKNOWN′ = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, false, true) # unknown really

function Effects(e::Effects = EFFECTS_UNKNOWN′;
consistent::UInt8 = e.consistent,
Expand Down Expand Up @@ -184,7 +184,8 @@ function encode_effects(e::Effects)
((e.terminates % UInt32) << 6) |
((e.notaskstate % UInt32) << 7) |
((e.inaccessiblememonly % UInt32) << 8) |
((e.nonoverlayed % UInt32) << 10)
((e.nonoverlayed % UInt32) << 10)|
((e.noinbounds % UInt32) << 11)
end

function decode_effects(e::UInt32)
Expand All @@ -195,7 +196,8 @@ function decode_effects(e::UInt32)
_Bool((e >> 6) & 0x01),
_Bool((e >> 7) & 0x01),
UInt8((e >> 8) & 0x03),
_Bool((e >> 10) & 0x01))
_Bool((e >> 10) & 0x01),
_Bool((e >> 11) & 0x01))
end

struct EffectsOverride
Expand Down
21 changes: 1 addition & 20 deletions base/compiler/inferencestate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,7 @@ mutable struct InferenceState

valid_worlds = WorldRange(src.min_world, src.max_world == typemax(UInt) ? get_world_counter() : src.max_world)
bestguess = Bottom
# TODO: Currently, any :inbounds declaration taints consistency,
# because we cannot be guaranteed whether or not boundschecks
# will be eliminated and if they are, we cannot be guaranteed
# that no undefined behavior will occur (the effects assumptions
# are stronger than the inbounds assumptions, since the latter
# requires dynamic reachability, while the former is global).
inbounds = inbounds_option()
noinbounds = inbounds === :on || (inbounds === :default && !any_inbounds(code))
consistent = noinbounds ? ALWAYS_TRUE : ALWAYS_FALSE
ipo_effects = Effects(EFFECTS_TOTAL; consistent, noinbounds)
ipo_effects = Effects(EFFECTS_TOTAL)

params = InferenceParams(interp)
restrict_abstract_call_sites = isa(linfo.def, Module)
Expand Down Expand Up @@ -240,16 +231,6 @@ function bail_out_apply(::AbstractInterpreter, @nospecialize(rt), sv::Union{Infe
return rt === Any
end

function any_inbounds(code::Vector{Any})
for i = 1:length(code)
stmt = code[i]
if isexpr(stmt, :inbounds)
return true
end
end
return false
end

was_reached(sv::InferenceState, pc::Int) = sv.ssavaluetypes[pc] !== NOT_FOUND

function compute_trycatch(code::Vector{Any}, ip::BitSet)
Expand Down
4 changes: 3 additions & 1 deletion base/compiler/ssair/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -858,7 +858,7 @@ function show_ir_stmts(io::IO, ir::Union{IRCode, CodeInfo, IncrementalCompact},
return bb_idx
end

function finish_show_ir(io::IO, cfg, config::IRShowConfig)
function finish_show_ir(io::IO, cfg::CFG, config::IRShowConfig)
max_bb_idx_size = length(string(length(cfg.blocks)))
config.line_info_preprinter(io, " "^(max_bb_idx_size + 2), 0)
return nothing
Expand Down Expand Up @@ -1012,6 +1012,8 @@ function Base.show(io::IO, e::Effects)
printstyled(io, effectbits_letter(e, :notaskstate, 's'); color=effectbits_color(e, :notaskstate))
print(io, ',')
printstyled(io, effectbits_letter(e, :inaccessiblememonly, 'm'); color=effectbits_color(e, :inaccessiblememonly))
print(io, ',')
printstyled(io, effectbits_letter(e, :noinbounds, 'i'); color=effectbits_color(e, :noinbounds))
print(io, ')')
e.nonoverlayed || printstyled(io, '′'; color=:red)
end
Expand Down
2 changes: 1 addition & 1 deletion base/compiler/tfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2039,7 +2039,7 @@ function getfield_effects(argtypes::Vector{Any}, @nospecialize(rt))
# If we cannot independently prove inboundsness, taint consistency.
# The inbounds-ness assertion requires dynamic reachability, while
# :consistent needs to be true for all input values.
# N.B. We do not taint for `--check-bounds=no` here -that happens in
# N.B. We do not taint for `--check-bounds=no` here that happens in
# InferenceState.
if length(argtypes) ≥ 2 && getfield_nothrow(argtypes[1], argtypes[2], true)
nothrow = true
Expand Down
2 changes: 0 additions & 2 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,6 @@ const var"@_noinline_meta" = var"@noinline"

# BEGIN 1.9 deprecations

@deprecate splat(x) Splat(x) false

# We'd generally like to avoid direct external access to internal fields
# Core.Compiler.is_inlineable and Core.Compiler.set_inlineable! move towards this direction,
# but we need to keep these around for compat
Expand Down
2 changes: 1 addition & 1 deletion base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -816,7 +816,7 @@ export
atreplinit,
exit,
ntuple,
Splat,
splat,

# I/O and events
close,
Expand Down
4 changes: 4 additions & 0 deletions base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,10 @@ currently equivalent to the following `setting`s:
however, that by the `:consistent`-cy requirements, any such annotated call
must consistently throw given the same argument values.

!!! note
An explict `@inbounds` annotation inside the function will also disable
constant propagation and not be overriden by :foldable.

---
## `:total`

Expand Down
6 changes: 3 additions & 3 deletions base/iddict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ end

empty(d::IdDict, ::Type{K}, ::Type{V}) where {K, V} = IdDict{K,V}()

function rehash!(d::IdDict, newsz = length(d.ht))
function rehash!(d::IdDict, newsz = length(d.ht)%UInt)
d.ht = ccall(:jl_idtable_rehash, Vector{Any}, (Any, Csize_t), d.ht, newsz)
d
end
Expand All @@ -89,7 +89,7 @@ function setindex!(d::IdDict{K,V}, @nospecialize(val), @nospecialize(key)) where
val = convert(V, val)
end
if d.ndel >= ((3*length(d.ht))>>2)
rehash!(d, max(length(d.ht)>>1, 32))
rehash!(d, max((length(d.ht)%UInt)>>1, 32))
d.ndel = 0
end
inserted = RefValue{Cint}(0)
Expand Down Expand Up @@ -143,7 +143,7 @@ end
_oidd_nextind(a, i) = reinterpret(Int, ccall(:jl_eqtable_nextind, Csize_t, (Any, Csize_t), a, i))

function iterate(d::IdDict{K,V}, idx=0) where {K, V}
idx = _oidd_nextind(d.ht, idx)
idx = _oidd_nextind(d.ht, idx%UInt)
idx == -1 && return nothing
return (Pair{K, V}(d.ht[idx + 1]::K, d.ht[idx + 2]::V), idx + 2)
end
Expand Down
2 changes: 1 addition & 1 deletion base/iterators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ the `zip` iterator is a tuple of values of its subiterators.

`zip()` with no arguments yields an infinite iterator of empty tuples.

See also: [`enumerate`](@ref), [`Splat`](@ref Base.Splat).
See also: [`enumerate`](@ref), [`Base.splat`](@ref).

# Examples
```jldoctest
Expand Down
Loading