Skip to content
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

Migrate a trait selection error to use diagnostic translation #114548

Merged
merged 1 commit into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 17 additions & 2 deletions compiler/rustc_middle/src/ty/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::fmt::Write;

use crate::query::Providers;
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, LangItem};
use rustc_span::def_id::LocalDefIdMap;
Expand Down Expand Up @@ -89,10 +90,18 @@ pub enum ClosureKind {
FnOnce,
}

impl<'tcx> ClosureKind {
impl ClosureKind {
/// This is the initial value used when doing upvar inference.
pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn;

pub const fn as_str(self) -> &'static str {
match self {
ClosureKind::Fn => "Fn",
ClosureKind::FnMut => "FnMut",
ClosureKind::FnOnce => "FnOnce",
}
}

/// Returns `true` if a type that impls this closure kind
/// must also implement `other`.
pub fn extends(self, other: ty::ClosureKind) -> bool {
Expand All @@ -115,7 +124,7 @@ impl<'tcx> ClosureKind {

/// Returns the representative scalar type for this closure kind.
/// See `Ty::to_opt_closure_kind` for more details.
pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
pub fn to_ty<'tcx>(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match self {
ClosureKind::Fn => tcx.types.i8,
ClosureKind::FnMut => tcx.types.i16,
Expand All @@ -124,6 +133,12 @@ impl<'tcx> ClosureKind {
}
}

impl IntoDiagnosticArg for ClosureKind {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(self.as_str().into())
}
}

/// A composite describing a `Place` that is captured by a closure.
#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
#[derive(TypeFoldable, TypeVisitable)]
Expand Down
6 changes: 1 addition & 5 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2870,11 +2870,7 @@ define_print_and_forward_display! {
}

ty::ClosureKind {
match *self {
ty::ClosureKind::Fn => p!("Fn"),
ty::ClosureKind::FnMut => p!("FnMut"),
ty::ClosureKind::FnOnce => p!("FnOnce"),
}
p!(write("{}", self.as_str()))
}

ty::Predicate<'tcx> {
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_trait_selection/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ trait_selection_adjust_signature_remove_borrow = consider adjusting the signatur
*[other] arguments
}

trait_selection_closure_fn_mut_label = closure is `FnMut` because it mutates the variable `{$place}` here

trait_selection_closure_fn_once_label = closure is `FnOnce` because it moves the variable `{$place}` out of its environment

trait_selection_closure_kind_mismatch = expected a closure that implements the `{$expected}` trait, but this closure only implements `{$found}`
.label = this closure implements `{$found}`, not `{$expected}`

trait_selection_closure_kind_requirement = the requirement to implement `{$expected}` derives from here

trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entries}

trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]`
Expand Down
36 changes: 35 additions & 1 deletion compiler/rustc_trait_selection/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use rustc_errors::{
SubdiagnosticMessage,
};
use rustc_macros::Diagnostic;
use rustc_middle::ty::{self, PolyTraitRef, Ty};
use rustc_middle::ty::{self, ClosureKind, PolyTraitRef, Ty};
use rustc_span::{Span, Symbol};

#[derive(Diagnostic)]
Expand Down Expand Up @@ -131,3 +131,37 @@ impl AddToDiagnostic for AdjustSignatureBorrow {
}
}
}

#[derive(Diagnostic)]
#[diag(trait_selection_closure_kind_mismatch, code = "E0525")]
pub struct ClosureKindMismatch {
#[primary_span]
#[label]
pub closure_span: Span,
pub expected: ClosureKind,
pub found: ClosureKind,
#[label(trait_selection_closure_kind_requirement)]
pub cause_span: Span,

#[subdiagnostic]
pub fn_once_label: Option<ClosureFnOnceLabel>,

#[subdiagnostic]
pub fn_mut_label: Option<ClosureFnMutLabel>,
}

#[derive(Subdiagnostic)]
#[label(trait_selection_closure_fn_once_label)]
pub struct ClosureFnOnceLabel {
#[primary_span]
pub span: Span,
pub place: String,
}

#[derive(Subdiagnostic)]
#[label(trait_selection_closure_fn_mut_label)]
pub struct ClosureFnMutLabel {
#[primary_span]
pub span: Span,
pub place: String,
}
50 changes: 17 additions & 33 deletions compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use super::{
ObligationCauseCode, ObligationCtxt, OutputTypeParameterMismatch, Overflow,
PredicateObligation, SelectionError, TraitNotObjectSafe,
};
use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch};
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use crate::infer::{self, InferCtxt};
Expand Down Expand Up @@ -3142,55 +3143,38 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
kind: ty::ClosureKind,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let closure_span = self.tcx.def_span(closure_def_id);
let mut err = struct_span_err!(
self.tcx.sess,
closure_span,
E0525,
"expected a closure that implements the `{}` trait, \
but this closure only implements `{}`",
kind,
found_kind
);

err.span_label(
let mut err = ClosureKindMismatch {
closure_span,
format!("this closure implements `{found_kind}`, not `{kind}`"),
);
err.span_label(
obligation.cause.span,
format!("the requirement to implement `{kind}` derives from here"),
);
expected: kind,
found: found_kind,
cause_span: obligation.cause.span,
fn_once_label: None,
fn_mut_label: None,
};

// Additional context information explaining why the closure only implements
// a particular trait.
if let Some(typeck_results) = &self.typeck_results {
let hir_id = self.tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local());
match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) {
(ty::ClosureKind::FnOnce, Some((span, place))) => {
err.span_label(
*span,
format!(
"closure is `FnOnce` because it moves the \
variable `{}` out of its environment",
ty::place_to_string_for_capture(self.tcx, place)
),
);
err.fn_once_label = Some(ClosureFnOnceLabel {
span: *span,
place: ty::place_to_string_for_capture(self.tcx, &place),
})
}
(ty::ClosureKind::FnMut, Some((span, place))) => {
err.span_label(
*span,
format!(
"closure is `FnMut` because it mutates the \
variable `{}` here",
ty::place_to_string_for_capture(self.tcx, place)
),
);
err.fn_mut_label = Some(ClosureFnMutLabel {
span: *span,
place: ty::place_to_string_for_capture(self.tcx, &place),
})
}
_ => {}
}
}

err
self.tcx.sess.create_err(err)
}

fn report_type_parameter_mismatch_cyclic_type_error(
Expand Down