Skip to content

Commit 3716a3f

Browse files
Mention that type parameters are used recursively
1 parent 3de0a7c commit 3716a3f

File tree

9 files changed

+119
-49
lines changed

9 files changed

+119
-49
lines changed

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,10 @@ hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is no
382382
hir_analysis_precise_capture_self_alias = `Self` can't be captured in `use<...>` precise captures list, since it is an alias
383383
.label = `Self` is not a generic argument, but an alias to the type of the {$what}
384384
385+
hir_analysis_recursive_generic_parameter = {$param_def_kind} `{$param_name}` is only used recursively
386+
.label = {$param_def_kind} must be used non-recursively in the definition
387+
.note = all type parameters must be used in a non-recursive way in order to constrain its variance
388+
385389
hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}`
386390
.note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}`
387391
@@ -549,6 +553,7 @@ hir_analysis_unused_generic_parameter =
549553
{$param_def_kind} `{$param_name}` is never used
550554
.label = unused {$param_def_kind}
551555
.const_param_help = if you intended `{$param_name}` to be a const parameter, use `const {$param_name}: /* Type */` instead
556+
552557
hir_analysis_unused_generic_parameter_adt_help =
553558
consider removing `{$param_name}`, referring to it in a field, or using a marker such as `{$phantom_data}`
554559
hir_analysis_unused_generic_parameter_adt_no_phantom_data_help =

compiler/rustc_hir_analysis/src/check/wfcheck.rs

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ use crate::constrained_generic_params::{identify_constrained_generic_params, Par
44
use crate::errors;
55
use crate::fluent_generated as fluent;
66

7-
use hir::intravisit::Visitor;
7+
use hir::intravisit::{self, Visitor};
88
use rustc_ast as ast;
99
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
1010
use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, ErrorGuaranteed};
1111
use rustc_hir as hir;
12-
use rustc_hir::def::DefKind;
12+
use rustc_hir::def::{DefKind, Res};
1313
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
1414
use rustc_hir::lang_items::LangItem;
1515
use rustc_hir::ItemKind;
@@ -1799,7 +1799,7 @@ fn receiver_is_implemented<'tcx>(
17991799

18001800
fn 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,19 +1915,60 @@ fn report_bivariance(
19151915
item_kind => bug!("report_bivariance: unexpected item kind: {item_kind:?}"),
19161916
};
19171917

1918-
let const_param_help =
1919-
matches!(param.kind, hir::GenericParamKind::Type { .. } if !has_explicit_bounds)
1920-
.then_some(());
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+
);
19211923

1922-
let mut diag = tcx.dcx().create_err(errors::UnusedGenericParameter {
1923-
span: param.span,
1924-
param_name,
1925-
param_def_kind: tcx.def_descr(param.def_id.to_def_id()),
1926-
help,
1927-
const_param_help,
1928-
});
1929-
diag.code(E0392);
1930-
diag.emit()
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()
1948+
}
1949+
}
1950+
1951+
struct CollectUsageSpans<'a> {
1952+
spans: &'a mut Vec<Span>,
1953+
param_def_id: DefId,
1954+
}
1955+
1956+
impl<'tcx> Visitor<'tcx> for CollectUsageSpans<'_> {
1957+
type Result = ();
1958+
1959+
fn visit_generics(&mut self, _g: &'tcx rustc_hir::Generics<'tcx>) -> Self::Result {
1960+
// Skip the generics. We only care about fields, not where clause/param bounds.
1961+
}
1962+
1963+
fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) -> Self::Result {
1964+
if let hir::TyKind::Path(hir::QPath::Resolved(None, qpath)) = t.kind
1965+
&& let Res::Def(DefKind::TyParam, def_id) = qpath.res
1966+
&& def_id == self.param_def_id
1967+
{
1968+
self.spans.push(t.span);
1969+
}
1970+
intravisit::walk_ty(self, t);
1971+
}
19311972
}
19321973

19331974
impl<'tcx> WfCheckingCtxt<'_, 'tcx> {

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,6 +1603,21 @@ pub(crate) struct UnusedGenericParameter {
16031603
pub const_param_help: Option<()>,
16041604
}
16051605

