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

JuliaLang / julia / #37435

pending completion
#37435

push

local

web-flow
add test cases for #48566 (#48584)

71489 of 82040 relevant lines covered (87.14%)

31307762.66 hits per line

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

85.71
/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)
2,017✔
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}, ())
482✔
15

16
gensym(s::String) = ccall(:jl_tagged_gensym, Ref{Symbol}, (Ptr{UInt8}, Csize_t), s, sizeof(s))
244✔
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
4,644,500,109✔
39
isexpr(@nospecialize(ex), head::Symbol, n::Int) = isa(ex, Expr) && ex.head === head && length(ex.args) == n
51,343✔
40

41
copy(e::Expr) = exprarray(e.head, copy_exprargs(e.args))
91,962,013✔
42

43
# copy parts of an AST that the compiler mutates
44
function copy_exprs(@nospecialize(x))
295,889,538✔
45
    if isa(x, Expr)
295,889,538✔
46
        return copy(x)
91,949,392✔
47
    elseif isa(x, PhiNode)
203,940,146✔
48
        values = x.values
1,747,247✔
49
        nvalues = length(values)
1,747,247✔
50
        new_values = Vector{Any}(undef, nvalues)
1,747,247✔
51
        @inbounds for i = 1:nvalues
3,490,913✔
52
            isassigned(values, i) || continue
3,305,236✔
53
            new_values[i] = copy_exprs(values[i])
3,305,221✔
54
        end
4,866,806✔
55
        return PhiNode(copy(x.edges), new_values)
1,747,247✔
56
    elseif isa(x, PhiCNode)
202,192,899✔
57
        values = x.values
23✔
58
        nvalues = length(values)
23✔
59
        new_values = Vector{Any}(undef, nvalues)
23✔
60
        @inbounds for i = 1:nvalues
46✔
61
            isassigned(values, i) || continue
30✔
62
            new_values[i] = copy_exprs(values[i])
30✔
63
        end
37✔
64
        return PhiCNode(new_values)
23✔
65
    end
66
    return x
202,192,876✔
67
end
68
copy_exprargs(x::Array{Any,1}) = Any[copy_exprs(@inbounds x[i]) for i in 1:length(x)]
100,635,676✔
69

70
@eval exprarray(head::Symbol, arg::Array{Any,1}) = $(Expr(:new, :Expr, :head, :arg))
91,963,980✔
71

72
# create copies of the CodeInfo definition, and any mutable fields
73
function copy(c::CodeInfo)
5,035,948✔
74
    cnew = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), c)
5,035,948✔
75
    cnew.code = copy_exprargs(cnew.code)
5,035,948✔
76
    cnew.slotnames = copy(cnew.slotnames)
5,035,948✔
77
    cnew.slotflags = copy(cnew.slotflags)
5,035,948✔
78
    cnew.codelocs  = copy(cnew.codelocs)
5,035,948✔
79
    cnew.linetable = copy(cnew.linetable::Union{Vector{Any},Vector{Core.LineInfoNode}})
10,071,245✔
80
    cnew.ssaflags  = copy(cnew.ssaflags)
5,035,948✔
81
    cnew.edges     = cnew.edges === nothing ? nothing : copy(cnew.edges::Vector)
5,035,949✔
82
    ssavaluetypes  = cnew.ssavaluetypes
5,035,948✔
83
    ssavaluetypes isa Vector{Any} && (cnew.ssavaluetypes = copy(ssavaluetypes))
5,035,948✔
84
    return cnew
5,035,948✔
85
end
86

87

88
==(x::Expr, y::Expr) = x.head === y.head && isequal(x.args, y.args)
13,571✔
89
==(x::QuoteNode, y::QuoteNode) = isequal(x.value, y.value)
1,528✔
90
==(stmt1::Core.PhiNode, stmt2::Core.PhiNode) = stmt1.edges == stmt2.edges && stmt1.values == stmt2.values
×
91

92
"""
93
    macroexpand(m::Module, x; recursive=true)
94

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

110
julia> macroexpand(M, :(@m2()), recursive=true)
111
42
112

113
julia> macroexpand(M, :(@m2()), recursive=false)
114
:(#= REPL[16]:6 =# M.@m1)
115
```
116
"""
117
function macroexpand(m::Module, @nospecialize(x); recursive=true)
424✔
118
    if recursive
