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

JuliaLang / julia / #37426

pending completion
#37426

push

local

web-flow
prevent heap snapshot test to write to julia dir (#48458)

Co-authored-by: Ian <i.r.butterworth@gmail.com>

81223 of 87425 relevant lines covered (92.91%)

29197321.38 hits per line

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

94.3
/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)
1,890✔
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}, ())
460✔
15

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

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

43
# copy parts of an AST that the compiler mutates
44
function copy_exprs(@nospecialize(x))
294,265,399✔
45
    if isa(x, Expr)
294,265,399✔
46
        return copy(x)
91,214,017✔
47
    elseif isa(x, PhiNode)
203,051,382✔
48
        values = x.values
1,715,683✔
49
        nvalues = length(values)
1,715,683✔
50
        new_values = Vector{Any}(undef, nvalues)
1,715,683✔
51
        @inbounds for i = 1:nvalues
3,427,570✔
52
            isassigned(values, i) || continue
3,264,874✔
53
            new_values[i] = copy_exprs(values[i])
3,264,859✔
54
        end
4,817,861✔
55
        return PhiNode(copy(x.edges), new_values)
1,715,683✔
56
    elseif isa(x, PhiCNode)
201,335,699✔
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
201,335,676✔
67
end
68
copy_exprargs(x::Array{Any,1}) = Any[copy_exprs(@inbounds x[i]) for i in 1:length(x)]
99,806,786✔
69

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

72
# create copies of the CodeInfo definition, and any mutable fields
73
function copy(c::CodeInfo)
4,991,287✔
74
    cnew = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), c)
4,991,287✔
75
    cnew.code = copy_exprargs(cnew.code)
4,991,287✔
76
    cnew.slotnames = copy(cnew.slotnames)
4,991,287✔
77
    cnew.slotflags = copy(cnew.slotflags)
4,991,287✔
78
    cnew.codelocs  = copy(cnew.codelocs)
4,991,287✔
79
    cnew.linetable = copy(cnew.linetable::Union{Vector{Any},Vector{Core.LineInfoNode}})
9,981,926✔
80
    cnew.ssaflags  = copy(cnew.ssaflags)
4,991,287✔
81
    cnew.edges     = cnew.edges === nothing ? nothing : copy(cnew.edges::Vector)
4,991,288✔
82
    ssavaluetypes  = cnew.ssavaluetypes
4,991,287✔
83
    ssavaluetypes isa Vector{Any} && (cnew.ssavaluetypes = copy(ssavaluetypes))
4,991,287✔
84
    return cnew
4,991,287✔
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)
402✔
118
    if recursive
103✔
119
        ccall(:jl_macroexpand, Any, (Any, Any), x, m)
239✔
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 the definition or in its 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)
2,044✔
262
    return annotate_meta_def_or_block(x, :inline)
2,044✔
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 the definition or in its 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)
255✔
339
    return annotate_meta_def_or_block(x, :noinline)
255✔
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
`@constprop` controls the mode of interprocedural constant propagation for the
364
annotated function. Two `setting`s are supported:
365

366
- `@constprop :aggressive ex`: apply constant propagation aggressively.
367
  For a method where the return type depends on the value of the arguments,
368
  this can yield improved inference results at the cost of additional compile time.
369
- `@constprop :none ex`: disable constant propagation. This can reduce compile
370
  times for functions that Julia might otherwise deem worthy of constant-propagation.
371
  Common cases are for functions with `Bool`- or `Symbol`-valued arguments or keyword arguments.
372
"""
373
macro constprop(setting, ex)
31✔
374
    if isa(setting, QuoteNode)
31✔
375
        setting = setting.value
31✔
376
    end
377
    setting === :aggressive && return esc(isa(ex, Expr) ? pushmeta!(ex, :aggressive_constprop) : ex)
31✔
378
    setting === :none && return esc(isa(ex, Expr) ? pushmeta!(ex, :no_constprop) : ex)
11✔
379
    throw(ArgumentError("@constprop $setting not supported"))
×
380
end
381

382
"""
383
    @assume_effects setting... ex
384