1606+
#[derive(Diagnostic)]
1607+
#[diag(hir_analysis_recursive_generic_parameter)]
1608+
pub(crate) struct RecursiveGenericParameter {
1609+
#[primary_span]
1610+
pub spans: Vec<Span>,
1611+
#[label]
1612+
pub param_span: Span,
1613+
pub param_name: Ident,
1614+
pub param_def_kind: &'static str,
1615+
#[subdiagnostic]
1616+
pub help: UnusedGenericParameterHelp,
1617+
#[note]
1618+
pub note: (),
1619+
}
1620+
16061621
#[derive(Subdiagnostic)]
16071622
pub(crate) enum UnusedGenericParameterHelp {
16081623
#[help(hir_analysis_unused_generic_parameter_adt_help)]

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

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,27 @@ error[E0275]: overflow evaluating the requirement `Loop == _`
44
LL | impl Loop {}
55
| ^^^^
66

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

16-
error[E0392]: type parameter `T` is never used
17-
--> $DIR/inherent-impls-overflow.rs:17:12
18+
error: type parameter `T` is only used recursively
19+
--> $DIR/inherent-impls-overflow.rs:17:24
1820
|
1921
LL | type Poly1<T> = Poly0<(T,)>;
20-
| ^ unused type parameter
22+
| - ^
23+
| |
24+
| type parameter must be used non-recursively in the definition
2125
|
2226
= help: consider removing `T` or referring to it in the body of the type alias
23-
= help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
27+
= note: all type parameters must be used in a non-recursive way in order to constrain its variance
2428

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

3337
error: aborting due to 4 previous errors
3438

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

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

Lines changed: 2 additions & 2 deletions
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 never used
16+
//[next]~^^ ERROR type parameter `T` is only used recursively
1717
type Poly1<T> = Poly0<(T,)>;
1818
//[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>`
19-
//[next]~^^ ERROR type parameter `T` is never used
19+
//[next]~^^ ERROR type parameter `T` is only used recursively
2020

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

tests/ui/traits/issue-105231.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//~ ERROR overflow evaluating the requirement `A<A<A<A<A<A<A<...>>>>>>>: Send`
22
struct A<T>(B<T>);
33
//~^ ERROR recursive types `A` and `B` have infinite size
4-
//~| ERROR `T` is never used
4+
//~| ERROR `T` is only used recursively
55
struct B<T>(A<A<T>>);
6-
//~^ ERROR `T` is never used
6+
//~^ ERROR `T` is only used recursively
77
trait Foo {}
88
impl<T> Foo for T where T: Send {}
99
impl Foo for B<u8> {}

tests/ui/traits/issue-105231.stderr

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,27 @@ LL |
1515
LL ~ struct B<T>(Box<A<A<T>>>);
1616
|
1717

18-
error[E0392]: type parameter `T` is never used
19-
--> $DIR/issue-105231.rs:2:10
18+
error: type parameter `T` is only used recursively
19+
--> $DIR/issue-105231.rs:2:15
2020
|
2121
LL | struct A<T>(B<T>);
22-
| ^ unused type parameter
22+
| - ^
23+
| |
24+
| type parameter must be used non-recursively in the definition
2325
|
2426
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
25-
= help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
27+
= note: all type parameters must be used in a non-recursive way in order to constrain its variance
2628

27-
error[E0392]: type parameter `T` is never used
28-
--> $DIR/issue-105231.rs:5:10
29+
error: type parameter `T` is only used recursively
30+
--> $DIR/issue-105231.rs:5:17
2931
|
3032
LL | struct B<T>(A<A<T>>);
31-
| ^ unused type parameter
33+
| - ^
34+
| |
35+
| type parameter must be used non-recursively in the definition
3236
|
3337
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
34-
= help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
38+
= note: all type parameters must be used in a non-recursive way in order to constrain its variance
3539

3640
error[E0275]: overflow evaluating the requirement `A<A<A<A<A<A<A<...>>>>>>>: Send`
3741
|
@@ -44,5 +48,5 @@ LL | struct B<T>(A<A<T>>);
4448

4549
error: aborting due to 4 previous errors
4650

47-
Some errors have detailed explanations: E0072, E0275, E0392.
51+
Some errors have detailed explanations: E0072, E0275.
4852
For more information about an error, try `rustc --explain E0072`.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ enum SomeEnum<A> { Nothing }
1111

1212
// Here T might *appear* used, but in fact it isn't.
1313
enum ListCell<T> {
14-
//~^ ERROR parameter `T` is never used
1514
Cons(Box<ListCell<T>>),
15+
//~^ ERROR parameter `T` is only used recursively
1616
Nil
1717
}
1818

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@ LL | enum SomeEnum<A> { Nothing }
1616
= help: consider removing `A`, referring to it in a field, or using a marker such as `PhantomData`
1717
= help: if you intended `A` to be a const parameter, use `const A: /* Type */` instead
1818

19-
error[E0392]: type parameter `T` is never used
20-
--> $DIR/variance-unused-type-param.rs:13:15
19+
error: type parameter `T` is only used recursively
20+
--> $DIR/variance-unused-type-param.rs:14:23
2121
|
2222
LL | enum ListCell<T> {
23-
| ^ unused type parameter
23+
| - type parameter must be used non-recursively in the definition
24+
LL | Cons(Box<ListCell<T>>),
25+
| ^
2426
|
2527
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
26-
= help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
28+
= note: all type parameters must be used in a non-recursive way in order to constrain its variance
2729

2830
error[E0392]: type parameter `T` is never used
2931
--> $DIR/variance-unused-type-param.rs:19:19

0 commit comments

Comments
 (0)