103✔
119
        ccall(:jl_macroexpand, Any, (Any, Any), x, m)
261✔
120
    else
121
        ccall(:jl_macroexpand1, Any, (Any, Any), x, m)
60✔
122
    end
123
end
124

125
"""
126
    @macroexpand
127

128
Return equivalent expression with all macros removed (expanded).
129

130
There are differences between `@macroexpand` and [`macroexpand`](@ref).
131

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

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

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

153
julia> macro m()
154
           2
155
       end
156
@m (macro with 1 method)
157

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

168

169
"""
170
    @macroexpand1
171

172
Non recursive version of [`@macroexpand`](@ref).
173
"""
174
macro macroexpand1(code)
1✔
175
    return :(macroexpand($__module__, $(QuoteNode(code)), recursive=false))
1✔
176
end
177

178
## misc syntax ##
179

180
"""
181
    Core.eval(m::Module, expr)
182

183
Evaluate an expression in the given module and return the result.
184
"""
185
Core.eval
186

187
"""
188
    @inline
189

190
Give a hint to the compiler that this function is worth inlining.
191

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

196
`@inline` can be applied immediately before a function definition or within a function body.
197

198
```julia
199
# annotate long-form definition
200
@inline function longdef(x)
201
    ...
202
end
203

204
# annotate short-form definition
205
@inline shortdef(x) = ...
206

207
# annotate anonymous function that a `do` block creates
208
f() do
209
    @inline
210
    ...
211
end
212
```
213

214
!!! compat "Julia 1.8"
215
    The usage within a function body requires at least Julia 1.8.
216

217
---
218
    @inline block
219

220
Give a hint to the compiler that calls within `block` are worth inlining.
221

222
```julia
223
# The compiler will try to inline `f`
224
@inline f(...)
225

226
# The compiler will try to inline `f`, `g` and `+`
227
@inline f(...) + g(...)
228
```
229

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

238
    let
239
        @inline explicit_noinline(args...) # will be inlined
240
    end
241
    ```
242

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

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

258
!!! compat "Julia 1.8"
259
    The callsite annotation requires at least Julia 1.8.
260
"""
261
macro inline(x)
1,932✔
262
    return annotate_meta_def_or_block(x, :inline)
1,932✔
263
end
264

265
"""
266
    @noinline
267

268
Give a hint to the compiler that it should not inline a function.
269

270
Small functions are typically inlined automatically.
271
By using `@noinline` on small functions, auto-inlining can be
272
prevented.
273

274
`@noinline` can be applied immediately before a function definition or within a function body.
275

276
```julia
277
# annotate long-form definition
278
@noinline function longdef(x)
279
    ...
280
end
281

282
# annotate short-form definition
283
@noinline shortdef(x) = ...
284

285
# annotate anonymous function that a `do` block creates
286
f() do
287
    @noinline
288
    ...
289
end
290
```
291

292
!!! compat "Julia 1.8"
293
    The usage within a function body requires at least Julia 1.8.
294

295
---
296
    @noinline block
297

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

300
```julia
301
# The compiler will try to not inline `f`
302
@noinline f(...)
303

304
# The compiler will try to not inline `f`, `g` and `+`
305
@noinline f(...) + g(...)
306
```
307

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

316
    let
317
        @noinline explicit_inline(args...) # will not be inlined
318
    end
319
    ```
320

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

331
!!! compat "Julia 1.8"
332
    The callsite annotation requires at least Julia 1.8.
333

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

342
"""
343
    @pure ex
344

345
`@pure` gives the compiler a hint for the definition of a pure function,
346
helping for type inference.
347

348
!!! warning
349
    This macro is intended for internal compiler use and may be subject to changes.
350

351
!!! warning
352
    In Julia 1.8 and higher, it is favorable to use [`@assume_effects`](@ref) instead of `@pure`.
353
    This is because `@assume_effects` allows a finer grained control over Julia's purity
354
    modeling and the effect system enables a wider range of optimizations.
355
"""
356
macro pure(ex)
10✔
357
    esc(isa(ex, Expr) ? pushmeta!(ex, :pure) : ex)
