Skip to content

Commit

Permalink
allow nested combinations of (named)tuples, symbols, and bits as type…
Browse files Browse the repository at this point in the history
… parameters (#46300)
  • Loading branch information
JeffBezanson committed Aug 31, 2022
1 parent 1ece3f6 commit 53bb7fb
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 28 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ New language features
handled via `Base.split_rest`. ([#42902])
* Character literals now support the same syntax allowed in string literals; i.e. the syntax can
represent invalid UTF-8 sequences as allowed by the `Char` type ([#44989]).
* Nested combinations of tuples and named tuples of symbols are now allowed as type parameters ([#46300]).

Language changes
----------------
Expand Down
13 changes: 1 addition & 12 deletions base/compiler/tfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1351,18 +1351,7 @@ end
add_tfunc(fieldtype, 2, 3, fieldtype_tfunc, 0)

# Like `valid_tparam`, but in the type domain.
function valid_tparam_type(T::DataType)
T === Symbol && return true
isbitstype(T) && return true
if T <: Tuple
isconcretetype(T) || return false
for P in T.parameters
(P === Symbol || isbitstype(P)) || return false
end
return true
end
return false
end
valid_tparam_type(T::DataType) = valid_typeof_tparam(T)
valid_tparam_type(U::Union) = valid_tparam_type(U.a) && valid_tparam_type(U.b)
valid_tparam_type(U::UnionAll) = valid_tparam_type(unwrap_unionall(U))

Expand Down
21 changes: 15 additions & 6 deletions base/compiler/typeutils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -103,17 +103,26 @@ function valid_as_lattice(@nospecialize(x))
return false
end

# test if non-Type, non-TypeVar `x` can be used to parameterize a type
function valid_tparam(@nospecialize(x))
if isa(x, Tuple)
for t in x
isa(t, Symbol) || isbits(t) || return false
function valid_typeof_tparam(@nospecialize(t))
if t === Symbol || isbitstype(t)
return true
end
isconcretetype(t) || return false
if t <: NamedTuple
t = t.parameters[2]
end
if t <: Tuple
for p in t.parameters
valid_typeof_tparam(p) || return false
end
return true
end
return isa(x, Symbol) || isbits(x)
return false
end

# test if non-Type, non-TypeVar `x` can be used to parameterize a type
valid_tparam(@nospecialize(x)) = valid_typeof_tparam(typeof(x))

function compatible_vatuple(a::DataType, b::DataType)
vaa = a.parameters[end]
vab = a.parameters[end]
Expand Down
22 changes: 15 additions & 7 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -1258,20 +1258,28 @@ JL_CALLABLE(jl_f_set_binding_type)

// apply_type -----------------------------------------------------------------

int jl_valid_type_param(jl_value_t *v)
static int is_nestable_type_param(jl_value_t *t)
{
if (jl_is_tuple(v)) {
if (jl_is_namedtuple_type(t))
t = jl_tparam1(t);
if (jl_is_tuple_type(t)) {
// NOTE: tuples of symbols are not currently bits types, but have been
// allowed as type parameters. this is a bit ugly.
jl_value_t *tt = jl_typeof(v);
size_t i, l = jl_nparams(tt);
for(i=0; i < l; i++) {
jl_value_t *pi = jl_tparam(tt,i);
if (!(pi == (jl_value_t*)jl_symbol_type || jl_isbits(pi)))
size_t i, l = jl_nparams(t);
for (i = 0; i < l; i++) {
jl_value_t *pi = jl_tparam(t, i);
if (!(pi == (jl_value_t*)jl_symbol_type || jl_isbits(pi) || is_nestable_type_param(pi)))
return 0;
}
return 1;
}
return 0;
}

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));
if (jl_is_vararg(v))
return 0;
// TODO: maybe more things
Expand Down
9 changes: 6 additions & 3 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1987,9 +1987,8 @@ mutable struct TupleParam{P}
x::Bool
end

function tupledispatch(a::TupleParam{(1,:a)})
a.x
end
tupledispatch(a::TupleParam{(1,:a)}) = a.x
tupledispatch(a::TupleParam{(1,(:a,))}) = 42

# tuples can be used as type params
let t1 = TupleParam{(1,:a)}(true),
Expand All @@ -2001,6 +2000,10 @@ let t1 = TupleParam{(1,:a)}(true),
# dispatch works properly
@test tupledispatch(t1) == true
@test_throws MethodError tupledispatch(t2)

@test tupledispatch(TupleParam{(1,(:a,))}(true)) === 42
@test_throws TypeError TupleParam{NamedTuple{(:a,), Tuple{Any}}((1,))}
@test_throws TypeError Val{NamedTuple{(:a,), Tuple{NamedTuple{<:Any,Tuple{Int}}}}(((x=2,),))}
end

# issue #5254
Expand Down

0 comments on commit 53bb7fb

Please sign in to comment.