Skip to content

Commit b1fba9e

Browse files
committed
avoid invalidations when it doesn't resolve an MethodError
In these cases, the new method would not be called because the call would still be an ambiguity error instead. We also might observe that the method doesn't resolve an old MethodError because it was previously covered by a different method.
1 parent 34b2ae5 commit b1fba9e

File tree

3 files changed

+120
-85
lines changed

3 files changed

+120
-85
lines changed

src/gc.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2641,6 +2641,7 @@ mark: {
26412641

26422642
extern jl_typemap_entry_t *call_cache[N_CALL_CACHE];
26432643
extern jl_array_t *jl_all_methods;
2644+
extern jl_array_t *_jl_debug_method_invalidation;
26442645

26452646
static void jl_gc_queue_thread_local(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp,
26462647
jl_ptls_t ptls2)
@@ -2680,6 +2681,8 @@ static void mark_roots(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp)
26802681
gc_mark_queue_obj(gc_cache, sp, call_cache[i]);
26812682
if (jl_all_methods != NULL)
26822683
gc_mark_queue_obj(gc_cache, sp, jl_all_methods);
2684+
if (_jl_debug_method_invalidation != NULL)
2685+
gc_mark_queue_obj(gc_cache, sp, _jl_debug_method_invalidation);
26832686

26842687
// constants
26852688
gc_mark_queue_obj(gc_cache, sp, jl_typetype_type);

src/gf.c

Lines changed: 113 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,68 +1197,29 @@ static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_datatype
11971197
return nf;
11981198
}
11991199

