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

JuliaLang / julia / #37539

pending completion
#37539

push

local

web-flow
add devdocs how to profile package precompilation with tracy (#49784)

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

72624 of 83590 relevant lines covered (86.88%)

35576540.76 hits per line

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

85.53
/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)
14,191✔
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}, ())
501✔
15

16
gensym(s::String) = ccall(:jl_tagged_gensym, Ref{Symbol}, (Ptr{UInt8}, Csize_t), s, sizeof(s))
310✔
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)
23✔
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...)
1✔
28
    blk = Expr(:block)
1✔
29
    for name in names
1✔
30
        push!(blk.args, :($(esc(name)) = gensym($(string(name)))))
1✔
31
    end
1✔
32
    push!(blk.args, :nothing)
1✔
33
    return blk
1✔
34
end
35

36
## expressions ##
37

38
isexpr(@nospecialize(ex), head::Symbol) = isa(ex, Expr) && ex.head === head
6,278,594,271✔
39
isexpr(@nospecialize(ex), head::Symbol, n::Int) = isa(ex, Expr) && ex.head === head && length(ex.args) == n
65,107✔
40

41
copy(e::Expr) = exprarray(e.head, copy_exprargs(e.args))
138,892,146✔
42

43
# copy parts of an AST that the compiler mutates
44
function copy_exprs(@nospecialize(x))
439,790,270✔
45
    if isa(x, Expr)
439,790,270✔
46
        return copy(x)
138,875,063✔
47
    elseif isa(x, PhiNode)
300,915,207✔
48
        values = x.values
2,862,428✔
49
        nvalues = length(values)
2,862,428✔
50
        new_values = Vector{Any}(undef, nvalues)
2,862,428✔
51
        @inbounds for i = 1:nvalues
5,721,308✔
52
            isassigned(values, i) || continue
5,485,201✔
53
            new_values[i] = copy_exprs(values[i])
5,485,176✔
54
        end
8,111,522✔
55
        return PhiNode(copy(x.edges), new_values)
2,862,428✔
56
    elseif isa(x, PhiCNode)
298,052,779✔
57
        values = x.values
22✔
58
        nvalues = length(values)
22✔
59
        new_values = Vector{Any}(undef, nvalues)
22✔
60
        @inbounds for i = 1:nvalues
44✔
61
            isassigned(values, i) || continue
29✔
62
            new_values[i] = copy_exprs(values[i])
29✔
63
        end
36✔
64
        return PhiCNode(new_values)
22✔
65
    end
66
    return x
298,052,757✔
67
end
68
copy_exprargs(x::Array{Any,1}) = Any[copy_exprs(@inbounds x[i]) for i in 1:length(x)]
155,241,010✔
69

70
@eval exprarray(head::Symbol, arg::Array{Any,1}) = $(Expr(:new, :Expr, :head, :arg))
138,894,108✔
71

72
# create copies of the CodeInfo definition, and any mutable fields
73
function copy(c::CodeInfo)
7,343,885✔
74
    cnew = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), c)
7,343,885✔
75
    cnew.code = copy_exprargs(cnew.code)
7,343,885✔
76
    cnew.slotnames = copy(cnew.slotnames)
7,343,885✔
77
    cnew.slotflags = copy(cnew.slotflags)
7,343,885✔
78
    if cnew.slottypes !== nothing
7,343,885✔
79
        cnew.slottypes = copy(cnew.slottypes)
5,050,888✔
80
    end
81
    cnew.codelocs  = copy(cnew.codelocs)
7,343,885✔
82
    cnew.linetable = copy(cnew.linetable::Union{Vector{Any},Vector{Core.LineInfoNode}})
14,686,984✔
83
    cnew.ssaflags  = copy(cnew.ssaflags)
7,343,885✔
84
    cnew.edges     = cnew.edges === nothing ? nothing : copy(cnew.edges::Vector)
7,343,886✔
85
    ssavaluetypes  = cnew.ssavaluetypes
7,343,885✔
86
    ssavaluetypes isa Vector{Any} && (cnew.ssavaluetypes = copy(ssavaluetypes))
7,343,885✔
87
    return cnew
7,343,885✔
88
end
89

90

91
==(x::Expr, y::Expr) = x.head === y.head && isequal(x.args, y.args)
13,561✔
92
==(x::QuoteNode, y::QuoteNode) = isequal(x.value, y.value)
1,519✔
93
==(stmt1::Core.PhiNode, stmt2::Core.PhiNode) = stmt1.edges == stmt2.edges && stmt1.values == stmt2.values
×
94

95
"""
96
    macroexpand(m::Module, x; recursive=true)
97

98
Take the expression `x` and return an equivalent expression with all macros removed (expanded)
99
for executing in module `m`.
100
The `recursive` keyword controls whether deeper levels of nested macros are also expanded.
101
This is demonstrated in the example below:
102
```julia-repl
103
julia> module M
104
           macro m1()
105
               42
106
           end
107
           macro m2()
108
               :(@m1())
109
           end
110
       end
111
M
112

113
julia> macroexpand(M, :(@m2()), recursive=true)
114
42
115

116
julia> macroexpand(M, :(@m2()), recursive=false)
117
:(#= REPL[16]:6 =# M.@m1)
118
```
119
"""
120
function macroexpand(m::Module, @nospecialize(x); recursive=true)
679✔
121
    if recursive
35✔
122
        ccall(:jl_macroexpand, Any, (Any, Any), x, m)
644✔
123
    else
124
        ccall(:jl_macroexpand1, Any, (Any, Any), x, m)
×
125
    end
