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

JuliaLang / julia / #37527

pending completion
#37527

push

local

web-flow
make `IRShow.method_name` inferrable (#49607)

18 of 18 new or added lines in 3 files covered. (100.0%)

68710 of 81829 relevant lines covered (83.97%)

33068903.12 hits per line

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

85.25
/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)
11,972✔
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}, ())
499✔
15

16
gensym(s::String) = ccall(:jl_tagged_gensym, Ref{Symbol}, (Ptr{UInt8}, Csize_t), s, sizeof(s))
262✔
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
5,286,282,176✔
39
isexpr(@nospecialize(ex), head::Symbol, n::Int) = isa(ex, Expr) && ex.head === head && length(ex.args) == n
64,277✔
40

41
copy(e::Expr) = exprarray(e.head, copy_exprargs(e.args))
114,669,900✔
42

43
# copy parts of an AST that the compiler mutates
44
function copy_exprs(@nospecialize(x))
369,019,934✔
45
    if isa(x, Expr)
369,019,934✔
46
        return copy(x)
114,661,071✔
47
    elseif isa(x, PhiNode)
254,358,863✔
48
        values = x.values
1,954,234✔
49
        nvalues = length(values)
1,954,234✔
50
        new_values = Vector{Any}(undef, nvalues)
1,954,234✔
51
        @inbounds for i = 1:nvalues
3,905,244✔
52
            isassigned(values, i) || continue
3,787,473✔
53
            new_values[i] = copy_exprs(values[i])
3,787,448✔
54
        end
5,623,936✔
55
        return PhiNode(copy(x.edges), new_values)
1,954,234✔
56
    elseif isa(x, PhiCNode)
252,404,629✔
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
252,404,606✔
67
end
68
copy_exprargs(x::Array{Any,1}) = Any[copy_exprs(@inbounds x[i]) for i in 1:length(x)]
128,967,914✔
69

70
@eval exprarray(head::Symbol, arg::Array{Any,1}) = $(Expr(:new, :Expr, :head, :arg))
114,671,862✔
71

72
# create copies of the CodeInfo definition, and any mutable fields
73
function copy(c::CodeInfo)
6,463,726✔
74
    cnew = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), c)
6,463,726✔
75
    cnew.code = copy_exprargs(cnew.code)
6,463,726✔
76
    cnew.slotnames = copy(cnew.slotnames)
6,463,726✔
77
    cnew.slotflags = copy(cnew.slotflags)
6,463,726✔
78
    if cnew.slottypes !== nothing
6,463,726✔
79
        cnew.slottypes = copy(cnew.slottypes)
4,468,658✔
80
    end
81
    cnew.codelocs  = copy(cnew.codelocs)
6,463,726✔
82
    cnew.linetable = copy(cnew.linetable::Union{Vector{Any},Vector{Core.LineInfoNode}})
12,926,852✔
83
    cnew.ssaflags  = copy(cnew.ssaflags)
6,463,726✔
84
    cnew.edges     = cnew.edges === nothing ? nothing : copy(cnew.edges::Vector)
6,463,727✔
85
    ssavaluetypes  = cnew.ssavaluetypes
6,463,726✔
86
    ssavaluetypes isa Vector{Any} && (cnew.ssavaluetypes = copy(ssavaluetypes))
6,463,726✔
87
    return cnew
6,463,726✔
88
end
89

90

91
==(x::Expr, y::Expr) = x.head === y.head && isequal(x.args, y.args)
13,533✔
92
==(x::QuoteNode, y::QuoteNode) = isequal(x.value, y.value)
1,514✔
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)
559✔
121
    if recursive
31✔
122
        ccall(:jl_macroexpand, Any, (Any, Any), x, m)
528✔
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)
33✔
168
    return :(macroexpand($__module__, $(QuoteNode(code)), recursive=true))
33✔
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,424✔
265
    return annotate_meta_def_or_block(x, :inline)
1,424✔
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)
238✔
342
    return annotate_meta_def_or_block(x, :noinline)
238✔
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
    If `:consistent` functions terminate by throwing an exception, that exception
518
    itself is not required to meet the egality requirement specified above.
519

520
---
521
## `:effect_free`
522

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

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

541
The rule of thumb here is that an externally visible side effect is anything
542
that would affect the execution of the remainder of the program if the function
543
were not executed.
544

