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

JuliaLang / julia / #38000

03 Feb 2025 05:43AM UTC coverage: 17.325% (+0.1%) from 17.218%
#38000

push

local

web-flow
Define publicity at a per-binding level, not per-symbol (#57094)

Symbols don't belong to modules and can't be marked public. The
statement `public Fix` in Base does not make the symbol `:Fix` public
everywhere. My package's `Package.Fix` may still be private. It's
bindings that are marked this way:


https://github.com/JuliaLang/julia/blob/fa9478b51/src/julia.h#L695-L705

7930 of 45772 relevant lines covered (17.33%)

98264.06 hits per line

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

7.4
/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)
5✔
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
64✔
39
isexpr(@nospecialize(ex), head::Symbol, n::Int) = isa(ex, Expr) && ex.head === head && length(ex.args) == n
49✔
40

41
copy(e::Expr) = exprarray(e.head, copy_exprargs(e.args))
×
42
function copy(x::PhiNode)
×
43
    values = x.values
×
44
    nvalues = length(values)
×
45
    new_values = Vector{Any}(undef, nvalues)
×
46
    @inbounds for i = 1:nvalues
×
47
        isassigned(values, i) || continue
×
48
        new_values[i] = copy_exprs(values[i])
×
49
    end
×
50
    return PhiNode(copy(x.edges), new_values)
×
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))
×
65
    if isa(x, Expr)
×
66
        return copy(x)
×
67
    elseif isa(x, PhiNode)
×
68
        return copy(x)
×
69
    elseif isa(x, PhiCNode)
×
70
        return copy(x)
×
71
    end
72
    return x
×
73
end
74
copy_exprargs(x::Array{Any,1}) = Any[copy_exprs(@inbounds x[i]) for i in eachindex(x)]
×
75

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

78
# create copies of the CodeInfo definition, and any mutable fields
79
function copy(c::CodeInfo)
×
80
    cnew = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), c)
×
81
    cnew.code = copy_exprargs(cnew.code)
×
82
    cnew.slotnames = copy(cnew.slotnames)
×
83
    cnew.slotflags = copy(cnew.slotflags)
×
84
    if cnew.slottypes !== nothing
×
85
        cnew.slottypes = copy(cnew.slottypes::Vector{Any})
×
86
    end
87
    cnew.ssaflags  = copy(cnew.ssaflags)
×
88
    cnew.edges     = cnew.edges === nothing || cnew.edges isa Core.SimpleVector ? cnew.edges : copy(cnew.edges::Vector)
×
89
    ssavaluetypes  = cnew.ssavaluetypes
×
90
    ssavaluetypes isa Vector{Any} && (cnew.ssavaluetypes = copy(ssavaluetypes))
×
91
    return cnew
×
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
```julia-repl
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[16]:6 =# M.@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
    When there are nested callsite annotations, the innermost annotation has the precedence:
261
    ```julia
262
    @noinline let a0, b0 = ...
263
        a = @inline f(a0)  # the compiler will try to inline this call
264
        b = f(b0)          # the compiler will NOT try to inline this call
265
        return a, b
266
    end
267
    ```
268

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

274
!!! compat "Julia 1.8"
275
    The callsite annotation requires at least Julia 1.8.
276
"""
277
macro inline(x)
278
    return annotate_meta_def_or_block(x, :inline)
279
end
280

281
"""
282
    @noinline
283

284
Give a hint to the compiler that it should not inline a function.
285

286
Small functions are typically inlined automatically.
287
By using `@noinline` on small functions, auto-inlining can be
288
prevented.
289

290
`@noinline` can be applied immediately before a function definition or within a function body.
291

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

298
# annotate short-form definition
299
@noinline shortdef(x) = ...
300

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

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

311
---
312
    @noinline block
313

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

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

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

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

332
    let
333
        @noinline explicit_inline(args...) # will not be inlined
334
    end
335
    ```
336

337
!!! note
338
    When there are nested callsite annotations, the innermost annotation has the precedence:
339
    ```julia
340
    @inline let a0, b0 = ...
341
        a = @noinline f(a0)  # the compiler will NOT try to inline this call
342
        b = f(b0)            # the compiler will try to inline this call
343
        return a, b
344
    end
345
    ```
346

347
!!! compat "Julia 1.8"
348
    The callsite annotation requires at least Julia 1.8.
349

350
---
351
!!! note
352
    If the function is trivial (for example returning a constant) it might get inlined anyway.
353
"""
354
macro noinline(x)
355
    return annotate_meta_def_or_block(x, :noinline)
356
end
357

358
"""
359
    Base.@constprop setting [ex]
360

361
Control the mode of interprocedural constant propagation for the annotated function.
362

363
Two `setting`s are supported:
364

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

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

374
```julia
375
# annotate long-form definition
376
Base.@constprop :aggressive function longdef(x)
377
    ...
378
end
379

380
# annotate short-form definition
381
Base.@constprop :aggressive shortdef(x) = ...
382

383
# annotate anonymous function that a `do` block creates
384
f() do
385
    Base.@constprop :aggressive
386
    ...
387
end
388
```
389

390
!!! compat "Julia 1.10"
391
    The usage within a function body requires at least Julia 1.10.
392
"""
393
macro constprop(setting, ex)
394
    sym = constprop_setting(setting)
395
    isa(ex, Expr) && return esc(pushmeta!(ex, sym))
396
    throw(ArgumentError(LazyString("Bad expression `", ex, "` in `@constprop settings ex`")))
397
end
398
macro constprop(setting)
399
    sym = constprop_setting(setting)
400
    return Expr(:meta, sym)
401
end
402

403
function constprop_setting(@nospecialize setting)
×
404
    s = setting
×
405
    isa(setting, QuoteNode) && (setting = setting.value)
×
406
    if setting === :aggressive
×
407
        return :aggressive_constprop
×
408
    elseif setting === :none
×
409
        return :no_constprop
×
410
    end
411
    throw(ArgumentError(LazyString("`Base.@constprop ", s, "` not supported")))
×
412
end
413

414
"""
415
    Base.@assume_effects setting... [ex]
