Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support method deletion #25050

Merged
merged 3 commits into from Dec 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions base/reflection.jl
Expand Up @@ -1083,6 +1083,21 @@ function isambiguous(m1::Method, m2::Method; ambiguous_bottom::Bool=false)
return true
end

"""
delete_method(m::Method)

Make method `m` uncallable and force recompilation of any methods that use(d) it.
"""
function delete_method(m::Method)
ccall(:jl_method_table_disable, Void, (Any, Any), MethodTable(m), m)
end

function MethodTable(m::Method)
ft = ccall(:jl_first_argument_datatype, Any, (Any,), m.sig)
ft == C_NULL && error("Method ", m, " does not correspond to a function type")
(ft::DataType).name.mt
end

"""
has_bottom_parameter(t) -> Bool

Expand Down
2 changes: 1 addition & 1 deletion src/codegen.cpp
Expand Up @@ -4523,7 +4523,7 @@ static Function *jl_cfunction_object(jl_function_t *ff, jl_value_t *declrt, jl_t
// check the cache
jl_typemap_entry_t *sf = NULL;
if (jl_cfunction_list.unknown != jl_nothing) {
sf = jl_typemap_assoc_by_type(jl_cfunction_list, (jl_tupletype_t*)cfunc_sig, NULL, /*subtype*/0, /*offs*/0, /*world*/1);
sf = jl_typemap_assoc_by_type(jl_cfunction_list, (jl_tupletype_t*)cfunc_sig, NULL, /*subtype*/0, /*offs*/0, /*world*/1, /*max_world_mask*/0);
if (sf) {
jl_value_t *v = sf->func.value;
if (v) {
Expand Down
35 changes: 27 additions & 8 deletions src/dump.c
Expand Up @@ -1876,7 +1876,10 @@ static void jl_insert_backedges(jl_array_t *list, arraylist_t *dependent_worlds)
if (jl_is_method_instance(callee)) {
sig = callee_mi->specTypes;
assert(!module_in_worklist(callee_mi->def.method->module));
assert(callee_mi->max_world == ~(size_t)0);
if (callee_mi->max_world != ~(size_t)0) {
valid = 0;
break;
}
}
else {
sig = callee;
Expand Down Expand Up @@ -2582,26 +2585,41 @@ static void jl_update_backref_list(jl_value_t *old, jl_value_t *_new, size_t sta

// repeatedly look up older methods until we come to one that existed
// at the time this module was serialized
static jl_method_t *jl_lookup_method_worldset(jl_methtable_t *mt, jl_datatype_t *sig, arraylist_t *dependent_worlds)
static jl_method_t *jl_lookup_method_worldset(jl_methtable_t *mt, jl_datatype_t *sig, arraylist_t *dependent_worlds, size_t *max_world)
{
size_t world = jl_world_counter;
jl_typemap_entry_t *entry;
jl_method_t *_new;
while (1) {
_new = (jl_method_t*)jl_methtable_lookup(mt, sig, world);
assert(_new && jl_is_method(_new));
entry = jl_typemap_assoc_by_type(
mt->defs, sig, NULL, /*subtype*/0, /*offs*/0, world, /*max_world_mask*/0);
if (!entry)
break;
_new = (jl_method_t*)entry->func.value;
world = lowerbound_dependent_world_set(_new->min_world, dependent_worlds);
if (world == _new->min_world)
if (world == _new->min_world) {
*max_world = entry->max_world;
return _new;
}
}
// If we failed to find a method (perhaps due to method deletion),
// grab anything
entry = jl_typemap_assoc_by_type(
mt->defs, sig, NULL, /*subtype*/0, /*offs*/0, /*world*/jl_world_counter, /*max_world_mask*/(~(size_t)0) >> 1);
assert(entry);
assert(entry->max_world != ~(size_t)0);
*max_world = entry->max_world;
return (jl_method_t*)entry->func.value;
}

static jl_method_t *jl_recache_method(jl_method_t *m, size_t start, arraylist_t *dependent_worlds)
{
jl_datatype_t *sig = (jl_datatype_t*)m->sig;
jl_datatype_t *ftype = jl_first_argument_datatype((jl_value_t*)sig);
jl_methtable_t *mt = ftype->name->mt;
size_t max_world = 0;
jl_set_typeof(m, (void*)(intptr_t)0x30); // invalidate the old value to help catch errors
jl_method_t *_new = jl_lookup_method_worldset(mt, sig, dependent_worlds);
jl_method_t *_new = jl_lookup_method_worldset(mt, sig, dependent_worlds, &max_world);
jl_update_backref_list((jl_value_t*)m, (jl_value_t*)_new, start);
return _new;
}
Expand All @@ -2612,8 +2630,8 @@ static jl_method_instance_t *jl_recache_method_instance(jl_method_instance_t *li
assert(jl_is_datatype(sig) || jl_is_unionall(sig));
jl_datatype_t *ftype = jl_first_argument_datatype((jl_value_t*)sig);
jl_methtable_t *mt = ftype->name->mt;
jl_method_t *m = jl_lookup_method_worldset(mt, sig, dependent_worlds);

size_t max_world = 0;
jl_method_t *m = jl_lookup_method_worldset(mt, sig, dependent_worlds, &max_world);
jl_datatype_t *argtypes = (jl_datatype_t*)li->specTypes;
jl_set_typeof(li, (void*)(intptr_t)0x40); // invalidate the old value to help catch errors
jl_svec_t *env = jl_emptysvec;
Expand All @@ -2622,6 +2640,7 @@ static jl_method_instance_t *jl_recache_method_instance(jl_method_instance_t *li
if (ti == jl_bottom_type)
env = jl_emptysvec; // the intersection may fail now if the type system had made an incorrect subtype env in the past
jl_method_instance_t *_new = jl_specializations_get_linfo(m, (jl_value_t*)argtypes, env, jl_world_counter);
_new->max_world = max_world;
jl_update_backref_list((jl_value_t*)li, (jl_value_t*)_new, start);
return _new;
}
Expand Down
96 changes: 83 additions & 13 deletions src/gf.c
Expand Up @@ -146,7 +146,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_specializations_get_linfo(jl_method_t *m,
assert(world >= m->min_world && "typemap lookup is corrupted");
JL_LOCK(&m->writelock);
jl_typemap_entry_t *sf =
jl_typemap_assoc_by_type(m->specializations, (jl_tupletype_t*)type, NULL, /*subtype*/0, /*offs*/0, world);
jl_typemap_assoc_by_type(m->specializations, (jl_tupletype_t*)type, NULL, /*subtype*/0, /*offs*/0, world, /*max_world_mask*/0);
if (sf && jl_is_method_instance(sf->func.value)) {
jl_method_instance_t *linfo = (jl_method_instance_t*)sf->func.value;
assert(linfo->min_world <= sf->min_world && linfo->max_world >= sf->max_world);
Expand Down Expand Up @@ -180,7 +180,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_specializations_get_linfo(jl_method_t *m,
JL_DLLEXPORT jl_value_t *jl_specializations_lookup(jl_method_t *m, jl_tupletype_t *type, size_t world)
{
jl_typemap_entry_t *sf = jl_typemap_assoc_by_type(
m->specializations, type, NULL, /*subtype*/0, /*offs*/0, world);
m->specializations, type, NULL, /*subtype*/0, /*offs*/0, world, /*max_world_mask*/0);
if (!sf)
return jl_nothing;
return sf->func.value;
Expand All @@ -189,7 +189,7 @@ JL_DLLEXPORT jl_value_t *jl_specializations_lookup(jl_method_t *m, jl_tupletype_
JL_DLLEXPORT jl_value_t *jl_methtable_lookup(jl_methtable_t *mt, jl_tupletype_t *type, size_t world)
{
jl_typemap_entry_t *sf = jl_typemap_assoc_by_type(
mt->defs, type, NULL, /*subtype*/0, /*offs*/0, world);
mt->defs, type, NULL, /*subtype*/0, /*offs*/0, world, /*max_world_mask*/0);
if (!sf)
return jl_nothing;
return sf->func.value;
Expand Down Expand Up @@ -1070,7 +1070,7 @@ static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_datatype
jl_method_instance_t *nf = NULL;
JL_GC_PUSH4(&env, &entry, &func, &sig);

entry = jl_typemap_assoc_by_type(mt->defs, tt, &env, /*subtype*/1, /*offs*/0, world);
entry = jl_typemap_assoc_by_type(mt->defs, tt, &env, /*subtype*/1, /*offs*/0, world, /*max_world_mask*/0);
if (entry != NULL) {
jl_method_t *m = entry->func.method;
if (!jl_has_call_ambiguities(tt, m)) {
Expand Down Expand Up @@ -1131,6 +1131,8 @@ static int check_ambiguous_visitor(jl_typemap_entry_t *oldentry, struct typemap_
closure->after = 1;
return 1;
}
if (oldentry->max_world < ~(size_t)0)
return 1;
union jl_typemap_t map = closure->defs;
jl_tupletype_t *type = (jl_tupletype_t*)closure->match.type;
jl_method_t *m = closure->newentry->func.method;
Expand Down Expand Up @@ -1166,7 +1168,7 @@ static int check_ambiguous_visitor(jl_typemap_entry_t *oldentry, struct typemap_
// that isect == type or isect == sig and return the original match)
jl_typemap_entry_t *l = jl_typemap_assoc_by_type(
map, (jl_tupletype_t*)isect, NULL, /*subtype*/0, /*offs*/0,
closure->newentry->min_world);
closure->newentry->min_world, /*max_world_mask*/0);
if (l != NULL) // ok, intersection is covered
return 1;
jl_method_t *mambig = oldentry->func.method;
Expand Down Expand Up @@ -1212,7 +1214,7 @@ static int check_ambiguous_visitor(jl_typemap_entry_t *oldentry, struct typemap_
return 1;
}

static jl_value_t *check_ambiguous_matches(union jl_typemap_t defs, jl_typemap_entry_t *newentry)
static jl_value_t *check_ambiguous_matches(union jl_typemap_t defs, jl_typemap_entry_t *newentry, jl_typemap_intersection_visitor_fptr fptr)
{
jl_tupletype_t *type = newentry->sig;
jl_tupletype_t *ttypes = (jl_tupletype_t*)jl_unwrap_unionall((jl_value_t*)type);
Expand All @@ -1226,7 +1228,7 @@ static jl_value_t *check_ambiguous_matches(union jl_typemap_t defs, jl_typemap_e
va = NULL;
}
struct ambiguous_matches_env env;
env.match.fptr = check_ambiguous_visitor;
env.match.fptr = fptr;
env.match.type = (jl_value_t*)type;
env.match.va = va;
env.match.ti = NULL;
Expand All @@ -1241,6 +1243,47 @@ static jl_value_t *check_ambiguous_matches(union jl_typemap_t defs, jl_typemap_e
return env.shadowed;
}

static int check_disabled_ambiguous_visitor(jl_typemap_entry_t *oldentry, struct typemap_intersection_env *closure0)
{
struct ambiguous_matches_env *closure = container_of(closure0, struct ambiguous_matches_env, match);
if (oldentry == closure->newentry) {
closure->after = 1;
return 1;
}
if (!closure->after || oldentry->max_world < ~(size_t)0) // the second condition prevents us from confusion in multiple cycles of add/delete
return 1;
jl_tupletype_t *sig = oldentry->sig;
jl_value_t *isect = closure->match.ti;
if (closure->shadowed == NULL)
closure->shadowed = (jl_value_t*)jl_alloc_vec_any(0);

int i, l = jl_array_len(closure->shadowed);
for (i = 0; i < l; i++) {
jl_method_t *mth = (jl_method_t*)jl_array_ptr_ref(closure->shadowed, i);
jl_value_t *isect2 = jl_type_intersection(mth->sig, (jl_value_t*)sig);
// see if the intersection was covered by precisely the disabled method
// that means we now need to record the ambiguity
if (jl_types_equal(isect, isect2)) {
jl_method_t *mambig = mth;
jl_method_t *m = oldentry->func.method;
if (m->ambig == jl_nothing) {
m->ambig = (jl_value_t*) jl_alloc_vec_any(0);
jl_gc_wb(m, m->ambig);
}
if (mambig->ambig == jl_nothing) {
mambig->ambig = (jl_value_t*) jl_alloc_vec_any(0);
jl_gc_wb(mambig, mambig->ambig);
}
jl_array_ptr_1d_push((jl_array_t*) m->ambig, (jl_value_t*) mambig);
jl_array_ptr_1d_push((jl_array_t*) mambig->ambig, (jl_value_t*) m);
}
}

jl_array_ptr_1d_push((jl_array_t*)closure->shadowed, oldentry->func.value);
return 1;
}


static void method_overwrite(jl_typemap_entry_t *newentry, jl_method_t *oldvalue)
{
// method overwritten
Expand Down Expand Up @@ -1405,6 +1448,33 @@ void jl_method_instance_delete(jl_method_instance_t *mi)
jl_uv_puts(JL_STDOUT, "<<<\n", 4);
}

static int typemap_search(jl_typemap_entry_t *entry, void *closure)
{
if ((void*)(entry->func.method) == *(jl_method_t**)closure) {
*(jl_typemap_entry_t**)closure = entry;
return 0;
}
return 1;
}

JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *method)
{
jl_typemap_entry_t *methodentry = (jl_typemap_entry_t*)(method);
if (jl_typemap_visitor(mt->defs, typemap_search, &methodentry))
jl_error("method not in method table");
JL_LOCK(&mt->writelock);
// Narrow the world age on the method to make it uncallable
methodentry->max_world = jl_world_counter++;
// Recompute ambiguities (deleting a more specific method might reveal ambiguities that it previously resolved)
check_ambiguous_matches(mt->defs, methodentry, check_disabled_ambiguous_visitor); // TODO: decrease repeated work?
// Invalidate the backedges
struct invalidate_conflicting_env env;
env.invalidated = 0;
env.max_world = methodentry->max_world;
jl_typemap_visitor(methodentry->func.method->specializations, (jl_typemap_visitor_fptr)invalidate_backedges, &env);
JL_UNLOCK(&mt->writelock);
}

JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method, jl_tupletype_t *simpletype)
{
assert(jl_is_method(method));
Expand All @@ -1430,7 +1500,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
method_overwrite(newentry, (jl_method_t*)oldvalue);
}
else {
oldvalue = check_ambiguous_matches(mt->defs, newentry);
oldvalue = check_ambiguous_matches(mt->defs, newentry, check_ambiguous_visitor);
if (mt->backedges) {
jl_value_t **backedges = (jl_value_t**)jl_array_data(mt->backedges);
size_t i, na = jl_array_len(mt->backedges);
Expand Down Expand Up @@ -1566,15 +1636,15 @@ jl_tupletype_t *arg_type_tuple(jl_value_t **args, size_t nargs)
jl_method_instance_t *jl_method_lookup_by_type(jl_methtable_t *mt, jl_tupletype_t *types,
int cache, int allow_exec, size_t world)
{
jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(mt->cache, types, NULL, /*subtype*/1, jl_cachearg_offset(mt), world);
jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(mt->cache, types, NULL, /*subtype*/1, jl_cachearg_offset(mt), world, /*max_world_mask*/0);
if (entry) {
jl_method_instance_t *linfo = (jl_method_instance_t*)entry->func.value;
assert(linfo->min_world <= entry->min_world && linfo->max_world >= entry->max_world &&
"typemap consistency error: MethodInstance doesn't apply to full range of its entry");
return linfo;
}
JL_LOCK(&mt->writelock);
entry = jl_typemap_assoc_by_type(mt->cache, types, NULL, /*subtype*/1, jl_cachearg_offset(mt), world);
entry = jl_typemap_assoc_by_type(mt->cache, types, NULL, /*subtype*/1, jl_cachearg_offset(mt), world, /*max_world_mask*/0);
if (entry) {
jl_method_instance_t *linfo = (jl_method_instance_t*)entry->func.value;
assert(linfo->min_world <= entry->min_world && linfo->max_world >= entry->max_world &&
Expand Down Expand Up @@ -2018,7 +2088,7 @@ JL_DLLEXPORT jl_value_t *jl_gf_invoke_lookup(jl_datatype_t *types, size_t world)
jl_svec_t *env = jl_emptysvec;
JL_GC_PUSH1(&env);
jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(
mt->defs, types, /*env*/&env, /*subtype*/1, /*offs*/0, world);
mt->defs, types, /*env*/&env, /*subtype*/1, /*offs*/0, world, /*max_world_mask*/0);
JL_GC_POP();
if (!entry)
return jl_nothing;
Expand Down Expand Up @@ -2136,7 +2206,7 @@ JL_DLLEXPORT jl_value_t *jl_get_invoke_lambda(jl_methtable_t *mt,
jl_typemap_entry_t *tm = NULL;
if (method->invokes.unknown != NULL) {
tm = jl_typemap_assoc_by_type(method->invokes, tt, NULL, /*subtype*/1,
jl_cachearg_offset(mt), world);
jl_cachearg_offset(mt), world, /*max_world_mask*/0);
if (tm) {
return (jl_value_t*)tm->func.linfo;
}
Expand All @@ -2145,7 +2215,7 @@ JL_DLLEXPORT jl_value_t *jl_get_invoke_lambda(jl_methtable_t *mt,
JL_LOCK(&method->writelock);
if (method->invokes.unknown != NULL) {
tm = jl_typemap_assoc_by_type(method->invokes, tt, NULL, /*subtype*/1,
jl_cachearg_offset(mt), world);
jl_cachearg_offset(mt), world, /*max_world_mask*/0);
if (tm) {
jl_method_instance_t *mfunc = tm->func.linfo;
JL_UNLOCK(&method->writelock);
Expand Down
2 changes: 1 addition & 1 deletion src/julia_internal.h
Expand Up @@ -927,7 +927,7 @@ jl_typemap_entry_t *jl_typemap_insert(union jl_typemap_t *cache, jl_value_t *par

jl_typemap_entry_t *jl_typemap_assoc_by_type(
union jl_typemap_t ml_or_cache, jl_tupletype_t *types, jl_svec_t **penv,
int8_t subtype, int8_t offs, size_t world);
int8_t subtype, int8_t offs, size_t world, size_t max_world_mask);
jl_typemap_entry_t *jl_typemap_level_assoc_exact(jl_typemap_level_t *cache, jl_value_t **args, size_t n, int8_t offs, size_t world);
jl_typemap_entry_t *jl_typemap_entry_assoc_exact(jl_typemap_entry_t *mn, jl_value_t **args, size_t n, size_t world);
STATIC_INLINE jl_typemap_entry_t *jl_typemap_assoc_exact(union jl_typemap_t ml_or_cache, jl_value_t **args, size_t n, int8_t offs, size_t world)
Expand Down