545
!!! note
546
    The `:effect_free` assertion is made both for the method itself and any code
547
    that is executed by the method. Keep in mind that the assertion must be
548
    valid for all world ages and limit use of this assertion accordingly.
549

550
---
551
## `:nothrow`
552

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

556
!!! note
557
    It is permissible for `:nothrow` annotated methods to make use of exception
558
    handling internally as long as the exception is not rethrown out of the
559
    method itself.
560

561
!!! note
562
    `MethodErrors` and similar exceptions count as abnormal termination.
563

564
---
565
## `:terminates_globally`
566

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

570
!!! note
571
    This `:terminates_globally` assertion covers any other methods called by the annotated method.
572

573
!!! note
574
    The compiler will consider this a strong indication that the method will
575
    terminate relatively *quickly* and may (if otherwise legal), call this
576
    method at compile time. I.e. it is a bad idea to annotate this setting
577
    on a method that *technically*, but not *practically*, terminates.
578

579
---
580
## `:terminates_locally`
581

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

587
!!! note
588
    `:terminates_globally` implies `:terminates_locally`.
589

590
---
591
## `:notaskstate`
592

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

597
!!! note
598
    The implementation of exception handling makes use of state stored in the
599
    task object. However, this state is currently not considered to be within
600
    the scope of `:notaskstate` and is tracked separately using the `:nothrow`
601
    effect.
602

603
!!! note
604
    The `:notaskstate` assertion concerns the state of the *currently running task*.
605
    If a reference to a `Task` object is obtained by some other means that
606
    does not consider which task is *currently* running, the `:notaskstate`
607
    effect need not be tainted. This is true, even if said task object happens
608
    to be `===` to the currently running task.
609

610
!!! note
611
    Access to task state usually also results in the tainting of other effects,
612
    such as `:effect_free` (if task state is modified) or `:consistent` (if
613
    task state is used in the computation of the result). In particular,
614
    code that is not `:notaskstate`, but is `:effect_free` and `:consistent`
615
    may still be dead-code-eliminated and thus promoted to `:total`.
616

617
---
618
## `:inaccessiblememonly`
619

620
The `:inaccessiblememonly` setting asserts that the method does not access or modify
621
externally accessible mutable memory. This means the method can access or modify mutable
622
memory for newly allocated objects that is not accessible by other methods or top-level
623
execution before return from the method, but it can not access or modify any mutable
624
global state or mutable memory pointed to by its arguments.
625

626
!!! note
627
    Below is an incomplete list of examples that invalidate this assumption:
628
    - a global reference or `getglobal` call to access a mutable global variable
629
    - a global assignment or `setglobal!` call to perform assignment to a non-constant global variable
630
    - `setfield!` call that changes a field of a global mutable variable
631

632
!!! note
633
    This `:inaccessiblememonly` assertion covers any other methods called by the annotated method.
634

635
---
636
## `:foldable`
637

638
This setting is a convenient shortcut for the set of effects that the compiler
639
requires to be guaranteed to constant fold a call at compile time. It is
640
currently equivalent to the following `setting`s:
641
- `:consistent`
642
- `:effect_free`
643
- `:terminates_globally`
644

645
!!! note
646
    This list in particular does not include `:nothrow`. The compiler will still
647
    attempt constant propagation and note any thrown error at compile time. Note
648
    however, that by the `:consistent`-cy requirements, any such annotated call
649
    must consistently throw given the same argument values.
650

651
!!! note
652
    An explicit `@inbounds` annotation inside the function will also disable
653
    constant folding and not be overriden by `:foldable`.
654

655
---
656
## `:removable`
657

658
This setting is a convenient shortcut for the set of effects that the compiler
659
requires to be guaranteed to delete a call whose result is unused at compile time.
660
It is currently equivalent to the following `setting`s:
661
- `:effect_free`
662
- `:nothrow`
663
- `:terminates_globally`
664

665
---
666
## `:total`
667

668
This `setting` is the maximum possible set of effects. It currently implies
669
the following other `setting`s:
670
- `:consistent`
671
- `:effect_free`
672
- `:nothrow`
673
- `:terminates_globally`
674
- `:notaskstate`
675
- `:inaccessiblememonly`
676

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

