• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37632

26 Sep 2023 06:44AM UTC coverage: 86.999% (-0.9%) from 87.914%
#37632

push

local

web-flow
inference: make `throw` block deoptimization concrete-eval friendly (#49235)

The deoptimization can sometimes destroy the effects analysis and
disable [semi-]concrete evaluation that is otherwise possible. This is
because the deoptimization was designed with the type domain
profitability in mind (#35982), and hasn't been adequately considering
the effects domain.

This commit makes the deoptimization aware of the effects domain more
and enables the `throw` block deoptimization only when the effects
already known to be ineligible for concrete-evaluation.

In our current effect system, `ALWAYS_FALSE`/`false` means that the
effect can not be refined to `ALWAYS_TRUE`/`true` anymore (unless given
user annotation later). Therefore we can enable the `throw` block
deoptimization without hindering the chance of concrete-evaluation when
any of the following conditions are met:
- `effects.consistent === ALWAYS_FALSE`
- `effects.effect_free === ALWAYS_FALSE`
- `effects.terminates === false`
- `effects.nonoverlayed === false`

Here are some numbers:

| Metric | master | this commit | #35982 reverted (set
`unoptimize_throw_blocks=false`) |

|-------------------------|-----------|-------------|--------------------------------------------|
| Base (seconds) | 15.579300 | 15.206645 | 15.296319 |
| Stdlibs (seconds) | 17.919013 | 17.667094 | 17.738128 |
| Total (seconds) | 33.499279 | 32.874737 | 33.035448 |
| Precompilation (seconds) | 49.967516 | 49.421121 | 49.999998 |
| First time `plot(rand(10,3))` [^1] | `2.476678 seconds (11.74 M
allocations)` | `2.430355 seconds (11.77 M allocations)` | `2.514874
seconds (11.64 M allocations)` |
| First time `solve(prob, QNDF())(5.0)` [^2] | `4.469492 seconds (15.32
M allocations)` | `4.499217 seconds (15.41 M allocations)` | `4.470772
seconds (15.38 M allocations)` |

[^1]: With disabling precompilation of Plots.jl.
[^2]: With disabling precompilation of OrdinaryDiffEq.

These numbers ma... (continued)

7 of 7 new or added lines in 1 file covered. (100.0%)

73407 of 84377 relevant lines covered (87.0%)

11275130.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.88
/base/namedtuple.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
"""
4
    NamedTuple
5

6
`NamedTuple`s are, as their name suggests, named [`Tuple`](@ref)s. That is, they're a
7
tuple-like collection of values, where each entry has a unique name, represented as a
8
[`Symbol`](@ref). Like `Tuple`s, `NamedTuple`s are immutable; neither the names nor the values
9
can be modified in place after construction.
10

11
A named tuple can be created as a tuple literal with keys, e.g. `(a=1, b=2)`,
12
or as a tuple literal with semicolon after the opening parenthesis, e.g. `(;
13
a=1, b=2)` (this form also accepts programmatically generated names as
14
described below), or using a `NamedTuple` type as constructor, e.g.
15
`NamedTuple{(:a, :b)}((1,2))`.
16

17
Accessing the value associated with a name in a named tuple can be done using field
18
access syntax, e.g. `x.a`, or using [`getindex`](@ref), e.g. `x[:a]` or `x[(:a, :b)]`.
19
A tuple of the names can be obtained using [`keys`](@ref), and a tuple of the values
20
can be obtained using [`values`](@ref).
21

22
!!! note
23
    Iteration over `NamedTuple`s produces the *values* without the names. (See example
24
    below.) To iterate over the name-value pairs, use the [`pairs`](@ref) function.
25

26
The [`@NamedTuple`](@ref) macro can be used for conveniently declaring `NamedTuple` types.
27

28
# Examples
29
```jldoctest
30
julia> x = (a=1, b=2)
31
(a = 1, b = 2)
32

33
julia> x.a
34
1
35

36
julia> x[:a]
37
1
38

39
julia> x[(:a,)]
40
(a = 1,)
41

42
julia> keys(x)
43
(:a, :b)
44

45
julia> values(x)
46
(1, 2)
47

48
julia> collect(x)
49
2-element Vector{Int64}:
50
 1
51
 2
52

53
julia> collect(pairs(x))
54
2-element Vector{Pair{Symbol, Int64}}:
55
 :a => 1
56
 :b => 2
57
```
58

59
In a similar fashion as to how one can define keyword arguments programmatically,
60
a named tuple can be created by giving pairs `name::Symbol => value` after a
61
semicolon inside a tuple literal. This and the `name=value` syntax can be mixed:
62

63
```jldoctest
64
julia> (; :a => 1, :b => 2, c=3)
65
(a = 1, b = 2, c = 3)
66
```
67

68
The name-value pairs can also be provided by splatting a named tuple or any
69
iterator that yields two-value collections holding each a symbol as first
70
value:
71

72
```jldoctest
73
julia> keys = (:a, :b, :c); values = (1, 2, 3);
74

75
julia> NamedTuple{keys}(values)
76
(a = 1, b = 2, c = 3)
77

78
julia> (; (keys .=> values)...)
79
(a = 1, b = 2, c = 3)
80

81
julia> nt1 = (a=1, b=2);
82

83
julia> nt2 = (c=3, d=4);
84

85
julia> (; nt1..., nt2..., b=20) # the final b overwrites the value from nt1
86
(a = 1, b = 20, c = 3, d = 4)
87

88
julia> (; zip(keys, values)...) # zip yields tuples such as (:a, 1)
89
(a = 1, b = 2, c = 3)
90
```
91

92
As in keyword arguments, identifiers and dot expressions imply names:
93

94
```jldoctest
95
julia> x = 0
96
0
97

98
julia> t = (; x)
99
(x = 0,)
100

101
julia> (; t.x)
102
(x = 0,)
103
```
104

105
!!! compat "Julia 1.5"
106
    Implicit names from identifiers and dot expressions are available as of Julia 1.5.
107

108
!!! compat "Julia 1.7"
109
    Use of `getindex` methods with multiple `Symbol`s is available as of Julia 1.7.
110
"""
111
Core.NamedTuple
112

113
if nameof(@__MODULE__) === :Base
114

115
@eval function NamedTuple{names,T}(args::Tuple) where {names, T <: Tuple}
7✔
116
    if length(args) != length(names::Tuple)
7✔
117
        throw(ArgumentError("Wrong number of arguments to named tuple constructor."))
1✔
118
    end
119
    # Note T(args) might not return something of type T; e.g.
120
    # Tuple{Type{Float64}}((Float64,)) returns a Tuple{DataType}
121
    $(Expr(:splatnew, :(NamedTuple{names,T}), :(T(args))))
6✔
122
end
123

124
function NamedTuple{names, T}(nt::NamedTuple) where {names, T <: Tuple}
1✔
125
    if @generated
1✔
126
        Expr(:new, :(NamedTuple{names, T}),
1✔
127
             Any[ :(let Tn = fieldtype(T, $n),
128
                      ntn = getfield(nt, $(QuoteNode(names[n])))
129
                      ntn isa Tn ? ntn : convert(Tn, ntn)
3✔
130
                  end) for n in 1:length(names) ]...)
131
    else
132
        NamedTuple{names, T}(map(Fix1(getfield, nt), names))
133
    end
134
end
135

136
function NamedTuple{names}(nt::NamedTuple) where {names}
22✔
137
    if @generated
22✔
138
        idx = Int[ fieldindex(nt, names[n]) for n in 1:length(names) ]
52✔
139
        types = Tuple{(fieldtype(nt, idx[n]) for n in 1:length(idx))...}
23✔
140
        Expr(:new, :(NamedTuple{names, $types}), Any[ :(getfield(nt, $(idx[n]))) for n in 1:length(idx) ]...)
20✔
141
    else
142
        length_names = length(names::Tuple)
4✔
143
        types = Tuple{(fieldtype(typeof(nt), names[n]) for n in 1:length_names)...}
4✔
144
        _new_NamedTuple(NamedTuple{names, types}, map(Fix1(getfield, nt), names))
×
145
    end
146
end
147

148
NamedTuple{names, T}(itr) where {names, T <: Tuple} = NamedTuple{names, T}(T(itr))
2✔
149
NamedTuple{names}(itr) where {names} = NamedTuple{names}(Tuple(itr))
1✔
150

151
NamedTuple(itr) = (; itr...)
6✔
152

153
# avoids invalidating Union{}(...)
154
NamedTuple{names, Union{}}(itr::Tuple) where {names} = throw(MethodError(NamedTuple{names, Union{}}, (itr,)))
×
155

156
end # if Base
157

158
# Like NamedTuple{names, T} as a constructor, but omits the additional
159
# `convert` call, when the types are known to match the fields
160
@eval function _new_NamedTuple(T::Type{NamedTuple{NTN, NTT}} where {NTN, NTT}, args::Tuple)
154,757✔
161
    $(Expr(:splatnew, :T, :args))
737,930✔
162
end
163

164
length(t::NamedTuple) = nfields(t)
70✔
165
iterate(t::NamedTuple, iter=1) = iter > nfields(t) ? nothing : (getfield(t, iter), iter + 1)
247✔
166
rest(t::NamedTuple) = t
×
167
@inline rest(t::NamedTuple{names}, i::Int) where {names} = NamedTuple{rest(names,i)}(t)
3✔
168
firstindex(t::NamedTuple) = 1
3✔
169
lastindex(t::NamedTuple) = nfields(t)
5✔
170
getindex(t::NamedTuple, i::Int) = getfield(t, i)
1,419✔
171
getindex(t::NamedTuple, i::Symbol) = getfield(t, i)
2,254✔
172
getindex(t::NamedTuple, ::Colon) = t
1✔
173
@inline getindex(t::NamedTuple, idxs::Tuple{Vararg{Symbol}}) = NamedTuple{idxs}(t)
7✔
174
@inline getindex(t::NamedTuple, idxs::AbstractVector{Symbol}) = NamedTuple{Tuple(idxs)}(t)
4✔
175
indexed_iterate(t::NamedTuple, i::Int, state=1) = (getfield(t, i), i+1)
20,176,712✔
176
isempty(::NamedTuple{()}) = true
13✔
177
isempty(::NamedTuple) = false
19,578✔
178
empty(::NamedTuple) = NamedTuple()
2✔
179

180
prevind(@nospecialize(t::NamedTuple), i::Integer) = Int(i)-1
1✔
181
nextind(@nospecialize(t::NamedTuple), i::Integer) = Int(i)+1
1✔
182

183
convert(::Type{NT}, nt::NT) where {names, NT<:NamedTuple{names}} = nt
2✔
184
convert(::Type{NT}, nt::NT) where {names, T<:Tuple, NT<:NamedTuple{names,T}} = nt
97✔
185

186
function convert(::Type{NamedTuple{names,T}}, nt::NamedTuple{names}) where {names,T<:Tuple}
690✔
187
    NamedTuple{names,T}(T(nt))::NamedTuple{names,T}
738,128✔
188
end
189

190
function convert(::Type{NT}, nt::NamedTuple{names}) where {names, NT<:NamedTuple{names}}
2✔
191
    # converting abstract NT to an abstract Tuple type, to a concrete NT1, is not straightforward, so this could just be an error, but we define it anyways
192
    # _tuple_error(NT, nt)
193
    T1 = Tuple{ntuple(i -> fieldtype(NT, i), Val(length(names)))...}
4✔
194
    NT1 = NamedTuple{names, T1}
2✔
195
    return NT1(T1(nt))::NT1::NT
2✔
196
end
197

198
if nameof(@__MODULE__) === :Base
199
    Tuple(nt::NamedTuple) = (nt...,)
855✔
200
    (::Type{T})(nt::NamedTuple) where {T <: Tuple} = (t = Tuple(nt); t isa T ? t : convert(T, t)::T)
738,822✔
201
end
202

203
function show(io::IO, t::NamedTuple)
22✔
204
    n = nfields(t)
22✔
205
    for i = 1:n
22✔
206
        # if field types aren't concrete, show full type
207
        if typeof(getfield(t, i)) !== fieldtype(typeof(t), i)
30✔
208
            show(io, typeof(t))
1✔
209
            print(io, "(")
1✔
210
            show(io, Tuple(t))
1✔
211
            print(io, ")")
1✔
212
            return
1✔
213
        end
214
    end
38✔
215
    if n == 0
21✔
216
        print(io, "NamedTuple()")
1✔
217
    else
218
        typeinfo = get(io, :typeinfo, Any)
50✔
219
        print(io, "(")
20✔
220
        for i = 1:n
20✔
221
            show_sym(io, fieldname(typeof(t), i))
29✔
222
            print(io, " = ")
29✔
223
            show(IOContext(io, :typeinfo =>
30✔
224
                           t isa typeinfo <: NamedTuple ? fieldtype(typeinfo, i) : Any),
225
                 getfield(t, i))
226
            if n == 1
29✔
227
                print(io, ",")
11✔
228
            elseif i < n
18✔
229
                print(io, ", ")
9✔
230
            end
231
        end
38✔
232
        print(io, ")")
20✔
233
    end
234
end
235

236
eltype(::Type{T}) where T<:NamedTuple = nteltype(T)
36,068✔
237
nteltype(::Type) = Any
×
238
nteltype(::Type{NamedTuple{names,T}} where names) where {T} = eltype(T)
36,068✔
239

240
keytype(@nospecialize nt::NamedTuple) = keytype(typeof(nt))
1✔
241
keytype(@nospecialize T::Type{<:NamedTuple}) = Symbol
4✔
242

243
valtype(@nospecialize nt::NamedTuple) = valtype(typeof(nt))
1✔
244
valtype(@nospecialize T::Type{<:NamedTuple}) = eltype(T)
1✔
245

246
==(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = Tuple(a) == Tuple(b)
59✔
247
==(a::NamedTuple, b::NamedTuple) = false
2✔
248

249
isequal(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = isequal(Tuple(a), Tuple(b))
6✔
250
isequal(a::NamedTuple, b::NamedTuple) = false
1✔
251

252
_nt_names(::NamedTuple{names}) where {names} = names
6✔
253
_nt_names(::Type{T}) where {names,T<:NamedTuple{names}} = names
520✔
254

255
hash(x::NamedTuple, h::UInt) = xor(objectid(_nt_names(x)), hash(Tuple(x), h))
6✔
256

257
(<)(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = Tuple(a) < Tuple(b)
6✔
258
isless(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = isless(Tuple(a), Tuple(b))
3✔
259

260
same_names(::NamedTuple{names}...) where {names} = true
4✔
261
same_names(::NamedTuple...) = false
1✔
262

263
# NOTE: this method signature makes sure we don't define map(f)
264
function map(f, nt::NamedTuple{names}, nts::NamedTuple...) where names
5✔
265
    if !same_names(nt, nts...)
5✔
266
        throw(ArgumentError("Named tuple names do not match."))
1✔
267
    end
268
    NamedTuple{names}(map(f, map(Tuple, (nt, nts...))...))
4✔
269
end
270

271
@assume_effects :total function merge_names(an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}})
×
272
    @nospecialize an bn
×
273
    names = Symbol[an...]
749✔
274
    for n in bn
749✔
275
        if !sym_in(n, an)
1,093✔
276
            push!(names, n)
586✔
277
        end
278
    end
1,842✔
279
    (names...,)
749✔
280
end
281

282
@assume_effects :total function merge_types(names::Tuple{Vararg{Symbol}}, a::Type{<:NamedTuple}, b::Type{<:NamedTuple})
749✔
283
    @nospecialize names a b
×
284
    bn = _nt_names(b)
749✔
285
    return Tuple{Any[ fieldtype(sym_in(names[n], bn) ? b : a, names[n]) for n in 1:length(names) ]...}
749✔
286
end
287

288
@assume_effects :foldable function merge_fallback(a::NamedTuple, b::NamedTuple,
4✔
289
                                                  an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}})
290
    @nospecialize
×
291
    names = merge_names(an, bn)
8✔
292
    types = merge_types(names, typeof(a), typeof(b))
4✔
293
    n = length(names)
4✔
294
    A = Vector{Any}(undef, n)
4✔
295
    for i=1:n
8✔
296
        n = names[i]
11✔
297
        A[i] = getfield(sym_in(n, bn) ? b : a, n)
11✔
298
    end
18✔
299
    _new_NamedTuple(NamedTuple{names, types}, (A...,))
4✔
300
end
301

302
# This is `Experimental.@max_methods 4 function merge end`, which is not
303
# defined at this point in bootstrap.
304
typeof(function merge end).name.max_methods = UInt8(4)
305

306
"""
307
    merge(a::NamedTuple, bs::NamedTuple...)
308

309
Construct a new named tuple by merging two or more existing ones, in a left-associative
310
manner. Merging proceeds left-to-right, between pairs of named tuples, and so the order of fields
311
present in both the leftmost and rightmost named tuples take the same position as they are found in the
312
leftmost named tuple. However, values are taken from matching fields in the rightmost named tuple that
313
contains that field. Fields present in only the rightmost named tuple of a pair are appended at the end.
314
A fallback is implemented for when only a single named tuple is supplied,
315
with signature `merge(a::NamedTuple)`.
316

317
!!! compat "Julia 1.1"
318
    Merging 3 or more `NamedTuple` requires at least Julia 1.1.
319

320
# Examples
321
```jldoctest
322
julia> merge((a=1, b=2, c=3), (b=4, d=5))
323
(a = 1, b = 4, c = 3, d = 5)
324
```
325

326
```jldoctest
327
julia> merge((a=1, b=2), (b=3, c=(d=1,)), (c=(d=2,),))
328
(a = 1, b = 3, c = (d = 2,))
329
```
330
"""
331
function merge(a::NamedTuple{an}, b::NamedTuple{bn}) where {an, bn}
806,516✔
332
    if @generated
806,384✔
333
        names = merge_names(an, bn)
1,490✔
334
        types = merge_types(names, a, b)
745✔
335
        vals = Any[ :(getfield($(sym_in(names[n], bn) ? :b : :a), $(QuoteNode(names[n])))) for n in 1:length(names) ]
2,340✔
336
        :( _new_NamedTuple(NamedTuple{$names,$types}, ($(vals...),)) )
745✔
337
    else
338
        merge_fallback(a, b, an, bn)
4✔
339
    end
340
end
341

342
merge(a::NamedTuple,     b::NamedTuple{()}) = a
2,427✔
343
merge(a::NamedTuple{()}, b::NamedTuple{()}) = a
13✔
344
merge(a::NamedTuple{()}, b::NamedTuple)     = b
156,467✔
345

346
merge(a::NamedTuple, b::Iterators.Pairs{<:Any,<:Any,<:Any,<:NamedTuple}) = merge(a, getfield(b, :data))
30,081,467✔
347

348
merge(a::NamedTuple, b::Iterators.Zip{<:Tuple{Any,Any}}) = merge(a, NamedTuple{Tuple(b.is[1])}(b.is[2]))
×
349

350
merge(a::NamedTuple, b::NamedTuple, cs::NamedTuple...) = merge(merge(a, b), cs...)
2✔
351

352
merge(a::NamedTuple) = a
1✔
353

354
"""
355
    merge(a::NamedTuple, iterable)
356

357
Interpret an iterable of key-value pairs as a named tuple, and perform a merge.
358

359
```jldoctest
360
julia> merge((a=1, b=2, c=3), [:b=>4, :d=>5])
361
(a = 1, b = 4, c = 3, d = 5)
362
```
363
"""
364
function merge(a::NamedTuple, itr)
1,420,921✔
365
    names = Symbol[]
1,420,921✔
366
    vals = Any[]
1,420,921✔
367
    inds = IdDict{Symbol,Int}()
1,420,921✔
368
    for (k, v) in itr
2,486,846✔
369
        k = k::Symbol
345,102✔
370
        oldind = get(inds, k, 0)
345,128✔
371
        if oldind > 0
345,118✔
372
            vals[oldind] = v
10✔
373
        else
374
            push!(names, k)
345,108✔
375
            push!(vals, v)
345,120✔
376
            inds[k] = length(names)
345,108✔
377
        end
378
    end
690,081✔
379
    merge(a, NamedTuple{(names...,)}((vals...,)))
1,420,920✔
380
end
381

382
keys(nt::NamedTuple{names}) where {names} = names::Tuple{Vararg{Symbol}}
35,976✔
383
values(nt::NamedTuple) = Tuple(nt)
6✔
384
haskey(nt::NamedTuple, key::Union{Integer, Symbol}) = isdefined(nt, key)
3✔
385
get(nt::NamedTuple, key::Union{Integer, Symbol}, default) = isdefined(nt, key) ? getfield(nt, key) : default
2,324,177✔
386
get(f::Callable, nt::NamedTuple, key::Union{Integer, Symbol}) = isdefined(nt, key) ? getfield(nt, key) : f()
6✔
387
tail(t::NamedTuple{names}) where names = NamedTuple{tail(names::Tuple)}(t)
3✔
388
front(t::NamedTuple{names}) where names = NamedTuple{front(names::Tuple)}(t)
3✔
389
reverse(nt::NamedTuple) = NamedTuple{reverse(keys(nt))}(reverse(values(nt)))
3✔
390

391
@assume_effects :total function diff_names(an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}})
×
392
    @nospecialize an bn
