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

JuliaLang / julia / #38162

06 Aug 2025 08:25PM UTC coverage: 25.688% (-43.6%) from 69.336%
#38162

push

local

web-flow
fix runtime cglobal builtin function implementation (#59210)

This had failed to be updated for the LazyLibrary changes to codegen.

12976 of 50513 relevant lines covered (25.69%)

676965.51 hits per line

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

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

3
isexpr(@nospecialize(ex), heads) = isa(ex, Expr) && in(ex.head, heads)
10✔
4
isexpr(@nospecialize(ex), heads, n::Int) = isa(ex, Expr) && in(ex.head, heads) && length(ex.args) == n
×
5
const is_expr = isexpr
6

7
## symbols ##
8

9
"""
10
    gensym([tag])
11

12
Generates a symbol which will not conflict with other variable names (in the same module).
13
"""
14
gensym() = ccall(:jl_gensym, Ref{Symbol}, ())
×
15

16
gensym(s::String) = ccall(:jl_tagged_gensym, Ref{Symbol}, (Ptr{UInt8}, Csize_t), s, sizeof(s))
×
17

18
gensym(ss::String...) = map(gensym, ss)
×
19
gensym(s::Symbol) = ccall(:jl_tagged_gensym, Ref{Symbol}, (Ptr{UInt8}, Csize_t), s, -1 % Csize_t)
×
20

21
"""
22
    @gensym
23

24
Generates a gensym symbol for a variable. For example, `@gensym x y` is transformed into
25
`x = gensym("x"); y = gensym("y")`.
26
"""
27
macro gensym(names...)
28
    blk = Expr(:block)
29
    for name in names
30
        push!(blk.args, :($(esc(name)) = gensym($(string(name)))))
31
    end
32
    push!(blk.args, :nothing)
33
    return blk
34
end
35

36
## expressions ##
37

38
isexpr(@nospecialize(ex), head::Symbol) = isa(ex, Expr) && ex.head === head
100,069,046✔
39
isexpr(@nospecialize(ex), head::Symbol, n::Int) = isa(ex, Expr) && ex.head === head && length(ex.args) == n
47✔
40

41
copy(e::Expr) = exprarray(e.head, copy_exprargs(e.args))
2,452,383✔
42
function copy(x::PhiNode)
48,987✔
43
    values = x.values
48,987✔
44
    nvalues = length(values)
48,987✔
45
    new_values = Vector{Any}(undef, nvalues)
48,987✔
46
    @inbounds for i = 1:nvalues
48,987✔
47
        isassigned(values, i) || continue
103,017✔
48
        new_values[i] = copy_exprs(values[i])
103,017✔
49
    end
156,835✔
50
    return PhiNode(copy(x.edges), new_values)
48,987✔
51
end
52
function copy(x::PhiCNode)
×
53
    values = x.values
×
54
    nvalues = length(values)
×
55
    new_values = Vector{Any}(undef, nvalues)
×
56
    @inbounds for i = 1:nvalues
×
57
        isassigned(values, i) || continue
×
58
        new_values[i] = copy_exprs(values[i])
×
59
    end
×
60
    return PhiCNode(new_values)
×
61
end
62

63
# copy parts of an IR that the compiler mutates
64
# (this is not a general-purpose copy for an Expr AST)
65
function copy_exprs(@nospecialize(x))
8,109,804✔
66
    if isa(x, Expr)
8,212,341✔
67
        return copy(x)
2,452,359✔
68
    elseif isa(x, PhiNode)
5,759,982✔
69
        return copy(x)
48,987✔
70
    elseif isa(x, PhiCNode)
5,710,995✔
71
        return copy(x)
×
72
    end
73
    return x
5,710,995✔
74
end
75
copy_exprargs(x::Array{Any,1}) = Any[copy_exprs(@inbounds x[i]) for i in eachindex(x)]
5,844,408✔
76

77
@eval exprarray(head::Symbol, arg::Array{Any,1}) = $(Expr(:new, :Expr, :head, :arg))
2,452,383✔
78

79
# create copies of the CodeInfo definition, and any mutable fields
80
function copy(c::CodeInfo)
41,385✔
81
    cnew = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), c)
41,385✔
82
    cnew.code = copy_exprargs(cnew.code)
310,233✔
83
    cnew.slotnames = copy(cnew.slotnames)
82,254✔
84
    cnew.slotflags = copy(cnew.slotflags)
82,254✔
85
    if cnew.slottypes !== nothing
41,385✔
86
        cnew.slottypes = copy(cnew.slottypes::Vector{Any})
4,602✔
87
    end
88
    cnew.ssaflags  = copy(cnew.ssaflags)
82,254✔
89
    cnew.edges     = cnew.edges === nothing || cnew.edges isa Core.SimpleVector ? cnew.edges : copy(cnew.edges::Vector)
82,770✔
90
    ssavaluetypes  = cnew.ssavaluetypes
41,385✔
91
    ssavaluetypes isa Vector{Any} && (cnew.ssavaluetypes = copy(ssavaluetypes))
82,254✔
92
    return cnew
41,385✔
93
end
94

95
function isequal_exprarg(@nospecialize(x), @nospecialize(y))
×
96
    x isa typeof(y) || return false
×
97
    x === y && return true
×
98
    # c.f. list of types in copy_expr also
99
    if x isa Expr
×
100
        x == (y::Expr) && return true
×
101
    elseif x isa QuoteNode
×
102
        x == (y::QuoteNode) && return true
×
103
    elseif x isa PhiNode
×
104
        x == (y::PhiNode) && return true
×
105
    elseif x isa PhiCNode
×
106
        x == (y::PhiCNode) && return true
×
107
    elseif x isa CodeInfo
×
108
        x == (y::CodeInfo) && return true
×
109
    end
110
    return false
×
111
end
112

113

114
function isequal_exprargs(x::Array{Any,1}, y::Array{Any,1})
×
115
    l = length(x)
×
116
    l == length(y) || return false
×
117
    for i = 1:l
×
118
        if !isassigned(x, i)
×
119
            # phi and phic values are permitted to be undef
120
            isassigned(y, i) && return false
×
121
        else
122
            isassigned(y, i) || return false
×
123
            isequal_exprarg(x[i], y[i]) || return false
×
124
        end
125
    end
×
126
    return true
×
127
end
128

129
# define == such that == inputs to parsing (including line numbers) yield == outputs from lowering (including all metadata)
130
# (aside from cases where parsing just returns a number, which are ambiguous here)
131
==(x::Expr, y::Expr) = x.head === y.head && isequal_exprargs(x.args, y.args)
×
132

133
==(x::QuoteNode, y::QuoteNode) = isequal_exprarg(x.value, y.value)
×
134

135
==(stmt1::Core.PhiNode, stmt2::Core.PhiNode) = isequal(stmt1.edges, stmt2.edges) && isequal_exprargs(stmt1.values, stmt2.values)
×
136

137
==(stmt1::Core.PhiCNode, stmt2::Core.PhiCNode) = isequal_exprargs(stmt1.values, stmt2.values)
×
138

139
function ==(stmt1::CodeInfo, stmt2::CodeInfo)
×
140
    for i in 1:nfields(stmt1)
×
141
        if !isdefined(stmt1, i)
×
142
            isdefined(stmt2, i) && return false
×
143
        else
144
            isdefined(stmt2, i) || return false
×
145
            f1 = getfield(stmt1, i)
×
146
            f2 = getfield(stmt2, i)
×
147
            f1 isa typeof(f2) || return false
×
148
            if f1 isa Vector{Any}
×
149
                # code or types vectors
150
                isequal_exprargs(f1, f2::Vector{Any}) || return false
×
151
            elseif f1 isa DebugInfo
×
152
                f1 == f2::DebugInfo || return false
×
153
            elseif f1 isa Vector
×
154
                # misc data
155
                l = length(f1)
×
156
                l == length(f2::Vector) || return false
×
157
                for i = 1:l
×
158
                    f1[i] === f2[i] || return false
×
159
                end
×
160
            else
161
                # misc fields
162
                f1 === f2 || return false
×
163
            end
164
        end
165
    end
×
166
    return true
×
167
end
168

169
function ==(x::DebugInfo, y::DebugInfo)
×
170
    for i in 1:nfields(x)
×
171
        getfield(x, i) == getfield(y, i) || return false
×
172
    end
×
173
    return true
×
174
end
175

176
"""
177
    macroexpand(m::Module, x; recursive=true)
178

179
Take the expression `x` and return an equivalent expression with all macros removed (expanded)
180
for executing in module `m`.
181
The `recursive` keyword controls whether deeper levels of nested macros are also expanded.
182
This is demonstrated in the example below:
183
```jldoctest; filter = r"#= .*:6 =#"
184
julia> module M
185
           macro m1()
186
               42
187
           end
188
           macro m2()
189
               :(@m1())
190
           end
191
       end
192
M
193

194
julia> macroexpand(M, :(@m2()), recursive=true)
195
42
196

197
julia> macroexpand(M, :(@m2()), recursive=false)
198
:(#= REPL[1]:6 =# @m1)
199
```
200
"""
201
function macroexpand(m::Module, @nospecialize(x); recursive=true)
2✔
202
    if recursive
