@@ -4,12 +4,12 @@ use crate::constrained_generic_params::{identify_constrained_generic_params, Par
44use crate :: errors;
55use crate :: fluent_generated as fluent;
66
7- use hir:: intravisit:: Visitor ;
7+ use hir:: intravisit:: { self , Visitor } ;
88use rustc_ast as ast;
99use rustc_data_structures:: fx:: { FxHashMap , FxHashSet , FxIndexSet } ;
1010use rustc_errors:: { codes:: * , pluralize, struct_span_code_err, Applicability , ErrorGuaranteed } ;
1111use rustc_hir as hir;
12- use rustc_hir:: def:: DefKind ;
12+ use rustc_hir:: def:: { DefKind , Res } ;
1313use rustc_hir:: def_id:: { DefId , LocalDefId , LocalModDefId } ;
1414use rustc_hir:: lang_items:: LangItem ;
1515use rustc_hir:: ItemKind ;
@@ -1799,7 +1799,7 @@ fn receiver_is_implemented<'tcx>(
17991799
18001800fn check_variances_for_type_defn < ' tcx > (
18011801 tcx : TyCtxt < ' tcx > ,
1802- item : & hir:: Item < ' tcx > ,
1802+ item : & ' tcx hir:: Item < ' tcx > ,
18031803 hir_generics : & hir:: Generics < ' tcx > ,
18041804) {
18051805 let identity_args = ty:: GenericArgs :: identity_for_item ( tcx, item. owner_id ) ;
@@ -1886,21 +1886,21 @@ fn check_variances_for_type_defn<'tcx>(
18861886 hir:: ParamName :: Error => { }
18871887 _ => {
18881888 let has_explicit_bounds = explicitly_bounded_params. contains ( & parameter) ;
1889- report_bivariance ( tcx, hir_param, has_explicit_bounds, item. kind ) ;
1889+ report_bivariance ( tcx, hir_param, has_explicit_bounds, item) ;
18901890 }
18911891 }
18921892 }
18931893}
18941894
1895- fn report_bivariance (
1896- tcx : TyCtxt < ' _ > ,
1897- param : & rustc_hir :: GenericParam < ' _ > ,
1895+ fn report_bivariance < ' tcx > (
1896+ tcx : TyCtxt < ' tcx > ,
1897+ param : & ' tcx hir :: GenericParam < ' tcx > ,
18981898 has_explicit_bounds : bool ,
1899- item_kind : ItemKind < ' _ > ,
1899+ item : & ' tcx hir :: Item < ' tcx > ,
19001900) -> ErrorGuaranteed {
19011901 let param_name = param. name . ident ( ) ;
19021902
1903- let help = match item_kind {
1903+ let help = match item . kind {
19041904 ItemKind :: Enum ( ..) | ItemKind :: Struct ( ..) | ItemKind :: Union ( ..) => {
19051905 if let Some ( def_id) = tcx. lang_items ( ) . phantom_data ( ) {
19061906 errors:: UnusedGenericParameterHelp :: Adt {
@@ -1915,6 +1915,49 @@ fn report_bivariance(
19151915 item_kind => bug ! ( "report_bivariance: unexpected item kind: {item_kind:?}" ) ,
19161916 } ;
19171917
1918+ let mut usage_spans = vec ! [ ] ;
1919+ intravisit:: walk_item (
1920+ & mut CollectUsageSpans { spans : & mut usage_spans, param_def_id : param. def_id . to_def_id ( ) } ,
1921+ item,
1922+ ) ;
1923+
1924+ if !usage_spans. is_empty ( ) {
1925+ // First, check if the ADT is (probably) cyclical. We say probably here, since
1926+ // we're not actually looking into substitutions, just walking through fields.
1927+ // And we only recurse into the fields of ADTs, and not the hidden types of
1928+ // opaques or anything else fancy.
1929+ let item_def_id = item. owner_id . to_def_id ( ) ;
1930+ let is_probably_cyclical = if matches ! (
1931+ tcx. def_kind( item_def_id) ,
1932+ DefKind :: Struct | DefKind :: Union | DefKind :: Enum
1933+ ) {
1934+ IsProbablyCyclical { tcx, adt_def_id : item_def_id, seen : Default :: default ( ) }
1935+ . visit_all_fields ( tcx. adt_def ( item_def_id) )
1936+ . is_break ( )
1937+ } else {
1938+ false
1939+ } ;
1940+ // If the ADT is cyclical, then if at least one usage of the type parameter or
1941+ // the `Self` alias is present in the, then it's probably a cyclical struct, and
1942+ // we should call those parameter usages recursive rather than just saying they're
1943+ // unused...
1944+ //
1945+ // We currently report *all* of the parameter usages, since computing the exact
1946+ // subset is very involved, and the fact we're mentioning recursion at all is
1947+ // likely to guide the user in the right direction.
1948+ if is_probably_cyclical {
1949+ let diag = tcx. dcx ( ) . create_err ( errors:: RecursiveGenericParameter {
1950+ spans : usage_spans,
1951+ param_span : param. span ,
1952+ param_name,
1953+ param_def_kind : tcx. def_descr ( param. def_id . to_def_id ( ) ) ,
1954+ help,
1955+ note : ( ) ,
1956+ } ) ;
1957+ return diag. emit ( ) ;
1958+ }
1959+ }
1960+
19181961 let const_param_help =
19191962 matches ! ( param. kind, hir:: GenericParamKind :: Type { .. } if !has_explicit_bounds)
19201963 . then_some ( ( ) ) ;
@@ -1923,13 +1966,85 @@ fn report_bivariance(
19231966 span : param. span ,
19241967 param_name,
19251968 param_def_kind : tcx. def_descr ( param. def_id . to_def_id ( ) ) ,
1969+ usage_spans,
19261970 help,
19271971 const_param_help,
19281972 } ) ;
19291973 diag. code ( E0392 ) ;
19301974 diag. emit ( )
19311975}
19321976
1977+ /// Detects cases where an ADT is trivially cyclical -- we want to detect this so
1978+ /// /we only mention that its parameters are used cyclically if the ADT is truly
1979+ /// cyclical.
1980+ ///
1981+ /// Notably, we don't consider substitutions here, so this may have false positives.
1982+ struct IsProbablyCyclical < ' tcx > {
1983+ tcx : TyCtxt < ' tcx > ,
1984+ adt_def_id : DefId ,
1985+ seen : FxHashSet < DefId > ,
1986+ }
1987+
1988+ impl < ' tcx > IsProbablyCyclical < ' tcx > {
1989+ fn visit_all_fields ( & mut self , adt_def : ty:: AdtDef < ' tcx > ) -> ControlFlow < ( ) , ( ) > {
1990+ for field in adt_def. all_fields ( ) {
1991+ self . tcx . type_of ( field. did ) . instantiate_identity ( ) . visit_with ( self ) ?;
1992+ }
1993+
1994+ ControlFlow :: Continue ( ( ) )
1995+ }
1996+ }
1997+
1998+ impl < ' tcx > TypeVisitor < TyCtxt < ' tcx > > for IsProbablyCyclical < ' tcx > {
1999+ type Result = ControlFlow < ( ) , ( ) > ;
2000+
2001+ fn visit_ty ( & mut self , t : Ty < ' tcx > ) -> ControlFlow < ( ) , ( ) > {
2002+ if let Some ( adt_def) = t. ty_adt_def ( ) {
2003+ if adt_def. did ( ) == self . adt_def_id {
2004+ return ControlFlow :: Break ( ( ) ) ;
2005+ }
2006+
2007+ if self . seen . insert ( adt_def. did ( ) ) {
2008+ self . visit_all_fields ( adt_def) ?;
2009+ }
2010+ }
2011+
2012+ t. super_visit_with ( self )
2013+ }
2014+ }
2015+
2016+ /// Collect usages of the `param_def_id` and `Res::SelfTyAlias` in the HIR.
2017+ ///
2018+ /// This is used to report places where the user has used parameters in a
2019+ /// non-variance-constraining way for better bivariance errors.
2020+ struct CollectUsageSpans < ' a > {
2021+ spans : & ' a mut Vec < Span > ,
2022+ param_def_id : DefId ,
2023+ }
2024+
2025+ impl < ' tcx > Visitor < ' tcx > for CollectUsageSpans < ' _ > {
2026+ type Result = ( ) ;
2027+
2028+ fn visit_generics ( & mut self , _g : & ' tcx rustc_hir:: Generics < ' tcx > ) -> Self :: Result {
2029+ // Skip the generics. We only care about fields, not where clause/param bounds.
2030+ }
2031+
2032+ fn visit_ty ( & mut self , t : & ' tcx hir:: Ty < ' tcx > ) -> Self :: Result {
2033+ if let hir:: TyKind :: Path ( hir:: QPath :: Resolved ( None , qpath) ) = t. kind {
2034+ if let Res :: Def ( DefKind :: TyParam , def_id) = qpath. res
2035+ && def_id == self . param_def_id
2036+ {
2037+ self . spans . push ( t. span ) ;
2038+ return ;
2039+ } else if let Res :: SelfTyAlias { .. } = qpath. res {
2040+ self . spans . push ( t. span ) ;
2041+ return ;
2042+ }
2043+ }
2044+ intravisit:: walk_ty ( self , t) ;
2045+ }
2046+ }
2047+
19332048impl < ' tcx > WfCheckingCtxt < ' _ , ' tcx > {
19342049 /// Feature gates RFC 2056 -- trivial bounds, checking for global bounds that
19352050 /// aren't true.
0 commit comments