126
end
127

128
"""
129
    @macroexpand
130

131
Return equivalent expression with all macros removed (expanded).
132

133
There are differences between `@macroexpand` and [`macroexpand`](@ref).
134

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

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

141
This is best seen in the following example:
142
```julia-repl
143
julia> module M
144
           macro m()
145
               1
146
           end
147
           function f()
148
               (@macroexpand(@m),
149
                macroexpand(M, :(@m)),
150
                macroexpand(Main, :(@m))
151
               )
152
           end
153
       end
154
M
155

156
julia> macro m()
157
           2
158
       end
159
@m (macro with 1 method)
160

161
julia> M.f()
162
(1, 1, 2)
163
```
164
With `@macroexpand` the expression expands where `@macroexpand` appears in the code (module `M` in the example).
165
With `macroexpand` the expression expands in the module given as the first argument.
166
"""
167
macro macroexpand(code)
37✔
168
    return :(macroexpand($__module__, $(QuoteNode(code)), recursive=true))
37✔
169
end
170

171

172
"""
173
    @macroexpand1
174

175
Non recursive version of [`@macroexpand`](@ref).
176
"""
177
macro macroexpand1(code)
178
    return :(macroexpand($__module__, $(QuoteNode(code)), recursive=false))
179
end
180

181
## misc syntax ##
182

183
"""
184
    Core.eval(m::Module, expr)
185

186
Evaluate an expression in the given module and return the result.
187
"""
188
Core.eval
189

190
"""
191
    @inline
192

193
Give a hint to the compiler that this function is worth inlining.
194

195
Small functions typically do not need the `@inline` annotation,
196
as the compiler does it automatically. By using `@inline` on bigger functions,
197
an extra nudge can be given to the compiler to inline it.
198

199
`@inline` can be applied immediately before a function definition or within a function body.
200

201
```julia
202
# annotate long-form definition
203
@inline function longdef(x)
204
    ...
205
end
206

207
# annotate short-form definition
208
@inline shortdef(x) = ...
209

210
# annotate anonymous function that a `do` block creates
211
f() do
212
    @inline
213
    ...
214
end
215
```
216

217
!!! compat "Julia 1.8"
218
    The usage within a function body requires at least Julia 1.8.
219

220
---
221
    @inline block
222

223
Give a hint to the compiler that calls within `block` are worth inlining.
224

225
```julia
226
# The compiler will try to inline `f`
227
@inline f(...)
228

229
# The compiler will try to inline `f`, `g` and `+`
230
@inline f(...) + g(...)
231
```
232

233
!!! note
234
    A callsite annotation always has the precedence over the annotation applied to the
235
    definition of the called function:
236
    ```julia
237
    @noinline function explicit_noinline(args...)
238
        # body
239
    end
240

241
    let
242
        @inline explicit_noinline(args...) # will be inlined
243
    end
244
    ```
245

246
!!! note
247
    When there are nested callsite annotations, the innermost annotation has the precedence:
248
    ```julia
249
    @noinline let a0, b0 = ...
250
        a = @inline f(a0)  # the compiler will try to inline this call
251
        b = f(b0)          # the compiler will NOT try to inline this call
252
        return a, b
253
    end
254
    ```
255

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

261
!!! compat "Julia 1.8"
262
    The callsite annotation requires at least Julia 1.8.
263
"""
264
macro inline(x)
1,866✔
265
    return annotate_meta_def_or_block(x, :inline)
1,866✔
266
end
267

268
"""
269
    @noinline
270

271
Give a hint to the compiler that it should not inline a function.
272

273
Small functions are typically inlined automatically.
274
By using `@noinline` on small functions, auto-inlining can be
275
prevented.
276

277
`@noinline` can be applied immediately before a function definition or within a function body.
278

279
```julia
280
# annotate long-form definition
281
@noinline function longdef(x)
282
    ...
283
end
284

285
# annotate short-form definition
286
@noinline shortdef(x) = ...
287

288
# annotate anonymous function that a `do` block creates
289
f() do
290
    @noinline
291
    ...
292
end
293
```
294

295
!!! compat "Julia 1.8"
296
    The usage within a function body requires at least Julia 1.8.
297

298
---
299
    @noinline block
300

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

303
```julia
304
# The compiler will try to not inline `f`
305
@noinline f(...)
306

307
# The compiler will try to not inline `f`, `g` and `+`
308
@noinline f(...) + g(...)
309
```
310

311
!!! note
312
    A callsite annotation always has the precedence over the annotation applied to the
313
    definition of the called function:
314
    ```julia
315
    @inline function explicit_inline(args...)
316
        # body
317
    end
318

319
    let
320
        @noinline explicit_inline(args...) # will not be inlined
321
    end
322
    ```
323

324
!!! note
325
    When there are nested callsite annotations, the innermost annotation has the precedence:
326
    ```julia
327
    @inline let a0, b0 = ...
328
        a = @noinline f(a0)  # the compiler will NOT try to inline this call
329
        b = f(b0)            # the compiler will try to inline this call
330
        return a, b
331
    end
332
    ```
333

334
!!! compat "Julia 1.8"
335
    The callsite annotation requires at least Julia 1.8.
336

337
---
338
!!! note
339
    If the function is trivial (for example returning a constant) it might get inlined anyway.
340
"""
341
macro noinline(x)
247✔
342
    return annotate_meta_def_or_block(x, :noinline)
247✔
343
end
344

345

