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

JuliaLang / julia / #37594

pending completion
#37594

push

local

web-flow
Move `round(T::Type, x)` docstring above `round(z::Complex, ...)` docstring (#50775)

73676 of 84540 relevant lines covered (87.15%)

32579691.71 hits per line

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

85.58
/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)
8,371✔
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}, ())
485✔
15

16
gensym(s::String) = ccall(:jl_tagged_gensym, Ref{Symbol}, (Ptr{UInt8}, Csize_t), s, sizeof(s))
240✔
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
## line numbers ##
37
convert(::Type{LineNumberNode}, lin::Core.LineInfoNode) = LineNumberNode(Int(lin.line), lin.file)
1✔
38

39
## expressions ##
40

41
isexpr(@nospecialize(ex), head::Symbol) = isa(ex, Expr) && ex.head === head
5,724,870,578✔
42
isexpr(@nospecialize(ex), head::Symbol, n::Int) = isa(ex, Expr) && ex.head === head && length(ex.args) == n
64,898✔
43

44
copy(e::Expr) = exprarray(e.head, copy_exprargs(e.args))
111,959,046✔
45

46
# copy parts of an AST that the compiler mutates
47
function copy_exprs(@nospecialize(x))
367,663,531✔
48
    if isa(x, Expr)
367,663,531✔
49
        return copy(x)
111,941,737✔
50
    elseif isa(x, PhiNode)
255,721,794✔
51
        values = x.values
2,514,804✔
52
        nvalues = length(values)
2,514,804✔
53
        new_values = Vector{Any}(undef, nvalues)
2,514,804✔
54
        @inbounds for i = 1:nvalues
5,025,726✔
55
            isassigned(values, i) || continue
4,818,813✔
56
            new_values[i] = copy_exprs(values[i])
4,818,790✔
57
        end
7,126,704✔
58
        return PhiNode(copy(x.edges), new_values)
2,514,804✔
59
    elseif isa(x, PhiCNode)
253,206,990✔
60
        values = x.values
25✔
61
        nvalues = length(values)
25✔
62
        new_values = Vector{Any}(undef, nvalues)
25✔
63
        @inbounds for i = 1:nvalues
50✔
64
            isassigned(values, i) || continue
35✔
65
            new_values[i] = copy_exprs(values[i])
35✔
66
        end
45✔
67
        return PhiCNode(new_values)
25✔
68
    end
69
    return x
253,206,965✔
70
end
71
copy_exprargs(x::Array{Any,1}) = Any[copy_exprs(@inbounds x[i]) for i in 1:length(x)]
275,009,099✔
72

73
@eval exprarray(head::Symbol, arg::Array{Any,1}) = $(Expr(:new, :Expr, :head, :arg))
111,960,968✔
74

75
# create copies of the CodeInfo definition, and any mutable fields
76
function copy(c::CodeInfo)
6,416,730✔
77
    cnew = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), c)
6,416,730✔
78
    cnew.code = copy_exprargs(cnew.code)
91,826,439✔
79
    cnew.slotnames = copy(cnew.slotnames)
6,416,730✔
80
    cnew.slotflags = copy(cnew.slotflags)
6,416,730✔
81
    if cnew.slottypes !== nothing
6,416,730✔
82
        cnew.slottypes = copy(cnew.slottypes)
4,508,423✔
83
    end
84
    cnew.codelocs  = copy(cnew.codelocs)
6,416,730✔
85
    cnew.linetable = copy(cnew.linetable::Union{Vector{Any},Vector{Core.LineInfoNode}})
12,832,507✔
86
    cnew.ssaflags  = copy(cnew.ssaflags)
6,416,730✔
87
    cnew.edges     = cnew.edges === nothing ? nothing : copy(cnew.edges::Vector)
6,416,731✔
88
    ssavaluetypes  = cnew.ssavaluetypes
6,416,730✔
89
    ssavaluetypes isa Vector{Any} && (cnew.ssavaluetypes = copy(ssavaluetypes))
6,416,730✔
90
    return cnew
6,416,730✔
91
end
92

93