686
---
687
## Negated effects
688

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

749
function compute_assumed_setting(@nospecialize(setting), val::Bool=true)
21✔
750
    if isexpr(setting, :call) && setting.args[1] === :(!)
101✔
751
        return compute_assumed_setting(setting.args[2], !val)
2✔
752
    elseif isa(setting, QuoteNode)
40✔
753
        return compute_assumed_setting(setting.value, val)
20✔
754
    else
755
        return (setting, val)
20✔
756
    end
757
end
758

759
"""
760
    @propagate_inbounds
761

762
Tells the compiler to inline a function while retaining the caller's inbounds context.
763
"""
764
macro propagate_inbounds(ex)
110✔
765
    if isa(ex, Expr)
110✔
766
        pushmeta!(ex, :inline)
110✔
767
        pushmeta!(ex, :propagate_inbounds)
110✔
768
    end
769
    esc(ex)
110✔
770
end
771

772
"""
773
    @polly
774

775
Tells the compiler to apply the polyhedral optimizer Polly to a function.
776
"""
777
macro polly(ex)
778
    esc(isa(ex, Expr) ? pushmeta!(ex, :polly) : ex)
779
end
780

781
## some macro utilities ##
782

783
unwrap_macrocalls(@nospecialize(x)) = x
1✔
784
function unwrap_macrocalls(ex::Expr)
18✔
785
    inner = ex
×
786
    while inner.head === :macrocall
3,649✔
787
        inner = inner.args[end]::Expr
73✔
788
    end
73✔
789
    return inner
3,576✔
790
end
791

792
function pushmeta!(ex::Expr, sym::Symbol, args::Any...)
1,896✔
793
    if isempty(args)
×
794
        tag = sym
×
795
    else
796
        tag = Expr(sym, args...)::Expr
15✔
797
    end
798

799
    inner = unwrap_macrocalls(ex)
1,953✔
800

801
    idx, exargs = findmeta(inner)
1,896✔
802
    if idx != 0
1,896✔
803
        push!(exargs[idx].args, tag)
306✔
804
    else
805
        body = inner.args[2]::Expr
1,743✔
806
        pushfirst!(body.args, Expr(:meta, tag))
1,743✔
807
    end
808
    ex
1,896✔
809
end
810

811
popmeta!(body, sym) = _getmeta(body, sym, true)
×
812
peekmeta(body, sym) = _getmeta(body, sym, false)
×
813

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

837
# Find index of `sym` in a meta expression argument list, or 0.
838
function findmetaarg(metaargs, sym)
×
839
    for i = 1:length(metaargs)
×
840
        arg = metaargs[i]
×
841
        if (isa(arg, Symbol) && (arg::Symbol)    == sym) ||
×
842
           (isa(arg, Expr)   && (arg::Expr).head == sym)
843
            return i
×
844
        end
845
    end
×
846
    return 0
×
847
end
848

849
function annotate_meta_def_or_block(@nospecialize(ex), meta::Symbol)
1,662✔
850
    inner = unwrap_macrocalls(ex)
1,662✔
851
    if is_function_def(inner)
2,673✔
852
        # annotation on a definition
853
        return esc(pushmeta!(ex, meta))
1,628✔
854
    else
855
        # annotation on a block
856
        return Expr(:block,
34✔
857
                    Expr(meta, true),
858
                    Expr(:local, Expr(:(=), :val, esc(ex))),
859
                    Expr(meta, false),
860
                    :val)
861
    end
862
end
863

864
function is_short_function_def(@nospecialize(ex))
2,187✔
865
    isexpr(ex, :(=)) || return false
2,236✔
866
    while length(ex.args) >= 1 && isa(ex.args[1], Expr)
4,898✔
867
        (ex.args[1].head === :call) && return true
4,896✔
868
        (ex.args[1].head === :where || ex.args[1].head === :(::)) || return false
319✔
869
        ex = ex.args[1]
622✔
870
    end
311✔
871
    return false
1✔
872
end
873
is_function_def(@nospecialize(ex)) =
5,742✔
874
    return isexpr(ex, :function) || is_short_function_def(ex) || isexpr(ex, :->)
875

876
function findmeta(ex::Expr)
1,896✔
877
    if is_function_def(ex)
