Julia: Segfault when defining show for types

Created on 14 Jun 2017  路  10Comments  路  Source: JuliaLang/julia

Here's the minimum repro

   _       _ _(_)_     |  A fresh approach to technical computing
  (_)     | (_) (_)    |  Documentation: https://docs.julialang.org
   _ _   _| |_  __ _   |  Type "?help" for help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 0.7.0-DEV.511 (2017-06-08 21:22 UTC)
 _/ |\__'_|_|_|\__'_|  |  (HEAD detached at origin/jb/namedtuples)/e2c2724982* (fork: 6 commits, 5 days)
|__/                   |  x86_64-apple-darwin16.6.0

julia> struct Null end

julia> Base.show(io::IO, ::Type{Union{T, Null}}) where {T} = print(io, "?$T")

julia> struct WeakRefString{T} <: AbstractString
           ptr::Ptr{T}
           len::Int # of code units
           ind::Int # used to keep track of a string data index
       end

julia> Base.show(io::IO, ::Type{WeakRefString{T}}) where {T} = print(io, "WeakRefString{$T}")

julia> temp = []
Segmentation fault: 11
jq-mbp:julia jacobquinn$

Note the last thing that happens is it's trying to display 0-element Array{Any,1}.

Most helpful comment

I think the best way to make this work --- even though I never condone defining show for particular types, which goes beyond "type piracy" and into "type treason" --- is to actually overwrite the method show(io::IO, x::Union) and look for Null inside the Union.

All 10 comments

I see a StackOverflowError for the slightly simpler

julia> struct Null end

julia> Base.show(io::IO, ::Type{Union{T, Null}}) where {T} = print(io, "?$T")

julia> struct WeakRefString{T} end

julia> Base.show(io::IO, ::Type{WeakRefString{T}}) where {T} = print(io, "WeakRefString{$T}")

julia> temp = []

The backtrace repeats this snippet:

unknown function (ip: 0x7fe856fd2fd6)
jl_call_fptr_internal at src/julia_internal.h:350 [inlined]
jl_call_method_internal at src/julia_internal.h:369 [inlined]
jl_apply_generic at src/gf.c:1925
print at ./strings/io.jl:29
unknown function (ip: 0x7fe875794eff)
jl_call_fptr_internal at src/julia_internal.h:350 [inlined]
jl_call_method_internal at src/julia_internal.h:369 [inlined]
jl_apply_generic at src/gf.c:1925
#print_to_string#232 at ./strings/io.jl:102
show at ./REPL[2]:1

Note Any == Union{Any,Void}.

I think the best way to make this work --- even though I never condone defining show for particular types, which goes beyond "type piracy" and into "type treason" --- is to actually overwrite the method show(io::IO, x::Union) and look for Null inside the Union.

The other alternative that seems to work is by also defining Base.show(io::IO, ::Type{Any}) = print(io, "Any"), which I guess goes from "type treason" to "type high treason", right? 馃槃

It also seems like this would be a great use-case for being able to specify "only-the-strict-subtypes-of-T" as a subtype relation; not sure if that's ever been brought up before or is possible at all, but seems like that would be a workable solution here.

But why does it need the second show method to crash?

Very valid question @martinholters !

Just found this, which might be related: https://github.com/JuliaLang/julia/issues/13306

Something isn't invalidated correctly:

julia> struct Null end

julia> struct WeakRefString{T} end

julia> methods(show, Tuple{IO,Type{Any}})
# 1 method for generic function "show":
[1] show(io::IO, x::DataType) in Base at show.jl:211

julia> foo() = show(Any)
foo (generic function with 1 method)

julia> foo()
Any
julia> Base.show(io::IO, ::Type{Union{T, Null}}) where {T} = print(io, "?$T")

julia> methods(show, Tuple{IO,Type{Any}})
# 1 method for generic function "show":
[1] show(io::IO, ::Type{Union{Null, T}}) where T in Main at REPL[6]:1

julia> foo()
Any
julia> show(Any)
?Any

So foo() hasn't been recompiled. And neither have the innards of the "?$T". Defining the second show method at least triggers recompilation of the latter:

julia> Base.show(io::IO, ::Type{WeakRefString{T}}) where {T} = print(io, "WeakRefString{$T}")

julia> foo()
Any
julia> show(Any)
ERROR: StackOverflowError:
[...]

Further reduced a bit:

julia> foo(::DataType) = 1
foo (generic function with 1 method)

julia> bar() = foo(Any)
bar (generic function with 1 method)

julia> bar()
1

julia> foo(::Type{Union{T,Void}}) where {T} = 2
foo (generic function with 2 methods)

julia> foo(Any)
2

julia> bar()
1

bump

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Keno picture Keno  路  3Comments

i-apellaniz picture i-apellaniz  路  3Comments

sbromberger picture sbromberger  路  3Comments

manor picture manor  路  3Comments

ararslan picture ararslan  路  3Comments