×
393
    names = Symbol[]
2,918✔
394
    for n in an
2,918✔
395
        if !sym_in(n, bn)
5,459✔
396
            push!(names, n)
1,454✔
397
        end
398
    end
8,377✔
399
    (names...,)
2,918✔
400
end
401

402
@assume_effects :foldable function diff_types(@nospecialize(a::NamedTuple), @nospecialize(names::Tuple{Vararg{Symbol}}))
×
403
    return Tuple{Any[ fieldtype(typeof(a), names[n]) for n in 1:length(names) ]...}
×
404
end
405

406
@assume_effects :foldable function diff_fallback(@nospecialize(a::NamedTuple), @nospecialize(an::Tuple{Vararg{Symbol}}), @nospecialize(bn::Tuple{Vararg{Symbol}}))
×
407
    names = diff_names(an, bn)
×
408
    isempty(names) && return (;)
×
409
    types = diff_types(a, names)
×
410
    n = length(names)
×
411
    A = Vector{Any}(undef, n)
×
412
    for i=1:n
×
413
        n = names[i]
×
414
        A[i] = getfield(a, n)
×
415
    end
×
416
    _new_NamedTuple(NamedTuple{names, types}, (A...,))
×
417
end
418

419
"""
420
    structdiff(a::NamedTuple, b::Union{NamedTuple,Type{NamedTuple}})
421

422
Construct a copy of named tuple `a`, except with fields that exist in `b` removed.
423
`b` can be a named tuple, or a type of the form `NamedTuple{field_names}`.
424
"""
425
function structdiff(a::NamedTuple{an}, b::Union{NamedTuple{bn}, Type{NamedTuple{bn}}}) where {an, bn}
2,191,602✔
426
    if @generated
