@@ -14,7 +14,9 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
14
14
use rustc_data_structures:: sorted_map:: SortedMap ;
15
15
use rustc_data_structures:: unord:: UnordSet ;
16
16
use rustc_errors:: codes:: * ;
17
- use rustc_errors:: { Applicability , Diag , MultiSpan , StashKey , pluralize, struct_span_code_err} ;
17
+ use rustc_errors:: {
18
+ Applicability , Diag , DiagStyledString , MultiSpan , StashKey , pluralize, struct_span_code_err,
19
+ } ;
18
20
use rustc_hir:: def:: { CtorKind , DefKind , Res } ;
19
21
use rustc_hir:: def_id:: DefId ;
20
22
use rustc_hir:: intravisit:: { self , Visitor } ;
@@ -1569,7 +1571,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1569
1571
) ;
1570
1572
}
1571
1573
1572
- if rcvr_ty. is_numeric ( ) && rcvr_ty. is_fresh ( ) || restrict_type_params || suggested_derive {
1574
+ if rcvr_ty. is_numeric ( ) && rcvr_ty. is_fresh ( )
1575
+ || restrict_type_params
1576
+ || suggested_derive
1577
+ || self . lookup_alternative_tuple_impls ( & mut err, & unsatisfied_predicates)
1578
+ {
1573
1579
} else {
1574
1580
self . suggest_traits_to_import (
1575
1581
& mut err,
@@ -1744,6 +1750,119 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1744
1750
err. emit ( )
1745
1751
}
1746
1752
1753
+ /// If the predicate failure is caused by an unmet bound on a tuple, recheck if the bound would
1754
+ /// succeed if all the types on the tuple had no borrows. This is a common problem for libraries
1755
+ /// like Bevy and ORMs, which rely heavily on traits being implemented on tuples.
1756
+ fn lookup_alternative_tuple_impls (
1757
+ & self ,
1758
+ err : & mut Diag < ' _ > ,
1759
+ unsatisfied_predicates : & [ (
1760
+ ty:: Predicate < ' tcx > ,
1761
+ Option < ty:: Predicate < ' tcx > > ,
1762
+ Option < ObligationCause < ' tcx > > ,
1763
+ ) ] ,
1764
+ ) -> bool {
1765
+ let mut found_tuple = false ;
1766
+ for ( pred, root, _ob) in unsatisfied_predicates {
1767
+ let mut preds = vec ! [ pred] ;
1768
+ if let Some ( root) = root {
1769
+ // We will look at both the current predicate and the root predicate that caused it
1770
+ // to be needed. If calling something like `<(A, &B)>::default()`, then `pred` is
1771
+ // `&B: Default` and `root` is `(A, &B): Default`, which is the one we are checking
1772
+ // for further down, so we check both.
1773
+ preds. push ( root) ;
1774
+ }
1775
+ for pred in preds {
1776
+ if let Some ( clause) = pred. as_clause ( )
1777
+ && let Some ( clause) = clause. as_trait_clause ( )
1778
+ && let ty = clause. self_ty ( ) . skip_binder ( )
1779
+ && let ty:: Tuple ( types) = ty. kind ( )
1780
+ {
1781
+ let path = clause. skip_binder ( ) . trait_ref . print_only_trait_path ( ) ;
1782
+ let def_id = clause. def_id ( ) ;
1783
+ let ty = Ty :: new_tup (
1784
+ self . tcx ,
1785
+ self . tcx . mk_type_list_from_iter ( types. iter ( ) . map ( |ty| ty. peel_refs ( ) ) ) ,
1786
+ ) ;
1787
+ let args = ty:: GenericArgs :: for_item ( self . tcx , def_id, |param, _| {
1788
+ if param. index == 0 {
1789
+ ty. into ( )
1790
+ } else {
1791
+ self . infcx . var_for_def ( DUMMY_SP , param)
1792
+ }
1793
+ } ) ;
1794
+ if self
1795
+ . infcx
1796
+ . type_implements_trait ( def_id, args, self . param_env )
1797
+ . must_apply_modulo_regions ( )
1798
+ {
1799
+ // "`Trait` is implemented for `(A, B)` but not for `(A, &B)`"
1800
+ let mut msg = DiagStyledString :: normal ( format ! ( "`{path}` " ) ) ;
1801
+ msg. push_highlighted ( "is" ) ;
1802
+ msg. push_normal ( " implemented for `(" ) ;
1803
+ let len = types. len ( ) ;
1804
+ for ( i, t) in types. iter ( ) . enumerate ( ) {
1805
+ msg. push (
1806
+ format ! ( "{}" , with_forced_trimmed_paths!( t. peel_refs( ) ) ) ,
1807
+ t. peel_refs ( ) != t,
1808
+ ) ;
1809
+ if i < len - 1 {
1810
+ msg. push_normal ( ", " ) ;
1811
+ }
1812
+ }
1813
+ msg. push_normal ( ")` but " ) ;
1814
+ msg. push_highlighted ( "not" ) ;
1815
+ msg. push_normal ( " for `(" ) ;
1816
+ for ( i, t) in types. iter ( ) . enumerate ( ) {
1817
+ msg. push (
1818
+ format ! ( "{}" , with_forced_trimmed_paths!( t) ) ,
1819
+ t. peel_refs ( ) != t,
1820
+ ) ;
1821
+ if i < len - 1 {
1822
+ msg. push_normal ( ", " ) ;
1823
+ }
1824
+ }
1825
+ msg. push_normal ( ")`" ) ;
1826
+
1827
+ // Find the span corresponding to the impl that was found to point at it.
1828
+ if let Some ( impl_span) = self
1829
+ . tcx
1830
+ . all_impls ( def_id)
1831
+ . filter ( |& impl_def_id| {
1832
+ let header = self . tcx . impl_trait_header ( impl_def_id) . unwrap ( ) ;
1833
+ let trait_ref = header. trait_ref . instantiate (
1834
+ self . tcx ,
1835
+ self . infcx . fresh_args_for_item ( DUMMY_SP , impl_def_id) ,
1836
+ ) ;
1837
+
1838
+ let value = ty:: fold_regions ( self . tcx , ty, |_, _| {
1839
+ self . tcx . lifetimes . re_erased
1840
+ } ) ;
1841
+ // FIXME: Don't bother dealing with non-lifetime binders here...
1842
+ if value. has_escaping_bound_vars ( ) {
1843
+ return false ;
1844
+ }
1845
+ self . infcx . can_eq ( ty:: ParamEnv :: empty ( ) , trait_ref. self_ty ( ) , value)
1846
+ && header. polarity == ty:: ImplPolarity :: Positive
1847
+ } )
1848
+ . map ( |impl_def_id| self . tcx . def_span ( impl_def_id) )
1849
+ . next ( )
1850
+ {
1851
+ err. highlighted_span_note ( impl_span, msg. 0 ) ;
1852
+ } else {
1853
+ err. highlighted_note ( msg. 0 ) ;
1854
+ }
1855
+ found_tuple = true ;
1856
+ }
1857
+ // If `pred` was already on the tuple, we don't need to look at the root
1858
+ // obligation too.
1859
+ break ;
1860
+ }
1861
+ }
1862
+ }
1863
+ found_tuple
1864
+ }
1865
+
1747
1866
/// If an appropriate error source is not found, check method chain for possible candidates
1748
1867
fn lookup_segments_chain_for_no_match_method (
1749
1868
& self ,
0 commit comments