Inconsistent StackOverflowError with recursive parametric dynamic types

I think I stumbled upon a bug in the Julia, but I am not sure what kind of issue is this.

The code is the following:

using Graphs

abstract type MyAbstract end

struct Composite{T <: MyAbstract,R <: MyAbstract} <: MyAbstract
    a::T
    av::Vector{Union{T,R}}
end

struct Leaf <: MyAbstract
    b
end
Leaf() = Leaf(1)

Composite{T,R}() where {T<:MyAbstract,R<:MyAbstract} = Composite{T,R}(T(),Vector{Union{T,R}}([T()]))

randcomp1(firsttype::Type) = randn() > 0.5 ? Composite{firsttype, firsttype}() : Composite{firsttype, Composite}()

and then I get

julia> randcomp1(Leaf)
Internal error: encountered unexpected error in runtime:
StackOverflowError()
jl_has_free_typevars at /buildworker/worker/package_linux64/build/src/jltypes.c:114
intersect_union at /buildworker/worker/package_linux64/build/src/subtype.c:2141
intersect at /buildworker/worker/package_linux64/build/src/subtype.c:3073
intersect_all at /buildworker/worker/package_linux64/build/src/subtype.c:3175
intersect_aside at /buildworker/worker/package_linux64/build/src/subtype.c:2131
intersect_var at /buildworker/worker/package_linux64/build/src/subtype.c:2334
intersect at /buildworker/worker/package_linux64/build/src/subtype.c:3057
intersect_union at /buildworker/worker/package_linux64/build/src/subtype.c:2154
intersect at /buildworker/worker/package_linux64/build/src/subtype.c:3073
intersect_all at /buildworker/worker/package_linux64/build/src/subtype.c:3175
intersect_aside at /buildworker/worker/package_linux64/build/src/subtype.c:2131
intersect_var at /buildworker/worker/package_linux64/build/src/subtype.c:2334
intersect at /buildworker/worker/package_linux64/build/src/subtype.c:3057
intersect_union at /buildworker/worker/package_linux64/build/src/subtype.c:2154
intersect at /buildworker/worker/package_linux64/build/src/subtype.c:3073
intersect_all at /buildworker/worker/package_linux64/build/src/subtype.c:3175
^CWARNING: Force throwing a SIGINT
ERROR: ^CInterruptException:
Stacktrace:
 [1] top-level scope
   @ REPL[2]:1