385
`@assume_effects` overrides the compiler's effect modeling for the given method.
386
`ex` must be a method definition or `@ccall` expression.
387

388
!!! compat "Julia 1.8"
389
    Using `Base.@assume_effects` requires Julia version 1.8.
390

391
# Examples
392
```jldoctest
393
julia> Base.@assume_effects :terminates_locally function pow(x)
394
           # this :terminates_locally allows `pow` to be constant-folded
395
           res = 1
396
           1 < x < 20 || error("bad pow")
397
           while x > 1
398
               res *= x
399
               x -= 1
400
           end
401
           return res
402
       end
403
pow (generic function with 1 method)
404

405
julia> code_typed() do
406
           pow(12)
407
       end
408
1-element Vector{Any}:
409
 CodeInfo(
410
1 ─     return 479001600
411
) => Int64
412

413
julia> Base.@assume_effects :total !:nothrow @ccall jl_type_intersection(Vector{Int}::Any, Vector{<:Integer}::Any)::Any
414
Vector{Int64} (alias for Array{Int64, 1})
415
```
416

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

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

430
The following `setting`s are supported.
431
- `:consistent`
432
- `:effect_free`
433
- `:nothrow`
434
- `:terminates_globally`
435
- `:terminates_locally`
436
- `:notaskstate`
437
- `:inaccessiblememonly`
438
- `:foldable`
439
- `:removable`
440
- `:total`
441

442
# Extended help
443

444
---
445
## `:consistent`
446

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

451
!!! note
452
    This in particular implies that the method must not return a freshly allocated
453
    mutable object. Multiple allocations of mutable objects (even with identical
454
    contents) are not egal.
455

456
!!! note
457
    The `:consistent`-cy assertion is made world-age wise. More formally, write
458
    ``fáµ¢`` for the evaluation of ``f`` in world-age ``i``, then we require:
459
    ```math
460
    ∀ i, x, y: x ≡ y → fᵢ(x) ≡ fᵢ(y)
461
    ```
462
    However, for two world ages ``i``, ``j`` s.t. ``i ≠ j``, we may have ``fᵢ(x) ≢ fⱼ(y)``.
463

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

468
!!! note
469
    The `:consistent`-cy includes all legal rewrites performed by the optimizer.
470
    For example, floating-point fastmath operations are not considered `:consistent`,
471
    because the optimizer may rewrite them causing the output to not be `:consistent`,
472
    even for the same world age (e.g. because one ran in the interpreter, while
473
    the other was optimized).
474

475
!!! note
476
    If `:consistent` functions terminate by throwing an exception, that exception
477
    itself is not required to meet the egality requirement specified above.
478

479
---
480
## `:effect_free`
481

482
The `:effect_free` setting asserts that the method is free of externally semantically
483
visible side effects. The following is an incomplete list of externally semantically
484
visible side effects:
485
- Changing the value of a global variable.
486
- Mutating the heap (e.g. an array or mutable value), except as noted below
487
- Changing the method table (e.g. through calls to eval)
488
- File/Network/etc. I/O
489
- Task switching
490

491
However, the following are explicitly not semantically visible, even if they
492
may be observable:
493
- Memory allocations (both mutable and immutable)
494
- Elapsed time
495
- Garbage collection
496
- Heap mutations of objects whose lifetime does not exceed the method (i.e.
497
  were allocated in the method and do not escape).
498
- The returned value (which is externally visible, but not a side effect)
499

500
The rule of thumb here is that an externally visible side effect is anything
501
that would affect the execution of the remainder of the program if the function
502
were not executed.
503

504
!!! note
505
    The `:effect_free` assertion is made both for the method itself and any code
506
    that is executed by the method. Keep in mind that the assertion must be
507
    valid for all world ages and limit use of this assertion accordingly.
508

509
---
510
## `:nothrow`
511

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

515
!!! note
516
    It is permissible for `:nothrow` annotated methods to make use of exception
517
    handling internally as long as the exception is not rethrown out of the
518
    method itself.
519

520
!!! note
521
    `MethodErrors` and similar exceptions count as abnormal termination.
522

523
---
524
## `:terminates_globally`
525

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