346
"""
347
    @constprop setting [ex]
348

349
Control the mode of interprocedural constant propagation for the annotated function.
350

351
Two `setting`s are supported:
352

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

360
`@constprop` can be applied immediately before a function definition or within a function body.
361

362
```julia
363
# annotate long-form definition
364
@constprop :aggressive function longdef(x)
365
  ...
366
end
367

368
# annotate short-form definition
369
@constprop :aggressive shortdef(x) = ...
370

371
# annotate anonymous function that a `do` block creates
372
f() do
373
    @constprop :aggressive
374
    ...
375
end
376
```
377

378
!!! compat "Julia 1.10"
379
  The usage within a function body requires at least Julia 1.10.
380
"""
381
macro constprop(setting, ex)
33✔
382
    sym = constprop_setting(setting)
33✔
383
    isa(ex, Expr) && return esc(pushmeta!(ex, sym))
33✔
384
    throw(ArgumentError(LazyString("Bad expression `", ex, "` in `@constprop settings ex`")))
×
385
end
386
macro constprop(setting)
387
    sym = constprop_setting(setting)
388
    return Expr(:meta, sym)
389
end
390

391
function constprop_setting(@nospecialize setting)
33✔
392
    isa(setting, QuoteNode) && (setting = setting.value)
33✔
393
    if setting === :aggressive
33✔
394
        return :aggressive_constprop
22✔
395
    elseif setting === :none
11✔
396
        return :no_constprop
11✔
397
    end
398
    throw(ArgumentError(LazyString("@constprop "), setting, "not supported"))
×
399
end
400