×
203
        ccall(:jl_macroexpand, Any, (Any, Any), x, m)
2✔
204
    else
205
        ccall(:jl_macroexpand1, Any, (Any, Any), x, m)
×
206
    end
207
end
208

209
"""
210
    @macroexpand [mod,] ex
211

212
Return equivalent expression with all macros removed (expanded).
213
If two arguments are provided, the first is the module to evaluate in.
214

215
There are differences between `@macroexpand` and [`macroexpand`](@ref).
216

217
* While [`macroexpand`](@ref) takes a keyword argument `recursive`, `@macroexpand`
218
  is always recursive. For a non recursive macro version, see [`@macroexpand1`](@ref).
219

220
* While [`macroexpand`](@ref) has an explicit `module` argument, `@macroexpand` always
221
  expands with respect to the module in which it is called.
222

223
This is best seen in the following example:
224
```jldoctest
225
julia> module M
226
           macro m()
227
               1
228
           end
229
           function f()
230
               (@macroexpand(@m),
231
                macroexpand(M, :(@m)),
232
                macroexpand(parentmodule(M), :(@m))
233
               )
234
           end
235
       end
236
M
237

238
julia> macro m()
239
           2
240
       end
241
@m (macro with 1 method)
242

243
julia> M.f()
244
(1, 1, 2)
245
```
246
With `@macroexpand` the expression expands where `@macroexpand` appears in the code (module `M` in the example).
247
With `macroexpand` the expression expands in the module given as the first argument.
248

249
!!! compat "Julia 1.11"
250
    The two-argument form requires at least Julia 1.11.
251
"""
252
macro macroexpand(code)
253
    return :(macroexpand($__module__, $(QuoteNode(code)), recursive=true))
254
end
255
macro macroexpand(mod, code)
256
    return :(macroexpand($(esc(mod)), $(QuoteNode(code)), recursive=true))
257
end
258

259
"""
260
    @macroexpand1 [mod,] ex
261

262
Non recursive version of [`@macroexpand`](@ref).
263
"""
264
macro macroexpand1(code)
265
    return :(macroexpand($__module__, $(QuoteNode(code)), recursive=false))
266
end
267
macro macroexpand1(mod, code)
268
    return :(macroexpand($(esc(mod)), $(QuoteNode(code)), recursive=false))
269
end
270

271
## misc syntax ##
272

273
"""
274
    Core.eval(m::Module, expr)
275

276
Evaluate an expression in the given module and return the result.
277
"""
278
Core.eval
279

280
"""
281
    @inline
282

283
Give a hint to the compiler that this function is worth inlining.
284

285
Small functions typically do not need the `@inline` annotation,
286
as the compiler does it automatically. By using `@inline` on bigger functions,
287
an extra nudge can be given to the compiler to inline it.
288

289
`@inline` can be applied immediately before a function definition or within a function body.
290

291
```julia
292
# annotate long-form definition
293
@inline function longdef(x)
294
    ...
295
end
296

297
# annotate short-form definition
298
@inline shortdef(x) = ...
299

300
# annotate anonymous function that a `do` block creates
301
f() do
302
    @inline
303
    ...
304
end
305
```
306

307
!!! compat "Julia 1.8"
308
    The usage within a function body requires at least Julia 1.8.
309

310
---
311
    @inline block
312

313
Give a hint to the compiler that calls within `block` are worth inlining.
314

315
```julia
316
# The compiler will try to inline `f`
317
@inline f(...)
318

319
# The compiler will try to inline `f`, `g` and `+`
320
@inline f(...) + g(...)
321
```
322

323
!!! note
324
    A callsite annotation always has the precedence over the annotation applied to the
325
    definition of the called function:
326
    ```julia
327
    @noinline function explicit_noinline(args...)
328
        # body
329
    end
330

331
    let
332
        @inline explicit_noinline(args...) # will be inlined
333
    end
334
    ```
335

336
!!! note
337
    The callsite annotation applies to all calls in the block, including function arguments
338
    that are themselves calls:
339
    ```julia
340
    # The compiler will not inline `getproperty`, `g` or `f`
341
    @noinline f(x.inner, g(y))
342
    ```
343

344
!!! note
345
    When there are nested callsite annotations, the innermost annotation has the precedence:
346
    ```julia
347
    @noinline let a0, b0 = ...
348
        a = @inline f(a0)  # the compiler will try to inline this call
349
        b = f(b0)          # the compiler will NOT try to inline this call
350
        return a, b
351
    end
352
    ```
353

354
!!! warning
355
    Although a callsite annotation will try to force inlining in regardless of the cost model,
356
    there are still chances it can't succeed in it. Especially, recursive calls can not be
357
    inlined even if they are annotated as `@inline`d.
358

359
!!! compat "Julia 1.8"
360
    The callsite annotation requires at least Julia 1.8.
361
"""
362
macro inline(x)
363
    return annotate_meta_def_or_block(x, :inline)
364
end
365

366
"""
367
    @noinline
368

369
Give a hint to the compiler that it should not inline a function.
370

371
Small functions are typically inlined automatically.
372
By using `@noinline` on small functions, auto-inlining can be
373
prevented.
374

375
`@noinline` can be applied immediately before a function definition or within a function body.
376

377
```julia
378
# annotate long-form definition
379
@noinline function longdef(x)
380
    ...
381
end
382

383
# annotate short-form definition
384
@noinline shortdef(x) = ...
385

386
# annotate anonymous function that a `do` block creates
387
f() do
388
    @noinline
389
    ...
390
end
391
```
392

393
!!! compat "Julia 1.8"
394
    The usage within a function body requires at least Julia 1.8.
395

396
---
397
    @noinline block
398

399
Give a hint to the compiler that it should not inline the calls within `block`.
400

401
```julia
402
# The compiler will try to not inline `f`
403
@noinline f(...)
404

405
# The compiler will try to not inline `f`, `g` and `+`
406
@noinline f(...) + g(...)
407
```
408

409
!!! note
410
    A callsite annotation always has the precedence over the annotation applied to the
411
    definition of the called function:
412
    ```julia
413
    @inline function explicit_inline(args...)
414
        # body
415
    end
416

417
    let
418
        @noinline explicit_inline(args...) # will not be inlined
419
    end
420
    ```
421

422
!!! note
423
    When there are nested callsite annotations, the innermost annotation has the precedence:
424
    ```julia
425
    @inline let a0, b0 = ...
426
        a = @noinline f(a0)  # the compiler will NOT try to inline this call
427
        b = f(b0)            # the compiler will try to inline this call
428
        return a, b
429
    end
430
    ```
431

432
!!! compat "Julia 1.8"
433
    The callsite annotation requires at least Julia 1.8.
434

435
---
436
!!! note
437
    If the function is trivial (for example returning a constant) it might get inlined anyway.
438
"""
439
macro noinline(x)
440
    return annotate_meta_def_or_block(x, :noinline)
441
end
442

443
"""
444
    Base.@constprop setting [ex]
445

446
Control the mode of interprocedural constant propagation for the annotated function.
447

448
Two `setting`s are supported:
449

450
- `Base.@constprop :aggressive [ex]`: apply constant propagation aggressively.
451
  For a method where the return type depends on the value of the arguments,
452
  this can yield improved inference results at the cost of additional compile time.
453
- `Base.@constprop :none [ex]`: disable constant propagation. This can reduce compile
454
  times for functions that Julia might otherwise deem worthy of constant-propagation.
455
  Common cases are for functions with `Bool`- or `Symbol`-valued arguments or keyword arguments.
456

457
`Base.@constprop` can be applied immediately before a function definition or within a function body.
458

459
```julia
460
# annotate long-form definition
461
Base.@constprop :aggressive function longdef(x)
462
    ...
463
end
464

465
# annotate short-form definition
466
Base.@constprop :aggressive shortdef(x) = ...
467

468
# annotate anonymous function that a `do` block creates
469
f() do
470
    Base.@constprop :aggressive
471
    ...
472
end
473
```
474

475
!!! compat "Julia 1.10"
476
    The usage within a function body requires at least Julia 1.10.
477
"""
478
macro constprop(setting, ex)
479
    sym = constprop_setting(setting)
480
    isa(ex, Expr) && return esc(pushmeta!(ex, sym))
481
    throw(ArgumentError(LazyString("Bad expression `", ex, "` in `@constprop settings ex`")))
482
end
483
macro constprop(setting)
484
    sym = constprop_setting(setting)
485
    return Expr(:meta, sym)
486
end
487

488
function constprop_setting(@nospecialize setting)
×
489
    s = setting
×
490
    isa(setting, QuoteNode) && (setting = setting.value)
×
491
    if setting === :aggressive
×
492
        return :aggressive_constprop
×
493
    elseif setting === :none
×
494
        return :no_constprop
×
495
    end
496
    throw(ArgumentError(LazyString("`Base.@constprop ", s, "` not supported")))
×
497
end
498

