From ccb0a02dc669a90cd6afdcece5a1f3ad2cc3bcc0 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sun, 16 Oct 2022 04:00:40 -0400 Subject: [PATCH] remove kwfuncs from Julia (#47157) * remove kwfuncs from Julia These kwsorter methods only exist for dispatch, which means they do not need to be unique. We can optionally have the primary MethodTable contain an extra kwtable::MethodTable field, if this turns out to be slow, since this already introduces the concept of `jl_kwmethod_table_for` (for better reflection and max_args). * remove jl_f_kwinvoke builtin, reimplement in Julia This instantly grants total inference and inlining support, where previously this was a completely opaque call! --- base/Base.jl | 11 +++++ base/boot.jl | 8 ++-- base/compiler/abstractinterpretation.jl | 11 ----- base/compiler/tfuncs.jl | 6 +-- base/compiler/utilities.jl | 2 +- base/deprecated.jl | 7 +-- base/error.jl | 2 +- base/errorshow.jl | 27 +++++++----- base/essentials.jl | 4 +- base/methodshow.jl | 8 +--- base/reflection.jl | 4 +- contrib/generate_precompile.jl | 1 - doc/src/devdocs/functions.md | 12 +++--- doc/src/devdocs/locks.md | 2 +- doc/src/devdocs/types.md | 1 - src/builtin_proto.h | 6 --- src/builtins.c | 49 --------------------- src/codegen.cpp | 1 - src/common_symbols1.inc | 2 +- src/datatype.c | 1 - src/dump.c | 44 ------------------- src/gf.c | 52 ++++++----------------- src/init.c | 3 ++ src/jl_exported_data.inc | 1 + src/jl_exported_funcs.inc | 2 - src/jltypes.c | 26 ++++++------ src/julia-syntax.scm | 10 ++--- src/julia.h | 3 +- src/julia_internal.h | 6 ++- src/method.c | 37 ++++++++++------ src/rtutils.c | 34 +++++++-------- src/staticdata.c | 7 ++- stdlib/InteractiveUtils/src/macros.jl | 2 +- stdlib/Serialization/src/Serialization.jl | 17 ++++++-- stdlib/Serialization/test/runtests.jl | 23 ++++++---- test/compiler/inference.jl | 4 -- test/compiler/inline.jl | 26 ++++++------ test/keywordargs.jl | 2 +- test/precompile.jl | 10 ++--- test/reduce.jl | 8 ++-- test/worlds.jl | 2 +- 41 files changed, 184 insertions(+), 300 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index 63728fdba3e4e..f8df4047eb6fa 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -103,6 +103,17 @@ include("generator.jl") include("reflection.jl") include("options.jl") +# define invoke(f, T, args...; kwargs...), without kwargs wrapping +# to forward to invoke +function Core.kwcall(kwargs, ::typeof(invoke), f, T, args...) + @inline + # prepend kwargs and f to the invoked from the user + T = rewrap_unionall(Tuple{Any, Core.Typeof(f), (unwrap_unionall(T)::DataType).parameters...}, T) + return invoke(Core.kwcall, T, kwargs, f, args...) +end +# invoke does not have its own call cache, but kwcall for invoke does +typeof(invoke).name.mt.max_args = 3 # invoke, f, T, args... + # core operations & types include("promotion.jl") include("tuple.jl") diff --git a/base/boot.jl b/base/boot.jl index 38d011a0a5d05..80ef23cd0fd78 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -369,9 +369,11 @@ include(m::Module, fname::String) = ccall(:jl_load_, Any, (Any, Any), m, fname) eval(m::Module, @nospecialize(e)) = ccall(:jl_toplevel_eval_in, Any, (Any, Any), m, e) -kwfunc(@nospecialize(f)) = ccall(:jl_get_keyword_sorter, Any, (Any,), f) - -kwftype(@nospecialize(t)) = typeof(ccall(:jl_get_kwsorter, Any, (Any,), t)) +# dispatch token indicating a kwarg (keyword sorter) call +function kwcall end +# deprecated internal functions: +kwfunc(@nospecialize(f)) = kwcall +kwftype(@nospecialize(t)) = typeof(kwcall) mutable struct Box contents::Any diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 65d0accb0b02f..3f84fff4f2e7b 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -1754,17 +1754,6 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), elseif isa(f, Core.OpaqueClosure) # calling an OpaqueClosure about which we have no information returns no information return CallMeta(Any, Effects(), NoCallInfo()) - elseif f === Core.kwfunc - if la == 2 - aty = argtypes[2] - if !isvarargtype(aty) - ft = widenconst(aty) - if isa(ft, DataType) && isdefined(ft.name, :mt) && isdefined(ft.name.mt, :kwsorter) - return CallMeta(Const(ft.name.mt.kwsorter), EFFECTS_TOTAL, MethodResultPure()) - end - end - end - return CallMeta(Any, EFFECTS_UNKNOWN, NoCallInfo()) elseif f === TypeVar # Manually look through the definition of TypeVar to # make sure to be able to get `PartialTypeVar`s out. diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index c715137b44a22..6222d9f735cfd 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1877,9 +1877,6 @@ function _builtin_nothrow(@specialize(lattice::AbstractLattice), @nospecialize(f elseif f === Core.sizeof length(argtypes) == 1 || return false return sizeof_nothrow(argtypes[1]) - elseif f === Core.kwfunc - length(argtypes) == 1 || return false - return isa(rt, Const) elseif f === Core.ifelse length(argtypes) == 3 || return false return argtypes[1] ⊑ₗ Bool @@ -1919,7 +1916,7 @@ const _PURE_BUILTINS = Any[tuple, svec, ===, typeof, nfields] const _EFFECT_FREE_BUILTINS = [ fieldtype, apply_type, isa, UnionAll, getfield, arrayref, const_arrayref, isdefined, Core.sizeof, - Core.kwfunc, Core.ifelse, Core._typevar, (<:), + Core.ifelse, Core._typevar, (<:), typeassert, throw, arraysize, getglobal, compilerbarrier ] @@ -1934,7 +1931,6 @@ const _CONSISTENT_BUILTINS = Any[ isa, UnionAll, Core.sizeof, - Core.kwfunc, Core.ifelse, (<:), typeassert, diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index e71597c68bdb2..a7f1e0982296e 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -146,7 +146,7 @@ end function get_compileable_sig(method::Method, @nospecialize(atype), sparams::SimpleVector) isa(atype, DataType) || return nothing - mt = ccall(:jl_method_table_for, Any, (Any,), atype) + mt = ccall(:jl_method_get_table, Any, (Any,), method) mt === nothing && return nothing return ccall(:jl_normalize_to_compilable_sig, Any, (Any, Any, Any, Any), mt, atype, sparams, method) diff --git a/base/deprecated.jl b/base/deprecated.jl index a2f5555a40da4..6953cd600cacd 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -167,11 +167,8 @@ function firstcaller(bt::Vector, funcsyms) if !found li = lkup.linfo if li isa Core.MethodInstance - ft = ccall(:jl_first_argument_datatype, Any, (Any,), (li.def::Method).sig) - if isType(ft) - ft = unwrap_unionall(ft.parameters[1]) - found = (isa(ft, DataType) && ft.name.name in funcsyms) - end + def = li.def + found = def isa Method && def.name in funcsyms end end end diff --git a/base/error.jl b/base/error.jl index 4459e54def19b..07f66aa5cf6d2 100644 --- a/base/error.jl +++ b/base/error.jl @@ -162,7 +162,7 @@ end ## keyword arg lowering generates calls to this ## function kwerr(kw, args::Vararg{Any,N}) where {N} @noinline - throw(MethodError(typeof(args[1]).name.mt.kwsorter, (kw,args...))) + throw(MethodError(Core.kwcall, (kw, args...))) end ## system error handling ## diff --git a/base/errorshow.jl b/base/errorshow.jl index 55e8e5af883b8..16190d64e01e4 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -235,17 +235,16 @@ function showerror(io::IO, ex::MethodError) show_candidates = true print(io, "MethodError: ") ft = typeof(f) - name = ft.name.mt.name f_is_function = false kwargs = () - if endswith(string(ft.name.name), "##kw") - f = ex.args[2] + if f === Core.kwcall && !is_arg_types + f = (ex.args::Tuple)[2] ft = typeof(f) - name = ft.name.mt.name arg_types_param = arg_types_param[3:end] kwargs = pairs(ex.args[1]) ex = MethodError(f, ex.args[3:end::Int]) end + name = ft.name.mt.name if f === Base.convert && length(arg_types_param) == 2 && !is_arg_types f_is_function = true show_convert_error(io, ex, arg_types_param) @@ -794,11 +793,6 @@ function show_backtrace(io::IO, t::Vector) end -function is_kw_sorter_name(name::Symbol) - sn = string(name) - return !startswith(sn, '#') && endswith(sn, "##kw") -end - # For improved user experience, filter out frames for include() implementation # - see #33065. See also #35371 for extended discussion of internal frames. function _simplify_include_frames(trace) @@ -850,15 +844,26 @@ function process_backtrace(t::Vector, limit::Int=typemax(Int); skipC = true) continue end - if (lkup.from_c && skipC) || is_kw_sorter_name(lkup.func) + if (lkup.from_c && skipC) continue end + code = lkup.linfo + if code isa MethodInstance + def = code.def + if def isa Method + if def.name === :kwcall && def.module === Core + continue + end + end + elseif !lkup.from_c + lkup.func === :kwcall && continue + end count += 1 if count > limit break end - if lkup.file != last_frame.file || lkup.line != last_frame.line || lkup.func != last_frame.func || lkup.linfo !== lkup.linfo + if lkup.file != last_frame.file || lkup.line != last_frame.line || lkup.func != last_frame.func || lkup.linfo !== last_frame.linfo if n > 0 push!(ret, (last_frame, n)) end diff --git a/base/essentials.jl b/base/essentials.jl index daee352b7649d..d33aca52073fa 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -809,7 +809,7 @@ function invokelatest(@nospecialize(f), @nospecialize args...; kwargs...) if isempty(kwargs) return Core._call_latest(f, args...) end - return Core._call_latest(Core.kwfunc(f), kwargs, f, args...) + return Core._call_latest(Core.kwcall, kwargs, f, args...) end """ @@ -843,7 +843,7 @@ function invoke_in_world(world::UInt, @nospecialize(f), @nospecialize args...; k if isempty(kwargs) return Core._call_in_world(world, f, args...) end - return Core._call_in_world(world, Core.kwfunc(f), kwargs, f, args...) + return Core._call_in_world(world, Core.kwcall, kwargs, f, args...) end inferencebarrier(@nospecialize(x)) = compilerbarrier(:type, x) diff --git a/base/methodshow.jl b/base/methodshow.jl index 4bd29f75c361d..25ac5bba97d03 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -78,12 +78,8 @@ end # NOTE: second argument is deprecated and is no longer used function kwarg_decl(m::Method, kwtype = nothing) - if m.sig === Tuple # OpaqueClosure - return Symbol[] - end - mt = get_methodtable(m) - if isdefined(mt, :kwsorter) - kwtype = typeof(mt.kwsorter) + if m.sig !== Tuple # OpaqueClosure or Builtin + kwtype = typeof(Core.kwcall) sig = rewrap_unionall(Tuple{kwtype, Any, (unwrap_unionall(m.sig)::DataType).parameters...}, m.sig) kwli = ccall(:jl_methtable_lookup, Any, (Any, Any, UInt), kwtype.name.mt, sig, get_world_counter()) if kwli !== nothing diff --git a/base/reflection.jl b/base/reflection.jl index 0c1a09068e418..3c5887ec8dfb6 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -866,7 +866,7 @@ function to_tuple_type(@nospecialize(t)) if isa(t, Type) && t <: Tuple for p in unwrap_unionall(t).parameters if isa(p, Core.TypeofVararg) - p = p.T + p = unwrapva(p) end if !(isa(p, Type) || isa(p, TypeVar)) error("argument tuple type must contain only types") @@ -1804,7 +1804,7 @@ function delete_method(m::Method) end function get_methodtable(m::Method) - return ccall(:jl_method_table_for, Any, (Any,), m.sig)::Core.MethodTable + return ccall(:jl_method_get_table, Any, (Any,), m)::Core.MethodTable end """ diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 63e0a86a94d26..8fd6e2542023e 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -212,7 +212,6 @@ if Test !== nothing precompile(Tuple{typeof(Test.match_logs), Function, Tuple{String, Regex}}) precompile(Tuple{typeof(Base.CoreLogging.shouldlog), Test.TestLogger, Base.CoreLogging.LogLevel, Module, Symbol, Symbol}) precompile(Tuple{typeof(Base.CoreLogging.handle_message), Test.TestLogger, Base.CoreLogging.LogLevel, String, Module, Symbol, Symbol, String, Int}) - precompile(Tuple{typeof(Core.kwfunc(Base.CoreLogging.handle_message)), typeof((exception=nothing,)), typeof(Base.CoreLogging.handle_message), Test.TestLogger, Base.CoreLogging.LogLevel, String, Module, Symbol, Symbol, String, Int}) precompile(Tuple{typeof(Test.detect_ambiguities), Any}) precompile(Tuple{typeof(Test.collect_test_logs), Function}) precompile(Tuple{typeof(Test.do_broken_test), Test.ExecutionResult, Any}) diff --git a/doc/src/devdocs/functions.md b/doc/src/devdocs/functions.md index 13f863cd26d81..283f63b2d0dce 100644 --- a/doc/src/devdocs/functions.md +++ b/doc/src/devdocs/functions.md @@ -48,7 +48,7 @@ jl_value_t *jl_call(jl_function_t *f, jl_value_t **args, int32_t nargs); Given the above dispatch process, conceptually all that is needed to add a new method is (1) a tuple type, and (2) code for the body of the method. `jl_method_def` implements this operation. -`jl_first_argument_datatype` is called to extract the relevant method table from what would be +`jl_method_table_for` is called to extract the relevant method table from what would be the type of the first argument. This is much more complicated than the corresponding procedure during dispatch, since the argument tuple type might be abstract. For example, we can define: @@ -141,9 +141,9 @@ but works reasonably well. ## Keyword arguments -Keyword arguments work by associating a special, hidden function object with each method table -that has definitions with keyword arguments. This function is called the "keyword argument sorter" -or "keyword sorter", or "kwsorter", and is stored in the `kwsorter` field of `MethodTable` objects. +Keyword arguments work by adding methods to the kwcall function. This function +is usually the "keyword argument sorter" or "keyword sorter", which then calls +the inner body of the function (defined anonymously). Every definition in the kwsorter function has the same arguments as some definition in the normal method table, except with a single `NamedTuple` argument prepended, which gives the names and values of passed keyword arguments. The kwsorter's job is to move keyword arguments @@ -220,10 +220,10 @@ circle((0,0), 1.0, color = red; other...) is lowered to: ```julia -kwfunc(circle)(merge((color = red,), other), circle, (0,0), 1.0) +kwcall(merge((color = red,), other), circle, (0,0), 1.0) ``` - `kwfunc` (also in`Core`) fetches the kwsorter for the called function. + `kwcall` (also in`Core`) denotes a kwcall signature and dispatch. The keyword splatting operation (written as `other...`) calls the named tuple `merge` function. This function further unpacks each *element* of `other`, expecting each one to contain two values (a symbol and a value). diff --git a/doc/src/devdocs/locks.md b/doc/src/devdocs/locks.md index 6cc0c1270ca85..f2ddc26fb954d 100644 --- a/doc/src/devdocs/locks.md +++ b/doc/src/devdocs/locks.md @@ -127,7 +127,7 @@ These data structures each need locks due to being shared mutable global state. list for the above lock priority list. This list does not include level 1 leaf resources due to their simplicity. -MethodTable modifications (def, cache, kwsorter type) : MethodTable->writelock +MethodTable modifications (def, cache) : MethodTable->writelock Type declarations : toplevel lock diff --git a/doc/src/devdocs/types.md b/doc/src/devdocs/types.md index 003574f99c182..c3afc26600c65 100644 --- a/doc/src/devdocs/types.md +++ b/doc/src/devdocs/types.md @@ -198,7 +198,6 @@ TypeName defs: Nothing nothing cache: Nothing nothing max_args: Int64 0 - kwsorter: #undef module: Module Core : Int64 0 : Int64 0 diff --git a/src/builtin_proto.h b/src/builtin_proto.h index f61f76c3966f8..64e3fbd1af366 100644 --- a/src/builtin_proto.h +++ b/src/builtin_proto.h @@ -62,12 +62,6 @@ DECLARE_BUILTIN(finalizer); DECLARE_BUILTIN(_compute_sparams); DECLARE_BUILTIN(_svec_ref); -JL_CALLABLE(jl_f_invoke_kwsorter); -#ifdef DEFINE_BUILTIN_GLOBALS -JL_DLLEXPORT jl_fptr_args_t jl_f_invoke_kwsorter_addr = &jl_f_invoke_kwsorter; -#else -JL_DLLEXPORT extern jl_fptr_args_t jl_f_invoke_kwsorter_addr; -#endif JL_CALLABLE(jl_f__structtype); JL_CALLABLE(jl_f__abstracttype); JL_CALLABLE(jl_f__primitivetype); diff --git a/src/builtins.c b/src/builtins.c index 595014e97ee50..323a42b91ca92 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1359,50 +1359,6 @@ JL_CALLABLE(jl_f_invoke) return res; } -JL_CALLABLE(jl_f_invoke_kwsorter) -{ - JL_NARGSV(invoke, 3); - jl_value_t *kwargs = args[0]; - // args[1] is `invoke` itself - jl_value_t *func = args[2]; - jl_value_t *argtypes = args[3]; - jl_value_t *kws = jl_get_keyword_sorter(func); - JL_GC_PUSH1(&argtypes); - if (jl_is_tuple_type(argtypes)) { - // construct a tuple type for invoking a keyword sorter by putting the kw container type - // and the type of the function at the front. - size_t i, nt = jl_nparams(argtypes) + 2; - if (nt < jl_page_size/sizeof(jl_value_t*)) { - jl_value_t **types = (jl_value_t**)alloca(nt*sizeof(jl_value_t*)); - types[0] = (jl_value_t*)jl_namedtuple_type; - types[1] = jl_is_type(func) ? (jl_value_t*)jl_wrap_Type(func) : jl_typeof(func); - for (i = 2; i < nt; i++) - types[i] = jl_tparam(argtypes, i - 2); - argtypes = (jl_value_t*)jl_apply_tuple_type_v(types, nt); - } - else { - jl_svec_t *types = jl_alloc_svec_uninit(nt); - JL_GC_PUSH1(&types); - jl_svecset(types, 0, jl_namedtuple_type); - jl_svecset(types, 1, jl_is_type(func) ? (jl_value_t*)jl_wrap_Type(func) : jl_typeof(func)); - for (i = 2; i < nt; i++) - jl_svecset(types, i, jl_tparam(argtypes, i - 2)); - argtypes = (jl_value_t*)jl_apply_tuple_type(types); - JL_GC_POP(); - } - } - else { - // invoke will throw an error - } - args[0] = kws; - args[1] = argtypes; - args[2] = kwargs; - args[3] = func; - jl_value_t *res = jl_f_invoke(NULL, args, nargs); - JL_GC_POP(); - return res; -} - // Expr constructor for internal use ------------------------------------------ jl_expr_t *jl_exprn(jl_sym_t *head, size_t n) @@ -2011,11 +1967,6 @@ void jl_init_primitives(void) JL_GC_DISABLED // method table utils jl_builtin_applicable = add_builtin_func("applicable", jl_f_applicable); jl_builtin_invoke = add_builtin_func("invoke", jl_f_invoke); - jl_typename_t *itn = ((jl_datatype_t*)jl_typeof(jl_builtin_invoke))->name; - jl_value_t *ikws = jl_new_generic_function_with_supertype(itn->name, jl_core_module, jl_builtin_type); - itn->mt->kwsorter = ikws; - jl_gc_wb(itn->mt, ikws); - jl_mk_builtin_func((jl_datatype_t*)jl_typeof(ikws), jl_symbol_name(jl_gf_name(ikws)), jl_f_invoke_kwsorter); // internal functions jl_builtin_apply_type = add_builtin_func("apply_type", jl_f_apply_type); diff --git a/src/codegen.cpp b/src/codegen.cpp index 372e9bc560e15..45401bca51e27 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1174,7 +1174,6 @@ static const auto &builtin_func_map() { { jl_f_svec_addr, new JuliaFunction{XSTR(jl_f_svec), get_func_sig, get_func_attrs} }, { jl_f_applicable_addr, new JuliaFunction{XSTR(jl_f_applicable), get_func_sig, get_func_attrs} }, { jl_f_invoke_addr, new JuliaFunction{XSTR(jl_f_invoke), get_func_sig, get_func_attrs} }, - { jl_f_invoke_kwsorter_addr, new JuliaFunction{XSTR(jl_f_invoke_kwsorter), get_func_sig, get_func_attrs} }, { jl_f_isdefined_addr, new JuliaFunction{XSTR(jl_f_isdefined), get_func_sig, get_func_attrs} }, { jl_f_getfield_addr, new JuliaFunction{XSTR(jl_f_getfield), get_func_sig, get_func_attrs} }, { jl_f_setfield_addr, new JuliaFunction{XSTR(jl_f_setfield), get_func_sig, get_func_attrs} }, diff --git a/src/common_symbols1.inc b/src/common_symbols1.inc index 7d445289e80fa..867961bc9a1d2 100644 --- a/src/common_symbols1.inc +++ b/src/common_symbols1.inc @@ -70,7 +70,7 @@ jl_symbol("toInt64"), jl_symbol("arraylen"), jl_symbol("typeassert"), jl_symbol("map"), -jl_symbol("kwfunc"), +jl_symbol("kwcall"), jl_symbol("ArgumentError"), jl_symbol("lshr_int"), jl_symbol("axes"), diff --git a/src/datatype.c b/src/datatype.c index 41e9d71a60843..fb63b67e5b830 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -52,7 +52,6 @@ JL_DLLEXPORT jl_methtable_t *jl_new_method_table(jl_sym_t *name, jl_module_t *mo jl_atomic_store_relaxed(&mt->leafcache, (jl_array_t*)jl_an_empty_vec_any); jl_atomic_store_relaxed(&mt->cache, jl_nothing); mt->max_args = 0; - mt->kwsorter = NULL; mt->backedges = NULL; JL_MUTEX_INIT(&mt->writelock); mt->offs = 0; diff --git a/src/dump.c b/src/dump.c index fbfbf2916d029..55d6fc0e3f3d3 100644 --- a/src/dump.c +++ b/src/dump.c @@ -475,40 +475,6 @@ static void jl_serialize_datatype(jl_serializer_state *s, jl_datatype_t *dt) JL_ tag = 12; } - char *dtname = jl_symbol_name(dt->name->name); - size_t dtnl = strlen(dtname); - if (dtnl > 4 && strcmp(&dtname[dtnl - 4], "##kw") == 0 && !internal && tag != 0) { - /* XXX: yuck, this is horrible, but the auto-generated kw types from the serializer isn't a real type, so we *must* be very careful */ - assert(tag == 6); // other struct types should never exist - tag = 9; - if (jl_type_type_mt->kwsorter != NULL && dt == (jl_datatype_t*)jl_typeof(jl_type_type_mt->kwsorter)) { - dt = jl_datatype_type; // any representative member with this MethodTable - } - else if (jl_nonfunction_mt->kwsorter != NULL && dt == (jl_datatype_t*)jl_typeof(jl_nonfunction_mt->kwsorter)) { - dt = jl_symbol_type; // any representative member with this MethodTable - } - else { - // search for the representative member of this MethodTable - jl_methtable_t *mt = dt->name->mt; - size_t l = strlen(jl_symbol_name(mt->name)); - char *prefixed; - prefixed = (char*)malloc_s(l + 2); - prefixed[0] = '#'; - strcpy(&prefixed[1], jl_symbol_name(mt->name)); - // remove ##kw suffix - prefixed[l-3] = 0; - jl_sym_t *tname = jl_symbol(prefixed); - free(prefixed); - jl_value_t *primarydt = jl_get_global(mt->module, tname); - if (!primarydt) - primarydt = jl_get_global(mt->module, mt->name); - primarydt = jl_unwrap_unionall(primarydt); - assert(jl_is_datatype(primarydt)); - assert(primarydt == (jl_value_t*)jl_any_type || jl_typeof(((jl_datatype_t*)primarydt)->name->mt->kwsorter) == (jl_value_t*)dt); - dt = (jl_datatype_t*)primarydt; - } - } - write_uint8(s->s, TAG_DATATYPE); write_uint8(s->s, tag); if (tag == 6 || tag == 7) { @@ -517,10 +483,6 @@ static void jl_serialize_datatype(jl_serializer_state *s, jl_datatype_t *dt) JL_ jl_serialize_value(s, dt->parameters); return; } - if (tag == 9) { - jl_serialize_value(s, dt); - return; - } write_int32(s->s, dt->size); int has_instance = (dt->instance != NULL); @@ -1675,12 +1637,6 @@ static jl_value_t *jl_deserialize_datatype(jl_serializer_state *s, int pos, jl_v backref_list.items[pos] = dtv; return dtv; } - if (tag == 9) { - jl_datatype_t *primarydt = (jl_datatype_t*)jl_deserialize_value(s, NULL); - jl_value_t *dtv = jl_typeof(jl_get_kwsorter((jl_value_t*)primarydt)); - backref_list.items[pos] = dtv; - return dtv; - } if (!(tag == 0 || tag == 5 || tag == 10 || tag == 11 || tag == 12)) { assert(0 && "corrupt deserialization state"); abort(); diff --git a/src/gf.c b/src/gf.c index 4de51e19a88ad..1f896921d45f5 100644 --- a/src/gf.c +++ b/src/gf.c @@ -871,10 +871,11 @@ JL_DLLEXPORT int jl_isa_compileable_sig( unsigned nspec_min = nargs + 1; // min number of non-vararg values before vararg unsigned nspec_max = INT32_MAX; // max number of non-vararg values before vararg jl_methtable_t *mt = jl_method_table_for(decl); + jl_methtable_t *kwmt = mt == jl_kwcall_mt ? jl_kwmethod_table_for(decl) : mt; if ((jl_value_t*)mt != jl_nothing) { // try to refine estimate of min and max - if (mt != jl_type_type_mt && mt != jl_nonfunction_mt) - nspec_min = mt->max_args + 2; + if (kwmt && kwmt != jl_type_type_mt && kwmt != jl_nonfunction_mt && kwmt != jl_kwcall_mt) + nspec_min = kwmt->max_args + 2 + 2 * (mt == jl_kwcall_mt); else nspec_max = nspec_min; } @@ -1080,7 +1081,8 @@ static jl_method_instance_t *cache_method( int cache_with_orig = 1; jl_tupletype_t *compilationsig = tt; - intptr_t nspec = (mt == NULL || mt == jl_type_type_mt || mt == jl_nonfunction_mt ? definition->nargs + 1 : mt->max_args + 2); + jl_methtable_t *kwmt = mt == jl_kwcall_mt ? jl_kwmethod_table_for(definition->sig) : mt; + intptr_t nspec = (kwmt == NULL || kwmt == jl_type_type_mt || kwmt == jl_nonfunction_mt || kwmt == jl_kwcall_mt ? definition->nargs + 1 : kwmt->max_args + 2 + 2 * (mt == jl_kwcall_mt)); jl_compilation_sig(tt, sparams, definition, nspec, &newparams); if (newparams) { compilationsig = jl_apply_tuple_type(newparams); @@ -1333,7 +1335,9 @@ static void method_overwrite(jl_typemap_entry_t *newentry, jl_method_t *oldvalue jl_method_t *method = (jl_method_t*)newentry->func.method; jl_module_t *newmod = method->module; jl_module_t *oldmod = oldvalue->module; - jl_datatype_t *dt = jl_first_argument_datatype(oldvalue->sig); + jl_datatype_t *dt = jl_nth_argument_datatype(oldvalue->sig, 1); + if (dt == (jl_datatype_t*)jl_typeof(jl_kwcall_func)) + dt = jl_nth_argument_datatype(oldvalue->sig, 3); int anon = dt && is_anonfn_typename(jl_symbol_name(dt->name->name)); if ((jl_options.warn_overwrite == JL_OPTIONS_WARN_OVERWRITE_ON) || (jl_options.incremental && jl_generating_output()) || anon) { @@ -1358,7 +1362,7 @@ static void method_overwrite(jl_typemap_entry_t *newentry, jl_method_t *oldvalue static void update_max_args(jl_methtable_t *mt, jl_value_t *type) { - if (mt == jl_type_type_mt || mt == jl_nonfunction_mt) + if (mt == jl_type_type_mt || mt == jl_nonfunction_mt || mt == jl_kwcall_mt) return; type = jl_unwrap_unionall(type); assert(jl_is_datatype(type)); @@ -2241,7 +2245,8 @@ JL_DLLEXPORT jl_value_t *jl_normalize_to_compilable_sig(jl_methtable_t *mt, jl_t jl_tupletype_t *tt = NULL; jl_svec_t *newparams = NULL; JL_GC_PUSH2(&tt, &newparams); - intptr_t nspec = (mt == jl_type_type_mt || mt == jl_nonfunction_mt ? m->nargs + 1 : mt->max_args + 2); + jl_methtable_t *kwmt = mt == jl_kwcall_mt ? jl_kwmethod_table_for(m->sig) : mt; + intptr_t nspec = (kwmt == NULL || kwmt == jl_type_type_mt || kwmt == jl_nonfunction_mt || kwmt == jl_kwcall_mt ? m->nargs + 1 : kwmt->max_args + 2 + 2 * (mt == jl_kwcall_mt)); jl_compilation_sig(ti, env, m, nspec, &newparams); tt = (newparams ? jl_apply_tuple_type(newparams) : ti); int is_compileable = ((jl_datatype_t*)ti)->isdispatchtuple || @@ -2277,7 +2282,7 @@ jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types JL_PROPAGATES jl_tupletype_t *ti = match->spec_types; jl_method_instance_t *nf = NULL; if (jl_is_datatype(ti)) { - jl_methtable_t *mt = jl_method_table_for((jl_value_t*)ti); + jl_methtable_t *mt = jl_method_get_table(m); if ((jl_value_t*)mt != jl_nothing) { // get the specialization without caching it if (mt_cache && ((jl_datatype_t*)ti)->isdispatchtuple) { @@ -2757,39 +2762,6 @@ jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_ return (jl_function_t*)f; } -JL_DLLEXPORT jl_function_t *jl_get_kwsorter(jl_value_t *ty) -{ - jl_methtable_t *mt = jl_argument_method_table(ty); - if ((jl_value_t*)mt == jl_nothing) - jl_error("cannot get keyword sorter for abstract type"); - if (!mt->kwsorter) { - JL_LOCK(&mt->writelock); - if (!mt->kwsorter) { - char *name; - if (mt == jl_nonfunction_mt) { - name = jl_symbol_name(mt->name); - } - else { - jl_datatype_t *dt = (jl_datatype_t*)jl_argument_datatype(ty); - assert(jl_is_datatype(dt)); - name = jl_symbol_name(dt->name->name); - if (name[0] == '#') - name++; - } - size_t l = strlen(name); - char *suffixed = (char*)malloc_s(l+5); - strcpy(&suffixed[0], name); - strcpy(&suffixed[l], "##kw"); - jl_sym_t *fname = jl_symbol(suffixed); - free(suffixed); - mt->kwsorter = jl_new_generic_function_with_supertype(fname, mt->module, jl_function_type); - jl_gc_wb(mt, mt->kwsorter); - } - JL_UNLOCK(&mt->writelock); - } - return mt->kwsorter; -} - jl_function_t *jl_new_generic_function(jl_sym_t *name, jl_module_t *module) { return jl_new_generic_function_with_supertype(name, module, jl_function_type); diff --git a/src/init.c b/src/init.c index 25ad798d040fe..54cce84c763af 100644 --- a/src/init.c +++ b/src/init.c @@ -854,6 +854,9 @@ static void post_boot_hooks(void) jl_loaderror_type = (jl_datatype_t*)core("LoadError"); jl_initerror_type = (jl_datatype_t*)core("InitError"); jl_pair_type = core("Pair"); + jl_kwcall_func = core("kwcall"); + jl_kwcall_mt = ((jl_datatype_t*)jl_typeof(jl_kwcall_func))->name->mt; + jl_kwcall_mt->max_args = 0; jl_weakref_type = (jl_datatype_t*)core("WeakRef"); jl_vecelement_typename = ((jl_datatype_t*)jl_unwrap_unionall(core("VecElement")))->name; diff --git a/src/jl_exported_data.inc b/src/jl_exported_data.inc index e426f2c1f8b42..6f0671ef0d6f7 100644 --- a/src/jl_exported_data.inc +++ b/src/jl_exported_data.inc @@ -55,6 +55,7 @@ XX(jl_interconditional_type) \ XX(jl_interrupt_exception) \ XX(jl_intrinsic_type) \ + XX(jl_kwcall_func) \ XX(jl_lineinfonode_type) \ XX(jl_linenumbernode_type) \ XX(jl_llvmpointer_type) \ diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index e2934824bad75..80b56f735b68b 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -221,8 +221,6 @@ XX(jl_get_JIT) \ XX(jl_get_julia_bin) \ XX(jl_get_julia_bindir) \ - XX(jl_get_keyword_sorter) \ - XX(jl_get_kwsorter) \ XX(jl_get_method_inferred) \ XX(jl_get_module_binding) \ XX(jl_get_module_compile) \ diff --git a/src/jltypes.c b/src/jltypes.c index 991a511ab2556..59f6dad2e87ff 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2084,17 +2084,17 @@ void jl_init_types(void) JL_GC_DISABLED jl_methtable_type->name->mt = jl_nonfunction_mt; jl_methtable_type->super = jl_any_type; jl_methtable_type->parameters = jl_emptysvec; - jl_methtable_type->name->n_uninitialized = 12 - 5; - jl_methtable_type->name->names = jl_perm_symsvec(12, "name", "defs", + jl_methtable_type->name->n_uninitialized = 11 - 6; + jl_methtable_type->name->names = jl_perm_symsvec(11, "name", "defs", "leafcache", "cache", "max_args", - "kwsorter", "module", - "backedges", "", "", "offs", ""); - jl_methtable_type->types = jl_svec(12, jl_symbol_type, jl_any_type, jl_any_type, + "module", "backedges", + "", "", "offs", ""); + jl_methtable_type->types = jl_svec(11, jl_symbol_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type/*jl_long*/, - jl_any_type, jl_any_type/*module*/, - jl_any_type/*any vector*/, jl_any_type/*voidpointer*/, jl_any_type/*int32*/, + jl_any_type/*module*/, jl_any_type/*any vector*/, + jl_any_type/*voidpointer*/, jl_any_type/*int32*/, jl_any_type/*uint8*/, jl_any_type/*uint8*/); - const static uint32_t methtable_constfields[1] = { 0x00000040 }; // (1<<6); + const static uint32_t methtable_constfields[1] = { 0x00000020 }; // (1<<5); jl_methtable_type->name->constfields = methtable_constfields; jl_precompute_memoized_dt(jl_methtable_type, 1); @@ -2722,12 +2722,12 @@ void jl_init_types(void) JL_GC_DISABLED jl_svecset(jl_typename_type->types, 13, jl_uint8_type); jl_svecset(jl_typename_type->types, 14, jl_uint8_type); jl_svecset(jl_methtable_type->types, 4, jl_long_type); - jl_svecset(jl_methtable_type->types, 6, jl_module_type); - jl_svecset(jl_methtable_type->types, 7, jl_array_any_type); - jl_svecset(jl_methtable_type->types, 8, jl_long_type); // voidpointer - jl_svecset(jl_methtable_type->types, 9, jl_long_type); // uint32_t plus alignment + jl_svecset(jl_methtable_type->types, 5, jl_module_type); + jl_svecset(jl_methtable_type->types, 6, jl_array_any_type); + jl_svecset(jl_methtable_type->types, 7, jl_long_type); // voidpointer + jl_svecset(jl_methtable_type->types, 8, jl_long_type); // uint32_t plus alignment + jl_svecset(jl_methtable_type->types, 9, jl_uint8_type); jl_svecset(jl_methtable_type->types, 10, jl_uint8_type); - jl_svecset(jl_methtable_type->types, 11, jl_uint8_type); jl_svecset(jl_method_type->types, 12, jl_method_instance_type); jl_svecset(jl_method_instance_type->types, 6, jl_code_instance_type); jl_svecset(jl_code_instance_type->types, 13, jl_voidpointer_type); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 8af1bc8b80d23..cdf6687d30a15 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1662,7 +1662,7 @@ (define (kwcall-unless-empty f pa kw-container-test kw-container) `(if (call (top isempty) ,kw-container-test) (call ,f ,@pa) - (call (call (core kwfunc) ,f) ,kw-container ,f ,@pa))) + (call (core kwcall) ,kw-container ,f ,@pa))) (let ((f (if (sym-ref? fexpr) fexpr (make-ssavalue))) (kw-container (make-ssavalue))) @@ -1676,7 +1676,7 @@ #t)) ,(if (every vararg? kw) (kwcall-unless-empty f pa kw-container kw-container) - `(call (call (core kwfunc) ,f) ,kw-container ,f ,@pa))))) + `(call (core kwcall) ,kw-container ,f ,@pa))))) ;; convert `a+=b` to `a=a+b` (define (expand-update-operator- op op= lhs rhs declT) @@ -3348,9 +3348,9 @@ (let ((vi (get tab (cadr e) #f))) (if vi (vinfo:set-called! vi #t)) - ;; calls to functions with keyword args go through `kwfunc` first - (if (and (length= e 3) (equal? (cadr e) '(core kwfunc))) - (let ((vi2 (get tab (caddr e) #f))) + ;; calls to functions with keyword args have head of `kwcall` first + (if (and (length> e 3) (equal? (cadr e) '(core kwcall))) + (let ((vi2 (get tab (cadddr e) #f))) (if vi2 (vinfo:set-called! vi2 #t)))) (for-each (lambda (x) (analyze-vars x env captvars sp tab)) diff --git a/src/julia.h b/src/julia.h index 163eda6e40198..8bb3d4deac839 100644 --- a/src/julia.h +++ b/src/julia.h @@ -656,7 +656,6 @@ typedef struct _jl_methtable_t { _Atomic(jl_array_t*) leafcache; _Atomic(jl_typemap_t*) cache; intptr_t max_args; // max # of non-vararg arguments in a signature - jl_value_t *kwsorter; // keyword argument sorter function jl_module_t *module; // used for incremental serialization to locate original binding jl_array_t *backedges; // (sig, caller::MethodInstance) pairs jl_mutex_t writelock; @@ -806,6 +805,7 @@ extern JL_DLLIMPORT jl_value_t *jl_emptytuple JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_true JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_false JL_GLOBALLY_ROOTED; extern JL_DLLIMPORT jl_value_t *jl_nothing JL_GLOBALLY_ROOTED; +extern JL_DLLIMPORT jl_value_t *jl_kwcall_func JL_GLOBALLY_ROOTED; // gc ------------------------------------------------------------------------- @@ -1496,7 +1496,6 @@ JL_DLLEXPORT jl_method_t *jl_method_def(jl_svec_t *argdata, jl_methtable_t *mt, JL_DLLEXPORT jl_code_info_t *jl_code_for_staged(jl_method_instance_t *linfo); JL_DLLEXPORT jl_code_info_t *jl_copy_code_info(jl_code_info_t *src); JL_DLLEXPORT size_t jl_get_world_counter(void) JL_NOTSAFEPOINT; -JL_DLLEXPORT jl_function_t *jl_get_kwsorter(jl_value_t *ty); JL_DLLEXPORT jl_value_t *jl_box_bool(int8_t x) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_box_int8(int8_t x) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_box_uint8(uint8_t x) JL_NOTSAFEPOINT; diff --git a/src/julia_internal.h b/src/julia_internal.h index 5423f07846456..7eb34239e783b 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -282,6 +282,7 @@ static inline void memmove_refs(void **dstp, void *const *srcp, size_t n) JL_NOT // useful constants extern jl_methtable_t *jl_type_type_mt JL_GLOBALLY_ROOTED; extern jl_methtable_t *jl_nonfunction_mt JL_GLOBALLY_ROOTED; +extern jl_methtable_t *jl_kwcall_mt JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT _Atomic(size_t) jl_world_counter; typedef void (*tracer_cb)(jl_value_t *tracee); @@ -621,7 +622,6 @@ JL_DLLEXPORT jl_value_t *jl_apply_2va(jl_value_t *f, jl_value_t **args, uint32_t void JL_NORETURN jl_method_error(jl_function_t *f, jl_value_t **args, size_t na, size_t world); JL_DLLEXPORT jl_value_t *jl_get_exceptionf(jl_datatype_t *exception_type, const char *fmt, ...); -JL_DLLEXPORT jl_value_t *jl_get_keyword_sorter(jl_value_t *f); JL_DLLEXPORT void jl_typeassert(jl_value_t *x, jl_value_t *t); #define JL_CALLABLE(name) \ @@ -720,10 +720,12 @@ JL_DLLEXPORT jl_value_t *jl_matching_methods(jl_tupletype_t *types, jl_value_t * JL_DLLEXPORT jl_value_t *jl_gf_invoke_lookup_worlds(jl_value_t *types, jl_value_t *mt, size_t world, size_t *min_world, size_t *max_world); -JL_DLLEXPORT jl_datatype_t *jl_first_argument_datatype(jl_value_t *argtypes JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; +jl_datatype_t *jl_nth_argument_datatype(jl_value_t *argtypes JL_PROPAGATES_ROOT, int n) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_value_t *jl_argument_datatype(jl_value_t *argt JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_methtable_t *jl_method_table_for( jl_value_t *argtypes JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; +jl_methtable_t *jl_kwmethod_table_for( + jl_value_t *argtypes JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_methtable_t *jl_method_get_table( jl_method_t *method JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; jl_methtable_t *jl_argument_method_table(jl_value_t *argt JL_PROPAGATES_ROOT); diff --git a/src/method.c b/src/method.c index 97bea4c08d486..ec49fdf32a193 100644 --- a/src/method.c +++ b/src/method.c @@ -17,6 +17,7 @@ extern "C" { extern jl_value_t *jl_builtin_getfield; extern jl_value_t *jl_builtin_tuple; +jl_methtable_t *jl_kwcall_mt; jl_method_t *jl_make_opaque_closure_method(jl_module_t *module, jl_value_t *name, int nargs, jl_value_t *functionloc, jl_code_info_t *ci, int isva); @@ -902,30 +903,30 @@ JL_DLLEXPORT jl_value_t *jl_generic_function_def(jl_sym_t *name, return gf; } -static jl_methtable_t *first_methtable(jl_value_t *a JL_PROPAGATES_ROOT, int got_tuple1) JL_NOTSAFEPOINT +static jl_methtable_t *nth_methtable(jl_value_t *a JL_PROPAGATES_ROOT, int n) JL_NOTSAFEPOINT { if (jl_is_datatype(a)) { - if (got_tuple1) { + if (n == 0) { jl_methtable_t *mt = ((jl_datatype_t*)a)->name->mt; if (mt != NULL) return mt; } if (jl_is_tuple_type(a)) { - if (jl_nparams(a) >= 1) - return first_methtable(jl_tparam0(a), 1); + if (jl_nparams(a) >= n) + return nth_methtable(jl_tparam(a, n - 1), 0); } } else if (jl_is_typevar(a)) { - return first_methtable(((jl_tvar_t*)a)->ub, got_tuple1); + return nth_methtable(((jl_tvar_t*)a)->ub, n); } else if (jl_is_unionall(a)) { - return first_methtable(((jl_unionall_t*)a)->body, got_tuple1); + return nth_methtable(((jl_unionall_t*)a)->body, n); } else if (jl_is_uniontype(a)) { jl_uniontype_t *u = (jl_uniontype_t*)a; - jl_methtable_t *m1 = first_methtable(u->a, got_tuple1); + jl_methtable_t *m1 = nth_methtable(u->a, n); if ((jl_value_t*)m1 != jl_nothing) { - jl_methtable_t *m2 = first_methtable(u->b, got_tuple1); + jl_methtable_t *m2 = nth_methtable(u->b, n); if (m1 == m2) return m1; } @@ -936,7 +937,15 @@ static jl_methtable_t *first_methtable(jl_value_t *a JL_PROPAGATES_ROOT, int got // get the MethodTable for dispatch, or `nothing` if cannot be determined JL_DLLEXPORT jl_methtable_t *jl_method_table_for(jl_value_t *argtypes JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT { - return first_methtable(argtypes, 0); + return nth_methtable(argtypes, 1); +} + +jl_methtable_t *jl_kwmethod_table_for(jl_value_t *argtypes JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT +{ + jl_methtable_t *kwmt = nth_methtable(argtypes, 3); + if ((jl_value_t*)kwmt == jl_nothing) + return NULL; + return kwmt; } JL_DLLEXPORT jl_methtable_t *jl_method_get_table(jl_method_t *method JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT @@ -947,7 +956,7 @@ JL_DLLEXPORT jl_methtable_t *jl_method_get_table(jl_method_t *method JL_PROPAGAT // get the MethodTable implied by a single given type, or `nothing` JL_DLLEXPORT jl_methtable_t *jl_argument_method_table(jl_value_t *argt JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT { - return first_methtable(argt, 1); + return nth_methtable(argt, 0); } jl_array_t *jl_all_methods JL_GLOBALLY_ROOTED; @@ -991,11 +1000,13 @@ JL_DLLEXPORT jl_method_t* jl_method_def(jl_svec_t *argdata, int32_t line = jl_linenode_line(functionloc); // TODO: derive our debug name from the syntax instead of the type - name = mt->name; - if (mt == jl_type_type_mt || mt == jl_nonfunction_mt || external_mt) { + jl_methtable_t *kwmt = mt == jl_kwcall_mt ? jl_kwmethod_table_for(argtype) : mt; + // if we have a kwcall, try to derive the name from the callee argument method table + name = (kwmt ? kwmt : mt)->name; + if (kwmt == jl_type_type_mt || kwmt == jl_nonfunction_mt || external_mt) { // our value for `name` is bad, try to guess what the syntax might have had, // like `jl_static_show_func_sig` might have come up with - jl_datatype_t *dt = jl_first_argument_datatype(argtype); + jl_datatype_t *dt = jl_nth_argument_datatype(argtype, mt == jl_kwcall_mt ? 3 : 1); if (dt != NULL) { name = dt->name->name; if (jl_is_type_type((jl_value_t*)dt)) { diff --git a/src/rtutils.c b/src/rtutils.c index f3a2e745ed651..497b348f871d5 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -202,12 +202,6 @@ JL_DLLEXPORT void JL_NORETURN jl_eof_error(void) jl_throw(jl_new_struct(eof_error)); } -// get kwsorter field, with appropriate error check and message -JL_DLLEXPORT jl_value_t *jl_get_keyword_sorter(jl_value_t *f) -{ - return jl_get_kwsorter(jl_typeof(f)); -} - JL_DLLEXPORT void jl_typeassert(jl_value_t *x, jl_value_t *t) { if (!jl_isa(x,t)) @@ -583,29 +577,29 @@ JL_DLLEXPORT int jl_is_identifier(char *str) JL_NOTSAFEPOINT return 1; } -static jl_datatype_t *first_arg_datatype(jl_value_t *a JL_PROPAGATES_ROOT, int got_tuple1) JL_NOTSAFEPOINT +static jl_datatype_t *nth_arg_datatype(jl_value_t *a JL_PROPAGATES_ROOT, int n) JL_NOTSAFEPOINT { if (jl_is_datatype(a)) { - if (got_tuple1) + if (n == 0) return (jl_datatype_t*)a; if (jl_is_tuple_type(a)) { - if (jl_nparams(a) < 1) + if (jl_nparams(a) < n) return NULL; - return first_arg_datatype(jl_tparam0(a), 1); + return nth_arg_datatype(jl_tparam(a, n - 1), 0); } return NULL; } else if (jl_is_typevar(a)) { - return first_arg_datatype(((jl_tvar_t*)a)->ub, got_tuple1); + return nth_arg_datatype(((jl_tvar_t*)a)->ub, n); } else if (jl_is_unionall(a)) { - return first_arg_datatype(((jl_unionall_t*)a)->body, got_tuple1); + return nth_arg_datatype(((jl_unionall_t*)a)->body, n); } else if (jl_is_uniontype(a)) { jl_uniontype_t *u = (jl_uniontype_t*)a; - jl_datatype_t *d1 = first_arg_datatype(u->a, got_tuple1); + jl_datatype_t *d1 = nth_arg_datatype(u->a, n); if (d1 == NULL) return NULL; - jl_datatype_t *d2 = first_arg_datatype(u->b, got_tuple1); + jl_datatype_t *d2 = nth_arg_datatype(u->b, n); if (d2 == NULL || d1->name != d2->name) return NULL; return d1; @@ -614,15 +608,15 @@ static jl_datatype_t *first_arg_datatype(jl_value_t *a JL_PROPAGATES_ROOT, int g } // get DataType of first tuple element (if present), or NULL if cannot be determined -JL_DLLEXPORT jl_datatype_t *jl_first_argument_datatype(jl_value_t *argtypes JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT +jl_datatype_t *jl_nth_argument_datatype(jl_value_t *argtypes JL_PROPAGATES_ROOT, int n) JL_NOTSAFEPOINT { - return first_arg_datatype(argtypes, 0); + return nth_arg_datatype(argtypes, n); } // get DataType implied by a single given type, or `nothing` JL_DLLEXPORT jl_value_t *jl_argument_datatype(jl_value_t *argt JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT { - jl_datatype_t *dt = first_arg_datatype(argt, 1); + jl_datatype_t *dt = nth_arg_datatype(argt, 0); if (dt == NULL) return jl_nothing; return (jl_value_t*)dt; @@ -1260,7 +1254,7 @@ JL_DLLEXPORT size_t jl_static_show_func_sig(JL_STREAM *s, jl_value_t *type) JL_N { size_t n = 0; size_t i; - jl_value_t *ftype = (jl_value_t*)jl_first_argument_datatype(type); + jl_value_t *ftype = (jl_value_t*)jl_nth_argument_datatype(type, 1); if (ftype == NULL) return jl_static_show(s, type); jl_unionall_t *tvars = (jl_unionall_t*)type; @@ -1279,7 +1273,9 @@ JL_DLLEXPORT size_t jl_static_show_func_sig(JL_STREAM *s, jl_value_t *type) JL_N n += jl_static_show(s, type); return n; } - if (jl_nparams(ftype) == 0 || ftype == ((jl_datatype_t*)ftype)->name->wrapper) { + if ((jl_nparams(ftype) == 0 || ftype == ((jl_datatype_t*)ftype)->name->wrapper) && + ((jl_datatype_t*)ftype)->name->mt != jl_type_type_mt && + ((jl_datatype_t*)ftype)->name->mt != jl_nonfunction_mt) { n += jl_printf(s, "%s", jl_symbol_name(((jl_datatype_t*)ftype)->name->mt->name)); } else { diff --git a/src/staticdata.c b/src/staticdata.c index 854f639360712..7ce5b0a427253 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -81,7 +81,7 @@ extern "C" { // TODO: put WeakRefs on the weak_refs list during deserialization // TODO: handle finalizers -#define NUM_TAGS 156 +#define NUM_TAGS 157 // An array of references that need to be restored from the sysimg // This is a manually constructed dual of the gvars array, which would be produced by codegen for Julia code, for C. @@ -223,6 +223,7 @@ jl_value_t **const*const get_tags(void) { INSERT_TAG(jl_typeinf_func); INSERT_TAG(jl_type_type_mt); INSERT_TAG(jl_nonfunction_mt); + INSERT_TAG(jl_kwcall_func); // some Core.Builtin Functions that we want to be able to reference: INSERT_TAG(jl_builtin_throw); @@ -310,7 +311,7 @@ static const jl_fptr_args_t id_to_fptrs[] = { &jl_f_throw, &jl_f_is, &jl_f_typeof, &jl_f_issubtype, &jl_f_isa, &jl_f_typeassert, &jl_f__apply_iterate, &jl_f__apply_pure, &jl_f__call_latest, &jl_f__call_in_world, &jl_f__call_in_world_total, &jl_f_isdefined, - &jl_f_tuple, &jl_f_svec, &jl_f_intrinsic_call, &jl_f_invoke_kwsorter, + &jl_f_tuple, &jl_f_svec, &jl_f_intrinsic_call, &jl_f_getfield, &jl_f_setfield, &jl_f_swapfield, &jl_f_modifyfield, &jl_f_replacefield, &jl_f_fieldtype, &jl_f_nfields, &jl_f_arrayref, &jl_f_const_arrayref, &jl_f_arrayset, &jl_f_arraysize, &jl_f_apply_type, @@ -2229,6 +2230,8 @@ static void jl_restore_system_image_from_stream(ios_t *f) JL_GC_DISABLED ios_close(&gvar_record); s.s = NULL; + jl_kwcall_mt = ((jl_datatype_t*)jl_typeof(jl_kwcall_func))->name->mt; + s.s = f; // reinit items except ccallables jl_finalize_deserializer(&s); diff --git a/stdlib/InteractiveUtils/src/macros.jl b/stdlib/InteractiveUtils/src/macros.jl index aec10a19aab03..a9c0283a6fba3 100644 --- a/stdlib/InteractiveUtils/src/macros.jl +++ b/stdlib/InteractiveUtils/src/macros.jl @@ -97,7 +97,7 @@ function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[]) return quote local arg1 = $(esc(ex0.args[1])) local args, kwargs = $separate_kwargs($(map(esc, ex0.args[2:end])...)) - $(fcn)(Core.kwfunc(arg1), + $(fcn)(Core.kwcall, Tuple{typeof(kwargs), Core.Typeof(arg1), map(Core.Typeof, args)...}; $(kws...)) end diff --git a/stdlib/Serialization/src/Serialization.jl b/stdlib/Serialization/src/Serialization.jl index 98bf7d447b3ec..ebf3bb1319b31 100644 --- a/stdlib/Serialization/src/Serialization.jl +++ b/stdlib/Serialization/src/Serialization.jl @@ -520,10 +520,11 @@ function serialize_typename(s::AbstractSerializer, t::Core.TypeName) serialize(s, t.mt.name) serialize(s, collect(Base.MethodList(t.mt))) serialize(s, t.mt.max_args) - if isdefined(t.mt, :kwsorter) - serialize(s, t.mt.kwsorter) - else + kws = collect(methods(Core.kwcall, (Any, t.wrapper, Vararg))) + if isempty(kws) writetag(s.io, UNDEFREF_TAG) + else + serialize(s, kws) end else writetag(s.io, UNDEFREF_TAG) @@ -1355,7 +1356,15 @@ function deserialize_typename(s::AbstractSerializer, number) if tag != UNDEFREF_TAG kws = handle_deserialize(s, tag) if makenew - tn.mt.kwsorter = kws + if kws isa Vector{Method} + for def in kws + kwmt = typeof(Core.kwcall).name.mt + ccall(:jl_method_table_insert, Cvoid, (Any, Any, Ptr{Cvoid}), mt, def, C_NULL) + end + else + # old object format -- try to forward from old to new + @eval Core.kwcall(kwargs, f::$ty, args...) = $kws(kwargs, f, args...) + end end end elseif makenew diff --git a/stdlib/Serialization/test/runtests.jl b/stdlib/Serialization/test/runtests.jl index 0d438040a4cd0..46749d4375538 100644 --- a/stdlib/Serialization/test/runtests.jl +++ b/stdlib/Serialization/test/runtests.jl @@ -317,18 +317,23 @@ main_ex = quote using Serialization $create_serialization_stream() do s local g() = :magic_token_anon_fun_test + local gkw(; kw=:thekw) = kw serialize(s, g) serialize(s, g) + serialize(s, gkw) seekstart(s) ds = Serializer(s) local g2 = deserialize(ds) - Base.invokelatest() do - $Test.@test g2 !== g - $Test.@test g2() === :magic_token_anon_fun_test - $Test.@test g2() === :magic_token_anon_fun_test - $Test.@test deserialize(ds) === g2 - end + @test g2 !== g + $Test.@test Base.invokelatest(g2) === :magic_token_anon_fun_test + $Test.@test Base.invokelatest(g2) === :magic_token_anon_fun_test + deserialize(ds) === g2 + + local gkw2 = deserialize(s) + $Test.@test gkw2 !== gkw + $Test.@test Base.invokelatest(gkw2) === :thekw + $Test.@test Base.invokelatest(gkw2, kw="kwtest") === "kwtest" # issue #21793 y = x -> (() -> x) @@ -336,10 +341,10 @@ main_ex = quote serialize(s, y) seekstart(s) y2 = deserialize(s) - Base.invokelatest() do + $Test.@test Base.invokelatest() do x2 = y2(2) - $Test.@test x2() == 2 - end + x2() + end === 2 end end # This needs to be run on `Main` since the serializer treats it differently. diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index e174fc1470eb8..2585ca59aec36 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -3933,10 +3933,6 @@ end +(UnhandledVarargCond(false), xs...) end |> only === Int - @test (Base.return_types((Vector{Any},)) do xs - Core.kwfunc(xs...) - end; true) - @test Base.return_types((Vector{Vector{Int}},)) do xs Tuple(xs...) end |> only === Tuple{Vararg{Int}} diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index f582277ec06f7..f1dbad5ef4d7b 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -120,9 +120,7 @@ f29083(;μ,σ) = μ + σ*randn() g29083() = f29083(μ=2.0,σ=0.1) let c = code_typed(g29083, ())[1][1].code # make sure no call to kwfunc remains - @test !any(e->(isa(e,Expr) && ((e.head === :invoke && e.args[1].def.name === :kwfunc) || - (e.head === :foreigncall && e.args[1] === QuoteNode(:jl_get_keyword_sorter)))), - c) + @test !any(e->(isa(e,Expr) && (e.head === :invoke && e.args[1].def.name === :kwfunc)), c) end @testset "issue #19122: [no]inline of short func. def. with return type annotation" begin @@ -1597,44 +1595,44 @@ end # @inline, @noinline, @constprop let @inline f(::Any; x::Int=1) = 2x @test is_inlineable(only(methods(f)).source) - @test is_inlineable(only(methods(Core.kwfunc(f))).source) + @test is_inlineable(only(methods(Core.kwcall, (Any, typeof(f), Vararg))).source) end let @noinline f(::Any; x::Int=1) = 2x @test !is_inlineable(only(methods(f)).source) - @test !is_inlineable(only(methods(Core.kwfunc(f))).source) + @test !is_inlineable(only(methods(Core.kwcall, (Any, typeof(f), Vararg))).source) end let Base.@constprop :aggressive f(::Any; x::Int=1) = 2x @test Core.Compiler.is_aggressive_constprop(only(methods(f))) - @test Core.Compiler.is_aggressive_constprop(only(methods(Core.kwfunc(f)))) + @test Core.Compiler.is_aggressive_constprop(only(methods(Core.kwcall, (Any, typeof(f), Vararg)))) end let Base.@constprop :none f(::Any; x::Int=1) = 2x @test Core.Compiler.is_no_constprop(only(methods(f))) - @test Core.Compiler.is_no_constprop(only(methods(Core.kwfunc(f)))) + @test Core.Compiler.is_no_constprop(only(methods(Core.kwcall, (Any, typeof(f), Vararg)))) end # @nospecialize let f(@nospecialize(A::Any); x::Int=1) = 2x @test only(methods(f)).nospecialize == 1 - @test only(methods(Core.kwfunc(f))).nospecialize == 4 + @test only(methods(Core.kwcall, (Any, typeof(f), Vararg))).nospecialize == 4 end let f(::Any; x::Int=1) = (@nospecialize; 2x) @test only(methods(f)).nospecialize == -1 - @test only(methods(Core.kwfunc(f))).nospecialize == -1 + @test only(methods(Core.kwcall, (Any, typeof(f), Vararg))).nospecialize == -1 end # Base.@assume_effects let Base.@assume_effects :notaskstate f(::Any; x::Int=1) = 2x @test Core.Compiler.decode_effects_override(only(methods(f)).purity).notaskstate - @test Core.Compiler.decode_effects_override(only(methods(Core.kwfunc(f))).purity).notaskstate + @test Core.Compiler.decode_effects_override(only(methods(Core.kwcall, (Any, typeof(f), Vararg))).purity).notaskstate end # propagate multiple metadata also let @inline Base.@assume_effects :notaskstate Base.@constprop :aggressive f(::Any; x::Int=1) = (@nospecialize; 2x) @test is_inlineable(only(methods(f)).source) @test Core.Compiler.is_aggressive_constprop(only(methods(f))) - @test is_inlineable(only(methods(Core.kwfunc(f))).source) - @test Core.Compiler.is_aggressive_constprop(only(methods(Core.kwfunc(f)))) + @test is_inlineable(only(methods(Core.kwcall, (Any, typeof(f), Vararg))).source) + @test Core.Compiler.is_aggressive_constprop(only(methods(Core.kwcall, (Any, typeof(f), Vararg)))) @test only(methods(f)).nospecialize == -1 - @test only(methods(Core.kwfunc(f))).nospecialize == -1 + @test only(methods(Core.kwcall, (Any, typeof(f), Vararg))).nospecialize == -1 @test Core.Compiler.decode_effects_override(only(methods(f)).purity).notaskstate - @test Core.Compiler.decode_effects_override(only(methods(Core.kwfunc(f))).purity).notaskstate + @test Core.Compiler.decode_effects_override(only(methods(Core.kwcall, (Any, typeof(f), Vararg))).purity).notaskstate end end diff --git a/test/keywordargs.jl b/test/keywordargs.jl index 0e651cf7f4531..366f14393a94f 100644 --- a/test/keywordargs.jl +++ b/test/keywordargs.jl @@ -297,7 +297,7 @@ end @test_throws UndefKeywordError f34516() @test_throws UndefKeywordError f34516(1) g34516(@nospecialize(x); k=0) = 0 - @test first(methods(Core.kwfunc(g34516))).nospecialize != 0 + @test only(methods(Core.kwcall, (Any, typeof(g34516), Vararg))).nospecialize != 0 end @testset "issue #21518" begin a = 0 diff --git a/test/precompile.jl b/test/precompile.jl index d84a8924e6318..098d1ffbba231 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -159,10 +159,9 @@ precompile_test_harness(false) do dir # issue 16529 (adding a method to a type with no instances) (::Task)(::UInt8, ::UInt16, ::UInt32) = 2 - # issue 16471 (capturing references to a kwfunc) - Test.@test !isdefined(typeof(sin).name.mt, :kwsorter) + # issue 16471 Base.sin(::UInt8, ::UInt16, ::UInt32; x = 52) = x - const sinkw = Core.kwfunc(Base.sin) + const sinkw = Core.kwcall # issue 16908 (some complicated types and external method definitions) abstract type CategoricalPool{T, R <: Integer, V} end @@ -253,9 +252,6 @@ precompile_test_harness(false) do dir Base.@ccallable Cint f35014(x::Cint) = x+Cint(1) end """) - # make sure `sin` didn't have a kwfunc (which would invalidate the attempted test) - @test !isdefined(typeof(sin).name.mt, :kwsorter) - # Issue #12623 @test __precompile__(false) === nothing @@ -387,7 +383,7 @@ precompile_test_harness(false) do dir @test current_task()(0x01, 0x4000, 0x30031234) == 2 @test sin(0x01, 0x4000, 0x30031234) == 52 @test sin(0x01, 0x4000, 0x30031234; x = 9142) == 9142 - @test Foo.sinkw === Core.kwfunc(Base.sin) + @test Foo.sinkw === Core.kwcall @test Foo.NominalValue() == 1 @test Foo.OrdinalValue() == 1 diff --git a/test/reduce.jl b/test/reduce.jl index c03013f880013..84d93b12913e4 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -160,12 +160,14 @@ plus(x,y) = x + y sum3(A) = reduce(plus, A) sum4(itr) = invoke(reduce, Tuple{Function, Any}, plus, itr) sum5(A) = reduce(plus, A; init=0) -sum6(itr) = invoke(Core.kwfunc(reduce), Tuple{NamedTuple{(:init,), Tuple{Int}}, typeof(reduce), Function, Any}, (init=0,), reduce, plus, itr) +sum6(itr) = invoke(Core.kwcall, Tuple{NamedTuple{(:init,), Tuple{Int}}, typeof(reduce), Function, Any}, (init=0,), reduce, plus, itr) +sum61(itr) = invoke(reduce, Tuple{Function, Any}, init=0, plus, itr) sum7(A) = mapreduce(x->x, plus, A) sum8(itr) = invoke(mapreduce, Tuple{Function, Function, Any}, x->x, plus, itr) sum9(A) = mapreduce(x->x, plus, A; init=0) -sum10(itr) = invoke(Core.kwfunc(mapreduce), Tuple{NamedTuple{(:init,),Tuple{Int}}, typeof(mapreduce), Function, Function, Any}, (init=0,), mapreduce, x->x, plus, itr) -for f in (sum2, sum5, sum6, sum9, sum10) +sum10(itr) = invoke(Core.kwcall, Tuple{NamedTuple{(:init,),Tuple{Int}}, typeof(mapreduce), Function, Function, Any}, (init=0,), mapreduce, x->x, plus, itr) +sum11(itr) = invoke(mapreduce, Tuple{Function, Function, Any}, init=0, x->x, plus, itr) +for f in (sum2, sum5, sum6, sum61, sum9, sum10, sum11) @test sum(z) == f(z) @test sum(Int[]) == f(Int[]) == 0 @test sum(Int[7]) == f(Int[7]) == 7 diff --git a/test/worlds.jl b/test/worlds.jl index 3c60f006faef2..39a9dc4d9a788 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -355,7 +355,7 @@ inner(s::Union{Vector,Dict}; kw=false) = inneri(s, kwi=maximum(s), kwb=kw) inneri(s, args...; kwargs...) = inneri(IOBuffer(), s, args...; kwargs...) inneri(io::IO, s::Union{Vector,Dict}; kwi=0, kwb=false) = (print(io, first(s), " "^kwi, kwb); String(take!(io))) @test outer(Ref{Any}([1,2,3])) == "1 false" -mi = method_instance(Core.kwfunc(inneri), (NamedTuple{(:kwi,:kwb),TT} where TT<:Tuple{Any,Bool}, typeof(inneri), Vector{T} where T)) +mi = method_instance(Core.kwcall, (NamedTuple{(:kwi,:kwb),TT} where TT<:Tuple{Any,Bool}, typeof(inneri), Vector{T} where T)) w = worlds(mi) abstract type Container{T} end Base.eltype(::Type{C}) where {T,C<:Container{T}} = T