401
"""
402
    @assume_effects setting... [ex]
403

404
Override the compiler's effect modeling for the given method or foreign call.
405
`@assume_effects` can be applied immediately before a function definition or within a function body.
406
It can also be applied immediately before a `@ccall` expression.
407

408
!!! compat "Julia 1.8"
409
    Using `Base.@assume_effects` requires Julia version 1.8.
410

411
# Examples
412
```jldoctest
413
julia> Base.@assume_effects :terminates_locally function pow(x)
414
           # this :terminates_locally allows `pow` to be constant-folded
415
           res = 1
416
           1 < x < 20 || error("bad pow")
417
           while x > 1
418
               res *= x
419
               x -= 1
420
           end
421
           return res
422
       end
423
pow (generic function with 1 method)
424

425
julia> code_typed() do
426
           pow(12)
427
       end
428
1-element Vector{Any}:
429
 CodeInfo(
430
1 ─     return 479001600
431
) => Int64
432

433
julia> code_typed() do
434
           map((2,3,4)) do x
435
               # this :terminates_locally allows this anonymous function to be constant-folded
436
               Base.@assume_effects :terminates_locally
437
               res = 1
438
               1 < x < 20 || error("bad pow")
439
               while x > 1
440
                   res *= x
441
                   x -= 1
442
               end
443
               return res
444
           end
445
       end
446
1-element Vector{Any}:
447
 CodeInfo(
448
1 ─     return (2, 6, 24)
449
) => Tuple{Int64, Int64, Int64}
450

451
julia> Base.@assume_effects :total !:nothrow @ccall jl_type_intersection(Vector{Int}::Any, Vector{<:Integer}::Any)::Any
452
Vector{Int64} (alias for Array{Int64, 1})
453
```
454

455
!!! compat "Julia 1.10"
456
  The usage within a function body requires at least Julia 1.10.
457

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

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

471
The following `setting`s are supported.
472
- `:consistent`
473
- `:effect_free`
474
- `:nothrow`
475
- `:terminates_globally`
476
- `:terminates_locally`
477
- `:notaskstate`
478
- `:inaccessiblememonly`
479
- `:foldable`
480
- `:removable`
481
- `:total`
482

483
# Extended help
484

485
---
486
## `:consistent`
487

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

492
!!! note
493
    This in particular implies that the method must not return a freshly allocated
494
    mutable object. Multiple allocations of mutable objects (even with identical
495
    contents) are not egal.
496

497
!!! note
498
    The `:consistent`-cy assertion is made world-age wise. More formally, write
499
    ``fáµ¢`` for the evaluation of ``f`` in world-age ``i``, then we require:
500
    ```math
501
    ∀ i, x, y: x ≡ y → fᵢ(x) ≡ fᵢ(y)
502
    ```
503
    However, for two world ages ``i``, ``j`` s.t. ``i ≠ j``, we may have ``fᵢ(x) ≢ fⱼ(y)``.
504

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

509
!!! note
510
    The `:consistent`-cy includes all legal rewrites performed by the optimizer.
511
    For example, floating-point fastmath operations are not considered `:consistent`,
512
    because the optimizer may rewrite them causing the output to not be `:consistent`,
513
    even for the same world age (e.g. because one ran in the interpreter, while
514
    the other was optimized).
515

516
!!! note
517
    The `:consistent`-cy assertion currrently includes the assertion that the function
518
    will not execute any undefined behavior (for any input). Note that undefined behavior
519
    may technically cause the function to violate other effect assertions (such as
520
    `:nothrow` or `:effect_free`) as well, but we do not model this, and all effects
521
    except `:consistent` assume the absence of undefined behavior.
522

523
!!! note
524
    If `:consistent` functions terminate by throwing an exception, that exception
525
    itself is not required to meet the egality requirement specified above.
526

527
---
528
## `:effect_free`
529

530
The `:effect_free` setting asserts that the method is free of externally semantically
531
visible side effects. The following is an incomplete list of externally semantically
532
visible side effects:
533
- Changing the value of a global variable.
534
- Mutating the heap (e.g. an array or mutable value), except as noted below
535
- Changing the method table (e.g. through calls to eval)
536
- File/Network/etc. I/O
537
- Task switching
538

539
However, the following are explicitly not semantically visible, even if they
540
may be observable:
541
- Memory allocations (both mutable and immutable)
542
- Elapsed time
543
- Garbage collection
544
- Heap mutations of objects whose lifetime does not exceed the method (i.e.
545
  were allocated in the method and do not escape).
546
- The returned value (which is externally visible, but not a side effect)
547

548
The rule of thumb here is that an externally visible side effect is anything
549
that would affect the execution of the remainder of the program if the function
550
were not executed.
551

552
!!! note
553
    The `:effect_free` assertion is made both for the method itself and any code
554
    that is executed by the method. Keep in mind that the assertion must be
555
    valid for all world ages and limit use of this assertion accordingly.
556

557
---
558
## `:nothrow`
559

560
The `:nothrow` settings asserts that this method does not terminate abnormally
561
(i.e. will either always return a value or never return).
562

563
!!! note
564
    It is permissible for `:nothrow` annotated methods to make use of exception
565
    handling internally as long as the exception is not rethrown out of the
566
    method itself.
567

568
!!! note
569
    `MethodErrors` and similar exceptions count as abnormal termination.
570

571
---
572
## `:terminates_globally`
573

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

577
!!! note
578
    This `:terminates_globally` assertion covers any other methods called by the annotated method.
579

580
!!! note
581
    The compiler will consider this a strong indication that the method will
582
    terminate relatively *quickly* and may (if otherwise legal), call this
583
    method at compile time. I.e. it is a bad idea to annotate this setting
584
    on a method that *technically*, but not *practically*, terminates.
585

586
---
587
## `:terminates_locally`
588

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

594
!!! note
595
    `:terminates_globally` implies `:terminates_locally`.
596

597
---
598
## `:notaskstate`
599

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

604
!!! note
605
    The implementation of exception handling makes use of state stored in the
606
    task object. However, this state is currently not considered to be within
607
    the scope of `:notaskstate` and is tracked separately using the `:nothrow`
608
    effect.
609

610
!!! note
611
    The `:notaskstate` assertion concerns the state of the *currently running task*.
612
    If a reference to a `Task` object is obtained by some other means that
613
    does not consider which task is *currently* running, the `:notaskstate`
614
    effect need not be tainted. This is true, even if said task object happens
615
    to be `===` to the currently running task.
616

617
!!! note
618
    Access to task state usually also results in the tainting of other effects,
619
    such as `:effect_free` (if task state is modified) or `:consistent` (if
620
    task state is used in the computation of the result). In particular,
621
    code that is not `:notaskstate`, but is `:effect_free` and `:consistent`
622
    may still be dead-code-eliminated and thus promoted to `:total`.
623

624
---
625
## `:inaccessiblememonly`
626

627
The `:inaccessiblememonly` setting asserts that the method does not access or modify
628
externally accessible mutable memory. This means the method can access or modify mutable
629
memory for newly allocated objects that is not accessible by other methods or top-level
630
execution before return from the method, but it can not access or modify any mutable
631
global state or mutable memory pointed to by its arguments.
632

633
!!! note
634
    Below is an incomplete list of examples that invalidate this assumption:
635
    - a global reference or `getglobal` call to access a mutable global variable
636
    - a global assignment or `setglobal!` call to perform assignment to a non-constant global variable
637
    - `setfield!` call that changes a field of a global mutable variable
638

639
!!! note
640
    This `:inaccessiblememonly` assertion covers any other methods called by the annotated method.
641

642
---
643
## `:foldable`
644

645
This setting is a convenient shortcut for the set of effects that the compiler
646
requires to be guaranteed to constant fold a call at compile time. It is
647
currently equivalent to the following `setting`s:
648
- `:consistent`
649
- `:effect_free`
650
- `:terminates_globally`
651

652
!!! note
653
    This list in particular does not include `:nothrow`. The compiler will still
654
    attempt constant propagation and note any thrown error at compile time. Note
655
    however, that by the `:consistent`-cy requirements, any such annotated call
656
    must consistently throw given the same argument values.
657

658
!!! note
659
    An explicit `@inbounds` annotation inside the function will also disable
660
    constant folding and not be overriden by `:foldable`.
661

662
---
663
## `:removable`
664

665
This setting is a convenient shortcut for the set of effects that the compiler
666
requires to be guaranteed to delete a call whose result is unused at compile time.
667
It is currently equivalent to the following `setting`s:
668
- `:effect_free`
669
- `:nothrow`
670
- `:terminates_globally`
671

672
---
673
## `:total`
674

675
This `setting` is the maximum possible set of effects. It currently implies
676
the following other `setting`s:
677
- `:consistent`
678
- `:effect_free`
679
- `:nothrow`
680
- `:terminates_globally`
681
- `:notaskstate`
682
- `:inaccessiblememonly`
683

684
!!! warning
685
    `:total` is a very strong assertion and will likely gain additional semantics
686
    in future versions of Julia (e.g. if additional effects are added and included
687
    in the definition of `:total`). As a result, it should be used with care.
688
    Whenever possible, prefer to use the minimum possible set of specific effect
689
    assertions required for a particular application. In cases where a large
690
    number of effect overrides apply to a set of functions, a custom macro is
691
    recommended over the use of `:total`.
692

693
---
694
## Negated effects
695

696
Effect names may be prefixed by `!` to indicate that the effect should be removed
697
from an earlier meta effect. For example, `:total !:nothrow` indicates that while
698
the call is generally total, it may however throw.
699
"""
700
macro assume_effects(args...)
19✔
701
    lastex = args[end]
