Skip to content

Commit

Permalink
more cleanup
Browse files Browse the repository at this point in the history
and improve NTuple show logic to handle constant length Vararg
  • Loading branch information
vtjnash committed Sep 13, 2023
1 parent d3d9470 commit f793245
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 28 deletions.
60 changes: 43 additions & 17 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1092,34 +1092,60 @@ function show_datatype(io::IO, x::DataType, wheres::Vector{TypeVar}=TypeVar[])
# find the length of the homogeneous tail
max_n = 3
taillen = 1
for i in (n-1):-1:1
if parameters[i] === parameters[n]
taillen += 1
pn = parameters[n]
fulln = n
vakind = :none
vaN = 0
if pn isa Core.TypeofVararg
if isdefined(pn, :N)
vaN = pn.N
if vaN isa Int
taillen = vaN
fulln += taillen - 1
vakind = :fixed
else
vakind = :bound
end
else
break
vakind = :unbound
end
pn = unwrapva(pn)
end

# ensure it only contains types
tailstart = n - taillen + 1
if !(parameters[tailstart] isa Type)
if !(pn isa TypeVar || pn isa Type)
# prefer Tuple over NTuple if it contains something other than types
# (e.g. if the user has switched the N and T accidentally)
taillen = 0
elseif vakind === :none || vakind === :fixed
for i in (n-1):-1:1
if parameters[i] === pn
taillen += 1
else
break
end
end
end

if n == taillen && n > max_n
print(io, "NTuple{", n, ", ")
show(io, parameters[1])
# prefer NTuple over Tuple if it is a Vararg without a fixed length
# and prefer Tuple for short lists of elements
if (vakind == :bound && n == 1 == taillen) || (vakind === :fixed && taillen == fulln > max_n) ||
(vakind === :none && taillen == fulln > max_n)
print(io, "NTuple{")
vakind === :bound ? show(io, vaN) : print(io, fulln)
print(io, ", ")
show(io, pn)
print(io, "}")
else
print(io, "Tuple{")
for i = 1:(taillen > max_n ? n-taillen : n)
headlen = (taillen > max_n ? fulln - taillen : fulln)
for i = 1:headlen
i > 1 && print(io, ", ")
show(io, parameters[i])
show(io, vakind === :fixed && i >= n ? pn : parameters[i])
end
if taillen > max_n
print(io, ", Vararg{")
show(io, parameters[n])
print(io, ", ", taillen, "}")
if headlen < fulln
headlen > 0 && print(io, ", ")
print(io, "Vararg{")
show(io, pn)
print(io, ", ", fulln - headlen, "}")
end
print(io, "}")
end
Expand Down
2 changes: 1 addition & 1 deletion src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -1324,7 +1324,7 @@ static int is_nestable_type_param(jl_value_t *t)
return 0;
}

