@@ -1816,6 +1816,36 @@ static int jl_type_intersection2(jl_value_t *t1, jl_value_t *t2, jl_value_t **is
18161816 return 1 ;
18171817}
18181818
1819+ enum morespec_options {
1820+ morespec_unknown ,
1821+ morespec_isnot ,
1822+ morespec_is
1823+ };
1824+
1825+ // check if `type` is replacing `m` with an ambiguity here, given other methods in `d` that already match it
1826+ // precondition: type is not more specific than `m`
1827+ static int is_replacing (jl_value_t * type , jl_method_t * m , jl_method_t * const * d , size_t n , jl_value_t * isect , jl_value_t * isect2 , char * morespec )
1828+ {
1829+ size_t k ;
1830+ for (k = 0 ; k < n ; k ++ ) {
1831+ jl_method_t * m2 = d [k ];
1832+ // see if m2 also fully covered this intersection
1833+ if (m == m2 || !(jl_subtype (isect , m2 -> sig ) || (isect2 && jl_subtype (isect2 , m2 -> sig ))))
1834+ continue ;
1835+ if (morespec [k ] == (char )morespec_unknown )
1836+ morespec [k ] = (char )(jl_type_morespecific (m2 -> sig , type ) ? morespec_is : morespec_isnot );
1837+ if (morespec [k ] == (char )morespec_is )
1838+ // not actually shadowing this--m2 will still be better
1839+ return 0 ;
1840+ // since m2 was also a previous match over isect,
1841+ // see if m was also previously dominant over all m2
1842+ if (!jl_type_morespecific (m -> sig , m2 -> sig ))
1843+ // m and m2 were previously ambiguous over the full intersection of mi with type, and will still be ambiguous with type
1844+ return 0 ;
1845+ }
1846+ return 1 ;
1847+ }
1848+
18191849JL_DLLEXPORT void jl_method_table_insert (jl_methtable_t * mt , jl_method_t * method , jl_tupletype_t * simpletype )
18201850{
18211851 JL_TIMING (ADD_METHOD );
@@ -1850,7 +1880,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
18501880 oldvalue = get_intersect_matches (jl_atomic_load_relaxed (& mt -> defs ), newentry );
18511881
18521882 int invalidated = 0 ;
1853- jl_method_t * * d ;
1883+ jl_method_t * const * d ;
18541884 size_t j , n ;
18551885 if (oldvalue == NULL ) {
18561886 d = NULL ;
@@ -1879,6 +1909,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
18791909 // -> less specific or ambiguous with any one of them: can ignore the missing edge (not missing)
18801910 // -> some may have been ambiguous: still are
18811911 // -> some may have been called: they may be partly replaced (will be detected in the loop later)
1912+ // c.f. `is_replacing`, which is a similar query, but with an existing method match to compare against
18821913 missing = 1 ;
18831914 size_t j ;
18841915 for (j = 0 ; j < n ; j ++ ) {
@@ -1913,11 +1944,6 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
19131944 }
19141945 if (oldvalue ) {
19151946 oldmi = jl_alloc_vec_any (0 );
1916- enum morespec_options {
1917- morespec_unknown ,
1918- morespec_isnot ,
1919- morespec_is
1920- };
19211947 char * morespec = (char * )alloca (n );
19221948 memset (morespec , morespec_unknown , n );
19231949 for (j = 0 ; j < n ; j ++ ) {
@@ -1934,6 +1960,11 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
19341960 continue ;
19351961 isect3 = jl_type_intersection (m -> sig , (jl_value_t * )mi -> specTypes );
19361962 if (jl_type_intersection2 (type , isect3 , & isect , & isect2 )) {
1963+ // TODO: this only checks pair-wise for ambiguities, but the ambiguities could arise from the interaction of multiple methods
1964+ // and thus might miss a case where we introduce an ambiguity between two existing methods
1965+ // We could instead work to sort this into 3 groups `morespecific .. ambiguous .. lesspecific`, with `type` in ambiguous,
1966+ // such that everything in `morespecific` dominates everything in `ambiguous`, and everything in `ambiguous` dominates everything in `lessspecific`
1967+ // And then compute where each isect falls, and whether it changed group--necessitating invalidation--or not.
19371968 if (morespec [j ] == (char )morespec_unknown )
19381969 morespec [j ] = (char )(jl_type_morespecific (m -> sig , type ) ? morespec_is : morespec_isnot );
19391970 if (morespec [j ] == (char )morespec_is )
@@ -1942,62 +1973,38 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
19421973 if (ambig == morespec_unknown )
19431974 ambig = jl_type_morespecific (type , m -> sig ) ? morespec_is : morespec_isnot ;
19441975 // replacing a method--see if this really was the selected method previously
1945- // over the intersection
1946- if (ambig == morespec_isnot ) {
1947- size_t k ;
1948- for (k = 0 ; k < n ; k ++ ) {
1949- jl_method_t * m2 = d [k ];
1950- if (m == m2 || !(jl_subtype (isect , m2 -> sig ) || (isect && jl_subtype (isect , m2 -> sig ))))
1951- continue ;
1952- if (morespec [k ] == (char )morespec_unknown )
1953- morespec [k ] = (char )(jl_type_morespecific (m2 -> sig , type ) ? morespec_is : morespec_isnot );
1954- if (morespec [k ] == (char )morespec_is )
1955- // not actually shadowing this--m2 will still be better
1956- break ;
1957- // since m2 was also a previous match over isect,
1958- // see if m was also previously dominant over all m2
1959- if (!jl_type_morespecific (m -> sig , m2 -> sig ))
1960- break ;
1961- }
1962- if (k != n )
1963- continue ;
1964- }
1965- // Before deciding whether to invalidate `mi`, check each backedge for `invoke`s
1966- if (mi -> backedges ) {
1967- jl_array_t * backedges = mi -> backedges ;
1976+ // over the intersection (not ambiguous) and the new method will be selected now (morespec_is)
1977+ int replaced_dispatch = ambig == morespec_is || is_replacing (type , m , d , n , isect , isect2 , morespec );
1978+ // found that this specialization dispatch got replaced by m
1979+ // call invalidate_backedges(&do_nothing_with_codeinst, mi, max_world, "jl_method_table_insert");
1980+ // but ignore invoke-type edges
1981+ jl_array_t * backedges = mi -> backedges ;
1982+ if (backedges ) {
19681983 size_t ib = 0 , insb = 0 , nb = jl_array_len (backedges );
19691984 jl_value_t * invokeTypes ;
19701985 jl_method_instance_t * caller ;
19711986 while (ib < nb ) {
19721987 ib = get_next_edge (backedges , ib , & invokeTypes , & caller );
1973- if (!invokeTypes ) {
1974- // ordinary dispatch, invalidate
1988+ int replaced_edge ;
1989+ if (invokeTypes ) {
1990+ // n.b. normally we must have mi.specTypes <: invokeTypes <: m.sig (though it might not strictly hold), so we only need to check the other subtypes
1991+ replaced_edge = jl_subtype (invokeTypes , type ) && (ambig == morespec_is || is_replacing (type , m , d , n , invokeTypes , NULL , morespec ));
1992+ }
1993+ else {
1994+ replaced_edge = replaced_dispatch ;
1995+ }
1996+ if (replaced_edge ) {
19751997 invalidate_method_instance (& do_nothing_with_codeinst , caller , max_world , 1 );
19761998 invalidated = 1 ;
1977- } else {
1978- // invoke-dispatch, check invokeTypes for validity
1979- struct jl_typemap_assoc search = {invokeTypes , method -> primary_world , NULL , 0 , ~(size_t )0 };
1980- oldentry = jl_typemap_assoc_by_type (jl_atomic_load_relaxed (& mt -> defs ), & search , /*offs*/ 0 , /*subtype*/ 0 );
1981- if (oldentry && oldentry -> func .method == mi -> def .method ) {
1982- // We can safely keep this method
1983- jl_array_ptr_set (backedges , insb ++ , invokeTypes );
1984- jl_array_ptr_set (backedges , insb ++ , caller );
1985- } else {
1986- invalidate_method_instance (& do_nothing_with_codeinst , caller , max_world , 1 );
1987- invalidated = 1 ;
1988- }
1999+ }
2000+ else {
2001+ insb = set_next_edge (backedges , insb , invokeTypes , caller );
19892002 }
19902003 }
19912004 jl_array_del_end (backedges , nb - insb );
19922005 }
1993- if (!mi -> backedges || jl_array_len (mi -> backedges ) == 0 ) {
1994- jl_array_ptr_1d_push (oldmi , (jl_value_t * )mi );
1995- invalidate_external (mi , max_world );
1996- if (mi -> backedges ) {
1997- invalidated = 1 ;
1998- invalidate_backedges (& do_nothing_with_codeinst , mi , max_world , "jl_method_table_insert" );
1999- }
2000- }
2006+ jl_array_ptr_1d_push (oldmi , (jl_value_t * )mi );
2007+ invalidate_external (mi , max_world );
20012008 }
20022009 }
20032010 }
0 commit comments