19✔
702
    inner = unwrap_macrocalls(lastex)
19✔
703
    if is_function_def(inner)
19✔
704
        ex = lastex
15✔
705
        idx = length(args)-1
15✔
706
    elseif isexpr(lastex, :macrocall) && lastex.args[1] === Symbol("@ccall")
4✔
707
        ex = lastex
3✔
708
        idx = length(args)-1
3✔
709
    else # anonymous function case
710
        ex = nothing
1✔
711
        idx = length(args)
1✔
712
    end
713
    (consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly) =
19✔
714
        (false, false, false, false, false, false, false, false)
715
    for org_setting in args[1:idx]
19✔
716
        (setting, val) = compute_assumed_setting(org_setting)
20✔
717
        if setting === :consistent
20✔
718
            consistent = val
×
719
        elseif setting === :effect_free
20✔
720
            effect_free = val
2✔
721
        elseif setting === :nothrow
18✔
722
            nothrow = val
1✔
723
        elseif setting === :terminates_globally
17✔
724
            terminates_globally = val
4✔
725
        elseif setting === :terminates_locally
13✔
726
            terminates_locally = val
2✔
727
        elseif setting === :notaskstate
11✔
728
            notaskstate = val
2✔
729
        elseif setting === :inaccessiblememonly
9✔
730
            inaccessiblememonly = val
×
731
        elseif setting === :foldable
9✔
732
            consistent = effect_free = terminates_globally = val
3✔
733
        elseif setting === :removable
6✔
734
            effect_free = nothrow = terminates_globally = val
2✔
735
        elseif setting === :total
4✔
736
            consistent = effect_free = nothrow = terminates_globally = notaskstate = inaccessiblememonly = val
4✔
737
        else
738
            throw(ArgumentError("@assume_effects $org_setting not supported"))
×
739
        end
740
    end
20✔
741
    if is_function_def(inner)
19✔
742
        return esc(pushmeta!(ex, :purity,
15✔
743
            consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly))
744
    elseif isexpr(ex, :macrocall) && ex.args[1] === Symbol("@ccall")
4✔
745
        ex.args[1] = GlobalRef(Base, Symbol("@ccall_effects"))
3✔
746
        insert!(ex.args, 3, Core.Compiler.encode_effects_override(Core.Compiler.EffectsOverride(
3✔
747
            consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly,
748
        )))
749
        return esc(ex)
3✔
750
    else # anonymous function case
751
        return Expr(:meta, Expr(:purity,
1✔
752
            consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly))
753
    end
754
end
755

756
function compute_assumed_setting(@nospecialize(setting), val::Bool=true)
21✔
757
    if isexpr(setting, :call) && setting.args[1] === :(!)
101✔
758
        return compute_assumed_setting(setting.args[2], !val)
2✔
759
    elseif isa(setting, QuoteNode)
40✔
760
        return compute_assumed_setting(setting.value, val)
20✔
761
    else
762
        return (setting, val)
20✔
763
    end
764
end
765

766
"""
767
    @propagate_inbounds
768

769
Tells the compiler to inline a function while retaining the caller's inbounds context.
770
"""
771
macro propagate_inbounds(ex)
142✔
772
    if isa(ex, Expr)
142✔
773
        pushmeta!(ex, :inline)
142✔
774
        pushmeta!(ex, :propagate_inbounds)
142✔
775
    end
776
    esc(ex)
142✔
777
end
778

779
"""
780
    @polly
781

782
Tells the compiler to apply the polyhedral optimizer Polly to a function.
783
"""
784
macro polly(ex)
785
    esc(isa(ex, Expr) ? pushmeta!(ex, :polly) : ex)
786
end
787

788
## some macro utilities ##
789

790
unwrap_macrocalls(@nospecialize(x)) = x
1✔
791
function unwrap_macrocalls(ex::Expr)
18✔
792
    inner = ex
×
793
    while inner.head === :macrocall
4,615✔
794
        inner = inner.args[end]::Expr
73✔
795
    end
73✔
796
    return inner
4,542✔
797
end
798

799
function pushmeta!(ex::Expr, sym::Symbol, args::Any...)
2,411✔
800
    if isempty(args)
×
801
        tag = sym
×
802
    else
803
        tag = Expr(sym, args...)::Expr
15✔
804
    end
805

806
    inner = unwrap_macrocalls(ex)
2,468✔
807

808
    idx, exargs = findmeta(inner)
2,411✔
809
    if idx != 0
2,411✔
810
        push!(exargs[idx].args, tag)
370✔
811
    else
812
        body = inner.args[2]::Expr
2,226✔
813
        pushfirst!(body.args, Expr(:meta, tag))
2,226✔
814
    end
815
    ex
2,411✔
816
end
817

818
popmeta!(body, sym) = _getmeta(body, sym, true)
×
819
peekmeta(body, sym) = _getmeta(body, sym, false)
×
820

821
function _getmeta(body::Expr, sym::Symbol, delete::Bool)
×
822
    body.head === :block || return false, []
×
823
    _getmeta(body.args, sym, delete)
×
824
end
825
_getmeta(arg, sym, delete::Bool) = (false, [])
×
826
function _getmeta(body::Array{Any,1}, sym::Symbol, delete::Bool)
×
827
    idx, blockargs = findmeta_block(body, args -> findmetaarg(args,sym)!=0)
×
828
    if idx == 0
×
829
        return false, []
×
830
    end
831
    metaargs = blockargs[idx].args
×
832
    i = findmetaarg(blockargs[idx].args, sym)
