Skip to content

Commit 59a87ee

Browse files
authored
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.
1 parent 371bfa8 commit 59a87ee

File tree

2 files changed

+16
-3
lines changed

2 files changed

+16
-3
lines changed

base/namedtuple.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,9 @@ function show(io::IO, t::NamedTuple)
161161
end
162162
end
163163

164-
eltype(::Type{NamedTuple{names,T}} where names) where {T} = eltype(T)
164+
eltype(::Type{T}) where T<:NamedTuple = nteltype(T)
165+
nteltype(::Type) = Any
166+
nteltype(::Type{NamedTuple{names,T}} where names) where {T} = eltype(T)
165167

166168
==(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = Tuple(a) == Tuple(b)
167169
==(a::NamedTuple, b::NamedTuple) = false

test/worlds.jl

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ function instance(f, types)
213213
for i = 1:length(specs)
214214
if isassigned(specs, i)
215215
mi = specs[i]::Core.MethodInstance
216-
if mi.specTypes === tt
216+
if mi.specTypes <: tt && tt <: mi.specTypes
217217
inst = mi
218218
break
219219
end
@@ -324,6 +324,18 @@ abstract type Colorant35855 end
324324
Base.convert(::Type{C}, c) where {C<:Colorant35855} = false
325325
@test worlds(mi) == w
326326

327+
# NamedTuple and extensions of eltype
328+
outer(anyc) = inner(anyc[])
329+
inner(s::Union{Vector,Dict}; kw=false) = inneri(s, kwi=maximum(s), kwb=kw)
330+
inneri(s, args...; kwargs...) = inneri(IOBuffer(), s, args...; kwargs...)
331+
inneri(io::IO, s::Union{Vector,Dict}; kwi=0, kwb=false) = (print(io, first(s), " "^kwi, kwb); String(take!(io)))
332+
@test outer(Ref{Any}([1,2,3])) == "1 false"
333+
mi = instance(Core.kwfunc(inneri), (NamedTuple{(:kwi,:kwb),TT} where TT<:Tuple{Any,Bool}, typeof(inneri), Vector{T} where T))
334+
w = worlds(mi)
335+
abstract type Container{T} end
336+
Base.eltype(::Type{C}) where {T,C<:Container{T}} = T
337+
@test worlds(mi) == w
338+
327339
# invoke_in_world
328340
f_inworld(x) = "world one; x=$x"
329341
g_inworld(x; y) = "world one; x=$x, y=$y"
@@ -336,4 +348,3 @@ wc_aiw2 = get_world_counter()
336348
@test Base.invoke_in_world(wc_aiw2, f_inworld, 2) == "world two; x=2"
337349
@test Base.invoke_in_world(wc_aiw1, g_inworld, 2, y=3) == "world one; x=2, y=3"
338350
@test Base.invoke_in_world(wc_aiw2, g_inworld, 2, y=3) == "world two; x=2, y=3"
339-

0 commit comments

Comments
 (0)