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

JuliaLang / julia / #38064

08 May 2025 12:35AM UTC coverage: 25.677% (-0.08%) from 25.752%
#38064

push

local

web-flow
Add default for inline command line switch (#58230)

Fixes small discrepancy between man page and documentation.

12790 of 49812 relevant lines covered (25.68%)

708025.35 hits per line

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

22.74
/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
97,327,622✔
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,418,980✔
42
function copy(x::PhiNode)
48,239✔
43
    values = x.values
48,239✔
44
    nvalues = length(values)
48,239✔
45
    new_values = Vector{Any}(undef, nvalues)
48,239✔
46
    @inbounds for i = 1:nvalues
48,239✔
47
        isassigned(values, i) || continue
101,287✔
48
        new_values[i] = copy_exprs(values[i])
101,287✔
49
    end
154,157✔
50
    return PhiNode(copy(x.edges), new_values)
48,239✔
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 AST that the compiler mutates
64
function copy_exprs(@nospecialize(x))
7,980,762✔
65
    if isa(x, Expr)
8,081,669✔
66
        return copy(x)
2,418,956✔
67
    elseif isa(x, PhiNode)
5,662,712✔
68
        return copy(x)
48,239✔
69
    elseif isa(x, PhiCNode)
5,614,473✔
70
        return copy(x)
×
71
    end
72
    return x
5,614,473✔
73
end
74
copy_exprargs(x::Array{Any,1}) = Any[copy_exprs(@inbounds x[i]) for i in eachindex(x)]
5,750,080✔
75

76
@eval exprarray(head::Symbol, arg::Array{Any,1}) = $(Expr(:new, :Expr, :head, :arg))
2,418,980✔
77

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

94

95
==(x::Expr, y::Expr) = x.head === y.head && isequal(x.args, y.args)
×
96
==(x::QuoteNode, y::QuoteNode) = isequal(x.value, y.value)
×
97
==(stmt1::Core.PhiNode, stmt2::Core.PhiNode) = stmt1.edges == stmt2.edges && stmt1.values == stmt2.values
×
98

99
"""
100
    macroexpand(m::Module, x; recursive=true)
101

102
Take the expression `x` and return an equivalent expression with all macros removed (expanded)
103
for executing in module `m`.
104
The `recursive` keyword controls whether deeper levels of nested macros are also expanded.
105
This is demonstrated in the example below:
106
```jldoctest; filter = r"#= .*:6 =#"
107
julia> module M
108
           macro m1()
109
               42
110
           end
111
           macro m2()
112
               :(@m1())
113
           end
114
       end
115
M
116

117
julia> macroexpand(M, :(@m2()), recursive=true)
118
42
119

120
julia> macroexpand(M, :(@m2()), recursive=false)
121
:(#= REPL[1]:6 =# @m1)
122
```
123
"""
124
function macroexpand(m::Module, @nospecialize(x); recursive=true)
2✔
125
    if recursive
×
126
        ccall(:jl_macroexpand, Any, (Any, Any), x, m)
2✔
127
    else
128
        ccall(:jl_macroexpand1, Any, (Any, Any), x, m)
×
129
    end
130
end
131

132
"""
133
    @macroexpand [mod,] ex
134

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

138
There are differences between `@macroexpand` and [`macroexpand`](@ref).
139

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

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

146
This is best seen in the following example:
147
```julia-repl
148
julia> module M
149
           macro m()
150
               1
151
           end
152
           function f()
153
               (@macroexpand(@m),
154
                macroexpand(M, :(@m)),
155
                macroexpand(Main, :(@m))
156
               )
157
           end
158
       end
159
M
160

161
julia> macro m()
162
           2
163
       end
164
@m (macro with 1 method)
165

166
julia> M.f()
167
(1, 1, 2)
168
```
169
With `@macroexpand` the expression expands where `@macroexpand` appears in the code (module `M` in the example).
170
With `macroexpand` the expression expands in the module given as the first argument.
171

172
!!! compat "Julia 1.11"
173
    The two-argument form requires at least Julia 1.11.
174
"""
175
macro macroexpand(code)
176
    return :(macroexpand($__module__, $(QuoteNode(code)), recursive=true))
177
end
178
macro macroexpand(mod, code)
179
    return :(macroexpand($(esc(mod)), $(QuoteNode(code)), recursive=true))
180
end
181

182
"""
183
    @macroexpand1 [mod,] ex
184

185
Non recursive version of [`@macroexpand`](@ref).
186
"""
187
macro macroexpand1(code)
188
    return :(macroexpand($__module__, $(QuoteNode(code)), recursive=false))
189
end
190
macro macroexpand1(mod, code)
191
    return :(macroexpand($(esc(mod)), $(QuoteNode(code)), recursive=false))
192
end
193

194
## misc syntax ##
195

196
"""
197
    Core.eval(m::Module, expr)
198

199
Evaluate an expression in the given module and return the result.
200
"""
201
Core.eval
202

203
"""
204
    @inline
205

206
Give a hint to the compiler that this function is worth inlining.
207

208
Small functions typically do not need the `@inline` annotation,
209
as the compiler does it automatically. By using `@inline` on bigger functions,
210
an extra nudge can be given to the compiler to inline it.
211

212
`@inline` can be applied immediately before a function definition or within a function body.
213

214
```julia
215
# annotate long-form definition
216
@inline function longdef(x)
217
    ...
218
end
219

220
# annotate short-form definition
221
@inline shortdef(x) = ...
222

223
# annotate anonymous function that a `do` block creates
224
f() do
225
    @inline
226
    ...
227
end
228
```
229

230
!!! compat "Julia 1.8"
231
    The usage within a function body requires at least Julia 1.8.
232

233
---
234
    @inline block
235

236
Give a hint to the compiler that calls within `block` are worth inlining.
237

238
```julia
239
# The compiler will try to inline `f`
240
@inline f(...)
241

242
# The compiler will try to inline `f`, `g` and `+`
243
@inline f(...) + g(...)
244
```
245

246
!!! note
247
    A callsite annotation always has the precedence over the annotation applied to the
248
    definition of the called function:
249
    ```julia
250
    @noinline function explicit_noinline(args...)
251
        # body
252
    end
253

254
    let
255
        @inline explicit_noinline(args...) # will be inlined
256
    end
257
    ```
258

259
!!! note
260
    The callsite annotation applies to all calls in the block, including function arguments
261
    that are themselves calls:
262
    ```julia
263
    # The compiler will not inline `getproperty`, `g` or `f`
264
    @noinline f(x.inner, g(y))
265
    ```
266

267
!!! note
268
    When there are nested callsite annotations, the innermost annotation has the precedence:
269
    ```julia
270
    @noinline let a0, b0 = ...
271
        a = @inline f(a0)  # the compiler will try to inline this call
272
        b = f(b0)          # the compiler will NOT try to inline this call
273
        return a, b
274
    end
275
    ```
276

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

282
!!! compat "Julia 1.8"
283
    The callsite annotation requires at least Julia 1.8.
284
"""
285
macro inline(x)
286
    return annotate_meta_def_or_block(x, :inline)
287
end
288

289
"""
290
    @noinline
291

292
Give a hint to the compiler that it should not inline a function.
293

294
Small functions are typically inlined automatically.
295
By using `@noinline` on small functions, auto-inlining can be
296
prevented.
297

298
`@noinline` can be applied immediately before a function definition or within a function body.
299

300
```julia
301
# annotate long-form definition
302
@noinline function longdef(x)
303
    ...
304
end
305

306
# annotate short-form definition
307
@noinline shortdef(x) = ...
308

309
# annotate anonymous function that a `do` block creates
310
f() do
311
    @noinline
312
    ...
313
end
314
```
315

316
!!! compat "Julia 1.8"
317
    The usage within a function body requires at least Julia 1.8.
318

319
---
320
    @noinline block
321

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

324
```julia
325
# The compiler will try to not inline `f`
326
@noinline f(...)
327

328
# The compiler will try to not inline `f`, `g` and `+`
329
@noinline f(...) + g(...)
330
```
331

332
!!! note
333
    A callsite annotation always has the precedence over the annotation applied to the
334
    definition of the called function:
335
    ```julia
336
    @inline function explicit_inline(args...)
337
        # body
338
    end
339

340
    let
341
        @noinline explicit_inline(args...) # will not be inlined
342
    end
343
    ```
344

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

355
!!! compat "Julia 1.8"
356
    The callsite annotation requires at least Julia 1.8.
357

358
---
359
!!! note
360
    If the function is trivial (for example returning a constant) it might get inlined anyway.
361
"""
362
macro noinline(x)
363
    return annotate_meta_def_or_block(x, :noinline)
364
end
365

366
"""
367
    Base.@constprop setting [ex]
368

369
Control the mode of interprocedural constant propagation for the annotated function.
370

371
Two `setting`s are supported:
372

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

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

382
```julia
383
# annotate long-form definition
384
Base.@constprop :aggressive function longdef(x)
385
    ...
386
end
387

388
# annotate short-form definition
389
Base.@constprop :aggressive shortdef(x) = ...
390

391
# annotate anonymous function that a `do` block creates
392
f() do
393
    Base.@constprop :aggressive
394
    ...
395
end
396
```
397

398
!!! compat "Julia 1.10"
399
    The usage within a function body requires at least Julia 1.10.
400
"""
401
macro constprop(setting, ex)
402
    sym = constprop_setting(setting)
403
    isa(ex, Expr) && return esc(pushmeta!(ex, sym))
404
    throw(ArgumentError(LazyString("Bad expression `", ex, "` in `@constprop settings ex`")))
405
end
406
macro constprop(setting)
407
    sym = constprop_setting(setting)
408
    return Expr(:meta, sym)
409
end
410

411
function constprop_setting(@nospecialize setting)
×
412
    s = setting
×
413
    isa(setting, QuoteNode) && (setting = setting.value)
×
414
    if setting === :aggressive
×
415
        return :aggressive_constprop
×
416
    elseif setting === :none
×
417
        return :no_constprop
×
418
    end
419
    throw(ArgumentError(LazyString("`Base.@constprop ", s, "` not supported")))
×
420
end
421

422
"""
423
    Base.@assume_effects setting... [ex]
424

425
Override the compiler's effect modeling.
426
This macro can be used in several contexts:
427
1. Immediately before a method definition, to override the entire effect modeling of the applied method.
428
2. Within a function body without any arguments, to override the entire effect modeling of the enclosing method.
429
3. Applied to a code block, to override the local effect modeling of the applied code block.
430

431
# Examples
432
```jldoctest
433
julia> Base.@assume_effects :terminates_locally function fact(x)
434
           # usage 1:
435
           # this :terminates_locally allows `fact` to be constant-folded
436
           res = 1
437
           0 ≤ x < 20 || error("bad fact")
438
           while x > 1
439
               res *= x
440
               x -= 1
441
           end
442
           return res
443
       end
444
fact (generic function with 1 method)
445

446
julia> code_typed() do
447
           fact(12)
448
       end |> only
449
CodeInfo(
450
1 ─     return 479001600
451
) => Int64
452

453
julia> code_typed() do
454
           map((2,3,4)) do x
455
               # usage 2:
456
               # this :terminates_locally allows this anonymous function to be constant-folded
457
               Base.@assume_effects :terminates_locally
458
               res = 1
459
               0 ≤ x < 20 || error("bad fact")
460
               while x > 1
461
                   res *= x
462
                   x -= 1
463
               end
464
               return res
465
           end
466
       end |> only
467
CodeInfo(
468
1 ─     return (2, 6, 24)
469
) => Tuple{Int64, Int64, Int64}
470

471
julia> code_typed() do
472
           map((2,3,4)) do x
473
               res = 1
474
               0 ≤ x < 20 || error("bad fact")
475
               # usage 3:
476
               # with this :terminates_locally annotation the compiler skips tainting
477
               # `:terminates` effect within this `while` block, allowing the parent
478
               # anonymous function to be constant-folded
479
               Base.@assume_effects :terminates_locally while x > 1
480
                   res *= x
481
                   x -= 1
482
               end
483
               return res
484
           end
485
       end |> only
486
CodeInfo(
487
1 ─     return (2, 6, 24)
488
) => Tuple{Int64, Int64, Int64}
489
```
490

491
!!! compat "Julia 1.8"
492
    Using `Base.@assume_effects` requires Julia version 1.8.
493

494
!!! compat "Julia 1.10"
495
    The usage within a function body requires at least Julia 1.10.
496

497
!!! compat "Julia 1.11"
498
    The code block annotation requires at least Julia 1.11.
499

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

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

513
The following `setting`s are supported.
514
- `:consistent`
515
- `:effect_free`
516
- `:nothrow`
517
- `:terminates_globally`
518
- `:terminates_locally`
519
- `:notaskstate`
520
- `:inaccessiblememonly`
521
- `:noub`
522
- `:noub_if_noinbounds`
523
- `:nortcall`
524
- `:foldable`
525
- `:removable`
526
- `:total`
527

528
# Extended help
529

530
---
531
## `:consistent`
532

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

537
!!! note
538
    This in particular implies that the method must not return a freshly allocated
539
    mutable object. Multiple allocations of mutable objects (even with identical
540
    contents) are not egal.
541

542
!!! note
543
    The `:consistent`-cy assertion is made with respect to a particular world range `R`.
544
    More formally, write ``fáµ¢`` for the evaluation of ``f`` in world-age ``i``, then this setting requires:
545
    ```math
546
    ∀ i ∈ R, j ∈ R, x, y: x ≡ y → fᵢ(x) ≡ fⱼ(y)
547
    ```
548

549
    For `@assume_effects`, the range `R` is `m.primary_world:m.deleted_world` of
550
    the annotated or containing method.
551

552
    For ordinary code instances, `R` is `ci.min_world:ci.max_world`.
553

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

558
!!! note
559
    The `:consistent`-cy includes all legal rewrites performed by the optimizer.
560
    For example, floating-point fastmath operations are not considered `:consistent`,
561
    because the optimizer may rewrite them causing the output to not be `:consistent`,
562
    even for the same world age (e.g. because one ran in the interpreter, while
563
    the other was optimized).
564

565
!!! note
566
    If `:consistent` functions terminate by throwing an exception, that exception
567
    itself is not required to meet the egality requirement specified above.
568

569
---
570
## `:effect_free`
571

572
The `:effect_free` setting asserts that the method is free of externally semantically
573
visible side effects. The following is an incomplete list of externally semantically
574
visible side effects:
575
- Changing the value of a global variable.
576
- Mutating the heap (e.g. an array or mutable value), except as noted below
577
- Changing the method table (e.g. through calls to eval)
578
- File/Network/etc. I/O
579
- Task switching
580

581
However, the following are explicitly not semantically visible, even if they
582
may be observable:
583
- Memory allocations (both mutable and immutable)
584
- Elapsed time
585
- Garbage collection
586
- Heap mutations of objects whose lifetime does not exceed the method (i.e.
587
  were allocated in the method and do not escape).
588
- The returned value (which is externally visible, but not a side effect)
589

590
The rule of thumb here is that an externally visible side effect is anything
591
that would affect the execution of the remainder of the program if the function
592
were not executed.
593

594
!!! note
595
    The `:effect_free` assertion is made both for the method itself and any code
596
    that is executed by the method. Keep in mind that the assertion must be
597
    valid for all world ages and limit use of this assertion accordingly.
598

599
---
600
## `:nothrow`
601

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

605
!!! note
606
    It is permissible for `:nothrow` annotated methods to make use of exception
607
    handling internally as long as the exception is not rethrown out of the
608
    method itself.
609

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

617
---
618
## `:terminates_globally`
619

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

623
!!! note
624
    This `:terminates_globally` assertion covers any other methods called by the annotated method.
625

626
!!! note
627
    The compiler will consider this a strong indication that the method will
628
    terminate relatively *quickly* and may (if otherwise legal) call this
629
    method at compile time. I.e. it is a bad idea to annotate this setting
630
    on a method that *technically*, but not *practically*, terminates.
631

632
---
633
## `:terminates_locally`
634

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

640
!!! note
641
    `:terminates_globally` implies `:terminates_locally`.
642

643
---
644
## `:notaskstate`
645

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

650
!!! note
651
    The implementation of exception handling makes use of state stored in the
652
    task object. However, this state is currently not considered to be within
653
    the scope of `:notaskstate` and is tracked separately using the `:nothrow`
654
    effect.
655

656
!!! note
657
    The `:notaskstate` assertion concerns the state of the *currently running task*.
658
    If a reference to a `Task` object is obtained by some other means that
659
    does not consider which task is *currently* running, the `:notaskstate`
660
    effect need not be tainted. This is true, even if said task object happens
661
    to be `===` to the currently running task.
662

663
!!! note
664
    Access to task state usually also results in the tainting of other effects,
665
    such as `:effect_free` (if task state is modified) or `:consistent` (if
666
    task state is used in the computation of the result). In particular,
667
    code that is not `:notaskstate`, but is `:effect_free` and `:consistent`
668
    may still be dead-code-eliminated and thus promoted to `:total`.
669

670
---
671
## `:inaccessiblememonly`
672

673
The `:inaccessiblememonly` setting asserts that the method does not access or modify
674
externally accessible mutable memory. This means the method can access or modify mutable
675
memory for newly allocated objects that is not accessible by other methods or top-level
676
execution before return from the method, but it can not access or modify any mutable
677
global state or mutable memory pointed to by its arguments.
678

679
!!! note
680
    Below is an incomplete list of examples that invalidate this assumption:
681
    - a global reference or `getglobal` call to access a mutable global variable
682
    - a global assignment or `setglobal!` call to perform assignment to a non-constant global variable
683
    - `setfield!` call that changes a field of a global mutable variable
684

685
!!! note
686
    This `:inaccessiblememonly` assertion covers any other methods called by the annotated method.
687

688
---
689
## `:noub`
690

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

696
---
697
## `:nortcall`
698

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

702
!!! note
703
    To be precise, this assertion can be used when a call to `Core.Compiler.return_type` is
704
    not made at runtime; that is, when the result of `Core.Compiler.return_type` is known
705
    exactly at compile time and the call is eliminated by the optimizer. However, since
706
    whether the result of `Core.Compiler.return_type` is folded at compile time depends
707
    heavily on the compiler's implementation, it is generally risky to assert this if
708
    the method in question uses `Core.Compiler.return_type` in any form.
709

710
---
711
## `:foldable`
712

713
This setting is a convenient shortcut for the set of effects that the compiler
714
requires to be guaranteed to constant fold a call at compile time. It is
715
currently equivalent to the following `setting`s:
716
- `:consistent`
717
- `:effect_free`
718
- `:terminates_globally`
719
- `:noub`
720
- `:nortcall`
721

722
!!! note
723
    This list in particular does not include `:nothrow`. The compiler will still
724
    attempt constant propagation and note any thrown error at compile time. Note
725
    however, that by the `:consistent`-cy requirements, any such annotated call
726
    must consistently throw given the same argument values.
727

728
!!! note
729
    An explicit `@inbounds` annotation inside the function will also disable
730
    constant folding and not be overridden by `:foldable`.
731

732
---
733
## `:removable`
734

735
This setting is a convenient shortcut for the set of effects that the compiler
736
requires to be guaranteed to delete a call whose result is unused at compile time.
737
It is currently equivalent to the following `setting`s:
738
- `:effect_free`
739
- `:nothrow`
740
- `:terminates_globally`
741

742
---
743
## `:total`
744

745
This `setting` is the maximum possible set of effects. It currently implies
746
the following other `setting`s:
747
- `:consistent`
748
- `:effect_free`
749
- `:nothrow`
750
- `:terminates_globally`
751
- `:notaskstate`
752
- `:inaccessiblememonly`
753
- `:noub`
754
- `:nortcall`
755

756
!!! warning
757
    `:total` is a very strong assertion and will likely gain additional semantics
758
    in future versions of Julia (e.g. if additional effects are added and included
759
    in the definition of `:total`). As a result, it should be used with care.
760
    Whenever possible, prefer to use the minimum possible set of specific effect
761
    assertions required for a particular application. In cases where a large
762
    number of effect overrides apply to a set of functions, a custom macro is
763
    recommended over the use of `:total`.
764

765
---
766
## Negated effects
767

768
Effect names may be prefixed by `!` to indicate that the effect should be removed
769
from an earlier meta effect. For example, `:total !:nothrow` indicates that while
770
the call is generally total, it may however throw.
771
"""
772
macro assume_effects(args...)
773
    lastex = args[end]
774
    override = compute_assumed_settings(args[begin:end-1])
775
    if is_function_def(unwrap_macrocalls(lastex))
776
        return esc(pushmeta!(lastex::Expr, form_purity_expr(override)))
777
    elseif isexpr(lastex, :macrocall) && lastex.args[1] === Symbol("@ccall")
778
        lastex.args[1] = GlobalRef(Base, Symbol("@ccall_effects"))
779
        insert!(lastex.args, 3, encode_effects_override(override))
780
        return esc(lastex)
781
    end
782
    override′ = compute_assumed_setting(override, lastex)
783
    if override′ !== nothing
784
        # anonymous function case
785
        return Expr(:meta, form_purity_expr(override′))
786
    else
787
        # call site annotation case
788
        return Expr(:block,
789
                    form_purity_expr(override),
790
                    Expr(:local, Expr(:(=), :val, esc(lastex))),
791
                    Expr(:purity), # region end token
792
                    :val)
793
    end
794
end
795

796
function compute_assumed_settings(settings)
×
797
    override = EffectsOverride()
×
798
    for setting in settings
×
799
        override = compute_assumed_setting(override, setting)
×
800
        override === nothing &&
×
801
            throw(ArgumentError("`@assume_effects $setting` not supported"))
802
    end
×
803
    return override
×
804
end
805

806
struct EffectsOverride
807
    consistent::Bool
57,352✔
808
    effect_free::Bool
809
    nothrow::Bool
810
    terminates_globally::Bool
811
    terminates_locally::Bool
812
    notaskstate::Bool
813
    inaccessiblememonly::Bool
814
    noub::Bool
815
    noub_if_noinbounds::Bool
816
    consistent_overlay::Bool
817
    nortcall::Bool
818
end
819

820
function EffectsOverride(
×
821
    override::EffectsOverride =
822
        EffectsOverride(false, false, false, false, false, false, false, false, false, false, false);
823
    consistent::Bool = override.consistent,
824
    effect_free::Bool = override.effect_free,
825
    nothrow::Bool = override.nothrow,
826
    terminates_globally::Bool = override.terminates_globally,
827
    terminates_locally::Bool = override.terminates_locally,
828
    notaskstate::Bool = override.notaskstate,
829
    inaccessiblememonly::Bool = override.inaccessiblememonly,
830
    noub::Bool = override.noub,
831
    noub_if_noinbounds::Bool = override.noub_if_noinbounds,
832
    consistent_overlay::Bool = override.consistent_overlay,
833
    nortcall::Bool = override.nortcall)
834
    return EffectsOverride(
835
        consistent,
836
        effect_free,
837
        nothrow,
838
        terminates_globally,
839
        terminates_locally,
840
        notaskstate,
841
        inaccessiblememonly,
842
        noub,
843
        noub_if_noinbounds,
844
        consistent_overlay,
845
        nortcall)
846
end
847

848
const NUM_EFFECTS_OVERRIDES = 11 # sync with julia.h
849

850
function compute_assumed_setting(override::EffectsOverride, @nospecialize(setting), val::Bool=true)
×
851
    if isexpr(setting, :call) && setting.args[1] === :(!)
×
852
        return compute_assumed_setting(override, setting.args[2], !val)
×
853
    elseif isa(setting, QuoteNode)
×
854
        return compute_assumed_setting(override, setting.value, val)
×
855
    end
856
    if setting === :consistent
×
857
        return EffectsOverride(override; consistent = val)
×
858
    elseif setting === :effect_free
×
859
        return EffectsOverride(override; effect_free = val)
×
860
    elseif setting === :nothrow
×
861
        return EffectsOverride(override; nothrow = val)
×
862
    elseif setting === :terminates_globally
×
863
        return EffectsOverride(override; terminates_globally = val)
×
864
    elseif setting === :terminates_locally
×
865
        return EffectsOverride(override; terminates_locally = val)
×
866
    elseif setting === :notaskstate
×
867
        return EffectsOverride(override; notaskstate = val)
×
868
    elseif setting === :inaccessiblememonly
×
869
        return EffectsOverride(override; inaccessiblememonly = val)
×
870
    elseif setting === :noub
×
871
        return EffectsOverride(override; noub = val)
×
872
    elseif setting === :noub_if_noinbounds
×
873
        return EffectsOverride(override; noub_if_noinbounds = val)
×
874
    elseif setting === :foldable
×
875
        consistent = effect_free = terminates_globally = noub = nortcall = val
×
876
        return EffectsOverride(override; consistent, effect_free, terminates_globally, noub, nortcall)
×
877
    elseif setting === :removable
×
878
        effect_free = nothrow = terminates_globally = val
×
879
        return EffectsOverride(override; effect_free, nothrow, terminates_globally)
×
880
    elseif setting === :total
×
881
        consistent = effect_free = nothrow = terminates_globally = notaskstate =
×
882
            inaccessiblememonly = noub = nortcall = val
883
        return EffectsOverride(override;
×
884
            consistent, effect_free, nothrow, terminates_globally, notaskstate,
885
            inaccessiblememonly, noub, nortcall)
886
    end
887
    return nothing
×
888
end
889

890
function encode_effects_override(eo::EffectsOverride)
×
891
    e = 0x0000
×
892
    eo.consistent          && (e |= (0x0001 << 0))
×
893
    eo.effect_free         && (e |= (0x0001 << 1))
×
894
    eo.nothrow             && (e |= (0x0001 << 2))
×
895
    eo.terminates_globally && (e |= (0x0001 << 3))
×
896
    eo.terminates_locally  && (e |= (0x0001 << 4))
×
897
    eo.notaskstate         && (e |= (0x0001 << 5))
×
898
    eo.inaccessiblememonly && (e |= (0x0001 << 6))
×
899
    eo.noub                && (e |= (0x0001 << 7))
×
900
    eo.noub_if_noinbounds  && (e |= (0x0001 << 8))
×
901
    eo.consistent_overlay  && (e |= (0x0001 << 9))
×
902
    eo.nortcall            && (e |= (0x0001 << 10))
×
903
    return e
×
904
end
905

906
function decode_effects_override(e::UInt16)
57,352✔
907
    return EffectsOverride(
2,018,563✔
908
        !iszero(e & (0x0001 << 0)),
909
        !iszero(e & (0x0001 << 1)),
910
        !iszero(e & (0x0001 << 2)),
911
        !iszero(e & (0x0001 << 3)),
912
        !iszero(e & (0x0001 << 4)),
913
        !iszero(e & (0x0001 << 5)),
914
        !iszero(e & (0x0001 << 6)),
915
        !iszero(e & (0x0001 << 7)),
916
        !iszero(e & (0x0001 << 8)),
917
        !iszero(e & (0x0001 << 9)),
918
        !iszero(e & (0x0001 << 10)))
919
end
920

921
function form_purity_expr(override::EffectsOverride)
×
922
    ex = Expr(:purity)
×
923
    for i = 1:NUM_EFFECTS_OVERRIDES
×
924
        push!(ex.args, getfield(override, i))
×
925
    end
×
926
    return ex
×
927
end
928

929
"""
930
    Base.@nospecializeinfer function f(args...)
931
        @nospecialize ...
932
        ...
933
    end
934
    Base.@nospecializeinfer f(@nospecialize args...) = ...
935

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

939
# Examples
940

941
```jldoctest; setup = :(using InteractiveUtils)
942
julia> f(A::AbstractArray) = g(A)
943
f (generic function with 1 method)
944

945
julia> @noinline Base.@nospecializeinfer g(@nospecialize(A::AbstractArray)) = A[1]
946
g (generic function with 1 method)
947

948
julia> @code_typed f([1.0])
949
CodeInfo(
950
1 ─ %1 =    invoke g(A::AbstractArray)::Any
951
└──      return %1
952
) => Any
953
```
954

955
In this example, `f` will be inferred for each specific type of `A`,
956
but `g` will only be inferred once with the declared argument type `A::AbstractArray`,
957
meaning that the compiler will not likely see the excessive inference time on it
958
while it can not infer the concrete return type of it.
959
Without the `@nospecializeinfer`, `f([1.0])` would infer the return type of `g` as `Float64`,
960
indicating that inference ran for `g(::Vector{Float64})` despite the prohibition on
961
specialized code generation.
962

963
!!! compat "Julia 1.10"
964
    Using `Base.@nospecializeinfer` requires Julia version 1.10.
965
"""
966
macro nospecializeinfer(ex)
967
    esc(isa(ex, Expr) ? pushmeta!(ex, :nospecializeinfer) : ex)
968
end
969

970
"""
971
    @propagate_inbounds
972

973
Tells the compiler to inline a function while retaining the caller's inbounds context.
974
"""
975
macro propagate_inbounds(ex)
976
    if isa(ex, Expr)
977
        pushmeta!(ex, :inline)
978
        pushmeta!(ex, :propagate_inbounds)
979
    end
980
    esc(ex)
981
end
982

983
"""
984
    @polly
985

986
Tells the compiler to apply the polyhedral optimizer Polly to a function.
987
"""
988
macro polly(ex)
989
    esc(isa(ex, Expr) ? pushmeta!(ex, :polly) : ex)
990
end
991

992
## some macro utilities ##
993

994
unwrap_macrocalls(@nospecialize(x)) = x
×
995
function unwrap_macrocalls(ex::Expr)
×
996
    inner = ex
×
997
    while isexpr(inner, :macrocall)
×
998
        inner = inner.args[end]
×
999
    end
×
1000
    return inner
×
1001
end
1002

1003
function pushmeta!(ex::Expr, tag::Union{Symbol,Expr})
×
1004
    inner = unwrap_macrocalls(ex)
×
1005
    idx, exargs = findmeta(inner)
×
1006
    if idx != 0
×
1007
        metastmt = exargs[idx]::Expr
×
1008
        push!(metastmt.args, tag)
×
1009
    else
1010
        body = inner.args[2]::Expr
×
1011
        pushfirst!(body.args, Expr(:meta, tag))
×
1012
    end
1013
    return ex
×
1014
end
1015

1016
popmeta!(body, sym) = _getmeta(body, sym, true)
×
1017
peekmeta(body, sym) = _getmeta(body, sym, false)
×
1018

1019
function _getmeta(body::Expr, sym::Symbol, delete::Bool)
×
1020
    body.head === :block || return false, []
×
1021
    _getmeta(body.args, sym, delete)
×
1022
end
1023
_getmeta(arg, sym, delete::Bool) = (false, [])
×
1024
function _getmeta(body::Array{Any,1}, sym::Symbol, delete::Bool)
×
1025
    idx, blockargs = findmeta_block(body, args -> findmetaarg(args,sym)!=0)
×
1026
    if idx == 0
×
1027
        return false, []
×
1028
    end
1029
    metaargs = blockargs[idx].args
×
1030
    i = findmetaarg(blockargs[idx].args, sym)
×
1031
    if i == 0
×
1032
        return false, []
×
1033
    end
1034
    ret = isa(metaargs[i], Expr) ? (metaargs[i]::Expr).args : []
×
1035
    if delete
×
1036
        deleteat!(metaargs, i)
×
1037
        isempty(metaargs) && deleteat!(blockargs, idx)
×
1038
    end
1039
    true, ret
×
1040
end
1041

1042
# Find index of `sym` in a meta expression argument list, or 0.
1043
function findmetaarg(metaargs, sym)
×
1044
    for i = 1:length(metaargs)
×
1045
        arg = metaargs[i]
×
1046
        if (isa(arg, Symbol) && (arg::Symbol)    == sym) ||
×
1047
           (isa(arg, Expr)   && (arg::Expr).head == sym)
1048
            return i
×
1049
        end
1050
    end
×
1051
    return 0
×
1052
end
1053

1054
function annotate_meta_def_or_block(@nospecialize(ex), meta::Symbol)
×
1055
    inner = unwrap_macrocalls(ex)
×
1056
    if is_function_def(inner)
×
1057
        # annotation on a definition
1058
        return esc(pushmeta!(ex, meta))
×
1059
    else
1060
        # annotation on a block
1061
        return Expr(:block,
×
1062
                    Expr(meta, true),
1063
                    Expr(:local, Expr(:(=), :val, esc(ex))),
1064
                    Expr(meta, false),
1065
                    :val)
1066
    end
1067
end
1068

1069
function is_short_function_def(@nospecialize(ex))
×
1070
    isexpr(ex, :(=)) || return false
×
1071
    while length(ex.args) >= 1 && isa(ex.args[1], Expr)
×
1072
        (ex.args[1].head === :call) && return true
×
1073
        (ex.args[1].head === :where || ex.args[1].head === :(::)) || return false
×
1074
        ex = ex.args[1]
×
1075
    end
×
1076
    return false
×
1077
end
1078
is_function_def(@nospecialize(ex)) =
×
1079
    return isexpr(ex, :function) || is_short_function_def(ex) || isexpr(ex, :->)
1080

1081
function findmeta(ex::Expr)
×
1082
    if is_function_def(ex)
×
1083
        body = ex.args[2]::Expr
×
1084
        body.head === :block || error(body, " is not a block expression")
×
1085
        return findmeta_block(ex.args)
×
1086
    end
1087
    error(ex, " is not a function expression")
×
1088
end
1089

1090
findmeta(ex::Array{Any,1}) = findmeta_block(ex)
×
1091

1092
function findmeta_block(exargs, argsmatch=args->true)
×
1093
    for i = 1:length(exargs)
×
1094
        a = exargs[i]
×
1095
        if isa(a, Expr)
×
1096
            if a.head === :meta && argsmatch(a.args)
×
1097
                return i, exargs
×
1098
            elseif a.head === :block
×
1099
                idx, exa = findmeta_block(a.args, argsmatch)
×
1100
                if idx != 0
×
1101
                    return idx, exa
×
1102
                end
1103
            end
1104
        end
1105
    end
×
1106
    return 0, []
×
1107
end
1108

1109
"""
1110
    Base.remove_linenums!(ex)
1111

1112
Remove all line-number metadata from expression-like object `ex`.
1113
"""
1114
function remove_linenums!(@nospecialize ex)
×
1115
    if ex isa Expr
×
1116
        if ex.head === :block || ex.head === :quote
×
1117
            # remove line number expressions from metadata (not argument literal or inert) position
1118
            filter!(ex.args) do x
×
1119
                isa(x, Expr) && x.head === :line && return false
×
1120
                isa(x, LineNumberNode) && return false
×
1121
                return true
×
1122
            end
1123
        end
1124
        for subex in ex.args
×
1125
            subex isa Expr && remove_linenums!(subex)
×
1126
        end
×
1127
    elseif ex isa CodeInfo
×
1128
        ex.debuginfo = Core.DebugInfo(ex.debuginfo.def) # TODO: filter partially, but keep edges
×
1129
    end
1130
    return ex
×
1131
end
1132

1133
replace_linenums!(ex, ln::LineNumberNode) = ex
×
1134
function replace_linenums!(ex::Expr, ln::LineNumberNode)
8✔
1135
    if ex.head === :block || ex.head === :quote
14✔
1136
        # replace line number expressions from metadata (not argument literal or inert) position
1137
        map!(ex.args, ex.args) do @nospecialize(x)
4✔
1138
            isa(x, Expr) && x.head === :line && length(x.args) == 1 && return Expr(:line, ln.line)
4✔
1139
            isa(x, Expr) && x.head === :line && length(x.args) == 2 && return Expr(:line, ln.line, ln.file)
4✔
1140
            isa(x, LineNumberNode) && return ln
4✔
1141
            return x
2✔
1142
        end
1143
    end
1144
    # preserve any linenums inside `esc(...)` guards
1145
    if ex.head !== :escape
8✔
1146
        for subex in ex.args
6✔
1147
            subex isa Expr && replace_linenums!(subex, ln)
8✔
1148
        end
8✔
1149
    end
1150
    return ex
8✔
1151
end
1152

1153
macro generated()
1154
    return Expr(:generated)
1155
end
1156

1157
"""
1158
    @generated f
1159

1160
`@generated` is used to annotate a function which will be generated.
1161
In the body of the generated function, only types of arguments can be read
1162
(not the values). The function returns a quoted expression evaluated when the
1163
function is called. The `@generated` macro should not be used on functions mutating
1164
the global scope or depending on mutable elements.
1165

1166
See [Metaprogramming](@ref) for further details.
1167

1168
# Examples
1169
```jldoctest
1170
julia> @generated function bar(x)
1171
           if x <: Integer
1172
               return :(x ^ 2)
1173
           else
1174
               return :(x)
1175
           end
1176
       end
1177
bar (generic function with 1 method)
1178

1179
julia> bar(4)
1180
16
1181

1182
julia> bar("baz")
1183
"baz"
1184
```
1185
"""
1186
macro generated(f)
1187
    if isa(f, Expr) && (f.head === :function || is_short_function_def(f))
1188
        body = f.args[2]
1189
        lno = body.args[1]
1190
        return Expr(:escape,
1191
                    Expr(f.head, f.args[1],
1192
                         Expr(:block,
1193
                              lno,
1194
                              Expr(:if, Expr(:generated),
1195
                                   body,
1196
                                   Expr(:block,
1197
                                        Expr(:meta, :generated_only),
1198
                                        Expr(:return, nothing))))))
1199
    else
1200
        error("invalid syntax; @generated must be used with a function definition")
1201
    end
1202
end
1203

1204

1205
"""
1206
    @atomic var
1207
    @atomic order ex
1208

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

1212
    @atomic a.b.x = new
1213
    @atomic a.b.x += addend
1214
    @atomic :release a.b.x = new
1215
    @atomic :acquire_release a.b.x += addend
1216
    @atomic m[idx] = new
1217
    @atomic m[idx] += addend
1218
    @atomic :release m[idx] = new
1219
    @atomic :acquire_release m[idx] += addend
1220

1221
Perform the store operation expressed on the right atomically and return the
1222
new value.
1223

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

1228
With any modifying operator this operation translates to a
1229
`modifyproperty!(a.b, :x, op, addend)[2]` or, in case of reference, to a
1230
`modifyindex_atomic!(m, order, op, addend, idx...)[2]` call,
1231
with `order` defaulting to `:sequentially_consistent`.
1232

1233
    @atomic a.b.x max arg2
1234
    @atomic a.b.x + arg2
1235
    @atomic max(a.b.x, arg2)
1236
    @atomic :acquire_release max(a.b.x, arg2)
1237
    @atomic :acquire_release a.b.x + arg2
1238
    @atomic :acquire_release a.b.x max arg2
1239
    @atomic m[idx] max arg2
1240
    @atomic m[idx] + arg2
1241
    @atomic max(m[idx], arg2)
1242
    @atomic :acquire_release max(m[idx], arg2)
1243
    @atomic :acquire_release m[idx] + arg2
1244
    @atomic :acquire_release m[idx] max arg2
1245

1246
Perform the binary operation expressed on the right atomically. Store the
1247
result into the field or the reference in the first argument, and return the values
1248
`(old, new)`.
1249

1250
This operation translates to a `modifyproperty!(a.b, :x, func, arg2)` or,
1251
in case of reference to a `modifyindex_atomic!(m, order, func, arg2, idx)` call,
1252
with `order` defaulting to `:sequentially_consistent`.
1253

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

1256
# Examples
1257
```jldoctest
1258
julia> mutable struct Atomic{T}; @atomic x::T; end
1259

1260
julia> a = Atomic(1)
1261
Atomic{Int64}(1)
1262

1263
julia> @atomic a.x # fetch field x of a, with sequential consistency
1264
1
1265

1266
julia> @atomic :sequentially_consistent a.x = 2 # set field x of a, with sequential consistency
1267
2
1268

1269
julia> @atomic a.x += 1 # increment field x of a, with sequential consistency
1270
3
1271

1272
julia> @atomic a.x + 1 # increment field x of a, with sequential consistency
1273
3 => 4
1274

1275
julia> @atomic a.x # fetch field x of a, with sequential consistency
1276
4
1277

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

1281
julia> @atomic a.x max 5 # again change field x of a to the max value, with sequential consistency
1282
10 => 10
1283
```
1284

1285
```jldoctest
1286
julia> mem = AtomicMemory{Int}(undef, 2);
1287

1288
julia> @atomic mem[1] = 2 # set mem[1] to value 2 with sequential consistency
1289
2
1290

1291
julia> @atomic :monotonic mem[1] # fetch the first value of mem, with monotonic consistency
1292
2
1293

1294
julia> @atomic mem[1] += 1 # increment the first value of mem, with sequential consistency
1295
3
1296

1297
julia> @atomic mem[1] + 1 # increment the first value of mem, with sequential consistency
1298
3 => 4
1299

1300
julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1301
4
1302

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

1306
julia> @atomic mem[1] max 5 # again change the first value of mem to the max value, with sequential consistency
1307
10 => 10
1308
```
1309

1310
!!! compat "Julia 1.7"
1311
    Atomic fields functionality requires at least Julia 1.7.
1312

1313
!!! compat "Julia 1.12"
1314
    Atomic reference functionality requires at least Julia 1.12.
1315
"""
1316
macro atomic(ex)
1317
    if !isa(ex, Symbol) && !is_expr(ex, :(::))
1318
        return make_atomic(QuoteNode(:sequentially_consistent), ex)
1319
    end
1320
    return esc(Expr(:atomic, ex))
1321
end
1322
macro atomic(order, ex)
1323
    order isa QuoteNode || (order = esc(order))
1324
    return make_atomic(order, ex)
1325
end
1326
macro atomic(a1, op, a2)
1327
    return make_atomic(QuoteNode(:sequentially_consistent), a1, op, a2)
1328
end
1329
macro atomic(order, a1, op, a2)
1330
    order isa QuoteNode || (order = esc(order))
1331
    return make_atomic(order, a1, op, a2)
1332
end
1333
function make_atomic(order, ex)
×
1334
    @nospecialize
×
1335
    if ex isa Expr
×
1336
        if isexpr(ex, :., 2)
×
1337
            l, r = esc(ex.args[1]), esc(ex.args[2])
×
1338
            return :(getproperty($l, $r, $order))
×
1339
        elseif isexpr(ex, :call, 3)
×
1340
            return make_atomic(order, ex.args[2], ex.args[1], ex.args[3])
×
1341
        elseif isexpr(ex, :ref)
×
1342
            x, idcs = esc(ex.args[1]), map(esc, ex.args[2:end])
×
1343
            return :(getindex_atomic($x, $order, $(idcs...)))
×
1344
        elseif ex.head === :(=)
×
1345
            l, r = ex.args[1], esc(ex.args[2])
×
1346
            if is_expr(l, :., 2)
×
1347
                ll, lr = esc(l.args[1]), esc(l.args[2])
×
1348
                return :(setproperty!($ll, $lr, $r, $order))
×
1349
            elseif is_expr(l, :ref)
×
1350
                x, idcs = esc(l.args[1]), map(esc, l.args[2:end])
×
1351
                return :(setindex_atomic!($x, $order, $r, $(idcs...)))
×
1352
            end
1353
        end
1354
        if length(ex.args) == 2
×
1355
            if ex.head === :(+=)
×
1356
                op = :+
×
1357
            elseif ex.head === :(-=)
×
1358
                op = :-
×
1359
            elseif ex.head === :(|=)
×
1360
                op = :|
×
1361
            elseif ex.head === :(&=)
×
1362
                op = :&
×
1363
            elseif @isdefined string
×
1364
                shead = string(ex.head)
×
1365
                if endswith(shead, '=')
×
1366
                    op = Symbol(shead[1:prevind(shead, end)])
×
1367
                end
1368
            end
1369
            if @isdefined(op)
×
1370
                return Expr(:ref, make_atomic(order, ex.args[1], op, ex.args[2]), 2)
×
1371
            end
1372
        end
1373
    end
1374
    error("could not parse @atomic expression $ex")
×
1375
end
1376
function make_atomic(order, a1, op, a2)
×
1377
    @nospecialize
×
1378
    if is_expr(a1, :., 2)
×
1379
        a1l, a1r, op, a2 = esc(a1.args[1]), esc(a1.args[2]), esc(op), esc(a2)
×
1380
        return :(modifyproperty!($a1l, $a1r, $op, $a2, $order))
×
1381
    elseif is_expr(a1, :ref)
×
1382
        x, idcs, op, a2 = esc(a1.args[1]), map(esc, a1.args[2:end]), esc(op), esc(a2)
×
1383
        return :(modifyindex_atomic!($x, $order, $op, $a2, $(idcs...)))
×
1384
    end
1385
    error("@atomic modify expression missing field access or indexing")
×
1386
end
1387

1388

1389
"""
1390
    @atomicswap a.b.x = new
1391
    @atomicswap :sequentially_consistent a.b.x = new
1392
    @atomicswap m[idx] = new
1393
    @atomicswap :sequentially_consistent m[idx] = new
1394

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

1398
This operation translates to a `swapproperty!(a.b, :x, new)` or,
1399
in case of reference, `swapindex_atomic!(mem, order, new, idx)` call,
1400
with `order` defaulting to `:sequentially_consistent`.
1401

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

1404
# Examples
1405
```jldoctest
1406
julia> mutable struct Atomic{T}; @atomic x::T; end
1407

1408
julia> a = Atomic(1)
1409
Atomic{Int64}(1)
1410

1411
julia> @atomicswap a.x = 2+2 # replace field x of a with 4, with sequential consistency
1412
1
1413

1414
julia> @atomic a.x # fetch field x of a, with sequential consistency
1415
4
1416
```
1417

1418
```jldoctest
1419
julia> mem = AtomicMemory{Int}(undef, 2);
1420

1421
julia> @atomic mem[1] = 1;
1422

1423
julia> @atomicswap mem[1] = 4 # replace the first value of `mem` with 4, with sequential consistency
1424
1
1425

1426
julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1427
4
1428
```
1429

1430
!!! compat "Julia 1.7"
1431
    Atomic fields functionality requires at least Julia 1.7.
1432

1433
!!! compat "Julia 1.12"
1434
    Atomic reference functionality requires at least Julia 1.12.
1435
"""
1436
macro atomicswap(order, ex)
1437
    order isa QuoteNode || (order = esc(order))
1438
    return make_atomicswap(order, ex)
1439
end
1440
macro atomicswap(ex)
1441
    return make_atomicswap(QuoteNode(:sequentially_consistent), ex)
1442
end
1443
function make_atomicswap(order, ex)
×
1444
    @nospecialize
×
1445
    is_expr(ex, :(=), 2) || error("@atomicswap expression missing assignment")
×
1446
    l, val = ex.args[1], esc(ex.args[2])
×
1447
    if is_expr(l, :., 2)
×
1448
        ll, lr = esc(l.args[1]), esc(l.args[2])
×
1449
        return :(swapproperty!($ll, $lr, $val, $order))
×
1450
    elseif is_expr(l, :ref)
×
1451
        x, idcs = esc(l.args[1]), map(esc, l.args[2:end])
×
1452
        return :(swapindex_atomic!($x, $order, $val, $(idcs...)))
×
1453
    end
1454
    error("@atomicswap expression missing field access or indexing")
×
1455
end
1456

1457

1458
"""
1459
    @atomicreplace a.b.x expected => desired
1460
    @atomicreplace :sequentially_consistent a.b.x expected => desired
1461
    @atomicreplace :sequentially_consistent :monotonic a.b.x expected => desired
1462
    @atomicreplace m[idx] expected => desired
1463
    @atomicreplace :sequentially_consistent m[idx] expected => desired
1464
    @atomicreplace :sequentially_consistent :monotonic m[idx] expected => desired
1465

1466
Perform the conditional replacement expressed by the pair atomically, returning
1467
the values `(old, success::Bool)`. Where `success` indicates whether the
1468
replacement was completed.
1469

1470
This operation translates to a `replaceproperty!(a.b, :x, expected, desired)` or,
1471
in case of reference, to a
1472
`replaceindex_atomic!(mem, success_order, fail_order, expected, desired, idx)` call,
1473
with both orders defaulting to `:sequentially_consistent`.
1474

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

1477
# Examples
1478
```jldoctest
1479
julia> mutable struct Atomic{T}; @atomic x::T; end
1480

1481
julia> a = Atomic(1)
1482
Atomic{Int64}(1)
1483

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

1487
julia> @atomic a.x # fetch field x of a, with sequential consistency
1488
2
1489

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

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

1495
julia> @atomicreplace a.x xchg
1496
(old = 2, success = true)
1497

1498
julia> @atomic a.x # fetch field x of a, with sequential consistency
1499
0
1500
```
1501

1502
```jldoctest
1503
julia> mem = AtomicMemory{Int}(undef, 2);
1504

1505
julia> @atomic mem[1] = 1;
1506

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

1510
julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1511
2
1512

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

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

1518
julia> @atomicreplace mem[1] xchg
1519
(old = 2, success = true)
1520

1521
julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1522
0
1523
```
1524

1525
!!! compat "Julia 1.7"
1526
    Atomic fields functionality requires at least Julia 1.7.
1527

1528
!!! compat "Julia 1.12"
1529
    Atomic reference functionality requires at least Julia 1.12.
1530
"""
1531
macro atomicreplace(success_order, fail_order, ex, old_new)
1532
    fail_order isa QuoteNode || (fail_order = esc(fail_order))
1533
    success_order isa QuoteNode || (success_order = esc(success_order))
1534
    return make_atomicreplace(success_order, fail_order, ex, old_new)
1535
end
1536
macro atomicreplace(order, ex, old_new)
1537
    order isa QuoteNode || (order = esc(order))
1538
    return make_atomicreplace(order, order, ex, old_new)
1539
end
1540
macro atomicreplace(ex, old_new)
1541
    return make_atomicreplace(QuoteNode(:sequentially_consistent), QuoteNode(:sequentially_consistent), ex, old_new)
1542
end
1543
function make_atomicreplace(success_order, fail_order, ex, old_new)
×
1544
    @nospecialize
×
1545
    if is_expr(ex, :., 2)
×
1546
        ll, lr = esc(ex.args[1]), esc(ex.args[2])
×
1547
        if is_expr(old_new, :call, 3) && old_new.args[1] === :(=>)
×
1548
            exp, rep = esc(old_new.args[2]), esc(old_new.args[3])
×
1549
            return :(replaceproperty!($ll, $lr, $exp, $rep, $success_order, $fail_order))
×
1550
        else
1551
            old_new = esc(old_new)
×
1552
            return :(replaceproperty!($ll, $lr, $old_new::Pair..., $success_order, $fail_order))
×
1553
        end
1554
    elseif is_expr(ex, :ref)
×
1555
        x, idcs = esc(ex.args[1]), map(esc, ex.args[2:end])
×
1556
        if is_expr(old_new, :call, 3) && old_new.args[1] === :(=>)
×
1557
            exp, rep = esc(old_new.args[2]), esc(old_new.args[3])
×
1558
            return :(replaceindex_atomic!($x, $success_order, $fail_order, $exp, $rep, $(idcs...)))
×
1559
        else
1560
            old_new = esc(old_new)
×
1561
            return :(replaceindex_atomic!($x, $success_order, $fail_order, $old_new::Pair..., $(idcs...)))
×
1562
        end
1563
    end
1564
    error("@atomicreplace expression missing field access or indexing")
×
1565
end
1566

1567
"""
1568
    @atomiconce a.b.x = value
1569
    @atomiconce :sequentially_consistent a.b.x = value
1570
    @atomiconce :sequentially_consistent :monotonic a.b.x = value
1571
    @atomiconce m[idx] = value
1572
    @atomiconce :sequentially_consistent m[idx] = value
1573
    @atomiconce :sequentially_consistent :monotonic m[idx] = value
1574

1575
Perform the conditional assignment of value atomically if it was previously
1576
unset. Returned value `success::Bool` indicates whether the assignment was completed.
1577

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

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

1584
# Examples
1585
```jldoctest
1586
julia> mutable struct AtomicOnce
1587
           @atomic x
1588
           AtomicOnce() = new()
1589
       end
1590

1591
julia> a = AtomicOnce()
1592
AtomicOnce(#undef)
1593

1594
julia> @atomiconce a.x = 1 # set field x of a to 1, if unset, with sequential consistency
1595
true
1596

1597
julia> @atomic a.x # fetch field x of a, with sequential consistency
1598
1
1599

1600
julia> @atomiconce :monotonic a.x = 2 # set field x of a to 1, if unset, with monotonic consistence
1601
false
1602
```
1603

1604
```jldoctest
1605
julia> mem = AtomicMemory{Vector{Int}}(undef, 1);
1606

1607
julia> isassigned(mem, 1)
1608
false
1609

1610
julia> @atomiconce mem[1] = [1] # set the first value of mem to [1], if unset, with sequential consistency
1611
true
1612

1613
julia> isassigned(mem, 1)
1614
true
1615

1616
julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1617
1-element Vector{Int64}:
1618
 1
1619

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

1623
julia> @atomic mem[1]
1624
1-element Vector{Int64}:
1625
 1
1626
```
1627

1628
!!! compat "Julia 1.11"
1629
    Atomic fields functionality requires at least Julia 1.11.
1630

1631
!!! compat "Julia 1.12"
1632
    Atomic reference functionality requires at least Julia 1.12.
1633
"""
1634
macro atomiconce(success_order, fail_order, ex)
1635
    fail_order isa QuoteNode || (fail_order = esc(fail_order))
1636
    success_order isa QuoteNode || (success_order = esc(success_order))
1637
    return make_atomiconce(success_order, fail_order, ex)
1638
end
1639
macro atomiconce(order, ex)
1640
    order isa QuoteNode || (order = esc(order))
1641
    return make_atomiconce(order, order, ex)
1642
end
1643
macro atomiconce(ex)
1644
    return make_atomiconce(QuoteNode(:sequentially_consistent), QuoteNode(:sequentially_consistent), ex)
1645
end
1646
function make_atomiconce(success_order, fail_order, ex)
×
1647
    @nospecialize
×
1648
    is_expr(ex, :(=), 2) || error("@atomiconce expression missing assignment")
×
1649
    l, val = ex.args[1], esc(ex.args[2])
×
1650
    if is_expr(l, :., 2)
×
1651
        ll, lr = esc(l.args[1]), esc(l.args[2])
×
1652
        return :(setpropertyonce!($ll, $lr, $val, $success_order, $fail_order))
×
1653
    elseif is_expr(l, :ref)
×
1654
        x, idcs = esc(l.args[1]), map(esc, l.args[2:end])
×
1655
        return :(setindexonce_atomic!($x, $success_order, $fail_order, $val, $(idcs...)))
×
1656
    end
1657
    error("@atomiconce expression missing field access or indexing")
×
1658
end
1659

1660
# Meta expression head, these generally can't be deleted even when they are
1661
# in a dead branch but can be ignored when analyzing uses/liveness.
1662
is_meta_expr_head(head::Symbol) = head === :boundscheck || head === :meta || head === :loopinfo
5,891,405✔
1663
is_meta_expr(@nospecialize x) = isa(x, Expr) && is_meta_expr_head(x.head)
1,072,438✔
1664

1665
function is_self_quoting(@nospecialize(x))
17,172✔
1666
    return isa(x,Number) || isa(x,AbstractString) || isa(x,Tuple) || isa(x,Type) ||
295,920✔
1667
        isa(x,Char) || x === nothing || isa(x,Function)
1668
end
1669

1670
function quoted(@nospecialize(x))
17,172✔
1671
    return is_self_quoting(x) ? x : QuoteNode(x)
213,007✔
1672
end
1673

1674
# Implementation of generated functions
1675
function generated_body_to_codeinfo(ex::Expr, defmod::Module, isva::Bool)
74✔
1676
    ci = ccall(:jl_fl_lower, Any, (Any, Any, Ptr{UInt8}, Csize_t, Csize_t, Cint),
74✔
1677
               ex, defmod, "none", 0, typemax(Csize_t), 0)[1]
1678
    if !isa(ci, CodeInfo)
74✔
1679
        if isa(ci, Expr) && ci.head === :error
×
1680
            msg = ci.args[1]
×
1681
            error(msg isa String ? strcat("syntax: ", msg) : msg)
×
1682
        end
1683
        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.")
×
1684
    end
1685
    ci.isva = isva
74✔
1686
    code = ci.code
74✔
1687
    bindings = IdSet{Core.Binding}()
74✔
1688
    for i = 1:length(code)
74✔
1689
        stmt = code[i]
1,166✔
1690
        if isa(stmt, GlobalRef)
1,166✔
1691
            push!(bindings, convert(Core.Binding, stmt))
340✔
1692
        end
1693
    end
2,258✔
1694
    if !isempty(bindings)
74✔
1695
        ci.edges = Core.svec(bindings...)
74✔
1696
    end
1697
    return ci
74✔
1698
end
1699

1700
# invoke and wrap the results of @generated expression
1701
function (g::Core.GeneratedFunctionStub)(world::UInt, source::Method, @nospecialize args...)
74✔
1702
    # args is (spvals..., argtypes...)
1703
    body = g.gen(args...)
74✔
1704
    file = source.file
74✔
1705
    file isa Symbol || (file = :none)
74✔
1706
    lam = Expr(:lambda, Expr(:argnames, g.argnames...).args,
74✔
1707
               Expr(:var"scope-block",
1708
                    Expr(:block,
1709
                         LineNumberNode(Int(source.line), source.file),
1710
                         Expr(:meta, :push_loc, file, :var"@generated body"),
1711
                         Expr(:return, body),
1712
                         Expr(:meta, :pop_loc))))
1713
    spnames = g.spnames
74✔
1714
    return generated_body_to_codeinfo(spnames === Core.svec() ? lam : Expr(Symbol("with-static-parameters"), lam, spnames...),
74✔
1715
        source.module,
1716
        source.isva)
1717
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