416

417
Override the compiler's effect modeling.
418
This macro can be used in several contexts:
419
1. Immediately before a method definition, to override the entire effect modeling of the applied method.
420
2. Within a function body without any arguments, to override the entire effect modeling of the enclosing method.
421
3. Applied to a code block, to override the local effect modeling of the applied code block.
422

423
# Examples
424
```jldoctest
425
julia> Base.@assume_effects :terminates_locally function fact(x)
426
           # usage 1:
427
           # this :terminates_locally allows `fact` to be constant-folded
428
           res = 1
429
           0 ≤ x < 20 || error("bad fact")
430
           while x > 1
431
               res *= x
432
               x -= 1
433
           end
434
           return res
435
       end
436
fact (generic function with 1 method)
437

438
julia> code_typed() do
439
           fact(12)
440
       end |> only
441
CodeInfo(
442
1 ─     return 479001600
443
) => Int64
444

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

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

483
!!! compat "Julia 1.8"
484
    Using `Base.@assume_effects` requires Julia version 1.8.
485

486
!!! compat "Julia 1.10"
487
    The usage within a function body requires at least Julia 1.10.
488

489
!!! compat "Julia 1.11"
490
    The code block annotation requires at least Julia 1.11.
491

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

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

505
The following `setting`s are supported.
506
- `:consistent`
507
- `:effect_free`
508
- `:nothrow`
509
- `:terminates_globally`
510
- `:terminates_locally`
511
- `:notaskstate`
512
- `:inaccessiblememonly`
513
- `:noub`
514
- `:noub_if_noinbounds`
515
- `:nortcall`
516
- `:foldable`
517
- `:removable`
518
- `:total`
519

520
# Extended help
521

522
---
523
## `:consistent`
524

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

529
!!! note
530
    This in particular implies that the method must not return a freshly allocated
531
    mutable object. Multiple allocations of mutable objects (even with identical
532
    contents) are not egal.
533

534
!!! note
535
    The `:consistent`-cy assertion is made world-age wise. More formally, write
536
    ``fáµ¢`` for the evaluation of ``f`` in world-age ``i``, then this setting requires:
537
    ```math
538
    ∀ i, x, y: x ≡ y → fᵢ(x) ≡ fᵢ(y)
