Skip to content

Commit

Permalink
Merge pull request #758 from JuliaLang/sv-fix-curr-exc
Browse files Browse the repository at this point in the history
Fix current_exceptions when called with backtrace=false (#757).
  • Loading branch information
Sacha0 authored Sep 24, 2021
2 parents 79af07b + ad62e78 commit 15191cb
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 17 deletions.
30 changes: 25 additions & 5 deletions src/Compat.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1101,17 +1101,37 @@ if VERSION < v"1.7.0-DEV.1106"

if VERSION >= v"1.1"
function current_exceptions(task=current_task(); backtrace=true)
stack = Base.catch_stack(task, include_bt=backtrace)
ExceptionStack(Any[(exception=x[1],backtrace=x[2]) for x in stack])
old_stack = Base.catch_stack(task, include_bt=backtrace)
# If include_bt=true, Base.catch_stack yields a Vector of two-tuples,
# where the first element of each tuple is an exception and the second
# element is the corresponding backtrace. If instead include_bt=false,
# Base.catch_stack yields a Vector of exceptions.
#
# Independent of its backtrace keyword argument, Base.current_exceptions
# yields an ExceptionStack that wraps a Vector of two-element
# NamedTuples, where the first element of each named tuple is an exception
# and the second element is either a correpsonding backtrace or `nothing`.
#
# The following constructs the ExceptionStack-wrapped Vector appropriately.
new_stack = backtrace ?
Any[(exception=exc_and_bt[1], backtrace=exc_and_bt[2]) for exc_and_bt in old_stack] :
Any[(exception=exc_only, backtrace=nothing) for exc_only in old_stack]
return ExceptionStack(new_stack)
end
else
# There's no exception stack in 1.0, but we can fall back to returning
# the (single) current exception and backtrace instead.
@eval function current_exceptions(task=current_task(); backtrace=true)
bt = catch_backtrace()
# `exc = Expr(:the_exception)` is the lowering for `catch exc`
exc = isempty(bt) ? nothing : $(Expr(:the_exception))
ExceptionStack(isempty(bt) ? Any[] : Any[(exception=exc, backtrace=bt)])
stack = if isempty(bt)
Any[]
else
# Note that `exc = Expr(:the_exception)` is the lowering for `catch exc`,
# and please see the comment in the implementation for >v1.1 regarding
# the `backtrace ? bt : nothing`.
Any[(exception=$(Expr(:the_exception)), backtrace = backtrace ? bt : nothing)]
end
return ExceptionStack(stack)
end
@eval function the_stack()
$(Expr(:the_exception)), catch_backtrace()
Expand Down
65 changes: 53 additions & 12 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1112,36 +1112,77 @@ end

# https://github.com/JuliaLang/julia/pull/29901
@testset "current_exceptions" begin
# Display of errors which cause more than one entry on the exception stack
excs = try
try
__not_a_binding__
# Helper method to retrieve an ExceptionStack that should contain two exceptions,
# each of which accompanied by a backtrace or `nothing` according to `with_backtraces`.
function _retrieve_exception_stack(;with_backtraces::Bool)
exception_stack = try
try
# Generate the first exception:
__not_a_binding__
catch
# Catch the first exception, and generate a second exception
# during what would be handling of the first exception:
1 ÷ 0
end
catch
1 ÷ 0 # Generate error while handling error
# Retrieve an ExceptionStack with both exceptions,
# and bind `exception_stack` (at the top of this block) thereto:
current_exceptions(;backtrace=with_backtraces)
end
catch
current_exceptions()
return exception_stack
end

excs_with_bts = _retrieve_exception_stack(with_backtraces = true)
excs_sans_bts = _retrieve_exception_stack(with_backtraces = false)

# Check that the ExceptionStack with backtraces contains backtraces:
BACKTRACE_TYPE = Vector{Union{Ptr{Nothing}, Base.InterpreterIP}}
@test all(exc_with_bt[2] isa BACKTRACE_TYPE for exc_with_bt in excs_with_bts)

# Check that the ExceptionStack without backtraces contains `nothing`s:
@test all(exc_sans_bt[2] isa Nothing for exc_sans_bt in excs_sans_bts)

if VERSION >= v"1.1"
@test typeof.(first.(excs)) == [UndefVarError, DivideError]
# Check that the ExceptionStacks contain the expected exception types:
@test typeof.(first.(excs_with_bts)) == [UndefVarError, DivideError]
@test typeof.(first.(excs_sans_bts)) == [UndefVarError, DivideError]

# Check that the ExceptionStack with backtraces `show`s correctly:
@test occursin(r"""
2-element ExceptionStack:
DivideError: integer division error
Stacktrace:.*
caused by: UndefVarError: __not_a_binding__ not defined
Stacktrace:.*
"""s, sprint(show, excs))
"""s, sprint(show, excs_with_bts))

# Check that the ExceptionStack without backtraces `show`s correctly:
@test occursin(r"""
2-element ExceptionStack:
DivideError: integer division error
caused by: UndefVarError: __not_a_binding__ not defined"""s,
sprint(show, excs_sans_bts))
else
# Due to runtime limitations, julia-1.0 only retains the last exception
@test typeof.(first.(excs)) == [DivideError]
# Due to runtime limitations, julia-1.0 only retains the last exception.

# Check that the ExceptionStacks contain the expected last exception type:
@test typeof.(first.(excs_with_bts)) == [DivideError]
@test typeof.(first.(excs_sans_bts)) == [DivideError]

# Check that the ExceptionStack with backtraces `show`s correctly:
@test occursin(r"""
1-element ExceptionStack:
DivideError: integer division error
Stacktrace:.*
""", sprint(show, excs))
""", sprint(show, excs_with_bts))

# Check that the ExceptionStack without backtraces `show`s correctly:
@test occursin(r"""
1-element ExceptionStack:
DivideError: integer division error""",
sprint(show, excs_sans_bts))
end
end

Expand Down

0 comments on commit 15191cb

Please sign in to comment.