@@ -15,6 +15,7 @@ use rustc_data_structures::fx::FxHashMap;
1515use rustc_errors:: { pluralize, struct_span_err, Applicability , DiagnosticBuilder , ErrorReported } ;
1616use rustc_hir as hir;
1717use rustc_hir:: def_id:: { DefId , LOCAL_CRATE } ;
18+ use rustc_hir:: intravisit:: Visitor ;
1819use rustc_hir:: Node ;
1920use rustc_middle:: mir:: interpret:: ErrorHandled ;
2021use rustc_middle:: ty:: error:: ExpectedFound ;
@@ -25,7 +26,7 @@ use rustc_middle::ty::{
2526 TypeFoldable , WithConstness ,
2627} ;
2728use rustc_session:: DiagnosticMessageId ;
28- use rustc_span:: { ExpnKind , Span , DUMMY_SP } ;
29+ use rustc_span:: { ExpnKind , MultiSpan , Span , DUMMY_SP } ;
2930use std:: fmt;
3031
3132use crate :: traits:: query:: evaluate_obligation:: InferCtxtExt as _;
@@ -1695,36 +1696,95 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
16951696 err : & mut DiagnosticBuilder < ' tcx > ,
16961697 obligation : & PredicateObligation < ' tcx > ,
16971698 ) {
1698- if let (
1699- ty:: PredicateKind :: Trait ( pred, _) ,
1700- ObligationCauseCode :: BindingObligation ( item_def_id, span) ,
1701- ) = ( obligation. predicate . kind ( ) , & obligation. cause . code )
1702- {
1703- if let ( Some ( generics) , true ) = (
1704- self . tcx . hir ( ) . get_if_local ( * item_def_id) . as_ref ( ) . and_then ( |n| n. generics ( ) ) ,
1705- Some ( pred. def_id ( ) ) == self . tcx . lang_items ( ) . sized_trait ( ) ,
1706- ) {
1707- for param in generics. params {
1708- if param. span == * span
1709- && !param. bounds . iter ( ) . any ( |bound| {
1710- bound. trait_ref ( ) . and_then ( |trait_ref| trait_ref. trait_def_id ( ) )
1711- == self . tcx . lang_items ( ) . sized_trait ( )
1712- } )
1713- {
1714- let ( span, separator) = match param. bounds {
1715- [ ] => ( span. shrink_to_hi ( ) , ":" ) ,
1716- [ .., bound] => ( bound. span ( ) . shrink_to_hi ( ) , " +" ) ,
1717- } ;
1718- err. span_suggestion_verbose (
1719- span,
1720- "consider relaxing the implicit `Sized` restriction" ,
1721- format ! ( "{} ?Sized" , separator) ,
1722- Applicability :: MachineApplicable ,
1699+ let ( pred, item_def_id, span) =
1700+ match ( obligation. predicate . kind ( ) , & obligation. cause . code . peel_derives ( ) ) {
1701+ (
1702+ ty:: PredicateKind :: Trait ( pred, _) ,
1703+ ObligationCauseCode :: BindingObligation ( item_def_id, span) ,
1704+ ) => ( pred, item_def_id, span) ,
1705+ _ => return ,
1706+ } ;
1707+
1708+ let node = match (
1709+ self . tcx . hir ( ) . get_if_local ( * item_def_id) ,
1710+ Some ( pred. def_id ( ) ) == self . tcx . lang_items ( ) . sized_trait ( ) ,
1711+ ) {
1712+ ( Some ( node) , true ) => node,
1713+ _ => return ,
1714+ } ;
1715+ let generics = match node. generics ( ) {
1716+ Some ( generics) => generics,
1717+ None => return ,
1718+ } ;
1719+ for param in generics. params {
1720+ if param. span != * span
1721+ || param. bounds . iter ( ) . any ( |bound| {
1722+ bound. trait_ref ( ) . and_then ( |trait_ref| trait_ref. trait_def_id ( ) )
1723+ == self . tcx . lang_items ( ) . sized_trait ( )
1724+ } )
1725+ {
1726+ continue ;
1727+ }
1728+ match node {
1729+ hir:: Node :: Item (
1730+ item
1731+ @
1732+ hir:: Item {
1733+ kind :
1734+ hir:: ItemKind :: Enum ( ..)
1735+ | hir:: ItemKind :: Struct ( ..)
1736+ | hir:: ItemKind :: Union ( ..) ,
1737+ ..
1738+ } ,
1739+ ) => {
1740+ // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a
1741+ // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);`
1742+ // is not.
1743+ let mut visitor = FindTypeParam {
1744+ param : param. name . ident ( ) . name ,
1745+ invalid_spans : vec ! [ ] ,
1746+ nested : false ,
1747+ } ;
1748+ visitor. visit_item ( item) ;
1749+ if !visitor. invalid_spans . is_empty ( ) {
1750+ let mut multispan: MultiSpan = param. span . into ( ) ;
1751+ multispan. push_span_label (
1752+ param. span ,
1753+ format ! ( "this could be changed to `{}: ?Sized`..." , param. name. ident( ) ) ,
1754+ ) ;
1755+ for sp in visitor. invalid_spans {
1756+ multispan. push_span_label (
1757+ sp,
1758+ format ! (
1759+ "...if indirection was used here: `Box<{}>`" ,
1760+ param. name. ident( ) ,
1761+ ) ,
1762+ ) ;
1763+ }
1764+ err. span_help (
1765+ multispan,
1766+ & format ! (
1767+ "you could relax the implicit `Sized` bound on `{T}` if it were \
1768+ used through indirection like `&{T}` or `Box<{T}>`",
1769+ T = param. name. ident( ) ,
1770+ ) ,
17231771 ) ;
17241772 return ;
17251773 }
17261774 }
1775+ _ => { }
17271776 }
1777+ let ( span, separator) = match param. bounds {
1778+ [ ] => ( span. shrink_to_hi ( ) , ":" ) ,
1779+ [ .., bound] => ( bound. span ( ) . shrink_to_hi ( ) , " +" ) ,
1780+ } ;
1781+ err. span_suggestion_verbose (
1782+ span,
1783+ "consider relaxing the implicit `Sized` restriction" ,
1784+ format ! ( "{} ?Sized" , separator) ,
1785+ Applicability :: MachineApplicable ,
1786+ ) ;
1787+ return ;
17281788 }
17291789 }
17301790
@@ -1744,6 +1804,50 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
17441804 }
17451805}
17461806
1807+ /// Look for type `param` in an ADT being used only through a reference to confirm that suggesting
1808+ /// `param: ?Sized` would be a valid constraint.
1809+ struct FindTypeParam {
1810+ param : rustc_span:: Symbol ,
1811+ invalid_spans : Vec < Span > ,
1812+ nested : bool ,
1813+ }
1814+
1815+ impl < ' v > Visitor < ' v > for FindTypeParam {
1816+ type Map = rustc_hir:: intravisit:: ErasedMap < ' v > ;
1817+
1818+ fn nested_visit_map ( & mut self ) -> hir:: intravisit:: NestedVisitorMap < Self :: Map > {
1819+ hir:: intravisit:: NestedVisitorMap :: None
1820+ }
1821+
1822+ fn visit_ty ( & mut self , ty : & hir:: Ty < ' _ > ) {
1823+ // We collect the spans of all uses of the "bare" type param, like in `field: T` or
1824+ // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be
1825+ // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized`
1826+ // obligations like `Box<T>` and `Vec<T>`, but we perform no extra analysis for those cases
1827+ // and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors
1828+ // in that case should make what happened clear enough.
1829+ match ty. kind {
1830+ hir:: TyKind :: Ptr ( _) | hir:: TyKind :: Rptr ( ..) | hir:: TyKind :: TraitObject ( ..) => { }
1831+ hir:: TyKind :: Path ( hir:: QPath :: Resolved ( None , path) )
1832+ if path. segments . len ( ) == 1 && path. segments [ 0 ] . ident . name == self . param =>
1833+ {
1834+ if !self . nested {
1835+ self . invalid_spans . push ( ty. span ) ;
1836+ }
1837+ }
1838+ hir:: TyKind :: Path ( _) => {
1839+ let prev = self . nested ;
1840+ self . nested = true ;
1841+ hir:: intravisit:: walk_ty ( self , ty) ;
1842+ self . nested = prev;
1843+ }
1844+ _ => {
1845+ hir:: intravisit:: walk_ty ( self , ty) ;
1846+ }
1847+ }
1848+ }
1849+ }
1850+
17471851pub fn recursive_type_with_infinite_size_error (
17481852 tcx : TyCtxt < ' tcx > ,
17491853 type_def_id : DefId ,
0 commit comments