529
!!! note
530
    This `:terminates_globally` assertion covers any other methods called by the annotated method.
531

532
!!! note
533
    The compiler will consider this a strong indication that the method will
534
    terminate relatively *quickly* and may (if otherwise legal), call this
535
    method at compile time. I.e. it is a bad idea to annotate this setting
536
    on a method that *technically*, but not *practically*, terminates.
537

538
---
539
## `:terminates_locally`
540

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

546
!!! note
547
    `:terminates_globally` implies `:terminates_locally`.
548

549
---
550
## `:notaskstate`
551

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

556
!!! note
557
    The implementation of exception handling makes use of state stored in the
558
    task object. However, this state is currently not considered to be within
559
    the scope of `:notaskstate` and is tracked separately using the `:nothrow`
560
    effect.
561

562
!!! note
563
    The `:notaskstate` assertion concerns the state of the *currently running task*.
564
    If a reference to a `Task` object is obtained by some other means that
565
    does not consider which task is *currently* running, the `:notaskstate`
566
    effect need not be tainted. This is true, even if said task object happens
567
    to be `===` to the currently running task.
568

569
!!! note
570
    Access to task state usually also results in the tainting of other effects,
571
    such as `:effect_free` (if task state is modified) or `:consistent` (if
572
    task state is used in the computation of the result). In particular,
573
    code that is not `:notaskstate`, but is `:effect_free` and `:consistent`
574
    may still be dead-code-eliminated and thus promoted to `:total`.
575

576
---
577
## `:inaccessiblememonly`
578

579
The `:inaccessiblememonly` setting asserts that the method does not access or modify
580
externally accessible mutable memory. This means the method can access or modify mutable
581
memory for newly allocated objects that is not accessible by other methods or top-level
582
execution before return from the method, but it can not access or modify any mutable
583
global state or mutable memory pointed to by its arguments.
584

585
!!! note
586
    Below is an incomplete list of examples that invalidate this assumption:
587
    - a global reference or `getglobal` call to access a mutable global variable
588
    - a global assignment or `setglobal!` call to perform assignment to a non-constant global variable
589
    - `setfield!` call that changes a field of a global mutable variable
590

591
!!! note
592
    This `:inaccessiblememonly` assertion covers any other methods called by the annotated method.
593

594
---
595
## `:foldable`
596

597
This setting is a convenient shortcut for the set of effects that the compiler
598
requires to be guaranteed to constant fold a call at compile time. It is
599
currently equivalent to the following `setting`s:
600
- `:consistent`
601
- `:effect_free`
602
- `:terminates_globally`
603

604
!!! note
605
    This list in particular does not include `:nothrow`. The compiler will still
606
    attempt constant propagation and note any thrown error at compile time. Note
607
    however, that by the `:consistent`-cy requirements, any such annotated call
608
    must consistently throw given the same argument values.
609

610
!!! note
611
    An explicit `@inbounds` annotation inside the function will also disable
612
    constant folding and not be overriden by `:foldable`.
613

614
---
615
## `:removable`
616

617
This setting is a convenient shortcut for the set of effects that the compiler
618
requires to be guaranteed to delete a call whose result is unused at compile time.
619
It is currently equivalent to the following `setting`s:
620
- `:effect_free`
621
- `:nothrow`
622
- `:terminates_globally`
623

624
---
625
## `:total`
626

627
This `setting` is the maximum possible set of effects. It currently implies
628
the following other `setting`s:
629
- `:consistent`
630
- `:effect_free`
631
- `:nothrow`
632
- `:terminates_globally`
633
- `:notaskstate`
634
- `:inaccessiblememonly`
635

636
!!! warning
637
    `:total` is a very strong assertion and will likely gain additional semantics
638
    in future versions of Julia (e.g. if additional effects are added and included
639
    in the definition of `:total`). As a result, it should be used with care.
640
    Whenever possible, prefer to use the minimum possible set of specific effect
641
    assertions required for a particular application. In cases where a large
642
    number of effect overrides apply to a set of functions, a custom macro is
643
    recommended over the use of `:total`.
644

645
---
646
## Negated effects
647

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

