From 2e690fcca7f77139f66bf3eae4500d4878267595 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 9 Aug 2022 21:55:31 -0400 Subject: [PATCH] allow nested combinations of (named)tuples, symbols, and bits as type parameters --- NEWS.md | 1 + base/compiler/tfuncs.jl | 13 +------------ base/compiler/typeutils.jl | 21 +++++++++++++++------ src/builtins.c | 22 +++++++++++++++------- test/core.jl | 9 ++++++--- 5 files changed, 38 insertions(+), 28 deletions(-) diff --git a/NEWS.md b/NEWS.md index c4e81ab4e2199..7944878fb6b96 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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 ---------------- diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 5616b50ef3913..4502a6059a2f3 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -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)) diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index 0e59fc9daa8ae..bda892651f844 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -102,17 +102,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] diff --git a/src/builtins.c b/src/builtins.c index 56c3310d511f5..954bff7771e3e 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -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 diff --git a/test/core.jl b/test/core.jl index 88635cf99bda1..4f0824ffdbdba 100644 --- a/test/core.jl +++ b/test/core.jl @@ -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), @@ -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