• 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

76.47
/base/experimental.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
"""
4
    Experimental
5

6
!!! warning
7
    Types, methods, or macros defined in this module are experimental and subject
8
    to change and will not have deprecations. Caveat emptor.
9
"""
10
module Experimental
11

12
using Base: Threads, sync_varname
13
using Base.Meta
14

15
"""
16
    Const(A::Array)
17

18
Mark an Array as constant/read-only. The invariant guaranteed is that you will not
19
modify an Array (through another reference) within an `@aliasscope` scope.
20

21
!!! warning
22
    Experimental API. Subject to change without deprecation.
23
"""
24
struct Const{T,N} <: DenseArray{T,N}
25
    a::Array{T,N}
26
end
27

28
Base.IndexStyle(::Type{<:Const}) = IndexLinear()
×
29
Base.size(C::Const) = size(C.a)
×
30
Base.axes(C::Const) = axes(C.a)
×
31
@eval Base.getindex(A::Const, i1::Int) =
×
32
    (Base.@inline; Core.const_arrayref($(Expr(:boundscheck)), A.a, i1))
×
33
@eval Base.getindex(A::Const, i1::Int, i2::Int, I::Int...) =
×
34
  (Base.@inline; Core.const_arrayref($(Expr(:boundscheck)), A.a, i1, i2, I...))
×
35

36
"""
37
    @aliasscope expr
38

39
Allows the compiler to assume that all `Const`s are not being modified through stores
40
within this scope, even if the compiler can't prove this to be the case.
41

42
!!! warning
43
    Experimental API. Subject to change without deprecation.
44
"""
45
macro aliasscope(body)
46
    sym = gensym()
47
    quote
48
        $(Expr(:aliasscope))
49
        $sym = $(esc(body))
50
        $(Expr(:popaliasscope))
51
        $sym
52
    end
53
end
54

55

56
function sync_end(c::Channel{Any})
50✔
57
    if !isready(c)
50✔
58
        # there must be at least one item to begin with
59
        close(c)
×
60
        return
×
61
    end
62
    nremaining::Int = 0
50✔
63
    while true
1,646✔
64
        event = take!(c)
1,646✔
65
        if event === :__completion__
1,646✔
66
            nremaining -= 1
823✔
67
            if nremaining == 0
823✔
68
                break
823✔
69
            end
70
        else
71
            nremaining += 1
823✔
72
            schedule(Task(()->begin
1,646✔
73
                try
823✔
74
                    wait(event)
823✔
75
                    put!(c, :__completion__)
823✔
76
                catch e
77
                    close(c, e)
×
78
                end
79
            end))
80
        end
81
    end
1,596✔
82
    close(c)
50✔
83
    nothing
50✔
84
end
85

86
"""
87
    Experimental.@sync
88

89
Wait until all lexically-enclosed uses of `@async`, `@spawn`, `@spawnat` and `@distributed`
90
are complete, or at least one of them has errored. The first exception is immediately
91
rethrown. It is the responsibility of the user to cancel any still-running operations
92
during error handling.
93

94
!!! Note
95
    This interface is experimental and subject to change or removal without notice.
96
"""
97
macro sync(block)
9✔
98
    var = esc(sync_varname)
9✔
99
    quote
9✔
100
        let $var = Channel(Inf)
45✔
101
            v = $(esc(block))
692✔
102
            sync_end($var)
45✔
103
            v
20✔
104
        end
105
    end
106
end
107

108
"""
109
    Experimental.@optlevel n::Int
110

111
Set the optimization level (equivalent to the `-O` command line argument)
112
for code in the current module. Submodules inherit the setting of their
113
parent module.
114

115
Supported values are 0, 1, 2, and 3.
116

117
The effective optimization level is the minimum of that specified on the
118
command line and in per-module settings. If a `--min-optlevel` value is
119
set on the command line, that is enforced as a lower bound.
120
"""
121
macro optlevel(n::Int)
122
    return Expr(:meta, :optlevel, n)
123
end
124

125
"""
126
    Experimental.@max_methods n::Int
127

128
Set the maximum number of potentially-matching methods considered when running inference
129
for methods defined in the current module. This setting affects inference of calls with
130
incomplete knowledge of the argument types.
131

132
The benefit of this setting is to avoid excessive compilation and reduce invalidation risks
133
in poorly-inferred cases. For example, when `@max_methods 2` is set and there are two
134
potentially-matching methods returning different types inside a function body, then Julia
135
will compile subsequent calls for both types so that the compiled function body accounts
136
for both possibilities. Also the compiled code is vulnerable to invalidations that would
137
happen when either of the two methods gets invalidated. This speculative compilation and
138
these invalidations can be avoided by setting `@max_methods 1` and allowing the compiled
139
code to resort to runtime dispatch instead.
140

141
Supported values are `1`, `2`, `3`, `4`, and `default` (currently equivalent to `3`).
142
"""
143
macro max_methods(n::Int)
144
    0 < n < 5 || error("We must have that `1 <= max_methods <= 4`, but `max_methods = $n`.")