499
"""
500
    Base.@assume_effects setting... [ex]
501

502
Override the compiler's effect modeling.
503
This macro can be used in several contexts:
504
1. Immediately before a method definition, to override the entire effect modeling of the applied method.
505
2. Within a function body without any arguments, to override the entire effect modeling of the enclosing method.
506
3. Applied to a code block, to override the local effect modeling of the applied code block.
507

508
# Examples
509
```jldoctest
510
julia> Base.@assume_effects :terminates_locally function fact(x)
511
           # usage 1:
512
           # this :terminates_locally allows `fact` to be constant-folded
513
           res = 1
514
           0 ≤ x < 20 || error("bad fact")
515
           while x > 1
516
               res *= x
517
               x -= 1
518
           end
519
           return res
520
       end
521
fact (generic function with 1 method)
522

523
julia> code_typed() do
524
           fact(12)
525
       end |> only
526
CodeInfo(
527
1 ─     return 479001600
528
) => Int64
529

530
julia> code_typed() do
531
           map((2,3,4)) do x
532
               # usage 2:
533
               # this :terminates_locally allows this anonymous function to be constant-folded
534
               Base.@assume_effects :terminates_locally
535
               res = 1
536
               0 ≤ x < 20 || error("bad fact")
537
               while x > 1
538
                   res *= x
539
                   x -= 1
540
               end
541
               return res
542
           end
543
       end |> only
544
CodeInfo(
545
1 ─     return (2, 6, 24)
546
) => Tuple{Int64, Int64, Int64}
547

548
julia> code_typed() do
549
           map((2,3,4)) do x
550
               res = 1
551
               0 ≤ x < 20 || error("bad fact")
552
               # usage 3:
553
               # with this :terminates_locally annotation the compiler skips tainting
554
               # `:terminates` effect within this `while` block, allowing the parent
555
               # anonymous function to be constant-folded
556
               Base.@assume_effects :terminates_locally while x > 1
557
                   res *= x
558
                   x -= 1
559
               end
560
               return res
561
           end
562
       end |> only
563
CodeInfo(
564
1 ─     return (2, 6, 24)
565
) => Tuple{Int64, Int64, Int64}
566
```
567

568
!!! compat "Julia 1.8"
569
    Using `Base.@assume_effects` requires Julia version 1.8.
570

571
!!! compat "Julia 1.10"
572
    The usage within a function body requires at least Julia 1.10.
573

574
!!! compat "Julia 1.11"
575
    The code block annotation requires at least Julia 1.11.
576

577
!!! warning
578
    Improper use of this macro causes undefined behavior (including crashes,
579
    incorrect answers, or other hard to track bugs). Use with care and only as a
580
    last resort if absolutely required. Even in such a case, you SHOULD take all
581
    possible steps to minimize the strength of the effect assertion (e.g.,
582
    do not use `:total` if `:nothrow` would have been sufficient).
583

584
In general, each `setting` value makes an assertion about the behavior of the
585
function, without requiring the compiler to prove that this behavior is indeed
586
true. These assertions are made for all world ages. It is thus advisable to limit
587
the use of generic functions that may later be extended to invalidate the
588
assumption (which would cause undefined behavior).
589

590
The following `setting`s are supported.
591
- `:consistent`
592
- `:effect_free`
593
- `:nothrow`
594
- `:terminates_globally`
595
- `:terminates_locally`
596
- `:notaskstate`
597
- `:inaccessiblememonly`
598
- `:noub`
599
- `:noub_if_noinbounds`
600
- `:nortcall`
601
- `:foldable`
602
- `:removable`
603
- `:total`
604

605
# Extended help
606

607
---
608
## `:consistent`
609

610
The `:consistent` setting asserts that for egal (`===`) inputs:
611
- The manner of termination (return value, exception, non-termination) will always be the same.
612
- If the method returns, the results will always be egal.
613

614
!!! note
615
    This in particular implies that the method must not return a freshly allocated
616
    mutable object. Multiple allocations of mutable objects (even with identical
617
    contents) are not egal.
618

619
!!! note
620
    The `:consistent`-cy assertion is made with respect to a particular world range `R`.
621
    More formally, write ``fáµ¢`` for the evaluation of ``f`` in world-age ``i``, then this setting requires:
622
    ```math
623
    ∀ i ∈ R, j ∈ R, x, y: x ≡ y → fᵢ(x) ≡ fⱼ(y)
624
    ```
625

626
    For `@assume_effects`, the range `R` is `m.primary_world:m.deleted_world` of
627
    the annotated or containing method.
628

629
    For ordinary code instances, `R` is `ci.min_world:ci.max_world`.
630

631
    A further implication is that `:consistent` functions may not make their
632
    return value dependent on the state of the heap or any other global state
633
    that is not constant over the given world age range.
634

635
!!! note
636
    The `:consistent`-cy includes all legal rewrites performed by the optimizer.
637
    For example, floating-point fastmath operations are not considered `:consistent`,
638
    because the optimizer may rewrite them causing the output to not be `:consistent`,
639
    even for the same world age (e.g. because one ran in the interpreter, while
640
    the other was optimized).
641

642
!!! note
643
    If `:consistent` functions terminate by throwing an exception, that exception
644
    itself is not required to meet the egality requirement specified above.
645

646
---
647
## `:effect_free`
648

649
The `:effect_free` setting asserts that the method is free of externally semantically
650
visible side effects. The following is an incomplete list of externally semantically
651
visible side effects:
652
- Changing the value of a global variable.
653
- Mutating the heap (e.g. an array or mutable value), except as noted below
654
- Changing the method table (e.g. through calls to eval)
655
- File/Network/etc. I/O
656
- Task switching
657

658
However, the following are explicitly not semantically visible, even if they
659
may be observable:
660
- Memory allocations (both mutable and immutable)
661
- Elapsed time
662
- Garbage collection
663
- Heap mutations of objects whose lifetime does not exceed the method (i.e.
664
  were allocated in the method and do not escape).
665
- The returned value (which is externally visible, but not a side effect)
666

667
The rule of thumb here is that an externally visible side effect is anything
668
that would affect the execution of the remainder of the program if the function
669
were not executed.
670

671
!!! note
672
    The `:effect_free` assertion is made both for the method itself and any code
673
    that is executed by the method. Keep in mind that the assertion must be
674
    valid for all world ages and limit use of this assertion accordingly.
675

676
---
677
## `:nothrow`
678

679
The `:nothrow` settings asserts that this method does not throw an exception
680
(i.e. will either always return a value or never return).
681

682
!!! note
683
    It is permissible for `:nothrow` annotated methods to make use of exception
684
    handling internally as long as the exception is not rethrown out of the
685
    method itself.
686

687
!!! note
688
    If the execution of a method may raise `MethodError`s and similar exceptions, then
689
    the method is not considered as `:nothrow`.
690
    However, note that environment-dependent errors like `StackOverflowError` or `InterruptException`
691
    are not modeled by this effect and thus a method that may result in `StackOverflowError`
692
    does not necessarily need to be `!:nothrow` (although it should usually be `!:terminates` too).
693

694
---
695
## `:terminates_globally`
696

697
The `:terminates_globally` settings asserts that this method will eventually terminate
698
(either normally or abnormally), i.e. does not loop indefinitely.
699

700
!!! note
701
    This `:terminates_globally` assertion covers any other methods called by the annotated method.
702

703
!!! note
704
    The compiler will consider this a strong indication that the method will
705
    terminate relatively *quickly* and may (if otherwise legal) call this
706
    method at compile time. I.e. it is a bad idea to annotate this setting
707
    on a method that *technically*, but not *practically*, terminates.
708

709
---
710
## `:terminates_locally`
711

712
The `:terminates_locally` setting is like `:terminates_globally`, except that it only
713
applies to syntactic control flow *within* the annotated method. It is thus
714
a much weaker (and thus safer) assertion that allows for the possibility of
715
non-termination if the method calls some other method that does not terminate.
716

717
!!! note
718
    `:terminates_globally` implies `:terminates_locally`.
719

720
---
721
## `:notaskstate`
722

723
The `:notaskstate` setting asserts that the method does not use or modify the
724
local task state (task local storage, RNG state, etc.) and may thus be safely
725
moved between tasks without observable results.
726

727
!!! note
728
    The implementation of exception handling makes use of state stored in the
729
    task object. However, this state is currently not considered to be within
730
    the scope of `:notaskstate` and is tracked separately using the `:nothrow`
731
    effect.
732

733
!!! note
734
    The `:notaskstate` assertion concerns the state of the *currently running task*.
735
    If a reference to a `Task` object is obtained by some other means that
736
    does not consider which task is *currently* running, the `:notaskstate`
737
    effect need not be tainted. This is true, even if said task object happens
738
    to be `===` to the currently running task.
739

740
!!! note
741
    Access to task state usually also results in the tainting of other effects,
742
    such as `:effect_free` (if task state is modified) or `:consistent` (if
743
    task state is used in the computation of the result). In particular,
744
    code that is not `:notaskstate`, but is `:effect_free` and `:consistent`
745
    may still be dead-code-eliminated and thus promoted to `:total`.
746

747
---
748
## `:inaccessiblememonly`
749

750
The `:inaccessiblememonly` setting asserts that the method does not access or modify
751
externally accessible mutable memory. This means the method can access or modify mutable
752
memory for newly allocated objects that is not accessible by other methods or top-level
753
execution before return from the method, but it can not access or modify any mutable
754
global state or mutable memory pointed to by its arguments.
755

756
!!! note
757
    Below is an incomplete list of examples that invalidate this assumption:
758
    - a global reference or `getglobal` call to access a mutable global variable
759
    - a global assignment or `setglobal!` call to perform assignment to a non-constant global variable
760
    - `setfield!` call that changes a field of a global mutable variable
761

762
!!! note
763
    This `:inaccessiblememonly` assertion covers any other methods called by the annotated method.
764

765
---
766
## `:noub`
767

768
The `:noub` setting asserts that the method will not execute any undefined behavior
769
(for any input). Note that undefined behavior may technically cause the method to violate
770
any other effect assertions (such as `:consistent` or `:effect_free`) as well, but we do
771
not model this, and they assume the absence of undefined behavior.
772

773
---
774
## `:nortcall`
775

776
The `:nortcall` setting asserts that the method does not call `Core.Compiler.return_type`,
777
and that any other methods this method might call also do not call `Core.Compiler.return_type`.
778

779
!!! note
780
    To be precise, this assertion can be used when a call to `Core.Compiler.return_type` is
781
    not made at runtime; that is, when the result of `Core.Compiler.return_type` is known
782
    exactly at compile time and the call is eliminated by the optimizer. However, since
783
    whether the result of `Core.Compiler.return_type` is folded at compile time depends
784
    heavily on the compiler's implementation, it is generally risky to assert this if
785
    the method in question uses `Core.Compiler.return_type` in any form.
786

787
---
788
## `:foldable`
789

790
This setting is a convenient shortcut for the set of effects that the compiler
791
requires to be guaranteed to constant fold a call at compile time. It is
792
currently equivalent to the following `setting`s:
793
- `:consistent`
794
- `:effect_free`
795
- `:terminates_globally`
796
- `:noub`
797
- `:nortcall`
798

799
!!! note
800
    This list in particular does not include `:nothrow`. The compiler will still
801
    attempt constant propagation and note any thrown error at compile time. Note
802
    however, that by the `:consistent`-cy requirements, any such annotated call
803
    must consistently throw given the same argument values.
804

805
!!! note
806
    An explicit `@inbounds` annotation inside the function will also disable
807
    constant folding and not be overridden by `:foldable`.
808

809
---
810
## `:removable`
811

812
This setting is a convenient shortcut for the set of effects that the compiler
813
requires to be guaranteed to delete a call whose result is unused at compile time.
814
It is currently equivalent to the following `setting`s:
815
- `:effect_free`
816
- `:nothrow`
817
- `:terminates_globally`
818

819
---
820
## `:total`
821

822
This `setting` is the maximum possible set of effects. It currently implies
823
the following other `setting`s:
824
- `:consistent`
825
- `:effect_free`
826
- `:nothrow`
827
- `:terminates_globally`
828
- `:notaskstate`
829
- `:inaccessiblememonly`
830
- `:noub`
831
- `:nortcall`
832

833
!!! warning
834
    `:total` is a very strong assertion and will likely gain additional semantics
835
    in future versions of Julia (e.g. if additional effects are added and included
836
    in the definition of `:total`). As a result, it should be used with care.
837
    Whenever possible, prefer to use the minimum possible set of specific effect
838
    assertions required for a particular application. In cases where a large
839
    number of effect overrides apply to a set of functions, a custom macro is
840
    recommended over the use of `:total`.
841

842
---
843
## Negated effects
844

845
Effect names may be prefixed by `!` to indicate that the effect should be removed
846
from an earlier meta effect. For example, `:total !:nothrow` indicates that while
847
the call is generally total, it may however throw.
848
"""
849
macro assume_effects(args...)
850
    lastex = args[end]
