From 59a87ee1582094461aa473247a6d18c3e286f50a Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 5 Aug 2020 09:08:29 -0500 Subject: [PATCH] Prevent invalidations from eltype(::Type{<:NamedTuple}) (#36921) This provides a fallback `eltype` method specialized for imprecise NamedType types. Formerly we were calling the generic eltype fallback `eltype(::Type) = Any`, but relying on the generic fallback makes code vulnerable to invalidation when new `eltype` methods are added. Since this affects any poorly-inferred keyword-arg function, it's best to isolate this by defining a specialization. --- base/namedtuple.jl | 4 +++- test/worlds.jl | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/base/namedtuple.jl b/base/namedtuple.jl index f8d41d0b21825..669d3b521153f 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -161,7 +161,9 @@ function show(io::IO, t::NamedTuple) end end -eltype(::Type{NamedTuple{names,T}} where names) where {T} = eltype(T) +eltype(::Type{T}) where T<:NamedTuple = nteltype(T) +nteltype(::Type) = Any +nteltype(::Type{NamedTuple{names,T}} where names) where {T} = eltype(T) ==(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = Tuple(a) == Tuple(b) ==(a::NamedTuple, b::NamedTuple) = false diff --git a/test/worlds.jl b/test/worlds.jl index 19b8857c352e3..c87e0104b944c 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -213,7 +213,7 @@ function instance(f, types) for i = 1:length(specs) if isassigned(specs, i) mi = specs[i]::Core.MethodInstance - if mi.specTypes === tt + if mi.specTypes <: tt && tt <: mi.specTypes inst = mi break end @@ -324,6 +324,18 @@ abstract type Colorant35855 end Base.convert(::Type{C}, c) where {C<:Colorant35855} = false @test worlds(mi) == w +# NamedTuple and extensions of eltype +outer(anyc) = inner(anyc[]) +inner(s::Union{Vector,Dict}; kw=false) = inneri(s, kwi=maximum(s), kwb=kw) +inneri(s, args...; kwargs...) = inneri(IOBuffer(), s, args...; kwargs...) +inneri(io::IO, s::Union{Vector,Dict}; kwi=0, kwb=false) = (print(io, first(s), " "^kwi, kwb); String(take!(io))) +@test outer(Ref{Any}([1,2,3])) == "1 false" +mi = instance(Core.kwfunc(inneri), (NamedTuple{(:kwi,:kwb),TT} where TT<:Tuple{Any,Bool}, typeof(inneri), Vector{T} where T)) +w = worlds(mi) +abstract type Container{T} end +Base.eltype(::Type{C}) where {T,C<:Container{T}} = T +@test worlds(mi) == w + # invoke_in_world f_inworld(x) = "world one; x=$x" g_inworld(x; y) = "world one; x=$x, y=$y" @@ -336,4 +348,3 @@ wc_aiw2 = get_world_counter() @test Base.invoke_in_world(wc_aiw2, f_inworld, 2) == "world two; x=2" @test Base.invoke_in_world(wc_aiw1, g_inworld, 2, y=3) == "world one; x=2, y=3" @test Base.invoke_in_world(wc_aiw2, g_inworld, 2, y=3) == "world two; x=2, y=3" -