145
    return Expr(:meta, :max_methods, n)
146
end
147

148
"""
149
    Experimental.@max_methods n::Int function fname end
150

151
Set the maximum number of potentially-matching methods considered when running inference
152
for the generic function `fname`. Overrides any module-level or global inference settings
153
for max_methods. This setting is global for the entire generic function (or more precisely
154
the MethodTable).
155
"""
156
macro max_methods(n::Int, fdef::Expr)
157
    0 < n <= 255 || error("We must have that `1 <= max_methods <= 255`, but `max_methods = $n`.")
158
    (fdef.head === :function && length(fdef.args) == 1) || error("Second argument must be a function forward declaration")
159
    return :(typeof($(esc(fdef))).name.max_methods = $(UInt8(n)))
160
end
161

162
"""
163
    Experimental.@compiler_options optimize={0,1,2,3} compile={yes,no,all,min} infer={yes,no} max_methods={default,1,2,3,...}
164

165
Set compiler options for code in the enclosing module. Options correspond directly to
166
command-line options with the same name, where applicable. The following options
167
are currently supported:
168

169
  * `optimize`: Set optimization level.
170
  * `compile`: Toggle native code compilation. Currently only `min` is supported, which
171
    requests the minimum possible amount of compilation.
172
  * `infer`: Enable or disable type inference. If disabled, implies [`@nospecialize`](@ref).
173
  * `max_methods`: Maximum number of matching methods considered when running type inference.
174
"""
175
macro compiler_options(args...)
16✔
176
    opts = Expr(:block)
16✔
177
    for ex in args
16✔
178
        if isa(ex, Expr) && ex.head === :(=) && length(ex.args) == 2
47✔
179
            if ex.args[1] === :optimize
47✔
180
                push!(opts.args, Expr(:meta, :optlevel, ex.args[2]::Int))
16✔
181
            elseif ex.args[1] === :compile
31✔
182
                a = ex.args[2]
16✔
183
                a = #a === :no  ? 0 :
16✔
184
                    #a === :yes ? 1 :
185
                    #a === :all ? 2 :
186
                    a === :min ? 3 : error("invalid argument to \"compile\" option")
187
                push!(opts.args, Expr(:meta, :compile, a))
16✔
188
            elseif ex.args[1] === :infer
15✔
189
                a = ex.args[2]
15✔
190
                a = a === false || a === :no  ? 0 :
15✔
191
                    a === true  || a === :yes ? 1 : error("invalid argument to \"infer\" option")
192
                push!(opts.args, Expr(:meta, :infer, a))
15✔
193
            elseif ex.args[1] === :max_methods
×
194
                a = ex.args[2]
×
195
                a = a === :default ? 3 :
×
196
                  a isa Int ? ((0 < a < 5) ? a : error("We must have that `1 <= max_methods <= 4`, but `max_methods = $a`.")) :
197
                  error("invalid argument to \"max_methods\" option")
198
                push!(opts.args, Expr(:meta, :max_methods, a))
×
199
            else
200
                error("unknown option \"$(ex.args[1])\"")
47✔
201
            end
202
        else
203
            error("invalid option syntax")
×
204
        end
205
    end
47✔
206
    return opts
16✔
207
end
208

209
"""
210
    Experimental.@force_compile
211

212
Force compilation of the block or function (Julia's built-in interpreter is blocked from executing it).
213

214
# Examples
215

216
```
217
julia> occursin("interpreter", string(stacktrace(begin
218
           # with forced compilation
219
           Base.Experimental.@force_compile
220
           backtrace()
221
       end, true)))
222
false
223

224
julia> occursin("interpreter", string(stacktrace(begin
225
           # without forced compilation
226
           backtrace()
227
       end, true)))
228
true
229
```
230
"""
231
macro force_compile() Expr(:meta, :force_compile) end
234✔
232

233
# UI features for errors
234

