Skip to content

Commit

Permalink
Merge pull request #25050 from JuliaLang/teh/delete_method
Browse files Browse the repository at this point in the history
Support method deletion
  • Loading branch information
timholy authored Dec 20, 2017
2 parents 7b1c06a + d16fb9a commit 564ecb0
Show file tree
Hide file tree
Showing 8 changed files with 325 additions and 37 deletions.
15 changes: 15 additions & 0 deletions base/reflection.jl
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 @@ -2558,26 +2561,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 @@ -2588,8 +2606,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 @@ -2598,6 +2616,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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading

0 comments on commit 564ecb0

Please sign in to comment.