94
==(x::Expr, y::Expr) = x.head === y.head && isequal(x.args, y.args)
13,463✔
95
==(x::QuoteNode, y::QuoteNode) = isequal(x.value, y.value)
1,513✔
96
==(stmt1::Core.PhiNode, stmt2::Core.PhiNode) = stmt1.edges == stmt2.edges && stmt1.values == stmt2.values
×
97

98
"""
99
    macroexpand(m::Module, x; recursive=true)
100

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

116
julia> macroexpand(M, :(@m2()), recursive=true)
117
42
118

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

131
"""
132
    @macroexpand
133

134
Return equivalent expression with all macros removed (expanded).
135

136
There are differences between `@macroexpand` and [`macroexpand`](@ref).
137

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

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

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

159
julia> macro m()
160
           2
161
       end
162
@m (macro with 1 method)
163

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

174

175
"""
176
    @macroexpand1
177

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

184
## misc syntax ##
185

186
"""
187
    Core.eval(m::Module, expr)
188

189
Evaluate an expression in the given module and return the result.
190
"""
191
Core.eval
192

193
"""
194
    @inline
195

196
Give a hint to the compiler that this function is worth inlining.
197

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

202
`@inline` can be applied immediately before a function definition or within a function body.
203

204
```julia
205
# annotate long-form definition
206
@inline function longdef(x)
207
    ...
208
end
209

210
# annotate short-form definition
211
@inline shortdef(x) = ...
212

213
# annotate anonymous function that a `do` block creates
214
f() do
215
    @inline
216
    ...
217
end
218
```
219

220
!!! compat "Julia 1.8"
221
    The usage within a function body requires at least Julia 1.8.
222

223
---
224
    @inline block
225

226
Give a hint to the compiler that calls within `block` are worth inlining.
227

228
```julia
229
# The compiler will try to inline `f`
230
@inline f(...)
231

232
# The compiler will try to inline `f`, `g` and `+`
233
@inline f(...) + g(...)
234
```
235

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

244
    let
245
        @inline explicit_noinline(args...) # will be inlined
246
    end
247
    ```
248

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

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

264
!!! compat "Julia 1.8"
265
    The callsite annotation requires at least Julia 1.8.
266
"""
267
macro inline(x)
1,517✔
268
    return annotate_meta_def_or_block(x, :inline)
1,517✔
269
end
270

271
"""
272
    @noinline
273

274
Give a hint to the compiler that it should not inline a function.
275

276
Small functions are typically inlined automatically.
277
By using `@noinline` on small functions, auto-inlining can be
278
prevented.
279

280
`@noinline` can be applied immediately before a function definition or within a function body.
281

282
```julia
283
# annotate long-form definition
284
@noinline function longdef(x)
285
    ...
286
end
287

288
# annotate short-form definition
289
@noinline shortdef(x) = ...
290

291
# annotate anonymous function that a `do` block creates
292
f() do
293
    @noinline
294
    ...
295
end
296
```
297

298
!!! compat "Julia 1.8"
299
    The usage within a function body requires at least Julia 1.8.
300

301
---
302
    @noinline block
303

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

306
```julia
307
# The compiler will try to not inline `f`
308
@noinline f(...)
309

310
# The compiler will try to not inline `f`, `g` and `+`
311
@noinline f(...) + g(...)
312
```
313

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

322
    let
323
        @noinline explicit_inline(args...) # will not be inlined
324
    end
325
    ```
326

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

337
!!! compat "Julia 1.8"
338
    The callsite annotation requires at least Julia 1.8.
339

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

348
"""
349
    @constprop setting [ex]
350

351
Control the mode of interprocedural constant propagation for the annotated function.
352

353
Two `setting`s are supported:
354

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

362
`@constprop` can be applied immediately before a function definition or within a function body.
363

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

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

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

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

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

403
"""
404
    @assume_effects setting... [ex]
405

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

410
!!! compat "Julia 1.8"
411
    Using `Base.@assume_effects` requires Julia version 1.8.
412

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

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

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

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

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

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

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

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

485
# Extended help
486

487
---
488
## `:consistent`
489

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

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

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

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

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

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

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

529
---
530
## `:effect_free`
531

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

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

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

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

559
---
560
## `:nothrow`
561

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

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

570
!!! note
571
    `MethodErrors` and similar exceptions count as abnormal termination.
572