851
    override = compute_assumed_settings(args[begin:end-1])
852
    if is_function_def(unwrap_macrocalls(lastex))
853
        return esc(pushmeta!(lastex::Expr, form_purity_expr(override)))
854
    elseif isexpr(lastex, :macrocall) && lastex.args[1] === Symbol("@ccall")
855
        lastex.args[1] = GlobalRef(Base, Symbol("@ccall_effects"))
856
        insert!(lastex.args, 3, encode_effects_override(override))
857
        return esc(lastex)
858
    end
859
    override′ = compute_assumed_setting(override, lastex)
860
    if override′ !== nothing
861
        # anonymous function case
862
        return Expr(:meta, form_purity_expr(override′))
863
    else
864
        # call site annotation case
865
        return Expr(:block,
866
                    form_purity_expr(override),
867
                    Expr(:local, Expr(:(=), :val, esc(lastex))),
868
                    Expr(:purity), # region end token
869
                    :val)
870
    end
871
end
872

873
function compute_assumed_settings(settings)
×
874
    override = EffectsOverride()
×
875
    for setting in settings
×
876
        override = compute_assumed_setting(override, setting)
×
877
        override === nothing &&
×
878
            throw(ArgumentError("`@assume_effects $setting` not supported"))
879
    end
×
880
    return override
×
881
end
882

883
struct EffectsOverride
884
    consistent::Bool
55,700✔
885
    effect_free::Bool
886
    nothrow::Bool
887
    terminates_globally::Bool
888
    terminates_locally::Bool
889
    notaskstate::Bool
890
    inaccessiblememonly::Bool
891
    noub::Bool
892
    noub_if_noinbounds::Bool
893
    consistent_overlay::Bool
894
    nortcall::Bool
895
end
896

897
function EffectsOverride(
×
898
    override::EffectsOverride =
899
        EffectsOverride(false, false, false, false, false, false, false, false, false, false, false);
900
    consistent::Bool = override.consistent,
901
    effect_free::Bool = override.effect_free,
902
    nothrow::Bool = override.nothrow,
903
    terminates_globally::Bool = override.terminates_globally,
904
    terminates_locally::Bool = override.terminates_locally,
905
    notaskstate::Bool = override.notaskstate,
906
    inaccessiblememonly::Bool = override.inaccessiblememonly,
907
    noub::Bool = override.noub,
908
    noub_if_noinbounds::Bool = override.noub_if_noinbounds,
909
    consistent_overlay::Bool = override.consistent_overlay,
910
    nortcall::Bool = override.nortcall)
911
    return EffectsOverride(
×
912
        consistent,
913
        effect_free,
914
        nothrow,
915
        terminates_globally,
916
        terminates_locally,
917
        notaskstate,
918
        inaccessiblememonly,
919
        noub,
920
        noub_if_noinbounds,
921
        consistent_overlay,
922
        nortcall)
923
end
924

925
const NUM_EFFECTS_OVERRIDES = 11 # sync with julia.h
926

927
function compute_assumed_setting(override::EffectsOverride, @nospecialize(setting), val::Bool=true)
×
928
    if isexpr(setting, :call) && setting.args[1] === :(!)
×
929
        return compute_assumed_setting(override, setting.args[2], !val)
×
930
    elseif isa(setting, QuoteNode)
×
931
        return compute_assumed_setting(override, setting.value, val)
×
932
    end
933
    if setting === :consistent
×
934
        return EffectsOverride(override; consistent = val)
×
935
    elseif setting === :effect_free
×
936
        return EffectsOverride(override; effect_free = val)
×
937
    elseif setting === :nothrow
×
938
        return EffectsOverride(override; nothrow = val)
×
939
    elseif setting === :terminates_globally
×
940
        return EffectsOverride(override; terminates_globally = val)
×
941
    elseif setting === :terminates_locally
×
942
        return EffectsOverride(override; terminates_locally = val)
×
943
    elseif setting === :notaskstate
×
944
        return EffectsOverride(override; notaskstate = val)
×
945
    elseif setting === :inaccessiblememonly
×
946
        return EffectsOverride(override; inaccessiblememonly = val)
×
947
    elseif setting === :noub
×
948
        return EffectsOverride(override; noub = val)
×
949
    elseif setting === :noub_if_noinbounds
×
950
        return EffectsOverride(override; noub_if_noinbounds = val)
×
951
    elseif setting === :foldable
×
952
        consistent = effect_free = terminates_globally = noub = nortcall = val
×
953
        return EffectsOverride(override; consistent, effect_free, terminates_globally, noub, nortcall)
×
954
    elseif setting === :removable
×
955
        effect_free = nothrow = terminates_globally = val
×
956
        return EffectsOverride(override; effect_free, nothrow, terminates_globally)
×
957
    elseif setting === :total
×
958
        consistent = effect_free = nothrow = terminates_globally = notaskstate =
×
959
            inaccessiblememonly = noub = nortcall = val
960
        return EffectsOverride(override;
×
961
            consistent, effect_free, nothrow, terminates_globally, notaskstate,
962
            inaccessiblememonly, noub, nortcall)
963
    end
964
    return nothing
×
965
end
966

967
function encode_effects_override(eo::EffectsOverride)
×
968
    e = 0x0000
×
969
    eo.consistent          && (e |= (0x0001 << 0))
×
970
    eo.effect_free         && (e |= (0x0001 << 1))
×
971
    eo.nothrow             && (e |= (0x0001 << 2))
×
972
    eo.terminates_globally && (e |= (0x0001 << 3))
×
973
    eo.terminates_locally  && (e |= (0x0001 << 4))
×
974
    eo.notaskstate         && (e |= (0x0001 << 5))
