Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions Compiler/test/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6515,4 +6515,11 @@ end <: Bool
Core.get_binding_type(m, n, xs...)
end <: Type

# issue #59269
function haskey_inference_test()
kwargs = Core.compilerbarrier(:const, Base.pairs((; item = false)))
return haskey(kwargs, :item) ? nothing : Any[]
end
@inferred haskey_inference_test()

end # module inference
4 changes: 2 additions & 2 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -503,9 +503,9 @@ end
Pairs{K, V, I, A}(data, itr) where {K, V, I, A} = $(Expr(:new, :(Pairs{K, V, I, A}), :(data isa A ? data : convert(A, data)), :(itr isa I ? itr : convert(I, itr))))
Pairs{K, V}(data::A, itr::I) where {K, V, I, A} = $(Expr(:new, :(Pairs{K, V, I, A}), :data, :itr))
Pairs{K}(data::A, itr::I) where {K, I, A} = $(Expr(:new, :(Pairs{K, eltype(A), I, A}), :data, :itr))
Pairs(data::A, itr::I) where {I, A} = $(Expr(:new, :(Pairs{eltype(I), eltype(A), I, A}), :data, :itr))
Pairs(data::A, itr::I) where {I, A} = $(Expr(:new, :(Pairs{I !== Nothing ? eltype(I) : keytype(A), eltype(A), I, A}), :data, :itr))
end
pairs(::Type{NamedTuple}) = Pairs{Symbol, V, NTuple{N, Symbol}, NamedTuple{names, T}} where {V, N, names, T<:NTuple{N, Any}}
pairs(::Type{NamedTuple}) = Pairs{Symbol, V, Nothing, NT} where {V, NT <: NamedTuple}