10✔
358
end
359

360
"""
361
    @constprop setting [ex]
362

363
Control the mode of interprocedural constant propagation for the annotated function.
364

365
Two `setting`s are supported:
366

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

374
`@constprop` can be applied immediately before a function definition or within a function body.
375

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

382
# annotate short-form definition
383
@constprop :aggressive shortdef(x) = ...
384

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

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

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

415
"""
416
    @assume_effects setting... [ex]
417

418
Override the compiler's effect modeling for the given method or foreign call.
419
`@assume_effects` can be applied immediately before a function definition or within a function body.
420
It can also be applied immediately before a `@ccall` expression.
421

422
!!! compat "Julia 1.8"
423
    Using `Base.@assume_effects` requires Julia version 1.8.
424

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

439
julia> code_typed() do
440
           pow(12)
441
       end
442
1-element Vector{Any}:
443
 CodeInfo(
444
1 ─     return 479001600
445
) => Int64
446

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

465
julia> Base.@assume_effects :total !:nothrow @ccall jl_type_intersection(Vector{Int}::Any, Vector{<:Integer}::Any)::Any
466
Vector{Int64} (alias for Array{Int64, 1})
467
```
468

469
!!! compat "Julia 1.10"
470
  The usage within a function body requires at least Julia 1.10.
471

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

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

485
The following `setting`s are supported.
486
- `:consistent`
487
- `:effect_free`
488
- `:nothrow`
489
- `:terminates_globally`
490
- `:terminates_locally`
491
- `:notaskstate`
492
- `:inaccessiblememonly`
493
- `:foldable`
494
- `:removable`
495
- `:total`
496

497
# Extended help
498

499
---
500
## `:consistent`
501

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

506
!!! note
507
    This in particular implies that the method must not return a freshly allocated
508
    mutable object. Multiple allocations of mutable objects (even with identical
509
    contents) are not egal.
510

511
!!! note
512
    The `:consistent`-cy assertion is made world-age wise. More formally, write
513
    ``fáµ¢`` for the evaluation of ``f`` in world-age ``i``, then we require:
514
    ```math
515
    ∀ i, x, y: x ≡ y → fᵢ(x) ≡ fᵢ(y)