×
833
    if i == 0
×
834
        return false, []
×
835
    end
836
    ret = isa(metaargs[i], Expr) ? (metaargs[i]::Expr).args : []
×
837
    if delete
×
838
        deleteat!(metaargs, i)
×
839
        isempty(metaargs) && deleteat!(blockargs, idx)
×
840
    end
841
    true, ret
×
842
end
843

844
# Find index of `sym` in a meta expression argument list, or 0.
845
function findmetaarg(metaargs, sym)
×
846
    for i = 1:length(metaargs)
×
847
        arg = metaargs[i]
×
848
        if (isa(arg, Symbol) && (arg::Symbol)    == sym) ||
×
849
           (isa(arg, Expr)   && (arg::Expr).head == sym)
850
            return i
×
851
        end
852
    end
×
853
    return 0
×
854
end
855

856
function annotate_meta_def_or_block(@nospecialize(ex), meta::Symbol)
2,113✔
857
    inner = unwrap_macrocalls(ex)
2,113✔
858
    if is_function_def(inner)
3,375✔
859
        # annotation on a definition
860
        return esc(pushmeta!(ex, meta))
2,079✔
861
    else
862
        # annotation on a block
863
        return Expr(:block,
34✔
864
                    Expr(meta, true),
865
                    Expr(:local, Expr(:(=), :val, esc(ex))),
866
                    Expr(meta, false),
867
                    :val)
868
    end
869
end
870

871
function is_short_function_def(@nospecialize(ex))
2,722✔
872
    isexpr(ex, :(=)) || return false
2,771✔
873
    while length(ex.args) >= 1 && isa(ex.args[1], Expr)
6,160✔
874
        (ex.args[1].head === :call) && return true
6,158✔
875
        (ex.args[1].head === :where || ex.args[1].head === :(::)) || return false
415✔
876
        ex = ex.args[1]
814✔
877
    end
407✔
878
    return false
1✔
879
end
880
is_function_def(@nospecialize(ex)) =
7,242✔
881
    return isexpr(ex, :function) || is_short_function_def(ex) || isexpr(ex, :->)
882

883
function findmeta(ex::Expr)
2,411✔
884
    if is_function_def(ex)
3,829✔
885
        body = ex.args[2]::Expr
2,411✔
886
        body.head === :block || error(body, " is not a block expression")
2,411✔
887
        return findmeta_block(ex.args)
2,411✔
888
    end
889
    error(ex, " is not a function expression")
×
890
end
891

892
findmeta(ex::Array{Any,1}) = findmeta_block(ex)
×
893

894
function findmeta_block(exargs, argsmatch=args->true)
2,411✔
895
    for i = 1:length(exargs)
16,874✔
896
        a = exargs[i]
13,374✔
897
        if isa(a, Expr)
13,374✔
898
            if a.head === :meta && argsmatch(a.args)
8,602✔
899
                return i, exargs
185✔
900
            elseif a.head === :block
8,417✔
901
                idx, exa = findmeta_block(a.args, argsmatch)
2,411✔
902
                if idx != 0
2,411✔
903
                    return idx, exa
185✔
904
                end
905
            end
906
        end
907
    end
21,556✔
908
    return 0, []
4,452✔
909
end
910

911
remove_linenums!(ex) = ex
48✔
912
function remove_linenums!(ex::Expr)
962,794✔
913
    if ex.head === :block || ex.head === :quote
1,772,557✔
914
        # remove line number expressions from metadata (not argument literal or inert) position
915
        filter!(ex.args) do x
154,650✔
916
            isa(x, Expr) && x.head === :line && return false
419,300✔
917
            isa(x, LineNumberNode) && return false
419,300✔
918
            return true
210,576✔
919
        end
920
    end
921
    for subex in ex.args
965,044✔
922
        subex isa Expr && remove_linenums!(subex)
2,239,930✔
923
    end
3,200,474✔
924
    return ex
962,794✔
925
end
926
function remove_linenums!(src::CodeInfo)
861✔
927
    src.codelocs .= 0
45,029✔
928
    length(src.linetable) > 1 && resize!(src.linetable, 1)
1,391✔
929
    return src
861✔
930
end
931

932
replace_linenums!(ex, ln::LineNumberNode) = ex
×
933
function replace_linenums!(ex::Expr, ln::LineNumberNode)
1,712✔
934
    if ex.head === :block || ex.head === :quote
2,996✔
935
        # replace line number expressions from metadata (not argument literal or inert) position
936
        map!(ex.args, ex.args) do @nospecialize(x)
856✔
937
            isa(x, Expr) && x.head === :line && length(x.args) == 1 && return Expr(:line, ln.line)
856✔
938
            isa(x, Expr) && x.head === :line && length(x.args) == 2 && return Expr(:line, ln.line, ln.file)
856✔
939
            isa(x, LineNumberNode) && return ln
856✔
940
            return x
428✔
941
        end
942
    end
943
    # preserve any linenums inside `esc(...)` guards
944
    if ex.head !== :escape
1,712✔
945
        for subex in ex.args
1,712✔
946
            subex isa Expr && replace_linenums!(subex, ln)
1,712✔
947
        end
2,568✔
948
    end
949
    return ex
1,712✔
950
end
951

952
macro generated()
2✔
953
    return Expr(:generated)
2✔
954
end
955