static int jl_valid_type_param(jl_value_t *v)
int jl_valid_type_param(jl_value_t *v)
{
if (jl_is_tuple(v) || jl_is_namedtuple(v))
return is_nestable_type_param(jl_typeof(v));
Expand Down
14 changes: 5 additions & 9 deletions src/jltypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -1833,10 +1833,10 @@ static jl_value_t *normalize_unionalls(jl_value_t *t)
static jl_value_t *jl_tupletype_fill(size_t n, jl_value_t *t, int check)
{
if (check) {
// Since we are skipping making the Vararg, we inline the checks from jl_wrap_vararg here
if (!jl_is_type(t) && !jl_is_typevar(t)) {
// Since we are skipping making the Vararg and skipping checks later,
// we inline the checks from jl_wrap_vararg here now
if (!jl_valid_type_param(t))
jl_type_error_rt("Vararg", "type", (jl_value_t*)jl_type_type, t);
}
// jl_wrap_vararg sometimes simplifies the type, so we only do this 1 time, instead of for each n later
t = normalize_unionalls(t);
jl_value_t *tw = extract_wrapper(t);
Expand Down Expand Up @@ -2227,7 +2227,6 @@ static jl_value_t *inst_tuple_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_
// And avoiding allocating the intermediate steps
// Note this does not instantiate Tuple{Vararg{Int,3}}; that's done in inst_datatype_inner
// Note this does not instantiate NTuple{N,T}, since it is unnecessary and inefficient to expand that now
// and it may skip the constructor checks for jl_wrap_vararg
if (jl_is_va_tuple(tt) && ntp == 1) {
// If this is a Tuple{Vararg{T,N}} with known N and T, expand it to
// a fixed-length tuple
Expand All @@ -2243,7 +2242,7 @@ static jl_value_t *inst_tuple_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_
N = e->val;
e = e->prev;
}
if (T != NULL && N != NULL && jl_is_long(N) && !jl_has_free_typevars(T)) {
if (T != NULL && N != NULL && jl_is_long(N)) { // TODO: && !jl_has_free_typevars(T) to match inst_datatype_inner, or even && jl_is_concrete_type(T)
// Since this is skipping jl_wrap_vararg, we inline the checks from it here
ssize_t nt = jl_unbox_long(N);
if (nt < 0)
Expand Down Expand Up @@ -2444,11 +2443,8 @@ jl_vararg_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n, int check)
}
}
if (t) {
if (!jl_is_type(t) && !jl_is_typevar(t)) {
// Vararg{T} validation is slightly more strict than DataType parameters,
// since it only allows Types and not other things like isbits
if (!jl_valid_type_param(t))
jl_type_error_rt("Vararg", "type", (jl_value_t*)jl_type_type, t);
}
t = normalize_unionalls(t);
jl_value_t *tw = extract_wrapper(t);
if (tw && t != tw && jl_types_equal(t, tw))
Expand Down
2 changes: 2 additions & 0 deletions src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,8 @@ int get_root_reference(rle_reference *rr, jl_method_t *m, size_t i) JL_NOTSAFEPO
jl_value_t *lookup_root(jl_method_t *m, uint64_t key, int index) JL_NOTSAFEPOINT;
int nroots_with_key(jl_method_t *m, uint64_t key) JL_NOTSAFEPOINT;

int jl_valid_type_param(jl_value_t *v);

JL_DLLEXPORT jl_value_t *jl_apply_2va(jl_value_t *f, jl_value_t **args, uint32_t nargs);

void JL_NORETURN jl_method_error(jl_function_t *f, jl_value_t **args, size_t na, size_t world);
Expand Down
2 changes: 1 addition & 1 deletion test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7842,7 +7842,7 @@ const RedefineVarargN{N} = Tuple{Vararg{RedefineVararg, N}}
const RedefineVarargN{N} = Tuple{Vararg{RedefineVararg, N}}

# NTuples with non-types
@test_throws TypeError NTuple{3, 2}
@test NTuple{3, 2} == Tuple{2, 2, 2}

# issue #18621
function f18621()
Expand Down
13 changes: 13 additions & 0 deletions test/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1361,6 +1361,9 @@ test_repr("(:).a")
@test repr(Tuple{Float32, Float32, Float32}) == "Tuple{Float32, Float32, Float32}"
@test repr(Tuple{String, Int64, Int64, Int64}) == "Tuple{String, Int64, Int64, Int64}"
@test repr(Tuple{String, Int64, Int64, Int64, Int64}) == "Tuple{String, Vararg{Int64, 4}}"
@test repr(NTuple) == "NTuple{N, T} where {N, T}"
@test repr(Tuple{NTuple{N}, Vararg{NTuple{N}, 4}} where N) == "NTuple{5, NTuple{N, T} where T} where N"
@test repr(Tuple{Float64, NTuple{N}, Vararg{NTuple{N}, 4}} where N) == "Tuple{Float64, Vararg{NTuple{N, T} where T, 5}} where N"

# Test printing of NamedTuples using the macro syntax
@test repr(@NamedTuple{kw::Int64}) == "@NamedTuple{kw::Int64}"
Expand All @@ -1373,10 +1376,20 @@ test_repr("(:).a")
@test repr(@Kwargs{init::Int}) == "Base.Pairs{Symbol, $Int, Tuple{Symbol}, @NamedTuple{init::$Int}}"

@testset "issue #42931" begin
@test repr(NTuple{4, :A}) == "Tuple{:A, :A, :A, :A}"
@test repr(NTuple{3, :A}) == "Tuple{:A, :A, :A}"
@test repr(NTuple{2, :A}) == "Tuple{:A, :A}"
@test repr(NTuple{1, :A}) == "Tuple{:A}"
@test repr(NTuple{0, :A}) == "Tuple{}"

@test repr(Tuple{:A, :A, :A, :B}) == "Tuple{:A, :A, :A, :B}"
@test repr(Tuple{:A, :A, :A, :A}) == "Tuple{:A, :A, :A, :A}"
@test repr(Tuple{:A, :A, :A}) == "Tuple{:A, :A, :A}"
@test repr(Tuple{:A}) == "Tuple{:A}"
@test repr(Tuple{}) == "Tuple{}"

@test repr(Tuple{Vararg{N, 10}} where N) == "NTuple{10, N} where N"
@test repr(Tuple{Vararg{10, N}} where N) == "Tuple{Vararg{10, N}} where N"
end

# Test that REPL/mime display of invalid UTF-8 data doesn't throw an exception:
Expand Down

0 comments on commit f793245

Please sign in to comment.