caused by: StackOverflowError:
Stacktrace:
  [1] _methods_by_ftype
    @ ./reflection.jl:908 [inlined]
  [2] #findall#246
    @ ./compiler/methodtable.jl:68 [inlined]
  [3] (::Core.Compiler.var"#249#250"{Int64, Core.Compiler.CachedMethodTable{Core.Compiler.InternalMethodTable}, Core.Box})()
    @ Core.Compiler ./compiler/methodtable.jl:97
  [4] get!
    @ ./iddict.jl:178 [inlined]
  [5] #findall#248
    @ ./compiler/methodtable.jl:96 [inlined]
  [6] find_matching_methods(argtypes::Vector{Any}, atype::Any, method_table::Core.Compiler.CachedMethodTable{Core.Compiler.InternalMethodTable}, union_split::Int64, max_methods::Int64)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:308
  [7] abstract_call_gf_by_type(interp::Core.Compiler.NativeInterpreter, f::Any, fargs::Vector{Any}, argtypes::Vector{Any}, atype::Any, sv::Core.Compiler.InferenceState, max_methods::Int64)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:39
  [8] abstract_call_known(interp::Core.Compiler.NativeInterpreter, f::Any, fargs::Vector{Any}, argtypes::Vector{Any}, sv::Core.Compiler.InferenceState, max_methods::Int64)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1342
  [9] abstract_call(interp::Core.Compiler.NativeInterpreter, fargs::Vector{Any}, argtypes::Vector{Any}, sv::Core.Compiler.InferenceState, max_methods::Int64)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1397
 [10] abstract_call(interp::Core.Compiler.NativeInterpreter, fargs::Vector{Any}, argtypes::Vector{Any}, sv::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1382
 [11] abstract_eval_statement(interp::Core.Compiler.NativeInterpreter, e::Any, vtypes::Vector{Any}, sv::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1534
 [12] typeinf_local(interp::Core.Compiler.NativeInterpreter, frame::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1918
 [13] typeinf_nocycle(interp::Core.Compiler.NativeInterpreter, frame::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:2014
 [14] _typeinf(interp::Core.Compiler.NativeInterpreter, frame::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/typeinfer.jl:226
 [15] typeinf(interp::Core.Compiler.NativeInterpreter, frame::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/typeinfer.jl:209
 [16] typeinf_edge
    @ ./compiler/typeinfer.jl:823 [inlined]
 [17] abstract_call_method(interp::Core.Compiler.NativeInterpreter, method::Method, sig::Any, sparams::Core.SimpleVector, hardlimit::Bool, sv::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:504
 [18] abstract_call_gf_by_type(interp::Core.Compiler.NativeInterpreter, f::Any, fargs::Vector{Any}, argtypes::Vector{Any}, atype::Any, sv::Core.Compiler.InferenceState, max_methods::Int64)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:105
 [19] abstract_call(interp::Core.Compiler.NativeInterpreter, fargs::Vector{Any}, argtypes::Vector{Any}, sv::Core.Compiler.InferenceState, max_methods::Int64)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1395
 [20] abstract_call(interp::Core.Compiler.NativeInterpreter, fargs::Vector{Any}, argtypes::Vector{Any}, sv::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1382
 [21] abstract_eval_statement(interp::Core.Compiler.NativeInterpreter, e::Any, vtypes::Vector{Any}, sv::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1534
 [22] typeinf_local(interp::Core.Compiler.NativeInterpreter, frame::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1918
 [23] typeinf_nocycle(interp::Core.Compiler.NativeInterpreter, frame::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:2014
 [24] _typeinf(interp::Core.Compiler.NativeInterpreter, frame::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/typeinfer.jl:226
 [25] typeinf(interp::Core.Compiler.NativeInterpreter, frame::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/typeinfer.jl:209
 [26] typeinf_edge
    @ ./compiler/typeinfer.jl:823 [inlined]
 [27] abstract_call_method(interp::Core.Compiler.NativeInterpreter, method::Method, sig::Any, sparams::Core.SimpleVector, hardlimit::Bool, sv::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:504
 [28] abstract_call_gf_by_type(interp::Core.Compiler.NativeInterpreter, f::Any, fargs::Vector{Any}, argtypes::Vector{Any}, atype::Any, sv::Core.Compiler.InferenceState, max_methods::Int64)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:105
 [29] abstract_call(interp::Core.Compiler.NativeInterpreter, fargs::Vector{Any}, argtypes::Vector{Any}, sv::Core.Compiler.InferenceState, max_methods::Int64)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1395
 [30] abstract_call(interp::Core.Compiler.NativeInterpreter, fargs::Vector{Any}, argtypes::Vector{Any}, sv::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1382
 [31] abstract_eval_statement(interp::Core.Compiler.NativeInterpreter, e::Any, vtypes::Vector{Any}, sv::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1534
 [32] typeinf_local(interp::Core.Compiler.NativeInterpreter, frame::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1918
 [33] typeinf_nocycle(interp::Core.Compiler.NativeInterpreter, frame::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:2014
 [34] _typeinf(interp::Core.Compiler.NativeInterpreter, frame::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/typeinfer.jl:226
 [35] typeinf(interp::Core.Compiler.NativeInterpreter, frame::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/typeinfer.jl:209
 [36] typeinf_edge
    @ ./compiler/typeinfer.jl:823 [inlined]
 [37] abstract_call_method(interp::Core.Compiler.NativeInterpreter, method::Method, sig::Any, sparams::Core.SimpleVector, hardlimit::Bool, sv::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:504
 [38] abstract_call_gf_by_type(interp::Core.Compiler.NativeInterpreter, f::Any, fargs::Vector{Any}, argtypes::Vector{Any}, atype::Any, sv::Core.Compiler.InferenceState, max_methods::Int64)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:105
 [39] abstract_call(interp::Core.Compiler.NativeInterpreter, fargs::Vector{Any}, argtypes::Vector{Any}, sv::Core.Compiler.InferenceState, max_methods::Int64)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1395
 [40] abstract_call(interp::Core.Compiler.NativeInterpreter, fargs::Vector{Any}, argtypes::Vector{Any}, sv::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1382
 [41] abstract_eval_statement(interp::Core.Compiler.NativeInterpreter, e::Any, vtypes::Vector{Any}, sv::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1534
 [42] typeinf_local(interp::Core.Compiler.NativeInterpreter, frame::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:1918
 [43] typeinf_nocycle(interp::Core.Compiler.NativeInterpreter, frame::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/abstractinterpretation.jl:2014
 [44] _typeinf(interp::Core.Compiler.NativeInterpreter, frame::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/typeinfer.jl:226
 [45] typeinf(interp::Core.Compiler.NativeInterpreter, frame::Core.Compiler.InferenceState)
    @ Core.Compiler ./compiler/typeinfer.jl:209
 [46] typeinf_ext(interp::Core.Compiler.NativeInterpreter, mi::Core.MethodInstance)
    @ Core.Compiler ./compiler/typeinfer.jl:909
 [47] typeinf_ext_toplevel(interp::Core.Compiler.NativeInterpreter, linfo::Core.MethodInstance)
    @ Core.Compiler ./compiler/typeinfer.jl:942
 [48] typeinf_ext_toplevel(mi::Core.MethodInstance, world::UInt64)
    @ Core.Compiler ./compiler/typeinfer.jl:938
 [49] top-level scope
    @ REPL[2]:1

if I try again, the second time it will work normally:

julia> randcomp2(Leaf)
Composite{Leaf, Composite}(Leaf(1), Union{Leaf, Composite}[Leaf(1)])

If I remove using Graphs on the above code everything will run normally directly with the first try.
The problem is not particular to Graphs. I also tried other libraries like JuMP. It seems that just having an environment “loaded” with a package does some hurm.

Also if I define and call a function like the following with more concrete type allowance, it will work again without problems:

randcomp2(firsttype::Type{Leaf}) = randn() > 0.5 ? Composite{firsttype, firsttype}() : Composite{firsttype, Composite}()

Finally, if I substitute the general constructor with more concrete cases, it will again work without problems:

Composite{Leaf,Composite}() = Composite{Leaf,Composite}(Leaf(),Vector{Union{Leaf,Composite}}([Leaf()]))
Composite{Leaf,Leaf}() = Composite{Leaf,Leaf}(Leaf(),Vector{Union{Leaf,Leaf}}([Leaf()]))

tested on a julia version version 1.7.2.

could someone confirm this behavior ?

Not yet confirmed on versioninfo()

Julia Version 1.9.0-DEV.81
Commit 8ae9dd5c5b (2022-02-24 19:13 UTC)
Platform Info:
  OS: Windows (x86_64-w64-mingw32)
  CPU: 12 Ă— Intel(R) Core(TM) i7-10710U CPU @ 1.10GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-13.0.1 (ORCJIT, skylake)
  Threads: 5 on 12 virtual cores
Environment:
  JULIA_EDITOR = code
  JULIA_NUM_THREADS = 5

I am on a Debian 11

Julia Version 1.7.2
Commit bf53498635 (2022-02-06 15:21 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-12.0.1 (ORCJIT, skylake)

Confirmed on

Julia Version 1.7.2
Commit bf53498635 (2022-02-06 15:21 UTC)        
Platform Info:
  OS: Windows (x86_64-w64-mingw32)
  CPU: Intel(R) Core(TM) i7-10710U CPU @ 1.10GHz
  WORD_SIZE: 64    
  LIBM: libopenlibm
  LLVM: libLLVM-12.0.1 (ORCJIT, skylake)
Environment:
  JULIA_EDITOR = code
  JULIA_NUM_THREADS = 5

This is probably worth an issue.

Edit: on second thought, it could be a Graphs issue, but nevertheless the compiler accepts the code in newer versions…

It’s not a Graphs issue. Graphs is not used at all in this example. It is just loaded. I also tried the same with other packages like JuMP instead of Graphs and the behavior is the same.

Which versions do you mean?

1 Like

Development versions

Glad to also confirm that it works in the nightly build. So I assumed that whatever the error is, it is already fixed for the future julia versions.

Issue filed.

1 Like