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}.
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
Most helpful comment
I think the best way to make this work --- even though I never condone defining
showfor particular types, which goes beyond "type piracy" and into "type treason" --- is to actually overwrite the methodshow(io::IO, x::Union)and look forNullinside the Union.