×
975
    eo.inaccessiblememonly && (e |= (0x0001 << 6))
×
976
    eo.noub                && (e |= (0x0001 << 7))
×
977
    eo.noub_if_noinbounds  && (e |= (0x0001 << 8))
×
978
    eo.consistent_overlay  && (e |= (0x0001 << 9))
×
979
    eo.nortcall            && (e |= (0x0001 << 10))
×
980
    return e
×
981
end
982

983
function decode_effects_override(e::UInt16)
55,700✔
984
    return EffectsOverride(
2,041,141✔
985
        !iszero(e & (0x0001 << 0)),
986
        !iszero(e & (0x0001 << 1)),
987
        !iszero(e & (0x0001 << 2)),
988
        !iszero(e & (0x0001 << 3)),
989
        !iszero(e & (0x0001 << 4)),
990
        !iszero(e & (0x0001 << 5)),
991
        !iszero(e & (0x0001 << 6)),
992
        !iszero(e & (0x0001 << 7)),
993
        !iszero(e & (0x0001 << 8)),
994
        !iszero(e & (0x0001 << 9)),
995
        !iszero(e & (0x0001 << 10)))
996
end
997

998
function form_purity_expr(override::EffectsOverride)
×
999
    ex = Expr(:purity)
×
1000
    for i = 1:NUM_EFFECTS_OVERRIDES
×
1001
        push!(ex.args, getfield(override, i))
×
1002
    end
×
1003
    return ex
×
1004
end
1005

1006
"""
1007
    Base.@nospecializeinfer function f(args...)
1008
        @nospecialize ...
1009
        ...
1010
    end
1011
    Base.@nospecializeinfer f(@nospecialize args...) = ...
1012

1013
Tells the compiler to infer `f` using the declared types of `@nospecialize`d arguments.
1014
This can be used to limit the number of compiler-generated specializations during inference.
1015

1016
# Examples
1017

1018
```jldoctest; setup = :(using InteractiveUtils)
1019
julia> f(A::AbstractArray) = g(A)
1020
f (generic function with 1 method)
1021

1022
julia> @noinline Base.@nospecializeinfer g(@nospecialize(A::AbstractArray)) = A[1]
1023
g (generic function with 1 method)
1024

1025
julia> @code_typed f([1.0])
1026
CodeInfo(
1027
1 ─ %1 =    invoke g(A::AbstractArray)::Any
1028
└──      return %1
1029
) => Any
1030
```
1031

1032
In this example, `f` will be inferred for each specific type of `A`,
1033
but `g` will only be inferred once with the declared argument type `A::AbstractArray`,
1034
meaning that the compiler will not likely see the excessive inference time on it
1035
while it can not infer the concrete return type of it.
1036
Without the `@nospecializeinfer`, `f([1.0])` would infer the return type of `g` as `Float64`,
1037
indicating that inference ran for `g(::Vector{Float64})` despite the prohibition on
1038
specialized code generation.
1039

1040
!!! compat "Julia 1.10"
1041
    Using `Base.@nospecializeinfer` requires Julia version 1.10.
1042
"""
1043
macro nospecializeinfer(ex)
1044
    esc(isa(ex, Expr) ? pushmeta!(ex, :nospecializeinfer) : ex)
1045
end
1046

1047
"""
1048
    @propagate_inbounds
1049

1050
Tells the compiler to inline a function while retaining the caller's inbounds context.
1051
"""
1052
macro propagate_inbounds(ex)
1053
    if isa(ex, Expr)
1054
        pushmeta!(ex, :inline)
1055
        pushmeta!(ex, :propagate_inbounds)
1056
    end
1057
    esc(ex)
1058
end
1059

1060
"""
1061
    @polly
1062

1063
Tells the compiler to apply the polyhedral optimizer Polly to a function.
1064
"""
1065
macro polly(ex)
1066
    esc(isa(ex, Expr) ? pushmeta!(ex, :polly) : ex)
1067
end
1068

1069
## some macro utilities ##
1070

1071
unwrap_macrocalls(@nospecialize(x)) = x
×
1072
function unwrap_macrocalls(ex::Expr)
×
1073
    inner = ex
×
1074
    while isexpr(inner, :macrocall)
×
1075
        inner = inner.args[end]
×
1076
    end
×
1077
    return inner
×
1078
end
1079

1080
function pushmeta!(ex::Expr, tag::Union{Symbol,Expr})
×
1081
    inner = unwrap_macrocalls(ex)
×
1082
    idx, exargs = findmeta(inner)
×
1083
    if idx != 0
×
1084
        metastmt = exargs[idx]::Expr
×
1085
        push!(metastmt.args, tag)
×
1086
    else
1087
        body = inner.args[2]::Expr
×
1088
        pushfirst!(body.args, Expr(:meta, tag))
×
1089
    end
1090
    return ex
×
1091
end
1092

1093
popmeta!(body, sym) = _getmeta(body, sym, true)
×
1094
peekmeta(body, sym) = _getmeta(body, sym, false)
×
1095

1096
function _getmeta(body::Expr, sym::Symbol, delete::Bool)
×
1097
    body.head === :block || return false, []
×
1098
    _getmeta(body.args, sym, delete)
×
1099
end
1100
_getmeta(arg, sym, delete::Bool) = (false, [])
×
1101
function _getmeta(body::Array{Any,1}, sym::Symbol, delete::Bool)
×
1102
    idx, blockargs = findmeta_block(body, args -> findmetaarg(args,sym)!=0)
×
1103
    if idx == 0
×
1104
        return false, []
×
1105
    end
1106
    metaargs = blockargs[idx].args
×
1107
    i = findmetaarg(blockargs[idx].args, sym)
×
1108
    if i == 0
×
1109
        return false, []
×
1110
    end
1111
    ret = isa(metaargs[i], Expr) ? (metaargs[i]::Expr).args : []
×
1112
    if delete
×
1113
        deleteat!(metaargs, i)
×
1114
        isempty(metaargs) && deleteat!(blockargs, idx)
×
1115
    end
1116
    true, ret
×
1117
end
1118

1119
# Find index of `sym` in a meta expression argument list, or 0.
1120
function findmetaarg(metaargs, sym)
×
1121
    for i = 1:length(metaargs)
×
1122
        arg = metaargs[i]
×
1123
        if (isa(arg, Symbol) && (arg::Symbol)    == sym) ||
×
1124
           (isa(arg, Expr)   && (arg::Expr).head == sym)
1125
            return i
×
1126
        end
1127
    end
×
1128
    return 0
×
1129
end
1130

1131
function annotate_meta_def_or_block(@nospecialize(ex), meta::Symbol)
×
1132
    inner = unwrap_macrocalls(ex)
×
1133
    if is_function_def(inner)
×
1134
        # annotation on a definition
1135
        return esc(pushmeta!(ex, meta))
×
1136
    else
1137
        # annotation on a block
1138
        return Expr(:block,
×
1139
                    Expr(meta, true),
1140
                    Expr(:local, Expr(:(=), :val, esc(ex))),
1141
                    Expr(meta, false),
1142
                    :val)
1143
    end
1144
end
1145

1146
function is_short_function_def(@nospecialize(ex))
×
1147
    isexpr(ex, :(=)) || return false
×
1148
    while length(ex.args) >= 1 && isa(ex.args[1], Expr)
×
1149
        (ex.args[1].head === :call) && return true
×
1150
        (ex.args[1].head === :where || ex.args[1].head === :(::)) || return false
×
1151
        ex = ex.args[1]
×
1152
    end
×
1153
    return false
×
1154
end
1155
is_function_def(@nospecialize(ex)) =
×
1156
    return isexpr(ex, :function) || is_short_function_def(ex) || isexpr(ex, :->)
1157

1158
function findmeta(ex::Expr)
×
1159
    if is_function_def(ex)
×
1160
        body = ex.args[2]::Expr
×
1161
        body.head === :block || error(body, " is not a block expression")
×
1162
        return findmeta_block(ex.args)
×
1163
    end
1164
    error(ex, " is not a function expression")
×
1165
end
1166

1167
findmeta(ex::Array{Any,1}) = findmeta_block(ex)
×
1168

1169
function findmeta_block(exargs, argsmatch=args->true)
×
1170
    for i = 1:length(exargs)
×
1171
        a = exargs[i]
×
1172
        if isa(a, Expr)
×
1173
            if a.head === :meta && argsmatch(a.args)
×
1174
                return i, exargs
×
1175
            elseif a.head === :block
×
1176
                idx, exa = findmeta_block(a.args, argsmatch)
×
1177
                if idx != 0
×
1178
                    return idx, exa
×
1179
                end
1180
            end
1181
        end
1182
    end
×
1183
    return 0, []
×
1184
end
1185

1186
"""
1187
    Base.remove_linenums!(ex)
1188

1189
Remove all line-number metadata from expression-like object `ex`.
1190
"""
1191
function remove_linenums!(@nospecialize ex)
×
1192
    if ex isa Expr
×
1193
        if ex.head === :block || ex.head === :quote
×
1194
            # remove line number expressions from metadata (not argument literal or inert) position
1195
            filter!(ex.args) do x
×
1196
                isa(x, Expr) && x.head === :line && return false