652
---
653
## Comparison to `@pure`
654

655
`@assume_effects :foldable` is similar to [`@pure`](@ref) with the primary
656
distinction that the `:consistent`-cy requirement applies world-age wise rather
657
than globally as described above. However, in particular, a method annotated
658
`@pure` should always be at least `:foldable`.
659
Another advantage is that effects introduced by `@assume_effects` are propagated to
660
callers interprocedurally while a purity defined by `@pure` is not.
661
"""
662
macro assume_effects(args...)
17✔
663
    (consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly) =
17✔
664
        (false, false, false, false, false, false, false, false)
665
    for org_setting in args[1:end-1]
17✔
666
        (setting, val) = compute_assumed_setting(org_setting)
18✔
667
        if setting === :consistent
18✔
668
            consistent = val
×
669
        elseif setting === :effect_free
18✔
670
            effect_free = val
2✔
671
        elseif setting === :nothrow
16✔
672
            nothrow = val
1✔
673
        elseif setting === :terminates_globally
15✔
674
            terminates_globally = val
4✔
675
        elseif setting === :terminates_locally
11✔
676
            terminates_locally = val
1✔
677
        elseif setting === :notaskstate
10✔
678
            notaskstate = val
2✔
679
        elseif setting === :inaccessiblememonly
8✔
680
            inaccessiblememonly = val
×
681
        elseif setting === :foldable
8✔
682
            consistent = effect_free = terminates_globally = val
2✔
683
        elseif setting === :removable
6✔
684
            effect_free = nothrow = terminates_globally = val
2✔
685
        elseif setting === :total
4✔
686
            consistent = effect_free = nothrow = terminates_globally = notaskstate = inaccessiblememonly = val
4✔
687
        else
688
            throw(ArgumentError("@assume_effects $org_setting not supported"))
×
689
        end
690
    end
18✔
691
    ex = args[end]
17✔
692
    isa(ex, Expr) || throw(ArgumentError("Bad expression `$ex` in `@assume_effects [settings] ex`"))
17✔
693
    if ex.head === :macrocall && ex.args[1] === Symbol("@ccall")
17✔
694
        ex.args[1] = GlobalRef(Base, Symbol("@ccall_effects"))
3✔
695
        insert!(ex.args, 3, Core.Compiler.encode_effects_override(Core.Compiler.EffectsOverride(
3✔
696
            consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly,
697
        )))
698
        return esc(ex)
3✔
699
    end
700
    return esc(pushmeta!(ex, :purity, consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly))
14✔
701
end
702

703
function compute_assumed_setting(@nospecialize(setting), val::Bool=true)
19✔
704
    if isexpr(setting, :call) && setting.args[1] === :(!)
91✔
705
        return compute_assumed_setting(setting.args[2], !val)
2✔
706
    elseif isa(setting, QuoteNode)
36✔
707
        return compute_assumed_setting(setting.value, val)
18✔
708
    else
709
        return (setting, val)
18✔
710
    end
711
end
712

713
"""
714
    @propagate_inbounds
715

716
Tells the compiler to inline a function while retaining the caller's inbounds context.
717
"""
718
macro propagate_inbounds(ex)
146✔
719
    if isa(ex, Expr)
146✔
720
        pushmeta!(ex, :inline)
146✔
721
        pushmeta!(ex, :propagate_inbounds)
146✔
722
    end
723
    esc(ex)
146✔
724
end
725

726
"""
727
    @polly
728