3,031✔
878
        body = ex.args[2]::Expr
1,896✔
879
        body.head === :block || error(body, " is not a block expression")
1,896✔
880
        return findmeta_block(ex.args)
1,896✔
881
    end
882
    error(ex, " is not a function expression")
×
883
end
884

885
findmeta(ex::Array{Any,1}) = findmeta_block(ex)
×
886

887
function findmeta_block(exargs, argsmatch=args->true)
1,896✔
888
    for i = 1:length(exargs)
13,269✔
889
        a = exargs[i]
10,536✔
890
        if isa(a, Expr)
10,536✔
891
            if a.head === :meta && argsmatch(a.args)
6,780✔
892
                return i, exargs
153✔
893
            elseif a.head === :block
6,627✔
894
                idx, exa = findmeta_block(a.args, argsmatch)
1,896✔
895
                if idx != 0
1,896✔
896
                    return idx, exa
153✔
897
                end
898
            end
899
        end
900
    end
16,974✔
901
    return 0, []
3,486✔
902
end
903

904
remove_linenums!(ex) = ex
48✔
905
function remove_linenums!(ex::Expr)
933,652✔
906
    if ex.head === :block || ex.head === :quote
1,719,620✔
907
        # remove line number expressions from metadata (not argument literal or inert) position
908
        filter!(ex.args) do x
149,294✔
909
            isa(x, Expr) && x.head === :line && return false
404,508✔
910
            isa(x, LineNumberNode) && return false
404,508✔
911
            return true
203,172✔
912
        end
913
    end
914
    for subex in ex.args
935,825✔
915
        subex isa Expr && remove_linenums!(subex)
2,174,799✔
916
    end
3,106,278✔
917
    return ex
933,652✔
918
end
919
function remove_linenums!(src::CodeInfo)
859✔
920
    src.codelocs .= 0
44,233✔
921
    length(src.linetable) > 1 && resize!(src.linetable, 1)
1,250✔
922
    return src
859✔
923
end
924

925
macro generated()
2✔
926
    return Expr(:generated)
2✔
927
end
928

929
"""
930
    @generated f
931

932
`@generated` is used to annotate a function which will be generated.
933
In the body of the generated function, only types of arguments can be read
934
(not the values). The function returns a quoted expression evaluated when the
935
function is called. The `@generated` macro should not be used on functions mutating
936
the global scope or depending on mutable elements.
937

938
See [Metaprogramming](@ref) for further details.
939

940
# Examples
941
```jldoctest
942
julia> @generated function bar(x)
943
           if x <: Integer
944
               return :(x ^ 2)
945
           else
946
               return :(x)
947
           end
948
       end
949
bar (generic function with 1 method)
950

951
julia> bar(4)
952
16
953

954
julia> bar("baz")
955
"baz"
956
```
957
"""
958
macro generated(f)
43✔
959
    if isa(f, Expr) && (f.head === :function || is_short_function_def(f))
58✔
960
        body = f.args[2]
43✔
961
        lno = body.args[1]
43✔
962
        tmp = gensym("tmp")
43✔
963
        return Expr(:escape,
43✔
964
                    Expr(f.head, f.args[1],
965
                         Expr(:block,
966
                              lno,
967
                              Expr(:if, Expr(:generated),
968
                                   body,
969
                                   Expr(:block,
970
                                        Expr(:meta, :generated_only),
971
                                        Expr(:return, nothing))))))
972
    else
973
        error("invalid syntax; @generated must be used with a function definition")
×
974
    end
975
end
976

977