516
    ```
517
    However, for two world ages ``i``, ``j`` s.t. ``i ≠ j``, we may have ``fᵢ(x) ≢ fⱼ(y)``.
518

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

523
!!! note
524
    The `:consistent`-cy includes all legal rewrites performed by the optimizer.
525
    For example, floating-point fastmath operations are not considered `:consistent`,
526
    because the optimizer may rewrite them causing the output to not be `:consistent`,
527
    even for the same world age (e.g. because one ran in the interpreter, while
528
    the other was optimized).
529

530
!!! note
531
    If `:consistent` functions terminate by throwing an exception, that exception
532
    itself is not required to meet the egality requirement specified above.
533

534
---
535
## `:effect_free`
536

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

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

555
The rule of thumb here is that an externally visible side effect is anything
556
that would affect the execution of the remainder of the program if the function
557
were not executed.
558

559
!!! note
560
    The `:effect_free` assertion is made both for the method itself and any code
561
    that is executed by the method. Keep in mind that the assertion must be
562
    valid for all world ages and limit use of this assertion accordingly.
563

564
---
565
## `:nothrow`
566

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

570
!!! note
571
    It is permissible for `:nothrow` annotated methods to make use of exception
572
    handling internally as long as the exception is not rethrown out of the
573
    method itself.
574

575
!!! note
576
    `MethodErrors` and similar exceptions count as abnormal termination.
577

578
---
579
## `:terminates_globally`
580

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

584
!!! note
585
    This `:terminates_globally` assertion covers any other methods called by the annotated method.
586

587
!!! note
588
    The compiler will consider this a strong indication that the method will
589
    terminate relatively *quickly* and may (if otherwise legal), call this
590
    method at compile time. I.e. it is a bad idea to annotate this setting
591
    on a method that *technically*, but not *practically*, terminates.
592

593
---
594
## `:terminates_locally`
595

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

601
!!! note
602
    `:terminates_globally` implies `:terminates_locally`.
603

604
---
605
## `:notaskstate`
606

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

611
!!! note
612
    The implementation of exception handling makes use of state stored in the
613
    task object. However, this state is currently not considered to be within
614
    the scope of `:notaskstate` and is tracked separately using the `:nothrow`
615
    effect.
616

617
!!! note
618
    The `:notaskstate` assertion concerns the state of the *currently running task*.
619
    If a reference to a `Task` object is obtained by some other means that
620
    does not consider which task is *currently* running, the `:notaskstate`
621
    effect need not be tainted. This is true, even if said task object happens
622
    to be `===` to the currently running task.
623

624
!!! note
625
    Access to task state usually also results in the tainting of other effects,
626
    such as `:effect_free` (if task state is modified) or `:consistent` (if
627
    task state is used in the computation of the result). In particular,
628
    code that is not `:notaskstate`, but is `:effect_free` and `:consistent`
629
    may still be dead-code-eliminated and thus promoted to `:total`.
630

631
---
632
## `:inaccessiblememonly`
633

634
The `:inaccessiblememonly` setting asserts that the method does not access or modify
635
externally accessible mutable memory. This means the method can access or modify mutable
636
memory for newly allocated objects that is not accessible by other methods or top-level
637
execution before return from the method, but it can not access or modify any mutable
638
global state or mutable memory pointed to by its arguments.
639

640
!!! note
641
    Below is an incomplete list of examples that invalidate this assumption:
642
    - a global reference or `getglobal` call to access a mutable global variable
643
    - a global assignment or `setglobal!` call to perform assignment to a non-constant global variable
644
    - `setfield!` call that changes a field of a global mutable variable
645

646
!!! note
647
    This `:inaccessiblememonly` assertion covers any other methods called by the annotated method.
648

649
---
650
## `:foldable`
651

652
This setting is a convenient shortcut for the set of effects that the compiler
653
requires to be guaranteed to constant fold a call at compile time. It is
654
currently equivalent to the following `setting`s:
655
- `:consistent`
656
- `:effect_free`
657
- `:terminates_globally`
658

659
!!! note
660
    This list in particular does not include `:nothrow`. The compiler will still
661
    attempt constant propagation and note any thrown error at compile time. Note
662
    however, that by the `:consistent`-cy requirements, any such annotated call
663
    must consistently throw given the same argument values.
664

665
!!! note
666
    An explicit `@inbounds` annotation inside the function will also disable
667
    constant folding and not be overriden by `:foldable`.
668

669
---
670
## `:removable`
671

672
This setting is a convenient shortcut for the set of effects that the compiler
673
requires to be guaranteed to delete a call whose result is unused at compile time.
674
It is currently equivalent to the following `setting`s:
675
- `:effect_free`
676
- `:nothrow`
677
- `:terminates_globally`
678

679
---
680
## `:total`
681

682
This `setting` is the maximum possible set of effects. It currently implies
683
the following other `setting`s:
684
- `:consistent`
685
- `:effect_free`
686
- `:nothrow`
687
- `:terminates_globally`
688
- `:notaskstate`
689
- `:inaccessiblememonly`
690

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

700
---
701
## Negated effects
702

703
Effect names may be prefixed by `!` to indicate that the effect should be removed
704
from an earlier meta effect. For example, `:total !:nothrow` indicates that while
705
the call is generally total, it may however throw.
706

707
---
708
## Comparison to `@pure`
709

710
`@assume_effects :foldable` is similar to [`@pure`](@ref) with the primary
711
distinction that the `:consistent`-cy requirement applies world-age wise rather
712
than globally as described above. However, in particular, a method annotated
713
`@pure` should always be at least `:foldable`.
714
Another advantage is that effects introduced by `@assume_effects` are propagated to
715
callers interprocedurally while a purity defined by `@pure` is not.
716
"""
717
macro assume_effects(args...)
18✔
718
    lastex = args[end]
18✔
719
    inner = unwrap_macrocalls(lastex)
18✔
720
    if is_function_def(inner)
18✔
721
        ex = lastex
14✔
722
        idx = length(args)-1
14✔
723
    elseif isexpr(lastex, :macrocall) && lastex.args[1] === Symbol("@ccall")
4✔
724
        ex = lastex
3✔
725
        idx = length(args)-1
3✔
726
    else # anonymous function case
727
        ex = nothing
1✔
728
        idx = length(args)
1✔
729
    end
730
    (consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly) =
18✔
731
        (false, false, false, false, false, false, false, false)
732
    for org_setting in args[1:idx]
18✔
733
        (setting, val) = compute_assumed_setting(org_setting)
19✔
734
        if setting === :consistent
19✔
735
            consistent = val
×
736
        elseif setting === :effect_free
19✔
737
            effect_free = val
2✔
738
        elseif setting === :nothrow
17✔
739
            nothrow = val
1✔
740
        elseif setting === :terminates_globally
16✔
741
            terminates_globally = val
4✔
742
        elseif setting === :terminates_locally
12✔
743
            terminates_locally = val
2✔
744
        elseif setting === :notaskstate
10✔
745
            notaskstate = val
2✔
746
        elseif setting === :inaccessiblememonly
8✔
747
            inaccessiblememonly = val
×
748
        elseif setting === :foldable
8✔
749
            consistent = effect_free = terminates_globally = val
2✔
750
        elseif setting === :removable
6✔
751
            effect_free = nothrow = terminates_globally = val
2✔
752
        elseif setting === :total
4✔
753
            consistent = effect_free = nothrow = terminates_globally = notaskstate = inaccessiblememonly = val
4✔
754
        else
755
            throw(ArgumentError("@assume_effects $org_setting not supported"))
×
756
        end
757
    end
19✔
758
    if is_function_def(inner)
18✔
759
        return esc(pushmeta!(ex, :purity,
14✔
760
            consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly))
761
    elseif isexpr(ex, :macrocall) && ex.args[1] === Symbol("@ccall")
4✔
762
        ex.args[1] = GlobalRef(Base, Symbol("@ccall_effects"))
3✔
763
        insert!(ex.args, 3, Core.Compiler.encode_effects_override(Core.Compiler.EffectsOverride(
3✔
764
            consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly,
765
        )))
766
        return esc(ex)
3✔
767
    else # anonymous function case
768
        return Expr(:meta, Expr(:purity,
1✔
769
            consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly))
770
    end
771
end
772

773
function compute_assumed_setting(@nospecialize(setting), val::Bool=true)
20✔
774
    if isexpr(setting, :call) && setting.args[1] === :(!)
96✔
775
        return compute_assumed_setting(setting.args[2], !val)
2✔
776
    elseif isa(setting, QuoteNode)
38✔
777
        return compute_assumed_setting(setting.value, val)
19✔
778
    else
779
        return (setting, val)
19✔
780
    end
781
end
782

783
"""
784
    @propagate_inbounds
785

786
Tells the compiler to inline a function while retaining the caller's inbounds context.
787
"""
788
macro propagate_inbounds(ex)
138✔
789
    if isa(ex, Expr)
138✔
790
        pushmeta!(ex, :inline)
138✔
791
        pushmeta!(ex, :propagate_inbounds)
138✔
792
    end
793
    esc(ex)
138✔
794
end
795

796
"""
797
    @polly
798

799
Tells the compiler to apply the polyhedral optimizer Polly to a function.
800
"""
801
macro polly(ex)
802
    esc(isa(ex, Expr) ? pushmeta!(ex, :polly) : ex)
803
end
804

805
## some macro utilities ##
806

807
unwrap_macrocalls(@nospecialize(x)) = x
1✔
808
function unwrap_macrocalls(ex::Expr)
17✔
809
    inner = ex
×
810
    while inner.head === :macrocall
4,715✔
811
        inner = inner.args[end]::Expr
47✔
812
    end
47✔
813
    return inner
4,668✔
814
end
815

816
function pushmeta!(ex::Expr, sym::Symbol, args::Any...)
2,476✔
817
    if isempty(args)
×
818
        tag = sym
×
819
    else
820
        tag = Expr(sym, args...)::Expr
14✔
821
    end
822

823
    inner = unwrap_macrocalls(ex)
2,507✔
824

825
    idx, exargs = findmeta(inner)
2,476✔
826
    if idx != 0
2,476✔
827
        push!(exargs[idx].args, tag)
336✔
828
    else
829
        body = inner.args[2]::Expr
2,308✔
830
        pushfirst!(body.args, Expr(:meta, tag))
2,308✔
831
    end
832
    ex
2,476✔
833
end
834

835
popmeta!(body, sym) = _getmeta(body, sym, true)
×
836
peekmeta(body, sym) = _getmeta(body, sym, false)
×
837

838
function _getmeta(body::Expr, sym::Symbol, delete::Bool)
×
839
    body.head === :block || return false, []
×
840
    _getmeta(body.args, sym, delete)
×
841
end
842
_getmeta(arg, sym, delete::Bool) = (false, [])
×
843
function _getmeta(body::Array{Any,1}, sym::Symbol, delete::Bool)
×
844
    idx, blockargs = findmeta_block(body, args -> findmetaarg(args,sym)!=0)
×
845
    if idx == 0
×
846
        return false, []
×
847
    end
848
    metaargs = blockargs[idx].args
×
849
    i = findmetaarg(blockargs[idx].args, sym)
×
850
    if i == 0
×
851
        return false, []
×
852
    end
853
    ret = isa(metaargs[i], Expr) ? (metaargs[i]::Expr).args : []
×
854
    if delete
×
855
        deleteat!(metaargs, i)
×
856
        isempty(metaargs) && deleteat!(blockargs, idx)
×
857
    end
858
    true, ret
×
859
end
860

861
# Find index of `sym` in a meta expression argument list, or 0.
862
function findmetaarg(metaargs, sym)
×
863
    for i = 1:length(metaargs)
×
864
        arg = metaargs[i]
×
865
        if (isa(arg, Symbol) && (arg::Symbol)    == sym) ||
×
866
           (isa(arg, Expr)   && (arg::Expr).head == sym)
867
            return i
×
868
        end
869
    end
×
870
    return 0
×
871
end
872

873
function annotate_meta_def_or_block(@nospecialize(ex), meta::Symbol)
2,175✔
874
    inner = unwrap_macrocalls(ex)
2,175✔
875
    if is_function_def(inner)
3,438✔
876
        # annotation on a definition
877
        return esc(pushmeta!(ex, meta))
2,145✔
878
    else
879
        # annotation on a block
880
        return Expr(:block,
30✔
881
                    Expr(meta, true),
882
                    Expr(:local, Expr(:(=), :val, esc(ex))),
883
                    Expr(meta, false),
884
                    :val)
885
    end
886
end
887

888
function is_short_function_def(@nospecialize(ex))
2,717✔
889
    isexpr(ex, :(=)) || return false
2,762✔
890
    while length(ex.args) >= 1 && isa(ex.args[1], Expr)
6,200✔
891
        (ex.args[1].head === :call) && return true
6,198✔
892
        (ex.args[1].head === :where || ex.args[1].head === :(::)) || return false
436✔
893
        ex = ex.args[1]
856✔
894
    end
428✔
895
    return false
1✔
896
end
897
is_function_def(@nospecialize(ex)) =
7,362✔
898
    return isexpr(ex, :function) || is_short_function_def(ex) || isexpr(ex, :->)
899

900
function findmeta(ex::Expr)
2,476✔
901
    if is_function_def(ex)
3,888✔
902
        body = ex.args[2]::Expr
2,476✔
903
        body.head === :block || error(body, " is not a block expression")
2,476✔
904
        return findmeta_block(ex.args)
2,476✔
905
    end
906
    error(ex, " is not a function expression")
×
907
end
908

909
findmeta(ex::Array{Any,1}) = findmeta_block(ex)
×
910

911
function findmeta_block(exargs, argsmatch=args->true)
2,476✔
912
    for i = 1:length(exargs)
17,329✔
913
        a = exargs[i]
13,841✔
914
        if isa(a, Expr)
13,841✔
915
            if a.head === :meta && argsmatch(a.args)
8,861✔
916
                return i, exargs
168✔
917
            elseif a.head === :block
8,693✔
918
                idx, exa = findmeta_block(a.args, argsmatch)
2,476✔
919
                if idx != 0
2,476✔
920
                    return idx, exa
168✔
921
                end
922
            end
923
        end
924
    end
22,394✔
925
    return 0, []
4,616✔
926
end
927

928
remove_linenums!(ex) = ex
48✔
929
function remove_linenums!(ex::Expr)
952,444✔
930
    if ex.head === :block || ex.head === :quote
1,753,629✔
931
        # remove line number expressions from metadata (not argument literal or inert) position
932
        filter!(ex.args) do x
152,886✔
933
            isa(x, Expr) && x.head === :line && return false
414,581✔
934
            isa(x, LineNumberNode) && return false
414,581✔
935
            return true
208,215✔
936
        end
937
    end
938
    for subex in ex.args
954,682✔
939
        subex isa Expr && remove_linenums!(subex)
2,216,355✔
940
    end
3,166,561✔
941
    return ex
952,444✔
942
end
943
function remove_linenums!(src::CodeInfo)
846✔
944
    src.codelocs .= 0
12,614✔
945
    length(src.linetable) > 1 && resize!(src.linetable, 1)
846✔
946
    return src
846✔
947
end
948

949
macro generated()
2✔
950
    return Expr(:generated)
2✔
951
end
952

953
"""
954
    @generated f
955

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

962
See [Metaprogramming](@ref) for further details.
963

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

975
julia> bar(4)
976
16
977

978
julia> bar("baz")
979
"baz"
980
```
981
"""
982
macro generated(f)
50✔
983
    if isa(f, Expr) && (f.head === :function || is_short_function_def(f))
68✔
984
        body = f.args[2]
50✔
985
        lno = body.args[1]
50✔
986
        tmp = gensym("tmp")
50✔
987
        return Expr(:escape,
50✔
988
                    Expr(f.head, f.args[1],
989
                         Expr(:block,
990
                              lno,
991
                              Expr(:if, Expr(:generated),
992
                                   # https://github.com/JuliaLang/julia/issues/25678
993
                                   Expr(:block,
994
                                        :(local $tmp = $body),
995
                                        :(if $tmp isa $(GlobalRef(Core, :CodeInfo)); return $tmp; else $tmp; end)),
2✔
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)
24✔
1071
    if !isa(ex, Symbol) && !is_expr(ex, :(::))
24✔
1072
        return make_atomic(QuoteNode(:sequentially_consistent), ex)
18✔
1073
    end
1074
    return esc(Expr(:atomic, ex))
6✔
1075
end
1076
macro atomic(order, ex)
12✔
1077
    order isa QuoteNode || (order = esc(order))
12✔
1078
    return make_atomic(order, ex)
12✔
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)
30✔
1088
    @nospecialize
×
1089
    if ex isa Expr
30✔
1090
        if isexpr(ex, :., 2)
53✔
1091
            l, r = esc(ex.args[1]), esc(ex.args[2])
7✔
1092
            return :(getproperty($l, $r, $order))
7✔
1093
        elseif isexpr(ex, :call, 3)
37✔
1094
            return make_atomic(order, ex.args[2], ex.args[1], ex.args[3])
5✔
1095
        elseif ex.head === :(=)
18✔
1096
            l, r = ex.args[1], esc(ex.args[2])
7✔
1097
            if is_expr(l, :., 2)
7✔
1098
                ll, lr = esc(l.args[1]), esc(l.args[2])
6✔
1099
                return :(setproperty!($ll, $lr, $r, $order))
6✔
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)
3✔
1223
    return make_atomicreplace(QuoteNode(:sequentially_consistent), QuoteNode(:sequentially_consistent), ex, old_new)
3✔
1224
end
1225
function make_atomicreplace(success_order, fail_order, ex, old_new)
11✔
1226
    @nospecialize
×
1227
    is_expr(ex, :., 2) || error("@atomicreplace expression missing field access")
12✔
1228
    ll, lr = esc(ex.args[1]), esc(ex.args[2])
10✔
1229
    if is_expr(old_new, :call, 3) && old_new.args[1] === :(=>)
10✔
1230
        exp, rep = esc(old_new.args[2]), esc(old_new.args[3])
5✔
1231
        return :(replaceproperty!($ll, $lr, $exp, $rep, $success_order, $fail_order))
5✔
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