From 8937f7e522c9b3f96920d2f196f452c9f8a9e248 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 8 Jan 2021 23:17:39 -0500 Subject: [PATCH] gf: avoid adding cache entries wider than the original method (#39140) Sometimes we want to widen the compilation signature, but then end up with something which does not fit the original pattern. This then can cause problems later, when we try to use the Method (from the cache), but discover it does not actually match the call. Fixes #38999 --- src/gf.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/gf.c b/src/gf.c index 52ed2d75250e7..09ce60df23f13 100644 --- a/src/gf.c +++ b/src/gf.c @@ -650,7 +650,7 @@ static void jl_compilation_sig( else if (jl_is_type_type(elt)) { // elt isa Type{T} if (very_general_type(decl_i)) { /* - here's a fairly simple heuristic: if this argument slot's + Here's a fairly simple heuristic: if this argument slot's declared type is general (Type or Any), then don't specialize for every Type that got passed. @@ -665,8 +665,9 @@ static void jl_compilation_sig( x::TypeConstructor matches the first but not the second, while also matching all other TypeConstructors. This means neither Type{TC} nor TypeConstructor is more specific. + + But don't apply this heuristic if the argument is called (issue #36783). */ - // don't apply this heuristic if the argument is called (issue #36783) int iscalled = i_arg > 0 && i_arg <= 8 && (definition->called & (1 << (i_arg - 1))); if (!iscalled) { if (!*newparams) *newparams = jl_svec_copy(tt->parameters); @@ -676,13 +677,13 @@ static void jl_compilation_sig( else if (jl_is_type_type(jl_tparam0(elt)) && // try to give up on specializing type parameters for Type{Type{Type{...}}} (jl_is_type_type(jl_tparam0(jl_tparam0(elt))) || !jl_has_free_typevars(decl_i))) { - // TODO: this is probably solidly unsound and would corrupt the cache in many cases /* actual argument was Type{...}, we computed its type as - Type{Type{...}}. we must avoid unbounded nesting here, so - cache the signature as Type{T}, unless something more - specific like Type{Type{Int32}} was actually declared. - this can be determined using a type intersection. + Type{Type{...}}. we like to avoid unbounded nesting here, so + compile (and hopefully cache) the signature as Type{T}, + unless something more specific like Type{Type{Int32}} was + actually declared. this can be determined using a type + intersection. */ if (!*newparams) *newparams = jl_svec_copy(tt->parameters); if (i < nargs || !definition->isva) { @@ -1024,12 +1025,12 @@ static jl_method_instance_t *cache_method( intptr_t nspec = (mt == NULL || mt == jl_type_type_mt || mt == jl_nonfunction_mt ? definition->nargs + 1 : mt->max_args + 2); jl_compilation_sig(tt, sparams, definition, nspec, &newparams); if (newparams) { - cache_with_orig = 0; compilationsig = jl_apply_tuple_type(newparams); temp2 = (jl_value_t*)compilationsig; // In most cases `!jl_isa_compileable_sig(tt, definition))`, // although for some cases, (notably Varargs) // we might choose a replacement type that's preferable but not strictly better + cache_with_orig = !jl_subtype(compilationsig, definition->sig); } // TODO: maybe assert(jl_isa_compileable_sig(compilationsig, definition)); newmeth = jl_specializations_get_linfo(definition, (jl_value_t*)compilationsig, sparams); @@ -1038,7 +1039,6 @@ static jl_method_instance_t *cache_method( jl_svec_t* guardsigs = jl_emptysvec; if (!cache_with_orig && mt) { // now examine what will happen if we chose to use this sig in the cache - // TODO: should we first check `compilationsig <: definition`? size_t min_valid2 = 1; size_t max_valid2 = ~(size_t)0; temp = ml_matches(mt, 0, compilationsig, MAX_UNSPECIALIZED_CONFLICTS, 1, 1, world, 0, &min_valid2, &max_valid2, NULL);