539
    ```
540
    However, for two world ages ``i``, ``j`` s.t. ``i ≠ j``, we may have ``fᵢ(x) ≢ fⱼ(y)``.
541

542
    A further implication is that `:consistent` functions may not make their
543
    return value dependent on the state of the heap or any other global state
544
    that is not constant for a given world age.
545

546
!!! note
547
    The `:consistent`-cy includes all legal rewrites performed by the optimizer.
548
    For example, floating-point fastmath operations are not considered `:consistent`,
549
    because the optimizer may rewrite them causing the output to not be `:consistent`,
550
    even for the same world age (e.g. because one ran in the interpreter, while
551
    the other was optimized).
552

553
!!! note
554
    If `:consistent` functions terminate by throwing an exception, that exception
555
    itself is not required to meet the egality requirement specified above.
556

557
---
558
## `:effect_free`
559

560
The `:effect_free` setting asserts that the method is free of externally semantically
561
visible side effects. The following is an incomplete list of externally semantically
562
visible side effects:
563
- Changing the value of a global variable.
564
- Mutating the heap (e.g. an array or mutable value), except as noted below
565
- Changing the method table (e.g. through calls to eval)
566
- File/Network/etc. I/O
567
- Task switching
568

569
However, the following are explicitly not semantically visible, even if they
570
may be observable:
571
- Memory allocations (both mutable and immutable)
572
- Elapsed time
573
- Garbage collection
574
- Heap mutations of objects whose lifetime does not exceed the method (i.e.
575
  were allocated in the method and do not escape).
576
- The returned value (which is externally visible, but not a side effect)
577

578
The rule of thumb here is that an externally visible side effect is anything
579
that would affect the execution of the remainder of the program if the function
580
were not executed.
581

582
!!! note
583
    The `:effect_free` assertion is made both for the method itself and any code
584
    that is executed by the method. Keep in mind that the assertion must be
585
    valid for all world ages and limit use of this assertion accordingly.
586

587
---
588
## `:nothrow`
589

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

593
!!! note
594
    It is permissible for `:nothrow` annotated methods to make use of exception
595
    handling internally as long as the exception is not rethrown out of the
596
    method itself.
597

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

605
---
606
## `:terminates_globally`
607

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

611
!!! note
612
    This `:terminates_globally` assertion covers any other methods called by the annotated method.
613

614
!!! note
615
    The compiler will consider this a strong indication that the method will
616
    terminate relatively *quickly* and may (if otherwise legal) call this
617
    method at compile time. I.e. it is a bad idea to annotate this setting
618
    on a method that *technically*, but not *practically*, terminates.
619

620
---
621
## `:terminates_locally`
622

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

628
!!! note
629
    `:terminates_globally` implies `:terminates_locally`.
630

631
---
632
## `:notaskstate`
633

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

638
!!! note
639
    The implementation of exception handling makes use of state stored in the
640
    task object. However, this state is currently not considered to be within
641
    the scope of `:notaskstate` and is tracked separately using the `:nothrow`
642
    effect.
643

644
!!! note
645
    The `:notaskstate` assertion concerns the state of the *currently running task*.
646
    If a reference to a `Task` object is obtained by some other means that
647
    does not consider which task is *currently* running, the `:notaskstate`
648
    effect need not be tainted. This is true, even if said task object happens
649
    to be `===` to the currently running task.
650

651
!!! note
652
    Access to task state usually also results in the tainting of other effects,
653
    such as `:effect_free` (if task state is modified) or `:consistent` (if
654
    task state is used in the computation of the result). In particular,
655
    code that is not `:notaskstate`, but is `:effect_free` and `:consistent`
656
    may still be dead-code-eliminated and thus promoted to `:total`.
657

658
---
659
## `:inaccessiblememonly`
660

661
The `:inaccessiblememonly` setting asserts that the method does not access or modify
662
externally accessible mutable memory. This means the method can access or modify mutable
663
memory for newly allocated objects that is not accessible by other methods or top-level
664
execution before return from the method, but it can not access or modify any mutable
665
global state or mutable memory pointed to by its arguments.
666

667
!!! note
668
    Below is an incomplete list of examples that invalidate this assumption:
669
    - a global reference or `getglobal` call to access a mutable global variable
670
    - a global assignment or `setglobal!` call to perform assignment to a non-constant global variable
671
    - `setfield!` call that changes a field of a global mutable variable
672

673
!!! note
674
    This `:inaccessiblememonly` assertion covers any other methods called by the annotated method.
675

676
---
677
## `:noub`
678

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

684
---
685
## `:nortcall`
686

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

690
!!! note
691
    To be precise, this assertion can be used when a call to `Core.Compiler.return_type` is
692
    not made at runtime; that is, when the result of `Core.Compiler.return_type` is known
693
    exactly at compile time and the call is eliminated by the optimizer. However, since
694
    whether the result of `Core.Compiler.return_type` is folded at compile time depends
695
    heavily on the compiler's implementation, it is generally risky to assert this if
696
    the method in question uses `Core.Compiler.return_type` in any form.
697

698
---
699
## `:foldable`
700

701
This setting is a convenient shortcut for the set of effects that the compiler
702
requires to be guaranteed to constant fold a call at compile time. It is
703
currently equivalent to the following `setting`s:
704
- `:consistent`
705
- `:effect_free`
706
- `:terminates_globally`
707
- `:noub`
708
- `:nortcall`
709

710
!!! note
711
    This list in particular does not include `:nothrow`. The compiler will still
712
    attempt constant propagation and note any thrown error at compile time. Note
713
    however, that by the `:consistent`-cy requirements, any such annotated call
714
    must consistently throw given the same argument values.
715

716
!!! note
717
    An explicit `@inbounds` annotation inside the function will also disable
718
    constant folding and not be overridden by `:foldable`.
719

720
---
721
## `:removable`
722

723
This setting is a convenient shortcut for the set of effects that the compiler
724
requires to be guaranteed to delete a call whose result is unused at compile time.
725
It is currently equivalent to the following `setting`s:
726
- `:effect_free`
727
- `:nothrow`
728
- `:terminates_globally`
729

730
---
731
## `:total`
732

733
This `setting` is the maximum possible set of effects. It currently implies
734
the following other `setting`s:
735
- `:consistent`
736
- `:effect_free`
737
- `:nothrow`
738
- `:terminates_globally`
739
- `:notaskstate`
740
- `:inaccessiblememonly`
741
- `:noub`
742
- `:nortcall`
743

744
!!! warning
745
    `:total` is a very strong assertion and will likely gain additional semantics
746
    in future versions of Julia (e.g. if additional effects are added and included
747
    in the definition of `:total`). As a result, it should be used with care.
748
    Whenever possible, prefer to use the minimum possible set of specific effect
749
    assertions required for a particular application. In cases where a large
750
    number of effect overrides apply to a set of functions, a custom macro is
751
    recommended over the use of `:total`.
752

753
---
754
## Negated effects
755

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

784
function compute_assumed_settings(settings)
×
785
    override = EffectsOverride()
×
786
    for setting in settings
×
787
        override = compute_assumed_setting(override, setting)
×
788
        override === nothing &&
×
789
            throw(ArgumentError("`@assume_effects $setting` not supported"))
790
    end
×
791
    return override
×
792
end
793

794
struct EffectsOverride
795
    consistent::Bool
796
    effect_free::Bool
797
    nothrow::Bool
798
    terminates_globally::Bool
799
    terminates_locally::Bool
800
    notaskstate::Bool
801
    inaccessiblememonly::Bool
802
    noub::Bool
803
    noub_if_noinbounds::Bool
804
    consistent_overlay::Bool
805
    nortcall::Bool
806
end
807

808
function EffectsOverride(
×
809
    override::EffectsOverride =
810
        EffectsOverride(false, false, false, false, false, false, false, false, false, false, false);
811
    consistent::Bool = override.consistent,
812
    effect_free::Bool = override.effect_free,
813
    nothrow::Bool = override.nothrow,
814
    terminates_globally::Bool = override.terminates_globally,
815
    terminates_locally::Bool = override.terminates_locally,
816
    notaskstate::Bool = override.notaskstate,
817
    inaccessiblememonly::Bool = override.inaccessiblememonly,
818
    noub::Bool = override.noub,
819
    noub_if_noinbounds::Bool = override.noub_if_noinbounds,
820
    consistent_overlay::Bool = override.consistent_overlay,
821
    nortcall::Bool = override.nortcall)
822
    return EffectsOverride(
×
823
        consistent,
824
        effect_free,
825
        nothrow,
826
        terminates_globally,
827
        terminates_locally,
828
        notaskstate,
829
        inaccessiblememonly,
830
        noub,
831
        noub_if_noinbounds,
832
        consistent_overlay,
833
        nortcall)
834
end
835

836
const NUM_EFFECTS_OVERRIDES = 11 # sync with julia.h
837

838
function compute_assumed_setting(override::EffectsOverride, @nospecialize(setting), val::Bool=true)
×
839
    if isexpr(setting, :call) && setting.args[1] === :(!)
×
840
        return compute_assumed_setting(override, setting.args[2], !val)
×
841
    elseif isa(setting, QuoteNode)
×
842
        return compute_assumed_setting(override, setting.value, val)
×
843
    end
844
    if setting === :consistent
×
845
        return EffectsOverride(override; consistent = val)
×
846
    elseif setting === :effect_free
×
847
        return EffectsOverride(override; effect_free = val)
×
848
    elseif setting === :nothrow
×
849
        return EffectsOverride(override; nothrow = val)
×
850
    elseif setting === :terminates_globally
×
851
        return EffectsOverride(override; terminates_globally = val)
×
852
    elseif setting === :terminates_locally
×
853
        return EffectsOverride(override; terminates_locally = val)
×
854
    elseif setting === :notaskstate
×
855
        return EffectsOverride(override; notaskstate = val)
×
856
    elseif setting === :inaccessiblememonly
×
857
        return EffectsOverride(override; inaccessiblememonly = val)
×
858
    elseif setting === :noub
×
859
        return EffectsOverride(override; noub = val)
×
860
    elseif setting === :noub_if_noinbounds
×
861
        return EffectsOverride(override; noub_if_noinbounds = val)
×
862
    elseif setting === :foldable
×
863
        consistent = effect_free = terminates_globally = noub = nortcall = val
×
864
        return EffectsOverride(override; consistent, effect_free, terminates_globally, noub, nortcall)
×
865
    elseif setting === :removable
×
866
        effect_free = nothrow = terminates_globally = val
×
867
        return EffectsOverride(override; effect_free, nothrow, terminates_globally)
×
868
    elseif setting === :total
×
869
        consistent = effect_free = nothrow = terminates_globally = notaskstate =
×
870
            inaccessiblememonly = noub = nortcall = val
871
        return EffectsOverride(override;
×
872
            consistent, effect_free, nothrow, terminates_globally, notaskstate,
873
            inaccessiblememonly, noub, nortcall)
874
    end
875
    return nothing
×
876
end
877

878
function encode_effects_override(eo::EffectsOverride)
×
879
    e = 0x0000
×
880
    eo.consistent          && (e |= (0x0001 << 0))
×
881
    eo.effect_free         && (e |= (0x0001 << 1))
×
882
    eo.nothrow             && (e |= (0x0001 << 2))
×
883
    eo.terminates_globally && (e |= (0x0001 << 3))
×
884
    eo.terminates_locally  && (e |= (0x0001 << 4))
×
885
    eo.notaskstate         && (e |= (0x0001 << 5))
×
886
    eo.inaccessiblememonly && (e |= (0x0001 << 6))
×
887
    eo.noub                && (e |= (0x0001 << 7))
×
888
    eo.noub_if_noinbounds  && (e |= (0x0001 << 8))
×
889
    eo.consistent_overlay  && (e |= (0x0001 << 9))
×
890
    eo.nortcall            && (e |= (0x0001 << 10))
×
891
    return e
×
892
end
893

894
function decode_effects_override(e::UInt16)
×
895
    return EffectsOverride(
×
896
        !iszero(e & (0x0001 << 0)),
897
        !iszero(e & (0x0001 << 1)),
898
        !iszero(e & (0x0001 << 2)),
899
        !iszero(e & (0x0001 << 3)),
900
        !iszero(e & (0x0001 << 4)),
901
        !iszero(e & (0x0001 << 5)),
902
        !iszero(e & (0x0001 << 6)),
903
        !iszero(e & (0x0001 << 7)),
904
        !iszero(e & (0x0001 << 8)),
905
        !iszero(e & (0x0001 << 9)),
906
        !iszero(e & (0x0001 << 10)))
907
end
908

909
function form_purity_expr(override::EffectsOverride)
×
910
    ex = Expr(:purity)
×
911
    for i = 1:NUM_EFFECTS_OVERRIDES
×
912
        push!(ex.args, getfield(override, i))
×
913
    end
×
914
    return ex
×
915
end
916

917
"""
918
    Base.@nospecializeinfer function f(args...)
919
        @nospecialize ...
920
        ...
921
    end
922
    Base.@nospecializeinfer f(@nospecialize args...) = ...
923

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

927
# Examples
928

929
```julia
930
julia> f(A::AbstractArray) = g(A)
931
f (generic function with 1 method)
932