×
1197
                isa(x, LineNumberNode) && return false
×
1198
                return true
×
1199
            end
1200
        end
1201
        for subex in ex.args
×
1202
            subex isa Expr && remove_linenums!(subex)
×
1203
        end
×
1204
    elseif ex isa CodeInfo
×
1205
        ex.debuginfo = Core.DebugInfo(ex.debuginfo.def) # TODO: filter partially, but keep edges
×
1206
    end
1207
    return ex
×
1208
end
1209

1210
replace_linenums!(ex, ln::LineNumberNode) = ex
×
1211
function replace_linenums!(ex::Expr, ln::LineNumberNode)
8✔
1212
    if ex.head === :block || ex.head === :quote
14✔
1213
        # replace line number expressions from metadata (not argument literal or inert) position
1214
        map!(ex.args, ex.args) do @nospecialize(x)
4✔
1215
            isa(x, Expr) && x.head === :line && length(x.args) == 1 && return Expr(:line, ln.line)
4✔
1216
            isa(x, Expr) && x.head === :line && length(x.args) == 2 && return Expr(:line, ln.line, ln.file)
4✔
1217
            isa(x, LineNumberNode) && return ln
4✔
1218
            return x
2✔
1219
        end
1220
    end
1221
    # preserve any linenums inside `esc(...)` guards
1222
    if ex.head !== :escape
8✔
1223
        for subex in ex.args
6✔
1224
            subex isa Expr && replace_linenums!(subex, ln)
8✔
1225
        end
8✔
1226
    end
1227
    return ex
8✔
1228
end
1229

1230
macro generated()
1231
    return Expr(:generated)
1232
end
1233

1234
"""
1235
    @generated f
1236

1237
`@generated` is used to annotate a function which will be generated.
1238
In the body of the generated function, only types of arguments can be read
1239
(not the values). The function returns a quoted expression evaluated when the
1240
function is called. The `@generated` macro should not be used on functions mutating
1241
the global scope or depending on mutable elements.
1242

1243
See [Metaprogramming](@ref) for further details.
1244

1245
# Examples
1246
```jldoctest
1247
julia> @generated function bar(x)
1248
           if x <: Integer
1249
               return :(x ^ 2)
1250
           else
1251
               return :(x)
1252
           end
1253
       end
1254
bar (generic function with 1 method)
1255

1256
julia> bar(4)
1257
16
1258

1259
julia> bar("baz")
1260
"baz"
1261
```
1262
"""
1263
macro generated(f)
1264
    if isa(f, Expr) && (f.head === :function || is_short_function_def(f))
1265
        body = f.args[2]
1266
        lno = body.args[1]
1267
        return Expr(:escape,
1268
                    Expr(f.head, f.args[1],
1269
                         Expr(:block,
1270
                              lno,
1271
                              Expr(:if, Expr(:generated),
1272
                                   body,
1273
                                   Expr(:block,
1274
                                        Expr(:meta, :generated_only),
1275
                                        Expr(:return, nothing))))))
1276
    else
1277
        error("invalid syntax; @generated must be used with a function definition")
1278
    end
1279
end
1280

1281

1282
"""
1283
    @atomic var
1284
    @atomic order ex
1285

1286
Mark `var` or `ex` as being performed atomically, if `ex` is a supported expression.
1287
If no `order` is specified it defaults to :sequentially_consistent.
1288

1289
    @atomic a.b.x = new
1290
    @atomic a.b.x += addend
1291
    @atomic :release a.b.x = new
1292
    @atomic :acquire_release a.b.x += addend
1293
    @atomic m[idx] = new
1294
    @atomic m[idx] += addend
1295
    @atomic :release m[idx] = new
1296
    @atomic :acquire_release m[idx] += addend
1297

1298
Perform the store operation expressed on the right atomically and return the
1299
new value.
1300

1301
With assignment (`=`), this operation translates to a `setproperty!(a.b, :x, new)`
1302
or, in case of reference, to a `setindex_atomic!(m, order, new, idx)` call,
1303
with `order` defaulting to `:sequentially_consistent`.
1304

1305
With any modifying operator this operation translates to a
1306
`modifyproperty!(a.b, :x, op, addend)[2]` or, in case of reference, to a
1307
`modifyindex_atomic!(m, order, op, addend, idx...)[2]` call,
1308
with `order` defaulting to `:sequentially_consistent`.
1309

1310
    @atomic a.b.x max arg2
1311
    @atomic a.b.x + arg2
1312
    @atomic max(a.b.x, arg2)
1313
    @atomic :acquire_release max(a.b.x, arg2)
1314
    @atomic :acquire_release a.b.x + arg2
1315
    @atomic :acquire_release a.b.x max arg2
1316
    @atomic m[idx] max arg2
1317
    @atomic m[idx] + arg2
1318
    @atomic max(m[idx], arg2)
1319
    @atomic :acquire_release max(m[idx], arg2)
1320
    @atomic :acquire_release m[idx] + arg2
1321
    @atomic :acquire_release m[idx] max arg2
1322

1323
Perform the binary operation expressed on the right atomically. Store the
1324
result into the field or the reference in the first argument, and return the values
1325
`(old, new)`.
1326

1327
This operation translates to a `modifyproperty!(a.b, :x, func, arg2)` or,
1328
in case of reference to a `modifyindex_atomic!(m, order, func, arg2, idx)` call,
1329
with `order` defaulting to `:sequentially_consistent`.
1330

1331
See [Per-field atomics](@ref man-atomics) section in the manual for more details.
1332

1333
# Examples
1334
```jldoctest
1335
julia> mutable struct Atomic{T}; @atomic x::T; end
1336

1337
julia> a = Atomic(1)
1338
Atomic{Int64}(1)
1339

1340
julia> @atomic a.x # fetch field x of a, with sequential consistency
1341
1
1342

1343
julia> @atomic :sequentially_consistent a.x = 2 # set field x of a, with sequential consistency
1344
2
1345

1346
julia> @atomic a.x += 1 # increment field x of a, with sequential consistency
1347
3
1348

1349
julia> @atomic a.x + 1 # increment field x of a, with sequential consistency
1350
3 => 4
1351

1352
julia> @atomic a.x # fetch field x of a, with sequential consistency
1353
4
1354

1355
julia> @atomic max(a.x, 10) # change field x of a to the max value, with sequential consistency
1356
4 => 10
1357

1358
julia> @atomic a.x max 5 # again change field x of a to the max value, with sequential consistency
1359
10 => 10
1360
```
1361

1362
```jldoctest
1363
julia> mem = AtomicMemory{Int}(undef, 2);
1364

1365
julia> @atomic mem[1] = 2 # set mem[1] to value 2 with sequential consistency
1366
2
1367

1368
julia> @atomic :monotonic mem[1] # fetch the first value of mem, with monotonic consistency
1369
2
1370

1371
julia> @atomic mem[1] += 1 # increment the first value of mem, with sequential consistency
1372
3
1373

1374
julia> @atomic mem[1] + 1 # increment the first value of mem, with sequential consistency
1375
3 => 4
1376

1377
julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1378
4
1379

1380
julia> @atomic max(mem[1], 10) # change the first value of mem to the max value, with sequential consistency
1381
4 => 10
1382

1383
julia> @atomic mem[1] max 5 # again change the first value of mem to the max value, with sequential consistency
1384
10 => 10
1385
```
1386

1387
!!! compat "Julia 1.7"
1388
    Atomic fields functionality requires at least Julia 1.7.
1389

1390
!!! compat "Julia 1.12"
1391
    Atomic reference functionality requires at least Julia 1.12.
1392
"""
1393
macro atomic(ex)
1394
    if !isa(ex, Symbol) && !is_expr(ex, :(::))
1395
        return make_atomic(QuoteNode(:sequentially_consistent), ex)
1396
    end
1397
    return esc(Expr(:atomic, ex))
1398
end
1399
macro atomic(order, ex)
1400
    order isa QuoteNode || (order = esc(order))
1401
    return make_atomic(order, ex)
1402
end
1403
macro atomic(a1, op, a2)
1404
    return make_atomic(QuoteNode(:sequentially_consistent), a1, op, a2)
1405
end
1406
macro atomic(order, a1, op, a2)
1407
    order isa QuoteNode || (order = esc(order))
1408
    return make_atomic(order, a1, op, a2)
1409
end
1410
function make_atomic(order, ex)
×
1411
    @nospecialize
×
1412
    if ex isa Expr
×
1413
        if isexpr(ex, :., 2)
×
1414
            l, r = esc(ex.args[1]), esc(ex.args[2])
×
1415
            return :(getproperty($l, $r, $order))
×
1416
        elseif isexpr(ex, :call, 3)
×
1417
            return make_atomic(order, ex.args[2], ex.args[1], ex.args[3])
×
1418
        elseif isexpr(ex, :ref)
×
1419
            x, idcs = esc(ex.args[1]), map(esc, ex.args[2:end])
×
1420
            return :(getindex_atomic($x, $order, $(idcs...)))
×
1421
        elseif ex.head === :(=)
×
1422
            l, r = ex.args[1], esc(ex.args[2])
×
1423
            if is_expr(l, :., 2)
