Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Error showing value of type: type TypeVar has no field var" - error displaying MethodInstance encountered from MathOptInterface #38195

Open
NHDaly opened this issue Oct 27, 2020 · 7 comments

Comments

@NHDaly
Copy link
Member

NHDaly commented Oct 27, 2020

When I was using the mechanism introduced in #37749 to log all of the MethodInstances that are inferred during execution of some code, I've encountered a MethodInstance that throws an error when it is displayed:

Error showing value of type Core.Compiler.Timings.Timing:
ERROR: type TypeVar has no field var
Stacktrace:
   [1] getproperty(x::TypeVar, f::Symbol)
     @ Base ./Base.jl:33
   [2] show(io::IOContext{IOBuffer}, x::Type)
     @ Base ./show.jl:799
   [3] sprint(f::Function, args::Type; context::IOContext{Base.TTY}, sizehint::Int64)
     @ Base ./strings/io.jl:103

As you can see, the MethodInstance and its specTypes do not print via the normal show():

julia> gt.mi_info.mi
MethodInstance for operate(::typeof(+), ::Type{T}, ::MathOptInterface.ScalarAffineFunction{T}, ::Error showing value of type Core.MethodInstance:
ERROR: type TypeVar has no field var
Stacktrace:


julia> gt.mi_info.mi.specTypes
Tuple{typeof(MathOptInterface.Utilities.operate), typeof(+), Type{T}, MathOptInterface.ScalarAffineFunction{T}, Error showing value of type UnionAll:
ERROR: type TypeVar has no field var
Stacktrace:

So in order to get more information about it, I've used static_show() to print it:

julia> function static_shown(x)
           p = Pipe()
           Base.link_pipe!(p, reader_supports_async=true, writer_supports_async=true)
           ccall(:jl_static_show, Cvoid, (Ptr{Cvoid}, Any), p.in, x)
           @async close(p.in)
           return read(p.out, String)
       end

Here's the method instance that fails to print:

julia> static_shown(gt.mi_info.mi.specTypes)
"Tuple{typeof(MathOptInterface.Utilities.operate), typeof(Base.:(+)), Type{T}, MathOptInterface.ScalarAffineFunction{T}, T where T<:T} where T<:(Blobs.Blob{_A} where _A)"

julia> static_shown(gt.mi_info.mi)
"operate(typeof(Base.:(+)), Type{T<:(Blobs.Blob{_A} where _A)}, MathOptInterface.ScalarAffineFunction{T<:(Blobs.Blob{_A} where _A)}, T where T<:T<:(Blobs.Blob{_A} where _A)) where {T<:(Blobs.Blob{_A} where _A)} from operate(Union{typeof(Base.:(+)), typeof(Base.:(-))}, Type{T}, MathOptInterface.ScalarAffineFunction{T}, Union{MathOptInterface.SingleVariable, MathOptInterface.ScalarAffineFunction{T}, T}) where {T}"

One weird thing that stands out to me is that from what I can tell this is the EXACT method instance that seems to be causing a StackOverflow in julia 1.6 during type inference, as reported in #22787! So maybe these are related?

Another weird thing is that if I copy/paste the output from static_shown into the REPL, it actually displays correctly!:

julia> Tuple{typeof(MathOptInterface.Utilities.operate), typeof(Base.:(+)), Type{T}, MathOptInterface.ScalarAffineFunction{T}, T where T<:T} where T<:(Blobs.Blob{_A} where _A)
Tuple{typeof(MathOptInterface.Utilities.operate),typeof(+),Type{T},MathOptInterface.ScalarAffineFunction{T},T} where T<:(Blob{_A} where _A)

Which makes me think there's some other error in the construction of the type that's not being captured by static_show()...

@NHDaly
Copy link
Member Author

NHDaly commented Oct 27, 2020

Also weird, the body and var print okay on their own, they just don't print correctly together:

julia> gt.mi_info.mi.specTypes.body
Tuple{typeof(MathOptInterface.Utilities.operate), typeof(+), Type{T<:(Blobs.Blob{_A} where _A)}, MathOptInterface.ScalarAffineFunction{T<:(Blobs.Blob{_A} where _A)}, T where T<:T<:(Blobs.Blob{_A} where _A)}

julia> gt.mi_info.mi.specTypes.var
T<:(Blobs.Blob{_A} where _A)

julia> gt.mi_info.mi.specTypes
Tuple{typeof(MathOptInterface.Utilities.operate), typeof(+), Type{T}, MathOptInterface.ScalarAffineFunction{T}, Error showing value of type UnionAll:
ERROR: type TypeVar has no field var
Stacktrace:
  [1] getproperty(x::TypeVar, f::Symbol)
    @ Base ./Base.jl:33
  [2] show(io::IOContext{Base.TTY}, x::Type)
    @ Base ./show.jl:799
  [3] show_datatype(io::IOContext{Base.TTY}, x::DataType)
    @ Base ./show.jl:875

Also there are a lot of seemingly redundant type variables in there..

@NHDaly
Copy link
Member Author

NHDaly commented Oct 27, 2020

It turns out this method is specialized on a bunch of different types and 16 / 88 of them don't print:

julia> const mi_problems = Any[]
Any[]

julia> for mi in MethodAnalysis.methodinstances(MathOptInterface.Utilities.operate)
           try
               repr(mi)
           catch e
               push!(mi_problems, mi)
               @show e
           end
       end
e = ErrorException("type TypeVar has no field var")
e = ErrorException("type TypeVar has no field var")
...

julia> mi_problems
16-element Array{Any,1}:
Error showing value of type Array{Any,1}:
ERROR: type TypeVar has no field var

Here are a few of them:

julia> static_shown(mi_problems[5])
"operate(typeof(Base.:(+)), Type{T<:Tuple{Base.OneTo{T} where T<:Integer}}, MathOptInterface.ScalarAffineFunction{T<:Tuple{Base.OneTo{T} where T<:Integer}}, T where T<:T<:Tuple{Base.OneTo{T} where T<:Integer}) where {T<:Tuple{Base.OneTo{T} where T<:Integer}}"

julia> static_shown(mi_problems[6])
"operate(typeof(Base.:(+)), Type{T<:(Blobs.Blob{_A} where _A)}, MathOptInterface.ScalarAffineFunction{T<:(Blobs.Blob{_A} where _A)}, T where T<:T<:(Blobs.Blob{_A} where _A)) where {T<:(Blobs.Blob{_A} where _A)}"

julia> static_shown(mi_problems[7])
"operate(typeof(Base.:(+)), Type{T<:(Ptr{_A} where _A)}, MathOptInterface.ScalarAffineFunction{T<:(Ptr{_A} where _A)}, T where T<:T<:(Ptr{_A} where _A)) where {T<:(Ptr{_A} where _A)}"

This last one (7) I think should be reproducible with only the MathOptInterface package.

@NHDaly
Copy link
Member Author

NHDaly commented Oct 27, 2020

In case this is helpful, here's an upload of the above mi_problems[7] Type Tuple serialized via Serialization:

bad-mi-operate-+-ScalarAffineFunction.jlserialized.zip

This was serialized on julia 1.5.2, like this:

julia> serialize("/tmp/bad-mi-operate-+-ScalarAffineFunction.jlserialized", mi_problems[7].specTypes)

Here's some version info:

  [b8f27783] MathOptInterface v0.9.17
julia> versioninfo()
Julia Version 1.5.2
Commit 5beddbf698 (2020-10-06 17:36 UTC)
Platform Info:
  OS: macOS (x86_64-apple-darwin19.6.0)
  CPU: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-9.0.1 (ORCJIT, skylake)

@JeffBezanson
Copy link
Member

Another weird thing is that if I copy/paste the output from static_shown into the REPL, it actually displays correctly!:

This can sometimes happen if there are name collisions in UnionAll variables, such that the output doesn't actually correspond to the same type.

This seems to be due to the non-normalized T where T<:T, causing the UnionAll to collapse during show. I wonder where it came from.

@NHDaly
Copy link
Member Author

NHDaly commented Oct 27, 2020

Another weird thing is that if I copy/paste the output from static_shown into the REPL, it actually displays correctly!:

This can sometimes happen if there are name collisions in UnionAll variables, such that the output doesn't actually correspond to the same type.

Yeah, makes sense. thanks!

This seems to be due to the non-normalized T where T<:T, causing the UnionAll to collapse during show. I wonder where it came from.

Hmm yeah, weird. I'm not sure, either. Do you think this is an issue that comes from MathOptInterface somehow? Or is it an issue in julia?

And also, do you think this is indeed related to #22787? It seems weird to me that this same method instance showed up in both issues.

@NHDaly
Copy link
Member Author

NHDaly commented Nov 13, 2020

I walked through with the Debugger on julia 1.5.3, and i think the results are interesting. I'll verify them on 1.6 once I have an example test case up and running on 1.6 again.

It seems like the problem occurs here, where the call to UnionAll doesn't produce a UnionAll, and instead produces a DataType:

x = UnionAll(newtv, x{newtv})

So then the subsequent call to x.var, right below, throws an exception, because type DataType has no field var:

show(IOContext(io, :unionall_env => x.var), x.body)


I verified this locally by reconstructing the variables, and indeed, calling UnionAll returns a DataType. Is that a bug?:

julia> mi.specTypes
Tuple{typeof(MathOptInterface.Utilities.operate),typeof(+),Type{T},MathOptInterface.SingleVariable,Error showing value of type UnionAll:
ERROR: type DataType has no field var
[...]

julia> x = mi.specTypes.body.parameters[5]
T where T<:MathOptInterface.ScalarAffineFunction{T<:(MathOptInterface.ScalarAffineFunction{T} where T<:MathOptInterface.AbstractScalarFunction)}

julia> typeof(x)
UnionAll

julia> T1 = @eval Base let x=$x, io=$(IOContext(stdout, :unionall_env => mi.specTypes.var));
                   counter = 1
                   newname = Symbol(x.var.name, counter)
                   newtv = TypeVar(newname, x.var.lb, x.var.ub)
       end
T1<:MathOptInterface.ScalarAffineFunction{T<:(MathOptInterface.ScalarAffineFunction{T} where T<:MathOptInterface.AbstractScalarFunction)}

julia> x{T1}
T1<:MathOptInterface.ScalarAffineFunction{T<:(MathOptInterface.ScalarAffineFunction{T} where T<:MathOptInterface.AbstractScalarFunction)}

julia> UnionAll(T1, x{T1})
MathOptInterface.ScalarAffineFunction{T<:(MathOptInterface.ScalarAffineFunction{T} where T<:MathOptInterface.AbstractScalarFunction)}

julia> typeof(UnionAll(T1, x{T1}))
DataType

I guess this is the problem. Is this a bug in UnionAll? Or is the entire type tuple malformed somehow? Or is it simply a bug in show() that we could fix?

It would be really great to fix this, since it currently throws an exception when attempting to build a FlameGraph from the result of @snoopi_deep, when it attempts to print these method instances.

Thanks! :)

@NHDaly
Copy link
Member Author

NHDaly commented Nov 13, 2020

Here are the results on 1.6 -- it seems to behave similarly incorrectly, except now it produces a TypeVar instead of a UnionAll, which still seems incorrect?:

julia> mi.specTypes
Tuple{typeof(MathOptInterface.Utilities.operate), typeof(+), Type{T}, MathOptInterface.ScalarAffineFunction{T}, Error showing value of type UnionAll:
ERROR: type TypeVar has no field var
Stacktrace:
  [1] getproperty(x::TypeVar, f::Symbol)
    @ Base ./Base.jl:33
  [2] show(io::IOContext{Base.TTY}, x::Type)
    @ Base ./show.jl:801
  [3] show_datatype(io::IOContext{Base.TTY}, x::DataType)
    @ Base ./show.jl:877
  [4] show(io::IOContext{Base.TTY}, x::Type)
    @ Base ./show.jl:775
  [5] show(io::IOContext{Base.TTY}, x::Type)
    @ Base ./show.jl:801
  [6] show(io::IOContext{Base.TTY}, #unused#::MIME{Symbol("text/plain")}, x::Type)
    @ Base ./show.jl:750
[...]

julia> x = mi.specTypes.body.parameters[5]
T where T<:T<:(Blobs.Blob{Delve.PagedDataStructures.BeMessageValue{V}} where V)

julia> typeof(x)
UnionAll

julia> T1 = @eval Base let x=$x, io=$(IOContext(stdout, :unionall_env => mi.specTypes.var));
                   counter = 1
                   newname = Symbol(x.var.name, counter)
                   newtv = TypeVar(newname, x.var.lb, x.var.ub)
       end
T1<:T<:(Blobs.Blob{Delve.PagedDataStructures.BeMessageValue{V}} where V)

julia> x{T1}
T1<:T<:(Blobs.Blob{Delve.PagedDataStructures.BeMessageValue{V}} where V)

julia> UnionAll(T1, x{T1})
T<:(Blobs.Blob{Delve.PagedDataStructures.BeMessageValue{V}} where V)

julia> typeof(UnionAll(T1, x{T1}))
TypeVar

NHDaly added a commit to timholy/SnoopCompile.jl that referenced this issue Nov 27, 2020
timholy pushed a commit to timholy/SnoopCompile.jl that referenced this issue Nov 27, 2020
* Dramatically improve `flamegraph(timing)` performance

Move method instance's specTypes Type Tuple from a compile-time argument
to a runtime argument, to prevent compiling a specialization for every
_value_ in the data!

I hadn't meant to write it that way in the first place, I had just done
it absent-mindedly, because `specTypes` is a Type, so I hadn't thought
about moving it back into the value domain.

We used `@snoopi_deep` to find out the problem and fix this performance
problem! It's neat to see it profiling itself! 🎉

* Add error-handling for malformed Type Tuples that fail to print.

This apparently can happen sometimes, see:
JuliaLang/julia#38195
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants