Unreachable reached, v1.11.5: file github issue, or discuss here first?

Hi all,

Despite (or, in this case, almost certainly because of) being fairly new to using Julia I seemed to have stumbled upon an “unreachable reached” crash. On the one hand, I understand this is cause for celebration, etc, and that I would typically file an issue. On the other hand, the way I triggered it was a bit unusual, and I wasn’t sure if this caveat applies.

The basic ingredients involve using Revise.jl to track a file where I define a struct, extend some of Base’s arithmetic operators for that struct, and then later remove one of those method definitions. Given that this involves an interaction with another package and isn’t a typical workflow, I wanted to ask here first if this is a known issue or if I should file a bug report on GitHub.


It took me some time to figure out exactly what steps got me to the crash, but after some tinkering I have the following pretty minimal working example. Four basic steps:

  1. I create and save the following file in my text editor:
#test.jl --- I'm sure we can all guess what this originally looked like
import Base: *, ^ 

struct D<: Number #<: Number needed to trigger an ambiguity later 
    val::Float64
    deriv::Float64
end

*(x::D,y::D) = D(x.val*y.val,
                          x.val*y.deriv+x.deriv*y.val)

^(x::D,y::D) = D(x.val^y.val,
                        x.val^(y.val-1)*
                        (y.val*x.deriv + x.val*y.deriv*log(x.val))
                        )
^(x::D,n::Real) = D(x.val^n,n*x.val^(n-1)*x.deriv) # Problem line

function test()
    D(1.0,1.0)^2
end
  1. I start a new Julia session, use Revise to includet this file, and run the test function:
    $ julia --startup-file=no
    julia> using Revise
    julia> includet("test.jl")
    julia> test()

This, of course, leads to a method error due to ambiguity.

REPL output of method error... we subtyped D, so ^(D, Real) and ^(Number,Integer) both fit

ERROR: MethodError: ^(::D, ::Int64) is ambiguous.

Candidates:
^(x::D, n::Real)
@ Main ~/test/bug/test.jl:15
^(x::Number, p::Integer)
@ Base intfuncs.jl:349

Possible fix, define
^(::D, ::Integer)

Stacktrace:
[1] literal_pow
@ ./intfuncs.jl:389 [inlined]
[2] test()
@ Main ~/test/bug/test.jl:18
[3] top-level scope
@ REPL[7]:1

  1. I switch back to the test.jl file and comment out the second of the two exponentiation methods (the ^(x::D,n::Real) = D(x.val^n,n*x.val^(n-1)*x.deriv) one, that is), and save the file.

  2. I go back to the running REPL. julia> test() reaches the unreachable:

Unreachable reached at 0x79faeaa82458

[1683] signal 4 (2): Illegal instruction
in expression starting at REPL[2]:1
literal_pow at ./intfuncs.jl:389 [inlined]
test at /home/daniel/test/bug/test.jl:18
unknown function (ip: 0x79faeaa8247f)
jl_apply at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/julia.h:2157 [inlined]
do_call at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/interpreter.c:126
eval_value at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/interpreter.c:223
eval_stmt_value at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/interpreter.c:174 [inlined]
eval_body at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/interpreter.c:666
jl_interpret_toplevel_thunk at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/interpreter.c:824
jl_toplevel_eval_flex at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/toplevel.c:943
jl_toplevel_eval_flex at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/toplevel.c:886
jl_toplevel_eval_flex at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/toplevel.c:886
jl_toplevel_eval_flex at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/toplevel.c:886
ijl_toplevel_eval_in at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/toplevel.c:994
eval at ./boot.jl:430 [inlined]
eval_user_input at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:261
repl_backend_loop at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:368
#start_repl_backend#59 at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:343
start_repl_backend at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:340
#run_repl#76 at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:500
run_repl at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:486
jfptr_run_repl_10123.1 at /home/daniel/.julia/juliaup/julia-1.11.5+0.x64.linux.gnu/share/julia/compiled/v1.11/REPL/u0gqU_4x0TT.so (unknown line)
#1150 at ./client.jl:446
jfptr_YY.1150_14797.1 at /home/daniel/.julia/juliaup/julia-1.11.5+0.x64.linux.gnu/share/julia/compiled/v1.11/REPL/u0gqU_4x0TT.so (unknown line)
jl_apply at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/julia.h:2157 [inlined]
jl_f__call_latest at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/builtins.c:875
#invokelatest#2 at ./essentials.jl:1055 [inlined]
invokelatest at ./essentials.jl:1052 [inlined]
run_main_repl at ./client.jl:430
repl_main at ./client.jl:567 [inlined]
_start at ./client.jl:541
jfptr__start_73430.1 at /home/daniel/.julia/juliaup/julia-1.11.5+0.x64.linux.gnu/lib/julia/sys.so (unknown line)
jl_apply at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/julia.h:2157 [inlined]
true_main at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/jlapi.c:900
jl_repl_entrypoint at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/src/jlapi.c:1059
main at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-11/cli/loader_exe.c:58
unknown function (ip: 0x79faf722a1c9)
__libc_start_main at /lib/x86_64-linux-gnu/libc.so.6 (unknown line)
unknown function (ip: 0x4010b8)
Allocations: 1309353 (Pool: 1309266; Big: 87); GC: 2
Illegal instruction (core dumped)

I can, of course, give extra context from versioninfo, etc, if that’s relevant – for reference I was able to reproduce this from the above steps on both windows 11 and also WSL2 running Ubuntu 24.01. This is obviously not a bug that affects my normal workflow or what I actually want to do with the language, I just stumbled into it while fumbling around. Thanks for your time!

3 Likes

Looks like a bug to me; commenting out a method and having Revise reload the functions should not cause an unreachable.

3 Likes

I also hit an unreachable by pasting the step 1 code into the REPL, not using Revise at all, and manually deleting that method, though the printout is slightly different (v1.11.6, v1.10.10, v1.9.4, Windows 11). This issue thus belongs with base Julia, not Revise. Same result if I omitted --startup-file=no of the julia command.

julia> methods(^)[40] # omitted prior, long methods(^) printout where I read this, important to verify
^(x::D, n::Real)
     @ Main REPL[5]:1

julia> Base.delete_method(methods(^)[40]) # internal, usually use Revise

julia> test()
Unreachable reached at 0000029d5f495c1c

Please submit a bug report with steps to reproduce this fault, and any error messages that follow (in their entirety). Thanks.
Exception: EXCEPTION_ILLEGAL_INSTRUCTION at 0x29d5f495c1c -- literal_pow at .\intfuncs.jl:389 [inlined]
test at .\REPL[6]:2
...

Didn’t try this as thoroughly, but this variant of test doesn’t go through literal_pow, and I still ran into an unreachable:

function test(x=2)
    D(1.0,1.0)^x
end

and it complains about test instead of an inlined literal_pow:

...
Exception: EXCEPTION_ILLEGAL_INSTRUCTION at 0x255251768ee -- test at .\REPL[9]:2
in expression starting at REPL[13]:1
test at .\REPL[9]:2
...
5 Likes

Thanks for confirming! I hadn’t even thought to try skipping the Revise part and manually deleting the method.

Anyway, I guess that makes this pretty definitively part of Base, so I’ll post an issue.

1 Like

Just for fun, this even happens with totally new annotated types and no number-crunching. The Revise.includet equivalent as stated in the post has the same outcome.

julia> begin
       abstract type AbstractBlah end
       struct Blah<:AbstractBlah end
       foo(::Blah, ::AbstractBlah) = 1
       foo(::AbstractBlah, ::Blah) = -1
       test() = foo(Blah(), Blah())
       end
test (generic function with 1 method)

julia> foo(Blah(), Blah())
ERROR: MethodError: foo(::Blah, ::Blah) is ambiguous.

Candidates:
  foo(::AbstractBlah, ::Blah)
    @ Main REPL[1]:5
  foo(::Blah, ::AbstractBlah)
    @ Main REPL[1]:4

Possible fix, define
  foo(::Blah, ::Blah)

Stacktrace:
 [1] top-level scope
   @ REPL[2]:1

julia> test() # same outcome as expected from forwarding to same call
ERROR: MethodError: foo(::Blah, ::Blah) is ambiguous.

Candidates:
  foo(::AbstractBlah, ::Blah)
    @ Main REPL[1]:5
  foo(::Blah, ::AbstractBlah)
    @ Main REPL[1]:4

Possible fix, define
  foo(::Blah, ::Blah)

Stacktrace:
 [1] test()
   @ Main .\REPL[1]:6
 [2] top-level scope
   @ REPL[3]:1

julia> Base.delete_method(methods(foo)[2]) # no Base methods to avoid deleting

julia> foo(Blah(), Blah()) # properly invalidated previous MethodError call
-1

julia> test()

Unreachable reached at 00000204af55136f

Please submit a bug report with steps to reproduce this fault, and any error messages that follow (in their entirety). Thanks.
Exception: EXCEPTION_ILLEGAL_INSTRUCTION at 0x204af55136f -- test at .\REPL[1]:6
in expression starting at REPL[6]:1
test at .\REPL[1]:6
unknown function (ip: 00000204af5513a4)
jl_apply at C:/workdir/src\julia.h:2157 [inlined]
...
4 Likes

Ha — gets right to the root! Very slick

Linking the open issue back here for reader convenience, has both Revise.includet and Base.delete_method MWEs from this thread:
Unreachable reached by deleting an extended method (v1.11.5) · Issue #58986 · JuliaLang/julia
Looking forward to even more Revise improvements

3 Likes