×
1424
                ll, lr = esc(l.args[1]), esc(l.args[2])
×
1425
                return :(setproperty!($ll, $lr, $r, $order))
×
1426
            elseif is_expr(l, :ref)
×
1427
                x, idcs = esc(l.args[1]), map(esc, l.args[2:end])
×
1428
                return :(setindex_atomic!($x, $order, $r, $(idcs...)))
×
1429
            end
1430
        end
1431
        if length(ex.args) == 2
×
1432
            if ex.head === :(+=)
×
1433
                op = :+
×
1434
            elseif ex.head === :(-=)
×
1435
                op = :-
×
1436
            elseif ex.head === :(|=)
×
1437
                op = :|
×
1438
            elseif ex.head === :(&=)
×
1439
                op = :&
×
1440
            elseif @isdefined string
×
1441
                shead = string(ex.head)
×
1442
                if endswith(shead, '=')
×
1443
                    op = Symbol(shead[1:prevind(shead, end)])
×
1444
                end
1445
            end
1446
            if @isdefined(op)
×
1447
                return Expr(:ref, make_atomic(order, ex.args[1], op, ex.args[2]), 2)
×
1448
            end
1449
        end
1450
    end
1451
    error("could not parse @atomic expression $ex")
×
1452
end
1453
function make_atomic(order, a1, op, a2)
×
1454
    @nospecialize
×
1455
    if is_expr(a1, :., 2)
×
1456
        a1l, a1r, op, a2 = esc(a1.args[1]), esc(a1.args[2]), esc(op), esc(a2)
×
1457
        return :(modifyproperty!($a1l, $a1r, $op, $a2, $order))
×
1458
    elseif is_expr(a1, :ref)
×
1459
        x, idcs, op, a2 = esc(a1.args[1]), map(esc, a1.args[2:end]), esc(op), esc(a2)
×
1460
        return :(modifyindex_atomic!($x, $order, $op, $a2, $(idcs...)))
×
1461
    end
1462
    error("@atomic modify expression missing field access or indexing")
×
1463
end
1464

1465

1466
"""
1467
    @atomicswap a.b.x = new
1468
    @atomicswap :sequentially_consistent a.b.x = new
1469
    @atomicswap m[idx] = new
1470
    @atomicswap :sequentially_consistent m[idx] = new
1471

1472
Stores `new` into `a.b.x` (`m[idx]` in case of reference) and returns the old
1473
value of `a.b.x` (the old value stored at `m[idx]`, respectively).
1474

1475
This operation translates to a `swapproperty!(a.b, :x, new)` or,
1476
in case of reference, `swapindex_atomic!(mem, order, new, idx)` call,
1477
with `order` defaulting to `:sequentially_consistent`.
1478

1479
See [Per-field atomics](@ref man-atomics) section in the manual for more details.
1480

1481
# Examples
1482
```jldoctest
1483
julia> mutable struct Atomic{T}; @atomic x::T; end
1484

1485
julia> a = Atomic(1)
1486
Atomic{Int64}(1)
1487

1488
julia> @atomicswap a.x = 2+2 # replace field x of a with 4, with sequential consistency
1489
1
1490

1491
julia> @atomic a.x # fetch field x of a, with sequential consistency
1492
4
1493
```
1494

1495
```jldoctest
1496
julia> mem = AtomicMemory{Int}(undef, 2);
1497

1498
julia> @atomic mem[1] = 1;
1499

1500
julia> @atomicswap mem[1] = 4 # replace the first value of `mem` with 4, with sequential consistency
1501
1
1502

1503
julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1504
4
1505
```
1506

1507
!!! compat "Julia 1.7"
1508
    Atomic fields functionality requires at least Julia 1.7.
1509

1510
!!! compat "Julia 1.12"
1511
    Atomic reference functionality requires at least Julia 1.12.
1512
"""
1513
macro atomicswap(order, ex)
1514
    order isa QuoteNode || (order = esc(order))
1515
    return make_atomicswap(order, ex)
1516
end
1517
macro atomicswap(ex)
1518
    return make_atomicswap(QuoteNode(:sequentially_consistent), ex)
1519
end
1520
function make_atomicswap(order, ex)
×
1521
    @nospecialize
×
1522
    is_expr(ex, :(=), 2) || error("@atomicswap expression missing assignment")
×
1523
    l, val = ex.args[1], esc(ex.args[2])
×
1524
    if is_expr(l, :., 2)
×
1525
        ll, lr = esc(l.args[1]), esc(l.args[2])
×
1526
        return :(swapproperty!($ll, $lr, $val, $order))
×
1527
    elseif is_expr(l, :ref)
×
1528
        x, idcs = esc(l.args[1]), map(esc, l.args[2:end])
×
1529
        return :(swapindex_atomic!($x, $order, $val, $(idcs...)))
×
1530
    end
1531
    error("@atomicswap expression missing field access or indexing")
×
1532
end
1533

1534

1535
"""
1536
    @atomicreplace a.b.x expected => desired
1537
    @atomicreplace :sequentially_consistent a.b.x expected => desired
1538
    @atomicreplace :sequentially_consistent :monotonic a.b.x expected => desired
1539
    @atomicreplace m[idx] expected => desired
1540
    @atomicreplace :sequentially_consistent m[idx] expected => desired
1541
    @atomicreplace :sequentially_consistent :monotonic m[idx] expected => desired
1542

1543
Perform the conditional replacement expressed by the pair atomically, returning
1544
the values `(old, success::Bool)`. Where `success` indicates whether the
1545
replacement was completed.
1546

1547
This operation translates to a `replaceproperty!(a.b, :x, expected, desired)` or,
1548
in case of reference, to a
1549
`replaceindex_atomic!(mem, success_order, fail_order, expected, desired, idx)` call,
1550
with both orders defaulting to `:sequentially_consistent`.
1551

1552
See [Per-field atomics](@ref man-atomics) section in the manual for more details.
1553

1554
# Examples
1555
```jldoctest
1556
julia> mutable struct Atomic{T}; @atomic x::T; end
1557

1558
julia> a = Atomic(1)
1559
Atomic{Int64}(1)
1560

1561
julia> @atomicreplace a.x 1 => 2 # replace field x of a with 2 if it was 1, with sequential consistency
1562
(old = 1, success = true)
1563

1564
julia> @atomic a.x # fetch field x of a, with sequential consistency
1565
2
1566

1567
julia> @atomicreplace a.x 1 => 3 # replace field x of a with 2 if it was 1, with sequential consistency
1568
(old = 2, success = false)
1569

1570
julia> xchg = 2 => 0; # replace field x of a with 0 if it was 2, with sequential consistency
1571

1572
julia> @atomicreplace a.x xchg
1573
(old = 2, success = true)
1574

1575
julia> @atomic a.x # fetch field x of a, with sequential consistency
1576
0
1577
```
1578

1579
```jldoctest
1580
julia> mem = AtomicMemory{Int}(undef, 2);
1581

1582
julia> @atomic mem[1] = 1;
1583

1584
julia> @atomicreplace mem[1] 1 => 2 # replace the first value of mem with 2 if it was 1, with sequential consistency
1585
(old = 1, success = true)
1586

1587
julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1588
2
1589

1590
julia> @atomicreplace mem[1] 1 => 3 # replace field x of a with 2 if it was 1, with sequential consistency
1591
(old = 2, success = false)
1592

1593
julia> xchg = 2 => 0; # replace field x of a with 0 if it was 2, with sequential consistency
1594

1595
julia> @atomicreplace mem[1] xchg
1596
(old = 2, success = true)
1597

1598
julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1599
0
1600
```
1601

1602
!!! compat "Julia 1.7"
1603
    Atomic fields functionality requires at least Julia 1.7.
1604

1605
!!! compat "Julia 1.12"
1606
    Atomic reference functionality requires at least Julia 1.12.
1607
"""
1608
macro atomicreplace(success_order, fail_order, ex, old_new)
1609
    fail_order isa QuoteNode || (fail_order = esc(fail_order))
1610
    success_order isa QuoteNode || (success_order = esc(success_order))
1611
    return make_atomicreplace(success_order, fail_order, ex, old_new)
1612
end
1613
macro atomicreplace(order, ex, old_new)
1614
    order isa QuoteNode || (order = esc(order))
1615
    return make_atomicreplace(order, order, ex, old_new)
1616
end
1617
macro atomicreplace(ex, old_new)
1618
    return make_atomicreplace(QuoteNode(:sequentially_consistent), QuoteNode(:sequentially_consistent), ex, old_new)
1619
end
1620
function make_atomicreplace(success_order, fail_order, ex, old_new)
×
1621
    @nospecialize
×
1622
    if is_expr(ex, :., 2)
×
1623
        ll, lr = esc(ex.args[1]), esc(ex.args[2])
×
1624
        if is_expr(old_new, :call, 3) && old_new.args[1] === :(=>)
×
1625
            exp, rep = esc(old_new.args[2]), esc(old_new.args[3])
×
1626
            return :(replaceproperty!($ll, $lr, $exp, $rep, $success_order, $fail_order))
×
1627
        else
1628
            old_new = esc(old_new)
×
1629
            return :(replaceproperty!($ll, $lr, $old_new::Pair..., $success_order, $fail_order))
