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

improve display of keyword argument methods in stack traces #33118

Merged
merged 2 commits into from
Oct 1, 2019

Conversation

JeffBezanson
Copy link
Member

part of #33065

Before:

 [1] checknonsingular at /home/jeff/src/julia/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/factorization.jl:19 [inlined]
 [2] #lu!#133(::Bool, ::typeof(lu!), ::Array{Float64,2}, ::Val{true}) at /home/jeff/src/julia/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/lu.jl:85
 [3] #lu#137 at /home/jeff/src/julia/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/lu.jl:81 [inlined]
 [4] lu at /home/jeff/src/julia/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/lu.jl:272 [inlined] (repeats 2 times)
 [5] top-level scope at REPL[2]:1

after:

 [1] checknonsingular at /home/jeff/src/julia/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/factorization.jl:19 [inlined]
 [2] lu!(::Array{Float64,2}, ::Val{true}; check::Bool) at /home/jeff/src/julia/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/lu.jl:85
 [3] lu at /home/jeff/src/julia/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/lu.jl:81 [inlined]
 [4] lu at /home/jeff/src/julia/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/lu.jl:272 [inlined] (repeats 2 times)
 [5] top-level scope at REPL[2]:1

Before:

julia> code_typed(sin, (2,), debuginfo=:source)
ERROR: argument tuple type must contain only types
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] to_tuple_type(::Any) at ./reflection.jl:727
 [3] #code_typed#20(::Bool, ::Symbol, ::UInt64, ::Core.Compiler.Params, ::typeof(code_typed), ::Any, ::Any) at ./reflection.jl:1052
 [4] (::getfield(Base, Symbol("#kw##code_typed")))(::NamedTuple{(:debuginfo,),Tuple{Symbol}}, ::typeof(code_typed), ::Function, ::Tuple{Int64}) at ./none:0
 [5] top-level scope at REPL[1]:1

after:

julia> code_typed(sin, (2,), debuginfo=:source)
ERROR: argument tuple type must contain only types
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] to_tuple_type(::Any) at ./reflection.jl:740
 [3] code_typed(::Any, ::Any; optimize::Bool, debuginfo::Symbol, world::UInt64, params::Core.Compiler.Params) at ./reflection.jl:1071
 [4] top-level scope at REPL[5]:1

I did several tweaks to get this to work. First, I made some of the name mangling more regular. Now the implementations of kwarg methods (where the actual code goes) have names like f#3 and the argument sorting functions have names like f#kw. Previously the #kw part was removed from the function name (kept only in the type name), which made it hard to identify kw sorters by name alone, which is needed for inlined functions. This change is then used to (1) demangle these names during display and (2) remove kw sorter functions from stack traces entirely.

Second, I added metadata to implementation methods (like #lu!#133 above). It's sufficient just to know that some number of leading arguments are actually keyword arguments. The original function is passed as an argument after those, so we can recover the signature of the original positional-only method.

Note that this still shows the extra dispatch when you call a method that has kw arguments without passing any:

julia> f(;k=1) = error();

julia> f()
ERROR: 
Stacktrace:
 [1] error() at ./error.jl:42
 [2] f(; k::Int64) at ./REPL[6]:1
 [3] f() at ./REPL[6]:1
 [4] top-level scope at REPL[7]:1

The extra f() is tricky to remove. Fortunately it's not so bad, since that frame ([3]) reflects what occurred at the call site so isn't horribly useless/confusing.

@StefanKarpinski
Copy link
Member

Huge improvement! ❤️

@JeffBezanson JeffBezanson force-pushed the jb/kwstacktrace branch 3 times, most recently from 2c5ba85 to 105f847 Compare August 30, 2019 02:28
Copy link
Member

@timholy timholy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nkw is my new favorite thing. Coupled with the lowering of kw methods in more recent Julia versions (which have the

$(Expr(:method, :f))
$(Expr(:method, Symbol("#f#10")))

header) this might be enough info to effectively close #30908. @KristofferC, can you think of anything else we'd really need? I haven't checked what this does to some of the more complicated cases, like kwarg-@generated methods, kwarg methods that define anonymous functions, etc, but presumably those don't introduce any extra unsolvable problems.

base/reflection.jl Outdated Show resolved Hide resolved
base/stacktraces.jl Outdated Show resolved Hide resolved
bt = stacktrace(catch_backtrace())
end
@test any(s->startswith(string(s), "f33065(::Float32; b::Symbol, a::String)"), bt)
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should add some tests for a vararg kwfunc too (e.g. f33065(x; c..., b=1.0, a=""))

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll be darned, that triggers a lowering error. I guess everybody puts the keyword vararg last 😂 Maybe an unintentional feature?

base/errorshow.jl Outdated Show resolved Hide resolved
@@ -602,6 +601,11 @@ function show_backtrace(io::IO, t::Vector{Any})
end
end

function is_kw_sorter_name(name::Symbol)
sn = string(name)
return !startswith(sn, '#') && endswith(sn, "##kw")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a problem?

julia> module Toplevel

       fname = Symbol("#hidden")
       @eval ($fname)(x; y=1, z="hi") = 1

       end

julia> names(Toplevel, all=true)
11-element Array{Symbol,1}:
 Symbol("###hidden#1") 
 Symbol("##hidden")    
 Symbol("##hidden#1")  
 Symbol("#eval")       
 Symbol("#hidden")     
 Symbol("#include")    
 Symbol("#kw###hidden")
 :Toplevel             
 :eval                 
 :fname                
 :include              

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be, but there are probably worse collisions that can happen if user identifiers contain #, so unofficially that is just not supported.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@timholy we now have this syntax for your example 😬

module Toplevel
var"#hidden"(x; y=1, z="hi") = 1
end

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, hadn't followed that. 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😨

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JeffBezanson indeed... unintended consequences.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's still worth having var I suspect. As this reveals, making important decisions on the basis of naming has some potential downsides. One could potentially solve this and #30908 by introducing more fields linking the various lowered representation to one another. Then you wouldn't be beholden to a naming convention.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I mean, we knew var could be used for this kind of thing... just I'd originally envisaged it as more of an internal thing or occasional escape hatch for naming variables in FFIs. Seeing it becoming directly "useful" is kind of disturbing. The most surprising use so far has been the DataFrames people using it to refer to columns with poorly normalized names.

@StefanKarpinski
Copy link
Member

Bump.

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

Successfully merging this pull request may close these issues.

5 participants