swh:1:snp:a72e953ecd624a7df6e6196bbdd05851996c5e40
Tip revision: 3da6f54faba300922fbd77136caed70f7d860d9e authored by Gabriel Baraldi on 17 July 2023, 17:40:00 UTC
Revert "relax assertion involving pg->nold to reflect that it may be a bit inaccurate with parallel marking (#50466)"
Revert "relax assertion involving pg->nold to reflect that it may be a bit inaccurate with parallel marking (#50466)"
Tip revision: 3da6f54
exceptions.jl
# This file is a part of Julia. License is MIT: https://julialang.org/license
using Test
@testset "Basic exception stack handling" begin
# Exiting the catch block normally pops the exception
try
error("A")
catch
@test length(current_exceptions()) == 1
end
@test length(current_exceptions()) == 0
# Exiting via a finally block does not pop the exception
try
try
error("A")
finally
@test length(current_exceptions()) == 1
end
catch
@test length(current_exceptions()) == 1
end
# The combined try-catch-finally form obeys the same rules as above
try
error("A")
catch
@test length(current_exceptions()) == 1
finally
@test length(current_exceptions()) == 0
end
@test length(current_exceptions()) == 0
# Errors are pushed onto the stack according to catch block nesting
try
error("RootCause")
catch
@test length(current_exceptions()) == 1
try
error("B")
catch
stack = current_exceptions()
@test length(stack) == 2
@test stack[1].exception.msg == "RootCause"
@test stack[2].exception.msg == "B"
end
# Stack pops correctly
stack = current_exceptions()
@test length(stack) == 1
@test stack[1].exception.msg == "RootCause"
end
end
@testset "Exception stack lowering special cases" begin
# try block in value position
val = try
error("A")
catch
@test length(current_exceptions()) == 1
1
end
@test val == 1
function test_exc_stack_tailpos()
# try block in tail position
try
error("A")
catch
length(current_exceptions())
end
end
@test test_exc_stack_tailpos() == 1
@test length(current_exceptions()) == 0
end
@testset "Exception stacks - early exit from try or catch" begin
# Exiting a catch block early with normal control flow — break, continue,
# return, goto — will result in popping of the exception stack.
function test_exc_stack_catch_return()
try
error("A")
catch
@test length(current_exceptions()) == 1
return
end
end
test_exc_stack_catch_return()
for i=1:1
try
error("A")
catch
@test length(current_exceptions()) == 1
break
end
end
# Also test try-break-finally forms here. See #31766
for i=1:1
try
error("A")
catch
@test length(current_exceptions()) == 1
break
finally
@test length(current_exceptions()) == 0
end
end
@test length(current_exceptions()) == 0
for i=1:1
try
error("A")
catch
@test length(current_exceptions()) == 1
continue
end
end
for i=1:1
try
error("A")
catch
@test length(current_exceptions()) == 1
continue
finally
@test length(current_exceptions()) == 0
end
end
@test length(current_exceptions()) == 0
try
error("A")
catch
@test length(current_exceptions()) == 1
@goto outofcatch
end
@label outofcatch
try
error("A")
catch
@test length(current_exceptions()) == 1
@goto outofcatch2
finally
@test length(current_exceptions()) == 0
end
@label outofcatch2
@test length(current_exceptions()) == 0
# Exiting from a try block in various ways should not affect the exception
# stack state.
try
error("ExceptionInOuterTry")
catch
@test length(current_exceptions()) == 1
function test_exc_stack_try_return()
try
return
catch
end
end
test_exc_stack_try_return()
for i=1:1
try
break
catch
end
end
for i=1:1
try
continue
catch
end
end
try
@goto outoftry
catch
end
@label outoftry
@test length(current_exceptions()) == 1
@test current_exceptions()[1].exception == ErrorException("ExceptionInOuterTry")
end
end
@testset "Finally handling with exception stacks" begin
# The lowering of finally is quite subtle when combined with break or
# return because each finally block may be entered via multiple code paths
# (eg, different occurrences of return), and these code paths must diverge
# again once the finally block has completed. To complicate matters
# further, the return code path must thread through every nested finally
# block before actually returning, all the while preserving the information
# about which variable to return.
# Issue #34579
(()-> begin
try
throw("err")
catch
# Explicit return => exception should be popped before finally block
return
finally
@test length(Base.current_exceptions()) == 0
end
end)()
@test length(Base.current_exceptions()) == 0
while true
try
error("err1")
catch
try
# Break target is outside catch block, but finally is inside =>
# exception should not be popped inside finally block
break
finally
@test length(Base.current_exceptions()) == 1
end
end
end
@test length(Base.current_exceptions()) == 0
# Nested finally handling with `return`: each finally block should observe
# only the active exceptions as according to its nesting depth.
(() -> begin
try
try
error("err1")
catch
try
try
error("err2")
catch
# This return needs to thread control flow through
# multiple finally blocks in the linearized IR.
return
end
finally
# At this point err2 is dealt with
@test length(Base.current_exceptions()) == 1
@test Base.current_exceptions()[1].exception == ErrorException("err1")
end
end
finally
# At this point err1 is dealt with
@test length(Base.current_exceptions()) == 0
end
end)()
@test length(Base.current_exceptions()) == 0
end
@testset "Deep exception stacks" begin
# Generate deep exception stack with recursive handlers.
#
# (Note that if you let this overflow the program stack (not the exception
# stack) julia will crash. See #28577.)
function test_exc_stack_deep(n)
n != 1 || error("RootCause")
try
test_exc_stack_deep(n-1)
catch
error("n==$n")
end
end
@test try
test_exc_stack_deep(100)
catch
@test current_exceptions()[1].exception == ErrorException("RootCause")
length(current_exceptions())
end == 100
@test length(current_exceptions()) == 0
end
@testset "Exception stacks and Tasks" begin
# Task switching should not affect exception state. See #12485.
try
error("A")
catch
t = @task try
error("B")
catch exc
exc
end
yield(t)
@test t.state === :done
@test t.result == ErrorException("B")
# Task exception state is preserved around task switches
@test length(current_exceptions()) == 1
@test current_exceptions()[1].exception == ErrorException("A")
end
@test length(current_exceptions()) == 0
# test rethrow() rethrows correct state
bt = []
try
try
error("A")
catch
bt = catch_backtrace()
t = @task try
error("B")
catch exc
exc
end
yield(t)
@test t.state === :done
@test t.result == ErrorException("B")
@test bt == catch_backtrace()
rethrow()
end
catch exc
@test exc == ErrorException("A")
@test bt == catch_backtrace()
end
@test length(current_exceptions()) == 0
# test rethrow with argument
bt = []
try
try
error("A")
catch
t = @task try
error("B")
catch exc
exc
end
yield(t)
@test t.state === :done
@test t.result == ErrorException("B")
bt = catch_backtrace()
rethrow(ErrorException("C"))
end
catch exc
@test exc == ErrorException("C")
@test bt == catch_backtrace()
end
@test length(current_exceptions()) == 0
# Exception stacks on other tasks
t = @task try
error("A")
catch
error("B")
end
yield(t)
@test t.state === :failed
@test t.result == ErrorException("B")
@test current_exceptions(t, backtrace=false) == [
(exception=ErrorException("A"),backtrace=nothing),
(exception=ErrorException("B"),backtrace=nothing)
]
# Exception stacks for tasks which never get the chance to start
t = @task nothing
@test (try
@async Base.throwto(t, ErrorException("expected"))
wait(t)
catch e
e
end).task.exception == ErrorException("expected")
@test length(current_exceptions(t)) == 1
@test length(current_exceptions(t)[1].backtrace) > 0 # backtrace is nonempty
# Exception stacks should not be accessed on concurrently running tasks
t = @task ()->nothing
@test_throws ErrorException("Inspecting the exception stack of a task which might "*
"be running concurrently isn't allowed.") current_exceptions(t)
end
@testset "rethrow" begin
@test try
rethrow()
catch exc
exc
end == ErrorException("rethrow() not allowed outside a catch block")
@test try
rethrow(ErrorException("A"))
catch exc
exc
end == ErrorException("rethrow(exc) not allowed outside a catch block")
end
# issue #36527
function f36527()
caught = false
🏡 = Core.eval(Main, :(module asdf36527 end))
try
Core.eval(🏡, :(include_string($🏡, "@assert z36527 == 10")))
catch ex
GC.gc()
catch_backtrace()
caught = true
end
return caught
end
@test f36527()
# accessing an undefined var in tail position in a catch block
function undef_var_in_catch()
try
error("first error")
catch
__probably_n0t_defined__
end
end
@test length(try
undef_var_in_catch()
[]
catch
current_exceptions()
end) == 2