Skip to content

Commit

Permalink
RFC: errorshow: simplify printing of keyword argument types using a n…
Browse files Browse the repository at this point in the history
…ew macro format

In Julia, keyword arguments are represented as `Base.Pairs` objects.
However, the object type often appears unnecessarily complex,
especially when printed in a stack trace.

This commit aims to simplify the printing of stack traces that involve
keyword method calls, while still allowing us to reconstruct the actual
method signature types from the printed signature types.

The approach is similar to #49117: this commit introduces a new macro
called `Base.@Kwargs`. It follows the same syntax as `@NamedTuple` and
returns a `Base.Pairs` type that is used for keyword method calls.
We use this syntax when printing keyword argument types.

Here's an example of a stack trace:
```diff
diff --git a/b.jl b/a.jl
index 91dd6f0464..b804ae4be5 100644
--- a/b.jl
+++ b/a.jl
@@ -22,12 +22,11 @@ Stacktrace:
     @ Base ./reduce.jl:44 [inlined]
   [6] mapfoldl(f::typeof(identity), op::typeof(Base.add_sum), itr::String; init::Int64)
     @ Base ./reduce.jl:175 [inlined]
-  [7] mapreduce(f::typeof(identity), op::typeof(Base.add_sum), itr::String; kw::Base.Pairs{…})
+  [7] mapreduce(f::typeof(identity), op::typeof(Base.add_sum), itr::String; kw::Base.@kwargs{init::Int64})
     @ Base ./reduce.jl:307 [inlined]
-  [8] sum(f::typeof(identity), a::String; kw::Base.Pairs{Symbol, Int64, Tuple{Symbol}, @NamedTuple{init::Int64}})
+  [8] sum(f::typeof(identity), a::String; kw::Base.@kwargs{init::Int64})
     @ Base ./reduce.jl:535 [inlined]
-  [9] sum(a::String; kw::Base.Pairs{Symbol, Int64, Tuple{Symbol}, @NamedTuple{init::Int64}})
+  [9] sum(a::String; kw::Base.@kwargs{init::Int64})
     @ Base ./reduce.jl:564 [inlined]
  [10] top-level scope
```

Feel free to share any comments or suggestions. If this idea seems
acceptable, I will add test cases and also address any broken test cases.
  • Loading branch information
aviatesk committed May 31, 2023
1 parent 84bf67c commit 60d0d89
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 13 deletions.
7 changes: 7 additions & 0 deletions base/namedtuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,13 @@ macro NamedTuple(ex)
return :(NamedTuple{($(vars...),), Tuple{$(types...)}})
end

macro Kwargs(ex)
return :(let
NT = @NamedTuple $ex
Base.Pairs{keytype(NT),eltype(NT),typeof(NT.parameters[1]),NT}
end)
end

@constprop :aggressive function split_rest(t::NamedTuple{names}, n::Int, st...) where {names}
_check_length_split_rest(length(t), n)
names_front, names_last_n = split_rest(names, n, st...)
Expand Down
52 changes: 39 additions & 13 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1057,10 +1057,27 @@ function show_type_name(io::IO, tn::Core.TypeName)
nothing
end

function maybe_kws_nt(x::DataType)
x.name === typename(Pairs) || return nothing
length(x.parameters) == 4 || return nothing
x.parameters[1] === Symbol || return nothing
p4 = x.parameters[4]
if (isa(p4, DataType) && p4.name === typename(NamedTuple) && length(p4.parameters) == 2)
syms, types = p4.parameters
types isa DataType || return nothing
x.parameters[2] === eltype(p4) || return nothing
isa(syms, Tuple) || return nothing
x.parameters[3] === typeof(syms) || return nothing
return p4
end
return nothing
end

function show_datatype(io::IO, x::DataType, wheres::Vector{TypeVar}=TypeVar[])
parameters = x.parameters::SimpleVector
istuple = x.name === Tuple.name
isnamedtuple = x.name === typename(NamedTuple)
kwsnt = maybe_kws_nt(x)
n = length(parameters)

# Print tuple types with homogeneous tails longer than max_n compactly using `NTuple` or `Vararg`
Expand Down Expand Up @@ -1094,30 +1111,39 @@ function show_datatype(io::IO, x::DataType, wheres::Vector{TypeVar}=TypeVar[])
return
elseif isnamedtuple
syms, types = parameters
first = true
if syms isa Tuple && types isa DataType
print(io, "@NamedTuple{")
for i in 1:length(syms)
if !first
print(io, ", ")
end
print(io, syms[i])
typ = types.parameters[i]
if typ !== Any
print(io, "::")
show(io, typ)
end
first = false
end
show_at_namedtuple(io, syms, types)
print(io, "}")
return
end
elseif kwsnt !== nothing
print(io, "Base.@Kwargs{")
show_at_namedtuple(io, kwsnt.parameters...)
print(io, "}")
return
end

show_type_name(io, x.name)
show_typeparams(io, parameters, (unwrap_unionall(x.name.wrapper)::DataType).parameters, wheres)
end

function show_at_namedtuple(io::IO, syms::Tuple, types::DataType)
first = true
for i in 1:length(syms)
if !first
print(io, ", ")
end
print(io, syms[i])
typ = types.parameters[i]
if typ !== Any
print(io, "::")
show(io, typ)
end
first = false
end
end

function show_supertypes(io::IO, typ::DataType)
print(io, typ)
while typ != Any
Expand Down

0 comments on commit 60d0d89

Please sign in to comment.