956
"""
957
    @generated f
958

959
`@generated` is used to annotate a function which will be generated.
960
In the body of the generated function, only types of arguments can be read
961
(not the values). The function returns a quoted expression evaluated when the
962
function is called. The `@generated` macro should not be used on functions mutating
963
the global scope or depending on mutable elements.
964

965
See [Metaprogramming](@ref) for further details.
966

967
# Examples
968
```jldoctest
969
julia> @generated function bar(x)
970
           if x <: Integer
971
               return :(x ^ 2)
972
           else
973
               return :(x)
974
           end
975
       end
976
bar (generic function with 1 method)
977

978
julia> bar(4)
979
16
980

981
julia> bar("baz")
982
"baz"
983
```
984
"""
985
macro generated(f)
47✔
986
    if isa(f, Expr) && (f.head === :function || is_short_function_def(f))
63✔
987
        body = f.args[2]
47✔
988
        lno = body.args[1]
47✔
989
        tmp = gensym("tmp")
47✔
990
        return Expr(:escape,
47✔
991
                    Expr(f.head, f.args[1],
992
                         Expr(:block,
993
                              lno,
994
                              Expr(:if, Expr(:generated),
995
                                   body,
996
                                   Expr(:block,
997
                                        Expr(:meta, :generated_only),
998
                                        Expr(:return, nothing))))))
999
    else
1000
        error("invalid syntax; @generated must be used with a function definition")
×
1001
    end
1002
end
1003

1004

1005
"""
1006
    @atomic var
1007
    @atomic order ex
1008

1009
Mark `var` or `ex` as being performed atomically, if `ex` is a supported expression.
1010

1011
    @atomic a.b.x = new
1012
    @atomic a.b.x += addend
1013
    @atomic :release a.b.x = new
1014
    @atomic :acquire_release a.b.x += addend
1015

1016
Perform the store operation expressed on the right atomically and return the
1017
new value.
1018

1019
With `=`, this operation translates to a `setproperty!(a.b, :x, new)` call.
1020
With any operator also, this operation translates to a `modifyproperty!(a.b,
1021
:x, +, addend)[2]` call.
1022

1023
    @atomic a.b.x max arg2
1024
    @atomic a.b.x + arg2
1025
    @atomic max(a.b.x, arg2)
1026
    @atomic :acquire_release max(a.b.x, arg2)
1027
    @atomic :acquire_release a.b.x + arg2
1028
    @atomic :acquire_release a.b.x max arg2
1029

1030
Perform the binary operation expressed on the right atomically. Store the
1031
result into the field in the first argument and return the values `(old, new)`.
1032

1033
This operation translates to a `modifyproperty!(a.b, :x, func, arg2)` call.
1034

1035

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

1038
# Examples
1039
```jldoctest
1040
julia> mutable struct Atomic{T}; @atomic x::T; end
1041

1042
julia> a = Atomic(1)
1043
Atomic{Int64}(1)
1044

1045
julia> @atomic a.x # fetch field x of a, with sequential consistency
1046
1
1047

1048
julia> @atomic :sequentially_consistent a.x = 2 # set field x of a, with sequential consistency
1049
2
1050

1051
julia> @atomic a.x += 1 # increment field x of a, with sequential consistency
1052
3
1053

1054
julia> @atomic a.x + 1 # increment field x of a, with sequential consistency
1055
3 => 4
1056

1057
julia> @atomic a.x # fetch field x of a, with sequential consistency
1058
4
1059

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

1063
julia> @atomic a.x max 5 # again change field x of a to the max value, with sequential consistency
1064
10 => 10
1065
```
1066

1067
!!! compat "Julia 1.7"
1068
    This functionality requires at least Julia 1.7.
1069
"""
1070
macro atomic(ex)
57✔
1071
    if !isa(ex, Symbol) && !is_expr(ex, :(::))
57✔
1072
        return make_atomic(QuoteNode(:sequentially_consistent), ex)
45✔
1073
    end
1074
    return esc(Expr(:atomic, ex))
12✔
1075
end
1076
macro atomic(order, ex)
27✔
1077
    order isa QuoteNode || (order = esc(order))
27✔
1078
    return make_atomic(order, ex)
27✔
1079
end
1080
macro atomic(a1, op, a2)
3✔
1081
    return make_atomic(QuoteNode(:sequentially_consistent), a1, op, a2)
3✔
1082
end
1083
macro atomic(order, a1, op, a2)
2✔
1084
    order isa QuoteNode || (order = esc(order))
2✔
1085
    return make_atomic(order, a1, op, a2)
2✔
1086
end
1087
function make_atomic(order, ex)
58✔
1088
    @nospecialize
×
1089
    if ex isa Expr
58✔
1090
        if isexpr(ex, :., 2)
89✔
1091
            l, r = esc(ex.args[1]), esc(ex.args[2])
27✔
1092
            return :(getproperty($l, $r, $order))
27✔
1093
        elseif isexpr(ex, :call, 3)
53✔
1094
            return make_atomic(order, ex.args[2], ex.args[1], ex.args[3])
5✔
1095
        elseif ex.head === :(=)
26✔
1096
            l, r = ex.args[1], esc(ex.args[2])
15✔
1097
            if is_expr(l, :., 2)
15✔
1098
                ll, lr = esc(l.args[1]), esc(l.args[2])
14✔
1099
                return :(setproperty!($ll, $lr, $r, $order))
14✔
1100
            end
1101
        end
1102
        if length(ex.args) == 2
12✔
1103
            if ex.head === :(+=)
9✔
1104
                op = :+
5✔
1105
            elseif ex.head === :(-=)
4✔
1106
                op = :-
1✔
1107
            elseif @isdefined string
3✔
1108
                shead = string(ex.head)
3✔
1109
                if endswith(shead, '=')