933
julia> @noinline Base.@nospecializeinfer g(@nospecialize(A::AbstractArray)) = A[1]
934
g (generic function with 1 method)
935

936
julia> @code_typed f([1.0])
937
CodeInfo(
938
1 ─ %1 = invoke Main.g(_2::AbstractArray)::Any
939
└──      return %1
940
) => Any
941
```
942

943
In this example, `f` will be inferred for each specific type of `A`,
944
but `g` will only be inferred once with the declared argument type `A::AbstractArray`,
945
meaning that the compiler will not likely see the excessive inference time on it
946
while it can not infer the concrete return type of it.
947
Without the `@nospecializeinfer`, `f([1.0])` would infer the return type of `g` as `Float64`,
948
indicating that inference ran for `g(::Vector{Float64})` despite the prohibition on
949
specialized code generation.
950

951
!!! compat "Julia 1.10"
952
    Using `Base.@nospecializeinfer` requires Julia version 1.10.
953
"""
954
macro nospecializeinfer(ex)
955
    esc(isa(ex, Expr) ? pushmeta!(ex, :nospecializeinfer) : ex)
956
end
957

958
"""
959
    @propagate_inbounds
960

961
Tells the compiler to inline a function while retaining the caller's inbounds context.
962
"""
963
macro propagate_inbounds(ex)
964
    if isa(ex, Expr)
965
        pushmeta!(ex, :inline)
966
        pushmeta!(ex, :propagate_inbounds)
967
    end
968
    esc(ex)
969
end
970

971
"""
972
    @polly
973

974
Tells the compiler to apply the polyhedral optimizer Polly to a function.
975
"""
976
macro polly(ex)
977
    esc(isa(ex, Expr) ? pushmeta!(ex, :polly) : ex)
978
end
979

980
## some macro utilities ##
981

982
unwrap_macrocalls(@nospecialize(x)) = x
×
983
function unwrap_macrocalls(ex::Expr)
×
984
    inner = ex
×
985
    while isexpr(inner, :macrocall)
×
986
        inner = inner.args[end]
×
987
    end
×
988
    return inner
×
989
end
990

991
function pushmeta!(ex::Expr, tag::Union{Symbol,Expr})
×
992
    inner = unwrap_macrocalls(ex)
×
993
    idx, exargs = findmeta(inner)
×
994
    if idx != 0
×
995
        metastmt = exargs[idx]::Expr
×
996
        push!(metastmt.args, tag)
×
997
    else
998
        body = inner.args[2]::Expr
×
999
        pushfirst!(body.args, Expr(:meta, tag))
×
1000
    end
1001
    return ex
×
1002
end
1003

1004
popmeta!(body, sym) = _getmeta(body, sym, true)
×
1005
peekmeta(body, sym) = _getmeta(body, sym, false)
×
1006

1007
function _getmeta(body::Expr, sym::Symbol, delete::Bool)
×
1008
    body.head === :block || return false, []
×
1009
    _getmeta(body.args, sym, delete)
×
1010
end
1011
_getmeta(arg, sym, delete::Bool) = (false, [])
×
1012
function _getmeta(body::Array{Any,1}, sym::Symbol, delete::Bool)
×
1013
    idx, blockargs = findmeta_block(body, args -> findmetaarg(args,sym)!=0)
×
1014
    if idx == 0
×
1015
        return false, []
×
1016
    end
1017
    metaargs = blockargs[idx].args
×
1018
    i = findmetaarg(blockargs[idx].args, sym)
×
1019
    if i == 0
×
1020
        return false, []
×
1021
    end
1022
    ret = isa(metaargs[i], Expr) ? (metaargs[i]::Expr).args : []
×
1023
    if delete
×
1024
        deleteat!(metaargs, i)
×
1025
        isempty(metaargs) && deleteat!(blockargs, idx)
×
1026
    end
1027
    true, ret
×
1028
end
1029

1030
# Find index of `sym` in a meta expression argument list, or 0.
1031
function findmetaarg(metaargs, sym)
×
1032
    for i = 1:length(metaargs)
×
1033
        arg = metaargs[i]
×
1034
        if (isa(arg, Symbol) && (arg::Symbol)    == sym) ||
×
1035
           (isa(arg, Expr)   && (arg::Expr).head == sym)
1036
            return i
×
1037
        end
1038
    end
×
1039
    return 0
×
1040
end
1041

1042
function annotate_meta_def_or_block(@nospecialize(ex), meta::Symbol)
×
1043
    inner = unwrap_macrocalls(ex)
×
1044
    if is_function_def(inner)
×
1045
        # annotation on a definition
1046
        return esc(pushmeta!(ex, meta))
×
1047
    else
1048
        # annotation on a block
1049
        return Expr(:block,
×
1050
                    Expr(meta, true),
1051
                    Expr(:local, Expr(:(=), :val, esc(ex))),
1052
                    Expr(meta, false),
1053
                    :val)
1054
    end
1055
end
1056

1057
function is_short_function_def(@nospecialize(ex))
×
1058
    isexpr(ex, :(=)) || return false
×
1059
    while length(ex.args) >= 1 && isa(ex.args[1], Expr)
×
1060
        (ex.args[1].head === :call) && return true
×
1061
        (ex.args[1].head === :where || ex.args[1].head === :(::)) || return false
×
1062
        ex = ex.args[1]
×
1063
    end
×
1064
    return false
×
1065
end
1066
is_function_def(@nospecialize(ex)) =
×
1067
    return isexpr(ex, :function) || is_short_function_def(ex) || isexpr(ex, :->)
1068

1069
function findmeta(ex::Expr)
×
1070
    if is_function_def(ex)
×
1071
        body = ex.args[2]::Expr
×
1072
        body.head === :block || error(body, " is not a block expression")
×
1073
        return findmeta_block(ex.args)
×
1074
    end
1075
    error(ex, " is not a function expression")
×
1076
end
1077

1078
findmeta(ex::Array{Any,1}) = findmeta_block(ex)
×
1079

1080
function findmeta_block(exargs, argsmatch=args->true)
×
1081
    for i = 1:length(exargs)
×
1082
        a = exargs[i]
×
1083
        if isa(a, Expr)
×
1084
            if a.head === :meta && argsmatch(a.args)
×
1085
                return i, exargs
×
1086
            elseif a.head === :block
×
1087
                idx, exa = findmeta_block(a.args, argsmatch)
×
1088
                if idx != 0
×
1089
                    return idx, exa
×
1090
                end
1091
            end
1092
        end
1093
    end
×
1094
    return 0, []
×
1095
end
1096

1097
"""
1098
    Base.remove_linenums!(ex)
1099

1100
Remove all line-number metadata from expression-like object `ex`.
1101
"""
1102
function remove_linenums!(@nospecialize ex)
×
1103
    if ex isa Expr
×
1104
        if ex.head === :block || ex.head === :quote
×
1105
            # remove line number expressions from metadata (not argument literal or inert) position
1106
            filter!(ex.args) do x
×
1107
                isa(x, Expr) && x.head === :line && return false
×
1108
                isa(x, LineNumberNode) && return false
×
1109
                return true
×
1110
            end
1111
        end
1112
        for subex in ex.args
×
1113
            subex isa Expr && remove_linenums!(subex)
×
1114
        end
×
1115
    elseif ex isa CodeInfo
×
1116
        ex.debuginfo = Core.DebugInfo(ex.debuginfo.def) # TODO: filter partially, but keep edges
×
1117
    end
1118
    return ex
×
1119
end
1120

1121
replace_linenums!(ex, ln::LineNumberNode) = ex
×
1122
function replace_linenums!(ex::Expr, ln::LineNumberNode)
×
1123
    if ex.head === :block || ex.head === :quote
×
1124
        # replace line number expressions from metadata (not argument literal or inert) position
1125
        map!(ex.args, ex.args) do @nospecialize(x)
×
1126
            isa(x, Expr) && x.head === :line && length(x.args) == 1 && return Expr(:line, ln.line)
×
1127
            isa(x, Expr) && x.head === :line && length(x.args) == 2 && return Expr(:line, ln.line, ln.file)
×
1128
            isa(x, LineNumberNode) && return ln
×
1129
            return x
×
1130
        end
1131
    end
1132
    # preserve any linenums inside `esc(...)` guards
1133
    if ex.head !== :escape
×
1134
        for subex in ex.args
×
1135
            subex isa Expr && replace_linenums!(subex, ln)
×
1136
        end
×
1137
    end
1138
    return ex
×
1139
end
1140

1141
macro generated()
1142
    return Expr(:generated)
1143
end
1144

1145
"""
1146
    @generated f
1147

1148
`@generated` is used to annotate a function which will be generated.
1149
In the body of the generated function, only types of arguments can be read
1150
(not the values). The function returns a quoted expression evaluated when the
1151
function is called. The `@generated` macro should not be used on functions mutating
1152
the global scope or depending on mutable elements.
1153

1154
See [Metaprogramming](@ref) for further details.
1155

1156
# Examples
1157
```jldoctest
1158
julia> @generated function bar(x)
1159
           if x <: Integer
