From 7ca82ab797914b4d1bca7221d9e99a20ee8b2da3 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 16 Jan 2026 13:35:48 +0100 Subject: [PATCH] Fix `jl_method_lookup_by_tt` for custom method tables. `jl_mt_assoc_by_type` was hardcoding `jl_method_table` in its fallback path, causing `jl_method_lookup_by_tt` to incorrectly search the global method table when given a custom one. This resulted in two issues: 1. Methods in custom MTs weren't found when not already cached 2. Global methods were incorrectly cached in custom MT caches, leading to stale entries after redefinition Fix by passing the method table through to `jl_mt_assoc_by_type` so it uses the correct MT for fallback lookups. --- src/gf.c | 12 ++++++------ test/core.jl | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/gf.c b/src/gf.c index 1d3a9636ddfa9..b18a791945578 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1926,7 +1926,7 @@ JL_DLLEXPORT jl_typemap_entry_t *jl_mt_find_cache_entry(jl_methcache_t *mc JL_PR return entry; } -static jl_method_instance_t *jl_mt_assoc_by_type(jl_methcache_t *mc JL_PROPAGATES_ROOT, jl_datatype_t *tt JL_MAYBE_UNROOTED, size_t world) +static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_methcache_t *mc JL_PROPAGATES_ROOT, jl_datatype_t *tt JL_MAYBE_UNROOTED, size_t world) { jl_typemap_entry_t *entry = jl_mt_find_cache_entry(mc, tt, world); if (entry) @@ -1943,11 +1943,11 @@ static jl_method_instance_t *jl_mt_assoc_by_type(jl_methcache_t *mc JL_PROPAGATE if (!mi) { size_t min_valid = 0; size_t max_valid = ~(size_t)0; - matc = _gf_invoke_lookup((jl_value_t*)tt, jl_method_table, world, 0, &min_valid, &max_valid); + matc = _gf_invoke_lookup((jl_value_t*)tt, mt, world, 0, &min_valid, &max_valid); if (matc) { jl_method_t *m = matc->method; jl_svec_t *env = matc->sparams; - mi = cache_method(jl_method_table, mc, &mc->cache, (jl_value_t*)mc, tt, m, world, min_valid, max_valid, env); + mi = cache_method(mt, mc, &mc->cache, (jl_value_t*)mc, tt, m, world, min_valid, max_valid, env); JL_GC_POP(); return mi; } @@ -3191,7 +3191,7 @@ JL_DLLEXPORT jl_value_t *jl_method_lookup_by_tt(jl_tupletype_t *tt, size_t world mt = (jl_methtable_t*) _mt; } jl_methcache_t *mc = mt->cache; - jl_method_instance_t *mi = jl_mt_assoc_by_type(mc, tt, world); + jl_method_instance_t *mi = jl_mt_assoc_by_type(mt, mc, tt, world); if (!mi) return jl_nothing; return (jl_value_t*) mi; @@ -3206,7 +3206,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_method_lookup(jl_value_t **args, size_t na if (entry) return entry->func.linfo; jl_tupletype_t *tt = arg_type_tuple(args[0], &args[1], nargs); - return jl_mt_assoc_by_type(mc, tt, world); + return jl_mt_assoc_by_type(jl_method_table, mc, tt, world); } // return a Vector{Any} of svecs, each describing a method match: @@ -4280,7 +4280,7 @@ STATIC_INLINE jl_method_instance_t *jl_lookup_generic_(jl_value_t *F, jl_value_t assert(tt); // cache miss case jl_methcache_t *mc = jl_method_table->cache; - mfunc = jl_mt_assoc_by_type(mc, tt, world); + mfunc = jl_mt_assoc_by_type(jl_method_table, mc, tt, world); if (jl_options.malloc_log) jl_gc_sync_total_bytes(last_alloc); // discard allocation count from compilation if (mfunc == NULL) { diff --git a/test/core.jl b/test/core.jl index 490b39625a962..252eab18c2326 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8443,6 +8443,27 @@ let ms = Base._methods_by_ftype(Tuple{typeof(sin), Int}, OverlayModule.mt, 1, Ba @test isempty(ms) end +# test that overlay method table caches don't perform fallback lookups in the global cache +let world = Base.get_world_counter() + # method_instance should find overlay methods in custom MT + mi = Base.method_instance(sin, Tuple{Float64}; world, method_table=OverlayModule.mt) + @test mi isa Core.MethodInstance + @test mi.def.module === OverlayModule + + # method_instance with global MT should find Base method, not overlay + mi_global = Base.method_instance(sin, Tuple{Float64}; world, method_table=nothing) + @test mi_global isa Core.MethodInstance + @test mi_global.def.module === Base.Math +end + +# test that global methods do not leak in overlay method tables caches +let + global_only_func(x) = x + 1 # defined in global MT only + world = Base.get_world_counter() + mi = Base.method_instance(global_only_func, Tuple{Int}; world, method_table=OverlayModule.mt) + @test mi === nothing # should NOT find global method via custom MT +end + # precompilation let load_path = mktempdir() depot_path = mkdepottempdir()