573
---
574
## `:terminates_globally`
575

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

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

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

588
---
589
## `:terminates_locally`
590

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

596
!!! note
597
    `:terminates_globally` implies `:terminates_locally`.
598

599
---
600
## `:notaskstate`
601

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

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

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

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

626
---
627
## `:inaccessiblememonly`
628

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

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

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

644
---
645
## `:foldable`
646

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

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

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

664
---
665
## `:removable`
666

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

674
---
675
## `:total`
676

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

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

695
---
696
## Negated effects
697

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

758
function compute_assumed_setting(@nospecialize(setting), val::Bool=true)
23✔
759
    if isexpr(setting, :call) && setting.args[1] === :(!)
111✔
760
        return compute_assumed_setting(setting.args[2], !val)
2✔
761
    elseif isa(setting, QuoteNode)
44✔
762
        return compute_assumed_setting(setting.value, val)
22✔
763
    else
764
        return (setting, val)
22✔
765
    end
766
end
767

768
"""
769
    Base.@nospecializeinfer function f(args...)
770
        @nospecialize ...
771
        ...
772
    end
773
    Base.@nospecializeinfer f(@nospecialize args...) = ...
774

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

778
# Example
779

780
```julia
781
julia> f(A::AbstractArray) = g(A)
782
f (generic function with 1 method)
783

784
julia> @noinline Base.@nospecializeinfer g(@nospecialize(A::AbstractArray)) = A[1]
785
g (generic function with 1 method)
786

787
julia> @code_typed f([1.0])
788
CodeInfo(
789
1 ─ %1 = invoke Main.g(_2::AbstractArray)::Any
790
└──      return %1
791
) => Any
792
```
793

794
In this example, `f` will be inferred for each specific type of `A`,
795
but `g` will only be inferred once with the declared argument type `A::AbstractArray`,
796
meaning that the compiler will not likely see the excessive inference time on it
797
while it can not infer the concrete return type of it.
798
Without the `@nospecializeinfer`, `f([1.0])` would infer the return type of `g` as `Float64`,
799
indicating that inference ran for `g(::Vector{Float64})` despite the prohibition on
800
specialized code generation.
801
"""
802
macro nospecializeinfer(ex)
803
    esc(isa(ex, Expr) ? pushmeta!(ex, :nospecializeinfer) : ex)
804
end
805

806
"""
807
    @propagate_inbounds
808

809
Tells the compiler to inline a function while retaining the caller's inbounds context.
810
"""
811
macro propagate_inbounds(ex)
106✔
812
    if isa(ex, Expr)
106✔
813
        pushmeta!(ex, :inline)
106✔
814
        pushmeta!(ex, :propagate_inbounds)
106✔
815
    end
816
    esc(ex)
106✔
817
end
818

819
"""
820
    @polly
821

822
Tells the compiler to apply the polyhedral optimizer Polly to a function.
823
"""
824
macro polly(ex)
825
    esc(isa(ex, Expr) ? pushmeta!(ex, :polly) : ex)
826
end
827

828
## some macro utilities ##
829

830
unwrap_macrocalls(@nospecialize(x)) = x
1✔
831
function unwrap_macrocalls(ex::Expr)
20✔
832
    inner = ex
×
833
    while inner.head === :macrocall
3,817✔
834
        inner = inner.args[end]::Expr
47✔
835
    end
47✔
836
    return inner
3,770✔
837
end
838

839
function pushmeta!(ex::Expr, sym::Symbol, args::Any...)
1,985✔
840
    if isempty(args)
×
841
        tag = sym
×
842
    else
843
        tag = Expr(sym, args...)::Expr
17✔
844
    end
845

846
    inner = unwrap_macrocalls(ex)
2,016✔
847

848
    idx, exargs = findmeta(inner)
1,985✔
849
    if idx != 0
1,985✔
850
        push!(exargs[idx].args, tag)
272✔
851
    else
852
        body = inner.args[2]::Expr
1,849✔
853
        pushfirst!(body.args, Expr(:meta, tag))
1,849✔
854
    end
855
    ex
1,985✔
856
end
857

858
popmeta!(body, sym) = _getmeta(body, sym, true)
×
859
peekmeta(body, sym) = _getmeta(body, sym, false)
×
860