1160
               return :(x ^ 2)
1161
           else
1162
               return :(x)
1163
           end
1164
       end
1165
bar (generic function with 1 method)
1166

1167
julia> bar(4)
1168
16
1169

1170
julia> bar("baz")
1171
"baz"
1172
```
1173
"""
1174
macro generated(f)
1175
    if isa(f, Expr) && (f.head === :function || is_short_function_def(f))
1176
        body = f.args[2]
1177
        lno = body.args[1]
1178
        return Expr(:escape,
1179
                    Expr(f.head, f.args[1],
1180
                         Expr(:block,
1181
                              lno,
1182
                              Expr(:if, Expr(:generated),
1183
                                   body,
1184
                                   Expr(:block,
1185
                                        Expr(:meta, :generated_only),
1186
                                        Expr(:return, nothing))))))
1187
    else
1188
        error("invalid syntax; @generated must be used with a function definition")
1189
    end
1190
end
1191

1192

1193
"""
1194
    @atomic var
1195
    @atomic order ex
1196

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

1200
    @atomic a.b.x = new
1201
    @atomic a.b.x += addend
1202
    @atomic :release a.b.x = new
1203
    @atomic :acquire_release a.b.x += addend
1204
    @atomic m[idx] = new
1205
    @atomic m[idx] += addend
1206
    @atomic :release m[idx] = new
1207
    @atomic :acquire_release m[idx] += addend
1208

1209
Perform the store operation expressed on the right atomically and return the
1210
new value.
1211

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

1216
With any modifying operator this operation translates to a
1217
`modifyproperty!(a.b, :x, op, addend)[2]` or, in case of reference, to a
1218
`modifyindex_atomic!(m, order, op, addend, idx...)[2]` call,
1219
with `order` defaulting to `:sequentially_consistent`.
1220

1221
    @atomic a.b.x max arg2
1222
    @atomic a.b.x + arg2
1223
    @atomic max(a.b.x, arg2)
1224
    @atomic :acquire_release max(a.b.x, arg2)
1225
    @atomic :acquire_release a.b.x + arg2
1226
    @atomic :acquire_release a.b.x max arg2
1227
    @atomic m[idx] max arg2
1228
    @atomic m[idx] + arg2
1229
    @atomic max(m[idx], arg2)
1230
    @atomic :acquire_release max(m[idx], arg2)
1231
    @atomic :acquire_release m[idx] + arg2
1232
    @atomic :acquire_release m[idx] max arg2
1233

1234
Perform the binary operation expressed on the right atomically. Store the
1235
result into the field or the reference in the first argument, and return the values
1236
`(old, new)`.
1237

1238
This operation translates to a `modifyproperty!(a.b, :x, func, arg2)` or,
1239
in case of reference to a `modifyindex_atomic!(m, order, func, arg2, idx)` call,
1240
with `order` defaulting to `:sequentially_consistent`.
1241

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

1244
# Examples
1245
```jldoctest
1246
julia> mutable struct Atomic{T}; @atomic x::T; end
1247