729
Tells the compiler to apply the polyhedral optimizer Polly to a function.
730
"""
731
macro polly(ex)
732
    esc(isa(ex, Expr) ? pushmeta!(ex, :polly) : ex)
733
end
734

735
## some macro utilities ##
736

737
unwrap_macrocalls(@nospecialize(x)) = x
×
738
function unwrap_macrocalls(ex::Expr)
×
739
    inner = ex
×
740
    while inner.head === :macrocall
4,955✔
741
        inner = inner.args[end]::Expr
41✔
742
    end
41✔
743
    return inner
4,914✔
744
end
745

746
function pushmeta!(ex::Expr, sym::Symbol, args::Any...)
2,615✔
747
    if isempty(args)
×
748
        tag = sym
×
749
    else
750
        tag = Expr(sym, args...)::Expr
14✔
751
    end
752

753
    inner = unwrap_macrocalls(ex)
2,646✔
754

755
    idx, exargs = findmeta(inner)
2,615✔
756
    if idx != 0
2,615✔
757
        push!(exargs[idx].args, tag)
352✔
758
    else
759
        body = inner.args[2]::Expr
2,439✔
760
        pushfirst!(body.args, Expr(:meta, tag))
2,439✔
761
    end
762
    ex
2,615✔
763
end
764

765
popmeta!(body, sym) = _getmeta(body, sym, true)
766
peekmeta(body, sym) = _getmeta(body, sym, false)
767

768
function _getmeta(body::Expr, sym::Symbol, delete::Bool)
769
    body.head === :block || return false, []
770
    _getmeta(body.args, sym, delete)
771
end
772
_getmeta(arg, sym, delete::Bool) = (false, [])
773
function _getmeta(body::Array{Any,1}, sym::Symbol, delete::Bool)
774
    idx, blockargs = findmeta_block(body, args -> findmetaarg(args,sym)!=0)
775
    if idx == 0
776
        return false, []
777
    end
778
    metaargs = blockargs[idx].args
779
    i = findmetaarg(blockargs[idx].args, sym)
780
    if i == 0
781
        return false, []
782
    end
783
    ret = isa(metaargs[i], Expr) ? (metaargs[i]::Expr).args : []
784
    if delete
785
        deleteat!(metaargs, i)
786
        isempty(metaargs) && deleteat!(blockargs, idx)
787
    end
788
    true, ret
789
end
790

791
# Find index of `sym` in a meta expression argument list, or 0.
792
function findmetaarg(metaargs, sym)
793
    for i = 1:length(metaargs)
794
        arg = metaargs[i]
795
        if (isa(arg, Symbol) && (arg::Symbol)    == sym) ||
796
           (isa(arg, Expr)   && (arg::Expr).head == sym)
797
            return i
798
        end
799
    end
800
    return 0
801
end
802

803
function annotate_meta_def_or_block(@nospecialize(ex), meta::Symbol)
2,299✔
804
    inner = unwrap_macrocalls(ex)
2,299✔
805
    if is_function_def(inner)
3,634✔
806
        # annotation on a definition
807
        return esc(pushmeta!(ex, meta))
2,268✔
808
    else
809
        # annotation on a block
810
        return Expr(:block,
31✔
811
                    Expr(meta, true),
812
                    Expr(:local, Expr(:(=), :val, esc(ex))),
813
                    Expr(meta, false),
814
                    :val)
815
    end
816
end
817

818
function is_short_function_def(@nospecialize(ex))
2,844✔
819
    isexpr(ex, :(=)) || return false
2,882✔
820
    while length(ex.args) >= 1 && isa(ex.args[1], Expr)
6,516✔
821
        (ex.args[1].head === :call) && return true
6,514✔
822
        (ex.args[1].head === :where || ex.args[1].head === :(::)) || return false
460✔
823
        ex = ex.args[1]
904✔
824
    end
452✔
825
    return false
1✔
826
end
827
is_function_def(@nospecialize(ex)) =
7,740✔
828
    return isexpr(ex, :function) || is_short_function_def(ex) || isexpr(ex, :->)
829

830
function findmeta(ex::Expr)
2,615✔
831
    if is_function_def(ex)
4,106✔
832
        body = ex.args[2]::Expr
2,615✔
833
        body.head === :block || error(body, " is not a block expression")
2,615✔
834
        return findmeta_block(ex.args)
2,615✔
835
    end
836
    error(ex, " is not a function expression")
×
837
end
838

839
findmeta(ex::Array{Any,1}) = findmeta_block(ex)
840

841
function findmeta_block(exargs, argsmatch=args->true)
2,615✔
842
    for i = 1:length(exargs)
18,302✔
843
        a = exargs[i]
14,577✔
844
        if isa(a, Expr)
14,577✔
845
            if a.head === :meta && argsmatch(a.args)
9,338✔
846
                return i, exargs
176✔
847
            elseif a.head === :block
9,162✔
848
                idx, exa = findmeta_block(a.args, argsmatch)
2,615✔
849
                if idx != 0
2,615✔
850
                    return idx, exa
176✔
851
                end
852
            end
853
        end
854
    end
23,572✔
855
    return 0, []
4,878✔
856
end
857

858
remove_linenums!(ex) = ex
48✔
859
function remove_linenums!(ex::Expr)
949,709✔
860
    if ex.head === :block || ex.head === :quote
1,748,354✔
861
        # remove line number expressions from metadata (not argument literal or inert) position
862
        filter!(ex.args) do x
152,691✔
863
            isa(x, Expr) && x.head === :line && return false
413,707✔
864
            isa(x, LineNumberNode) && return false
413,707✔
865
            return true
207,777✔
866
        end
867
    end
868
    for subex in ex.args
951,888✔
869
        subex isa Expr && remove_linenums!(subex)
2,209,736✔
870
    end
3,157,266✔
871
    return ex
949,709✔
872
end
873
function remove_linenums!(src::CodeInfo)
845✔
874
    src.codelocs .= 0
12,612✔
875
    length(src.linetable) > 1 && resize!(src.linetable, 1)
845✔
876
    return src
845✔
877
end
878

879
macro generated()
2✔
880
    return Expr(:generated)
2✔
881
end
882

883
"""
884
    @generated f
