diff --git a/base/coreimg.jl b/base/coreimg.jl index 8b552d51252e70..10fc79a2947e5f 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 cd41a725d669f9..73c78165ae2b08 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 17a447a154d1fa..bdca28c77ec30c 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 c5322273ba4a56..805fa564ef2a1e 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -369,3 +369,82 @@ 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() + +""" + 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/sysimg.jl b/base/sysimg.jl index acfb4c7b68ee47..2e5a04122e3bad 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 842327fe740d72..ae869625e2fb6c 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)))))))) @@ -479,15 +477,17 @@ ;; 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)))) ;; 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 +523,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 +534,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 diff --git a/test/keywordargs.jl b/test/keywordargs.jl index 0ac76fa7bcaafa..d5388430d6a38b 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