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