885

886
`@generated` is used to annotate a function which will be generated.
887
In the body of the generated function, only types of arguments can be read
888
(not the values). The function returns a quoted expression evaluated when the
889
function is called. The `@generated` macro should not be used on functions mutating
890
the global scope or depending on mutable elements.
891

892
See [Metaprogramming](@ref) for further details.
893

894
# Examples
895
```jldoctest
896
julia> @generated function bar(x)
897
           if x <: Integer
898
               return :(x ^ 2)
899
           else
900
               return :(x)
901
           end
902
       end
903
bar (generic function with 1 method)
904

905
julia> bar(4)
906
16
907

908
julia> bar("baz")
909
"baz"
910
```
911
"""
912
macro generated(f)
50✔
913
    if isa(f, Expr) && (f.head === :function || is_short_function_def(f))
68✔
914
        body = f.args[2]
50✔
915
        lno = body.args[1]
50✔
916
        tmp = gensym("tmp")
50✔
917
        return Expr(:escape,
50✔
918
                    Expr(f.head, f.args[1],
919
                         Expr(:block,
920
                              lno,
921
                              Expr(:if, Expr(:generated),
922
                                   # https://github.com/JuliaLang/julia/issues/25678
923
                                   Expr(:block,
924
                                        :(local $tmp = $body),
925
                                        :(if $tmp isa $(GlobalRef(Core, :CodeInfo)); return $tmp; else $tmp; end)),
2✔
926
                                   Expr(:block,
927
                                        Expr(:meta, :generated_only),
928
                                        Expr(:return, nothing))))))
929
    else
930
        error("invalid syntax; @generated must be used with a function definition")
×
931
    end
932
end
933

934

935
"""
936
    @atomic var
937
    @atomic order ex
938

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

941
    @atomic a.b.x = new
942
    @atomic a.b.x += addend
943
    @atomic :release a.b.x = new
944
    @atomic :acquire_release a.b.x += addend
945

946
Perform the store operation expressed on the right atomically and return the
947
new value.
948

949
With `=`, this operation translates to a `setproperty!(a.b, :x, new)` call.
950
With any operator also, this operation translates to a `modifyproperty!(a.b,
951
:x, +, addend)[2]` call.
952

953
    @atomic a.b.x max arg2
954
    @atomic a.b.x + arg2
955
    @atomic max(a.b.x, arg2)
956
    @atomic :acquire_release max(a.b.x, arg2)
957
    @atomic :acquire_release a.b.x + arg2
958
    @atomic :acquire_release a.b.x max arg2
959

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

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

965

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

