Skip to content

Commit c02d0de

Browse files
Account for structs that have unused params in nested types in fields
1 parent a0a251a commit c02d0de

File tree

8 files changed

+129
-38
lines changed

8 files changed

+129
-38
lines changed

compiler/rustc_hir_analysis/messages.ftl

+1
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,7 @@ hir_analysis_unused_generic_parameter =
553553
{$param_def_kind} `{$param_name}` is never used
554554
.label = unused {$param_def_kind}
555555
.const_param_help = if you intended `{$param_name}` to be a const parameter, use `const {$param_name}: /* Type */` instead
556+
.usage_spans = `{$param_name}` is named here, but is likely unused in the containing type
556557
557558
hir_analysis_unused_generic_parameter_adt_help =
558559
consider removing `{$param_name}`, referring to it in a field, or using a marker such as `{$phantom_data}`

compiler/rustc_hir_analysis/src/check/check.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1572,6 +1572,7 @@ fn check_type_alias_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalD
15721572
param_name,
15731573
param_def_kind: tcx.def_descr(param.def_id),
15741574
help: errors::UnusedGenericParameterHelp::TyAlias { param_name },
1575+
usage_spans: vec![],
15751576
const_param_help,
15761577
});
15771578
diag.code(E0091);

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+93-24
Original file line numberDiff line numberDiff line change
@@ -1921,33 +1921,102 @@ fn report_bivariance<'tcx>(
19211921
item,
19221922
);
19231923

1924-
if usage_spans.is_empty() {
1925-
let const_param_help =
1926-
matches!(param.kind, hir::GenericParamKind::Type { .. } if !has_explicit_bounds)
1927-
.then_some(());
1928-
1929-
let mut diag = tcx.dcx().create_err(errors::UnusedGenericParameter {
1930-
span: param.span,
1931-
param_name,
1932-
param_def_kind: tcx.def_descr(param.def_id.to_def_id()),
1933-
help,
1934-
const_param_help,
1935-
});
1936-
diag.code(E0392);
1937-
diag.emit()
1938-
} else {
1939-
let diag = tcx.dcx().create_err(errors::RecursiveGenericParameter {
1940-
spans: usage_spans,
1941-
param_span: param.span,
1942-
param_name,
1943-
param_def_kind: tcx.def_descr(param.def_id.to_def_id()),
1944-
help,
1945-
note: (),
1946-
});
1947-
diag.emit()
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+
1961+
let const_param_help =
1962+
matches!(param.kind, hir::GenericParamKind::Type { .. } if !has_explicit_bounds)
1963+
.then_some(());
1964+
1965+
let mut diag = tcx.dcx().create_err(errors::UnusedGenericParameter {
1966+
span: param.span,
1967+
param_name,
1968+
param_def_kind: tcx.def_descr(param.def_id.to_def_id()),
1969+
usage_spans,
1970+
help,
1971+
const_param_help,
1972+
});
1973+
diag.code(E0392);
1974+
diag.emit()
1975+
}
1976+
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(())
19481995
}
19491996
}
19501997

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.
19512020
struct CollectUsageSpans<'a> {
19522021
spans: &'a mut Vec<Span>,
19532022
param_def_id: DefId,

compiler/rustc_hir_analysis/src/errors.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1597,6 +1597,8 @@ pub(crate) struct UnusedGenericParameter {
15971597
pub span: Span,
15981598
pub param_name: Ident,
15991599
pub param_def_kind: &'static str,
1600+
#[label(hir_analysis_usage_spans)]
1601+
pub usage_spans: Vec<Span>,
16001602
#[subdiagnostic]
16011603
pub help: UnusedGenericParameterHelp,
16021604
#[help(hir_analysis_const_param_help)]

tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr

+12-11
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,27 @@ error[E0275]: overflow evaluating the requirement `Loop == _`
44
LL | impl Loop {}
55
| ^^^^
66

7-
error: type parameter `T` is only used recursively
8-
--> $DIR/inherent-impls-overflow.rs:14:24
7+
error[E0392]: type parameter `T` is never used
8+
--> $DIR/inherent-impls-overflow.rs:14:12
99
|
1010
LL | type Poly0<T> = Poly1<(T,)>;
11-
| - ^
11+
| ^ - `T` is named here, but is likely unused in the containing type
1212
| |
13-
| type parameter must be used non-recursively in the definition
13+
| unused type parameter
1414
|
1515
= help: consider removing `T` or referring to it in the body of the type alias
16-
= note: all type parameters must be used in a non-recursive way in order to constrain their variance
16+
= help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
1717

18-
error: type parameter `T` is only used recursively
19-
--> $DIR/inherent-impls-overflow.rs:17:24
18+
error[E0392]: type parameter `T` is never used
19+
--> $DIR/inherent-impls-overflow.rs:17:12
2020
|
2121
LL | type Poly1<T> = Poly0<(T,)>;
22-
| - ^
22+
| ^ - `T` is named here, but is likely unused in the containing type
2323
| |
24-
| type parameter must be used non-recursively in the definition
24+
| unused type parameter
2525
|
2626
= help: consider removing `T` or referring to it in the body of the type alias
27-
= note: all type parameters must be used in a non-recursive way in order to constrain their variance
27+
= help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
2828

2929
error[E0275]: overflow evaluating the requirement `Poly0<()> == _`
3030
--> $DIR/inherent-impls-overflow.rs:21:6
@@ -36,4 +36,5 @@ LL | impl Poly0<()> {}
3636

3737
error: aborting due to 4 previous errors
3838

39-
For more information about this error, try `rustc --explain E0275`.
39+
Some errors have detailed explanations: E0275, E0392.
40+
For more information about an error, try `rustc --explain E0275`.

tests/ui/lazy-type-alias/inherent-impls-overflow.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ impl Loop {}
1313

1414
type Poly0<T> = Poly1<(T,)>;
1515
//[current]~^ ERROR overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>`
16-
//[next]~^^ ERROR type parameter `T` is only used recursively
16+
//[next]~^^ ERROR type parameter `T` is never used
1717
type Poly1<T> = Poly0<(T,)>;
1818
//[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>`
19-
//[next]~^^ ERROR type parameter `T` is only used recursively
19+
//[next]~^^ ERROR type parameter `T` is never used
2020

2121
impl Poly0<()> {}
2222
//[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>`

tests/ui/variance/variance-unused-type-param.rs

+5
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,9 @@ struct WithWhereBounds<T> where T: Sized {}
2828
struct WithOutlivesBounds<T: 'static> {}
2929
//~^ ERROR parameter `T` is never used
3030

31+
struct DoubleNothing<T> {
32+
//~^ ERROR parameter `T` is never used
33+
s: SomeStruct<T>,
34+
}
35+
3136
fn main() {}

tests/ui/variance/variance-unused-type-param.stderr

+13-1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ LL | struct WithOutlivesBounds<T: 'static> {}
6262
|
6363
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
6464

65-
error: aborting due to 7 previous errors
65+
error[E0392]: type parameter `T` is never used
66+
--> $DIR/variance-unused-type-param.rs:31:22
67+
|
68+
LL | struct DoubleNothing<T> {
69+
| ^ unused type parameter
70+
LL |
71+
LL | s: SomeStruct<T>,
72+
| - `T` is named here, but is likely unused in the containing type
73+
|
74+
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
75+
= help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
76+
77+
error: aborting due to 8 previous errors
6678

6779
For more information about this error, try `rustc --explain E0392`.

0 commit comments

Comments
 (0)