861
function _getmeta(body::Expr, sym::Symbol, delete::Bool)
×
862
    body.head === :block || return false, []
×
863
    _getmeta(body.args, sym, delete)
×
864
end
865
_getmeta(arg, sym, delete::Bool) = (false, [])
×
866
function _getmeta(body::Array{Any,1}, sym::Symbol, delete::Bool)
×
867
    idx, blockargs = findmeta_block(body, args -> findmetaarg(args,sym)!=0)
×
868
    if idx == 0
×
869
        return false, []
×
870
    end
871
    metaargs = blockargs[idx].args
×
872
    i = findmetaarg(blockargs[idx].args, sym)
×
873
    if i == 0
×
874
        return false, []
×
875
    end
876
    ret = isa(metaargs[i], Expr) ? (metaargs[i]::Expr).args : []
×
877
    if delete
×
878
        deleteat!(metaargs, i)
×
879
        isempty(metaargs) && deleteat!(blockargs, idx)
×
880
    end
881
    true, ret
×
882
end
883

884
# Find index of `sym` in a meta expression argument list, or 0.
885
function findmetaarg(metaargs, sym)
×
886
    for i = 1:length(metaargs)
×
887
        arg = metaargs[i]
×
888
        if (isa(arg, Symbol) && (arg::Symbol)    == sym) ||
×
889
           (isa(arg, Expr)   && (arg::Expr).head == sym)
890
            return i
×
891
        end
892
    end
×
893
    return 0
×
894
end
895

896
function annotate_meta_def_or_block(@nospecialize(ex), meta::Symbol)
1,765✔
897
    inner = unwrap_macrocalls(ex)
1,765✔
898
    if is_function_def(inner)
2,799✔
899
        # annotation on a definition
900
        return esc(pushmeta!(ex, meta))
1,724✔
901
    else
902
        # annotation on a block
903
        return Expr(:block,
41✔
904
                    Expr(meta, true),
905
                    Expr(:local, Expr(:(=), :val, esc(ex))),
906
                    Expr(meta, false),
907
                    :val)
908
    end
909
end
910

911
function is_short_function_def(@nospecialize(ex))
2,208✔
912
    isexpr(ex, :(=)) || return false
2,264✔
913
    while length(ex.args) >= 1 && isa(ex.args[1], Expr)
4,978✔
914
        (ex.args[1].head === :call) && return true
4,976✔
915
        (ex.args[1].head === :where || ex.args[1].head === :(::)) || return false
345✔
916
        ex = ex.args[1]
674✔
917
    end
337✔
918
    return false
1✔
919
end
920
is_function_def(@nospecialize(ex)) =
5,959✔
921
    return isexpr(ex, :function) || is_short_function_def(ex) || isexpr(ex, :->)
922

923
function findmeta(ex::Expr)
1,985✔
924
    if is_function_def(ex)
3,118✔
925
        body = ex.args[2]::Expr
1,985✔
926
        body.head === :block || error(body, " is not a block expression")
1,985✔
927
        return findmeta_block(ex.args)
1,985✔
928
    end
929
    error(ex, " is not a function expression")
×
930
end
931

932
findmeta(ex::Array{Any,1}) = findmeta_block(ex)
×
933

934
function findmeta_block(exargs, argsmatch=args->true)
1,985✔
935
    for i = 1:length(exargs)
13,892✔
936
        a = exargs[i]
11,245✔
937
        if isa(a, Expr)
11,245✔
938
            if a.head === :meta && argsmatch(a.args)
7,167✔
939
                return i, exargs
136✔
940
            elseif a.head === :block
7,031✔
941
                idx, exa = findmeta_block(a.args, argsmatch)
1,985✔
942
                if idx != 0
1,985✔
943
                    return idx, exa
136✔
944
                end
945
            end
946
        end
947
    end
18,248✔
948
    return 0, []
3,698✔
949
end
950

951
remove_linenums!(ex) = ex
48✔
952
function remove_linenums!(ex::Expr)
70,985✔
953
    if ex.head === :block || ex.head === :quote
137,313✔
954
        # remove line number expressions from metadata (not argument literal or inert) position
955
        filter!(ex.args) do x