1200-
struct shadowed_matches_env {
1200+
1201+
struct matches_env {
12011202
struct typemap_intersection_env match;
12021203
jl_typemap_entry_t *newentry;
12031204
jl_value_t *shadowed;
12041205
};
1205-
static int check_shadowed_visitor(jl_typemap_entry_t *oldentry, struct typemap_intersection_env *closure0)
1206+
static int get_intersect_visitor(jl_typemap_entry_t *oldentry, struct typemap_intersection_env *closure0)
12061207
{
1207-
struct shadowed_matches_env *closure = container_of(closure0, struct shadowed_matches_env, match);
1208+
struct matches_env *closure = container_of(closure0, struct matches_env, match);
12081209
if (oldentry == closure->newentry)
12091210
return 1;
12101211
if (oldentry->max_world < ~(size_t)0 || oldentry->min_world == closure->newentry->min_world)
12111212
// skip if no world has both active
12121213
// also be careful not to try to scan something from the current dump-reload though
12131214
return 1;
12141215
jl_method_t *oldmethod = oldentry->func.method;
1215-
if (oldmethod->specializations == jl_emptysvec)
1216-
// nothing inferred ever before means nothing shadowed ever before
1217-
return 1;
1218-
1219-
jl_tupletype_t *type = closure->newentry->sig;
1220-
jl_tupletype_t *sig = oldentry->sig;
1221-
1222-
int shadowed = 0;
1223-
if (closure->match.issubty) { // (new)type <: (old)sig
1224-
// new entry is more specific
1225-
shadowed = 1;
1226-
}
1227-
else if (jl_subtype((jl_value_t*)sig, (jl_value_t*)type)) {
1228-
// old entry is more specific
1229-
}
1230-
else if (jl_type_morespecific_no_subtype((jl_value_t*)type, (jl_value_t*)sig)) {
1231-
// new entry is more specific
1232-
shadowed = 1;
1233-
}
1234-
else if (jl_type_morespecific_no_subtype((jl_value_t*)sig, (jl_value_t*)type)) {
1235-
// old entry is more specific
1236-
}
1237-
else {
1238-
// sort order is ambiguous
1239-
shadowed = 1;
1240-
}
1241-
1242-
// ok: record that this method definition is being partially replaced
1243-
// (either with a real definition, or an ambiguity error)
1244-
if (shadowed) {
1245-
if (closure->shadowed == NULL) {
1246-
closure->shadowed = (jl_value_t*)oldmethod;
1247-
}
1248-
else if (!jl_is_array(closure->shadowed)) {
1249-
jl_array_t *list = jl_alloc_vec_any(2);
1250-
jl_array_ptr_set(list, 0, closure->shadowed);
1251-
jl_array_ptr_set(list, 1, (jl_value_t*)oldmethod);
1252-
closure->shadowed = (jl_value_t*)list;
1253-
}
1254-
else {
1255-
jl_array_ptr_1d_push((jl_array_t*)closure->shadowed, (jl_value_t*)oldmethod);
1256-
}
1257-
}
1216+
if (closure->shadowed == NULL)
1217+
closure->shadowed = (jl_value_t*)jl_alloc_vec_any(0);
1218+
jl_array_ptr_1d_push((jl_array_t*)closure->shadowed, (jl_value_t*)oldmethod);
12581219
return 1;
12591220
}
12601221

1261-
static jl_value_t *check_shadowed_matches(jl_typemap_t *defs, jl_typemap_entry_t *newentry)
1222+
static jl_value_t *get_intersect_matches(jl_typemap_t *defs, jl_typemap_entry_t *newentry)
12621223
{
12631224
jl_tupletype_t *type = newentry->sig;
12641225
jl_tupletype_t *ttypes = (jl_tupletype_t*)jl_unwrap_unionall((jl_value_t*)type);
@@ -1271,7 +1232,7 @@ static jl_value_t *check_shadowed_matches(jl_typemap_t *defs, jl_typemap_entry_t
12711232
else
12721233
va = NULL;
12731234
}
1274-
struct shadowed_matches_env env = {{check_shadowed_visitor, (jl_value_t*)type, va}};
1235+
struct matches_env env = {{get_intersect_visitor, (jl_value_t*)type, va}};
12751236
env.match.ti = NULL;
12761237
env.match.env = jl_emptysvec;
12771238
env.newentry = newentry;
@@ -1608,8 +1569,9 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
16081569
size_t max_world = method->primary_world - 1;
16091570
int invalidated = 0;
16101571
jl_value_t *loctag = NULL; // debug info for invalidation
1572+
jl_value_t *isect = NULL;
16111573
jl_typemap_entry_t *newentry = NULL;
1612-
JL_GC_PUSH3(&oldvalue, &newentry, &loctag);
1574+
JL_GC_PUSH4(&oldvalue, &newentry, &loctag, &isect);
16131575
JL_LOCK(&mt->writelock);
16141576
// first delete the existing entry (we'll disable it later)
16151577
struct jl_typemap_assoc search = {(jl_value_t*)type, method->primary_world, NULL, 0, ~(size_t)0};
@@ -1622,19 +1584,57 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
16221584
newentry = jl_typemap_alloc((jl_tupletype_t*)type, simpletype, jl_emptysvec,
16231585
(jl_value_t*)method, method->primary_world, method->deleted_world);
16241586
jl_typemap_insert(&mt->defs, (jl_value_t*)mt, newentry, 0, &method_defs);
1587+
int any_to_drop = 0;
16251588
if (oldentry) {
1589+
jl_method_t *m = oldentry->func.method;
1590+
method_overwrite(newentry, m);
1591+
jl_svec_t *specializations = jl_atomic_load_acquire(&m->specializations);
1592+
jl_method_instance_t **data = (jl_method_instance_t**)jl_svec_data(specializations);
1593+
size_t i, l = jl_svec_len(specializations);
1594+
for (i = 0; i < l; i++) {
1595+
jl_method_instance_t *mi = jl_atomic_load_relaxed(&data[i]);
1596+
if (mi == NULL)
1597+
continue;
1598+
if (mi->backedges)
1599+
invalidate_backedges(mi, max_world, "jl_method_table_insert");
1600+
}
1601+
any_to_drop = l > 0;
16261602
oldvalue = oldentry->func.value;
1627-
method_overwrite(newentry, (jl_method_t*)oldvalue);
16281603
}
16291604
else {
1605+
oldvalue = get_intersect_matches(mt->defs, newentry);
1606+
1607+
jl_method_t **d;
1608+
size_t j, n;
1609+
if (oldvalue == NULL) {
1610+
d = NULL;
1611+
n = 0;
1612+
}
1613+
else {
1614+
assert(jl_is_array(oldvalue));
1615+
d = (jl_method_t**)jl_array_ptr_data(oldvalue);
1616+
n = jl_array_len(oldvalue);
1617+
}
16301618
if (mt->backedges) {
16311619
jl_value_t **backedges = jl_array_ptr_data(mt->backedges);
16321620
size_t i, na = jl_array_len(mt->backedges);
16331621
size_t ins = 0;
16341622
for (i = 1; i < na; i += 2) {
16351623
jl_value_t *backedgetyp = backedges[i - 1];
1636-
if (!jl_has_empty_intersection(backedgetyp, (jl_value_t*)type)) {
1637-
// TODO: only delete if the ml_matches list (with intersection=0, include_ambiguous=1) is empty
1624+
isect = jl_type_intersection(backedgetyp, (jl_value_t*)type);
1625+
if (isect != jl_bottom_type) {
1626+
// see if the intersection was actually already fully
1627+
// covered by anything (method or ambiguity is okay)
1628+
size_t j;
1629+
for (j = 0; j < n; j++) {
1630+
jl_method_t *m = d[j];
1631+
if (jl_subtype(isect, m->sig))
1632+
break;
1633+
}
1634+
if (j != n)
1635+
isect = jl_bottom_type;
1636+
}
1637+
if (isect != jl_bottom_type) {
16381638
jl_method_instance_t *backedge = (jl_method_instance_t*)backedges[i];
16391639
invalidate_method_instance(backedge, max_world, 0);
16401640
invalidated = 1;
@@ -1651,10 +1651,67 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
16511651
else
16521652
jl_array_del_end(mt->backedges, na - ins);
16531653
}
1654-
oldvalue = check_shadowed_matches(mt->defs, newentry);
1654+
if (oldvalue) {
1655+
char *morespec = (char*)alloca(n);
1656+
memset(morespec, 0, n);
1657+
for (j = 0; j < n; j++) {
1658+
jl_method_t *m = d[j];
1659+
if (morespec[j])
1660+
continue;
1661+
jl_svec_t *specializations = jl_atomic_load_acquire(&m->specializations);
1662+
jl_method_instance_t **data = (jl_method_instance_t**)jl_svec_data(specializations);
1663+
size_t i, l = jl_svec_len(specializations);
1664+
int shadowing = 0;
1665+
for (i = 0; i < l; i++) {
1666+
jl_method_instance_t *mi = jl_atomic_load_relaxed(&data[i]);
1667+
if (mi == NULL)
1668+
continue;
1669+
isect = jl_type_intersection(type, (jl_value_t*)mi->specTypes);
1670+
if (isect != jl_bottom_type) {
1671+
if (shadowing == 0) {
1672+
if (jl_type_morespecific(m->sig, type))
1673+
// not actually shadowing--the existing method is still better
1674+
break;
1675+
if (!jl_type_morespecific(type, mi->def.method->sig)) {
1676+
// adding an ambiguity--see if there already was one
1677+
size_t k;
1678+
for (k = 0; k < n; k++) {
1679+
jl_method_t *m2 = d[k];
1680+
if (m == m2 || !jl_subtype(isect, m2->sig))
1681+
continue;
1682+
if (k > i) {
1683+
if (jl_type_morespecific(m2->sig, type)) {
1684+
// not actually shadowing this--m2 will still be better
1685+
morespec[k] = 1;
1686+
continue;
1687+
}
1688+
}
1689+
if (!jl_type_morespecific(m->sig, m2->sig) &&
1690+
!jl_type_morespecific(m2->sig, m->sig)) {
1691+
break;
1692+
}
1693+
}
1694+
if (k != n)
1695+
continue;
1696+
}
1697+
shadowing = 1;
1698+
}
1699+
if (mi->backedges)
1700+
invalidate_backedges(mi, max_world, "jl_method_table_insert");
1701+
}
1702+
}
1703+
if (shadowing == 0)
1704+
morespec[j] = 1; // the method won't need to be dropped from any cache
1705+
}
1706+
for (j = 0; j < n; j++) {
1707+
if (morespec[j])
1708+
d[j] = NULL;
1709+
else
1710+
any_to_drop = 1;
1711+
}
1712+
}
16551713
}
1656-
1657-
if (oldvalue) {
1714+
if (any_to_drop) {
16581715
// drop anything in mt->cache that might overlap with the new method
16591716
struct invalidate_mt_env mt_cache_env;
16601717
mt_cache_env.max_world = max_world;
@@ -1674,31 +1731,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
16741731
}
16751732
}
16761733
}
1677-
1678-
jl_value_t **d;
1679-
size_t j, n;
1680-
if (jl_is_method(oldvalue)) {
1681-
d = &oldvalue;
1682-
n = 1;
1683-
}
1684-
else {
1685-
assert(jl_is_array(oldvalue));
1686-
d = jl_array_ptr_data(oldvalue);
1687-
n = jl_array_len(oldvalue);
1688-
}
1689-
for (j = 0; j < n; j++) {
1690-
jl_value_t *m = d[j];
1691-
jl_svec_t *specializations = jl_atomic_load_acquire(&((jl_method_t*)m)->specializations);
1692-
jl_method_instance_t **data = (jl_method_instance_t**)jl_svec_data(specializations);
1693-
size_t i, l = jl_svec_len(specializations);
1694-
for (i = 0; i < l; i++) {
1695-
jl_method_instance_t *mi = jl_atomic_load_relaxed(&data[i]);
1696-
if (mi != NULL && mi->backedges && !jl_has_empty_intersection(type, (jl_value_t*)mi->specTypes)) {
1697-
invalidate_backedges(mi, max_world, "jl_method_table_insert");
1698-
invalidated = 1;
1699-
}
1700-
}
1701-
}
1734+
invalidated = 1;
17021735
}
17031736
if (invalidated && _jl_debug_method_invalidation) {
17041737
jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)method);

test/worlds.jl

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -315,15 +315,14 @@ struct Normed35855 <: FixedPoint35855{UInt8}
315315
i::UInt8
316316
Normed35855(i::Integer, _) = new(i % UInt8)
317317
end
318-
(::Type{X})(x::Real) where X<:FixedPoint35855{T} where T = X(round(T, typemax(T)*x), 0)
319-
320-
@test_broken worlds(mi) == w
318+
(::Type{X})(x::Real) where {T, X<:FixedPoint35855{T}} = X(round(T, typemax(T)*x), 0)
319+
@test worlds(mi) == w
321320

322321
mi = instance(convert, (Type{Nothing}, String))
323322
w = worlds(mi)
324323
abstract type Colorant35855 end
325-
Base.convert(::Type{C}, c) where C<:Colorant35855 = false
326-
@test_broken worlds(mi) == w
324+
Base.convert(::Type{C}, c) where {C<:Colorant35855} = false
325+
@test worlds(mi) == w
327326

328327
# invoke_in_world
329328
f_inworld(x) = "world one; x=$x"

0 commit comments

Comments
 (0)