978
"""
979
    @atomic var
980
    @atomic order ex
981

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

984
    @atomic a.b.x = new
985
    @atomic a.b.x += addend
986
    @atomic :release a.b.x = new
987
    @atomic :acquire_release a.b.x += addend
988

989
Perform the store operation expressed on the right atomically and return the
990
new value.
991

992
With `=`, this operation translates to a `setproperty!(a.b, :x, new)` call.
993
With any operator also, this operation translates to a `modifyproperty!(a.b,
994
:x, +, addend)[2]` call.
995

996
    @atomic a.b.x max arg2
997
    @atomic a.b.x + arg2
998
    @atomic max(a.b.x, arg2)
999
    @atomic :acquire_release max(a.b.x, arg2)
1000
    @atomic :acquire_release a.b.x + arg2
1001
    @atomic :acquire_release a.b.x max arg2
1002

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

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

1008

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

1011
# Examples
1012
```jldoctest
1013
julia> mutable struct Atomic{T}; @atomic x::T; end
1014

1015
julia> a = Atomic(1)
1016
Atomic{Int64}(1)
1017

1018
julia> @atomic a.x # fetch field x of a, with sequential consistency
1019
1
1020

1021
julia> @atomic :sequentially_consistent a.x = 2 # set field x of a, with sequential consistency
1022
2
1023

1024
julia> @atomic a.x += 1 # increment field x of a, with sequential consistency
1025
3
1026

1027
julia> @atomic a.x + 1 # increment field x of a, with sequential consistency
1028
3 => 4
1029

1030
julia> @atomic a.x # fetch field x of a, with sequential consistency
1031
4
1032

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

1036
julia> @atomic a.x max 5 # again change field x of a to the max value, with sequential consistency
1037
10 => 10
1038
```
1039

1040
!!! compat "Julia 1.7"
1041
    This functionality requires at least Julia 1.7.
1042
"""
1043
macro atomic(ex)
57✔
1044
    if !isa(ex, Symbol) && !is_expr(ex, :(::))
57✔
1045
        return make_atomic(QuoteNode(:sequentially_consistent), ex)
45✔
1046
    end
1047
    return esc(Expr(:atomic, ex))
12✔
1048
end
1049
macro atomic(order, ex)
27✔
1050
    order isa QuoteNode || (order = esc(order))
27✔
1051
    return make_atomic(order, ex)
27✔
1052
end
1053
macro atomic(a1, op, a2)
3✔
1054
    return make_atomic(QuoteNode(:sequentially_consistent), a1, op, a2)
3✔
1055
end
1056
macro atomic(order, a1, op, a2)
2✔
1057
    order isa QuoteNode || (order = esc(order))
2✔
1058
    return make_atomic(order, a1, op, a2)
2✔
1059
end
1060
function make_atomic(order, ex)
58✔
1061
    @nospecialize
×
1062
    if ex isa Expr
58✔
1063
        if isexpr(ex, :., 2)
89✔
1064
            l, r = esc(ex.args[1]), esc(ex.args[2])
27✔
1065
            return :(getproperty($l, $r, $order))
27✔
1066
        elseif isexpr(ex, :call, 3)
53✔
1067
            return make_atomic(order, ex.args[2], ex.args[1], ex.args[3])
5✔
1068
        elseif ex.head === :(=)
26✔
1069
            l, r = ex.args[1], esc(ex.args[2])
15✔
1070
            if is_expr(l, :., 2)
15✔
1071
                ll, lr = esc(l.args[1]), esc(l.args[2])
14✔
1072
                return :(setproperty!($ll, $lr, $r, $order))
14✔
1073
            end
1074
        end
1075
        if length(ex.args) == 2
12✔
1076
            if ex.head === :(+=)
9✔
1077
                op = :+
5✔
1078
            elseif ex.head === :(-=)
4✔
1079
                op = :-
1✔
1080
            elseif @isdefined string
3✔
1081
                shead = string(ex.head)
3✔
1082
                if endswith(shead, '=')
3✔
1083
                    op = Symbol(shead[1:prevind(shead, end)])
2✔
1084
                end
1085
            end
1086
            if @isdefined(op)
9✔
1087
                return Expr(:ref, make_atomic(order, ex.args[1], op, ex.args[2]), 2)
8✔
1088
            end
1089
        end
1090
    end
1091
    error("could not parse @atomic expression $ex")
4✔
1092
end
1093
function make_atomic(order, a1, op, a2)
18✔
1094
    @nospecialize
×
1095
    is_expr(a1, :., 2) || error("@atomic modify expression missing field access")
22✔
1096
    a1l, a1r, op, a2 = esc(a1.args[1]), esc(a1.args[2]), esc(op), esc(a2)
14✔
1097
    return :(modifyproperty!($a1l, $a1r, $op, $a2, $order))
14✔
1098
end
1099

1100

