Skip to content

Commit c97222a

Browse files
authored
Unrolled build for rust-lang#127871
Rollup merge of rust-lang#127871 - compiler-errors:recursive, r=estebank Mention that type parameters are used recursively on bivariance error Right now when a type parameter is used recursively, even with indirection (so it has a finite size) we say that the type parameter is unused: ``` struct B<T>(Box<B<T>>); ``` This is confusing, because the type parameter is *used*, it just doesn't have its variance constrained. This PR tweaks that message to mention that it must be used *non-recursively*. Not sure if we should actually mention "variance" here, but also I'd somewhat prefer we don't keep the power users in the dark w.r.t the real underlying issue, which is that the variance isn't constrained. That technical detail is reserved for a note, though. cc `@fee1-dead` Fixes rust-lang#118976 Fixes rust-lang#26283 Fixes rust-lang#53191 Fixes rust-lang#105740 Fixes rust-lang#110466
2 parents 5affbb1 + c02d0de commit c97222a

File tree

9 files changed

+211
-31
lines changed

9 files changed

+211
-31
lines changed

compiler/rustc_hir_analysis/messages.ftl

+6
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 their 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,8 @@ 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+
.usage_spans = `{$param_name}` is named here, but is likely unused in the containing type
557+
552558
hir_analysis_unused_generic_parameter_adt_help =
553559
consider removing `{$param_name}`, referring to it in a field, or using a marker such as `{$phantom_data}`
554560
hir_analysis_unused_generic_parameter_adt_no_phantom_data_help =

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

+124-9
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,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+
19332048
impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
19342049
/// Feature gates RFC 2056 -- trivial bounds, checking for global bounds that
19352050
/// aren't true.

compiler/rustc_hir_analysis/src/errors.rs

+17
Original file line numberDiff line numberDiff line change
@@ -1597,12 +1597,29 @@ 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)]
16031605
pub const_param_help: Option<()>,
16041606
}
16051607

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

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

+6-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ error[E0392]: type parameter `T` is never used
88
--> $DIR/inherent-impls-overflow.rs:14:12
99
|
1010
LL | type Poly0<T> = Poly1<(T,)>;
11-
| ^ unused type parameter
11+
| ^ - `T` is named here, but is likely unused in the containing type
12+
| |
13+
| unused type parameter
1214
|
1315
= help: consider removing `T` or referring to it in the body of the type alias
1416
= help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
@@ -17,7 +19,9 @@ error[E0392]: type parameter `T` is never used
1719
--> $DIR/inherent-impls-overflow.rs:17:12
1820
|
1921
LL | type Poly1<T> = Poly0<(T,)>;
20-
| ^ unused type parameter
22+
| ^ - `T` is named here, but is likely unused in the containing type
23+
| |
24+
| unused type parameter
2125
|
2226
= help: consider removing `T` or referring to it in the body of the type alias
2327
= help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead

tests/ui/traits/issue-105231.rs

+2-2
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

+13-9
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 their 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 their 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

+9-1
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ 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

19+
struct SelfTyAlias<T>(Box<Self>);
20+
//~^ ERROR parameter `T` is only used recursively
21+
1922
struct WithBounds<T: Sized> {}
2023
//~^ ERROR parameter `T` is never used
2124

@@ -25,4 +28,9 @@ struct WithWhereBounds<T> where T: Sized {}
2528
struct WithOutlivesBounds<T: 'static> {}
2629
//~^ ERROR parameter `T` is never used
2730

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

0 commit comments

Comments
 (0)