968
# Examples
969
```jldoctest
970
julia> mutable struct Atomic{T}; @atomic x::T; end
971

972
julia> a = Atomic(1)
973
Atomic{Int64}(1)
974

975
julia> @atomic a.x # fetch field x of a, with sequential consistency
976
1
977

978
julia> @atomic :sequentially_consistent a.x = 2 # set field x of a, with sequential consistency
979
2
980

981
julia> @atomic a.x += 1 # increment field x of a, with sequential consistency
982
3
983

984
julia> @atomic a.x + 1 # increment field x of a, with sequential consistency
985
3 => 4
986

987
julia> @atomic a.x # fetch field x of a, with sequential consistency
988
4
989

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

993
julia> @atomic a.x max 5 # again change field x of a to the max value, with sequential consistency
994
10 => 10
995
```
996

997
!!! compat "Julia 1.7"
998
    This functionality requires at least Julia 1.7.
999
"""
1000
macro atomic(ex)
24✔
1001
    if !isa(ex, Symbol) && !is_expr(ex, :(::))
24✔
1002
        return make_atomic(QuoteNode(:sequentially_consistent), ex)
18✔
1003
    end
1004
    return esc(Expr(:atomic, ex))
6✔
1005
end
1006
macro atomic(order, ex)
12✔
1007
    order isa QuoteNode || (order = esc(order))
12✔
1008
    return make_atomic(order, ex)
12✔
1009
end
1010
macro atomic(a1, op, a2)
3✔
1011
    return make_atomic(QuoteNode(:sequentially_consistent), a1, op, a2)
3✔
1012
end
1013
macro atomic(order, a1, op, a2)
2✔
1014
    order isa QuoteNode || (order = esc(order))
2✔
1015
    return make_atomic(order, a1, op, a2)
2✔
1016
end
1017
function make_atomic(order, ex)
30✔
1018
    @nospecialize
×
1019
    if ex isa Expr
30✔
1020
        if isexpr(ex, :., 2)
53✔
1021
            l, r = esc(ex.args[1]), esc(ex.args[2])
7✔
1022
            return :(getproperty($l, $r, $order))
7✔
1023
        elseif isexpr(ex, :call, 3)
37✔
1024
            return make_atomic(order, ex.args[2], ex.args[1], ex.args[3])
5✔
1025
        elseif ex.head === :(=)
18✔
1026
            l, r = ex.args[1], esc(ex.args[2])
7✔
1027
            if is_expr(l, :., 2)
7✔
1028
                ll, lr = esc(l.args[1]), esc(l.args[2])
6✔
1029
                return :(setproperty!($ll, $lr, $r, $order))
6✔
1030
            end
1031
        end
1032
        if length(ex.args) == 2
12✔
1033
            if ex.head === :(+=)
9✔
1034
                op = :+
5✔
1035
            elseif ex.head === :(-=)
4✔
1036
                op = :-
1✔
1037
            elseif @isdefined string
3✔
1038
                shead = string(ex.head)
3✔
1039
                if endswith(shead, '=')
3✔
1040
                    op = Symbol(shead[1:prevind(shead, end)])
2✔
1041
                end
1042
            end
1043
            if @isdefined(op)
9✔
1044
                return Expr(:ref, make_atomic(order, ex.args[1], op, ex.args[2]), 2)
8✔
1045
            end
1046
        end
1047
    end
1048
    error("could not parse @atomic expression $ex")
4✔
1049
end
1050
function make_atomic(order, a1, op, a2)
18✔
1051
    @nospecialize
×
1052
    is_expr(a1, :., 2) || error("@atomic modify expression missing field access")
22✔
1053
    a1l, a1r, op, a2 = esc(a1.args[1]), esc(a1.args[2]), esc(op), esc(a2)
14✔
1054
    return :(modifyproperty!($a1l, $a1r, $op, $a2, $order))
14✔
1055
end
1056

1057

1058
"""
1059
    @atomicswap a.b.x = new
1060
    @atomicswap :sequentially_consistent a.b.x = new
1061

1062
Stores `new` into `a.b.x` and returns the old value of `a.b.x`.
1063

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

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

