Skip to content

Mention that type parameters are used recursively on bivariance error #127871

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,10 @@ hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is no
hir_analysis_precise_capture_self_alias = `Self` can't be captured in `use<...>` precise captures list, since it is an alias
.label = `Self` is not a generic argument, but an alias to the type of the {$what}

hir_analysis_recursive_generic_parameter = {$param_def_kind} `{$param_name}` is only used recursively
.label = {$param_def_kind} must be used non-recursively in the definition
.note = all type parameters must be used in a non-recursive way in order to constrain its variance

hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}`
.note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}`

Expand Down Expand Up @@ -549,6 +553,7 @@ hir_analysis_unused_generic_parameter =
{$param_def_kind} `{$param_name}` is never used
.label = unused {$param_def_kind}
.const_param_help = if you intended `{$param_name}` to be a const parameter, use `const {$param_name}: /* Type */` instead

hir_analysis_unused_generic_parameter_adt_help =
consider removing `{$param_name}`, referring to it in a field, or using a marker such as `{$phantom_data}`
hir_analysis_unused_generic_parameter_adt_no_phantom_data_help =
Expand Down
83 changes: 62 additions & 21 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ use crate::constrained_generic_params::{identify_constrained_generic_params, Par
use crate::errors;
use crate::fluent_generated as fluent;

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

fn check_variances_for_type_defn<'tcx>(
tcx: TyCtxt<'tcx>,
item: &hir::Item<'tcx>,
item: &'tcx hir::Item<'tcx>,
hir_generics: &hir::Generics<'tcx>,
) {
let identity_args = ty::GenericArgs::identity_for_item(tcx, item.owner_id);
Expand Down Expand Up @@ -1886,21 +1886,21 @@ fn check_variances_for_type_defn<'tcx>(
hir::ParamName::Error => {}
_ => {
let has_explicit_bounds = explicitly_bounded_params.contains(&parameter);
report_bivariance(tcx, hir_param, has_explicit_bounds, item.kind);
report_bivariance(tcx, hir_param, has_explicit_bounds, item);
}
}
}
}