1248
julia> a = Atomic(1)
1249
Atomic{Int64}(1)
1250

1251
julia> @atomic a.x # fetch field x of a, with sequential consistency
1252
1
1253

1254
julia> @atomic :sequentially_consistent a.x = 2 # set field x of a, with sequential consistency
1255
2
1256

1257
julia> @atomic a.x += 1 # increment field x of a, with sequential consistency
1258
3
1259

1260
julia> @atomic a.x + 1 # increment field x of a, with sequential consistency
1261
3 => 4
1262

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

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

1269
julia> @atomic a.x max 5 # again change field x of a to the max value, with sequential consistency
1270
10 => 10
1271
```
1272

1273
```jldoctest
1274
julia> mem = AtomicMemory{Int}(undef, 2);
1275

1276
julia> @atomic mem[1] = 2 # set mem[1] to value 2 with sequential consistency
1277
2
1278

1279
julia> @atomic :monotonic mem[1] # fetch the first value of mem, with monotonic consistency
1280
2
1281

1282
julia> @atomic mem[1] += 1 # increment the first value of mem, with sequential consistency
1283
3
1284

1285
julia> @atomic mem[1] + 1 # increment the first value of mem, with sequential consistency
1286
3 => 4
1287

1288
julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1289
4
1290

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

1294
julia> @atomic mem[1] max 5 # again change the first value of mem to the max value, with sequential consistency
1295
10 => 10
1296
```
1297

1298
!!! compat "Julia 1.7"
1299
    Atomic fields functionality requires at least Julia 1.7.
1300

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

1372

1373
"""
1374
    @atomicswap a.b.x = new
1375
    @atomicswap :sequentially_consistent a.b.x = new
1376
    @atomicswap m[idx] = new
1377
    @atomicswap :sequentially_consistent m[idx] = new
1378

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

1382
This operation translates to a `swapproperty!(a.b, :x, new)` or,
1383
in case of reference, `swapindex_atomic!(mem, order, new, idx)` call,
1384
with `order` defaulting to `:sequentially_consistent`.
1385

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

1388
# Examples
1389
```jldoctest
1390
julia> mutable struct Atomic{T}; @atomic x::T; end
1391

1392
julia> a = Atomic(1)
1393
Atomic{Int64}(1)
1394

1395
julia> @atomicswap a.x = 2+2 # replace field x of a with 4, with sequential consistency
1396
1
1397

1398
julia> @atomic a.x # fetch field x of a, with sequential consistency
1399
4
1400
```
1401

1402
```jldoctest
1403
julia> mem = AtomicMemory{Int}(undef, 2);
1404

1405
julia> @atomic mem[1] = 1;
1406

