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

JuliaLang / julia / #38002

06 Feb 2025 06:14AM UTC coverage: 20.322% (-2.4%) from 22.722%
#38002

push

local

web-flow
bpart: Fully switch to partitioned semantics (#57253)

This is the final PR in the binding partitions series (modulo bugs and
tweaks), i.e. it closes #54654 and thus closes #40399, which was the
original design sketch.

This thus activates the full designed semantics for binding partitions,
in particular allowing safe replacement of const bindings. It in
particular allows struct redefinitions. This thus closes
timholy/Revise.jl#18 and also closes #38584.

The biggest semantic change here is probably that this gets rid of the
notion of "resolvedness" of a binding. Previously, a lot of the behavior
of our implementation depended on when bindings were "resolved", which
could happen at basically an arbitrary point (in the compiler, in REPL
completion, in a different thread), making a lot of the semantics around
bindings ill- or at least implementation-defined. There are several
related issues in the bugtracker, so this closes #14055 closes #44604
closes #46354 closes #30277

It is also the last step to close #24569.
It also supports bindings for undef->defined transitions and thus closes
#53958 closes #54733 - however, this is not activated yet for
performance reasons and may need some further optimization.

Since resolvedness no longer exists, we need to replace it with some
hopefully more well-defined semantics. I will describe the semantics
below, but before I do I will make two notes:

1. There are a number of cases where these semantics will behave
slightly differently than the old semantics absent some other task going
around resolving random bindings.
2. The new behavior (except for the replacement stuff) was generally
permissible under the old semantics if the bindings happened to be
resolved at the right time.

With all that said, there are essentially three "strengths" of bindings:

1. Implicit Bindings: Anything implicitly obtained from `using Mod`, "no
binding", plus slightly more exotic corner cases around conflicts

2. Weakly declared bindin... (continued)

11 of 111 new or added lines in 7 files covered. (9.91%)

1273 existing lines in 68 files now uncovered.

9908 of 48755 relevant lines covered (20.32%)

105126.48 hits per line

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

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

3
# pseudo-definitions to show how everything behaves
4
#
5
# throw(label, val) = # throw a value to a dynamically enclosing block
6
#
7
# function rethrow(val)
8
#     global current_exception = val
9
#     throw(current_handler(), current_exception)
10
# end
11
#
12
# rethrow() = rethrow(current_exception)
13
#
14
# function throw(val)
15
#     global catch_backtrace = backtrace()
16
#     rethrow(val)
17
# end
18

19
"""
20
    throw(e)
21

22
Throw an object as an exception.
23

24
See also: [`rethrow`](@ref), [`error`](@ref).
25
"""
26
throw
27

28
## native julia error handling ##
29

30
# This is `Experimental.@max_methods 2 function error end`, which is not available at this point in bootstrap.
31
# NOTE It is important to always be able to infer the return type of `error` as `Union{}`,
32
# but there's a hitch when a package globally sets `@max_methods 1` and it causes inference
33
# for `error(::Any)` to fail (JuliaLang/julia#54029).
34
# This definition site `@max_methods 2` setting overrides any global `@max_methods 1` settings
35
# on package side, guaranteeing that return type inference on `error` is successful always.
36
function error end
37
typeof(error).name.max_methods = UInt8(2)
38

39
"""
40
    error(message::AbstractString)
41

42
Raise an `ErrorException` with the given message.
43
"""
UNCOV
44
error(s::AbstractString) = throw(ErrorException(s))
×
45
error() = throw(ErrorException(""))
×
46

47
"""
48
    error(msg...)
49

50
Raise an `ErrorException` with a message constructed by `string(msg...)`.
51
"""
52
function error(s::Vararg{Any,N}) where {N}
×
53
    @noinline
×
54
    throw(ErrorException(Main.Base.string(s...)))
×
55
end
56

57
"""
58
    rethrow()
59

60
Rethrow the current exception from within a `catch` block. The rethrown
61
exception will continue propagation as if it had not been caught.
62

63
!!! note
64
    The alternative form `rethrow(e)` allows you to associate an alternative
65
    exception object `e` with the current backtrace. However this misrepresents
66
    the program state at the time of the error so you're encouraged to instead
67
    throw a new exception using `throw(e)`. In Julia 1.1 and above, using
68
    `throw(e)` will preserve the root cause exception on the stack, as
69
    described in [`current_exceptions`](@ref).
70
"""
UNCOV
71
rethrow() = ccall(:jl_rethrow, Bottom, ())
×
72
rethrow(@nospecialize(e)) = ccall(:jl_rethrow_other, Bottom, (Any,), e)
×
73

74
struct InterpreterIP
75
    code::Union{CodeInfo,Core.MethodInstance,Core.CodeInstance,Nothing}
1✔
76
    stmt::Csize_t
77
    mod::Union{Module,Nothing}
78
end
79

80
# convert dual arrays (raw bt buffer, array of GC managed values) to a single
81
# array of locations
82
function _reformat_bt(bt::Array{Ptr{Cvoid},1}, bt2::Array{Any,1})
1✔
83
    ret = Vector{Union{InterpreterIP,Ptr{Cvoid}}}()
1✔
84
    i, j = 1, 1
×
85
    while i <= length(bt)
22✔
86
        ip = bt[i]::Ptr{Cvoid}
21✔
87
        if UInt(ip) != (-1 % UInt) # See also jl_bt_is_native
21✔
88
            # native frame
89
            push!(ret, ip)
20✔
90
            i += 1
20✔
91
            continue
20✔
92
        end
93
        # Extended backtrace entry
94
        entry_metadata = reinterpret(UInt, bt[i+1])::UInt
1✔
95
        njlvalues =  entry_metadata & 0x7
1✔
96
        nuintvals = (entry_metadata >> 3) & 0x7
1✔
97
        tag       = (entry_metadata >> 6) & 0xf
1✔
98
        header    =  entry_metadata >> 10
1✔
99
        if tag == 1 # JL_BT_INTERP_FRAME_TAG
1✔
100
            code = bt2[j]::Union{CodeInfo,Core.MethodInstance,Core.CodeInstance,Nothing}
1✔
101
            mod = njlvalues == 2 ? bt2[j+1]::Union{Module,Nothing} : nothing
1✔
102
            push!(ret, InterpreterIP(code, header, mod))
2✔
103
        else
104
            # Tags we don't know about are an error
105
            throw(ArgumentError("Unexpected extended backtrace entry tag $tag at bt[$i]"))
×
106
        end
107
        # See jl_bt_entry_size
108
        j += Int(njlvalues)
1✔
109
        i += 2 + Int(njlvalues + nuintvals)
1✔
110
    end
21✔
111
    ret
×
112
end
113

114
"""
115
    backtrace()
116

117
Get a backtrace object for the current program point.
118
"""
119
function backtrace()
×
120
    @noinline
×
121
    # skip frame for backtrace(). Note that for this to work properly,
122
    # backtrace() itself must not be interpreted nor inlined.
123
    skip = 1
×
124
    bt1, bt2 = ccall(:jl_backtrace_from_here, Ref{SimpleVector}, (Cint, Cint), false, skip)
×
125
    return _reformat_bt(bt1::Vector{Ptr{Cvoid}}, bt2::Vector{Any})
×
126
end
127

128
"""
129
    catch_backtrace()
130

131
Get the backtrace of the current exception, for use within `catch` blocks.
132
"""
133
function catch_backtrace()
×
134
    bt, bt2 = ccall(:jl_get_backtrace, Ref{SimpleVector}, ())
×
135
    return _reformat_bt(bt::Vector{Ptr{Cvoid}}, bt2::Vector{Any})
×
136
end
137

138
struct ExceptionStack <: AbstractArray{Any,1}
139
    stack::Array{Any,1}
2✔
140
end
141

142
"""
143
    current_exceptions(task::Task=current_task(); [backtrace::Bool=true])
144

145
Get the stack of exceptions currently being handled. For nested catch blocks
146
there may be more than one current exception in which case the most recently
147
thrown exception is last in the stack. The stack is returned as an
148
`ExceptionStack` which is an AbstractVector of named tuples
149
`(exception,backtrace)`. If `backtrace` is false, the backtrace in each pair
150
will be set to `nothing`.
151

152
Explicitly passing `task` will return the current exception stack on an
153
arbitrary task. This is useful for inspecting tasks which have failed due to
154
uncaught exceptions.
155

156
!!! compat "Julia 1.7"
157
    This function went by the experimental name `catch_stack()` in Julia
158
    1.1–1.6, and had a plain Vector-of-tuples as a return type.
159
"""
160
function current_exceptions(task::Task=current_task(); backtrace::Bool=true)
7✔
161
    raw = ccall(:jl_get_excstack, Any, (Any,Cint,Cint), task, backtrace, typemax(Cint))::Vector{Any}
1✔
162
    formatted = Any[]
1✔
163
    stride = backtrace ? 3 : 1
1✔
164
    for i = reverse(1:stride:length(raw))
1✔
165
        exc = raw[i]
1✔
166
        bt = backtrace ? Base._reformat_bt(raw[i+1],raw[i+2]) : nothing
1✔
167
        push!(formatted, (exception=exc,backtrace=bt))
1✔
168
    end
1✔
169
    ExceptionStack(formatted)
1✔
170
end
171

172
## keyword arg lowering generates calls to this ##
173
function kwerr(kw, args::Vararg{Any,N}) where {N}
×
174
    @noinline
×
175
    throw(MethodError(Core.kwcall, (kw, args...), tls_world_age()))
×
176
end
177

178
## system error handling ##
179
"""
180
    systemerror(sysfunc[, errno::Cint=Libc.errno()])
181
    systemerror(sysfunc, iftrue::Bool)
182

183
Raises a `SystemError` for `errno` with the descriptive string `sysfunc` if `iftrue` is `true`
184
"""
185
systemerror(p, b::Bool; extrainfo=nothing) = b ? systemerror(p, extrainfo=extrainfo) : nothing
86✔
186
systemerror(p, errno::Cint=Libc.errno(); extrainfo=nothing) = throw(Main.Base.SystemError(string(p), errno, extrainfo))
×
187

188
## system errors from Windows API functions
189
struct WindowsErrorInfo
190
    errnum::UInt32
191
    extrainfo
192
end
193
"""
194
    windowserror(sysfunc[, code::UInt32=Libc.GetLastError()])
195
    windowserror(sysfunc, iftrue::Bool)
196

197
Like [`systemerror`](@ref), but for Windows API functions that use [`GetLastError`](@ref Base.Libc.GetLastError) to
198
return an error code instead of setting [`errno`](@ref Base.Libc.errno).
199
"""
200
windowserror(p, b::Bool; extrainfo=nothing) = b ? windowserror(p, extrainfo=extrainfo) : nothing
×
201
windowserror(p, code::UInt32=Libc.GetLastError(); extrainfo=nothing) = throw(Main.Base.SystemError(string(p), 0, WindowsErrorInfo(code, extrainfo)))
×
202

203

204
## assertion macro ##
205

206

207
"""
208
    @assert cond [text]
209

210
Throw an [`AssertionError`](@ref) if `cond` is `false`. This is the preferred syntax for
211
writing assertions, which are conditions that are assumed to be true, but that the user
212
might decide to check anyways, as an aid to debugging if they fail.
213
The optional message `text` is displayed upon assertion failure.
214

215
!!! warning
216
    An assert might be disabled at some optimization levels.
217
    Assert should therefore only be used as a debugging tool
218
    and not used for authentication verification (e.g., verifying passwords or checking array bounds).
219
    The code must not rely on the side effects of running `cond` for the correct behavior
220
    of a function.
221

222
# Examples
223
```jldoctest
224
julia> @assert iseven(3) "3 is an odd number!"
225
ERROR: AssertionError: 3 is an odd number!
226

227
julia> @assert isodd(3) "What even are numbers?"
228
```
229
"""
230
macro assert(ex, msgs...)
231
    msg = isempty(msgs) ? ex : msgs[1]
232
    if isa(msg, AbstractString)
233
        msg = msg # pass-through
234
    elseif !isempty(msgs) && (isa(msg, Expr) || isa(msg, Symbol))
235
        # message is an expression needing evaluating
236
        # N.B. To reduce the risk of invalidation caused by the complex callstack involved
237
        # with `string`, use `inferencebarrier` here to hide this `string` from the compiler.
238
        msg = :(Main.Base.inferencebarrier(Main.Base.string)($(esc(msg))))
239
    elseif isdefined(Main, :Base) && isdefined(Main.Base, :string) && applicable(Main.Base.string, msg)
240
        msg = Main.Base.string(msg)
241
    else
242
        # string() might not be defined during bootstrap
243
        msg = :(Main.Base.inferencebarrier(_assert_tostring)($(Expr(:quote,msg))))
244
    end
245
    return :($(esc(ex)) ? $(nothing) : throw(AssertionError($msg)))
246
end
247

248
# this may be overridden in contexts where `string(::Expr)` doesn't work
249
_assert_tostring(msg) = isdefined(Main, :Base) ? Main.Base.string(msg) :
×
250
    (Core.println(msg); "Error during bootstrap. See stdout.")
×
251

252
struct ExponentialBackOff
253
    n::Int
254
    first_delay::Float64
255
    max_delay::Float64
256
    factor::Float64
257
    jitter::Float64
258

259
    function ExponentialBackOff(n, first_delay, max_delay, factor, jitter)
×
260
        all(x->x>=0, (n, first_delay, max_delay, factor, jitter)) || error("all inputs must be non-negative")
×
261
        new(n, first_delay, max_delay, factor, jitter)
×
262
    end
263
end
264

265
"""
266
    ExponentialBackOff(; n=1, first_delay=0.05, max_delay=10.0, factor=5.0, jitter=0.1)
267

268
A [`Float64`](@ref) iterator of length `n` whose elements exponentially increase at a
269
rate in the interval `factor` * (1 ± `jitter`).  The first element is
270
`first_delay` and all elements are clamped to `max_delay`.
271
"""
272
ExponentialBackOff(; n=1, first_delay=0.05, max_delay=10.0, factor=5.0, jitter=0.1) =
×
273
    ExponentialBackOff(n, first_delay, max_delay, factor, jitter)
274
function iterate(ebo::ExponentialBackOff, state= (ebo.n, min(ebo.first_delay, ebo.max_delay)))
×
275
    state[1] < 1 && return nothing
×
276
    next_n = state[1]-1
×
277
    curr_delay = state[2]
×
278
    next_delay = min(ebo.max_delay, state[2] * ebo.factor * (1.0 - ebo.jitter + (Libc.rand(Float64) * 2.0 * ebo.jitter)))
×
279
    (curr_delay, (next_n, next_delay))
×
280
end
281
length(ebo::ExponentialBackOff) = ebo.n
×
282
eltype(::Type{ExponentialBackOff}) = Float64
×
283

284
"""
285
    retry(f;  delays=ExponentialBackOff(), check=nothing) -> Function
286

287
Return an anonymous function that calls function `f`.  If an exception arises,
288
`f` is repeatedly called again, each time `check` returns `true`, after waiting the
289
number of seconds specified in `delays`.  `check` should input `delays`'s
290
current state and the `Exception`.
291

292
!!! compat "Julia 1.2"
293
    Before Julia 1.2 this signature was restricted to `f::Function`.
294

295
# Examples
296
```julia
297
retry(f, delays=fill(5.0, 3))
298
retry(f, delays=rand(5:10, 2))
299
retry(f, delays=Base.ExponentialBackOff(n=3, first_delay=5, max_delay=1000))
300
retry(http_get, check=(s,e)->e.status == "503")(url)
301
retry(read, check=(s,e)->isa(e, IOError))(io, 128; all=false)
302
```
303
"""
304
function retry(f;  delays=ExponentialBackOff(), check=nothing)
4✔
305
    (args...; kwargs...) -> begin
12✔
306
        y = iterate(delays)
4✔
307
        while y !== nothing
4✔
308
            (delay, state) = y
4✔
309
            try
4✔
310
                return f(args...; kwargs...)
4✔
311
            catch e
312
                if check !== nothing
×
313
                    result = check(state, e)
×
314
                    state, retry_or_not = length(result) == 2 ? result : (state, result)
×
315
                    retry_or_not || rethrow()
×
316
                end
317
            end
318
            sleep(delay)
×
319
            y = iterate(delays, state)
×
320
        end
×
321
        # When delays is out, just run the function without try/catch
322
        return f(args...; kwargs...)
×
323
    end
324
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