×
1630
        end
1631
    elseif is_expr(ex, :ref)
×
1632
        x, idcs = esc(ex.args[1]), map(esc, ex.args[2:end])
×
1633
        if is_expr(old_new, :call, 3) && old_new.args[1] === :(=>)
×
1634
            exp, rep = esc(old_new.args[2]), esc(old_new.args[3])
×
1635
            return :(replaceindex_atomic!($x, $success_order, $fail_order, $exp, $rep, $(idcs...)))
×
1636
        else
1637
            old_new = esc(old_new)
×
1638
            return :(replaceindex_atomic!($x, $success_order, $fail_order, $old_new::Pair..., $(idcs...)))
×
1639
        end
1640
    end
1641
    error("@atomicreplace expression missing field access or indexing")
×
1642
end
1643

1644
"""
1645
    @atomiconce a.b.x = value
1646
    @atomiconce :sequentially_consistent a.b.x = value
1647
    @atomiconce :sequentially_consistent :monotonic a.b.x = value
1648
    @atomiconce m[idx] = value
1649
    @atomiconce :sequentially_consistent m[idx] = value
1650
    @atomiconce :sequentially_consistent :monotonic m[idx] = value
1651

1652
Perform the conditional assignment of value atomically if it was previously
1653
unset. Returned value `success::Bool` indicates whether the assignment was completed.
1654

1655
This operation translates to a `setpropertyonce!(a.b, :x, value)` or,
1656
in case of reference, to a `setindexonce_atomic!(m, success_order, fail_order, value, idx)` call,
1657
with both orders defaulting to `:sequentially_consistent`.
1658

1659
See [Per-field atomics](@ref man-atomics) section in the manual for more details.
1660

1661
# Examples
1662
```jldoctest
1663
julia> mutable struct AtomicOnce
1664
           @atomic x
1665
           AtomicOnce() = new()
1666
       end
1667

1668
julia> a = AtomicOnce()
1669
AtomicOnce(#undef)
1670

1671
julia> @atomiconce a.x = 1 # set field x of a to 1, if unset, with sequential consistency
1672
true
1673

1674
julia> @atomic a.x # fetch field x of a, with sequential consistency
1675
1
1676

1677
julia> @atomiconce :monotonic a.x = 2 # set field x of a to 1, if unset, with monotonic consistence
1678
false
1679
```
1680

1681
```jldoctest
1682
julia> mem = AtomicMemory{Vector{Int}}(undef, 1);
1683

1684
julia> isassigned(mem, 1)
1685
false
1686

1687
julia> @atomiconce mem[1] = [1] # set the first value of mem to [1], if unset, with sequential consistency
1688
true
1689

1690
julia> isassigned(mem, 1)
1691
true
1692

1693
julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1694
1-element Vector{Int64}:
1695
 1
1696

1697
julia> @atomiconce :monotonic mem[1] = [2] # set the first value of mem to [2], if unset, with monotonic
1698
false
1699

1700
julia> @atomic mem[1]
1701
1-element Vector{Int64}:
1702
 1
1703
```
1704

1705
!!! compat "Julia 1.11"
1706
    Atomic fields functionality requires at least Julia 1.11.
1707

1708
!!! compat "Julia 1.12"
1709
    Atomic reference functionality requires at least Julia 1.12.
1710
"""
1711
macro atomiconce(success_order, fail_order, ex)
1712
    fail_order isa QuoteNode || (fail_order = esc(fail_order))
1713
    success_order isa QuoteNode || (success_order = esc(success_order))
1714
    return make_atomiconce(success_order, fail_order, ex)
1715
end
1716
macro atomiconce(order, ex)
1717
    order isa QuoteNode || (order = esc(order))
1718
    return make_atomiconce(order, order, ex)
1719
end
1720
macro atomiconce(ex)
1721
    return make_atomiconce(QuoteNode(:sequentially_consistent), QuoteNode(:sequentially_consistent), ex)
1722
end
1723
function make_atomiconce(success_order, fail_order, ex)
×
1724
    @nospecialize
×
1725
    is_expr(ex, :(=), 2) || error("@atomiconce expression missing assignment")
×
1726
    l, val = ex.args[1], esc(ex.args[2])
×
1727
    if is_expr(l, :., 2)
×
1728
        ll, lr = esc(l.args[1]), esc(l.args[2])
×
1729
        return :(setpropertyonce!($ll, $lr, $val, $success_order, $fail_order))
×
1730
    elseif is_expr(l, :ref)
×
1731
        x, idcs = esc(l.args[1]), map(esc, l.args[2:end])
×
1732
        return :(setindexonce_atomic!($x, $success_order, $fail_order, $val, $(idcs...)))
×
1733
    end
1734
    error("@atomiconce expression missing field access or indexing")
×
1735
end
1736

1737
# Meta expression head, these generally can't be deleted even when they are
1738
# in a dead branch but can be ignored when analyzing uses/liveness.
1739
is_meta_expr_head(head::Symbol) = head === :boundscheck || head === :meta || head === :loopinfo
5,989,166✔
1740
is_meta_expr(@nospecialize x) = isa(x, Expr) && is_meta_expr_head(x.head)
1,082,416✔
1741

1742
"""
1743
    isa_ast_node(x)
1744

1745
Return false if `x` is not interpreted specially by any of inference, lowering,
1746
or codegen as either an AST or IR special form.
1747
"""
1748
function isa_ast_node(@nospecialize x)
16,906✔
1749
    # c.f. Core.IR module, augmented with AST types
1750
    return x isa NewvarNode ||
372,820✔
1751
           x isa CodeInfo ||
1752
           x isa LineNumberNode ||
1753
           x isa GotoNode ||
1754
           x isa GotoIfNot ||
1755
           x isa EnterNode ||
1756
           x isa ReturnNode ||
1757
           x isa SSAValue ||
1758
           x isa SlotNumber ||
1759
           x isa Argument ||
1760
           x isa QuoteNode ||
1761
           x isa GlobalRef ||
1762
           x isa Symbol ||
1763
           x isa PiNode ||
1764
           x isa PhiNode ||
1765
           x isa PhiCNode ||
1766
           x isa UpsilonNode ||
1767
           x isa Expr
1768
end
1769

1770
is_self_quoting(@nospecialize(x)) = !isa_ast_node(x)
×
1771

1772
"""
1773
    quoted(x)
1774

1775
Return `x` made safe for inserting as a constant into IR. Note that this does
1776
not make it safe for inserting into an AST, since eval will sometimes copy some
1777
types of AST object inside, and even may sometimes evaluate and interpolate any
1778
`\$` inside, depending on the context.
1779
"""
1780
quoted(@nospecialize(x)) = isa_ast_node(x) ? QuoteNode(x) : x
195,909✔
1781

1782
# Implementation of generated functions
1783
function generated_body_to_codeinfo(ex::Expr, defmod::Module, isva::Bool)
73✔
1784
    ci = ccall(:jl_fl_lower, Any, (Any, Any, Ptr{UInt8}, Csize_t, Csize_t, Cint),
73✔
1785
               ex, defmod, "none", 0, typemax(Csize_t), 0)[1]
1786
    if !isa(ci, CodeInfo)
73✔
1787
        if isa(ci, Expr) && ci.head === :error
×
1788
            msg = ci.args[1]
×
1789
            error(msg isa String ? strcat("syntax: ", msg) : msg)
×
1790
        end
1791
        error("The function body AST defined by this @generated function is not pure. This likely means it contains a closure, a comprehension or a generator.")
×
1792
    end
1793
    ci.isva = isva
73✔
1794
    code = ci.code
73✔
1795
    bindings = IdSet{Core.Binding}()
73✔
1796
    for i = 1:length(code)
73✔
1797
        stmt = code[i]
1,154✔
1798
        if isa(stmt, GlobalRef)
1,154✔
1799
            push!(bindings, convert(Core.Binding, stmt))
335✔
1800
        end
1801
    end
2,235✔
1802
    if !isempty(bindings)
73✔
1803
        ci.edges = Core.svec(bindings...)
73✔
1804
    end
1805
    return ci
73✔
1806
end
1807

1808
# invoke and wrap the results of @generated expression
1809
function (g::Core.GeneratedFunctionStub)(world::UInt, source::Method, @nospecialize args...)
73✔
1810
    # args is (spvals..., argtypes...)
1811
    body = g.gen(args...)
73✔
1812
    file = source.file
73✔
1813
    file isa Symbol || (file = :none)
73✔
1814
    lam = Expr(:lambda, Expr(:argnames, g.argnames...).args,
73✔
1815
               Expr(:var"scope-block",
1816
                    Expr(:block,
1817
                         LineNumberNode(Int(source.line), source.file),
1818
                         Expr(:meta, :push_loc, file, :var"@generated body"),
1819
                         Expr(:return, body),
1820
                         Expr(:meta, :pop_loc))))
1821
    spnames = g.spnames
73✔
1822
    return generated_body_to_codeinfo(spnames === Core.svec() ? lam : Expr(Symbol("with-static-parameters"), lam, spnames...),
73✔
1823
        source.module,
1824
        source.isva)
1825
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

© 2026 Coveralls, Inc