1101
"""
1102
    @atomicswap a.b.x = new
1103
    @atomicswap :sequentially_consistent a.b.x = new
1104

1105
Stores `new` into `a.b.x` and returns the old value of `a.b.x`.
1106

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

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

1111
# Examples
1112
```jldoctest
1113
julia> mutable struct Atomic{T}; @atomic x::T; end
1114

1115
julia> a = Atomic(1)
1116
Atomic{Int64}(1)
1117

1118
julia> @atomicswap a.x = 2+2 # replace field x of a with 4, with sequential consistency
1119
1
1120

1121
julia> @atomic a.x # fetch field x of a, with sequential consistency
1122
4
1123
```
1124

1125
!!! compat "Julia 1.7"
1126
    This functionality requires at least Julia 1.7.
1127
"""
1128
macro atomicswap(order, ex)
2✔
1129
    order isa QuoteNode || (order = esc(order))
2✔
1130
    return make_atomicswap(order, ex)
2✔
1131
end
1132
macro atomicswap(ex)
1✔
1133
    return make_atomicswap(QuoteNode(:sequentially_consistent), ex)
1✔
1134
end
1135
function make_atomicswap(order, ex)
3✔
1136
    @nospecialize
×
1137
    is_expr(ex, :(=), 2) || error("@atomicswap expression missing assignment")
3✔
1138
    l, val = ex.args[1], esc(ex.args[2])
3✔
1139
    is_expr(l, :., 2) || error("@atomicswap expression missing field access")
3✔
1140
    ll, lr = esc(l.args[1]), esc(l.args[2])
3✔
1141
    return :(swapproperty!($ll, $lr, $val, $order))
3✔
1142
end
1143

1144

1145
"""
1146
    @atomicreplace a.b.x expected => desired
1147
    @atomicreplace :sequentially_consistent a.b.x expected => desired
1148
    @atomicreplace :sequentially_consistent :monotonic a.b.x expected => desired
1149

1150
Perform the conditional replacement expressed by the pair atomically, returning
1151
the values `(old, success::Bool)`. Where `success` indicates whether the
1152
replacement was completed.
1153

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

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

1158
# Examples
1159
```jldoctest
1160
julia> mutable struct Atomic{T}; @atomic x::T; end
1161

1162
julia> a = Atomic(1)
1163
Atomic{Int64}(1)
1164

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

1168
julia> @atomic a.x # fetch field x of a, with sequential consistency
1169
2
1170

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

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

1176
julia> @atomicreplace a.x xchg
1177
(old = 2, success = true)
1178

1179
julia> @atomic a.x # fetch field x of a, with sequential consistency
1180
0
1181
```
1182

1183
!!! compat "Julia 1.7"
1184
    This functionality requires at least Julia 1.7.
1185
"""
1186
macro atomicreplace(success_order, fail_order, ex, old_new)
4✔
1187
    fail_order isa QuoteNode || (fail_order = esc(fail_order))
4✔
1188
    success_order isa QuoteNode || (success_order = esc(success_order))
4✔
1189
    return make_atomicreplace(success_order, fail_order, ex, old_new)
4✔
1190
end
1191
macro atomicreplace(order, ex, old_new)
4✔
1192
    order isa QuoteNode || (order = esc(order))
4✔
1193
    return make_atomicreplace(order, order, ex, old_new)
4✔
1194
end
1195
macro atomicreplace(ex, old_new)
12✔
1196
    return make_atomicreplace(QuoteNode(:sequentially_consistent), QuoteNode(:sequentially_consistent), ex, old_new)
12✔
1197
end
1198
function make_atomicreplace(success_order, fail_order, ex, old_new)
17✔
1199
    @nospecialize
×
1200
    is_expr(ex, :., 2) || error("@atomicreplace expression missing field access")
18✔
1201
    ll, lr = esc(ex.args[1]), esc(ex.args[2])
16✔
1202
    if is_expr(old_new, :call, 3) && old_new.args[1] === :(=>)
16✔
1203
        exp, rep = esc(old_new.args[2]), esc(old_new.args[3])
11✔
1204
        return :(replaceproperty!($ll, $lr, $exp, $rep, $success_order, $fail_order))
11✔
1205
    else
1206
        old_new = esc(old_new)
5✔
1207
        return :(replaceproperty!($ll, $lr, $old_new::Pair..., $success_order, $fail_order))
5✔
1208
    end
1209
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

© 2025 Coveralls, Inc