427
        names = diff_names(an, bn)
5,836✔
428
        isempty(names) && return (;) # just a fast pass
3,842✔
429
        idx = Int[ fieldindex(a, names[n]) for n in 1:length(names) ]
1,454✔
430
        types = Tuple{Any[ fieldtype(a, idx[n]) for n in 1:length(idx) ]...}
1,454✔
431
        vals = Any[ :(getfield(a, $(idx[n]))) for n in 1:length(idx) ]
1,454✔
432
        return :( _new_NamedTuple(NamedTuple{$names,$types}, ($(vals...),)) )
924✔
433
    else
434
        return diff_fallback(a, an, bn)
435
    end
436
end
437

438
structdiff(a::NamedTuple{an}, b::Union{NamedTuple{an}, Type{NamedTuple{an}}}) where {an} = (;)
1,418,694✔
439

440
"""
441
    setindex(nt::NamedTuple, val, key::Symbol)
442

443
Constructs a new `NamedTuple` with the key `key` set to `val`.
444
If `key` is already in the keys of `nt`, `val` replaces the old value.
445

446
```jldoctest
447
julia> nt = (a = 3,)
448
(a = 3,)
449

450
julia> Base.setindex(nt, 33, :b)
451
(a = 3, b = 33)
452

453
julia> Base.setindex(nt, 4, :a)
454
(a = 4,)
455

456
julia> Base.setindex(nt, "a", :a)
457
(a = "a",)
458
```
459
"""
460
function setindex(nt::NamedTuple, v, idx::Symbol)
5✔
461
    merge(nt, (; idx => v))
