@@ -1566,10 +1566,14 @@ static int concretesig_equal(jl_value_t *tt, jl_value_t *simplesig) JL_NOTSAFEPO
15661566 return 1 ;
15671567}
15681568
1569+ // if available, returns a TypeMapEntry in the "leafcache" that matches `tt` (by type-equality) and is valid during `world`
15691570static inline jl_typemap_entry_t * lookup_leafcache (jl_genericmemory_t * leafcache JL_PROPAGATES_ROOT , jl_value_t * tt , size_t world ) JL_NOTSAFEPOINT
15701571{
15711572 jl_typemap_entry_t * entry = (jl_typemap_entry_t * )jl_eqtable_get (leafcache , (jl_value_t * )tt , NULL );
15721573 if (entry ) {
1574+ // search tail of the linked-list (including the returned entry) for an entry intersecting world
1575+ //
1576+ // n.b. this entire chain is type-equal to tt (by construction), so it is unnecessary to call `tt<:entry->sig`
15731577 do {
15741578 if (jl_atomic_load_relaxed (& entry -> min_world ) <= world && world <= jl_atomic_load_relaxed (& entry -> max_world )) {
15751579 if (entry -> simplesig == (void * )jl_nothing || concretesig_equal (tt , (jl_value_t * )entry -> simplesig ))
@@ -4492,21 +4496,31 @@ static jl_method_match_t *make_method_match(jl_tupletype_t *spec_types, jl_svec_
44924496 return match ;
44934497}
44944498
4499+ // callback for typemap_visitor
4500+ //
4501+ // This will exit the search early (by returning 0 / false) if the match limit is proven to be
4502+ // exceeded early. This is only best-effort, since specificity means that many matched methods
4503+ // may be sorted and removed in the output processing for ml_matches and therefore we can only
4504+ // conservatively under-approximate the matches during the search.
44954505static int ml_matches_visitor (jl_typemap_entry_t * ml , struct typemap_intersection_env * closure0 )
44964506{
44974507 struct ml_matches_env * closure = container_of (closure0 , struct ml_matches_env , match );
44984508 if (closure -> intersections == 0 && !closure0 -> issubty )
44994509 return 1 ;
4510+
4511+ // First, check the world range of the typemap entry to ensure that it intersects
4512+ // the query world. If it does not, narrow the result world range to guarantee
4513+ // excluding it from the results is valid for the full span.
45004514 size_t min_world = jl_atomic_load_relaxed (& ml -> min_world );
45014515 size_t max_world = jl_atomic_load_relaxed (& ml -> max_world );
45024516 if (closure -> world < min_world ) {
4503- // ignore method table entries that are part of a later world
4517+ // exclude method table entries that are part of a later world
45044518 if (closure -> match .max_valid >= min_world )
45054519 closure -> match .max_valid = min_world - 1 ;
45064520 return 1 ;
45074521 }
45084522 else if (closure -> world > max_world ) {
4509- // ignore method table entries that have been replaced in the current world
4523+ // exclude method table entries that have been replaced in the current world
45104524 if (closure -> match .min_valid <= max_world )
45114525 closure -> match .min_valid = max_world + 1 ;
45124526 return 1 ;
@@ -4844,21 +4858,47 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
48444858 else
48454859 va = NULL ;
48464860 }
4847- struct ml_matches_env env = {{ml_matches_visitor , (jl_value_t * )type , va , /* .search_slurp = */ 0 ,
4848- /* .min_valid = */ * min_valid , /* .max_valid = */ * max_valid ,
4849- /* .ti = */ NULL , /* .env = */ jl_emptysvec , /* .issubty = */ 0 },
4850- intersections , world , lim , include_ambiguous , /* .t = */ jl_an_empty_vec_any ,
4851- /* .matc = */ NULL };
4861+ struct ml_matches_env env = {
4862+ /* match */ {
4863+ /* inputs */
4864+ /* fptr / callback */ ml_matches_visitor ,
4865+ /* sig */ (jl_value_t * )type ,
4866+ /* vararg type / tparam0 */ va ,
4867+
4868+ /* temporaries */
4869+ /* .search_slurp = */ 0 ,
4870+
4871+ /* outputs */
4872+ /* .min_valid = */ * min_valid ,
4873+ /* .max_valid = */ * max_valid ,
4874+ /* .ti = */ NULL ,
4875+ /* .env = */ jl_emptysvec ,
4876+ /* .issubty = */ 0
4877+ },
4878+ /* inputs */
4879+ intersections ,
4880+ world ,
4881+ lim ,
4882+ include_ambiguous ,
4883+
4884+ /* outputs */
4885+ /* .t = */ jl_an_empty_vec_any ,
4886+
4887+ /* temporaries */
4888+ /* .matc = */ NULL
4889+ };
48524890 struct jl_typemap_assoc search = {(jl_value_t * )type , world , jl_emptysvec };
48534891 jl_value_t * isect2 = NULL ;
48544892 JL_GC_PUSH6 (& env .t , & env .matc , & env .match .env , & search .env , & env .match .ti , & isect2 );
48554893
48564894 if (mc ) {
4857- // check the leaf cache if this type can be in there
4895+ // first check the leaf cache if the type might have been put in there
48584896 if (((jl_datatype_t * )unw )-> isdispatchtuple ) {
48594897 jl_genericmemory_t * leafcache = jl_atomic_load_relaxed (& mc -> leafcache );
48604898 jl_typemap_entry_t * entry = lookup_leafcache (leafcache , (jl_value_t * )type , world );
48614899 if (entry ) {
4900+ // leafcache found a match, construct the MethodMatch by computing the effective
4901+ // types + sparams and the world bounds
48624902 jl_method_instance_t * mi = entry -> func .linfo ;
48634903 jl_method_t * meth = mi -> def .method ;
48644904 if (!jl_is_unionall (meth -> sig )) {
@@ -4887,10 +4927,13 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
48874927 return env .t ;
48884928 }
48894929 }
4930+
48904931 // then check the full cache if it seems profitable
48914932 if (((jl_datatype_t * )unw )-> isdispatchtuple ) {
48924933 jl_typemap_entry_t * entry = jl_typemap_assoc_by_type (jl_atomic_load_relaxed (& mc -> cache ), & search , jl_cachearg_offset (), /*subtype*/ 1 );
48934934 if (entry && (((jl_datatype_t * )unw )-> isdispatchtuple || entry -> guardsigs == jl_emptysvec )) {
4935+ // full cache found a match, construct the MethodMatch by computing the effective
4936+ // types + sparams and the world bounds
48944937 jl_method_instance_t * mi = entry -> func .linfo ;
48954938 jl_method_t * meth = mi -> def .method ;
48964939 size_t min_world = jl_atomic_load_relaxed (& entry -> min_world );
@@ -4922,7 +4965,8 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
49224965 // then scan everything
49234966 if (!jl_typemap_intersection_visitor (jl_atomic_load_relaxed (& mt -> defs ), 0 , & env .match ) && env .t == jl_an_empty_vec_any ) {
49244967 JL_GC_POP ();
4925- // if we return early without returning methods, set only the min/max valid collected from matching
4968+ // if we return early without returning methods, lim was proven to be exceeded
4969+ // during the search set only the min/max valid collected from matching
49264970 * min_valid = env .match .min_valid ;
49274971 * max_valid = env .match .max_valid ;
49284972 return jl_nothing ;
@@ -4932,12 +4976,19 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
49324976 * max_valid = env .match .max_valid ;
49334977 // done with many of these values now
49344978 env .match .ti = NULL ; env .matc = NULL ; env .match .env = NULL ; search .env = NULL ;
4979+
4980+ // all intersecting methods have been collected now. the remaining work is to sort
4981+ // these and apply specificity to determine a list of dispatch-possible call targets
49354982 size_t i , j , len = jl_array_nrows (env .t );
4983+
4984+ // the 'minmax' method is a method that (1) fully-covers the queried type, and (2) is
4985+ // more-specific than any other fully-covering method (but if !all_subtypes, there are
4986+ // non-fully-covering methods to which it is _likely_ not more specific)
49364987 jl_method_match_t * minmax = NULL ;
49374988 int any_subtypes = 0 ;
49384989 if (len > 1 ) {
4939- // first try to pre-process the results to find the most specific
4940- // result that fully covers the input , since we can do this in O(n^2)
4990+ // first try to pre-process the results to find the most specific option
4991+ // among the fully-covering methods , since we can do this in O(n^2)
49414992 // time, and the rest is O(n^3)
49424993 // - first find a candidate for the best of these method results
49434994 for (i = 0 ; i < len ; i ++ ) {
@@ -4962,8 +5013,8 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
49625013 }
49635014 }
49645015 }
4965- // - it may even dominate some choices that are not subtypes !
4966- // move those into the subtype group, where we're filter them out shortly after
5016+ // - it may even dominate (be more specific than) some choices that are not fully-covering !
5017+ // move those into the subtype group, where we'll filter them out shortly after
49675018 // (potentially avoiding reporting these as an ambiguity, and
49685019 // potentially allowing us to hit the next fast path)
49695020 // - we could always check here if *any* FULLY_COVERS method is
@@ -4976,6 +5027,8 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
49765027 jl_method_t * minmaxm = NULL ;
49775028 if (minmax != NULL )
49785029 minmaxm = minmax -> method ;
5030+ // scan through all the non-fully-matching methods and count them as "fully-covering" (ish)
5031+ // (i.e. in the 'subtype' group) if `minmax` is more-specific
49795032 for (i = 0 ; i < len ; i ++ ) {
49805033 jl_method_match_t * matc = (jl_method_match_t * )jl_array_ptr_ref (env .t , i );
49815034 if (matc -> fully_covers != FULLY_COVERS ) {
@@ -4996,16 +5049,21 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
49965049 // we've already processed all of the possible outputs
49975050 if (all_subtypes ) {
49985051 if (minmax == NULL ) {
5052+ // all intersecting methods are fully-covering, but there is no unique most-specific method
49995053 if (!include_ambiguous ) {
5054+ // there no unambiguous choice of method
50005055 len = 0 ;
50015056 env .t = jl_an_empty_vec_any ;
50025057 }
50035058 else if (lim == 1 ) {
5059+ // we'd have to return >1 method due to the ambiguity, so bail early
50045060 JL_GC_POP ();
50055061 return jl_nothing ;
50065062 }
50075063 }
50085064 else {
5065+ // `minmax` is more-specific than all other matches and is fully-covering
5066+ // we can return it as our only result
50095067 jl_array_ptr_set (env .t , 0 , minmax );
50105068 jl_array_del_end ((jl_array_t * )env .t , len - 1 );
50115069 len = 1 ;
0 commit comments