3✔
1110
                    op = Symbol(shead[1:prevind(shead, end)])
2✔
1111
                end
1112
            end
1113
            if @isdefined(op)
9✔
1114
                return Expr(:ref, make_atomic(order, ex.args[1], op, ex.args[2]), 2)
8✔
1115
            end
1116
        end
1117
    end
1118
    error("could not parse @atomic expression $ex")
4✔
1119
end
1120
function make_atomic(order, a1, op, a2)
18✔
1121
    @nospecialize
×
1122
    is_expr(a1, :., 2) || error("@atomic modify expression missing field access")
22✔
1123
    a1l, a1r, op, a2 = esc(a1.args[1]), esc(a1.args[2]), esc(op), esc(a2)
14✔
1124
    return :(modifyproperty!($a1l, $a1r, $op, $a2, $order))
14✔
1125
end
1126

1127

1128
"""
1129
    @atomicswap a.b.x = new
1130
    @atomicswap :sequentially_consistent a.b.x = new
1131

1132
Stores `new` into `a.b.x` and returns the old value of `a.b.x`.
1133

1134
This operation translates to a `swapproperty!(a.b, :x, new)` call.
1135

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

1138
# Examples
1139
```jldoctest
1140
julia> mutable struct Atomic{T}; @atomic x::T; end
1141

1142
julia> a = Atomic(1)
1143
Atomic{Int64}(1)
1144

1145
julia> @atomicswap a.x = 2+2 # replace field x of a with 4, with sequential consistency
1146
1
1147

1148
julia> @atomic a.x # fetch field x of a, with sequential consistency
1149
4
1150
```
1151

1152
!!! compat "Julia 1.7"
1153
    This functionality requires at least Julia 1.7.
1154
"""
1155
macro atomicswap(order, ex)
2✔
1156
    order isa QuoteNode || (order = esc(order))
2✔
1157
    return make_atomicswap(order, ex)
2✔
1158
end
1159
macro atomicswap(ex)
1✔
1160
    return make_atomicswap(QuoteNode(:sequentially_consistent), ex)
1✔
1161
end
1162
function make_atomicswap(order, ex)
3✔
1163
    @nospecialize
×
1164
    is_expr(ex, :(=), 2) || error("@atomicswap expression missing assignment")
3✔
1165
    l, val = ex.args[1], esc(ex.args[2])
3✔
1166
    is_expr(l, :., 2) || error("@atomicswap expression missing field access")
3✔
1167
    ll, lr = esc(l.args[1]), esc(l.args[2])
3✔
1168
    return :(swapproperty!($ll, $lr, $val, $order))
3✔
1169
end
1170

1171

1172
"""
1173
    @atomicreplace a.b.x expected => desired
1174
    @atomicreplace :sequentially_consistent a.b.x expected => desired
1175
    @atomicreplace :sequentially_consistent :monotonic a.b.x expected => desired
1176

1177
Perform the conditional replacement expressed by the pair atomically, returning
1178
the values `(old, success::Bool)`. Where `success` indicates whether the
1179
replacement was completed.
1180

1181
This operation translates to a `replaceproperty!(a.b, :x, expected, desired)` call.
1182

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

1185
# Examples
1186
```jldoctest
1187
julia> mutable struct Atomic{T}; @atomic x::T; end
1188

1189
julia> a = Atomic(1)
1190
Atomic{Int64}(1)
1191

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

1195
julia> @atomic a.x # fetch field x of a, with sequential consistency
1196
2
1197

1198
julia> @atomicreplace a.x 1 => 2 # replace field x of a with 2 if it was 1, with sequential consistency
1199
(old = 2, success = false)
1200

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

1203
julia> @atomicreplace a.x xchg
1204
(old = 2, success = true)
1205

1206
julia> @atomic a.x # fetch field x of a, with sequential consistency
1207
0
1208
```
1209

1210
!!! compat "Julia 1.7"
1211
    This functionality requires at least Julia 1.7.
1212
"""
1213
macro atomicreplace(success_order, fail_order, ex, old_new)
4✔
1214
    fail_order isa QuoteNode || (fail_order = esc(fail_order))
4✔
1215
    success_order isa QuoteNode || (success_order = esc(success_order))
4✔
1216
    return make_atomicreplace(success_order, fail_order, ex, old_new)
4✔
1217
end
1218
macro atomicreplace(order, ex, old_new)
4✔
1219
    order isa QuoteNode || (order = esc(order))
4✔
1220
    return make_atomicreplace(order, order, ex, old_new)
4✔
1221
end
1222
macro atomicreplace(ex, old_new)
12✔
1223
    return make_atomicreplace(QuoteNode(:sequentially_consistent), QuoteNode(:sequentially_consistent), ex, old_new)
12✔
1224
end
1225
function make_atomicreplace(success_order, fail_order, ex, old_new)
17✔
1226
    @nospecialize
×
1227
    is_expr(ex, :., 2) || error("@atomicreplace expression missing field access")
18✔
1228
    ll, lr = esc(ex.args[1]), esc(ex.args[2])
16✔
1229
    if is_expr(old_new, :call, 3) && old_new.args[1] === :(=>)
16✔
1230
        exp, rep = esc(old_new.args[2]), esc(old_new.args[3])
11✔
1231
        return :(replaceproperty!($ll, $lr, $exp, $rep, $success_order, $fail_order))
11✔
1232
    else
1233
        old_new = esc(old_new)
5✔
1234
        return :(replaceproperty!($ll, $lr, $old_new::Pair..., $success_order, $fail_order))
5✔
1235
    end
1236
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