1407
julia> @atomicswap mem[1] = 4 # replace the first value of `mem` with 4, with sequential consistency
1408
1
1409

1410
julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1411
4
1412
```
1413

1414
!!! compat "Julia 1.7"
1415
    Atomic fields functionality requires at least Julia 1.7.
1416

1417
!!! compat "Julia 1.12"
1418
    Atomic reference functionality requires at least Julia 1.12.
1419
"""
1420
macro atomicswap(order, ex)
1421
    order isa QuoteNode || (order = esc(order))
1422
    return make_atomicswap(order, ex)
1423
end
1424
macro atomicswap(ex)
1425
    return make_atomicswap(QuoteNode(:sequentially_consistent), ex)
1426
end
1427
function make_atomicswap(order, ex)
×
1428
    @nospecialize
×
1429
    is_expr(ex, :(=), 2) || error("@atomicswap expression missing assignment")
×
1430
    l, val = ex.args[1], esc(ex.args[2])
×
1431
    if is_expr(l, :., 2)
×
1432
        ll, lr = esc(l.args[1]), esc(l.args[2])
×
1433
        return :(swapproperty!($ll, $lr, $val, $order))
×
1434
    elseif is_expr(l, :ref)
×
1435
        x, idcs = esc(l.args[1]), map(esc, l.args[2:end])
×
1436
        return :(swapindex_atomic!($x, $order, $val, $(idcs...)))
×
1437
    end
1438
    error("@atomicswap expression missing field access or indexing")
×
1439
end
1440

1441

1442
"""
1443
    @atomicreplace a.b.x expected => desired
1444
    @atomicreplace :sequentially_consistent a.b.x expected => desired
1445
    @atomicreplace :sequentially_consistent :monotonic a.b.x expected => desired
1446
    @atomicreplace m[idx] expected => desired
1447
    @atomicreplace :sequentially_consistent m[idx] expected => desired
1448
    @atomicreplace :sequentially_consistent :monotonic m[idx] expected => desired
1449

1450
Perform the conditional replacement expressed by the pair atomically, returning
1451
the values `(old, success::Bool)`. Where `success` indicates whether the
1452
replacement was completed.
1453

1454
This operation translates to a `replaceproperty!(a.b, :x, expected, desired)` or,
1455
in case of reference, to a
1456
`replaceindex_atomic!(mem, success_order, fail_order, expected, desired, idx)` call,
1457
with both orders defaulting to `:sequentially_consistent`.
1458

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

1461
# Examples
1462
```jldoctest
1463
julia> mutable struct Atomic{T}; @atomic x::T; end
1464

1465
julia> a = Atomic(1)
1466
Atomic{Int64}(1)
1467

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

1471
julia> @atomic a.x # fetch field x of a, with sequential consistency
1472
2
1473

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

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

1479
julia> @atomicreplace a.x xchg
1480
(old = 2, success = true)
1481

1482
julia> @atomic a.x # fetch field x of a, with sequential consistency
1483
0
1484
```
1485

1486
```jldoctest
1487
julia> mem = AtomicMemory{Int}(undef, 2);
1488

1489
julia> @atomic mem[1] = 1;
1490

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

1494
julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1495
2
1496

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

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

1502
julia> @atomicreplace mem[1] xchg
1503
(old = 2, success = true)
1504

1505
julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1506
0
1507
```
1508

1509
!!! compat "Julia 1.7"
1510
    Atomic fields functionality requires at least Julia 1.7.
1511

1512
!!! compat "Julia 1.12"
1513
    Atomic reference functionality requires at least Julia 1.12.
1514
"""
1515
macro atomicreplace(success_order, fail_order, ex, old_new)
1516
    fail_order isa QuoteNode || (fail_order = esc(fail_order))
1517
    success_order isa QuoteNode || (success_order = esc(success_order))
1518
    return make_atomicreplace(success_order, fail_order, ex, old_new)
1519
end
1520
macro atomicreplace(order, ex, old_new)
1521
    order isa QuoteNode || (order = esc(order))
1522
    return make_atomicreplace(order, order, ex, old_new)
1523
end
1524
macro atomicreplace(ex, old_new)
1525
    return make_atomicreplace(QuoteNode(:sequentially_consistent), QuoteNode(:sequentially_consistent), ex, old_new)
1526
end
1527
function make_atomicreplace(success_order, fail_order, ex, old_new)
×
1528
    @nospecialize
×
1529
    if is_expr(ex, :., 2)
×
1530
        ll, lr = esc(ex.args[1]), esc(ex.args[2])
×
1531
        if is_expr(old_new, :call, 3) && old_new.args[1] === :(=>)
×
1532
            exp, rep = esc(old_new.args[2]), esc(old_new.args[3])
×
1533
            return :(replaceproperty!($ll, $lr, $exp, $rep, $success_order, $fail_order))
×
1534
        else
1535
            old_new = esc(old_new)
×
1536
            return :(replaceproperty!($ll, $lr, $old_new::Pair..., $success_order, $fail_order))
×
1537
        end
1538
    elseif is_expr(ex, :ref)
×
1539
        x, idcs = esc(ex.args[1]), map(esc, ex.args[2:end])
×
1540
        if is_expr(old_new, :call, 3) && old_new.args[1] === :(=>)
×
1541
            exp, rep = esc(old_new.args[2]), esc(old_new.args[3])
×
1542
            return :(replaceindex_atomic!($x, $success_order, $fail_order, $exp, $rep, $(idcs...)))
×
1543
        else
1544
            old_new = esc(old_new)
×
1545
            return :(replaceindex_atomic!($x, $success_order, $fail_order, $old_new::Pair..., $(idcs...)))
×
1546
        end
1547
    end
1548
    error("@atomicreplace expression missing field access or indexing")
×
1549
end
1550

1551
"""
1552
    @atomiconce a.b.x = value
1553
    @atomiconce :sequentially_consistent a.b.x = value
1554
    @atomiconce :sequentially_consistent :monotonic a.b.x = value
1555
    @atomiconce m[idx] = value
1556
    @atomiconce :sequentially_consistent m[idx] = value
1557
    @atomiconce :sequentially_consistent :monotonic m[idx] = value
1558

1559
Perform the conditional assignment of value atomically if it was previously
1560
unset. Returned value `success::Bool` indicates whether the assignment was completed.
1561

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

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

1568
# Examples
1569
```jldoctest
1570
julia> mutable struct AtomicOnce
1571
           @atomic x