5,549✔
956
            isa(x, Expr) && x.head === :line && return false
27,028✔
957
            isa(x, LineNumberNode) && return false
27,028✔
958
            return true
14,011✔
959
        end
960
    end
961
    for subex in ex.args
71,337✔
962
        subex isa Expr && remove_linenums!(subex)
154,068✔
963
    end
224,701✔
964
    return ex
70,985✔
965
end
966
function remove_linenums!(src::CodeInfo)
876✔
967
    src.codelocs .= 0
44,693✔
968
    length(src.linetable) > 1 && resize!(src.linetable, 1)
876✔
969
    return src
876✔
970
end
971

972
replace_linenums!(ex, ln::LineNumberNode) = ex
×
973
function replace_linenums!(ex::Expr, ln::LineNumberNode)
1,644✔
974
    if ex.head === :block || ex.head === :quote
2,877✔
975
        # replace line number expressions from metadata (not argument literal or inert) position
976
        map!(ex.args, ex.args) do @nospecialize(x)
822✔
977
            isa(x, Expr) && x.head === :line && length(x.args) == 1 && return Expr(:line, ln.line)
822✔
978
            isa(x, Expr) && x.head === :line && length(x.args) == 2 && return Expr(:line, ln.line, ln.file)
822✔
979
            isa(x, LineNumberNode) && return ln
822✔
980
            return x
411✔
981
        end
982
    end
983
    # preserve any linenums inside `esc(...)` guards
984
    if ex.head !== :escape
1,644✔
985
        for subex in ex.args
1,644✔
986
            subex isa Expr && replace_linenums!(subex, ln)
1,644✔
987
        end
2,466✔
988
    end
989
    return ex
1,644✔
990
end
991

992
macro generated()
2✔
993
    return Expr(:generated)
2✔
994
end
995

996
"""
997
    @generated f
998

999
`@generated` is used to annotate a function which will be generated.
1000
In the body of the generated function, only types of arguments can be read
1001
(not the values). The function returns a quoted expression evaluated when the
1002
function is called. The `@generated` macro should not be used on functions mutating
1003
the global scope or depending on mutable elements.
1004

1005
See [Metaprogramming](@ref) for further details.
1006

1007
# Examples
1008
```jldoctest
1009
julia> @generated function bar(x)
1010
           if x <: Integer
1011
               return :(x ^ 2)
1012
           else
1013
               return :(x)
1014
           end
1015
       end
1016
bar (generic function with 1 method)
1017

1018
julia> bar(4)
1019
16
1020

1021
julia> bar("baz")
1022
"baz"
1023
```
1024
"""
1025
macro generated(f)
46✔
1026
    if isa(f, Expr) && (f.head === :function || is_short_function_def(f))
61✔
1027
        body = f.args[2]
46✔
1028
        lno = body.args[1]
46✔
1029
        tmp = gensym("tmp")
46✔
1030
        return Expr(:escape,
46✔
1031
                    Expr(f.head, f.args[1],
1032
                         Expr(:block,
1033
                              lno,
1034
                              Expr(:if, Expr(:generated),
1035
                                   body,
1036
                                   Expr(:block,
1037
                                        Expr(:meta, :generated_only),
1038
                                        Expr(:return, nothing))))))
1039
    else
1040
        error("invalid syntax; @generated must be used with a function definition")
×
1041
    end
1042
end
1043

1044

