diff options
Diffstat (limited to '')
9 files changed, 586 insertions, 497 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 20864c657..eb5afe828 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -58,14 +58,16 @@ use crate::traits::{ }; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed}; +use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg}; use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan}; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::Node; use rustc_middle::dep_graph::DepContext; use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::relate::{self, RelateResult, TypeRelation}; use rustc_middle::ty::{ self, error::TypeError, Binder, List, Region, Subst, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, @@ -77,7 +79,7 @@ use std::{cmp, fmt, iter}; mod note; -mod need_type_info; +pub(crate) mod need_type_info; pub use need_type_info::TypeAnnotationNeeded; pub mod nice_region_error; @@ -95,11 +97,6 @@ pub(super) fn note_and_explain_region<'tcx>( msg_span_from_free_region(tcx, region, alt_span) } - ty::ReEmpty(ty::UniverseIndex::ROOT) => ("the empty lifetime".to_owned(), alt_span), - - // uh oh, hope no user ever sees THIS - ty::ReEmpty(ui) => (format!("the empty lifetime in universe {:?}", ui), alt_span), - ty::RePlaceholder(_) => return, // FIXME(#13998) RePlaceholder should probably print like @@ -138,8 +135,6 @@ fn msg_span_from_free_region<'tcx>( (msg, Some(span)) } ty::ReStatic => ("the static lifetime".to_owned(), alt_span), - ty::ReEmpty(ty::UniverseIndex::ROOT) => ("an empty lifetime".to_owned(), alt_span), - ty::ReEmpty(ui) => (format!("an empty lifetime in universe {:?}", ui), alt_span), _ => bug!("{:?}", region), } } @@ -249,17 +244,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( // Explain the region we are capturing. match *hidden_region { - ty::ReEmpty(ty::UniverseIndex::ROOT) => { - // All lifetimes shorter than the function body are `empty` in - // lexical region resolution. The default explanation of "an empty - // lifetime" isn't really accurate here. - let message = format!( - "hidden type `{}` captures lifetime smaller than the function body", - hidden_ty - ); - err.span_note(span, &message); - } - ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty(_) => { + ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => { // Assuming regionck succeeded (*), we ought to always be // capturing *some* region from the fn header, and hence it // ought to be free. So under normal circumstances, we will go @@ -385,7 +370,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { RegionResolutionError::UpperBoundUniverseConflict( _, _, - var_universe, + _, sup_origin, sup_r, ) => { @@ -396,7 +381,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // placeholder. In practice, we expect more // tailored errors that don't really use this // value. - let sub_r = self.tcx.mk_region(ty::ReEmpty(var_universe)); + let sub_r = self.tcx.lifetimes.re_erased; self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit(); } @@ -457,7 +442,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } /// Adds a note if the types come from similarly named crates - fn check_and_note_conflicting_crates(&self, err: &mut Diagnostic, terr: &TypeError<'tcx>) { + fn check_and_note_conflicting_crates(&self, err: &mut Diagnostic, terr: TypeError<'tcx>) { use hir::def_id::CrateNum; use rustc_hir::definitions::DisambiguatedDefPathData; use ty::print::Printer; @@ -561,7 +546,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } }; - match *terr { + match terr { TypeError::Sorts(ref exp_found) => { // if they are both "path types", there's a chance of ambiguity // due to different versions of the same crate @@ -583,7 +568,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err: &mut Diagnostic, cause: &ObligationCause<'tcx>, exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>, - terr: &TypeError<'tcx>, + terr: TypeError<'tcx>, ) { match *cause.code() { ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => { @@ -739,12 +724,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err.help("...or use `match` instead of `let...else`"); } _ => { - if let ObligationCauseCode::BindingObligation(_, binding_span) = - cause.code().peel_derives() + if let ObligationCauseCode::BindingObligation(_, span) + | ObligationCauseCode::ExprBindingObligation(_, span, ..) + = cause.code().peel_derives() + && let TypeError::RegionsPlaceholderMismatch = terr { - if matches!(terr, TypeError::RegionsPlaceholderMismatch) { - err.span_note(*binding_span, "the lifetime requirement is introduced here"); - } + err.span_note(*span, "the lifetime requirement is introduced here"); } } } @@ -960,12 +945,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + fn normalize_fn_sig_for_diagnostic(&self, sig: ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> { + if let Some(normalize) = &self.normalize_fn_sig_for_diagnostic { + normalize(self, sig) + } else { + sig + } + } + /// Given two `fn` signatures highlight only sub-parts that are different. fn cmp_fn_sig( &self, sig1: &ty::PolyFnSig<'tcx>, sig2: &ty::PolyFnSig<'tcx>, ) -> (DiagnosticStyledString, DiagnosticStyledString) { + let sig1 = &self.normalize_fn_sig_for_diagnostic(*sig1); + let sig2 = &self.normalize_fn_sig_for_diagnostic(*sig2); + let get_lifetimes = |sig| { use rustc_hir::def::Namespace; let (_, sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS) @@ -1422,9 +1418,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// the message in `secondary_span` as the primary label, and apply the message that would /// otherwise be used for the primary label on the `secondary_span` `Span`. This applies on /// E0271, like `src/test/ui/issues/issue-39970.stderr`. - #[tracing::instrument( + #[instrument( level = "debug", - skip(self, diag, secondary_span, swap_secondary_and_primary, force_label) + skip(self, diag, secondary_span, swap_secondary_and_primary, prefer_label) )] pub fn note_type_err( &self, @@ -1432,9 +1428,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { cause: &ObligationCause<'tcx>, secondary_span: Option<(Span, String)>, mut values: Option<ValuePairs<'tcx>>, - terr: &TypeError<'tcx>, + terr: TypeError<'tcx>, swap_secondary_and_primary: bool, - force_label: bool, + prefer_label: bool, ) { let span = cause.span(); @@ -1574,23 +1570,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { Some(values) => { let values = self.resolve_vars_if_possible(values); let (is_simple_error, exp_found) = match values { - ValuePairs::Terms(infer::ExpectedFound { - expected: ty::Term::Ty(expected), - found: ty::Term::Ty(found), - }) => { - let is_simple_err = expected.is_simple_text() && found.is_simple_text(); - OpaqueTypesVisitor::visit_expected_found(self.tcx, expected, found, span) - .report(diag); - - ( - is_simple_err, - Mismatch::Variable(infer::ExpectedFound { expected, found }), - ) + ValuePairs::Terms(infer::ExpectedFound { expected, found }) => { + match (expected.unpack(), found.unpack()) { + (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => { + let is_simple_err = + expected.is_simple_text() && found.is_simple_text(); + OpaqueTypesVisitor::visit_expected_found( + self.tcx, expected, found, span, + ) + .report(diag); + + ( + is_simple_err, + Mismatch::Variable(infer::ExpectedFound { expected, found }), + ) + } + (ty::TermKind::Const(_), ty::TermKind::Const(_)) => { + (false, Mismatch::Fixed("constant")) + } + _ => (false, Mismatch::Fixed("type")), + } } ValuePairs::TraitRefs(_) | ValuePairs::PolyTraitRefs(_) => { (false, Mismatch::Fixed("trait")) } - _ => (false, Mismatch::Fixed("type")), + ValuePairs::Regions(_) => (false, Mismatch::Fixed("lifetime")), }; let vals = match self.values_str(values) { Some((expected, found)) => Some((expected, found)), @@ -1612,7 +1616,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { TypeError::ObjectUnsafeCoercion(_) => {} _ => { let mut label_or_note = |span: Span, msg: &str| { - if force_label || &[span] == diag.span.primary_spans() { + if (prefer_label && is_simple_error) || &[span] == diag.span.primary_spans() { diag.span_label(span, msg); } else { diag.span_note(span, msg); @@ -1662,6 +1666,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pos.col.to_usize() + 1, ) } + (true, ty::Projection(proj)) + if self.tcx.def_kind(proj.item_def_id) + == DefKind::ImplTraitPlaceholder => + { + let sm = self.tcx.sess.source_map(); + let pos = sm.lookup_char_pos(self.tcx.def_span(proj.item_def_id).lo()); + format!( + " (trait associated opaque type at <{}:{}:{}>)", + sm.filename_for_diagnostics(&pos.file.name), + pos.line, + pos.col.to_usize() + 1, + ) + } (true, _) => format!(" ({})", ty.sort_string(self.tcx)), (false, _) => "".to_string(), }; @@ -1713,7 +1730,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ty::error::TypeError::Sorts(terr) if exp_found.map_or(false, |ef| terr.found == ef.found) => { - Some(*terr) + Some(terr) } _ => exp_found, }; @@ -1750,6 +1767,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values && let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind() && let Some(def_id) = def_id.as_local() + && terr.involves_regions() { let span = self.tcx.def_span(def_id); diag.span_note(span, "this closure does not fulfill the lifetime requirements"); @@ -2037,22 +2055,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { (exp_found.expected.kind(), exp_found.found.kind()) { if let ty::Adt(found_def, found_substs) = *found_ty.kind() { - let path_str = format!("{:?}", exp_def); if exp_def == &found_def { - let opt_msg = "you can convert from `&Option<T>` to `Option<&T>` using \ - `.as_ref()`"; - let result_msg = "you can convert from `&Result<T, E>` to \ - `Result<&T, &E>` using `.as_ref()`"; let have_as_ref = &[ - ("std::option::Option", opt_msg), - ("core::option::Option", opt_msg), - ("std::result::Result", result_msg), - ("core::result::Result", result_msg), + ( + sym::Option, + "you can convert from `&Option<T>` to `Option<&T>` using \ + `.as_ref()`", + ), + ( + sym::Result, + "you can convert from `&Result<T, E>` to \ + `Result<&T, &E>` using `.as_ref()`", + ), ]; - if let Some(msg) = have_as_ref - .iter() - .find_map(|(path, msg)| (&path_str == path).then_some(msg)) - { + if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| { + self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg) + }) { let mut show_suggestion = true; for (exp_ty, found_ty) in iter::zip(exp_substs.types(), found_substs.types()) @@ -2078,7 +2096,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { diag.span_suggestion( span, *msg, - format!("{}.as_ref()", snippet), + // HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&` + format!("{}.as_ref()", snippet.trim_start_matches('&')), Applicability::MachineApplicable, ); } @@ -2091,7 +2110,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn report_and_explain_type_error( &self, trace: TypeTrace<'tcx>, - terr: &TypeError<'tcx>, + terr: TypeError<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { use crate::traits::ObligationCauseCode::MatchExpressionArm; @@ -2254,11 +2273,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { return None; } - Some(match (exp_found.expected, exp_found.found) { - (ty::Term::Ty(expected), ty::Term::Ty(found)) => self.cmp(expected, found), - (expected, found) => ( - DiagnosticStyledString::highlighted(expected.to_string()), - DiagnosticStyledString::highlighted(found.to_string()), + Some(match (exp_found.expected.unpack(), exp_found.found.unpack()) { + (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => self.cmp(expected, found), + _ => ( + DiagnosticStyledString::highlighted(exp_found.expected.to_string()), + DiagnosticStyledString::highlighted(exp_found.found.to_string()), ), }) } @@ -2376,19 +2395,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { type_param_span: Option<(Span, bool)>, bound_kind: GenericKind<'tcx>, sub: S, + add_lt_sugg: Option<(Span, String)>, ) { let msg = "consider adding an explicit lifetime bound"; if let Some((sp, has_lifetimes)) = type_param_span { let suggestion = if has_lifetimes { format!(" + {}", sub) } else { format!(": {}", sub) }; - err.span_suggestion_verbose( - sp, - &format!("{}...", msg), - suggestion, + let mut suggestions = vec![(sp, suggestion)]; + if let Some(add_lt_sugg) = add_lt_sugg { + suggestions.push(add_lt_sugg); + } + err.multipart_suggestion_verbose( + format!("{msg}..."), + suggestions, Applicability::MaybeIncorrect, // Issue #41966 ); } else { - let consider = format!("{} `{}: {}`...", msg, bound_kind, sub,); + let consider = format!("{} `{}: {}`...", msg, bound_kind, sub); err.help(&consider); } } @@ -2404,7 +2427,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }; let mut sugg = vec![(sp, suggestion), (span.shrink_to_hi(), format!(" + {}", new_lt))]; - if let Some(lt) = add_lt_sugg { + if let Some(lt) = add_lt_sugg.clone() { sugg.push(lt); sugg.rotate_right(1); } @@ -2510,7 +2533,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // for the bound is not suitable for suggestions when `-Zverbose` is set because it // uses `Debug` output, so we handle it specially here so that suggestions are // always correct. - binding_suggestion(&mut err, type_param_span, bound_kind, name); + binding_suggestion(&mut err, type_param_span, bound_kind, name, None); err } @@ -2523,7 +2546,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { "{} may not live long enough", labeled_user_string ); - binding_suggestion(&mut err, type_param_span, bound_kind, "'static"); + binding_suggestion(&mut err, type_param_span, bound_kind, "'static", None); err } @@ -2557,7 +2580,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { new_binding_suggestion(&mut err, type_param_span); } _ => { - binding_suggestion(&mut err, type_param_span, bound_kind, new_lt); + binding_suggestion( + &mut err, + type_param_span, + bound_kind, + new_lt, + add_lt_sugg, + ); } } } @@ -2659,67 +2688,95 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// Float types, respectively). When comparing two ADTs, these rules apply recursively. pub fn same_type_modulo_infer(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { let (a, b) = self.resolve_vars_if_possible((a, b)); - match (a.kind(), b.kind()) { - (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) => { - if def_a != def_b { - return false; - } + SameTypeModuloInfer(self).relate(a, b).is_ok() + } +} - substs_a - .types() - .zip(substs_b.types()) - .all(|(a, b)| self.same_type_modulo_infer(a, b)) - } - (&ty::FnDef(did_a, substs_a), &ty::FnDef(did_b, substs_b)) => { - if did_a != did_b { - return false; - } +struct SameTypeModuloInfer<'a, 'tcx>(&'a InferCtxt<'a, 'tcx>); - substs_a - .types() - .zip(substs_b.types()) - .all(|(a, b)| self.same_type_modulo_infer(a, b)) - } - (&ty::Int(_) | &ty::Uint(_), &ty::Infer(ty::InferTy::IntVar(_))) +impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.0.tcx + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + // Unused, only for consts which we treat as always equal + ty::ParamEnv::empty() + } + + fn tag(&self) -> &'static str { + "SameTypeModuloInfer" + } + + fn a_is_expected(&self) -> bool { + true + } + + fn relate_with_variance<T: relate::Relate<'tcx>>( + &mut self, + _variance: ty::Variance, + _info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> relate::RelateResult<'tcx, T> { + self.relate(a, b) + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + match (a.kind(), b.kind()) { + (ty::Int(_) | ty::Uint(_), ty::Infer(ty::InferTy::IntVar(_))) | ( - &ty::Infer(ty::InferTy::IntVar(_)), - &ty::Int(_) | &ty::Uint(_) | &ty::Infer(ty::InferTy::IntVar(_)), + ty::Infer(ty::InferTy::IntVar(_)), + ty::Int(_) | ty::Uint(_) | ty::Infer(ty::InferTy::IntVar(_)), ) - | (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_))) + | (ty::Float(_), ty::Infer(ty::InferTy::FloatVar(_))) | ( - &ty::Infer(ty::InferTy::FloatVar(_)), - &ty::Float(_) | &ty::Infer(ty::InferTy::FloatVar(_)), + ty::Infer(ty::InferTy::FloatVar(_)), + ty::Float(_) | ty::Infer(ty::InferTy::FloatVar(_)), ) - | (&ty::Infer(ty::InferTy::TyVar(_)), _) - | (_, &ty::Infer(ty::InferTy::TyVar(_))) => true, - (&ty::Ref(_, ty_a, mut_a), &ty::Ref(_, ty_b, mut_b)) => { - mut_a == mut_b && self.same_type_modulo_infer(ty_a, ty_b) - } - (&ty::RawPtr(a), &ty::RawPtr(b)) => { - a.mutbl == b.mutbl && self.same_type_modulo_infer(a.ty, b.ty) - } - (&ty::Slice(a), &ty::Slice(b)) => self.same_type_modulo_infer(a, b), - (&ty::Array(a_ty, a_ct), &ty::Array(b_ty, b_ct)) => { - self.same_type_modulo_infer(a_ty, b_ty) && a_ct == b_ct - } - (&ty::Tuple(a), &ty::Tuple(b)) => { - if a.len() != b.len() { - return false; - } - std::iter::zip(a.iter(), b.iter()).all(|(a, b)| self.same_type_modulo_infer(a, b)) - } - (&ty::FnPtr(a), &ty::FnPtr(b)) => { - let a = a.skip_binder().inputs_and_output; - let b = b.skip_binder().inputs_and_output; - if a.len() != b.len() { - return false; - } - std::iter::zip(a.iter(), b.iter()).all(|(a, b)| self.same_type_modulo_infer(a, b)) - } - // FIXME(compiler-errors): This needs to be generalized more - _ => a == b, + | (ty::Infer(ty::InferTy::TyVar(_)), _) + | (_, ty::Infer(ty::InferTy::TyVar(_))) => Ok(a), + (ty::Infer(_), _) | (_, ty::Infer(_)) => Err(TypeError::Mismatch), + _ => relate::super_relate_tys(self, a, b), + } + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + if (a.is_var() && b.is_free_or_static()) + || (b.is_var() && a.is_free_or_static()) + || (a.is_var() && b.is_var()) + || a == b + { + Ok(a) + } else { + Err(TypeError::Mismatch) } } + + fn binders<T>( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> relate::RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: relate::Relate<'tcx>, + { + Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + _b: ty::Const<'tcx>, + ) -> relate::RelateResult<'tcx, ty::Const<'tcx>> { + // FIXME(compiler-errors): This could at least do some first-order + // relation + Ok(a) + } } impl<'a, 'tcx> InferCtxt<'a, 'tcx> { @@ -2781,12 +2838,12 @@ pub enum FailureCode { } pub trait ObligationCauseExt<'tcx> { - fn as_failure_code(&self, terr: &TypeError<'tcx>) -> FailureCode; + fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode; fn as_requirement_str(&self) -> &'static str; } impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> { - fn as_failure_code(&self, terr: &TypeError<'tcx>) -> FailureCode { + fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode { use self::FailureCode::*; use crate::traits::ObligationCauseCode::*; match self.code() { @@ -2823,7 +2880,7 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> { TypeError::IntrinsicCast => { Error0308("cannot coerce intrinsics to function pointers") } - TypeError::ObjectUnsafeCoercion(did) => Error0038(*did), + TypeError::ObjectUnsafeCoercion(did) => Error0038(did), _ => Error0308("mismatched types"), }, } @@ -2853,6 +2910,30 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> { } } +/// Newtype to allow implementing IntoDiagnosticArg +pub struct ObligationCauseAsDiagArg<'tcx>(pub ObligationCause<'tcx>); + +impl IntoDiagnosticArg for ObligationCauseAsDiagArg<'_> { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + use crate::traits::ObligationCauseCode::*; + let kind = match self.0.code() { + CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => "method_compat", + CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => "type_compat", + CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => "const_compat", + ExprAssignable => "expr_assignable", + IfExpression { .. } => "if_else_different", + IfExpressionWithNoElse => "no_else", + MainFunctionType => "fn_main_correct_type", + StartFunctionType => "fn_start_correct_type", + IntrinsicType => "intristic_correct_type", + MethodReceiver => "method_correct_type", + _ => "other", + } + .into(); + rustc_errors::DiagnosticArgValue::Str(kind) + } +} + /// This is a bare signal of what kind of type we're dealing with. `ty::TyKind` tracks /// extra information about each type, but we only care about the category. #[derive(Clone, Copy, PartialEq, Eq, Hash)] diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 561d1354e..cb2be9358 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -1,6 +1,10 @@ +use crate::errors::{ + AmbigousImpl, AmbigousReturn, AnnotationRequired, InferenceBadError, NeedTypeInfoInGenerator, + SourceKindMultiSuggestion, SourceKindSubdiag, +}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::InferCtxt; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, IntoDiagnosticArg}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def::{CtorOf, DefKind, Namespace}; @@ -14,6 +18,7 @@ use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef}; use rustc_middle::ty::{self, DefIdTree, InferConst}; use rustc_middle::ty::{IsSuggestable, Ty, TyCtxt, TypeckResults}; +use rustc_session::SessionDiagnostic; use rustc_span::symbol::{kw, Ident}; use rustc_span::{BytePos, Span}; use std::borrow::Cow; @@ -60,38 +65,49 @@ pub struct InferenceDiagnosticsParentData { name: String, } +#[derive(Clone)] pub enum UnderspecifiedArgKind { Type { prefix: Cow<'static, str> }, Const { is_parameter: bool }, } impl InferenceDiagnosticsData { - /// Generate a label for a generic argument which can't be inferred. When not - /// much is known about the argument, `use_diag` may be used to describe the - /// labeled value. - fn cannot_infer_msg(&self) -> String { - if self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }) { - return "cannot infer type".to_string(); - } - - let suffix = match &self.parent { - Some(parent) => parent.suffix_string(), - None => String::new(), - }; - - // For example: "cannot infer type for type parameter `T`" - format!("cannot infer {} `{}`{}", self.kind.prefix_string(), self.name, suffix) + fn can_add_more_info(&self) -> bool { + !(self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. })) } - fn where_x_is_specified(&self, in_type: Ty<'_>) -> String { + fn where_x_is_kind(&self, in_type: Ty<'_>) -> &'static str { if in_type.is_ty_infer() { - String::new() + "empty" } else if self.name == "_" { // FIXME: Consider specializing this message if there is a single `_` // in the type. - ", where the placeholders `_` are specified".to_string() + "underscore" } else { - format!(", where the {} `{}` is specified", self.kind.prefix_string(), self.name) + "has_name" + } + } + + /// Generate a label for a generic argument which can't be inferred. When not + /// much is known about the argument, `use_diag` may be used to describe the + /// labeled value. + fn make_bad_error(&self, span: Span) -> InferenceBadError<'_> { + let has_parent = self.parent.is_some(); + let bad_kind = if self.can_add_more_info() { "more_info" } else { "other" }; + let (parent_prefix, parent_name) = self + .parent + .as_ref() + .map(|parent| (parent.prefix, parent.name.clone())) + .unwrap_or_default(); + InferenceBadError { + span, + bad_kind, + prefix_kind: self.kind.clone(), + prefix: self.kind.try_get_prefix().unwrap_or_default(), + name: self.name.clone(), + has_parent, + parent_prefix, + parent_name, } } } @@ -113,18 +129,24 @@ impl InferenceDiagnosticsParentData { fn for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<InferenceDiagnosticsParentData> { Self::for_parent_def_id(tcx, tcx.parent(def_id)) } +} - fn suffix_string(&self) -> String { - format!(" declared on the {} `{}`", self.prefix, self.name) +impl IntoDiagnosticArg for UnderspecifiedArgKind { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + let kind = match self { + Self::Type { .. } => "type", + Self::Const { is_parameter: true } => "const_with_param", + Self::Const { is_parameter: false } => "const", + }; + rustc_errors::DiagnosticArgValue::Str(kind.into()) } } impl UnderspecifiedArgKind { - fn prefix_string(&self) -> Cow<'static, str> { + fn try_get_prefix(&self) -> Option<&str> { match self { - Self::Type { prefix } => format!("type for {}", prefix).into(), - Self::Const { is_parameter: true } => "the value of const parameter".into(), - Self::Const { is_parameter: false } => "the value of the constant".into(), + Self::Type { prefix } => Some(prefix.as_ref()), + Self::Const { .. } => None, } } } @@ -177,7 +199,7 @@ fn ty_to_string<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String { } /// We don't want to directly use `ty_to_string` for closures as their type isn't really -/// something users are familar with. Directly printing the `fn_sig` of closures also +/// something users are familiar with. Directly printing the `fn_sig` of closures also /// doesn't work as they actually use the "rust-call" API. fn closure_as_fn_str<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String { let ty::Closure(_, substs) = ty.kind() else { unreachable!() }; @@ -303,11 +325,44 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { arg_data: InferenceDiagnosticsData, error_code: TypeAnnotationNeeded, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let error_code = error_code.into(); - let mut err = - self.tcx.sess.struct_span_err_with_code(span, "type annotations needed", error_code); - err.span_label(span, arg_data.cannot_infer_msg()); - err + let source_kind = "other"; + let source_name = ""; + let failure_span = None; + let infer_subdiags = Vec::new(); + let multi_suggestions = Vec::new(); + let bad_label = Some(arg_data.make_bad_error(span)); + match error_code { + TypeAnnotationNeeded::E0282 => AnnotationRequired { + span, + source_kind, + source_name, + failure_span, + infer_subdiags, + multi_suggestions, + bad_label, + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic), + TypeAnnotationNeeded::E0283 => AmbigousImpl { + span, + source_kind, + source_name, + failure_span, + infer_subdiags, + multi_suggestions, + bad_label, + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic), + TypeAnnotationNeeded::E0284 => AmbigousReturn { + span, + source_kind, + source_name, + failure_span, + infer_subdiags, + multi_suggestions, + bad_label, + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic), + } } pub fn emit_inference_failure_err( @@ -340,48 +395,39 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { return self.bad_inference_failure_err(failure_span, arg_data, error_code) }; - let error_code = error_code.into(); - let mut err = self.tcx.sess.struct_span_err_with_code( - span, - &format!("type annotations needed{}", kind.ty_msg(self)), - error_code, - ); - - if should_label_span && !failure_span.overlaps(span) { - err.span_label(failure_span, "type must be known at this point"); - } + let (source_kind, name) = kind.ty_localized_msg(self); + let failure_span = if should_label_span && !failure_span.overlaps(span) { + Some(failure_span) + } else { + None + }; + let mut infer_subdiags = Vec::new(); + let mut multi_suggestions = Vec::new(); match kind { InferSourceKind::LetBinding { insert_span, pattern_name, ty } => { - let suggestion_msg = if let Some(name) = pattern_name { - format!( - "consider giving `{}` an explicit type{}", - name, - arg_data.where_x_is_specified(ty) - ) - } else { - format!( - "consider giving this pattern a type{}", - arg_data.where_x_is_specified(ty) - ) - }; - err.span_suggestion_verbose( - insert_span, - &suggestion_msg, - format!(": {}", ty_to_string(self, ty)), - Applicability::HasPlaceholders, - ); + infer_subdiags.push(SourceKindSubdiag::LetLike { + span: insert_span, + name: pattern_name.map(|name| name.to_string()).unwrap_or_else(String::new), + x_kind: arg_data.where_x_is_kind(ty), + prefix_kind: arg_data.kind.clone(), + prefix: arg_data.kind.try_get_prefix().unwrap_or_default(), + arg_name: arg_data.name, + kind: if pattern_name.is_some() { "with_pattern" } else { "other" }, + type_name: ty_to_string(self, ty), + }); } InferSourceKind::ClosureArg { insert_span, ty } => { - err.span_suggestion_verbose( - insert_span, - &format!( - "consider giving this closure parameter an explicit type{}", - arg_data.where_x_is_specified(ty) - ), - format!(": {}", ty_to_string(self, ty)), - Applicability::HasPlaceholders, - ); + infer_subdiags.push(SourceKindSubdiag::LetLike { + span: insert_span, + name: String::new(), + x_kind: arg_data.where_x_is_kind(ty), + prefix_kind: arg_data.kind.clone(), + prefix: arg_data.kind.try_get_prefix().unwrap_or_default(), + arg_name: arg_data.name, + kind: "closure", + type_name: ty_to_string(self, ty), + }); } InferSourceKind::GenericArg { insert_span, @@ -393,19 +439,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let generics = self.tcx.generics_of(generics_def_id); let is_type = matches!(arg.unpack(), GenericArgKind::Type(_)); - let cannot_infer_msg = format!( - "cannot infer {} of the {} parameter `{}`{}", - if is_type { "type" } else { "the value" }, - if is_type { "type" } else { "const" }, - generics.params[argument_index].name, - // We use the `generics_def_id` here, as even when suggesting `None::<T>`, - // the type parameter `T` was still declared on the enum, not on the - // variant. + let (parent_exists, parent_prefix, parent_name) = InferenceDiagnosticsParentData::for_parent_def_id(self.tcx, generics_def_id) - .map_or(String::new(), |parent| parent.suffix_string()), - ); + .map_or((false, String::new(), String::new()), |parent| { + (true, parent.prefix.to_string(), parent.name) + }); - err.span_label(span, cannot_infer_msg); + infer_subdiags.push(SourceKindSubdiag::GenericLabel { + span, + is_type, + param_name: generics.params[argument_index].name.to_string(), + parent_exists, + parent_prefix, + parent_name, + }); let args = fmt_printer(self, Namespace::TypeNS) .comma_sep(generic_args.iter().copied().map(|arg| { @@ -435,15 +482,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .unwrap() .into_buffer(); - err.span_suggestion_verbose( - insert_span, - &format!( - "consider specifying the generic argument{}", - pluralize!(generic_args.len()), - ), - format!("::<{}>", args), - Applicability::HasPlaceholders, - ); + infer_subdiags.push(SourceKindSubdiag::GenericSuggestion { + span: insert_span, + arg_count: generic_args.len(), + args, + }); } InferSourceKind::FullyQualifiedMethodCall { receiver, successor, substs, def_id } => { let printer = fmt_printer(self, Namespace::ValueNS); @@ -468,37 +511,54 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { _ => "", }; - let suggestion = vec![ - (receiver.span.shrink_to_lo(), format!("{def_path}({adjustment}")), - (receiver.span.shrink_to_hi().with_hi(successor.1), successor.0.to_string()), - ]; - err.multipart_suggestion_verbose( - "try using a fully qualified path to specify the expected types", - suggestion, - Applicability::HasPlaceholders, - ); + multi_suggestions.push(SourceKindMultiSuggestion::new_fully_qualified( + receiver.span, + def_path, + adjustment, + successor, + )); } InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => { - let ret = ty_to_string(self, ty); - let (arrow, post) = match data { - FnRetTy::DefaultReturn(_) => ("-> ", " "), - _ => ("", ""), - }; - let suggestion = match should_wrap_expr { - Some(end_span) => vec![ - (data.span(), format!("{}{}{}{{ ", arrow, ret, post)), - (end_span, " }".to_string()), - ], - None => vec![(data.span(), format!("{}{}{}", arrow, ret, post))], - }; - err.multipart_suggestion_verbose( - "try giving this closure an explicit return type", - suggestion, - Applicability::HasPlaceholders, - ); + let ty_info = ty_to_string(self, ty); + multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return( + ty_info, + data, + should_wrap_expr, + )); + } + } + match error_code { + TypeAnnotationNeeded::E0282 => AnnotationRequired { + span, + source_kind, + source_name: &name, + failure_span, + infer_subdiags, + multi_suggestions, + bad_label: None, + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic), + TypeAnnotationNeeded::E0283 => AmbigousImpl { + span, + source_kind, + source_name: &name, + failure_span, + infer_subdiags, + multi_suggestions, + bad_label: None, + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic), + TypeAnnotationNeeded::E0284 => AmbigousReturn { + span, + source_kind, + source_name: &name, + failure_span, + infer_subdiags, + multi_suggestions, + bad_label: None, } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic), } - err } pub fn need_type_info_err_in_generator( @@ -510,15 +570,26 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let ty = self.resolve_vars_if_possible(ty); let data = self.extract_inference_diagnostics_data(ty.into(), None); - let mut err = struct_span_err!( - self.tcx.sess, + NeedTypeInfoInGenerator { + bad_label: data.make_bad_error(span), span, - E0698, - "type inside {} must be known in this context", - kind, - ); - err.span_label(span, data.cannot_infer_msg()); - err + generator_kind: GeneratorKindAsDiagArg(kind), + } + .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic) + } +} + +pub struct GeneratorKindAsDiagArg(pub hir::GeneratorKind); + +impl IntoDiagnosticArg for GeneratorKindAsDiagArg { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + let kind = match self.0 { + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "async_block", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "async_closure", + hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "async_fn", + hir::GeneratorKind::Gen => "generator", + }; + rustc_errors::DiagnosticArgValue::Str(kind.into()) } } @@ -579,22 +650,22 @@ impl<'tcx> InferSource<'tcx> { } impl<'tcx> InferSourceKind<'tcx> { - fn ty_msg(&self, infcx: &InferCtxt<'_, 'tcx>) -> String { + fn ty_localized_msg(&self, infcx: &InferCtxt<'_, 'tcx>) -> (&'static str, String) { match *self { InferSourceKind::LetBinding { ty, .. } | InferSourceKind::ClosureArg { ty, .. } | InferSourceKind::ClosureReturn { ty, .. } => { if ty.is_closure() { - format!(" for the closure `{}`", closure_as_fn_str(infcx, ty)) + ("closure", closure_as_fn_str(infcx, ty)) } else if !ty.is_ty_infer() { - format!(" for `{}`", ty_to_string(infcx, ty)) + ("normal", ty_to_string(infcx, ty)) } else { - String::new() + ("other", String::new()) } } // FIXME: We should be able to add some additional info here. InferSourceKind::GenericArg { .. } - | InferSourceKind::FullyQualifiedMethodCall { .. } => String::new(), + | InferSourceKind::FullyQualifiedMethodCall { .. } => ("other", String::new()), } } } @@ -830,7 +901,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { } } } - hir::ExprKind::MethodCall(segment, _, _) => { + hir::ExprKind::MethodCall(segment, ..) => { if let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) { let generics = tcx.generics_of(def_id); let insertable: Option<_> = try { @@ -838,7 +909,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { None? } let substs = self.node_substs_opt(expr.hir_id)?; - let span = tcx.hir().span(segment.hir_id?); + let span = tcx.hir().span(segment.hir_id); let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); InsertableGenericArgs { insert_span, @@ -886,13 +957,13 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { path.segments .iter() .filter_map(move |segment| { - let res = segment.res?; + let res = segment.res; let generics_def_id = tcx.res_generics_def_id(res)?; let generics = tcx.generics_of(generics_def_id); if generics.has_impl_trait() { return None; } - let span = tcx.hir().span(segment.hir_id?); + let span = tcx.hir().span(segment.hir_id); let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); Some(InsertableGenericArgs { insert_span, @@ -925,7 +996,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { if !segment.infer_args || generics.has_impl_trait() { None?; } - let span = tcx.hir().span(segment.hir_id?); + let span = tcx.hir().span(segment.hir_id); let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); InsertableGenericArgs { insert_span, substs, generics_def_id: def_id, def_id } }; @@ -1061,7 +1132,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { let generic_args = &generics.own_substs_no_defaults(tcx, substs) [generics.own_counts().lifetimes..]; let span = match expr.kind { - ExprKind::MethodCall(path, _, _) => path.ident.span, + ExprKind::MethodCall(path, ..) => path.ident.span, _ => expr.span, }; @@ -1110,7 +1181,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { }) .any(|generics| generics.has_impl_trait()) }; - if let ExprKind::MethodCall(path, args, span) = expr.kind + if let ExprKind::MethodCall(path, receiver, args, span) = expr.kind && let Some(substs) = self.node_substs_opt(expr.hir_id) && substs.iter().any(|arg| self.generic_arg_contains_target(arg)) && let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) @@ -1118,12 +1189,12 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { && !has_impl_trait(def_id) { let successor = - args.get(1).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo())); + args.get(0).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo())); let substs = self.infcx.resolve_vars_if_possible(substs); self.update_infer_source(InferSource { span: path.ident.span, kind: InferSourceKind::FullyQualifiedMethodCall { - receiver: args.first().unwrap(), + receiver, successor, substs, def_id, diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs index 9a2ab3e32..3a4320a9a 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -1,6 +1,9 @@ //! Error Reporting for Anonymous Region Lifetime Errors //! where both the regions are anonymous. +use crate::errors::AddLifetimeParamsSuggestion; +use crate::errors::LifetimeMismatch; +use crate::errors::LifetimeMismatchLabels; use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::error_reporting::nice_region_error::util::AnonymousParamInfo; use crate::infer::error_reporting::nice_region_error::NiceRegionError; @@ -8,11 +11,10 @@ use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::SubregionOrigin; use crate::infer::TyCtxt; -use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed}; -use rustc_hir as hir; -use rustc_hir::{GenericParamKind, Ty}; +use rustc_errors::AddSubdiagnostic; +use rustc_errors::{Diagnostic, ErrorGuaranteed}; +use rustc_hir::Ty; use rustc_middle::ty::Region; -use rustc_span::symbol::kw; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// Print the error message for lifetime errors when both the concerned regions are anonymous. @@ -98,137 +100,50 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let sub_is_ret_type = self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub); - let span_label_var1 = match anon_param_sup.pat.simple_ident() { - Some(simple_ident) => format!(" from `{}`", simple_ident), - None => String::new(), - }; - - let span_label_var2 = match anon_param_sub.pat.simple_ident() { - Some(simple_ident) => format!(" into `{}`", simple_ident), - None => String::new(), - }; - debug!( "try_report_anon_anon_conflict: sub_is_ret_type={:?} sup_is_ret_type={:?}", sub_is_ret_type, sup_is_ret_type ); - let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch"); - - match (sup_is_ret_type, sub_is_ret_type) { + let labels = match (sup_is_ret_type, sub_is_ret_type) { (ret_capture @ Some(ret_span), _) | (_, ret_capture @ Some(ret_span)) => { let param_span = if sup_is_ret_type == ret_capture { ty_sub.span } else { ty_sup.span }; - - err.span_label( + LifetimeMismatchLabels::InRet { param_span, - "this parameter and the return type are declared with different lifetimes...", - ); - err.span_label(ret_span, ""); - err.span_label(span, format!("...but data{} is returned here", span_label_var1)); - } - - (None, None) => { - if ty_sup.hir_id == ty_sub.hir_id { - err.span_label(ty_sup.span, "this type is declared with multiple lifetimes..."); - err.span_label(ty_sub.span, ""); - err.span_label(span, "...but data with one lifetime flows into the other here"); - } else { - err.span_label( - ty_sup.span, - "these two types are declared with different lifetimes...", - ); - err.span_label(ty_sub.span, ""); - err.span_label( - span, - format!("...but data{} flows{} here", span_label_var1, span_label_var2), - ); + ret_span, + span, + label_var1: anon_param_sup.pat.simple_ident(), } } - } - if suggest_adding_lifetime_params(self.tcx(), sub, ty_sup, ty_sub, &mut err) { - err.note("each elided lifetime in input position becomes a distinct lifetime"); - } + (None, None) => LifetimeMismatchLabels::Normal { + hir_equal: ty_sup.hir_id == ty_sub.hir_id, + ty_sup: ty_sup.span, + ty_sub: ty_sub.span, + span, + sup: anon_param_sup.pat.simple_ident(), + sub: anon_param_sub.pat.simple_ident(), + }, + }; - let reported = err.emit(); + let suggestion = + AddLifetimeParamsSuggestion { tcx: self.tcx(), sub, ty_sup, ty_sub, add_note: true }; + let err = LifetimeMismatch { span, labels, suggestion }; + let reported = self.tcx().sess.emit_err(err); Some(reported) } } +/// Currently only used in rustc_borrowck, probably should be +/// removed in favour of public_errors::AddLifetimeParamsSuggestion pub fn suggest_adding_lifetime_params<'tcx>( tcx: TyCtxt<'tcx>, sub: Region<'tcx>, - ty_sup: &Ty<'_>, - ty_sub: &Ty<'_>, + ty_sup: &'tcx Ty<'_>, + ty_sub: &'tcx Ty<'_>, err: &mut Diagnostic, -) -> bool { - let ( - hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. }, - hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. }, - ) = (ty_sub, ty_sup) else { - return false; - }; - - if !lifetime_sub.name.is_anonymous() || !lifetime_sup.name.is_anonymous() { - return false; - }; - - let Some(anon_reg) = tcx.is_suitable_region(sub) else { - return false; - }; - - let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id); - - let node = tcx.hir().get(hir_id); - let is_impl = matches!(&node, hir::Node::ImplItem(_)); - let generics = match node { - hir::Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, ref generics, ..), .. }) - | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. }) - | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics, - _ => return false, - }; - - let suggestion_param_name = generics - .params - .iter() - .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) - .map(|p| p.name.ident().name) - .find(|i| *i != kw::UnderscoreLifetime); - let introduce_new = suggestion_param_name.is_none(); - let suggestion_param_name = - suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| "'a".to_owned()); - - debug!(?lifetime_sup.span); - debug!(?lifetime_sub.span); - let make_suggestion = |span: rustc_span::Span| { - if span.is_empty() { - (span, format!("{}, ", suggestion_param_name)) - } else if let Ok("&") = tcx.sess.source_map().span_to_snippet(span).as_deref() { - (span.shrink_to_hi(), format!("{} ", suggestion_param_name)) - } else { - (span, suggestion_param_name.clone()) - } - }; - let mut suggestions = - vec![make_suggestion(lifetime_sub.span), make_suggestion(lifetime_sup.span)]; - - if introduce_new { - let new_param_suggestion = - if let Some(first) = generics.params.iter().find(|p| !p.name.ident().span.is_empty()) { - (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name)) - } else { - (generics.span, format!("<{}>", suggestion_param_name)) - }; - - suggestions.push(new_param_suggestion); - } - - let mut sugg = String::from("consider introducing a named lifetime parameter"); - if is_impl { - sugg.push_str(" and update trait if needed"); - } - err.multipart_suggestion(sugg, suggestions, Applicability::MaybeIncorrect); - - true +) { + let suggestion = AddLifetimeParamsSuggestion { tcx, sub, ty_sup, ty_sub, add_note: false }; + suggestion.add_to_diagnostic(err); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs index c1b201da6..d8f540b74 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -91,7 +91,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { hir::TyKind::TraitObject(bounds, ..) => { for bound in bounds { self.current_index.shift_in(1); - self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); + self.visit_poly_trait_ref(bound); self.current_index.shift_out(1); } } @@ -103,7 +103,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { // Find the index of the named region that was part of the // error. We will then search the function parameters for a bound // region at the right depth with the same index - (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => { + (Some(rl::Region::EarlyBound(id)), ty::BrNamed(def_id, _)) => { debug!("EarlyBound id={:?} def_id={:?}", id, def_id); if id == def_id { self.found_type = Some(arg); @@ -133,7 +133,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { Some( rl::Region::Static | rl::Region::Free(_, _) - | rl::Region::EarlyBound(_, _) + | rl::Region::EarlyBound(_) | rl::Region::LateBound(_, _, _), ) | None, @@ -188,7 +188,7 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { match (self.tcx.named_region(lifetime.hir_id), self.bound_region) { // the lifetime of the TyPath! - (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => { + (Some(rl::Region::EarlyBound(id)), ty::BrNamed(def_id, _)) => { debug!("EarlyBound id={:?} def_id={:?}", id, def_id); if id == def_id { self.found_it = true; @@ -209,7 +209,7 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { ( Some( rl::Region::Static - | rl::Region::EarlyBound(_, _) + | rl::Region::EarlyBound(_) | rl::Region::LateBound(_, _, _) | rl::Region::Free(_, _), ) diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs index 893ca3cf7..1410e2b63 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs @@ -1,13 +1,14 @@ //! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate //! to hold. +use crate::errors::{note_and_explain, IntroducesStaticBecauseUnmetLifetimeReq}; +use crate::errors::{ImplNote, MismatchedStaticLifetime, TraitSubdiag}; use crate::infer::error_reporting::nice_region_error::NiceRegionError; -use crate::infer::error_reporting::note_and_explain_region; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::ObligationCauseCode; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; use rustc_middle::ty::TypeVisitor; @@ -35,15 +36,27 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let ObligationCauseCode::MatchImpl(parent, impl_def_id) = code else { return None; }; - let ObligationCauseCode::BindingObligation(_def_id, binding_span) = *parent.code() else { + let (ObligationCauseCode::BindingObligation(_, binding_span) | ObligationCauseCode::ExprBindingObligation(_, binding_span, ..)) + = *parent.code() else { return None; }; - let mut err = self.tcx().sess.struct_span_err(cause.span, "incompatible lifetime on type"); + // FIXME: we should point at the lifetime - let mut multi_span: MultiSpan = vec![binding_span].into(); - multi_span.push_span_label(binding_span, "introduces a `'static` lifetime requirement"); - err.span_note(multi_span, "because this has an unmet lifetime requirement"); - note_and_explain_region(self.tcx(), &mut err, "", sup, "...", Some(binding_span)); + let multi_span: MultiSpan = vec![binding_span].into(); + let multispan_subdiag = IntroducesStaticBecauseUnmetLifetimeReq { + unmet_requirements: multi_span, + binding_span, + }; + + let expl = note_and_explain::RegionExplanation::new( + self.tcx(), + sup, + Some(binding_span), + note_and_explain::PrefixKind::Empty, + note_and_explain::SuffixKind::Continues, + ); + let mut impl_span = None; + let mut trait_subdiags = Vec::new(); if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) { // If an impl is local, then maybe this isn't what they want. Try to // be as helpful as possible with implicit lifetimes. @@ -72,31 +85,30 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // there aren't trait objects or because none are implicit, then just // write a single note on the impl itself. - let impl_span = self.tcx().def_span(*impl_def_id); - err.span_note(impl_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); + impl_span = Some(self.tcx().def_span(*impl_def_id)); } else { // Otherwise, point at all implicit static lifetimes - err.note("...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); for span in &traits { - err.span_note(*span, "this has an implicit `'static` lifetime requirement"); + trait_subdiags.push(TraitSubdiag::Note { span: *span }); // It would be nice to put this immediately under the above note, but they get // pushed to the end. - err.span_suggestion_verbose( - span.shrink_to_hi(), - "consider relaxing the implicit `'static` requirement", - " + '_", - Applicability::MaybeIncorrect, - ); + trait_subdiags.push(TraitSubdiag::Sugg { span: span.shrink_to_hi() }); } } } else { // Otherwise just point out the impl. - let impl_span = self.tcx().def_span(*impl_def_id); - err.span_note(impl_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); + impl_span = Some(self.tcx().def_span(*impl_def_id)); } - let reported = err.emit(); + let err = MismatchedStaticLifetime { + cause_span: cause.span, + unmet_lifetime_reqs: multispan_subdiag, + expl, + impl_note: ImplNote { impl_span }, + trait_subdiags, + }; + let reported = self.tcx().sess.emit_err(err); Some(reported) } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs index 998699158..d4db07512 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs @@ -211,7 +211,10 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { ); let mut err = self.tcx().sess.struct_span_err(span, &msg); - let leading_ellipsis = if let ObligationCauseCode::ItemObligation(def_id) = *cause.code() { + let leading_ellipsis = if let ObligationCauseCode::ItemObligation(def_id) + | ObligationCauseCode::ExprItemObligation(def_id, ..) = + *cause.code() + { err.span_label(span, "doesn't satisfy where-clause"); err.span_label( self.tcx().def_span(def_id), diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index 9886c572a..ae56bea6f 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -232,7 +232,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ObligationCauseCode::MatchImpl(parent, ..) => parent.code(), _ => cause.code(), } - && let (&ObligationCauseCode::ItemObligation(item_def_id), None) = (code, override_error_code) + && let (&ObligationCauseCode::ItemObligation(item_def_id) | &ObligationCauseCode::ExprItemObligation(item_def_id, ..), None) = (code, override_error_code) { // Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static` // lifetime as above, but called using a fully-qualified path to the method: @@ -300,7 +300,7 @@ pub fn suggest_new_region_bound( continue; } match fn_return.kind { - TyKind::OpaqueDef(item_id, _) => { + TyKind::OpaqueDef(item_id, _, _) => { let item = tcx.hir().item(item_id); let ItemKind::OpaqueTy(opaque) = &item.kind else { return; @@ -544,7 +544,7 @@ pub struct TraitObjectVisitor(pub FxHashSet<DefId>); impl<'tcx> TypeVisitor<'tcx> for TraitObjectVisitor { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { match t.kind() { - ty::Dynamic(preds, re) if re.is_static() => { + ty::Dynamic(preds, re, _) if re.is_static() => { if let Some(def_id) = preds.principal_def_id() { self.0.insert(def_id); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs index da465a764..a6a39d062 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -154,16 +154,11 @@ impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { } hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments { [segment] - if segment - .res - .map(|res| { - matches!( - res, - Res::SelfTy { trait_: _, alias_to: _ } - | Res::Def(hir::def::DefKind::TyParam, _) - ) - }) - .unwrap_or(false) => + if matches!( + segment.res, + Res::SelfTy { trait_: _, alias_to: _ } + | Res::Def(hir::def::DefKind::TyParam, _) + ) => { self.types.push(path.span); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index c1940c5c0..adaa47c01 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -1,100 +1,89 @@ -use crate::infer::error_reporting::{note_and_explain_region, ObligationCauseExt}; +use crate::errors::RegionOriginNote; +use crate::infer::error_reporting::note_and_explain_region; use crate::infer::{self, InferCtxt, SubregionOrigin}; -use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{ + fluent, struct_span_err, AddSubdiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, +}; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Region}; +use super::ObligationCauseAsDiagArg; + impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) { - let mut label_or_note = |span, msg: &str| { - let sub_count = err.children.iter().filter(|d| d.span.is_dummy()).count(); - let expanded_sub_count = err.children.iter().filter(|d| !d.span.is_dummy()).count(); - let span_is_primary = err.span.primary_spans().iter().all(|&sp| sp == span); - if span_is_primary && sub_count == 0 && expanded_sub_count == 0 { - err.span_label(span, msg); - } else if span_is_primary && expanded_sub_count == 0 { - err.note(msg); - } else { - err.span_note(span, msg); - } - }; match *origin { - infer::Subtype(ref trace) => { - if let Some((expected, found)) = self.values_str(trace.values) { - label_or_note( - trace.cause.span, - &format!("...so that the {}", trace.cause.as_requirement_str()), - ); - - err.note_expected_found(&"", expected, &"", found); - } else { - // FIXME: this really should be handled at some earlier stage. Our - // handling of region checking when type errors are present is - // *terrible*. - - label_or_note( - trace.cause.span, - &format!("...so that {}", trace.cause.as_requirement_str()), - ); - } - } - infer::Reborrow(span) => { - label_or_note(span, "...so that reference does not outlive borrowed content"); + infer::Subtype(ref trace) => RegionOriginNote::WithRequirement { + span: trace.cause.span, + requirement: ObligationCauseAsDiagArg(trace.cause.clone()), + expected_found: self.values_str(trace.values), } + .add_to_diagnostic(err), + infer::Reborrow(span) => RegionOriginNote::Plain { span, msg: fluent::infer::reborrow } + .add_to_diagnostic(err), infer::ReborrowUpvar(span, ref upvar_id) => { let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); - label_or_note(span, &format!("...so that closure can access `{}`", var_name)); + RegionOriginNote::WithName { + span, + msg: fluent::infer::reborrow, + name: &var_name.to_string(), + continues: false, + } + .add_to_diagnostic(err); } infer::RelateObjectBound(span) => { - label_or_note(span, "...so that it can be closed over into an object"); + RegionOriginNote::Plain { span, msg: fluent::infer::relate_object_bound } + .add_to_diagnostic(err); } infer::DataBorrowed(ty, span) => { - label_or_note( + RegionOriginNote::WithName { span, - &format!( - "...so that the type `{}` is not borrowed for too long", - self.ty_to_string(ty) - ), - ); + msg: fluent::infer::data_borrowed, + name: &self.ty_to_string(ty), + continues: false, + } + .add_to_diagnostic(err); } infer::ReferenceOutlivesReferent(ty, span) => { - label_or_note( + RegionOriginNote::WithName { span, - &format!( - "...so that the reference type `{}` does not outlive the data it points at", - self.ty_to_string(ty) - ), - ); + msg: fluent::infer::reference_outlives_referent, + name: &self.ty_to_string(ty), + continues: false, + } + .add_to_diagnostic(err); } - infer::RelateParamBound(span, t, opt_span) => { - label_or_note( + infer::RelateParamBound(span, ty, opt_span) => { + RegionOriginNote::WithName { span, - &format!( - "...so that the type `{}` will meet its required lifetime bounds{}", - self.ty_to_string(t), - if opt_span.is_some() { "..." } else { "" }, - ), - ); + msg: fluent::infer::relate_param_bound, + name: &self.ty_to_string(ty), + continues: opt_span.is_some(), + } + .add_to_diagnostic(err); if let Some(span) = opt_span { - err.span_note(span, "...that is required by this bound"); + RegionOriginNote::Plain { span, msg: fluent::infer::relate_param_bound_2 } + .add_to_diagnostic(err); } } infer::RelateRegionParamBound(span) => { - label_or_note( - span, - "...so that the declared lifetime parameter bounds are satisfied", - ); + RegionOriginNote::Plain { span, msg: fluent::infer::relate_region_param_bound } + .add_to_diagnostic(err); } infer::CompareImplItemObligation { span, .. } => { - label_or_note( - span, - "...so that the definition in impl matches the definition from the trait", - ); + RegionOriginNote::Plain { span, msg: fluent::infer::compare_impl_item_obligation } + .add_to_diagnostic(err); } infer::CheckAssociatedTypeBounds { ref parent, .. } => { self.note_region_origin(err, &parent); } + infer::AscribeUserTypeProvePredicate(span) => { + RegionOriginNote::Plain { + span, + msg: fluent::infer::ascribe_user_type_prove_predicate, + } + .add_to_diagnostic(err); + } } } @@ -107,7 +96,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { match origin { infer::Subtype(box trace) => { let terr = TypeError::RegionsDoesNotOutlive(sup, sub); - let mut err = self.report_and_explain_type_error(trace, &terr); + let mut err = self.report_and_explain_type_error(trace, terr); match (*sub, *sup) { (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {} (ty::RePlaceholder(_), _) => { @@ -374,6 +363,27 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err } + infer::AscribeUserTypeProvePredicate(span) => { + let mut err = + struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied"); + note_and_explain_region( + self.tcx, + &mut err, + "lifetime instantiated with ", + sup, + "", + None, + ); + note_and_explain_region( + self.tcx, + &mut err, + "but lifetime must outlive ", + sub, + "", + None, + ); + err + } } } @@ -390,10 +400,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if matches!( &trace.cause.code().peel_derives(), ObligationCauseCode::BindingObligation(..) + | ObligationCauseCode::ExprBindingObligation(..) ) => { // Hack to get around the borrow checker because trace.cause has an `Rc`. - if let ObligationCauseCode::BindingObligation(_, span) = + if let ObligationCauseCode::BindingObligation(_, span) + | ObligationCauseCode::ExprBindingObligation(_, span, ..) = &trace.cause.code().peel_derives() { let span = *span; @@ -406,7 +418,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } infer::Subtype(box trace) => { let terr = TypeError::RegionsPlaceholderMismatch; - return self.report_and_explain_type_error(trace, &terr); + return self.report_and_explain_type_error(trace, terr); } _ => return self.report_concrete_failure(placeholder_origin, sub, sup), } |