5✔
462
end
463

464
"""
465
    @NamedTuple{key1::Type1, key2::Type2, ...}
466
    @NamedTuple begin key1::Type1; key2::Type2; ...; end
467

468
This macro gives a more convenient syntax for declaring `NamedTuple` types. It returns a `NamedTuple`
469
type with the given keys and types, equivalent to `NamedTuple{(:key1, :key2, ...), Tuple{Type1,Type2,...}}`.
470
If the `::Type` declaration is omitted, it is taken to be `Any`.   The `begin ... end` form allows the
471
declarations to be split across multiple lines (similar to a `struct` declaration), but is otherwise
472
equivalent. The `NamedTuple` macro is used when printing `NamedTuple` types to e.g. the REPL.
473

474
For example, the tuple `(a=3.1, b="hello")` has a type `NamedTuple{(:a, :b), Tuple{Float64, String}}`, which
475
can also be declared via `@NamedTuple` as:
476

477
```jldoctest
478
julia> @NamedTuple{a::Float64, b::String}
479
@NamedTuple{a::Float64, b::String}
480

481
julia> @NamedTuple begin
482
           a::Float64
483
           b::String
484
       end
485
@NamedTuple{a::Float64, b::String}
486
```
487

488
!!! compat "Julia 1.5"
489
    This macro is available as of Julia 1.5.
490
"""
491
macro NamedTuple(ex)
14✔
492
    Meta.isexpr(ex, :braces) || Meta.isexpr(ex, :block) ||
