From 04507d05bacb18aabc3b147444c4f5d21d7d815a Mon Sep 17 00:00:00 2001
From: Andy Ferris <andy.ferris@roames.com.au>
Date: Sun, 10 Dec 2017 00:33:29 +1000
Subject: [PATCH] WIP: deprecate iteration on bare `Associative`.

Users should use `pairs(dict)` or `values(dict)` (or `keys(dict)`) to
disambiguate iteration result.
---
 NEWS.md                     |   4 ++
 base/associative.jl         | 109 +++++++++++++++++++++---------------
 base/deepcopy.jl            |   2 +-
 base/deprecated.jl          |   5 ++
 base/dict.jl                |  21 ++++---
 base/docs/utils.jl          |   2 +-
 base/env.jl                 |   8 +--
 base/exports.jl             |   1 +
 base/inference.jl           |   2 +-
 base/interactiveutil.jl     |   4 +-
 base/process.jl             |   2 +-
 base/repl/LineEdit.jl       |   6 +-
 base/repl/REPL.jl           |   2 +-
 base/replutil.jl            |   4 +-
 base/serialize.jl           |   2 +-
 base/show.jl                |   6 +-
 base/sparse/sparsevector.jl |   4 +-
 base/weakkeydict.jl         |   6 +-
 stdlib/Test/src/Test.jl     |   8 +--
 test/dict.jl                |  37 ++++++------
 test/env.jl                 |   4 +-
 21 files changed, 135 insertions(+), 104 deletions(-)

