Broken Base.show method

I’m working on function that either shows full types or not based on some variable.
I encountered weird bug I can’t figure out because the function is not behaving the same when running it and when debugging it.
The function is here: https://github.com/racinmat/Mill.jl/blob/master/src/Mill.jl#L74

When I run it, it crashes with

ERROR: type DataType has no field var
Stacktrace:
 [1] getproperty at .\Base.jl:28 [inlined]
 [2] show(::IOContext{Base.GenericIOBuffer{Array{UInt8,1}}}, ::Type{ERROR: type DataType has no field var

but when I use Debugger and debug it line per line, Julia executes the code succesfully, it does not crash, it returns correctly.
During debugging, x isa DataType is true, but when running it outside of debug, it’s false.
I restarted Julia multiple times to make sure it’s not some old version of code in the REPL.

The function itself is here:

function Base.show(io::IO, x::Type{T}) where {T<:Union{AbstractNode,AbstractMillModel}}
    if _terseprint[]
        if hasproperty(x, :body) && !hasproperty(x.body, :name) && hasproperty(x.body, :body)
            print(io, "$(x.body.body.name){…}")
            return
        elseif hasproperty(x, :body) && !hasproperty(x, :name)
            print(io, "$(x.body.name){…}")
            return
        else
            print(io, "$(x.name){…}")
            return
        end
    else
        # specifically function show(io::IO, @nospecialize(x::Type))
        if x isa DataType
            Base.show_datatype(io, x)
            return
        elseif x isa Union
            if x.a isa DataType && Core.Compiler.typename(x.a) === Core.Compiler.typename(DenseArray)
                T2, N = x.a.parameters
                if x == StridedArray{T2,N}
                    print(io, "StridedArray")
                    Base.show_delim_array(io, (T2,N), '{', ',', '}', false)
                    return
                elseif x == StridedVecOrMat{T2}
                    print(io, "StridedVecOrMat")
                    Base.show_delim_array(io, (T2,), '{', ',', '}', false)
                    return
                elseif StridedArray{T2,N} <: x
                    print(io, "Union")
                    Base.show_delim_array(io, vcat(StridedArray{T2,N}, Base.uniontypes(Core.Compiler.typesubtract(x, StridedArray{T2,N}))), '{', ',', '}', false)
                    return
                end
            end
            print(io, "Union")
            Base.show_delim_array(io, Base.uniontypes(x), '{', ',', '}', false)
            return
        end

        if Base.print_without_params(x)
            return show(io, Base.unwrap_unionall(x).name)
        end

        if x.var.name === :_ || Base.io_has_tvar_name(io, x.var.name, x)
            counter = 1
            while true
                newname = Symbol(x.var.name, counter)
                if !Base.io_has_tvar_name(io, newname, x)
                    newtv = TypeVar(newname, x.var.lb, x.var.ub)
                    x = UnionAll(newtv, x{newtv})
                    break
                end
                counter += 1
            end
        end

        show(IOContext(io, :unionall_env => x.var), x.body)
        print(io, " where ")
        show(io, x.var)
    end
end

I’m desperate, I can’t figure out how to make it work and make it consistent.

I know one should generally not overload Base.show, but we have good usecase for this.

The error can be seen here: https://travis-ci.com/github/racinmat/Mill.jl/jobs/400338894

Often when it works in the debugger and not the compiler, it’s a bug in the compiler. The best thing to do is probably to try reduce the example as much as possible and open an issue.

2 Likes

See also https://github.com/JuliaLang/julia/pull/37773#issuecomment-701976134
and the discussion in These stack traces are out of control
This sounds related.

1 Like

Oh, thanks. I’ll try to make an MWE and open issue if I’ll be successful.
Is there some workaround what could I do so it would be working for me?
Make different function for _terseprint[] == true and another for _terseprint[] == false hoping that function barrier could avoid this bug?

1 Like

I would say that overloading printing for union types is definitely piracy, so it should generally be avoided. That said, it does indeed look like there’s something else going wrong here.

2 Likes

If it’s really reproducible, you could create an rr-trace and submit that.

2 Likes

I’ve run into some weird stuff with overloaded show methods, the root cause of which is this https://discourse.julialang.org/t/dispatching-on-the-result-of-unwrap-unionall-seems-weird

I’m not sure its related to your problem (in particular not sure why debug mode would matter), but there certainly are alot of the same ingredients as in your code snippet so figured I’d mention.

2 Likes

I am paranoid about overloading show for types. It often makes it hard to debug problems, and there’s a long history of weird behavior associated with such overloads. The best usage of overloading show for types I’ve ever seen was for Unitful.jl, and even they decided to stop doing it: https://github.com/PainterQubits/Unitful.jl/issues/321.

1 Like