15✔
493
        throw(ArgumentError("@NamedTuple expects {...} or begin...end"))
494
    decls = filter(e -> !(e isa LineNumberNode), ex.args)
38✔
495
    all(e -> e isa Symbol || Meta.isexpr(e, :(::)), decls) ||
56✔
496
        throw(ArgumentError("@NamedTuple must contain a sequence of name or name::type expressions"))
497
    vars = [QuoteNode(e isa Symbol ? e : e.args[1]) for e in decls]
13✔
498
    types = [esc(e isa Symbol ? :Any : e.args[2]) for e in decls]
13✔
499
    return :(NamedTuple{($(vars...),), Tuple{$(types...)}})
13✔
500
end
501

502
"""
503
    @Kwargs{key1::Type1, key2::Type2, ...}
504

505
This macro gives a convenient way to construct the type representation of keyword arguments
506
from the same syntax as [`@NamedTuple`](@ref).
507
For example, when we have a function call like `func([positional arguments]; kw1=1.0, kw2="2")`,
508
we can use this macro to construct the internal type representation of the keyword arguments
509
as `@Kwargs{kw1::Float64, kw2::String}`.
510
The macro syntax is specifically designed to simplify the signature type of a keyword method
511
when it is printed in the stack trace view.
512

513
```julia
514
julia> @Kwargs{init::Int} # the internal representation of keyword arguments
515
Base.Pairs{Symbol, Int64, Tuple{Symbol}, @NamedTuple{init::Int64}}
516

517
julia> sum("julia"; init=1)
518
ERROR: MethodError: no method matching +(::Char, ::Char)
519

520
Closest candidates are:
521
  +(::Any, ::Any, ::Any, ::Any...)
522
   @ Base operators.jl:585
523
  +(::Integer, ::AbstractChar)
524
   @ Base char.jl:247
525
  +(::T, ::Integer) where T<:AbstractChar
526
   @ Base char.jl:237
527

528
Stacktrace:
529
  [1] add_sum(x::Char, y::Char)
530
    @ Base ./reduce.jl:24
531
  [2] BottomRF
532
    @ Base ./reduce.jl:86 [inlined]
533
  [3] _foldl_impl(op::Base.BottomRF{typeof(Base.add_sum)}, init::Int64, itr::String)
534
    @ Base ./reduce.jl:62
535
  [4] foldl_impl(op::Base.BottomRF{typeof(Base.add_sum)}, nt::Int64, itr::String)
536
    @ Base ./reduce.jl:48 [inlined]
537
  [5] mapfoldl_impl(f::typeof(identity), op::typeof(Base.add_sum), nt::Int64, itr::String)
538
    @ Base ./reduce.jl:44 [inlined]
539
  [6] mapfoldl(f::typeof(identity), op::typeof(Base.add_sum), itr::String; init::Int64)
540
    @ Base ./reduce.jl:175 [inlined]
541
  [7] mapreduce(f::typeof(identity), op::typeof(Base.add_sum), itr::String; kw::@Kwargs{init::Int64})
542
    @ Base ./reduce.jl:307 [inlined]
543
  [8] sum(f::typeof(identity), a::String; kw::@Kwargs{init::Int64})
544
    @ Base ./reduce.jl:535 [inlined]
545
  [9] sum(a::String; kw::@Kwargs{init::Int64})
546
    @ Base ./reduce.jl:564 [inlined]
547
 [10] top-level scope
548
    @ REPL[12]:1
549
```
550

551
!!! compat "Julia 1.10"
552
    This macro is available as of Julia 1.10.
553
"""
554
macro Kwargs(ex)
3✔
555
    return :(let
3✔
556
        NT = @NamedTuple $ex
557
        Base.Pairs{keytype(NT),eltype(NT),typeof(NT.parameters[1]),NT}
558
    end)
559
end
560

561
@constprop :aggressive function split_rest(t::NamedTuple{names}, n::Int, st...) where {names}
1✔
562
    _check_length_split_rest(length(t), n)
1✔
563
    names_front, names_last_n = split_rest(names, n, st...)
1✔
564
    return NamedTuple{names_front}(t), NamedTuple{names_last_n}(t)
1✔
565
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc