Julia: Unreachable reached

Created on 22 Nov 2018  路  7Comments  路  Source: JuliaLang/julia

I hit "Unreachable reached" error with Julia 1.0.2 on Archlinux, then I tried backports-1.0.3 branch, assuming that #29986 will fix the problem, but it's still there.
Unfortunately, I don't have a small reproducible test case: it happens when I try to run the ]test BlackBoxOptim using pareto_rtree branch, which requires SpatialIndexing master (not yet in Julia registry).

Here's the log:

              _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.0.3-pre.29 (2018-11-19)
 _/ |\__'_|_|_|\__'_|  |  makepkg/33a5d6a2d9* (fork: 316 commits, 105 days)
|__/                   |

julia> versioninfo()
Julia Version 1.0.3-pre.29
Commit 33a5d6a2d9* (2018-11-19 15:19 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i7-7600U CPU @ 2.80GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.0 (ORCJIT, skylake)

(v1.0) pkg> test BlackBoxOptim
   Testing BlackBoxOptim
 Resolving package versions...
    Status `/tmp/tmpZWmfd8/Manifest.toml`
....

[ Info: subtract!(): region=BlackBoxOptim.DominanceCone{Int64,2,true}((22, 11))
[ Info: subtract!(): region=BlackBoxOptim.DominanceCone{Int64,2,true}((32, 11))
[ Info: _subtract!(): lev=0 i=0 len=1
[ Info: _subtract!(): delete subtree lev=0 i=0 len=1
[ Info: delete_subtree(): lev=0
[ Info: _delete_subtree(): empty children (1 objs) of node lev=0 parent=false
[ Info: _delete_subtree(): done node lev=0 parent=false
[ Info: _subtract!(): delete subtree done lev=0 i=0 len=0, returning 1
Unreachable reached at 0x7f8e4dabf335

signal (4): Illegal instruction
in expression starting at /home/ge68wan/.julia/dev/BlackBoxOptim/test/test_epsbox_archive.jl:1
subtract! at /home/ge68wan/.julia/dev/SpatialIndexing/src/rtree/delete.jl:65
add_candidate! at /home/ge68wan/.julia/dev/BlackBoxOptim/src/archives/epsbox_archive.jl:235
add_candidate! at /home/ge68wan/.julia/dev/BlackBoxOptim/src/archives/epsbox_archive.jl:262 [inlined]
add_candidate! at /home/ge68wan/.julia/dev/BlackBoxOptim/src/archives/epsbox_archive.jl:262
unknown function (ip: 0x7f8e4dad9d35)
jl_fptr_trampoline at /usr/bin/../lib/libjulia.so.1 (unknown line)
jl_apply_generic at /usr/bin/../lib/libjulia.so.1 (unknown line)
macro expansion at /home/ge68wan/.julia/dev/BlackBoxOptim/test/test_epsbox_archive.jl:50 [inlined]
macro expansion at /home/ge68wan/aur/julia-git/src/julia/usr/share/julia/stdlib/v1.0/Test/src/Test.jl:1083 [inlined]
macro expansion at /home/ge68wan/.julia/dev/BlackBoxOptim/test/test_epsbox_archive.jl:32 [inlined]
macro expansion at /home/ge68wan/aur/julia-git/src/julia/usr/share/julia/stdlib/v1.0/Test/src/Test.jl:1083 [inlined]
top-level scope at /home/ge68wan/.julia/dev/BlackBoxOptim/test/test_epsbox_archive.jl:4
jl_fptr_trampoline at /usr/bin/../lib/libjulia.so.1 (unknown line)
....

Here's the code in question:

function subtract!(tree::RTree{T,N}, reg::Region{T,N}) where {T,N}
    @info "subtract!(): region=$(reg)"
    isempty(tree) && return tree

    tmpdetached = Vector{nodetype(tree)}()
    status = _subtract!(tree.root, 0, reg, tree, tmpdetached)
    @info "_subtract!(root) done"
    @show status
    @show typeof(tmpdetached) length(tmpdetached)
    @info "subtract!(): status=$(status) tmpdetached=$(length(tmpdetached))"
    if status == 1 # tree changed, but not removed
        _condense!(tree.root, tree, tmpdetached) # try to condense the root
    elseif status == 2 # whole tree removed
        @assert isempty(tree)
        @assert isempty(tmpdetached)
    end
    _reinsert!(tree, tmpdetached)
    return tree
end

function _subtract!(node::Node, node_ix::Int, reg::Region, tree::RTree,
                    tmpdetached::AbstractVector{<:Node})
    nodembr = mbr(node)
    @info "_subtract!(): lev=$(level(node)) i=$node_ix len=$(length(node))"
    # FIXME juxtaposition() method that combines in() and intersects()?
    if in(nodembr, reg)
        @info "_subtract!(): delete subtree lev=$(level(node)) i=$node_ix len=$(length(node))"
        delete_subtree!(tree, node)
        @info "_subtract!(): delete subtree done lev=$(level(node)) i=$node_ix len=$(length(node)), returning 1"
        return 2 # node removed
    elseif intersects(nodembr, reg)
        mbr_dirty = false
        i = 1
        while i <= length(node)
            #@debug "1: lev=$(level(node)) i=$i len=$(length(node))"
            child = node[i]
            oldmbr = mbr(child)
            if node isa Branch
                child_res = _subtract!(child, i, reg, tree, tmpdetached)
            else
                @assert node isa Leaf
                if in(oldmbr, reg)
                    @info "_subtract!(): detach elem lev=$(level(node)) i=$i len=$(length(node))"
                    _detach!(node, i, tree, updatembr = false) # don't update MBR, we do it later
                    tree.nelems -= 1
                    tree.nelem_deletions += 1
                    child_res = 2
                    # FIXME intersect is not considered (would be important if elem mbr is not a point), should be handled by `filter`
                else
                    child_res = 0
                end
            end
            if !mbr_dirty && (child_res != 0) && tree.tight_mbrs
                mbr_dirty = touches(nodembr, oldmbr)
            end
            if child_res != 2 # don't increment if i-th node removed
                i += 1
            end
        end
        if hasparent(node) && length(node) < floor(Int, tree.reinsert_factor * capacity(node, tree))
            _detach!(parent(node), node_ix, tree)
            tree.nnodes_perlevel[level(node)+1] -= 1
            push!(tmpdetached, node)
            return 2 # node removed
        elseif mbr_dirty
            syncmbr!(node)
            return 1 # mbr changed
        else
            return 0 # no mbr change
        end
    else
        return 0 # no change
    end
end

It looks like the error happens immediately before/after _subtract!() returns to subtract!().

I think it's related to the concrete types of the RTree and Node, because this code runs fine in SpatialIndexing tests (nodetype(tree) = SpatialIndexing.Node{Float64,3,SpatialElem{Float64,3,Int64,String}}), but fails when (nodetype(tree) = SpatialIndexing.Node{Int64,2,BlackBoxOptim.FrontierIndividual{BlackBoxOptim.IndexedTupleFitness{2,Float64}}}).
Since it looks like it's type-related, I thought that it might be similar to #30067.

bug types and dispatch

Most helpful comment

I'm looking at it now. Ok to ping issues to gently remind us; thanks for your patience.

All 7 comments

Looks like the _subtract! was inferred as not returning, but then returned anyway:

(SpatialIndexing._subtract!)(%154, 0, %148, %147, %153)::Union{}

in particular because of this:

julia> code_typed(SpatialIndexing._subtract!, AT)[1][2]
Union{}

julia> code_typed(SpatialIndexing._subtract!, TT)[1][2]
Int64

julia> TT <: AT
true

julia> TT
Tuple{SpatialIndexing.Leaf{Int64,2,FrontierIndividual{IndexedTupleFitness{2,Float64}}},Int64,DominanceCone{Int64,2,true},RTree{Int64,2,FrontierIndividual{IndexedTupleFitness{2,Float64}}},Array{Node{Int64,2,FrontierIndividual{IndexedTupleFitness{2,Float64}}},1}}

julia> AT
Tuple{Node{Int64,2,V} where V,Int64,DominanceCone{Int64,2,true},RTree{Int64,2,FrontierIndividual{IndexedTupleFitness{2,Float64}}},Array{Node{Int64,2,FrontierIndividual{IndexedTupleFitness{2,Float64}}},1}}

Reduced:

struct Foo{F,N}; end
f(a::Foo{F,N}, b::Tuple{Vararg{F,N}}) where {F,N} = nothing
Base._methods_by_ftype(Tuple{typeof(f), Foo{Int64,2}, NTuple}, 1, typemax(UInt))

gives zero matches, despite:

S = Tuple{typeof(f), Foo{Int64,2}, NTuple{2, Int64}}
@assert S <: Tuple{typeof(f), Foo{Int64,2}, NTuple}
length(Base._methods_by_ftype(S, 1, typemax(UInt))) # 1

Or even simpler:

struct Foo{F,N}; end
typeintersect(Tuple{Foo{Int64,2}, NTuple}, Tuple{Foo{F,N},Tuple{Vararg{F,N}}} where N where F)

is giving the wrong answer.

Thanks for the amazing work of tracking it down! I wonder whether it's possible to detect issues like that earlier (during type inference or compilation).

Not really, what you're seeing is already the result of extra code inserted to make these sorts of issues obvious. The compiler thought one thing was gonna happen, but another thing did.

I'm very sorry for pinging this issue: I know/see you are very busy improving various aspects of Julia.
It's just that I hoped the code in question would be working, and I can use it for my projects.
Is the issue something that could be easily fixed in the near future or I'd better devise a workaround?
I'm only asking because it was stated that compiler correctness has top priority.

I'm looking at it now. Ok to ping issues to gently remind us; thanks for your patience.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

TotalVerb picture TotalVerb  路  3Comments

wilburtownsend picture wilburtownsend  路  3Comments

ararslan picture ararslan  路  3Comments

omus picture omus  路  3Comments

helgee picture helgee  路  3Comments