1045
"""
1046
    @atomic var
1047
    @atomic order ex
1048

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

1052
    @atomic a.b.x = new
1053
    @atomic a.b.x += addend
1054
    @atomic :release a.b.x = new
1055
    @atomic :acquire_release a.b.x += addend
1056

1057
Perform the store operation expressed on the right atomically and return the
1058
new value.
1059

1060
With `=`, this operation translates to a `setproperty!(a.b, :x, new)` call.
1061
With any operator also, this operation translates to a `modifyproperty!(a.b,
1062
:x, +, addend)[2]` call.
1063

1064
    @atomic a.b.x max arg2
1065
    @atomic a.b.x + arg2
1066
    @atomic max(a.b.x, arg2)
1067
    @atomic :acquire_release max(a.b.x, arg2)
1068
    @atomic :acquire_release a.b.x + arg2
1069
    @atomic :acquire_release a.b.x max arg2
1070

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

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

1076

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

1079
# Examples
1080
```jldoctest
1081
julia> mutable struct Atomic{T}; @atomic x::T; end
1082

1083
julia> a = Atomic(1)
1084
Atomic{Int64}(1)
1085

1086
julia> @atomic a.x # fetch field x of a, with sequential consistency
1087
1
1088

1089
julia> @atomic :sequentially_consistent a.x = 2 # set field x of a, with sequential consistency
1090
2
1091

1092
julia> @atomic a.x += 1 # increment field x of a, with sequential consistency
1093
3
1094

1095
julia> @atomic a.x + 1 # increment field x of a, with sequential consistency
1096
3 => 4
1097

1098
julia> @atomic a.x # fetch field x of a, with sequential consistency
1099
4
1100

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

1104
julia> @atomic a.x max 5 # again change field x of a to the max value, with sequential consistency
1105
10 => 10
1106
```
1107

1108
!!! compat "Julia 1.7"
1109
    This functionality requires at least Julia 1.7.
1110
"""
1111
macro atomic(ex)
37✔
1112
    if !isa(ex, Symbol) && !is_expr(ex, :(::))
37✔
1113
        return make_atomic(QuoteNode(:sequentially_consistent), ex)
29✔
1114
    end
1115
    return esc(Expr(:atomic, ex))
8✔
1116
end
1117
macro atomic(order, ex)
17✔
1118
    order isa QuoteNode || (order = esc(order))
17✔
1119
    return make_atomic(order, ex)
17✔
1120
end
1121
macro atomic(a1, op, a2)
3✔
1122
    return make_atomic(QuoteNode(:sequentially_consistent), a1, op, a2)
3✔
1123
end
1124
macro atomic(order, a1, op, a2)
2✔
1125
    order isa QuoteNode || (order = esc(order))
2✔
1126
    return make_atomic(order, a1, op, a2)
2✔
1127
end
1128
function make_atomic(order, ex)
46✔
1129
    @nospecialize
×
1130
    if ex isa Expr
46✔
1131
        if isexpr(ex, :., 2)
75✔
1132
            l, r = esc(ex.args[1]), esc(ex.args[2])
17✔
1133
            return :(getproperty($l, $r, $order))
17✔
1134
        elseif isexpr(ex, :call, 3)
49✔
1135
            return make_atomic(order, ex.args[2], ex.args[1], ex.args[3])
5✔
1136
        elseif ex.head === :(=)
24✔
1137
            l, r = ex.args[1], esc(ex.args[2])
13✔
1138
            if is_expr(l, :., 2)
13✔
1139
                ll, lr = esc(l.args[1]), esc(l.args[2])
12✔
1140
                return :(setproperty!($ll, $lr, $r, $order))
12✔
1141
            end
1142
        end
1143
        if length(ex.args) == 2
12✔
1144
            if ex.head === :(+=)
9✔
1145
                op = :+
5✔
1146
            elseif ex.head === :(-=)
4✔
1147
                op = :-
1✔
1148
            elseif @isdefined string
3✔
1149
                shead = string(ex.head)
3✔
1150
                if endswith(shead, '=')
3✔
1151
                    op = Symbol(shead[1:prevind(shead, end)])
2✔
1152
                end
1153
            end
1154
            if @isdefined(op)
9✔
1155
                return Expr(:ref, make_atomic(order, ex.args[1], op, ex.args[2]), 2)
8✔
1156
            end
1157
        end
1158
    end
1159
    error("could not parse @atomic expression $ex")
4✔
1160
end
1161
function make_atomic(order, a1, op, a2)
18✔
1162
    @nospecialize
×
1163
    is_expr(a1, :., 2) || error("@atomic modify expression missing field access")
22✔
1164
    a1l, a1r, op, a2 = esc(a1.args[1]), esc(a1.args[2]), esc(op), esc(a2)
14✔
1165
    return :(modifyproperty!($a1l, $a1r, $op, $a2, $order))
14✔
1166
end
1167

1168

