Skip to content

Commit e9c84c8

Browse files
authored
permit NamedTuple{<:Any, Union{}} to be created (#53516)
1 parent fc6c618 commit e9c84c8

File tree

6 files changed

+39
-20
lines changed

6 files changed

+39
-20
lines changed

base/boot.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -736,8 +736,8 @@ eval(Core, :(NamedTuple{names}(args::Tuple) where {names} =
736736

737737
using .Intrinsics: sle_int, add_int
738738

739-
eval(Core, :(NamedTuple{names,T}(args::T) where {names, T <: Tuple} =
740-
$(Expr(:splatnew, :(NamedTuple{names,T}), :args))))
739+
eval(Core, :((NT::Type{NamedTuple{names,T}})(args::T) where {names, T <: Tuple} =
740+
$(Expr(:splatnew, :NT, :args))))
741741

742742
# constructors for built-in types
743743

base/compiler/abstractinterpretation.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -2695,7 +2695,7 @@ function refine_partial_type(@nospecialize t)
26952695
# if the first/second parameter of `NamedTuple` is known to be empty,
26962696
# the second/first argument should also be empty tuple type,
26972697
# so refine it here
2698-
return Const(NamedTuple())
2698+
return Const((;))
26992699
end
27002700
return t
27012701
end

base/namedtuple.jl

+8-11
Original file line numberDiff line numberDiff line change
@@ -112,24 +112,24 @@ Core.NamedTuple
112112

113113
if nameof(@__MODULE__) === :Base
114114

115-
@eval function NamedTuple{names,T}(args::Tuple) where {names, T <: Tuple}
115+
@eval function (NT::Type{NamedTuple{names,T}})(args::Tuple) where {names, T <: Tuple}
116116
if length(args) != length(names::Tuple)
117117
throw(ArgumentError("Wrong number of arguments to named tuple constructor."))
118118
end
119119
# Note T(args) might not return something of type T; e.g.
120120
# Tuple{Type{Float64}}((Float64,)) returns a Tuple{DataType}
121-
$(Expr(:splatnew, :(NamedTuple{names,T}), :(T(args))))
121+
$(Expr(:splatnew, :NT, :(T(args))))
122122
end
123123

124-
function NamedTuple{names, T}(nt::NamedTuple) where {names, T <: Tuple}
124+
function (NT::Type{NamedTuple{names, T}})(nt::NamedTuple) where {names, T <: Tuple}
125125
if @generated
126-
Expr(:new, :(NamedTuple{names, T}),
127-
Any[ :(let Tn = fieldtype(T, $n),
126+
Expr(:new, :NT,
127+
Any[ :(let Tn = fieldtype(NT, $n),
128128
ntn = getfield(nt, $(QuoteNode(names[n])))
129129
ntn isa Tn ? ntn : convert(Tn, ntn)
130130
end) for n in 1:length(names) ]...)
131131
else
132-
NamedTuple{names, T}(map(Fix1(getfield, nt), names))
132+
NT(map(Fix1(getfield, nt), names))
133133
end
134134
end
135135

@@ -145,14 +145,11 @@ function NamedTuple{names}(nt::NamedTuple) where {names}
145145
end
146146
end
147147

148-
NamedTuple{names, T}(itr) where {names, T <: Tuple} = NamedTuple{names, T}(T(itr))
149-
NamedTuple{names}(itr) where {names} = NamedTuple{names}(Tuple(itr))
148+
(NT::Type{NamedTuple{names, T}})(itr) where {names, T <: Tuple} = NT(T(itr))
149+
(NT::Type{NamedTuple{names}})(itr) where {names} = NT(Tuple(itr))
150150

151151
NamedTuple(itr) = (; itr...)
152152

153-
# avoids invalidating Union{}(...)
154-
NamedTuple{names, Union{}}(itr::Tuple) where {names} = throw(MethodError(NamedTuple{names, Union{}}, (itr,)))
155-
156153
end # if Base
157154

158155
# Like NamedTuple{names, T} as a constructor, but omits the additional

src/builtins.c

+2
Original file line numberDiff line numberDiff line change
@@ -1219,6 +1219,8 @@ static jl_value_t *get_fieldtype(jl_value_t *t, jl_value_t *f, int dothrow)
12191219
tt = ((jl_tvar_t*)tt)->ub;
12201220
if (tt == (jl_value_t*)jl_any_type)
12211221
return (jl_value_t*)jl_any_type;
1222+
if (tt == (jl_value_t*)jl_bottom_type)
1223+
return (jl_value_t*)jl_bottom_type;
12221224
JL_GC_PUSH1(&f);
12231225
if (jl_is_symbol(f))
12241226
f = jl_box_long(field_index+1);

src/jltypes.c

+11-6
Original file line numberDiff line numberDiff line change
@@ -2141,15 +2141,20 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value
21412141
jl_errorf("duplicate field name in NamedTuple: \"%s\" is not unique", jl_symbol_name((jl_sym_t*)ni));
21422142
}
21432143
}
2144-
if (!jl_is_datatype(values_tt))
2145-
jl_error("NamedTuple field type must be a tuple type");
2146-
if (jl_is_va_tuple((jl_datatype_t*)values_tt) || jl_nparams(values_tt) != nf)
2147-
jl_error("NamedTuple names and field types must have matching lengths");
2148-
ndt->types = ((jl_datatype_t*)values_tt)->parameters;
2144+
if (values_tt == jl_bottom_type && nf > 0) {
2145+
ndt->types = jl_svec_fill(nf, jl_bottom_type);
2146+
}
2147+
else {
2148+
if (!jl_is_datatype(values_tt))
2149+
jl_error("NamedTuple field type must be a tuple datatype");
2150+
if (jl_is_va_tuple((jl_datatype_t*)values_tt) || jl_nparams(values_tt) != nf)
2151+
jl_error("NamedTuple names and field types must have matching lengths");
2152+
ndt->types = ((jl_datatype_t*)values_tt)->parameters;
2153+
}
21492154
jl_gc_wb(ndt, ndt->types);
21502155
}
21512156
else {
2152-
ndt->types = jl_emptysvec; // XXX: this is essentially always false
2157+
ndt->types = jl_emptysvec; // XXX: this is essentially always incorrect
21532158
}
21542159
}
21552160
else if (tn == jl_genericmemoryref_typename || tn == jl_genericmemory_typename) {

test/namedtuple.jl

+15
Original file line numberDiff line numberDiff line change
@@ -437,3 +437,18 @@ let c = (a=1, b=2),
437437
d = (b=3, c=(d=1,))
438438
@test @inferred(mergewith51009((x,y)->y, c, d)) === (a = 1, b = 3, c = (d = 1,))
439439
end
440+
441+
@test_throws ErrorException NamedTuple{(), Union{}}
442+
for NT in (NamedTuple{(:a, :b), Union{}}, NamedTuple{(:a, :b), T} where T<:Union{})
443+
@test fieldtype(NT, 1) == Union{}
444+
@test fieldtype(NT, :b) == Union{}
445+
@test_throws ErrorException fieldtype(NT, :c)
446+
@test_throws BoundsError fieldtype(NT, 0)
447+
@test_throws BoundsError fieldtype(NT, 3)
448+
@test Base.return_types((Type{NT},)) do NT; fieldtype(NT, :a); end == Any[Type{Union{}}]
449+
@test fieldtype(NamedTuple{<:Any, Union{}}, 1) == Union{}
450+
end
451+
let NT = NamedTuple{<:Any, Union{}}
452+
@test fieldtype(NT, 100) == Union{}
453+
@test only(Base.return_types((Type{NT},)) do NT; fieldtype(NT, 100); end) >: Type{Union{}}
454+
end

0 commit comments

Comments
 (0)