fn report_bivariance(
tcx: TyCtxt<'_>,
param: &rustc_hir::GenericParam<'_>,
fn report_bivariance<'tcx>(
tcx: TyCtxt<'tcx>,
param: &'tcx hir::GenericParam<'tcx>,
has_explicit_bounds: bool,
item_kind: ItemKind<'_>,
item: &'tcx hir::Item<'tcx>,
) -> ErrorGuaranteed {
let param_name = param.name.ident();

let help = match item_kind {
let help = match item.kind {
ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => {
if let Some(def_id) = tcx.lang_items().phantom_data() {
errors::UnusedGenericParameterHelp::Adt {
Expand All @@ -1915,19 +1915,60 @@ fn report_bivariance(
item_kind => bug!("report_bivariance: unexpected item kind: {item_kind:?}"),
};

let const_param_help =
matches!(param.kind, hir::GenericParamKind::Type { .. } if !has_explicit_bounds)
.then_some(());
let mut usage_spans = vec![];
intravisit::walk_item(
&mut CollectUsageSpans { spans: &mut usage_spans, param_def_id: param.def_id.to_def_id() },
item,
);

let mut diag = tcx.dcx().create_err(errors::UnusedGenericParameter {
span: param.span,
param_name,
param_def_kind: tcx.def_descr(param.def_id.to_def_id()),
help,
const_param_help,
});
diag.code(E0392);
diag.emit()
if usage_spans.is_empty() {
let const_param_help =
matches!(param.kind, hir::GenericParamKind::Type { .. } if !has_explicit_bounds)
.then_some(());

let mut diag = tcx.dcx().create_err(errors::UnusedGenericParameter {
span: param.span,
param_name,
param_def_kind: tcx.def_descr(param.def_id.to_def_id()),
help,
const_param_help,
});
diag.code(E0392);
diag.emit()
} else {
let diag = tcx.dcx().create_err(errors::RecursiveGenericParameter {
spans: usage_spans,
param_span: param.span,
param_name,
param_def_kind: tcx.def_descr(param.def_id.to_def_id()),
help,
note: (),
});
diag.emit()
}
}

struct CollectUsageSpans<'a> {
spans: &'a mut Vec<Span>,
param_def_id: DefId,
}

impl<'tcx> Visitor<'tcx> for CollectUsageSpans<'_> {
type Result = ();

fn visit_generics(&mut self, _g: &'tcx rustc_hir::Generics<'tcx>) -> Self::Result {
// Skip the generics. We only care about fields, not where clause/param bounds.
}

fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) -> Self::Result {
if let hir::TyKind::Path(hir::QPath::Resolved(None, qpath)) = t.kind
&& let Res::Def(DefKind::TyParam, def_id) = qpath.res
&& def_id == self.param_def_id
{
self.spans.push(t.span);
}
intravisit::walk_ty(self, t);
}
}

impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
Expand Down
15 changes: 15 additions & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1603,6 +1603,21 @@ pub(crate) struct UnusedGenericParameter {
pub const_param_help: Option<()>,
}

#[derive(Diagnostic)]
#[diag(hir_analysis_recursive_generic_parameter)]
pub(crate) struct RecursiveGenericParameter {
#[primary_span]
pub spans: Vec<Span>,
#[label]
pub param_span: Span,
pub param_name: Ident,
pub param_def_kind: &'static str,
#[subdiagnostic]
pub help: UnusedGenericParameterHelp,
#[note]
pub note: (),
}

#[derive(Subdiagnostic)]
pub(crate) enum UnusedGenericParameterHelp {
#[help(hir_analysis_unused_generic_parameter_adt_help)]
Expand Down
23 changes: 13 additions & 10 deletions tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,27 @@ error[E0275]: overflow evaluating the requirement `Loop == _`
LL | impl Loop {}
| ^^^^

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

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

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

error: aborting due to 4 previous errors

Some errors have detailed explanations: E0275, E0392.
For more information about an error, try `rustc --explain E0275`.
For more information about this error, try `rustc --explain E0275`.
4 changes: 2 additions & 2 deletions tests/ui/lazy-type-alias/inherent-impls-overflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ impl Loop {}

type Poly0<T> = Poly1<(T,)>;
//[current]~^ ERROR overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>`
//[next]~^^ ERROR type parameter `T` is never used
//[next]~^^ ERROR type parameter `T` is only used recursively
type Poly1<T> = Poly0<(T,)>;
//[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>`
//[next]~^^ ERROR type parameter `T` is never used
//[next]~^^ ERROR type parameter `T` is only used recursively

impl Poly0<()> {}
//[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>`
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/traits/issue-105231.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//~ ERROR overflow evaluating the requirement `A<A<A<A<A<A<A<...>>>>>>>: Send`
struct A<T>(B<T>);
//~^ ERROR recursive types `A` and `B` have infinite size
//~| ERROR `T` is never used
//~| ERROR `T` is only used recursively
struct B<T>(A<A<T>>);
//~^ ERROR `T` is never used
//~^ ERROR `T` is only used recursively
trait Foo {}
impl<T> Foo for T where T: Send {}
impl Foo for B<u8> {}
Expand Down
22 changes: 13 additions & 9 deletions tests/ui/traits/issue-105231.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,27 @@ LL |
LL ~ struct B<T>(Box<A<A<T>>>);
|

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

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

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

error: aborting due to 4 previous errors

Some errors have detailed explanations: E0072, E0275, E0392.
Some errors have detailed explanations: E0072, E0275.
For more information about an error, try `rustc --explain E0072`.
2 changes: 1 addition & 1 deletion tests/ui/variance/variance-unused-type-param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ enum SomeEnum<A> { Nothing }

// Here T might *appear* used, but in fact it isn't.
enum ListCell<T> {
//~^ ERROR parameter `T` is never used
Cons(Box<ListCell<T>>),
//~^ ERROR parameter `T` is only used recursively
Nil
}

Expand Down
10 changes: 6 additions & 4 deletions tests/ui/variance/variance-unused-type-param.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ LL | enum SomeEnum<A> { Nothing }
= help: consider removing `A`, referring to it in a field, or using a marker such as `PhantomData`
= help: if you intended `A` to be a const parameter, use `const A: /* Type */` instead

error[E0392]: type parameter `T` is never used
--> $DIR/variance-unused-type-param.rs:13:15
error: type parameter `T` is only used recursively
--> $DIR/variance-unused-type-param.rs:14:23
|
LL | enum ListCell<T> {
| ^ unused type parameter
| - type parameter must be used non-recursively in the definition
LL | Cons(Box<ListCell<T>>),
| ^
Comment on lines +23 to +25
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notably, the primary span is now the usage, not the definition. We could put that back, I guess.

|
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
= help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
= note: all type parameters must be used in a non-recursive way in order to constrain its variance

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