Skip to content

Commit 40ce72d

Browse files
vtjnashDrvi
authored andcommitted
permit NamedTuple{<:Any, Union{}} to be created (JuliaLang#53516)
(cherry picked from commit e9c84c8)
1 parent 4c68837 commit 40ce72d

File tree

6 files changed

+39
-20
lines changed

6 files changed

+39
-20
lines changed

base/boot.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -624,8 +624,8 @@ eval(Core, :(NamedTuple{names}(args::Tuple) where {names} =
624624

625625
using .Intrinsics: sle_int, add_int
626626

627-
eval(Core, :(NamedTuple{names,T}(args::T) where {names, T <: Tuple} =
628-
$(Expr(:splatnew, :(NamedTuple{names,T}), :args))))
627+
eval(Core, :((NT::Type{NamedTuple{names,T}})(args::T) where {names, T <: Tuple} =
628+
$(Expr(:splatnew, :NT, :args))))
629629

630630
# constructors for built-in types
631631

base/compiler/abstractinterpretation.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2565,7 +2565,7 @@ function refine_partial_type(@nospecialize t)
25652565
# if the first/second parameter of `NamedTuple` is known to be empty,
25662566
# the second/first argument should also be empty tuple type,
25672567
# so refine it here
2568-
return Const(NamedTuple())
2568+
return Const((;))
25692569
end
25702570
return t
25712571
end

base/namedtuple.jl

Lines changed: 8 additions & 11 deletions
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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,6 +1125,8 @@ static jl_value_t *get_fieldtype(jl_value_t *t, jl_value_t *f, int dothrow)
11251125
tt = ((jl_tvar_t*)tt)->ub;
11261126
if (tt == (jl_value_t*)jl_any_type)
11271127
return (jl_value_t*)jl_any_type;
1128+
if (tt == (jl_value_t*)jl_bottom_type)
1129+
return (jl_value_t*)jl_bottom_type;
11281130
JL_GC_PUSH1(&f);
11291131
if (jl_is_symbol(f))
11301132
f = jl_box_long(field_index+1);

src/jltypes.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2065,15 +2065,20 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value
20652065
jl_errorf("duplicate field name in NamedTuple: \"%s\" is not unique", jl_symbol_name((jl_sym_t*)ni));
20662066
}
20672067
}
2068-
if (!jl_is_datatype(values_tt))
2069-
jl_error("NamedTuple field type must be a tuple type");
2070-
if (jl_is_va_tuple((jl_datatype_t*)values_tt) || jl_nparams(values_tt) != nf)
2071-
jl_error("NamedTuple names and field types must have matching lengths");
2072-
ndt->types = ((jl_datatype_t*)values_tt)->parameters;
2068+
if (values_tt == jl_bottom_type && nf > 0) {
2069+
ndt->types = jl_svec_fill(nf, jl_bottom_type);
2070+
}
2071+
else {
2072+
if (!jl_is_datatype(values_tt))
2073+
jl_error("NamedTuple field type must be a tuple datatype");
2074+
if (jl_is_va_tuple((jl_datatype_t*)values_tt) || jl_nparams(values_tt) != nf)
2075+
jl_error("NamedTuple names and field types must have matching lengths");
2076+
ndt->types = ((jl_datatype_t*)values_tt)->parameters;
2077+
}
20732078
jl_gc_wb(ndt, ndt->types);
20742079
}
20752080
else {
2076-
ndt->types = jl_emptysvec; // XXX: this is essentially always false
2081+
ndt->types = jl_emptysvec; // XXX: this is essentially always incorrect
20772082
}
20782083
}
20792084

test/namedtuple.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,3 +414,18 @@ let c = (a=1, b=2),
414414
d = (b=3, c=(d=1,))
415415
@test @inferred(mergewith51009((x,y)->y, c, d)) === (a = 1, b = 3, c = (d = 1,))
416416
end
417+
418+
@test_throws ErrorException NamedTuple{(), Union{}}
419+
for NT in (NamedTuple{(:a, :b), Union{}}, NamedTuple{(:a, :b), T} where T<:Union{})
420+
@test fieldtype(NT, 1) == Union{}
421+
@test fieldtype(NT, :b) == Union{}
422+
@test_throws ErrorException fieldtype(NT, :c)
423+
@test_throws BoundsError fieldtype(NT, 0)
424+
@test_throws BoundsError fieldtype(NT, 3)
425+
@test Base.return_types((Type{NT},)) do NT; fieldtype(NT, :a); end == Any[Type{Union{}}]
426+
@test fieldtype(NamedTuple{<:Any, Union{}}, 1) == Union{}
427+
end
428+
let NT = NamedTuple{<:Any, Union{}}
429+
@test fieldtype(NT, 100) == Union{}
430+
@test only(Base.return_types((Type{NT},)) do NT; fieldtype(NT, 100); end) >: Type{Union{}}
431+
end

0 commit comments

Comments
 (0)