@@ -14,7 +14,9 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
1414use rustc_data_structures:: sorted_map:: SortedMap ;
1515use rustc_data_structures:: unord:: UnordSet ;
1616use 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+ } ;
1820use rustc_hir:: def:: { CtorKind , DefKind , Res } ;
1921use rustc_hir:: def_id:: DefId ;
2022use rustc_hir:: intravisit:: { self , Visitor } ;
@@ -1569,7 +1571,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15691571 ) ;
15701572 }
15711573
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+ {
15731579 } else {
15741580 self . suggest_traits_to_import (
15751581 & mut err,
@@ -1744,6 +1750,119 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17441750 err. emit ( )
17451751 }
17461752
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+
17471866 /// If an appropriate error source is not found, check method chain for possible candidates
17481867 fn lookup_segments_chain_for_no_match_method (
17491868 & self ,
0 commit comments