Skip to content

Commit

Permalink
gf: avoid adding cache entries wider than the original method (JuliaL…
Browse files Browse the repository at this point in the history
…ang#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 JuliaLang#38999
  • Loading branch information
vtjnash authored Jan 9, 2021
1 parent ca07546 commit 8937f7e
Showing 1 changed file with 9 additions and 9 deletions.
18 changes: 9 additions & 9 deletions src/gf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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);
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down

0 comments on commit 8937f7e

Please sign in to comment.