1068
# Examples
1069
```jldoctest
1070
julia> mutable struct Atomic{T}; @atomic x::T; end
1071

1072
julia> a = Atomic(1)
1073
Atomic{Int64}(1)
1074

1075
julia> @atomicswap a.x = 2+2 # replace field x of a with 4, with sequential consistency
1076
1
1077

1078
julia> @atomic a.x # fetch field x of a, with sequential consistency
1079
4
1080
```
1081

1082
!!! compat "Julia 1.7"
1083
    This functionality requires at least Julia 1.7.
1084
"""
1085
macro atomicswap(order, ex)
2✔
1086
    order isa QuoteNode || (order = esc(order))
2✔
1087
    return make_atomicswap(order, ex)
2✔
1088
end
1089
macro atomicswap(ex)
1✔
1090
    return make_atomicswap(QuoteNode(:sequentially_consistent), ex)
1✔
1091
end
1092
function make_atomicswap(order, ex)
3✔
1093
    @nospecialize
×
1094
    is_expr(ex, :(=), 2) || error("@atomicswap expression missing assignment")
3✔
1095
    l, val = ex.args[1], esc(ex.args[2])
3✔
1096
    is_expr(l, :., 2) || error("@atomicswap expression missing field access")
3✔
1097
    ll, lr = esc(l.args[1]), esc(l.args[2])
3✔
1098
    return :(swapproperty!($ll, $lr, $val, $order))
3✔
1099
end
1100

1101

1102
"""
1103
    @atomicreplace a.b.x expected => desired
1104
    @atomicreplace :sequentially_consistent a.b.x expected => desired
1105
    @atomicreplace :sequentially_consistent :monotonic a.b.x expected => desired
1106

1107
Perform the conditional replacement expressed by the pair atomically, returning
1108
the values `(old, success::Bool)`. Where `success` indicates whether the
1109
replacement was completed.
1110

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

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

1115
# Examples
1116
```jldoctest
1117
julia> mutable struct Atomic{T}; @atomic x::T; end
1118

1119
julia> a = Atomic(1)
1120
Atomic{Int64}(1)
1121

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

1125
julia> @atomic a.x # fetch field x of a, with sequential consistency
1126
2
1127

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

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

1133
julia> @atomicreplace a.x xchg
1134
(old = 2, success = true)
1135

1136
julia> @atomic a.x # fetch field x of a, with sequential consistency
1137
0
1138
```
1139

1140
!!! compat "Julia 1.7"
1141
    This functionality requires at least Julia 1.7.
1142
"""
1143
macro atomicreplace(success_order, fail_order, ex, old_new)
4✔
1144
    fail_order isa QuoteNode || (fail_order = esc(fail_order))
4✔
1145
    success_order isa QuoteNode || (success_order = esc(success_order))
4✔
1146
    return make_atomicreplace(success_order, fail_order, ex, old_new)
4✔
1147
end
1148
macro atomicreplace(order, ex, old_new)
4✔
1149
    order isa QuoteNode || (order = esc(order))
4✔
1150
    return make_atomicreplace(order, order, ex, old_new)
4✔
1151
end
1152
macro atomicreplace(ex, old_new)
3✔
1153
    return make_atomicreplace(QuoteNode(:sequentially_consistent), QuoteNode(:sequentially_consistent), ex, old_new)
3✔
1154
end
1155
function make_atomicreplace(success_order, fail_order, ex, old_new)
11✔
1156
    @nospecialize
×
1157
    is_expr(ex, :., 2) || error("@atomicreplace expression missing field access")
12✔
1158
    ll, lr = esc(ex.args[1]), esc(ex.args[2])
10✔
1159
    if is_expr(old_new, :call, 3) && old_new.args[1] === :(=>)
10✔
1160
        exp, rep = esc(old_new.args[2]), esc(old_new.args[3])
5✔
1161
        return :(replaceproperty!($ll, $lr, $exp, $rep, $success_order, $fail_order))
5✔
1162
    else
1163
        old_new = esc(old_new)
5✔
1164
        return :(replaceproperty!($ll, $lr, $old_new::Pair..., $success_order, $fail_order))
5✔
1165
    end
1166
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