1572
           AtomicOnce() = new()
1573
       end
1574

1575
julia> a = AtomicOnce()
1576
AtomicOnce(#undef)
1577

1578
julia> @atomiconce a.x = 1 # set field x of a to 1, if unset, with sequential consistency
1579
true
1580

1581
julia> @atomic a.x # fetch field x of a, with sequential consistency
1582
1
1583

1584
julia> @atomiconce :monotonic a.x = 2 # set field x of a to 1, if unset, with monotonic consistence
1585
false
1586
```
1587

1588
```jldoctest
1589
julia> mem = AtomicMemory{Vector{Int}}(undef, 1);
1590

1591
julia> isassigned(mem, 1)
1592
false
1593

1594
julia> @atomiconce mem[1] = [1] # set the first value of mem to [1], if unset, with sequential consistency
1595
true
1596

1597
julia> isassigned(mem, 1)
1598
true
1599

1600
julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
1601
1-element Vector{Int64}:
1602
 1
1603

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

1607
julia> @atomic mem[1]
1608
1-element Vector{Int64}:
1609
 1
1610
```
1611

1612
!!! compat "Julia 1.11"
1613
    Atomic fields functionality requires at least Julia 1.11.
1614

1615
!!! compat "Julia 1.12"
1616
    Atomic reference functionality requires at least Julia 1.12.
1617
"""
1618
macro atomiconce(success_order, fail_order, ex)
1619
    fail_order isa QuoteNode || (fail_order = esc(fail_order))
1620
    success_order isa QuoteNode || (success_order = esc(success_order))
1621
    return make_atomiconce(success_order, fail_order, ex)
1622
end
1623
macro atomiconce(order, ex)
1624
    order isa QuoteNode || (order = esc(order))
1625
    return make_atomiconce(order, order, ex)
1626
end
1627
macro atomiconce(ex)
1628
    return make_atomiconce(QuoteNode(:sequentially_consistent), QuoteNode(:sequentially_consistent), ex)
1629
end
1630
function make_atomiconce(success_order, fail_order, ex)
×
1631
    @nospecialize
×
1632
    is_expr(ex, :(=), 2) || error("@atomiconce expression missing assignment")
×
1633
    l, val = ex.args[1], esc(ex.args[2])
×
1634
    if is_expr(l, :., 2)
×
1635
        ll, lr = esc(l.args[1]), esc(l.args[2])
×
1636
        return :(setpropertyonce!($ll, $lr, $val, $success_order, $fail_order))
×
1637
    elseif is_expr(l, :ref)
×
1638
        x, idcs = esc(l.args[1]), map(esc, l.args[2:end])
×
1639
        return :(setindexonce_atomic!($x, $success_order, $fail_order, $val, $(idcs...)))
×
1640
    end
1641
    error("@atomiconce expression missing field access or indexing")
×
1642
end
1643

1644
# Meta expression head, these generally can't be deleted even when they are
1645
# in a dead branch but can be ignored when analyzing uses/liveness.
1646
is_meta_expr_head(head::Symbol) = head === :boundscheck || head === :meta || head === :loopinfo
×
1647
is_meta_expr(@nospecialize x) = isa(x, Expr) && is_meta_expr_head(x.head)
×
1648

1649
function is_self_quoting(@nospecialize(x))
×
1650
    return isa(x,Number) || isa(x,AbstractString) || isa(x,Tuple) || isa(x,Type) ||
×
1651
        isa(x,Char) || x === nothing || isa(x,Function)
1652
end
1653

1654
function quoted(@nospecialize(x))
×
1655
    return is_self_quoting(x) ? x : QuoteNode(x)
×
1656
end
1657

1658
# Implementation of generated functions
1659
function generated_body_to_codeinfo(ex::Expr, defmod::Module, isva::Bool)
34✔
1660
    ci = ccall(:jl_expand, Any, (Any, Any), ex, defmod)
34✔
1661
    if !isa(ci, CodeInfo)
34✔
1662
        if isa(ci, Expr) && ci.head === :error
×
1663
            error("syntax: $(ci.args[1])")
×
1664
        end
1665
        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.")
×
1666
    end
1667
    ci.isva = isva
34✔
1668
    code = ci.code
34✔
1669
    bindings = IdSet{Core.Binding}()
34✔
1670
    for i = 1:length(code)
34✔
1671
        stmt = code[i]
368✔
1672
        if isa(stmt, GlobalRef)
368✔
1673
            push!(bindings, convert(Core.Binding, stmt))
118✔
1674
        end
1675
    end
702✔
1676
    if !isempty(bindings)
34✔
1677
        ci.edges = Core.svec(bindings...)
34✔
1678
    end
1679
    return ci
34✔
1680
end
1681

1682
# invoke and wrap the results of @generated expression
1683
function (g::Core.GeneratedFunctionStub)(world::UInt, source::Method, @nospecialize args...)
34✔
1684
    # args is (spvals..., argtypes...)
1685
    body = g.gen(args...)
34✔
1686
    file = source.file
34✔
1687
    file isa Symbol || (file = :none)
×
1688
    lam = Expr(:lambda, Expr(:argnames, g.argnames...).args,
34✔
1689
               Expr(:var"scope-block",
1690
                    Expr(:block,
1691
                         LineNumberNode(Int(source.line), source.file),
1692
                         Expr(:meta, :push_loc, file, :var"@generated body"),
1693
                         Expr(:return, body),
1694
                         Expr(:meta, :pop_loc))))
1695
    spnames = g.spnames
34✔
1696
    return generated_body_to_codeinfo(spnames === Core.svec() ? lam : Expr(Symbol("with-static-parameters"), lam, spnames...),
34✔
1697
        typename(typeof(g.gen)).module,
1698
        source.isva)
1699
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