235
"""
236
    Experimental.register_error_hint(handler, exceptiontype)
237

238
Register a "hinting" function `handler(io, exception)` that can
239
suggest potential ways for users to circumvent errors.  `handler`
240
should examine `exception` to see whether the conditions appropriate
241
for a hint are met, and if so generate output to `io`.
242
Packages should call `register_error_hint` from within their
243
`__init__` function.
244

245
For specific exception types, `handler` is required to accept additional arguments:
246

247
- `MethodError`: provide `handler(io, exc::MethodError, argtypes, kwargs)`,
248
  which splits the combined arguments into positional and keyword arguments.
249

250
When issuing a hint, the output should typically start with `\\n`.
251

252
If you define custom exception types, your `showerror` method can
253
support hints by calling [`Experimental.show_error_hints`](@ref).
254

255
# Example
256

257
```
258
julia> module Hinter
259

260
       only_int(x::Int)      = 1
261
       any_number(x::Number) = 2
262

263
       function __init__()
264
           Base.Experimental.register_error_hint(MethodError) do io, exc, argtypes, kwargs
265
               if exc.f == only_int
266
                    # Color is not necessary, this is just to show it's possible.
267
                    print(io, "\\nDid you mean to call ")
268
                    printstyled(io, "`any_number`?", color=:cyan)
269
               end
270
           end
271
       end
272

273
       end
274
```
275

276
Then if you call `Hinter.only_int` on something that isn't an `Int` (thereby triggering a `MethodError`), it issues the hint:
277

278
```
279
julia> Hinter.only_int(1.0)
280
ERROR: MethodError: no method matching only_int(::Float64)
281
Did you mean to call `any_number`?
282
Closest candidates are:
283
    ...
284
```
285

286
!!! compat "Julia 1.5"
287
    Custom error hints are available as of Julia 1.5.
288
!!! warning
289
    This interface is experimental and subject to change or removal without notice.
290
    To insulate yourself against changes, consider putting any registrations inside an
291
    `if isdefined(Base.Experimental, :register_error_hint) ... end` block.
292
"""
293
function register_error_hint(@nospecialize(handler), @nospecialize(exct::Type))
×
294
    list = get!(Vector{Any}, _hint_handlers, exct)
443✔
295
    push!(list, handler)
443✔
296
    return nothing
443✔
297
end
298

299
const _hint_handlers = IdDict{Type,Vector{Any}}()
300

301
"""
302
    Experimental.show_error_hints(io, ex, args...)
303

304
Invoke all handlers from [`Experimental.register_error_hint`](@ref) for the particular
305
exception type `typeof(ex)`. `args` must contain any other arguments expected by
306
the handler for that type.
307

308
!!! compat "Julia 1.5"
309
    Custom error hints are available as of Julia 1.5.
310
!!! warning
311
    This interface is experimental and subject to change or removal without notice.
312
"""
313
function show_error_hints(io, ex, args...)
77✔
314
    hinters = get(_hint_handlers, typeof(ex), nothing)
136✔
315
    isnothing(hinters) && return
136✔
316
    for handler in hinters
59✔
317
        try
115✔
318
            Base.invokelatest(handler, io, ex, args...)
115✔
319
        catch err
320
            tn = typeof(handler).name
×
321
            @error "Hint-handler $handler for $(typeof(ex)) in $(tn.module) caused an error"
×
322
        end
323
    end
115✔
324
end
325

326
# OpaqueClosure
327
include("opaque_closure.jl")
328

329
"""
330
    Experimental.@overlay mt [function def]
331

332
Define a method and add it to the method table `mt` instead of to the global method table.
333
This can be used to implement a method override mechanism. Regular compilation will not
334
consider these methods, and you should customize the compilation flow to look in these
335
method tables (e.g., using [`Core.Compiler.OverlayMethodTable`](@ref)).
336

337
"""
338
macro overlay(mt, def)
8✔
339
    def = macroexpand(__module__, def) # to expand @inline, @generated, etc
8✔
340
    if !isexpr(def, [:function, :(=)])
8✔
341
        error("@overlay requires a function Expr")
×
342
    end
343
    if isexpr(def.args[1], :call)
8✔
344
        def.args[1].args[1] = Expr(:overlay, mt, def.args[1].args[1])
6✔
345
    elseif isexpr(def.args[1], :where)
2✔
346
        def.args[1].args[1].args[1] = Expr(:overlay, mt, def.args[1].args[1].args[1])
2✔
347
    else
348
        error("@overlay requires a function Expr")
×
349
    end
350
    esc(def)
8✔
351
end
352

353
let new_mt(name::Symbol, mod::Module) = begin
5✔
354
        ccall(:jl_check_top_level_effect, Cvoid, (Any, Cstring), mod, "@MethodTable")
5✔
355
        ccall(:jl_new_method_table, Any, (Any, Any), name, mod)
5✔
356
    end
357
    @eval macro MethodTable(name::Symbol)
5✔
358
        esc(:(const $name = $$new_mt($(quot(name)), $(__module__))))
5✔
359
    end
360
end
361

362
"""
363
    Experimental.@MethodTable(name)
364

365
Create a new MethodTable in the current module, bound to `name`. This method table can be
366
used with the [`Experimental.@overlay`](@ref) macro to define methods for a function without
367
adding them to the global method table.
368
"""
369
:@MethodTable
370

371
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