1169
"""
1170
    @atomicswap a.b.x = new
1171
    @atomicswap :sequentially_consistent a.b.x = new
1172

1173
Stores `new` into `a.b.x` and returns the old value of `a.b.x`.
1174

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

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

1179
# Examples
1180
```jldoctest
1181
julia> mutable struct Atomic{T}; @atomic x::T; end
1182

1183
julia> a = Atomic(1)
1184
Atomic{Int64}(1)
1185

1186
julia> @atomicswap a.x = 2+2 # replace field x of a with 4, with sequential consistency
1187
1
1188

1189
julia> @atomic a.x # fetch field x of a, with sequential consistency
1190
4
1191
```
1192

1193
!!! compat "Julia 1.7"
1194
    This functionality requires at least Julia 1.7.
1195
"""
1196
macro atomicswap(order, ex)
2✔
1197
    order isa QuoteNode || (order = esc(order))
2✔
1198
    return make_atomicswap(order, ex)
2✔
1199
end
1200
macro atomicswap(ex)
1✔
1201
    return make_atomicswap(QuoteNode(:sequentially_consistent), ex)
1✔
1202
end
1203
function make_atomicswap(order, ex)
3✔
1204
    @nospecialize
×
1205
    is_expr(ex, :(=), 2) || error("@atomicswap expression missing assignment")
3✔
1206
    l, val = ex.args[1], esc(ex.args[2])
3✔
1207
    is_expr(l, :., 2) || error("@atomicswap expression missing field access")
3✔
1208
    ll, lr = esc(l.args[1]), esc(l.args[2])
3✔
1209
    return :(swapproperty!($ll, $lr, $val, $order))
3✔
1210
end
1211

1212

1213
"""
1214
    @atomicreplace a.b.x expected => desired
1215
    @atomicreplace :sequentially_consistent a.b.x expected => desired
1216
    @atomicreplace :sequentially_consistent :monotonic a.b.x expected => desired
1217

1218
Perform the conditional replacement expressed by the pair atomically, returning
1219
the values `(old, success::Bool)`. Where `success` indicates whether the
1220
replacement was completed.
1221

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

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

1226
# Examples
1227
```jldoctest
1228
julia> mutable struct Atomic{T}; @atomic x::T; end
1229

1230
julia> a = Atomic(1)
1231
Atomic{Int64}(1)
1232

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

1236
julia> @atomic a.x # fetch field x of a, with sequential consistency
1237
2
1238

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

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

1244
julia> @atomicreplace a.x xchg
1245
(old = 2, success = true)
1246

1247
julia> @atomic a.x # fetch field x of a, with sequential consistency
1248
0
1249
```
1250

1251
!!! compat "Julia 1.7"
1252
    This functionality requires at least Julia 1.7.
1253
"""
1254
macro atomicreplace(success_order, fail_order, ex, old_new)
4✔
1255
    fail_order isa QuoteNode || (fail_order = esc(fail_order))
4✔
1256
    success_order isa QuoteNode || (success_order = esc(success_order))
4✔
1257
    return make_atomicreplace(success_order, fail_order, ex, old_new)
4✔
1258
end
1259
macro atomicreplace(order, ex, old_new)
4✔
1260
    order isa QuoteNode || (order = esc(order))
4✔
1261
    return make_atomicreplace(order, order, ex, old_new)
4✔
1262
end
1263
macro atomicreplace(ex, old_new)
6✔
1264
    return make_atomicreplace(QuoteNode(:sequentially_consistent), QuoteNode(:sequentially_consistent), ex, old_new)
6✔
1265
end
1266
function make_atomicreplace(success_order, fail_order, ex, old_new)
14✔
1267
    @nospecialize
×
1268
    is_expr(ex, :., 2) || error("@atomicreplace expression missing field access")
15✔
1269
    ll, lr = esc(ex.args[1]), esc(ex.args[2])
13✔
1270
    if is_expr(old_new, :call, 3) && old_new.args[1] === :(=>)
13✔
1271
        exp, rep = esc(old_new.args[2]), esc(old_new.args[3])
8✔
1272
        return :(replaceproperty!($ll, $lr, $exp, $rep, $success_order, $fail_order))
8✔
1273
    else
1274
        old_new = esc(old_new)
5✔
1275
        return :(replaceproperty!($ll, $lr, $old_new::Pair..., $success_order, $fail_order))
5✔
1276
    end
1277
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