diff --git a/base/boot.jl b/base/boot.jl index d0a686e761bf5..8440465167315 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -147,8 +147,6 @@ export # constants nothing, Main -const AnyVector = Array{Any,1} - abstract type Number end abstract type Real <: Number end abstract type AbstractFloat <: Real end diff --git a/base/coreimg.jl b/base/coreimg.jl index 8b552d51252e7..10fc79a2947e5 100644 --- a/base/coreimg.jl +++ b/base/coreimg.jl @@ -16,6 +16,7 @@ isdefined(Main, :Base) || ((::Type{T})(arg) where {T} = convert(T, arg)::T) function return_type end ## Load essential files and libraries +include("pair.jl") include("essentials.jl") include("ctypes.jl") include("generator.jl") @@ -25,7 +26,6 @@ include("options.jl") # core operations & types include("promotion.jl") include("tuple.jl") -include("pair.jl") include("traits.jl") include("range.jl") include("expr.jl") diff --git a/base/dict.jl b/base/dict.jl index cd41a725d669f..73c78165ae2b0 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -595,81 +595,5 @@ function filter!(f, d::Union{ObjectIdDict,Dict}) return d end -struct ImmutableDict{K,V} <: Associative{K,V} - parent::ImmutableDict{K,V} - key::K - value::V - ImmutableDict{K,V}() where {K,V} = new() # represents an empty dictionary - ImmutableDict{K,V}(key, value) where {K,V} = (empty = new(); new(empty, key, value)) - ImmutableDict{K,V}(parent::ImmutableDict, key, value) where {K,V} = new(parent, key, value) -end - -""" - ImmutableDict - -ImmutableDict is a Dictionary implemented as an immutable linked list, -which is optimal for small dictionaries that are constructed over many individual insertions -Note that it is not possible to remove a value, although it can be partially overridden and hidden -by inserting a new value with the same key - - ImmutableDict(KV::Pair) - -Create a new entry in the Immutable Dictionary for the key => value pair - - - use `(key => value) in dict` to see if this particular combination is in the properties set - - use `get(dict, key, default)` to retrieve the most recent value for a particular key - -""" -ImmutableDict -ImmutableDict(KV::Pair{K,V}) where {K,V} = ImmutableDict{K,V}(KV[1], KV[2]) -ImmutableDict(t::ImmutableDict{K,V}, KV::Pair) where {K,V} = ImmutableDict{K,V}(t, KV[1], KV[2]) - -function in(key_value::Pair, dict::ImmutableDict, valcmp=(==)) - key, value = key_value - while isdefined(dict, :parent) - if dict.key == key - valcmp(value, dict.value) && return true - end - dict = dict.parent - end - return false -end - -function haskey(dict::ImmutableDict, key) - while isdefined(dict, :parent) - dict.key == key && return true - dict = dict.parent - end - return false -end - -function getindex(dict::ImmutableDict, key) - while isdefined(dict, :parent) - dict.key == key && return dict.value - dict = dict.parent - end - throw(KeyError(key)) -end -function get(dict::ImmutableDict, key, default) - while isdefined(dict, :parent) - dict.key == key && return dict.value - dict = dict.parent - end - return default -end - -# this actually defines reverse iteration (e.g. it should not be used for merge/copy/filter type operations) -start(t::ImmutableDict) = t -next(::ImmutableDict{K,V}, t) where {K,V} = (Pair{K,V}(t.key, t.value), t.parent) -done(::ImmutableDict, t) = !isdefined(t, :parent) -length(t::ImmutableDict) = count(x->true, t) -isempty(t::ImmutableDict) = done(t, start(t)) -function similar(t::ImmutableDict) - while isdefined(t, :parent) - t = t.parent - end - return t -end - _similar_for{P<:Pair}(c::Dict, ::Type{P}, itr, isz) = similar(c, P) _similar_for(c::Associative, T, itr, isz) = throw(ArgumentError("for Associatives, similar requires an element type of Pair;\n if calling map, consider a comprehension instead")) diff --git a/base/distributed/messages.jl b/base/distributed/messages.jl index 17a447a154d1f..bdca28c77ec30 100644 --- a/base/distributed/messages.jl +++ b/base/distributed/messages.jl @@ -38,17 +38,17 @@ null_id(id) = id == RRID(0, 0) struct CallMsg{Mode} <: AbstractMsg f::Function args::Tuple - kwargs::Array + kwargs::Base.KWDict end struct CallWaitMsg <: AbstractMsg f::Function args::Tuple - kwargs::Array + kwargs::Base.KWDict end struct RemoteDoMsg <: AbstractMsg f::Function args::Tuple - kwargs::Array + kwargs::Base.KWDict end struct ResultMsg <: AbstractMsg value::Any diff --git a/base/essentials.jl b/base/essentials.jl index c5322273ba4a5..0e4299f7032a5 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -336,7 +336,9 @@ function vector_any(xs::ANY...) a end -function as_kwargs(xs::Union{AbstractArray,Associative}) +as_kwargs(xs::Union{AbstractArray,Associative}) = collect_as_kwargs(xs) + +function collect_as_kwargs(xs::Union{AbstractArray,Associative}) n = length(xs) to = Vector{Any}(n*2) i = 1 @@ -369,3 +371,84 @@ call obsolete versions of a function `f`. `f` directly, and the type of the result cannot be inferred by the compiler.) """ invokelatest(f, args...) = Core._apply_latest(f, args) + +struct ImmutableDict{K,V} <: Associative{K,V} + parent::ImmutableDict{K,V} + key::K + value::V + ImmutableDict{K,V}() where {K,V} = new() # represents an empty dictionary + ImmutableDict{K,V}(key, value) where {K,V} = (empty = new(); new(empty, key, value)) + ImmutableDict{K,V}(parent::ImmutableDict, key, value) where {K,V} = new(parent, key, value) +end + +const KWDict = ImmutableDict{Symbol,Any} +const EmptyKWDict = KWDict() + +as_kwargs(xs::KWDict) = xs + +""" + ImmutableDict + +ImmutableDict is a Dictionary implemented as an immutable linked list, +which is optimal for small dictionaries that are constructed over many individual insertions +Note that it is not possible to remove a value, although it can be partially overridden and hidden +by inserting a new value with the same key + + ImmutableDict(KV::Pair) + +Create a new entry in the Immutable Dictionary for the key => value pair + + - use `(key => value) in dict` to see if this particular combination is in the properties set + - use `get(dict, key, default)` to retrieve the most recent value for a particular key + +""" +ImmutableDict +ImmutableDict(KV::Pair{K,V}) where {K,V} = ImmutableDict{K,V}(KV[1], KV[2]) +ImmutableDict(t::ImmutableDict{K,V}, KV::Pair) where {K,V} = ImmutableDict{K,V}(t, KV[1], KV[2]) + +function in(key_value::Pair, dict::ImmutableDict, valcmp=(==)) + key, value = key_value + while isdefined(dict, :parent) + if dict.key == key + valcmp(value, dict.value) && return true + end + dict = dict.parent + end + return false +end + +function haskey(dict::ImmutableDict, key) + while isdefined(dict, :parent) + dict.key == key && return true + dict = dict.parent + end + return false +end + +function getindex(dict::ImmutableDict, key) + while isdefined(dict, :parent) + dict.key == key && return dict.value + dict = dict.parent + end + throw(KeyError(key)) +end +function get(dict::ImmutableDict, key, default) + while isdefined(dict, :parent) + dict.key == key && return dict.value + dict = dict.parent + end + return default +end + +# this actually defines reverse iteration (e.g. it should not be used for merge/copy/filter type operations) +start(t::ImmutableDict) = t +next(::ImmutableDict{K,V}, t) where {K,V} = (Pair{K,V}(t.key, t.value), t.parent) +done(::ImmutableDict, t) = !isdefined(t, :parent) +length(t::ImmutableDict) = count(x->true, t) +isempty(t::ImmutableDict) = done(t, start(t)) +function similar(t::ImmutableDict) + while isdefined(t, :parent) + t = t.parent + end + return t +end diff --git a/base/methodshow.jl b/base/methodshow.jl index c9f304a509c9e..a258b9df7b627 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -67,7 +67,7 @@ function arg_decl_parts(m::Method) end function kwarg_decl(m::Method, kwtype::DataType) - sig = rewrap_unionall(Tuple{kwtype, Core.AnyVector, unwrap_unionall(m.sig).parameters...}, m.sig) + sig = rewrap_unionall(Tuple{kwtype, Any, unwrap_unionall(m.sig).parameters...}, m.sig) kwli = ccall(:jl_methtable_lookup, Any, (Any, Any, UInt), kwtype.name.mt, sig, typemax(UInt)) if kwli !== nothing kwli = kwli::Method diff --git a/base/sysimg.jl b/base/sysimg.jl index acfb4c7b68ee4..2e5a04122e3ba 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -45,6 +45,7 @@ if false end ## Load essential files and libraries +include("pair.jl") include("essentials.jl") include("ctypes.jl") include("base.jl") @@ -55,7 +56,6 @@ include("options.jl") # core operations & types include("promotion.jl") include("tuple.jl") -include("pair.jl") include("traits.jl") include("range.jl") include("twiceprecision.jl") diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index ac378d93a5a14..0f5e2f18b3f84 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -374,8 +374,6 @@ (or (number? x) (string? x) (char? x) (and (pair? x) (memq (car x) '(quote inert))) (eq? x 'true) (eq? x 'false))) -(define empty-vector-any '(call (core AnyVector) 0)) - (define (keywords-method-def-expr name sparams argl body isstaged rett) (let* ((kargl (cdar argl)) ;; keyword expressions (= k v) (pargl (cdr argl)) ;; positional args @@ -443,7 +441,7 @@ ;; call mangled(vals..., [rest_kw,] pargs..., [vararg]...) (return (call ,mangled ,@(if ordered-defaults keynames vals) - ,@(if (null? restkw) '() (list empty-vector-any)) + ,@(if (null? restkw) '() (list '(top EmptyKWDict))) ,@(map arg-name pargl) ,@(if (null? vararg) '() (list `(... ,(arg-name (car vararg)))))))) @@ -474,20 +472,29 @@ `((|::| ;; if there are optional positional args, we need to be able to reference the function name ,(if (any kwarg? pargl) (gensy) UNUSED) - (call (core kwftype) ,ftype)) (:: ,kw (core AnyVector)) ,@pargl ,@vararg) + (call (core kwftype) ,ftype)) ,kw ,@pargl ,@vararg) `(block ;; initialize keyword args to their defaults, or set a flag telling ;; whether this keyword needs to be set. ,@(map (lambda (kwname) `(local ,kwname)) keynames) - ,@(map (lambda (name dflt flag) - (if (const-default? dflt) - `(= ,name ,dflt) - `(= ,flag true))) - keynames vals flags) + ,@(apply append (map (lambda (name dflt) + (if (const-default? dflt) + `((= ,name ,dflt)) + '())) + keynames vals)) + ,@(map (lambda (flag) `(= ,flag true)) + flags) ,@(if (null? restkw) '() - `((= ,rkw ,empty-vector-any))) + `((= ,rkw (top EmptyKWDict)))) + ,(if (and (not (null? restkw)) (null? keynames)) + `(if (call (core isa) ,kw (top KWDict)) + (block + (= ,rkw ,kw) ;; reuse kwarg list for delegation if possible + (symbolicgoto do_call))) + `(if (call (core isa) ,kw (top KWDict)) + (= ,kw (call (top collect_as_kwargs) ,kw)))) ;; for i = 1:(length(kw)>>1) - (for (= ,i (: 1 (call (top >>) (call (top length) ,kw) 1))) + (for (= ,i (: (call (top >>) (call (top length) ,kw) 1) -1 1)) (block ;; ii = i*2 - 1 (= ,ii (call (top -) (call (top *) ,i 2) 1)) @@ -523,11 +530,10 @@ rval0))) ;; if kw[ii] == 'k; k = kw[ii+1]::Type; end `(if (comparison ,elt === (quote ,(decl-var k))) - (block - (= ,(decl-var k) ,rval) - ,@(if (not (const-default? (cadr kvf))) - `((= ,(caddr kvf) false)) - '())) + (if ,(caddr kvf) + (block + (= ,(decl-var k) ,rval) + (= ,(caddr kvf) false))) ,else))) (if (null? restkw) ;; if no rest kw, give error for unrecognized @@ -535,10 +541,10 @@ ,@(if (null? vararg) '() (list `(... ,(arg-name (car vararg)))))) ;; otherwise add to rest keywords - `(foreigncall 'jl_array_ptr_1d_push (core Void) (call (core svec) Any Any) - ,rkw 0 (tuple ,elt - (call (core arrayref) ,kw - (call (top +) ,ii 1))) 0)) + `(if (call (top haskey) ,rkw ,elt) + (null) + (= ,rkw (call (top KWDict) ,rkw ,elt (call (core arrayref) ,kw + (call (top +) ,ii 1)))))) (map list vars vals flags)))) ;; set keywords that weren't present to their default values ,@(apply append @@ -547,6 +553,7 @@ '() `((if ,flag (= ,name ,dflt))))) keynames vals flags)) + (symboliclabel do_call) ;; finally, call the core function (return (call ,mangled ,@keynames diff --git a/test/keywordargs.jl b/test/keywordargs.jl index 0ac76fa7bcaaf..bd13515814ac4 100644 --- a/test/keywordargs.jl +++ b/test/keywordargs.jl @@ -188,7 +188,7 @@ function test4974(;kwargs...) end end -@test test4974(a=1) == (2, [(:a, 1)]) +@test test4974(a=1) == (2, Base.KWDict(:a, 1)) # issue #7704, computed keywords @test kwf1(1; (:tens, 2)) == 21 @@ -285,3 +285,17 @@ let a = 0 g21518()(; :kw=>1) @test a == 2 end + +# issue #4916 - rest keywords as a dict +let ks(;xs...) = xs + d = ks(a=1, b=2) + @test d[:a] == 1 + @test d[:b] == 2 + # preserving order + @test collect(ks(a=1, b=2)) == [:a=>1, :b=>2] + @test collect(ks(b=2, a=1)) == [:b=>2, :a=>1] + # deduplication + @test collect(ks(b=2, a=1; :b=>3)) == [:a=>1, :b=>3] + @test isempty(ks()) + @test eltype(ks()) <: Pair +end diff --git a/test/parse.jl b/test/parse.jl index 12377dbdbb01e..c7f99d587fb20 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -989,7 +989,7 @@ let f = function (x; kw...) g = function (x; a = 2) return (x, a) end - @test f(1) == (1, Any[]) + @test f(1) == (1, Base.KWDict()) @test g(1) == (1, 2) end