diff --git a/NEWS.md b/NEWS.md
index 7d1277a02dc3a..5b6f89bce201d 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -738,6 +738,10 @@ Deprecated or removed
   * The `sum_kbn` and `cumsum_kbn` functions have been moved to the
     [KahanSummation](https://github.com/JuliaMath/KahanSummation.jl) package ([#24869]).
 
+  * Iteration (`next`, `eltype`, `in`, `map`, etc) on `Associative`s has been deprecated.
+    Explicitly nominate `pairs(dict)`, `values(dict)` or `keys(dict)` as appropriate
+    ([#25013]).
+
 Command-line option changes
 ---------------------------
 
diff --git a/base/associative.jl b/base/associative.jl
index f3719ba65eaac..56c39590c55a8 100644
--- a/base/associative.jl
+++ b/base/associative.jl
@@ -16,20 +16,6 @@ const secret_table_token = :__c782dbf1cf4d6a2e5e3865d7e95634f2e09b5902__
 
 haskey(d::Associative, k) = in(k, keys(d))
 
-function in(p::Pair, a::Associative, valcmp=(==))
-    v = get(a,p[1],secret_table_token)
-    if v !== secret_table_token
-        valcmp(v, p[2]) && return true
-    end
-    return false
-end
-
-function in(p, a::Associative)
-    error("""Associative collections only contain Pairs;
-             Either look for e.g. A=>B instead, or use the `keys` or `values`
-             function if you are looking for a key or value respectively.""")
-end
-
 function summary(t::Associative)
     n = length(t)
     return string(typeof(t), " with ", n, (n==1 ? " entry" : " entries"))
@@ -44,26 +30,38 @@ struct ValueIterator{T<:Associative}
     dict::T
 end
 
-summary(iter::T) where {T<:Union{KeySet,ValueIterator}} =
+struct PairIterator{T<:Associative}
+    dict::T
+end
+
+function in(p::Pair, a::PairIterator, valcmp=(==))
+    v = get(a.dict, p[1], secret_table_token)
+    if v !== secret_table_token
+        valcmp(v, p[2]) && return true
+    end
+    return false
+end
+
+summary(iter::T) where {T<:Union{KeySet,ValueIterator,PairIterator}} =
     string(T.name, " for a ", summary(iter.dict))
 
-show(io::IO, iter::Union{KeySet,ValueIterator}) = show(io, collect(iter))
+show(io::IO, iter::Union{KeySet,ValueIterator,PairIterator}) = show(io, collect(iter))
 
-length(v::Union{KeySet,ValueIterator}) = length(v.dict)
-isempty(v::Union{KeySet,ValueIterator}) = isempty(v.dict)
-_tt2(::Type{Pair{A,B}}) where {A,B} = B
-eltype(::Type{ValueIterator{D}}) where {D} = _tt2(eltype(D))
+length(v::Union{KeySet,ValueIterator,PairIterator}) = length(v.dict)
+isempty(v::Union{KeySet,ValueIterator,PairIterator}) = isempty(v.dict)
+eltype(::Type{ValueIterator{D}}) where {D} = valtype(D)
+eltype(::Type{PairIterator{D}}) where {D} = pairtype(D)
 
-start(v::Union{KeySet,ValueIterator}) = start(v.dict)
-done(v::Union{KeySet,ValueIterator}, state) = done(v.dict, state)
+start(v::Union{KeySet,ValueIterator,PairIterator}) = start(v.dict)
+done(v::Union{KeySet,ValueIterator,PairIterator}, state) = done(v.dict, state)
 
 function next(v::KeySet, state)
-    n = next(v.dict, state)
+    n = next(PairIterator(v.dict), state)
     n[1][1], n[2]
 end
 
 function next(v::ValueIterator, state)
-    n = next(v.dict, state)
+    n = next(PairIterator(v.dict), state)
     n[1][2], n[2]
 end
 
@@ -136,7 +134,13 @@ This includes arrays, where the keys are the array indices.
 """
 pairs(collection) = Generator(=>, keys(collection), values(collection))
 
-pairs(a::Associative) = a
+pairs(a::Associative) = PairIterator(a)
+
+function next(a::Associative, i)
+    println("next(::Associative, ::Any) is deprecated.")
+    display(stacktrace()); println() # TODO remove after problems fixed
+    next(PairIterator(a), i)
+end
 
 """
     empty(a::Associative, [index_type=keytype(a)], [value_type=valtype(a)])
@@ -155,7 +159,7 @@ empty(a::Associative, ::Type{V}) where {V} = empty(a, keytype(a), V) # Note: thi
 
 function copy(a::Associative)
     b = empty(a)
-    for (k,v) in a
+    for (k,v) in pairs(a)
         b[k] = v
     end
     return b
@@ -184,7 +188,7 @@ Dict{Int64,Int64} with 3 entries:
 """
 function merge!(d::Associative, others::Associative...)
     for other in others
-        for (k,v) in other
+        for (k,v) in pairs(other)
             d[k] = v
         end
     end
@@ -223,7 +227,7 @@ Dict{Int64,Int64} with 3 entries:
 """
 function merge!(combine::Function, d::Associative, others::Associative...)
     for other in others
-        for (k,v) in other
+        for (k,v) in pairs(other)
             d[k] = haskey(d, k) ? combine(d[k], v) : v
         end
     end
@@ -250,9 +254,9 @@ julia> keytype(Dict(Int32(1) => "foo"))
 Int32
 ```
 """
-keytype(::Type{Associative{K,V}}) where {K,V} = K
+keytype(::Type{Associative{K, V}}) where {K, V} = K
 keytype(a::Associative) = keytype(typeof(a))
-keytype(::Type{A}) where {A<:Associative} = keytype(supertype(A))
+keytype(::Type{A}) where {A <: Associative} = keytype(supertype(A))
 
 """
     valtype(type)
@@ -265,10 +269,25 @@ julia> valtype(Dict(Int32(1) => "foo"))
 String
 ```
 """
-valtype(::Type{Associative{K,V}}) where {K,V} = V
-valtype(::Type{A}) where {A<:Associative} = valtype(supertype(A))
+valtype(::Type{Associative{K, V}}) where {K, V} = V
+valtype(::Type{A}) where {A <: Associative} = valtype(supertype(A))
 valtype(a::Associative) = valtype(typeof(a))
 
+"""
+    pairtype(type)
+
+Get the `Pair` type of an associative collection type. Behaves similarly to [`eltype`](@ref).
+
+# Examples
+```jldoctest
+julia> pairtype(Dict(Int32(1) => "foo"))
+Pair{Int32,String}
+```
+"""
+pairtype(::Type{Associative{K, V}}) where {K, V} = Pair{K, V}
+pairtype(::Type{A}) where {A <: Associative} = pairtype(supertype(A))
+pairtype(a::Associative) = pairtype(typeof(a))
+
 """
     merge(d::Associative, others::Associative...)
 
@@ -368,7 +387,7 @@ Dict{Int64,String} with 2 entries:
 function filter!(f, d::Associative)
     badkeys = Vector{keytype(d)}()
     try
-        for pair in d
+        for pair in pairs(d)
             # don't delete!(d, k) here, since associative types
             # may not support mutation during iteration
             f(pair) || push!(badkeys, pair.first)
@@ -384,7 +403,7 @@ end
 
 function filter_in_one_pass!(f, d::Associative)
     try
-        for pair in d
+        for pair in pairs(d)
             if !f(pair)
                 delete!(d, pair.first)
             end
@@ -399,7 +418,7 @@ function filter!_dict_deprecation(e, f, d::Associative)
     if isa(e, MethodError) && e.f === f
         depwarn("In `filter!(f, dict)`, `f` is now passed a single pair instead of two arguments.", :filter!)
         badkeys = Vector{keytype(d)}()
-        for (k,v) in d
+        for (k,v) in pairs(d)
             # don't delete!(d, k) here, since associative types
             # may not support mutation during iteration
             f(k, v) || push!(badkeys, k)
@@ -435,7 +454,7 @@ function filter(f, d::Associative)
     # don't just do filter!(f, copy(d)): avoid making a whole copy of d
     df = empty(d)
     try
-        for pair in d
+        for pair in pairs(d)
             if f(pair)
                 df[pair.first] = pair.second
             end
@@ -443,7 +462,7 @@ function filter(f, d::Associative)
     catch e
         if isa(e, MethodError) && e.f === f
             depwarn("In `filter(f, dict)`, `f` is now passed a single pair instead of two arguments.", :filter)
-            for (k, v) in d
+            for (k, v) in pairs(d)
                 if f(k, v)
                     df[k] = v
                 end
@@ -455,16 +474,14 @@ function filter(f, d::Associative)
     return df
 end
 
-eltype(::Type{Associative{K,V}}) where {K,V} = Pair{K,V}
-
 function isequal(l::Associative, r::Associative)
     l === r && return true
     if isa(l,ObjectIdDict) != isa(r,ObjectIdDict)
         return false
     end
     if length(l) != length(r) return false end
-    for pair in l
-        if !in(pair, r, isequal)
+    for pair in pairs(l)
+        if !in(pair, pairs(r), isequal)
             return false
         end
     end
@@ -477,8 +494,8 @@ function ==(l::Associative, r::Associative)
         return false
     end
     if length(l) != length(r) return false end
-    for pair in l
-        if !in(pair, r, ==)
+    for pair in pairs(l)
+        if !in(pair, pairs(r), ==)
             return false
         end
     end
@@ -488,7 +505,7 @@ end
 const hasha_seed = UInt === UInt64 ? 0x6d35bb51952d5539 : 0x952d5539
 function hash(a::Associative, h::UInt)
     hv = hasha_seed
-    for (k,v) in a
+    for (k,v) in pairs(a)
         hv ⊻= hash(k, hash(v))
     end
     hash(hv, h)
@@ -599,11 +616,11 @@ _oidd_nextind(a, i) = reinterpret(Int,ccall(:jl_eqtable_nextind, Csize_t, (Any,
 
 start(t::ObjectIdDict) = _oidd_nextind(t.ht, 0)
 done(t::ObjectIdDict, i) = (i == -1)
-next(t::ObjectIdDict, i) = (Pair{Any,Any}(t.ht[i+1],t.ht[i+2]), _oidd_nextind(t.ht, i+2))
+next(t::PairIterator{<:ObjectIdDict}, i) = (Pair{Any,Any}(t.dict.ht[i+1],t.dict.ht[i+2]), _oidd_nextind(t.dict.ht, i+2))
 
 function length(d::ObjectIdDict)
     n = 0
-    for pair in d
+    for pair in pairs(d)
         n+=1
     end
     n
diff --git a/base/deepcopy.jl b/base/deepcopy.jl
index 2f22a32679697..aca6f3fb632fa 100644
--- a/base/deepcopy.jl
+++ b/base/deepcopy.jl
@@ -107,7 +107,7 @@ function deepcopy_internal(x::Dict, stackdict::ObjectIdDict)
 
     dest = empty(x)
     stackdict[x] = dest
-    for (k, v) in x
+    for (k, v) in pairs(x)
         dest[deepcopy_internal(k, stackdict)] = deepcopy_internal(v, stackdict)
     end
     dest
diff --git a/base/deprecated.jl b/base/deprecated.jl
index 68960bb291104..e8278f9b38f00 100644
--- a/base/deprecated.jl
+++ b/base/deprecated.jl
@@ -2186,6 +2186,11 @@ end
 @deprecate_moved sum_kbn "KahanSummation"
 @deprecate_moved cumsum_kbn "KahanSummation"
 
+# PR #25013
+@deprecate eltype(A::Type{Associative{K,V}}) where {K,V} pairtype(A)
+@deprecate next(a::Associative, i) next(pairs(a), i)
+#@deprecate Base.in(p::Pair, a::Associative, valcmp = (==)) Base.in(p, PairIterator(a), valcmp)
+
 # END 0.7 deprecations
 
 # BEGIN 1.0 deprecations
diff --git a/base/dict.jl b/base/dict.jl
index bd3ac41287e55..41a3ccf09674c 100644
--- a/base/dict.jl
+++ b/base/dict.jl
@@ -43,7 +43,7 @@ function show(io::IO, t::Associative{K,V}) where V where K
         if !show_circular(io, t)
             first = true
             n = 0
-            for pair in t
+            for pair in pairs(t)
                 first || print(io, ',')
                 first = false
                 show(recur_io, pair)
@@ -139,9 +139,12 @@ Dict(ps::Pair{K}...)             where {K}   = Dict{K,Any}(ps)
 Dict(ps::(Pair{K,V} where K)...) where {V}   = Dict{Any,V}(ps)
 Dict(ps::Pair...)                            = Dict{Any,Any}(ps)
 
+pair_or_eltype(x) = eltype(x)
+pair_or_eltype(x::Associative) = pairtype(x)
+
 function Dict(kv)
     try
-        associative_with_eltype((K, V) -> Dict{K, V}, kv, eltype(kv))
+        associative_with_eltype((K, V) -> Dict{K, V}, kv, pair_or_eltype(kv))
     catch e
         if !applicable(start, kv) || !all(x->isa(x,Union{Tuple,Pair}),kv)
             throw(ArgumentError("Dict(kv): kv needs to be an iterator of tuples or pairs"))
@@ -193,7 +196,7 @@ empty(a::Associative, ::Type{K}, ::Type{V}) where {K, V} = Dict{K, V}()
 # conversion between Dict types
 function convert(::Type{Dict{K,V}},d::Associative) where V where K
     h = Dict{K,V}()
-    for (k,v) in d
+    for (k,v) in pairs(d)
         ck = convert(K,k)
         if !haskey(h,ck)
             h[ck] = convert(V,v)
@@ -711,8 +714,8 @@ function start(t::Dict)
     return i
 end
 done(t::Dict, i) = i > length(t.vals)
-@propagate_inbounds function next(t::Dict{K,V}, i) where {K,V}
-    return (Pair{K,V}(t.keys[i],t.vals[i]), skip_deleted(t,i+1))
+@propagate_inbounds function next(t::PairIterator{Dict{K,V}}, i) where {K,V}
+    return (Pair{K,V}(t.dict.keys[i],t.dict.vals[i]), skip_deleted(t.dict,i+1))
 end
 
 isempty(t::Dict) = (t.count == 0)
@@ -756,11 +759,11 @@ 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=(==))
+function in(key_value::Pair, dict::PairIterator{<:ImmutableDict}, valcmp=(==))
     key, value = key_value
     while isdefined(dict, :parent)
-        if dict.key == key
-            valcmp(value, dict.value) && return true
+        if dict.dict.key == key
+            valcmp(value, dict.dict.value) && return true
         end
         dict = dict.parent
     end
@@ -792,7 +795,7 @@ 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)
+next(::PairIterator{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))
diff --git a/base/docs/utils.jl b/base/docs/utils.jl
index b4a1f4eb947ec..66f245d98d190 100644
--- a/base/docs/utils.jl
+++ b/base/docs/utils.jl
@@ -139,7 +139,7 @@ repl_corrections(s) = repl_corrections(STDOUT, s)
 const symbols_latex = Dict{String,String}()
 function symbol_latex(s::String)
     if isempty(symbols_latex)
-        for (k,v) in Base.REPLCompletions.latex_symbols
+        for (k,v) in pairs(Base.REPLCompletions.latex_symbols)
             symbols_latex[v] = k
         end
     end
diff --git a/base/env.jl b/base/env.jl
index e7849373e308f..379532f10b845 100644
--- a/base/env.jl
+++ b/base/env.jl
@@ -81,7 +81,7 @@ pop!(::EnvDict, k::AbstractString) = (v = ENV[k]; _unsetenv(k); v)
 pop!(::EnvDict, k::AbstractString, def) = haskey(ENV,k) ? pop!(ENV,k) : def
 delete!(::EnvDict, k::AbstractString) = (_unsetenv(k); ENV)
 setindex!(::EnvDict, v, k::AbstractString) = _setenv(k,string(v))
-push!(::EnvDict, k::AbstractString, v) = setindex!(ENV, v, k)
+push!(::EnvDict, k::AbstractString, v) = setindex!(ENV, v, k) # Should this be a `Pair`?
 
 if Sys.iswindows()
     start(hash::EnvDict) = (pos = ccall(:GetEnvironmentStringsW,stdcall,Ptr{UInt16},()); (pos,pos))
@@ -92,7 +92,7 @@ if Sys.iswindows()
         end
         return false
     end
-    function next(hash::EnvDict, block::Tuple{Ptr{UInt16},Ptr{UInt16}})
+    function next(hash::PairIterator{EnvDict}, block::Tuple{Ptr{UInt16},Ptr{UInt16}})
         pos = block[1]
         blk = block[2]
         len = ccall(:wcslen, UInt, (Ptr{UInt16},), pos)
@@ -109,7 +109,7 @@ else # !windows
     start(::EnvDict) = 0
     done(::EnvDict, i) = (ccall(:jl_environ, Any, (Int32,), i) === nothing)
 
-    function next(::EnvDict, i)
+    function next(::PairIterator{EnvDict}, i)
         env = ccall(:jl_environ, Any, (Int32,), i)
         if env === nothing
             throw(BoundsError())
@@ -126,7 +126,7 @@ end # os-test
 #TODO: Make these more efficent
 function length(::EnvDict)
     i = 0
-    for (k,v) in ENV
+    for (k,v) in pairs(ENV)
         i += 1
     end
     return i
diff --git a/base/exports.jl b/base/exports.jl
index ed3f5d2b34b49..85b7cd29d300c 100644
--- a/base/exports.jl
+++ b/base/exports.jl
@@ -82,6 +82,7 @@ export
     ObjectIdDict,
     OrdinalRange,
     Pair,
+    PairIterator,
     PartialQuickSort,
     PermutedDimsArray,
     QuickSort,
diff --git a/base/inference.jl b/base/inference.jl
index 6b5f867eabf7f..190ecb36a2ab4 100644
--- a/base/inference.jl
+++ b/base/inference.jl
@@ -5686,7 +5686,7 @@ function remove_redundant_temp_vars!(src::CodeInfo, nargs::Int, sa::ObjectIdDict
     slottypes = src.slottypes
     ssavaluetypes = src.ssavaluetypes
     repls = ObjectIdDict()
-    for (v, init) in sa
+    for (v, init) in pairs(sa)
         repl = get_replacement(sa, v, init, nargs, slottypes, ssavaluetypes)
         compare = isa(repl, TypedSlot) ? normslot(repl) : repl
         if compare !== v
diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl
index fdad979f6b664..1165a6ce7becd 100644
--- a/base/interactiveutil.jl
+++ b/base/interactiveutil.jl
@@ -314,13 +314,13 @@ function versioninfo(io::IO=STDOUT; verbose::Bool=false, packages::Bool=false)
     println(io, "  LLVM: libLLVM-",libllvm_version," (", Sys.JIT, ", ", Sys.cpu_name, ")")
 
     println(io, "Environment:")
-    for (k,v) in ENV
+    for (k,v) in pairs(ENV)
         if ismatch(r"JULIA", String(k))
             println(io, "  $(k) = $(v)")
         end
     end
     if verbose
-        for (k,v) in ENV
+        for (k,v) in pairs(ENV)
             if ismatch(r"PATH|FLAG|^TERM$|HOME", String(k))
                 println(io, "  $(k) = $(v)")
             end
diff --git a/base/process.jl b/base/process.jl
index 0d56aaee5b2f1..3d80dd00ba574 100644
--- a/base/process.jl
+++ b/base/process.jl
@@ -208,7 +208,7 @@ end
 byteenv(env::AbstractArray{<:AbstractString}) =
     String[cstr(x) for x in env]
 byteenv(env::Associative) =
-    String[cstr(string(k)*"="*string(v)) for (k,v) in env]
+    String[cstr(string(k)*"="*string(v)) for (k,v) in pairs(env)]
 byteenv(env::Void) = nothing
 byteenv(env::Union{AbstractVector{Pair{T}}, Tuple{Vararg{Pair{T}}}}) where {T<:AbstractString} =
     String[cstr(k*"="*string(v)) for (k,v) in env]
diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl
index 5e09ce730b6c6..da5dff938bc07 100644
--- a/base/repl/LineEdit.jl
+++ b/base/repl/LineEdit.jl
@@ -1233,7 +1233,7 @@ end
 
 function normalize_keys(keymap::Dict)
     ret = Dict{Any,Any}()
-    for (k,v) in keymap
+    for (k,v) in pairs(keymap)
         normalized = normalize_key(k)
         if haskey(ret,normalized)
             error("""Multiple spellings of a key in a single keymap
@@ -1396,7 +1396,7 @@ function postprocess!(dict::Dict)
     if haskey(dict, wildcard)
         add_specialisations(dict, dict, 1)
     end
-    for (k,v) in dict
+    for (k,v) in pairs(dict)
         k == wildcard && continue
         postprocess!(v)
     end
@@ -2179,7 +2179,7 @@ function reset_state(s::PromptState)
 end
 
 function reset_state(s::MIState)
-    for (mode, state) in s.mode_state
+    for (mode, state) in pairs(s.mode_state)
         reset_state(state)
     end
 end
diff --git a/base/repl/REPL.jl b/base/repl/REPL.jl
index c291bd7de8ca4..2bfdc64887c51 100644
--- a/base/repl/REPL.jl
+++ b/base/repl/REPL.jl
@@ -432,7 +432,7 @@ end
 
 function mode_idx(hist::REPLHistoryProvider, mode)
     c = :julia
-    for (k,v) in hist.mode_mapping
+    for (k,v) in pairs(hist.mode_mapping)
         isequal(v, mode) && (c = k)
     end
     return c
diff --git a/base/replutil.jl b/base/replutil.jl
index 65a72b0864007..9258b7e4d9939 100644
--- a/base/replutil.jl
+++ b/base/replutil.jl
@@ -59,7 +59,7 @@ function show(io::IO, ::MIME"text/plain", t::Associative{K,V}) where {K,V}
         vs = Vector{AbstractString}(uninitialized, min(rows, length(t)))
         keylen = 0
         vallen = 0
-        for (i, (k, v)) in enumerate(t)
+        for (i, (k, v)) in enumerate(pairs(t))
             i > rows && break
             ks[i] = sprint(0, show, k, env=recur_io)
             vs[i] = sprint(0, show, v, env=recur_io)
@@ -73,7 +73,7 @@ function show(io::IO, ::MIME"text/plain", t::Associative{K,V}) where {K,V}
         rows = cols = typemax(Int)
     end
 
-    for (i, (k, v)) in enumerate(t)
+    for (i, (k, v)) in enumerate(pairs(t))
         print(io, "\n  ")
         i == rows < length(t) && (print(io, rpad("⋮", keylen), " => ⋮"); break)
 
diff --git a/base/serialize.jl b/base/serialize.jl
index 2efe10e480de6..0f6b8149be028 100644
--- a/base/serialize.jl
+++ b/base/serialize.jl
@@ -345,7 +345,7 @@ end
 function serialize(s::AbstractSerializer, d::Dict)
     serialize_cycle_header(s, d) && return
     write(s.io, Int32(length(d)))
-    for (k,v) in d
+    for (k,v) in pairs(d)
         serialize(s, k)
         serialize(s, v)
     end
diff --git a/base/show.jl b/base/show.jl
index 15ab70c465584..9e051c865ea25 100644
--- a/base/show.jl
+++ b/base/show.jl
@@ -94,7 +94,7 @@ pipe_writer(io::IOContext) = io.io
 lock(io::IOContext) = lock(io.io)
 unlock(io::IOContext) = unlock(io.io)
 
-in(key_value::Pair, io::IOContext) = in(key_value, io.dict, ===)
+in(key_value::Pair, io::IOContext) = in(key_value, pairs(io.dict), ===)
 in(key_value::Pair, io::IO) = false
 haskey(io::IOContext, key) = haskey(io.dict, key)
 haskey(io::IO, key) = false
@@ -108,7 +108,7 @@ displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize] : displ
 show_circular(io::IO, @nospecialize(x)) = false
 function show_circular(io::IOContext, @nospecialize(x))
     d = 1
-    for (k, v) in io.dict
+    for (k, v) in pairs(io.dict)
         if k === :SHOWN_SET
             if v === x
                 print(io, "#= circular reference @-$d =#")
@@ -215,7 +215,7 @@ end
 has_typevar(@nospecialize(t), v::TypeVar) = ccall(:jl_has_typevar, Cint, (Any, Any), t, v)!=0
 
 function io_has_tvar_name(io::IOContext, name::Symbol, @nospecialize(x))
-    for (key, val) in io.dict
+    for (key, val) in pairs(io.dict)
         if key === :unionall_env && val isa TypeVar && val.name === name && has_typevar(x, val)
             return true
         end
diff --git a/base/sparse/sparsevector.jl b/base/sparse/sparsevector.jl
index 4cb4f3ce8f3b7..5cc6d51d5669f 100644
--- a/base/sparse/sparsevector.jl
+++ b/base/sparse/sparsevector.jl
@@ -244,7 +244,7 @@ function sparsevec(dict::Associative{Ti,Tv}) where {Tv,Ti<:Integer}
 
     cnt = 0
     len = zero(Ti)
-    for (k, v) in dict
+    for (k, v) in pairs(dict)
         k >= 1 || throw(ArgumentError("index must be positive."))
         if k > len
             len = k
@@ -265,7 +265,7 @@ function sparsevec(dict::Associative{Ti,Tv}, len::Integer) where {Tv,Ti<:Integer
 
     cnt = 0
     maxk = convert(Ti, len)
-    for (k, v) in dict
+    for (k, v) in pairs(dict)
         1 <= k <= maxk || throw(ArgumentError("an index (key) is out of bound."))
         cnt += 1
         @inbounds nzind[cnt] = k
diff --git a/base/weakkeydict.jl b/base/weakkeydict.jl
index f07d2f29f5ea0..49422260719ad 100644
--- a/base/weakkeydict.jl
+++ b/base/weakkeydict.jl
@@ -73,7 +73,7 @@ empty(d::WeakKeyDict, ::Type{K}, ::Type{V}) where {K, V} = WeakKeyDict{K, V}()
 # conversion between Dict types
 function convert(::Type{WeakKeyDict{K,V}},d::Associative) where V where K
     h = WeakKeyDict{K,V}()
-    for (k,v) in d
+    for (k,v) in pairs(d)
         ck = convert(K,k)
         if !haskey(h,ck)
             h[ck] = convert(V,v)
@@ -132,9 +132,9 @@ function start(t::WeakKeyDict{K,V}) where V where K
     return (start(t.ht), gc_token)
 end
 done(t::WeakKeyDict, i) = done(t.ht, i[1])
-function next(t::WeakKeyDict{K,V}, i)  where V where K
+function next(t::PairIterator{WeakKeyDict{K,V}}, i)  where V where K
     gc_token = i[2]
-    wkv, i = next(t.ht, i[1])
+    wkv, i = next(PairIterator(t.dict.ht), i[1])
     kv = Pair{K,V}(wkv[1].value::K, wkv[2])
     return (kv, (i, gc_token))
 end
diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl
index 916834c42bf3b..b4a53c76eef06 100644
--- a/stdlib/Test/src/Test.jl
+++ b/stdlib/Test/src/Test.jl
@@ -1420,14 +1420,14 @@ struct GenericDict{K,V} <: Associative{K,V}
     s::Associative{K,V}
 end
 
-for (G, A) in ((GenericSet, AbstractSet),
-               (GenericDict, Associative))
+for (G, I, A) in ((GenericSet, GenericSet, AbstractSet),
+               (GenericDict, PairIterator{<:GenericSet}, Associative))
     @eval begin
         Base.convert(::Type{$G}, s::$A) = $G(s)
         Base.done(s::$G, state) = done(s.s, state)
-        Base.next(s::$G, state) = next(s.s, state)
+        Base.next(s::$I, state) = next(s.s, state)
     end
-    for f in (:eltype, :isempty, :length, :start)
+    for f in (:isempty, :length, :start)
         @eval begin
             Base.$f(s::$G) = $f(s.s)
         end
diff --git a/test/dict.jl b/test/dict.jl
index 7212022e51ea4..1b674c662129d 100644
--- a/test/dict.jl
+++ b/test/dict.jl
@@ -77,13 +77,13 @@ end
     h["a","b","c"] = 4
     @test h["a","b","c"] == h[("a","b","c")] == 4
 
-    @testset "eltype, keytype and valtype" begin
-        @test eltype(h) == Pair{Any,Any}
+    @testset "pairtype, keytype and valtype" begin
+        @test pairtype(h) == Pair{Any,Any}
         @test keytype(h) == Any
         @test valtype(h) == Any
 
         td = Dict{AbstractString,Float64}()
-        @test eltype(td) == Pair{AbstractString,Float64}
+        @test pairtype(td) == Pair{AbstractString,Float64}
         @test keytype(td) == AbstractString
         @test valtype(td) == Float64
         @test keytype(Dict{AbstractString,Float64}) === AbstractString
@@ -421,7 +421,7 @@ end
     d = @inferred ObjectIdDict(Pair(1,1), Pair(2,2), Pair(3,3))
     @test isa(d, ObjectIdDict)
     @test d == ObjectIdDict(1=>1, 2=>2, 3=>3)
-    @test eltype(d) == Pair{Any,Any}
+    @test pairtype(d) == Pair{Any,Any}
 end
 
 @testset "Issue #7944" begin
@@ -435,7 +435,8 @@ end
 @testset "iteration" begin
     d = Dict('a'=>1, 'b'=>1, 'c'=> 3)
     @test [d[k] for k in keys(d)] == [d[k] for k in eachindex(d)] ==
-          [v for (k, v) in d] == [d[x[1]] for (i, x) in enumerate(d)]
+          [v for (k, v) in pairs(d)] == [d[x[1]] for (i, x) in enumerate(d)] ==
+          [v for v in values(d)]
 end
 
 @testset "generators, similar" begin
@@ -496,19 +497,19 @@ import Base.ImmutableDict
     @test collect(d1) == [Pair(k1, v1)]
     @test collect(d4) == reverse([Pair(k1, v1), Pair(k2, v2), Pair(k1, v2), Pair(k2, v1)])
     @test d1 == ImmutableDict(d, k1 => v1)
-    @test !((k1 => v2) in d2)
-    @test (k1 => v2) in d3
-    @test (k1 => v1) in d4
-    @test (k1 => v2) in d4
-    @test in(k2 => "value2", d4, ===)
-    @test in(k2 => v2, d4, ===)
-    @test in(k2 => NaN, dnan, isequal)
-    @test in(k2 => NaN, dnan, ===)
-    @test !in(k2 => NaN, dnan, ==)
-    @test !in(k2 => 1, dnum, ===)
-    @test in(k2 => 1.0, dnum, ===)
-    @test !in(k2 => 1, dnum, <)
-    @test in(k2 => 0, dnum, <)
+    @test !((k1 => v2) in pairs(d2))
+    @test (k1 => v2) in pairs(d3)
+    @test (k1 => v1) in pairs(d4)
+    @test (k1 => v2) in pairs(d4)
+    @test in(k2 => "value2", pairs(d4), ===)
+    @test in(k2 => v2, pairs(d4), ===)
+    @test in(k2 => NaN, pairs(dnan), isequal)
+    @test in(k2 => NaN, pairs(dnan), ===)
+    @test !in(k2 => NaN, pairs(dnan), ==)
+    @test !in(k2 => 1, pairs(dnum), ===)
+    @test in(k2 => 1.0, pairs(dnum), ===)
+    @test !in(k2 => 1, pairs(dnum), <)
+    @test in(k2 => 0, pairs(dnum), <)
     @test get(d1, "key1", :default) === v1
     @test get(d4, "key1", :default) === v2
     @test get(d4, "foo", :default) === :default
diff --git a/test/env.jl b/test/env.jl
index ea24759aa5941..4f3d86bc186b5 100644
--- a/test/env.jl
+++ b/test/env.jl
@@ -37,7 +37,7 @@ end
     k1, k2 = "__test__", "__test1__"
     withenv(k1=>k1, k2=>k2) do
         b_k1, b_k2 = false, false
-        for (k, v) in ENV
+        for (k, v) in pairs(ENV)
             if k==k1
                 b_k1=true
             elseif k==k2
@@ -66,7 +66,7 @@ end
 @test withenv(Dict{Any,Any}()...) do; true; end
 
 # Test for #18141
-for (k, v) in ENV
+for (k, v) in pairs(ENV)
     if length(v) > 0
         @test v[end] != '\0'
     end