From 4fc5592351fde4e7dee03b3b8bc8a7c1854a6f99 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 13 Dec 2017 04:56:15 -0600 Subject: [PATCH 1/3] Support method deletion --- base/reflection.jl | 15 ++++++ src/gf.c | 76 +++++++++++++++++++++++++-- test/reflection.jl | 126 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 214 insertions(+), 3 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index 26a339d843bc3..5fe5fda2e4cc7 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -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 diff --git a/src/gf.c b/src/gf.c index 5c1fcfef0525e..f7882133e8f0b 100644 --- a/src/gf.c +++ b/src/gf.c @@ -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; @@ -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); @@ -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; @@ -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 @@ -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)); @@ -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); diff --git a/test/reflection.jl b/test/reflection.jl index d471f005db531..91362303c3523 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -771,3 +771,129 @@ cinfo = cinfos[] test_similar_codeinfo(cinfo, cinfo_generated) @test_throws ErrorException code_lowered(f22979, typeof.(x22979), false) + +module MethodDeletion +using Test + +# Deletion after compiling top-level call +bar1(x) = 1 +bar1(x::Int) = 2 +foo1(x) = bar1(x) +faz1(x) = foo1(x) +@test faz1(1) == 2 +@test faz1(1.0) == 1 +m = first(methods(bar1, Tuple{Int})) +Base.delete_method(m) +@test bar1(1) == 1 +@test bar1(1.0) == 1 +@test foo1(1) == 1 +@test foo1(1.0) == 1 +@test faz1(1) == 1 +@test faz1(1.0) == 1 + +# Deletion after compiling middle-level call +bar2(x) = 1 +bar2(x::Int) = 2 +foo2(x) = bar2(x) +faz2(x) = foo2(x) +@test foo2(1) == 2 +@test foo2(1.0) == 1 +m = first(methods(bar2, Tuple{Int})) +Base.delete_method(m) +@test bar2(1.0) == 1 +@test bar2(1) == 1 +@test foo2(1) == 1 +@test foo2(1.0) == 1 +@test faz2(1) == 1 +@test faz2(1.0) == 1 + +# Deletion after compiling low-level call +bar3(x) = 1 +bar3(x::Int) = 2 +foo3(x) = bar3(x) +faz3(x) = foo3(x) +@test bar3(1) == 2 +@test bar3(1.0) == 1 +m = first(methods(bar3, Tuple{Int})) +Base.delete_method(m) +@test bar3(1) == 1 +@test bar3(1.0) == 1 +@test foo3(1) == 1 +@test foo3(1.0) == 1 +@test faz3(1) == 1 +@test faz3(1.0) == 1 + +# Deletion before any compilation +bar4(x) = 1 +bar4(x::Int) = 2 +foo4(x) = bar4(x) +faz4(x) = foo4(x) +m = first(methods(bar4, Tuple{Int})) +Base.delete_method(m) +@test bar4(1) == 1 +@test bar4(1.0) == 1 +@test foo4(1) == 1 +@test foo4(1.0) == 1 +@test faz4(1) == 1 +@test faz4(1.0) == 1 + +# Methods with keyword arguments +fookw(x; direction=:up) = direction +fookw(y::Int) = 2 +@test fookw("string") == :up +@test fookw(1) == 2 +m = collect(methods(fookw))[2] +Base.delete_method(m) +@test fookw(1) == 2 +@test_throws MethodError fookw("string") + +# functions with many methods +types = (Float64, Int32, String) +for T1 in types, T2 in types, T3 in types + @eval foomany(x::$T1, y::$T2, z::$T3) = y +end +@test foomany(Int32(5), "hello", 3.2) == "hello" +m = first(methods(foomany, Tuple{Int32, String, Float64})) +Base.delete_method(m) +@test_throws MethodError foomany(Int32(5), "hello", 3.2) + +struct EmptyType end +Base.convert(::Type{EmptyType}, x::Integer) = EmptyType() +m = first(methods(convert, Tuple{Type{EmptyType}, Integer})) +Base.delete_method(m) +@test_throws MethodError convert(EmptyType, 1) + +# parametric methods +parametric(A::Array{T,N}, i::Vararg{Int,N}) where {T,N} = N +@test parametric(rand(2,2), 1, 1) == 2 +m = first(methods(parametric)) +Base.delete_method(m) +@test_throws MethodError parametric(rand(2,2), 1, 1) + +# Deletion and ambiguity detection +foo(::Int, ::Int) = 1 +foo(::Real, ::Int) = 2 +foo(::Int, ::Real) = 3 +@test all(map(g->g.ambig==nothing, methods(foo))) +Base.delete_method(first(methods(foo))) +@test !all(map(g->g.ambig==nothing, methods(foo))) +@test_throws MethodError foo(1, 1) +foo(::Int, ::Int) = 1 +foo(1, 1) +@test map(g->g.ambig==nothing, methods(foo)) == [true, false, false] +Base.delete_method(first(methods(foo))) +@test_throws MethodError foo(1, 1) +@test map(g->g.ambig==nothing, methods(foo)) == [false, false] + +# multiple deletions and ambiguities +typeparam(::Type{T}, a::Array{T}) where T<:AbstractFloat = 1 +typeparam(::Type{T}, a::Array{T}) where T = 2 +for mth in collect(methods(typeparam)) + Base.delete_method(mth) +end +typeparam(::Type{T}, a::AbstractArray{T}) where T<:AbstractFloat = 1 +typeparam(::Type{T}, a::AbstractArray{T}) where T = 2 +@test typeparam(Float64, rand(2)) == 1 +@test typeparam(Int, rand(Int, 2)) == 2 + +end From 97c3c0b6cb3d0b453351b7ed279abb16df926f3a Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 14 Dec 2017 11:40:01 -0600 Subject: [PATCH 2/3] Make module loading play nicely with method deletion --- src/dump.c | 22 +++++++++++++++------ test/compile.jl | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/src/dump.c b/src/dump.c index 0181d0e83c791..ecaee48570a83 100644 --- a/src/dump.c +++ b/src/dump.c @@ -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; @@ -2588,7 +2591,8 @@ static jl_method_t *jl_lookup_method_worldset(jl_methtable_t *mt, jl_datatype_t jl_method_t *_new; while (1) { _new = (jl_method_t*)jl_methtable_lookup(mt, sig, world); - assert(_new && jl_is_method(_new)); + if (!(_new && jl_is_method(_new))) + return NULL; // the method wasn't found, probably because it was disabled world = lowerbound_dependent_world_set(_new->min_world, dependent_worlds); if (world == _new->min_world) return _new; @@ -2602,6 +2606,8 @@ static jl_method_t *jl_recache_method(jl_method_t *m, size_t start, arraylist_t jl_methtable_t *mt = ftype->name->mt; 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); + if (!_new) + return NULL; jl_update_backref_list((jl_value_t*)m, (jl_value_t*)_new, start); return _new; } @@ -2613,6 +2619,8 @@ static jl_method_instance_t *jl_recache_method_instance(jl_method_instance_t *li 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); + if (!m) + return NULL; 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 @@ -2645,10 +2653,12 @@ static void jl_recache_other(arraylist_t *dependent_worlds) else { abort(); } - if (loc) - *loc = _new; - if (offs > 0) - backref_list.items[offs] = _new; + if (_new) { + if (loc) + *loc = _new; + if (offs > 0) + backref_list.items[offs] = _new; + } } } diff --git a/test/compile.jl b/test/compile.jl index daafb6091e8c6..9a942bca87efa 100644 --- a/test/compile.jl +++ b/test/compile.jl @@ -623,4 +623,56 @@ let end end +# Ensure that module-loading plays nicely with Base.delete_method +dir = mktempdir() +insert!(LOAD_PATH, 1, dir) +insert!(Base.LOAD_CACHE_PATH, 1, dir) +try + A_module = :Aedb164bd3a126418 + B_module = :Bedb164bd3a126418 + A_file = joinpath(dir, "$A_module.jl") + B_file = joinpath(dir, "$B_module.jl") + + write(A_file, + """ + __precompile__() + + module $A_module + + export afunc + + afunc(::Int, ::Int) = 1 + afunc(::Any, ::Any) = 2 + + end + """) + write(B_file, + """ + __precompile__() + + module $B_module + + using $A_module + export bfunc + + bfunc(x) = afunc(x, x) + + precompile(bfunc, (Int,)) + precompile(bfunc, (Float64,)) + + end + """) + Base.require(A_module) + A = root_module(A_module) + mths = collect(methods(A.afunc)) + Base.delete_method(mths[1]) + Base.require(B_module) + B = root_module(B_module) + @test Base.invokelatest(B.bfunc, 1) == Base.invokelatest(B.bfunc, 1.0) == 2 +finally + shift!(LOAD_PATH) + shift!(Base.LOAD_CACHE_PATH) + rm(dir, recursive=true) end + +end # !withenv From d16fb9a425f9b7f1e0fc72f5395650f4084af56e Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Fri, 15 Dec 2017 04:56:09 -0600 Subject: [PATCH 3/3] Add max_world_mask to typemap_assoc/lookup --- src/codegen.cpp | 2 +- src/dump.c | 45 ++++++++++++++++++++++++++------------------ src/gf.c | 20 ++++++++++---------- src/julia_internal.h | 2 +- src/typemap.c | 29 ++++++++++++++-------------- test/compile.jl | 25 ++++++++++++++---------- 6 files changed, 69 insertions(+), 54 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 9a71d00ae99d1..495a2b839ac90 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -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) { diff --git a/src/dump.c b/src/dump.c index ecaee48570a83..19ebc53001409 100644 --- a/src/dump.c +++ b/src/dump.c @@ -2585,18 +2585,31 @@ 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); - if (!(_new && jl_is_method(_new))) - return NULL; // the method wasn't found, probably because it was disabled + 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) @@ -2604,10 +2617,9 @@ static jl_method_t *jl_recache_method(jl_method_t *m, size_t start, arraylist_t 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); - if (!_new) - return NULL; + 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; } @@ -2618,10 +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); - if (!m) - return NULL; - + 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; @@ -2630,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; } @@ -2653,12 +2664,10 @@ static void jl_recache_other(arraylist_t *dependent_worlds) else { abort(); } - if (_new) { - if (loc) - *loc = _new; - if (offs > 0) - backref_list.items[offs] = _new; - } + if (loc) + *loc = _new; + if (offs > 0) + backref_list.items[offs] = _new; } } diff --git a/src/gf.c b/src/gf.c index f7882133e8f0b..cf6e77c44833e 100644 --- a/src/gf.c +++ b/src/gf.c @@ -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); @@ -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; @@ -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; @@ -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)) { @@ -1168,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; @@ -1636,7 +1636,7 @@ 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 && @@ -1644,7 +1644,7 @@ jl_method_instance_t *jl_method_lookup_by_type(jl_methtable_t *mt, jl_tupletype_ 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 && @@ -2088,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; @@ -2206,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; } @@ -2215,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); diff --git a/src/julia_internal.h b/src/julia_internal.h index c3b4b8cc78eb6..4f384dca23312 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -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) diff --git a/src/typemap.c b/src/typemap.c index b166a849ea51e..f49a73f028181 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -562,12 +562,12 @@ int jl_typemap_intersection_visitor(union jl_typemap_t map, int offs, (the function) is always the same for most functions. */ static jl_typemap_entry_t *jl_typemap_assoc_by_type_(jl_typemap_entry_t *ml, jl_tupletype_t *types, - jl_svec_t **penv, size_t world) + jl_svec_t **penv, size_t world, size_t max_world_mask) { size_t n = jl_field_count(types); int typesisva = n == 0 ? 0 : jl_is_vararg_type(jl_tparam(types, n-1)); for (; ml != (void*)jl_nothing; ml = ml->next) { - if (world < ml->min_world || world > ml->max_world) + if (world < ml->min_world || world > (ml->max_world | max_world_mask)) continue; // ignore replaced methods size_t lensig = jl_field_count(jl_unwrap_unionall((jl_value_t*)ml->sig)); if (lensig == n || (ml->va && lensig <= n+1)) { @@ -617,10 +617,11 @@ static jl_typemap_entry_t *jl_typemap_assoc_by_type_(jl_typemap_entry_t *ml, jl_ int jl_obviously_unequal(jl_value_t *a, jl_value_t *b); -static jl_typemap_entry_t *jl_typemap_lookup_by_type_(jl_typemap_entry_t *ml, jl_tupletype_t *types, size_t world) +static jl_typemap_entry_t *jl_typemap_lookup_by_type_(jl_typemap_entry_t *ml, jl_tupletype_t *types, + size_t world, size_t max_world_mask) { for (; ml != (void*)jl_nothing; ml = ml->next) { - if (world < ml->min_world || world > ml->max_world) + if (world < ml->min_world || world > (ml->max_world | max_world_mask)) continue; // TODO: more efficient jl_value_t *a = (jl_value_t*)types; @@ -651,7 +652,7 @@ static jl_typemap_entry_t *jl_typemap_lookup_by_type_(jl_typemap_entry_t *ml, jl // this is the general entry point for looking up a type in the cache // as a subtype, or with type_equal 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) { if (jl_typeof(ml_or_cache.unknown) == (jl_value_t*)jl_typemap_level_type) { jl_typemap_level_t *cache = ml_or_cache.node; @@ -678,7 +679,7 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ // If there is a type at offs, look in the optimized caches if (!subtype) { if (ty && jl_is_any(ty)) - return jl_typemap_assoc_by_type(cache->any, types, penv, subtype, offs + 1, world); + return jl_typemap_assoc_by_type(cache->any, types, penv, subtype, offs + 1, world, max_world_mask); if (isva) // in lookup mode, want to match Vararg exactly, not as a subtype ty = NULL; } @@ -689,7 +690,7 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ union jl_typemap_t ml = mtcache_hash_lookup(&cache->targ, a0, 1, offs); if (ml.unknown != jl_nothing) { jl_typemap_entry_t *li = - jl_typemap_assoc_by_type(ml, types, penv, subtype, offs + 1, world); + jl_typemap_assoc_by_type(ml, types, penv, subtype, offs + 1, world, max_world_mask); if (li) return li; } } @@ -699,7 +700,7 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ union jl_typemap_t ml = mtcache_hash_lookup(&cache->arg1, ty, 0, offs); if (ml.unknown != jl_nothing) { jl_typemap_entry_t *li = - jl_typemap_assoc_by_type(ml, types, penv, subtype, offs + 1, world); + jl_typemap_assoc_by_type(ml, types, penv, subtype, offs + 1, world, max_world_mask); if (li) return li; } } @@ -707,18 +708,18 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ } // Always check the list (since offs doesn't always start at 0) if (subtype) { - jl_typemap_entry_t *li = jl_typemap_assoc_by_type_(cache->linear, types, penv, world); + jl_typemap_entry_t *li = jl_typemap_assoc_by_type_(cache->linear, types, penv, world, max_world_mask); if (li) return li; - return jl_typemap_assoc_by_type(cache->any, types, penv, subtype, offs + 1, world); + return jl_typemap_assoc_by_type(cache->any, types, penv, subtype, offs + 1, world, max_world_mask); } else { - return jl_typemap_lookup_by_type_(cache->linear, types, world); + return jl_typemap_lookup_by_type_(cache->linear, types, world, max_world_mask); } } else { return subtype ? - jl_typemap_assoc_by_type_(ml_or_cache.leaf, types, penv, world) : - jl_typemap_lookup_by_type_(ml_or_cache.leaf, types, world); + jl_typemap_assoc_by_type_(ml_or_cache.leaf, types, penv, world, max_world_mask) : + jl_typemap_lookup_by_type_(ml_or_cache.leaf, types, world, max_world_mask); } } @@ -977,7 +978,7 @@ jl_typemap_entry_t *jl_typemap_insert(union jl_typemap_t *cache, jl_value_t *par jl_value_t *ttype = jl_unwrap_unionall((jl_value_t*)type); if ((jl_value_t*)simpletype == jl_nothing) { - jl_typemap_entry_t *ml = jl_typemap_assoc_by_type(*cache, type, NULL, 0, offs, min_world); + jl_typemap_entry_t *ml = jl_typemap_assoc_by_type(*cache, type, NULL, 0, offs, min_world, 0); if (ml && ml->simplesig == (void*)jl_nothing) { if (overwritten != NULL) *overwritten = ml->func.value; diff --git a/test/compile.jl b/test/compile.jl index 9a942bca87efa..fd979a560c0f2 100644 --- a/test/compile.jl +++ b/test/compile.jl @@ -639,10 +639,13 @@ try module $A_module - export afunc + export apc, anopc - afunc(::Int, ::Int) = 1 - afunc(::Any, ::Any) = 2 + apc(::Int, ::Int) = 1 + apc(::Any, ::Any) = 2 + + anopc(::Int, ::Int) = 1 + anopc(::Any, ::Any) = 2 end """) @@ -653,22 +656,24 @@ try module $B_module using $A_module - export bfunc - bfunc(x) = afunc(x, x) + bpc(x) = apc(x, x) + bnopc(x) = anopc(x, x) - precompile(bfunc, (Int,)) - precompile(bfunc, (Float64,)) + precompile(bpc, (Int,)) + precompile(bpc, (Float64,)) end """) Base.require(A_module) A = root_module(A_module) - mths = collect(methods(A.afunc)) - Base.delete_method(mths[1]) + for mths in (collect(methods(A.apc)), collect(methods(A.anopc))) + Base.delete_method(mths[1]) + end Base.require(B_module) B = root_module(B_module) - @test Base.invokelatest(B.bfunc, 1) == Base.invokelatest(B.bfunc, 1.0) == 2 + @test Base.invokelatest(B.bpc, 1) == Base.invokelatest(B.bpc, 1.0) == 2 + @test Base.invokelatest(B.bnopc, 1) == Base.invokelatest(B.bnopc, 1.0) == 2 finally shift!(LOAD_PATH) shift!(Base.LOAD_CACHE_PATH)