From b8d0111bdd97ce0e01e50ea00975ae100b082dd2 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Mon, 9 Sep 2024 01:17:29 -0400 Subject: [PATCH] builtins: add `Core.throw_methoderror` (#55705) This allows us to simulate/mark calls that are known-to-fail. Required for https://github.com/JuliaLang/julia/pull/54972/ --- base/compiler/abstractinterpretation.jl | 13 +++++++++++++ .../compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl | 1 + base/compiler/tfuncs.jl | 4 ++++ src/builtin_proto.h | 1 + src/builtins.c | 9 +++++++++ src/gf.c | 4 ++-- src/julia_internal.h | 2 +- src/staticdata.c | 2 +- 8 files changed, 32 insertions(+), 4 deletions(-) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 8623a32ddbb2be..83a39ce10d891a 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2226,6 +2226,17 @@ function abstract_throw(interp::AbstractInterpreter, argtypes::Vector{Any}, ::Ab return CallMeta(Union{}, exct, EFFECTS_THROWS, NoCallInfo()) end +function abstract_throw_methoderror(interp::AbstractInterpreter, argtypes::Vector{Any}, ::AbsIntState) + exct = if length(argtypes) == 1 + ArgumentError + elseif !isvarargtype(argtypes[2]) + MethodError + else + tmerge(๐•ƒแตข, MethodError, ArgumentError) + end + return CallMeta(Union{}, exct, EFFECTS_THROWS, NoCallInfo()) +end + # call where the function is known exactly function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), arginfo::ArgInfo, si::StmtInfo, sv::AbsIntState, @@ -2246,6 +2257,8 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), return abstract_applicable(interp, argtypes, sv, max_methods) elseif f === throw return abstract_throw(interp, argtypes, sv) + elseif f === Core.throw_methoderror + return abstract_throw_methoderror(interp, argtypes, sv) end rt = abstract_call_builtin(interp, f, arginfo, sv) ft = popfirst!(argtypes) diff --git a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl index f74cb90e6ab516..6967efe495be10 100644 --- a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl +++ b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl @@ -1213,6 +1213,7 @@ escape_builtin!(::typeof(Core.donotdelete), _...) = false # not really safe, but `ThrownEscape` will be imposed later escape_builtin!(::typeof(isdefined), _...) = false escape_builtin!(::typeof(throw), _...) = false +escape_builtin!(::typeof(Core.throw_methoderror), _...) = false function escape_builtin!(::typeof(ifelse), astate::AnalysisState, pc::Int, args::Vector{Any}) length(args) == 4 || return false diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 0c57c04a6ddeae..64a93bd07c2fa5 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -89,6 +89,7 @@ function add_tfunc(@nospecialize(f::Builtin), minarg::Int, maxarg::Int, @nospeci end add_tfunc(throw, 1, 1, @nospecs((๐•ƒ::AbstractLattice, x)->Bottom), 0) +add_tfunc(Core.throw_methoderror, 1, INT_INF, @nospecs((๐•ƒ::AbstractLattice, x)->Bottom), 0) # the inverse of typeof_tfunc # returns (type, isexact, isconcrete, istype) @@ -2313,6 +2314,7 @@ const _CONSISTENT_BUILTINS = Any[ (<:), typeassert, throw, + Core.throw_methoderror, setfield!, donotdelete ] @@ -2335,6 +2337,7 @@ const _EFFECT_FREE_BUILTINS = [ (<:), typeassert, throw, + Core.throw_methoderror, getglobal, compilerbarrier, ] @@ -2350,6 +2353,7 @@ const _INACCESSIBLEMEM_BUILTINS = Any[ isa, nfields, throw, + Core.throw_methoderror, tuple, typeassert, typeof, diff --git a/src/builtin_proto.h b/src/builtin_proto.h index 8b97c46df72daf..7fbd5557586757 100644 --- a/src/builtin_proto.h +++ b/src/builtin_proto.h @@ -69,6 +69,7 @@ DECLARE_BUILTIN(svec); DECLARE_BUILTIN(swapfield); DECLARE_BUILTIN(swapglobal); DECLARE_BUILTIN(throw); +DECLARE_BUILTIN(throw_methoderror); DECLARE_BUILTIN(tuple); DECLARE_BUILTIN(typeassert); DECLARE_BUILTIN(typeof); diff --git a/src/builtins.c b/src/builtins.c index 152836bcab6a91..75c4d02c898b26 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -580,6 +580,14 @@ JL_CALLABLE(jl_f_throw) return jl_nothing; } +JL_CALLABLE(jl_f_throw_methoderror) +{ + JL_NARGSV(throw_methoderror, 1); + size_t world = jl_get_tls_world_age(); + jl_method_error(args[0], &args[1], nargs, world); + return jl_nothing; +} + JL_CALLABLE(jl_f_ifelse) { JL_NARGS(ifelse, 3, 3); @@ -2437,6 +2445,7 @@ void jl_init_primitives(void) JL_GC_DISABLED add_builtin_func("_compute_sparams", jl_f__compute_sparams); add_builtin_func("_svec_ref", jl_f__svec_ref); add_builtin_func("current_scope", jl_f_current_scope); + add_builtin_func("throw_methoderror", jl_f_throw_methoderror); // builtin types add_builtin("Any", (jl_value_t*)jl_any_type); diff --git a/src/gf.c b/src/gf.c index 95bab0d0f832ec..970cb62b8a862c 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2335,7 +2335,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method JL_GC_POP(); } -static void JL_NORETURN jl_method_error_bare(jl_function_t *f, jl_value_t *args, size_t world) +static void JL_NORETURN jl_method_error_bare(jl_value_t *f, jl_value_t *args, size_t world) { if (jl_methoderror_type) { jl_value_t *e = jl_new_struct_uninit(jl_methoderror_type); @@ -2360,7 +2360,7 @@ static void JL_NORETURN jl_method_error_bare(jl_function_t *f, jl_value_t *args, // not reached } -void JL_NORETURN jl_method_error(jl_function_t *f, jl_value_t **args, size_t na, size_t world) +void JL_NORETURN jl_method_error(jl_value_t *f, jl_value_t **args, size_t na, size_t world) { jl_value_t *argtup = jl_f_tuple(NULL, args, na - 1); JL_GC_PUSH1(&argtup); diff --git a/src/julia_internal.h b/src/julia_internal.h index ac955e2bcb3a60..f00667d016796d 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -705,7 +705,7 @@ 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); +void JL_NORETURN jl_method_error(jl_value_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 void jl_typeassert(jl_value_t *x, jl_value_t *t); diff --git a/src/staticdata.c b/src/staticdata.c index 6dfe5e91a9c557..b991dfe8f37f38 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -497,7 +497,7 @@ static htable_t relocatable_ext_cis; // (reverse of fptr_to_id) // This is a manually constructed dual of the fvars array, which would be produced by codegen for Julia code, for C. 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_throw, &jl_f_throw_methoderror, &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,