"""
Base.Pairs(values, keys) <: AbstractDict{eltype(keys), eltype(values)}
Expand Down
20 changes: 10 additions & 10 deletions base/iterators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -267,48 +267,48 @@ pairs(::IndexLinear, A::AbstractArray) = Pairs(A, LinearIndices(A))
# preserve indexing capabilities for known indexable types
# faster than zip(keys(a), values(a)) for arrays
pairs(tuple::Tuple) = Pairs{Int}(tuple, keys(tuple))
pairs(nt::NamedTuple) = Pairs{Symbol}(nt, keys(nt))
pairs(nt::NamedTuple) = Pairs{Symbol}(nt, nothing)
pairs(v::Core.SimpleVector) = Pairs(v, LinearIndices(v))
pairs(A::AbstractVector) = pairs(IndexLinear(), A)
# pairs(v::Pairs) = v # listed for reference, but already defined from being an AbstractDict

pairs(::IndexCartesian, A::AbstractArray) = Pairs(A, Base.CartesianIndices(axes(A)))
pairs(A::AbstractArray) = pairs(IndexCartesian(), A)

length(v::Pairs) = length(getfield(v, :itr))
axes(v::Pairs) = axes(getfield(v, :itr))
size(v::Pairs) = size(getfield(v, :itr))
length(v::Pairs) = length(keys(v))
axes(v::Pairs) = axes(keys(v))
size(v::Pairs) = size(keys(v))

Base.@eval @propagate_inbounds function _pairs_elt(p::Pairs{K, V}, idx) where {K, V}
return $(Expr(:new, :(Pair{K, V}), :idx, :(getfield(p, :data)[idx])))
end

@propagate_inbounds function iterate(p::Pairs{K, V}, state...) where {K, V}
x = iterate(getfield(p, :itr), state...)
x = iterate(keys(p), state...)
x === nothing && return x
idx, next = x
return (_pairs_elt(p, idx), next)
end

@propagate_inbounds function iterate(r::Reverse{<:Pairs}, state=(reverse(getfield(r.itr, :itr)),))
@propagate_inbounds function iterate(r::Reverse{<:Pairs}, state=(reverse(keys(r.itr)),))
x = iterate(state...)
x === nothing && return x
idx, next = x
return (_pairs_elt(r.itr, idx), (state[1], next))
end

@inline isdone(v::Pairs, state...) = isdone(getfield(v, :itr), state...)
@inline isdone(v::Pairs, state...) = isdone(keys(v), state...)

IteratorSize(::Type{<:Pairs{<:Any, <:Any, I}}) where {I} = IteratorSize(I)
IteratorSize(::Type{<:Pairs{<:Any, <:Any, <:AbstractUnitRange, <:Tuple}}) = HasLength()

function last(v::Pairs{K, V}) where {K, V}
idx = last(getfield(v, :itr))
idx = last(keys(v))
return Pair{K, V}(idx, v[idx])
end

haskey(v::Pairs, key) = (key in getfield(v, :itr))
keys(v::Pairs) = getfield(v, :itr)
haskey(v::Pairs, key) = key in keys(v)
keys(v::Pairs) = getfield(v, :itr) === nothing ? keys(getfield(v, :data)) : getfield(v, :itr)
values(v::Pairs) = getfield(v, :data) # TODO: this should be a view of data subset by itr
getindex(v::Pairs, key) = getfield(v, :data)[key]
setindex!(v::Pairs, value, key) = (getfield(v, :data)[key] = value; v)
Expand Down
6 changes: 3 additions & 3 deletions base/namedtuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ merge(a::NamedTuple, b::NamedTuple{()}) = a
merge(a::NamedTuple{()}, b::NamedTuple{()}) = a
merge(a::NamedTuple{()}, b::NamedTuple) = b

merge(a::NamedTuple, b::Iterators.Pairs{<:Any,<:Any,<:Any,<:NamedTuple}) = merge(a, getfield(b, :data))
merge(a::NamedTuple, b::Iterators.Pairs{<:Any,<:Any,Nothing,<:NamedTuple}) = merge(a, getfield(b, :data))

merge(a::NamedTuple, b::Iterators.Zip{<:Tuple{Any,Any}}) = merge(a, NamedTuple{Tuple(b.is[1])}(b.is[2]))

Expand Down Expand Up @@ -535,7 +535,7 @@ when it is printed in the stack trace view.

```julia
julia> @Kwargs{init::Int} # the internal representation of keyword arguments
Base.Pairs{Symbol, Int64, Tuple{Symbol}, @NamedTuple{init::Int64}}
Base.Pairs{Symbol, Int64, Nothing, @NamedTuple{init::Int64}}

julia> sum("julia"; init=1)
ERROR: MethodError: no method matching +(::Char, ::Char)
Expand Down Expand Up @@ -578,7 +578,7 @@ Stacktrace:
macro Kwargs(ex)
return :(let
NT = @NamedTuple $ex
Base.Pairs{keytype(NT),eltype(NT),typeof(NT.parameters[1]),NT}
Base.Pairs{keytype(NT),eltype(NT),Nothing,NT}
end)
end

Expand Down
6 changes: 4 additions & 2 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,8 @@ function show_type_name(io::IO, tn::Core.TypeName)
end

function maybe_kws_nt(x::DataType)
# manually-written version of
# x <: (Pairs{Symbol, eltype(NT), Nothing, NT} where NT <: NamedTuple)
x.name === typename(Pairs) || return nothing
length(x.parameters) == 4 || return nothing
x.parameters[1] === Symbol || return nothing
Expand All @@ -1125,7 +1127,7 @@ function maybe_kws_nt(x::DataType)
types isa DataType || return nothing
x.parameters[2] === eltype(p4) || return nothing
isa(syms, Tuple) || return nothing
x.parameters[3] === typeof(syms) || return nothing
x.parameters[3] === Nothing || return nothing
return p4
end
return nothing
Expand Down Expand Up @@ -3219,7 +3221,7 @@ function Base.showarg(io::IO, r::Iterators.Pairs{<:Integer, <:Any, <:Any, T}, to
print(io, "pairs(IndexLinear(), ::", T, ")")
end

function Base.showarg(io::IO, r::Iterators.Pairs{Symbol, <:Any, <:Any, T}, toplevel) where {T <: NamedTuple}
function Base.showarg(io::IO, r::Iterators.Pairs{Symbol, <:Any, Nothing, T}, toplevel) where {T <: NamedTuple}
print(io, "pairs(::NamedTuple)")
end

Expand Down
4 changes: 4 additions & 0 deletions test/namedtuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ end
@test merge(NamedTuple(), [:a=>1, :b=>2, :c=>3, :a=>4, :c=>5]) == (a=4, b=2, c=5)
@test merge((c=0, z=1), [:a=>1, :b=>2, :c=>3, :a=>4, :c=>5]) == (c=5, z=1, a=4, b=2)

# https://github.com/JuliaLang/julia/issues/59292
@test merge((; a = 1), Base.Pairs((; b = 2, c = 3), (:b,))) == (a = 1, b = 2)
@test merge((; a = 1), Base.pairs((; b = 2, c = 3))) == (a = 1, b = 2, c = 3)

@test keys((a=1, b=2, c=3)) == (:a, :b, :c)
@test keys(NamedTuple()) == ()
@test keys((a=1,)) == (:a,)
Expand Down
2 changes: 1 addition & 1 deletion test/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1450,7 +1450,7 @@ test_repr("(:).a")
@test repr(@NamedTuple{var"#"::Int64}) == "@NamedTuple{var\"#\"::Int64}"

# Test general printing of `Base.Pairs` (it should not use the `@Kwargs` macro syntax)
@test repr(@Kwargs{init::Int}) == "Base.Pairs{Symbol, $Int, Tuple{Symbol}, @NamedTuple{init::$Int}}"
@test repr(@Kwargs{init::Int}) == "Base.Pairs{Symbol, $Int, Nothing, @NamedTuple{init::$Int}}"

@testset "issue #42931" begin
@test repr(NTuple{4, :A}) == "Tuple{:A, :A, :A, :A}"
Expand Down