@@ -1482,10 +1482,14 @@ static int concretesig_equal(jl_value_t *tt, jl_value_t *simplesig) JL_NOTSAFEPO
1482
1482
return 1 ;
1483
1483
}
1484
1484
1485
+ // if available, returns a TypeMapEntry in the "leafcache" that matches `tt` (by type-equality) and is valid during `world`
1485
1486
static inline jl_typemap_entry_t * lookup_leafcache (jl_genericmemory_t * leafcache JL_PROPAGATES_ROOT , jl_value_t * tt , size_t world ) JL_NOTSAFEPOINT
1486
1487
{
1487
1488
jl_typemap_entry_t * entry = (jl_typemap_entry_t * )jl_eqtable_get (leafcache , (jl_value_t * )tt , NULL );
1488
1489
if (entry ) {
1490
+ // search tail of the linked-list (including the returned entry) for an entry intersecting world
1491
+ //
1492
+ // n.b. this entire chain is type-equal to tt (by construction), so it is unnecessary to call `tt<:entry->sig`
1489
1493
do {
1490
1494
if (jl_atomic_load_relaxed (& entry -> min_world ) <= world && world <= jl_atomic_load_relaxed (& entry -> max_world )) {
1491
1495
if (entry -> simplesig == (void * )jl_nothing || concretesig_equal (tt , (jl_value_t * )entry -> simplesig ))
@@ -4389,21 +4393,31 @@ static jl_method_match_t *make_method_match(jl_tupletype_t *spec_types, jl_svec_
4389
4393
return match ;
4390
4394
}
4391
4395
4396
+ // callback for typemap_visitor
4397
+ //
4398
+ // This will exit the search early (by returning 0 / false) if the match limit is proven to be
4399
+ // exceeded early. This is only best-effort, since specificity means that many matched methods
4400
+ // may be sorted and removed in the output processing for ml_matches and therefore we can only
4401
+ // conservatively under-approximate the matches during the search.
4392
4402
static int ml_matches_visitor (jl_typemap_entry_t * ml , struct typemap_intersection_env * closure0 )
4393
4403
{
4394
4404
struct ml_matches_env * closure = container_of (closure0 , struct ml_matches_env , match );
4395
4405
if (closure -> intersections == 0 && !closure0 -> issubty )
4396
4406
return 1 ;
4407
+
4408
+ // First, check the world range of the typemap entry to ensure that it intersects
4409
+ // the query world. If it does not, narrow the result world range to guarantee
4410
+ // excluding it from the results is valid for the full span.
4397
4411
size_t min_world = jl_atomic_load_relaxed (& ml -> min_world );
4398
4412
size_t max_world = jl_atomic_load_relaxed (& ml -> max_world );
4399
4413
if (closure -> world < min_world ) {
4400
- // ignore method table entries that are part of a later world
4414
+ // exclude method table entries that are part of a later world
4401
4415
if (closure -> match .max_valid >= min_world )
4402
4416
closure -> match .max_valid = min_world - 1 ;
4403
4417
return 1 ;
4404
4418
}
4405
4419
else if (closure -> world > max_world ) {
4406
- // ignore method table entries that have been replaced in the current world
4420
+ // exclude method table entries that have been replaced in the current world
4407
4421
if (closure -> match .min_valid <= max_world )
4408
4422
closure -> match .min_valid = max_world + 1 ;
4409
4423
return 1 ;
@@ -4601,21 +4615,47 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
4601
4615
else
4602
4616
va = NULL ;
4603
4617
}
4604
- struct ml_matches_env env = {{ml_matches_visitor , (jl_value_t * )type , va , /* .search_slurp = */ 0 ,
4605
- /* .min_valid = */ * min_valid , /* .max_valid = */ * max_valid ,
4606
- /* .ti = */ NULL , /* .env = */ jl_emptysvec , /* .issubty = */ 0 },
4607
- intersections , world , lim , include_ambiguous , /* .t = */ jl_an_empty_vec_any ,
4608
- /* .matc = */ NULL };
4618
+ struct ml_matches_env env = {
4619
+ /* match */ {
4620
+ /* inputs */
4621
+ /* fptr / callback */ ml_matches_visitor ,
4622
+ /* sig */ (jl_value_t * )type ,
4623
+ /* vararg type / tparam0 */ va ,
4624
+
4625
+ /* temporaries */
4626
+ /* .search_slurp = */ 0 ,
4627
+
4628
+ /* outputs */
4629
+ /* .min_valid = */ * min_valid ,
4630
+ /* .max_valid = */ * max_valid ,
4631
+ /* .ti = */ NULL ,
4632
+ /* .env = */ jl_emptysvec ,
4633
+ /* .issubty = */ 0
4634
+ },
4635
+ /* inputs */
4636
+ intersections ,
4637
+ world ,
4638
+ lim ,
4639
+ include_ambiguous ,
4640
+
4641
+ /* outputs */
4642
+ /* .t = */ jl_an_empty_vec_any ,
4643
+
4644
+ /* temporaries */
4645
+ /* .matc = */ NULL
4646
+ };
4609
4647
struct jl_typemap_assoc search = {(jl_value_t * )type , world , jl_emptysvec };
4610
4648
jl_value_t * isect2 = NULL ;
4611
4649
JL_GC_PUSH6 (& env .t , & env .matc , & env .match .env , & search .env , & env .match .ti , & isect2 );
4612
4650
4613
4651
if (mc ) {
4614
- // check the leaf cache if this type can be in there
4652
+ // first check the leaf cache if the type might have been put in there
4615
4653
if (((jl_datatype_t * )unw )-> isdispatchtuple ) {
4616
4654
jl_genericmemory_t * leafcache = jl_atomic_load_relaxed (& mc -> leafcache );
4617
4655
jl_typemap_entry_t * entry = lookup_leafcache (leafcache , (jl_value_t * )type , world );
4618
4656
if (entry ) {
4657
+ // leafcache found a match, construct the MethodMatch by computing the effective
4658
+ // types + sparams and the world bounds
4619
4659
jl_method_instance_t * mi = entry -> func .linfo ;
4620
4660
jl_method_t * meth = mi -> def .method ;
4621
4661
if (!jl_is_unionall (meth -> sig )) {
@@ -4644,10 +4684,13 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
4644
4684
return env .t ;
4645
4685
}
4646
4686
}
4687
+
4647
4688
// then check the full cache if it seems profitable
4648
4689
if (((jl_datatype_t * )unw )-> isdispatchtuple ) {
4649
4690
jl_typemap_entry_t * entry = jl_typemap_assoc_by_type (jl_atomic_load_relaxed (& mc -> cache ), & search , jl_cachearg_offset (), /*subtype*/ 1 );
4650
4691
if (entry && (((jl_datatype_t * )unw )-> isdispatchtuple || entry -> guardsigs == jl_emptysvec )) {
4692
+ // full cache found a match, construct the MethodMatch by computing the effective
4693
+ // types + sparams and the world bounds
4651
4694
jl_method_instance_t * mi = entry -> func .linfo ;
4652
4695
jl_method_t * meth = mi -> def .method ;
4653
4696
size_t min_world = jl_atomic_load_relaxed (& entry -> min_world );
@@ -4679,7 +4722,8 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
4679
4722
// then scan everything
4680
4723
if (!jl_typemap_intersection_visitor (jl_atomic_load_relaxed (& mt -> defs ), 0 , & env .match ) && env .t == jl_an_empty_vec_any ) {
4681
4724
JL_GC_POP ();
4682
- // if we return early without returning methods, set only the min/max valid collected from matching
4725
+ // if we return early without returning methods, lim was proven to be exceeded
4726
+ // during the search set only the min/max valid collected from matching
4683
4727
* min_valid = env .match .min_valid ;
4684
4728
* max_valid = env .match .max_valid ;
4685
4729
return jl_nothing ;
@@ -4689,12 +4733,19 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
4689
4733
* max_valid = env .match .max_valid ;
4690
4734
// done with many of these values now
4691
4735
env .match .ti = NULL ; env .matc = NULL ; env .match .env = NULL ; search .env = NULL ;
4736
+
4737
+ // all intersecting methods have been collected now. the remaining work is to sort
4738
+ // these and apply specificity to determine a list of dispatch-possible call targets
4692
4739
size_t i , j , len = jl_array_nrows (env .t );
4740
+
4741
+ // the 'minmax' method is a method that (1) fully-covers the queried type, and (2) is
4742
+ // more-specific than any other fully-covering method (but if !all_subtypes, there are
4743
+ // non-fully-covering methods to which it is _likely_ not more specific)
4693
4744
jl_method_match_t * minmax = NULL ;
4694
4745
int any_subtypes = 0 ;
4695
4746
if (len > 1 ) {
4696
- // first try to pre-process the results to find the most specific
4697
- // result that fully covers the input , since we can do this in O(n^2)
4747
+ // first try to pre-process the results to find the most specific option
4748
+ // among the fully-covering methods , since we can do this in O(n^2)
4698
4749
// time, and the rest is O(n^3)
4699
4750
// - first find a candidate for the best of these method results
4700
4751
for (i = 0 ; i < len ; i ++ ) {
@@ -4719,8 +4770,8 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
4719
4770
}
4720
4771
}
4721
4772
}
4722
- // - it may even dominate some choices that are not subtypes !
4723
- // move those into the subtype group, where we're filter them out shortly after
4773
+ // - it may even dominate (be more specific than) some choices that are not fully-covering !
4774
+ // move those into the subtype group, where we'll filter them out shortly after
4724
4775
// (potentially avoiding reporting these as an ambiguity, and
4725
4776
// potentially allowing us to hit the next fast path)
4726
4777
// - we could always check here if *any* FULLY_COVERS method is
@@ -4733,6 +4784,8 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
4733
4784
jl_method_t * minmaxm = NULL ;
4734
4785
if (minmax != NULL )
4735
4786
minmaxm = minmax -> method ;
4787
+ // scan through all the non-fully-matching methods and count them as "fully-covering" (ish)
4788
+ // (i.e. in the 'subtype' group) if `minmax` is more-specific
4736
4789
for (i = 0 ; i < len ; i ++ ) {
4737
4790
jl_method_match_t * matc = (jl_method_match_t * )jl_array_ptr_ref (env .t , i );
4738
4791
if (matc -> fully_covers != FULLY_COVERS ) {
@@ -4753,16 +4806,21 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, jl_methcache_t *mc,
4753
4806
// we've already processed all of the possible outputs
4754
4807
if (all_subtypes ) {
4755
4808
if (minmax == NULL ) {
4809
+ // all intersecting methods are fully-covering, but there is no unique most-specific method
4756
4810
if (!include_ambiguous ) {
4811
+ // there no unambiguous choice of method
4757
4812
len = 0 ;
4758
4813
env .t = jl_an_empty_vec_any ;
4759
4814
}
4760
4815
else if (lim == 1 ) {
4816
+ // we'd have to return >1 method due to the ambiguity, so bail early
4761
4817
JL_GC_POP ();
4762
4818
return jl_nothing ;
4763
4819
}
4764
4820
}
4765
4821
else {
4822
+ // `minmax` is more-specific than all other matches and is fully-covering
4823
+ // we can return it as our only result
4766
4824
jl_array_ptr_set (env .t , 0 , minmax );
4767
4825
jl_array_del_end ((jl_array_t * )env .t , len - 1 );
4768
4826
len = 1 ;
0 commit comments