diff options
Diffstat (limited to 'compiler/rustc_infer/src')
24 files changed, 46 insertions, 11620 deletions
diff --git a/compiler/rustc_infer/src/error_reporting/infer/mod.rs b/compiler/rustc_infer/src/error_reporting/infer/mod.rs deleted file mode 100644 index 9998fbca056..00000000000 --- a/compiler/rustc_infer/src/error_reporting/infer/mod.rs +++ /dev/null @@ -1,2221 +0,0 @@ -//! Error Reporting Code for the inference engine -//! -//! Because of the way inference, and in particular region inference, -//! works, it often happens that errors are not detected until far after -//! the relevant line of code has been type-checked. Therefore, there is -//! an elaborate system to track why a particular constraint in the -//! inference graph arose so that we can explain to the user what gave -//! rise to a particular error. -//! -//! The system is based around a set of "origin" types. An "origin" is the -//! reason that a constraint or inference variable arose. There are -//! different "origin" enums for different kinds of constraints/variables -//! (e.g., `TypeOrigin`, `RegionVariableOrigin`). An origin always has -//! a span, but also more information so that we can generate a meaningful -//! error message. -//! -//! Having a catalog of all the different reasons an error can arise is -//! also useful for other reasons, like cross-referencing FAQs etc, though -//! we are not really taking advantage of this yet. -//! -//! # Region Inference -//! -//! Region inference is particularly tricky because it always succeeds "in -//! the moment" and simply registers a constraint. Then, at the end, we -//! can compute the full graph and report errors, so we need to be able to -//! store and later report what gave rise to the conflicting constraints. -//! -//! # Subtype Trace -//! -//! Determining whether `T1 <: T2` often involves a number of subtypes and -//! subconstraints along the way. A "TypeTrace" is an extended version -//! of an origin that traces the types and other values that were being -//! compared. It is not necessarily comprehensive (in fact, at the time of -//! this writing it only tracks the root values being compared) but I'd -//! like to extend it to include significant "waypoints". For example, if -//! you are comparing `(T1, T2) <: (T3, T4)`, and the problem is that `T2 -//! <: T4` fails, I'd like the trace to include enough information to say -//! "in the 2nd element of the tuple". Similarly, failures when comparing -//! arguments or return types in fn types should be able to cite the -//! specific position, etc. -//! -//! # Reality vs plan -//! -//! Of course, there is still a LOT of code in typeck that has yet to be -//! ported to this system, and which relies on string concatenation at the -//! time of error detection. - -use std::borrow::Cow; -use std::ops::{ControlFlow, Deref}; -use std::path::PathBuf; -use std::{cmp, fmt, iter}; - -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_errors::{ - pluralize, Applicability, Diag, DiagCtxtHandle, DiagStyledString, IntoDiagArg, StringPart, -}; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; -use rustc_hir::lang_items::LangItem; -use rustc_hir::{self as hir}; -use rustc_macros::extension; -use rustc_middle::bug; -use rustc_middle::dep_graph::DepContext; -use rustc_middle::ty::error::ExpectedFound; -use rustc_middle::ty::error::TypeErrorToStringExt; -use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError, PrintTraitRefExt as _}; -use rustc_middle::ty::{ - self, error::TypeError, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, -}; -use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span}; -use rustc_target::spec::abi; - -use crate::errors::{ObligationCauseFailureCode, TypeErrorAdditionalDiags}; -use crate::infer; -use crate::infer::relate::{self, RelateResult, TypeRelation}; -use crate::infer::{InferCtxt, TypeTrace, ValuePairs}; -use crate::traits::{ - IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, - PredicateObligation, -}; - -mod note_and_explain; -mod suggest; - -pub mod region; -pub mod sub_relations; - -pub mod nice_region_error; - -/// Makes a valid string literal from a string by escaping special characters (" and \), -/// unless they are already escaped. -fn escape_literal(s: &str) -> String { - let mut escaped = String::with_capacity(s.len()); - let mut chrs = s.chars().peekable(); - while let Some(first) = chrs.next() { - match (first, chrs.peek()) { - ('\\', Some(&delim @ '"') | Some(&delim @ '\'')) => { - escaped.push('\\'); - escaped.push(delim); - chrs.next(); - } - ('"' | '\'', _) => { - escaped.push('\\'); - escaped.push(first) - } - (c, _) => escaped.push(c), - }; - } - escaped -} - -/// A helper for building type related errors. The `typeck_results` -/// field is only populated during an in-progress typeck. -/// Get an instance by calling `InferCtxt::err_ctxt` or `FnCtxt::err_ctxt`. -/// -/// You must only create this if you intend to actually emit an error (or -/// perhaps a warning, though preferably not.) It provides a lot of utility -/// methods which should not be used during the happy path. -pub struct TypeErrCtxt<'a, 'tcx> { - pub infcx: &'a InferCtxt<'tcx>, - pub sub_relations: std::cell::RefCell<sub_relations::SubRelations>, - - pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>, - pub fallback_has_occurred: bool, - - pub normalize_fn_sig: Box<dyn Fn(ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> + 'a>, - - pub autoderef_steps: - Box<dyn Fn(Ty<'tcx>) -> Vec<(Ty<'tcx>, Vec<PredicateObligation<'tcx>>)> + 'a>, -} - -impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - pub fn dcx(&self) -> DiagCtxtHandle<'a> { - self.infcx.dcx() - } - - /// This is just to avoid a potential footgun of accidentally - /// dropping `typeck_results` by calling `InferCtxt::err_ctxt` - #[deprecated(note = "you already have a `TypeErrCtxt`")] - #[allow(unused)] - pub fn err_ctxt(&self) -> ! { - bug!("called `err_ctxt` on `TypeErrCtxt`. Try removing the call"); - } -} - -impl<'tcx> Deref for TypeErrCtxt<'_, 'tcx> { - type Target = InferCtxt<'tcx>; - fn deref(&self) -> &InferCtxt<'tcx> { - self.infcx - } -} - -impl<'tcx> InferCtxt<'tcx> { - pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { - let (def_id, args) = match *ty.kind() { - ty::Alias(_, ty::AliasTy { def_id, args, .. }) - if matches!(self.tcx.def_kind(def_id), DefKind::OpaqueTy) => - { - (def_id, args) - } - ty::Alias(_, ty::AliasTy { def_id, args, .. }) - if self.tcx.is_impl_trait_in_trait(def_id) => - { - (def_id, args) - } - _ => return None, - }; - - let future_trait = self.tcx.require_lang_item(LangItem::Future, None); - let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; - - self.tcx - .explicit_item_super_predicates(def_id) - .iter_instantiated_copied(self.tcx, args) - .find_map(|(predicate, _)| { - predicate - .kind() - .map_bound(|kind| match kind { - ty::ClauseKind::Projection(projection_predicate) - if projection_predicate.projection_term.def_id == item_def_id => - { - projection_predicate.term.as_type() - } - _ => None, - }) - .no_bound_vars() - .flatten() - }) - } -} - -impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - /// Adds a note if the types come from similarly named crates - fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) { - use hir::def_id::CrateNum; - use rustc_hir::definitions::DisambiguatedDefPathData; - use ty::print::Printer; - use ty::GenericArg; - - struct AbsolutePathPrinter<'tcx> { - tcx: TyCtxt<'tcx>, - segments: Vec<String>, - } - - impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { - fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { - self.tcx - } - - fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> { - Err(fmt::Error) - } - - fn print_type(&mut self, _ty: Ty<'tcx>) -> Result<(), PrintError> { - Err(fmt::Error) - } - - fn print_dyn_existential( - &mut self, - _predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, - ) -> Result<(), PrintError> { - Err(fmt::Error) - } - - fn print_const(&mut self, _ct: ty::Const<'tcx>) -> Result<(), PrintError> { - Err(fmt::Error) - } - - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { - self.segments = vec![self.tcx.crate_name(cnum).to_string()]; - Ok(()) - } - fn path_qualified( - &mut self, - _self_ty: Ty<'tcx>, - _trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<(), PrintError> { - Err(fmt::Error) - } - - fn path_append_impl( - &mut self, - _print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, - _disambiguated_data: &DisambiguatedDefPathData, - _self_ty: Ty<'tcx>, - _trait_ref: Option<ty::TraitRef<'tcx>>, - ) -> Result<(), PrintError> { - Err(fmt::Error) - } - fn path_append( - &mut self, - print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, - disambiguated_data: &DisambiguatedDefPathData, - ) -> Result<(), PrintError> { - print_prefix(self)?; - self.segments.push(disambiguated_data.to_string()); - Ok(()) - } - fn path_generic_args( - &mut self, - print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, - _args: &[GenericArg<'tcx>], - ) -> Result<(), PrintError> { - print_prefix(self) - } - } - - let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId| { - // Only report definitions from different crates. If both definitions - // are from a local module we could have false positives, e.g. - // let _ = [{struct Foo; Foo}, {struct Foo; Foo}]; - if did1.krate != did2.krate { - let abs_path = |def_id| { - let mut printer = AbsolutePathPrinter { tcx: self.tcx, segments: vec![] }; - printer.print_def_path(def_id, &[]).map(|_| printer.segments) - }; - - // We compare strings because DefPath can be different - // for imported and non-imported crates - let same_path = || -> Result<_, PrintError> { - Ok(self.tcx.def_path_str(did1) == self.tcx.def_path_str(did2) - || abs_path(did1)? == abs_path(did2)?) - }; - if same_path().unwrap_or(false) { - let crate_name = self.tcx.crate_name(did1.krate); - let msg = if did1.is_local() || did2.is_local() { - format!( - "the crate `{crate_name}` is compiled multiple times, possibly with different configurations" - ) - } else { - format!( - "perhaps two different versions of crate `{crate_name}` are being used?" - ) - }; - err.note(msg); - } - } - }; - 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 - if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) = - (exp_found.expected.kind(), exp_found.found.kind()) - { - report_path_match(err, exp_adt.did(), found_adt.did()); - } - } - TypeError::Traits(ref exp_found) => { - report_path_match(err, exp_found.expected, exp_found.found); - } - _ => (), // FIXME(#22750) handle traits and stuff - } - } - - fn note_error_origin( - &self, - err: &mut Diag<'_>, - cause: &ObligationCause<'tcx>, - exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>, - terr: TypeError<'tcx>, - ) { - match *cause.code() { - ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => { - let ty = self.resolve_vars_if_possible(root_ty); - if !matches!(ty.kind(), ty::Infer(ty::InferTy::TyVar(_) | ty::InferTy::FreshTy(_))) - { - // don't show type `_` - if span.desugaring_kind() == Some(DesugaringKind::ForLoop) - && let ty::Adt(def, args) = ty.kind() - && Some(def.did()) == self.tcx.get_diagnostic_item(sym::Option) - { - err.span_label( - span, - format!("this is an iterator with items of type `{}`", args.type_at(0)), - ); - } else { - err.span_label(span, format!("this expression has type `{ty}`")); - } - } - if let Some(ty::error::ExpectedFound { found, .. }) = exp_found - && ty.is_box() - && ty.boxed_ty() == found - && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) - { - err.span_suggestion( - span, - "consider dereferencing the boxed value", - format!("*{snippet}"), - Applicability::MachineApplicable, - ); - } - } - ObligationCauseCode::Pattern { origin_expr: false, span: Some(span), .. } => { - err.span_label(span, "expected due to this"); - } - ObligationCauseCode::BlockTailExpression( - _, - hir::MatchSource::TryDesugar(scrut_hir_id), - ) => { - if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found { - let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id); - let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind { - let arg_expr = args.first().expect("try desugaring call w/out arg"); - self.typeck_results - .as_ref() - .and_then(|typeck_results| typeck_results.expr_ty_opt(arg_expr)) - } else { - bug!("try desugaring w/out call expr as scrutinee"); - }; - - match scrut_ty { - Some(ty) if expected == ty => { - let source_map = self.tcx.sess.source_map(); - err.span_suggestion( - source_map.end_point(cause.span()), - "try removing this `?`", - "", - Applicability::MachineApplicable, - ); - } - _ => {} - } - } - } - ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { - arm_block_id, - arm_span, - arm_ty, - prior_arm_block_id, - prior_arm_span, - prior_arm_ty, - source, - ref prior_non_diverging_arms, - scrut_span, - .. - }) => match source { - hir::MatchSource::TryDesugar(scrut_hir_id) => { - if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found { - let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id); - let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind { - let arg_expr = args.first().expect("try desugaring call w/out arg"); - self.typeck_results - .as_ref() - .and_then(|typeck_results| typeck_results.expr_ty_opt(arg_expr)) - } else { - bug!("try desugaring w/out call expr as scrutinee"); - }; - - match scrut_ty { - Some(ty) if expected == ty => { - let source_map = self.tcx.sess.source_map(); - err.span_suggestion( - source_map.end_point(cause.span()), - "try removing this `?`", - "", - Applicability::MachineApplicable, - ); - } - _ => {} - } - } - } - _ => { - // `prior_arm_ty` can be `!`, `expected` will have better info when present. - let t = self.resolve_vars_if_possible(match exp_found { - Some(ty::error::ExpectedFound { expected, .. }) => expected, - _ => prior_arm_ty, - }); - let source_map = self.tcx.sess.source_map(); - let mut any_multiline_arm = source_map.is_multiline(arm_span); - if prior_non_diverging_arms.len() <= 4 { - for sp in prior_non_diverging_arms { - any_multiline_arm |= source_map.is_multiline(*sp); - err.span_label(*sp, format!("this is found to be of type `{t}`")); - } - } else if let Some(sp) = prior_non_diverging_arms.last() { - any_multiline_arm |= source_map.is_multiline(*sp); - err.span_label( - *sp, - format!("this and all prior arms are found to be of type `{t}`"), - ); - } - let outer = if any_multiline_arm || !source_map.is_multiline(cause.span) { - // Cover just `match` and the scrutinee expression, not - // the entire match body, to reduce diagram noise. - cause.span.shrink_to_lo().to(scrut_span) - } else { - cause.span - }; - let msg = "`match` arms have incompatible types"; - err.span_label(outer, msg); - if let Some(subdiag) = self.suggest_remove_semi_or_return_binding( - prior_arm_block_id, - prior_arm_ty, - prior_arm_span, - arm_block_id, - arm_ty, - arm_span, - ) { - err.subdiagnostic(subdiag); - } - } - }, - ObligationCauseCode::IfExpression(box IfExpressionCause { - then_id, - else_id, - then_ty, - else_ty, - outer_span, - .. - }) => { - let then_span = self.find_block_span_from_hir_id(then_id); - let else_span = self.find_block_span_from_hir_id(else_id); - err.span_label(then_span, "expected because of this"); - if let Some(sp) = outer_span { - err.span_label(sp, "`if` and `else` have incompatible types"); - } - if let Some(subdiag) = self.suggest_remove_semi_or_return_binding( - Some(then_id), - then_ty, - then_span, - Some(else_id), - else_ty, - else_span, - ) { - err.subdiagnostic(subdiag); - } - } - ObligationCauseCode::LetElse => { - err.help("try adding a diverging expression, such as `return` or `panic!(..)`"); - err.help("...or use `match` instead of `let...else`"); - } - _ => { - if let ObligationCauseCode::WhereClause(_, span) - | ObligationCauseCode::WhereClauseInExpr(_, span, ..) = - cause.code().peel_derives() - && !span.is_dummy() - && let TypeError::RegionsPlaceholderMismatch = terr - { - err.span_note(*span, "the lifetime requirement is introduced here"); - } - } - } - } - - /// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value` - /// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and - /// populate `other_value` with `other_ty`. - /// - /// ```text - /// Foo<Bar<Qux>> - /// ^^^^--------^ this is highlighted - /// | | - /// | this type argument is exactly the same as the other type, not highlighted - /// this is highlighted - /// Bar<Qux> - /// -------- this type is the same as a type argument in the other type, not highlighted - /// ``` - fn highlight_outer( - &self, - value: &mut DiagStyledString, - other_value: &mut DiagStyledString, - name: String, - sub: ty::GenericArgsRef<'tcx>, - pos: usize, - other_ty: Ty<'tcx>, - ) { - // `value` and `other_value` hold two incomplete type representation for display. - // `name` is the path of both types being compared. `sub` - value.push_highlighted(name); - let len = sub.len(); - if len > 0 { - value.push_highlighted("<"); - } - - // Output the lifetimes for the first type - let lifetimes = sub - .regions() - .map(|lifetime| { - let s = lifetime.to_string(); - if s.is_empty() { "'_".to_string() } else { s } - }) - .collect::<Vec<_>>() - .join(", "); - if !lifetimes.is_empty() { - if sub.regions().count() < len { - value.push_normal(lifetimes + ", "); - } else { - value.push_normal(lifetimes); - } - } - - // Highlight all the type arguments that aren't at `pos` and compare the type argument at - // `pos` and `other_ty`. - for (i, type_arg) in sub.types().enumerate() { - if i == pos { - let values = self.cmp(type_arg, other_ty); - value.0.extend((values.0).0); - other_value.0.extend((values.1).0); - } else { - value.push_highlighted(type_arg.to_string()); - } - - if len > 0 && i != len - 1 { - value.push_normal(", "); - } - } - if len > 0 { - value.push_highlighted(">"); - } - } - - /// If `other_ty` is the same as a type argument present in `sub`, highlight `path` in `t1_out`, - /// as that is the difference to the other type. - /// - /// For the following code: - /// - /// ```ignore (illustrative) - /// let x: Foo<Bar<Qux>> = foo::<Bar<Qux>>(); - /// ``` - /// - /// The type error output will behave in the following way: - /// - /// ```text - /// Foo<Bar<Qux>> - /// ^^^^--------^ this is highlighted - /// | | - /// | this type argument is exactly the same as the other type, not highlighted - /// this is highlighted - /// Bar<Qux> - /// -------- this type is the same as a type argument in the other type, not highlighted - /// ``` - fn cmp_type_arg( - &self, - t1_out: &mut DiagStyledString, - t2_out: &mut DiagStyledString, - path: String, - sub: &'tcx [ty::GenericArg<'tcx>], - other_path: String, - other_ty: Ty<'tcx>, - ) -> Option<()> { - // FIXME/HACK: Go back to `GenericArgsRef` to use its inherent methods, - // ideally that shouldn't be necessary. - let sub = self.tcx.mk_args(sub); - for (i, ta) in sub.types().enumerate() { - if ta == other_ty { - self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty); - return Some(()); - } - if let ty::Adt(def, _) = ta.kind() { - let path_ = self.tcx.def_path_str(def.did()); - if path_ == other_path { - self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty); - return Some(()); - } - } - } - None - } - - /// Adds a `,` to the type representation only if it is appropriate. - fn push_comma( - &self, - value: &mut DiagStyledString, - other_value: &mut DiagStyledString, - len: usize, - pos: usize, - ) { - if len > 0 && pos != len - 1 { - value.push_normal(", "); - other_value.push_normal(", "); - } - } - - /// Given two `fn` signatures highlight only sub-parts that are different. - fn cmp_fn_sig( - &self, - sig1: &ty::PolyFnSig<'tcx>, - sig2: &ty::PolyFnSig<'tcx>, - ) -> (DiagStyledString, DiagStyledString) { - let sig1 = &(self.normalize_fn_sig)(*sig1); - let sig2 = &(self.normalize_fn_sig)(*sig2); - - let get_lifetimes = |sig| { - use rustc_hir::def::Namespace; - let (sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS) - .name_all_regions(sig) - .unwrap(); - let lts: Vec<String> = - reg.into_items().map(|(_, kind)| kind.to_string()).into_sorted_stable_ord(); - (if lts.is_empty() { String::new() } else { format!("for<{}> ", lts.join(", ")) }, sig) - }; - - let (lt1, sig1) = get_lifetimes(sig1); - let (lt2, sig2) = get_lifetimes(sig2); - - // unsafe extern "C" for<'a> fn(&'a T) -> &'a T - let mut values = - (DiagStyledString::normal("".to_string()), DiagStyledString::normal("".to_string())); - - // unsafe extern "C" for<'a> fn(&'a T) -> &'a T - // ^^^^^^ - values.0.push(sig1.safety.prefix_str(), sig1.safety != sig2.safety); - values.1.push(sig2.safety.prefix_str(), sig1.safety != sig2.safety); - - // unsafe extern "C" for<'a> fn(&'a T) -> &'a T - // ^^^^^^^^^^ - if sig1.abi != abi::Abi::Rust { - values.0.push(format!("extern {} ", sig1.abi), sig1.abi != sig2.abi); - } - if sig2.abi != abi::Abi::Rust { - values.1.push(format!("extern {} ", sig2.abi), sig1.abi != sig2.abi); - } - - // unsafe extern "C" for<'a> fn(&'a T) -> &'a T - // ^^^^^^^^ - let lifetime_diff = lt1 != lt2; - values.0.push(lt1, lifetime_diff); - values.1.push(lt2, lifetime_diff); - - // unsafe extern "C" for<'a> fn(&'a T) -> &'a T - // ^^^ - values.0.push_normal("fn("); - values.1.push_normal("fn("); - - // unsafe extern "C" for<'a> fn(&'a T) -> &'a T - // ^^^^^ - let len1 = sig1.inputs().len(); - let len2 = sig2.inputs().len(); - if len1 == len2 { - for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() { - let (x1, x2) = self.cmp(*l, *r); - (values.0).0.extend(x1.0); - (values.1).0.extend(x2.0); - self.push_comma(&mut values.0, &mut values.1, len1, i); - } - } else { - for (i, l) in sig1.inputs().iter().enumerate() { - values.0.push_highlighted(l.to_string()); - if i != len1 - 1 { - values.0.push_highlighted(", "); - } - } - for (i, r) in sig2.inputs().iter().enumerate() { - values.1.push_highlighted(r.to_string()); - if i != len2 - 1 { - values.1.push_highlighted(", "); - } - } - } - - if sig1.c_variadic { - if len1 > 0 { - values.0.push_normal(", "); - } - values.0.push("...", !sig2.c_variadic); - } - if sig2.c_variadic { - if len2 > 0 { - values.1.push_normal(", "); - } - values.1.push("...", !sig1.c_variadic); - } - - // unsafe extern "C" for<'a> fn(&'a T) -> &'a T - // ^ - values.0.push_normal(")"); - values.1.push_normal(")"); - - // unsafe extern "C" for<'a> fn(&'a T) -> &'a T - // ^^^^^^^^ - let output1 = sig1.output(); - let output2 = sig2.output(); - let (x1, x2) = self.cmp(output1, output2); - let output_diff = x1 != x2; - if !output1.is_unit() || output_diff { - values.0.push_normal(" -> "); - (values.0).0.extend(x1.0); - } - if !output2.is_unit() || output_diff { - values.1.push_normal(" -> "); - (values.1).0.extend(x2.0); - } - - values - } - - /// Compares two given types, eliding parts that are the same between them and highlighting - /// relevant differences, and return two representation of those types for highlighted printing. - pub fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagStyledString, DiagStyledString) { - debug!("cmp(t1={}, t1.kind={:?}, t2={}, t2.kind={:?})", t1, t1.kind(), t2, t2.kind()); - - // helper functions - let recurse = |t1, t2, values: &mut (DiagStyledString, DiagStyledString)| { - let (x1, x2) = self.cmp(t1, t2); - (values.0).0.extend(x1.0); - (values.1).0.extend(x2.0); - }; - - fn fmt_region<'tcx>(region: ty::Region<'tcx>) -> String { - let mut r = region.to_string(); - if r == "'_" { - r.clear(); - } else { - r.push(' '); - } - format!("&{r}") - } - - fn push_ref<'tcx>( - region: ty::Region<'tcx>, - mutbl: hir::Mutability, - s: &mut DiagStyledString, - ) { - s.push_highlighted(fmt_region(region)); - s.push_highlighted(mutbl.prefix_str()); - } - - fn maybe_highlight<T: Eq + ToString>( - t1: T, - t2: T, - (buf1, buf2): &mut (DiagStyledString, DiagStyledString), - tcx: TyCtxt<'_>, - ) { - let highlight = t1 != t2; - let (t1, t2) = if highlight || tcx.sess.opts.verbose { - (t1.to_string(), t2.to_string()) - } else { - // The two types are the same, elide and don't highlight. - ("_".into(), "_".into()) - }; - buf1.push(t1, highlight); - buf2.push(t2, highlight); - } - - fn cmp_ty_refs<'tcx>( - r1: ty::Region<'tcx>, - mut1: hir::Mutability, - r2: ty::Region<'tcx>, - mut2: hir::Mutability, - ss: &mut (DiagStyledString, DiagStyledString), - ) { - let (r1, r2) = (fmt_region(r1), fmt_region(r2)); - if r1 != r2 { - ss.0.push_highlighted(r1); - ss.1.push_highlighted(r2); - } else { - ss.0.push_normal(r1); - ss.1.push_normal(r2); - } - - if mut1 != mut2 { - ss.0.push_highlighted(mut1.prefix_str()); - ss.1.push_highlighted(mut2.prefix_str()); - } else { - ss.0.push_normal(mut1.prefix_str()); - ss.1.push_normal(mut2.prefix_str()); - } - } - - // process starts here - match (t1.kind(), t2.kind()) { - (&ty::Adt(def1, sub1), &ty::Adt(def2, sub2)) => { - let did1 = def1.did(); - let did2 = def2.did(); - - let generics1 = self.tcx.generics_of(did1); - let generics2 = self.tcx.generics_of(did2); - - let non_default_after_default = generics1 - .check_concrete_type_after_default(self.tcx, sub1) - || generics2.check_concrete_type_after_default(self.tcx, sub2); - let sub_no_defaults_1 = if non_default_after_default { - generics1.own_args(sub1) - } else { - generics1.own_args_no_defaults(self.tcx, sub1) - }; - let sub_no_defaults_2 = if non_default_after_default { - generics2.own_args(sub2) - } else { - generics2.own_args_no_defaults(self.tcx, sub2) - }; - let mut values = (DiagStyledString::new(), DiagStyledString::new()); - let path1 = self.tcx.def_path_str(did1); - let path2 = self.tcx.def_path_str(did2); - if did1 == did2 { - // Easy case. Replace same types with `_` to shorten the output and highlight - // the differing ones. - // let x: Foo<Bar, Qux> = y::<Foo<Quz, Qux>>(); - // Foo<Bar, _> - // Foo<Quz, _> - // --- ^ type argument elided - // | - // highlighted in output - values.0.push_normal(path1); - values.1.push_normal(path2); - - // Avoid printing out default generic parameters that are common to both - // types. - let len1 = sub_no_defaults_1.len(); - let len2 = sub_no_defaults_2.len(); - let common_len = cmp::min(len1, len2); - let remainder1: Vec<_> = sub1.types().skip(common_len).collect(); - let remainder2: Vec<_> = sub2.types().skip(common_len).collect(); - let common_default_params = - iter::zip(remainder1.iter().rev(), remainder2.iter().rev()) - .filter(|(a, b)| a == b) - .count(); - let len = sub1.len() - common_default_params; - let consts_offset = len - sub1.consts().count(); - - // Only draw `<...>` if there are lifetime/type arguments. - if len > 0 { - values.0.push_normal("<"); - values.1.push_normal("<"); - } - - fn lifetime_display(lifetime: Region<'_>) -> String { - let s = lifetime.to_string(); - if s.is_empty() { "'_".to_string() } else { s } - } - // At one point we'd like to elide all lifetimes here, they are irrelevant for - // all diagnostics that use this output - // - // Foo<'x, '_, Bar> - // Foo<'y, '_, Qux> - // ^^ ^^ --- type arguments are not elided - // | | - // | elided as they were the same - // not elided, they were different, but irrelevant - // - // For bound lifetimes, keep the names of the lifetimes, - // even if they are the same so that it's clear what's happening - // if we have something like - // - // for<'r, 's> fn(Inv<'r>, Inv<'s>) - // for<'r> fn(Inv<'r>, Inv<'r>) - let lifetimes = sub1.regions().zip(sub2.regions()); - for (i, lifetimes) in lifetimes.enumerate() { - let l1 = lifetime_display(lifetimes.0); - let l2 = lifetime_display(lifetimes.1); - if lifetimes.0 != lifetimes.1 { - values.0.push_highlighted(l1); - values.1.push_highlighted(l2); - } else if lifetimes.0.is_bound() || self.tcx.sess.opts.verbose { - values.0.push_normal(l1); - values.1.push_normal(l2); - } else { - values.0.push_normal("'_"); - values.1.push_normal("'_"); - } - self.push_comma(&mut values.0, &mut values.1, len, i); - } - - // We're comparing two types with the same path, so we compare the type - // arguments for both. If they are the same, do not highlight and elide from the - // output. - // Foo<_, Bar> - // Foo<_, Qux> - // ^ elided type as this type argument was the same in both sides - let type_arguments = sub1.types().zip(sub2.types()); - let regions_len = sub1.regions().count(); - let num_display_types = consts_offset - regions_len; - for (i, (ta1, ta2)) in type_arguments.take(num_display_types).enumerate() { - let i = i + regions_len; - if ta1 == ta2 && !self.tcx.sess.opts.verbose { - values.0.push_normal("_"); - values.1.push_normal("_"); - } else { - recurse(ta1, ta2, &mut values); - } - self.push_comma(&mut values.0, &mut values.1, len, i); - } - - // Do the same for const arguments, if they are equal, do not highlight and - // elide them from the output. - let const_arguments = sub1.consts().zip(sub2.consts()); - for (i, (ca1, ca2)) in const_arguments.enumerate() { - let i = i + consts_offset; - maybe_highlight(ca1, ca2, &mut values, self.tcx); - self.push_comma(&mut values.0, &mut values.1, len, i); - } - - // Close the type argument bracket. - // Only draw `<...>` if there are lifetime/type arguments. - if len > 0 { - values.0.push_normal(">"); - values.1.push_normal(">"); - } - values - } else { - // Check for case: - // let x: Foo<Bar<Qux> = foo::<Bar<Qux>>(); - // Foo<Bar<Qux> - // ------- this type argument is exactly the same as the other type - // Bar<Qux> - if self - .cmp_type_arg( - &mut values.0, - &mut values.1, - path1.clone(), - sub_no_defaults_1, - path2.clone(), - t2, - ) - .is_some() - { - return values; - } - // Check for case: - // let x: Bar<Qux> = y:<Foo<Bar<Qux>>>(); - // Bar<Qux> - // Foo<Bar<Qux>> - // ------- this type argument is exactly the same as the other type - if self - .cmp_type_arg( - &mut values.1, - &mut values.0, - path2, - sub_no_defaults_2, - path1, - t1, - ) - .is_some() - { - return values; - } - - // We can't find anything in common, highlight relevant part of type path. - // let x: foo::bar::Baz<Qux> = y:<foo::bar::Bar<Zar>>(); - // foo::bar::Baz<Qux> - // foo::bar::Bar<Zar> - // -------- this part of the path is different - - let t1_str = t1.to_string(); - let t2_str = t2.to_string(); - let min_len = t1_str.len().min(t2_str.len()); - - const SEPARATOR: &str = "::"; - let separator_len = SEPARATOR.len(); - let split_idx: usize = - iter::zip(t1_str.split(SEPARATOR), t2_str.split(SEPARATOR)) - .take_while(|(mod1_str, mod2_str)| mod1_str == mod2_str) - .map(|(mod_str, _)| mod_str.len() + separator_len) - .sum(); - - debug!(?separator_len, ?split_idx, ?min_len, "cmp"); - - if split_idx >= min_len { - // paths are identical, highlight everything - ( - DiagStyledString::highlighted(t1_str), - DiagStyledString::highlighted(t2_str), - ) - } else { - let (common, uniq1) = t1_str.split_at(split_idx); - let (_, uniq2) = t2_str.split_at(split_idx); - debug!(?common, ?uniq1, ?uniq2, "cmp"); - - values.0.push_normal(common); - values.0.push_highlighted(uniq1); - values.1.push_normal(common); - values.1.push_highlighted(uniq2); - - values - } - } - } - - // When finding `&T != &T`, compare the references, then recurse into pointee type - (&ty::Ref(r1, ref_ty1, mutbl1), &ty::Ref(r2, ref_ty2, mutbl2)) => { - let mut values = (DiagStyledString::new(), DiagStyledString::new()); - cmp_ty_refs(r1, mutbl1, r2, mutbl2, &mut values); - recurse(ref_ty1, ref_ty2, &mut values); - values - } - // When finding T != &T, highlight the borrow - (&ty::Ref(r1, ref_ty1, mutbl1), _) => { - let mut values = (DiagStyledString::new(), DiagStyledString::new()); - push_ref(r1, mutbl1, &mut values.0); - recurse(ref_ty1, t2, &mut values); - values - } - (_, &ty::Ref(r2, ref_ty2, mutbl2)) => { - let mut values = (DiagStyledString::new(), DiagStyledString::new()); - push_ref(r2, mutbl2, &mut values.1); - recurse(t1, ref_ty2, &mut values); - values - } - - // When encountering tuples of the same size, highlight only the differing types - (&ty::Tuple(args1), &ty::Tuple(args2)) if args1.len() == args2.len() => { - let mut values = (DiagStyledString::normal("("), DiagStyledString::normal("(")); - let len = args1.len(); - for (i, (left, right)) in args1.iter().zip(args2).enumerate() { - recurse(left, right, &mut values); - self.push_comma(&mut values.0, &mut values.1, len, i); - } - if len == 1 { - // Keep the output for single element tuples as `(ty,)`. - values.0.push_normal(","); - values.1.push_normal(","); - } - values.0.push_normal(")"); - values.1.push_normal(")"); - values - } - - (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => { - let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1); - let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2); - let mut values = self.cmp_fn_sig(&sig1, &sig2); - let path1 = format!(" {{{}}}", self.tcx.def_path_str_with_args(*did1, args1)); - let path2 = format!(" {{{}}}", self.tcx.def_path_str_with_args(*did2, args2)); - let same_path = path1 == path2; - values.0.push(path1, !same_path); - values.1.push(path2, !same_path); - values - } - - (ty::FnDef(did1, args1), ty::FnPtr(sig2)) => { - let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1); - let mut values = self.cmp_fn_sig(&sig1, sig2); - values.0.push_highlighted(format!( - " {{{}}}", - self.tcx.def_path_str_with_args(*did1, args1) - )); - values - } - - (ty::FnPtr(sig1), ty::FnDef(did2, args2)) => { - let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2); - let mut values = self.cmp_fn_sig(sig1, &sig2); - values - .1 - .push_normal(format!(" {{{}}}", self.tcx.def_path_str_with_args(*did2, args2))); - values - } - - (ty::FnPtr(sig1), ty::FnPtr(sig2)) => self.cmp_fn_sig(sig1, sig2), - - _ => { - let mut strs = (DiagStyledString::new(), DiagStyledString::new()); - maybe_highlight(t1, t2, &mut strs, self.tcx); - strs - } - } - } - - /// Extend a type error with extra labels pointing at "non-trivial" types, like closures and - /// the return type of `async fn`s. - /// - /// `secondary_span` gives the caller the opportunity to expand `diag` with a `span_label`. - /// - /// `swap_secondary_and_primary` is used to make projection errors in particular nicer by using - /// 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 `tests/ui/issues/issue-39970.stderr`. - #[instrument( - level = "debug", - skip(self, diag, secondary_span, swap_secondary_and_primary, prefer_label) - )] - pub fn note_type_err( - &self, - diag: &mut Diag<'_>, - cause: &ObligationCause<'tcx>, - secondary_span: Option<(Span, Cow<'static, str>)>, - mut values: Option<ValuePairs<'tcx>>, - terr: TypeError<'tcx>, - swap_secondary_and_primary: bool, - prefer_label: bool, - ) { - let span = cause.span(); - - // For some types of errors, expected-found does not make - // sense, so just ignore the values we were given. - if let TypeError::CyclicTy(_) = terr { - values = None; - } - struct OpaqueTypesVisitor<'tcx> { - types: FxIndexMap<TyCategory, FxIndexSet<Span>>, - expected: FxIndexMap<TyCategory, FxIndexSet<Span>>, - found: FxIndexMap<TyCategory, FxIndexSet<Span>>, - ignore_span: Span, - tcx: TyCtxt<'tcx>, - } - - impl<'tcx> OpaqueTypesVisitor<'tcx> { - fn visit_expected_found( - tcx: TyCtxt<'tcx>, - expected: impl TypeVisitable<TyCtxt<'tcx>>, - found: impl TypeVisitable<TyCtxt<'tcx>>, - ignore_span: Span, - ) -> Self { - let mut types_visitor = OpaqueTypesVisitor { - types: Default::default(), - expected: Default::default(), - found: Default::default(), - ignore_span, - tcx, - }; - // The visitor puts all the relevant encountered types in `self.types`, but in - // here we want to visit two separate types with no relation to each other, so we - // move the results from `types` to `expected` or `found` as appropriate. - expected.visit_with(&mut types_visitor); - std::mem::swap(&mut types_visitor.expected, &mut types_visitor.types); - found.visit_with(&mut types_visitor); - std::mem::swap(&mut types_visitor.found, &mut types_visitor.types); - types_visitor - } - - fn report(&self, err: &mut Diag<'_>) { - self.add_labels_for_types(err, "expected", &self.expected); - self.add_labels_for_types(err, "found", &self.found); - } - - fn add_labels_for_types( - &self, - err: &mut Diag<'_>, - target: &str, - types: &FxIndexMap<TyCategory, FxIndexSet<Span>>, - ) { - for (kind, values) in types.iter() { - let count = values.len(); - for &sp in values { - err.span_label( - sp, - format!( - "{}{} {:#}{}", - if count == 1 { "the " } else { "one of the " }, - target, - kind, - pluralize!(count), - ), - ); - } - } - } - } - - impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for OpaqueTypesVisitor<'tcx> { - fn visit_ty(&mut self, t: Ty<'tcx>) { - if let Some((kind, def_id)) = TyCategory::from_ty(self.tcx, t) { - let span = self.tcx.def_span(def_id); - // Avoid cluttering the output when the "found" and error span overlap: - // - // error[E0308]: mismatched types - // --> $DIR/issue-20862.rs:2:5 - // | - // LL | |y| x + y - // | ^^^^^^^^^ - // | | - // | the found closure - // | expected `()`, found closure - // | - // = note: expected unit type `()` - // found closure `{closure@$DIR/issue-20862.rs:2:5: 2:14 x:_}` - // - // Also ignore opaque `Future`s that come from async fns. - if !self.ignore_span.overlaps(span) - && !span.is_desugaring(DesugaringKind::Async) - { - self.types.entry(kind).or_default().insert(span); - } - } - t.super_visit_with(self) - } - } - - debug!("note_type_err(diag={:?})", diag); - enum Mismatch<'a> { - Variable(ty::error::ExpectedFound<Ty<'a>>), - Fixed(&'static str), - } - let (expected_found, exp_found, is_simple_error, values) = match values { - None => (None, Mismatch::Fixed("type"), false, None), - Some(values) => { - let values = self.resolve_vars_if_possible(values); - let (is_simple_error, exp_found) = match values { - ValuePairs::Terms(ExpectedFound { expected, found }) => { - match (expected.unpack(), found.unpack()) { - (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => { - let is_simple_err = expected.is_simple_text(self.tcx) - && found.is_simple_text(self.tcx); - OpaqueTypesVisitor::visit_expected_found( - self.tcx, expected, found, span, - ) - .report(diag); - - ( - is_simple_err, - Mismatch::Variable(ExpectedFound { expected, found }), - ) - } - (ty::TermKind::Const(_), ty::TermKind::Const(_)) => { - (false, Mismatch::Fixed("constant")) - } - _ => (false, Mismatch::Fixed("type")), - } - } - ValuePairs::PolySigs(ExpectedFound { expected, found }) => { - OpaqueTypesVisitor::visit_expected_found(self.tcx, expected, found, span) - .report(diag); - (false, Mismatch::Fixed("signature")) - } - ValuePairs::TraitRefs(_) => (false, Mismatch::Fixed("trait")), - ValuePairs::Aliases(ExpectedFound { expected, .. }) => { - (false, Mismatch::Fixed(self.tcx.def_descr(expected.def_id))) - } - ValuePairs::Regions(_) => (false, Mismatch::Fixed("lifetime")), - ValuePairs::ExistentialTraitRef(_) => { - (false, Mismatch::Fixed("existential trait ref")) - } - ValuePairs::ExistentialProjection(_) => { - (false, Mismatch::Fixed("existential projection")) - } - ValuePairs::Dummy => { - bug!("do not expect to report a type error from a ValuePairs::Dummy") - } - }; - let Some(vals) = self.values_str(values) else { - // Derived error. Cancel the emitter. - // NOTE(eddyb) this was `.cancel()`, but `diag` - // is borrowed, so we can't fully defuse it. - diag.downgrade_to_delayed_bug(); - return; - }; - (Some(vals), exp_found, is_simple_error, Some(values)) - } - }; - - let mut label_or_note = |span: Span, msg: Cow<'static, str>| { - if (prefer_label && is_simple_error) || &[span] == diag.span.primary_spans() { - diag.span_label(span, msg); - } else { - diag.span_note(span, msg); - } - }; - if let Some((sp, msg)) = secondary_span { - if swap_secondary_and_primary { - let terr = if let Some(infer::ValuePairs::Terms(ExpectedFound { - expected, .. - })) = values - { - Cow::from(format!("expected this to be `{expected}`")) - } else { - terr.to_string(self.tcx) - }; - label_or_note(sp, terr); - label_or_note(span, msg); - } else { - label_or_note(span, terr.to_string(self.tcx)); - label_or_note(sp, msg); - } - } else { - if let Some(values) = values - && let Some((e, f)) = values.ty() - && let TypeError::ArgumentSorts(..) | TypeError::Sorts(_) = terr - { - let e = self.tcx.erase_regions(e); - let f = self.tcx.erase_regions(f); - let expected = with_forced_trimmed_paths!(e.sort_string(self.tcx)); - let found = with_forced_trimmed_paths!(f.sort_string(self.tcx)); - if expected == found { - label_or_note(span, terr.to_string(self.tcx)); - } else { - label_or_note(span, Cow::from(format!("expected {expected}, found {found}"))); - } - } else { - label_or_note(span, terr.to_string(self.tcx)); - } - } - - if let Some((expected, found, path)) = expected_found { - let (expected_label, found_label, exp_found) = match exp_found { - Mismatch::Variable(ef) => ( - ef.expected.prefix_string(self.tcx), - ef.found.prefix_string(self.tcx), - Some(ef), - ), - Mismatch::Fixed(s) => (s.into(), s.into(), None), - }; - - enum Similar<'tcx> { - Adts { expected: ty::AdtDef<'tcx>, found: ty::AdtDef<'tcx> }, - PrimitiveFound { expected: ty::AdtDef<'tcx>, found: Ty<'tcx> }, - PrimitiveExpected { expected: Ty<'tcx>, found: ty::AdtDef<'tcx> }, - } - - let similarity = |ExpectedFound { expected, found }: ExpectedFound<Ty<'tcx>>| { - if let ty::Adt(expected, _) = expected.kind() - && let Some(primitive) = found.primitive_symbol() - { - let path = self.tcx.def_path(expected.did()).data; - let name = path.last().unwrap().data.get_opt_name(); - if name == Some(primitive) { - return Some(Similar::PrimitiveFound { expected: *expected, found }); - } - } else if let Some(primitive) = expected.primitive_symbol() - && let ty::Adt(found, _) = found.kind() - { - let path = self.tcx.def_path(found.did()).data; - let name = path.last().unwrap().data.get_opt_name(); - if name == Some(primitive) { - return Some(Similar::PrimitiveExpected { expected, found: *found }); - } - } else if let ty::Adt(expected, _) = expected.kind() - && let ty::Adt(found, _) = found.kind() - { - if !expected.did().is_local() && expected.did().krate == found.did().krate { - // Most likely types from different versions of the same crate - // are in play, in which case this message isn't so helpful. - // A "perhaps two different versions..." error is already emitted for that. - return None; - } - let f_path = self.tcx.def_path(found.did()).data; - let e_path = self.tcx.def_path(expected.did()).data; - - if let (Some(e_last), Some(f_last)) = (e_path.last(), f_path.last()) - && e_last == f_last - { - return Some(Similar::Adts { expected: *expected, found: *found }); - } - } - None - }; - - match terr { - // If two types mismatch but have similar names, mention that specifically. - TypeError::Sorts(values) if let Some(s) = similarity(values) => { - let diagnose_primitive = - |prim: Ty<'tcx>, shadow: Ty<'tcx>, defid: DefId, diag: &mut Diag<'_>| { - let name = shadow.sort_string(self.tcx); - diag.note(format!( - "{prim} and {name} have similar names, but are actually distinct types" - )); - diag.note(format!("{prim} is a primitive defined by the language")); - let def_span = self.tcx.def_span(defid); - let msg = if defid.is_local() { - format!("{name} is defined in the current crate") - } else { - let crate_name = self.tcx.crate_name(defid.krate); - format!("{name} is defined in crate `{crate_name}`") - }; - diag.span_note(def_span, msg); - }; - - let diagnose_adts = - |expected_adt: ty::AdtDef<'tcx>, - found_adt: ty::AdtDef<'tcx>, - diag: &mut Diag<'_>| { - let found_name = values.found.sort_string(self.tcx); - let expected_name = values.expected.sort_string(self.tcx); - - let found_defid = found_adt.did(); - let expected_defid = expected_adt.did(); - - diag.note(format!("{found_name} and {expected_name} have similar names, but are actually distinct types")); - for (defid, name) in - [(found_defid, found_name), (expected_defid, expected_name)] - { - let def_span = self.tcx.def_span(defid); - - let msg = if found_defid.is_local() && expected_defid.is_local() { - let module = self - .tcx - .parent_module_from_def_id(defid.expect_local()) - .to_def_id(); - let module_name = - self.tcx.def_path(module).to_string_no_crate_verbose(); - format!( - "{name} is defined in module `crate{module_name}` of the current crate" - ) - } else if defid.is_local() { - format!("{name} is defined in the current crate") - } else { - let crate_name = self.tcx.crate_name(defid.krate); - format!("{name} is defined in crate `{crate_name}`") - }; - diag.span_note(def_span, msg); - } - }; - - match s { - Similar::Adts { expected, found } => diagnose_adts(expected, found, diag), - Similar::PrimitiveFound { expected, found: prim } => { - diagnose_primitive(prim, values.expected, expected.did(), diag) - } - Similar::PrimitiveExpected { expected: prim, found } => { - diagnose_primitive(prim, values.found, found.did(), diag) - } - } - } - TypeError::Sorts(values) => { - let extra = expected == found; - let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) { - (true, ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) => { - let sm = self.tcx.sess.source_map(); - let pos = sm.lookup_char_pos(self.tcx.def_span(*def_id).lo()); - format!( - " (opaque type at <{}:{}:{}>)", - sm.filename_for_diagnostics(&pos.file.name), - pos.line, - pos.col.to_usize() + 1, - ) - } - (true, ty::Alias(ty::Projection, proj)) - if self.tcx.is_impl_trait_in_trait(proj.def_id) => - { - let sm = self.tcx.sess.source_map(); - let pos = sm.lookup_char_pos(self.tcx.def_span(proj.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(), - }; - if !(values.expected.is_simple_text(self.tcx) - && values.found.is_simple_text(self.tcx)) - || (exp_found.is_some_and(|ef| { - // This happens when the type error is a subset of the expectation, - // like when you have two references but one is `usize` and the other - // is `f32`. In those cases we still want to show the `note`. If the - // value from `ef` is `Infer(_)`, then we ignore it. - if !ef.expected.is_ty_or_numeric_infer() { - ef.expected != values.expected - } else if !ef.found.is_ty_or_numeric_infer() { - ef.found != values.found - } else { - false - } - })) - { - if let Some(ExpectedFound { found: found_ty, .. }) = exp_found { - // `Future` is a special opaque type that the compiler - // will try to hide in some case such as `async fn`, so - // to make an error more use friendly we will - // avoid to suggest a mismatch type with a - // type that the user usually are not using - // directly such as `impl Future<Output = u8>`. - if !self.tcx.ty_is_opaque_future(found_ty) { - diag.note_expected_found_extra( - &expected_label, - expected, - &found_label, - found, - &sort_string(values.expected), - &sort_string(values.found), - ); - if let Some(path) = path { - diag.note(format!( - "the full type name has been written to '{}'", - path.display(), - )); - diag.note("consider using `--verbose` to print the full type name to the console"); - } - } - } - } - } - _ => { - debug!( - "note_type_err: exp_found={:?}, expected={:?} found={:?}", - exp_found, expected, found - ); - if !is_simple_error || terr.must_include_note() { - diag.note_expected_found(&expected_label, expected, &found_label, found); - - if let Some(ty::Closure(_, args)) = - exp_found.map(|expected_type_found| expected_type_found.found.kind()) - { - diag.highlighted_note(vec![ - StringPart::normal("closure has signature: `"), - StringPart::highlighted( - self.tcx - .signature_unclosure( - args.as_closure().sig(), - rustc_hir::Safety::Safe, - ) - .to_string(), - ), - StringPart::normal("`"), - ]); - } - } - } - } - } - let exp_found = match exp_found { - Mismatch::Variable(exp_found) => Some(exp_found), - Mismatch::Fixed(_) => None, - }; - let exp_found = match terr { - // `terr` has more accurate type information than `exp_found` in match expressions. - ty::error::TypeError::Sorts(terr) - if exp_found.is_some_and(|ef| terr.found == ef.found) => - { - Some(terr) - } - _ => exp_found, - }; - debug!("exp_found {:?} terr {:?} cause.code {:?}", exp_found, terr, cause.code()); - if let Some(exp_found) = exp_found { - let should_suggest_fixes = - if let ObligationCauseCode::Pattern { root_ty, .. } = cause.code() { - // Skip if the root_ty of the pattern is not the same as the expected_ty. - // If these types aren't equal then we've probably peeled off a layer of arrays. - self.same_type_modulo_infer(*root_ty, exp_found.expected) - } else { - true - }; - - // FIXME(#73154): For now, we do leak check when coercing function - // pointers in typeck, instead of only during borrowck. This can lead - // to these `RegionsInsufficientlyPolymorphic` errors that aren't helpful. - if should_suggest_fixes - && !matches!(terr, TypeError::RegionsInsufficientlyPolymorphic(..)) - { - self.suggest_tuple_pattern(cause, &exp_found, diag); - self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); - self.suggest_await_on_expect_found(cause, span, &exp_found, diag); - self.suggest_function_pointers(cause, span, &exp_found, diag); - self.suggest_turning_stmt_into_expr(cause, &exp_found, diag); - } - } - - self.check_and_note_conflicting_crates(diag, terr); - - self.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id()); - if let Some(exp_found) = exp_found - && let exp_found = TypeError::Sorts(exp_found) - && exp_found != terr - { - self.note_and_explain_type_err(diag, exp_found, cause, span, cause.body_id.to_def_id()); - } - - if let Some(ValuePairs::TraitRefs(exp_found)) = values - && let ty::Closure(def_id, _) = exp_found.expected.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"); - self.suggest_for_all_lifetime_closure( - span, - self.tcx.hir_node_by_def_id(def_id), - &exp_found, - diag, - ); - } - - // It reads better to have the error origin as the final - // thing. - self.note_error_origin(diag, cause, exp_found, terr); - - debug!(?diag); - } - - pub fn type_error_additional_suggestions( - &self, - trace: &TypeTrace<'tcx>, - terr: TypeError<'tcx>, - ) -> Vec<TypeErrorAdditionalDiags> { - let mut suggestions = Vec::new(); - let span = trace.cause.span(); - let values = self.resolve_vars_if_possible(trace.values); - if let Some((expected, found)) = values.ty() { - match (expected.kind(), found.kind()) { - (ty::Tuple(_), ty::Tuple(_)) => {} - // If a tuple of length one was expected and the found expression has - // parentheses around it, perhaps the user meant to write `(expr,)` to - // build a tuple (issue #86100) - (ty::Tuple(fields), _) => { - suggestions.extend(self.suggest_wrap_to_build_a_tuple(span, found, fields)) - } - // If a byte was expected and the found expression is a char literal - // containing a single ASCII character, perhaps the user meant to write `b'c'` to - // specify a byte literal - (ty::Uint(ty::UintTy::U8), ty::Char) => { - if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) - && let Some(code) = - code.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) - // forbid all Unicode escapes - && !code.starts_with("\\u") - // forbids literal Unicode characters beyond ASCII - && code.chars().next().is_some_and(|c| c.is_ascii()) - { - suggestions.push(TypeErrorAdditionalDiags::MeantByteLiteral { - span, - code: escape_literal(code), - }) - } - } - // If a character was expected and the found expression is a string literal - // containing a single character, perhaps the user meant to write `'c'` to - // specify a character literal (issue #92479) - (ty::Char, ty::Ref(_, r, _)) if r.is_str() => { - if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) - && let Some(code) = code.strip_prefix('"').and_then(|s| s.strip_suffix('"')) - && code.chars().count() == 1 - { - suggestions.push(TypeErrorAdditionalDiags::MeantCharLiteral { - span, - code: escape_literal(code), - }) - } - } - // If a string was expected and the found expression is a character literal, - // perhaps the user meant to write `"s"` to specify a string literal. - (ty::Ref(_, r, _), ty::Char) if r.is_str() => { - if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) - && code.starts_with("'") - && code.ends_with("'") - { - suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { - start: span.with_hi(span.lo() + BytePos(1)), - end: span.with_lo(span.hi() - BytePos(1)), - }); - } - } - // For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`, - // we try to suggest to add the missing `let` for `if let Some(..) = expr` - (ty::Bool, ty::Tuple(list)) => { - if list.len() == 0 { - suggestions.extend(self.suggest_let_for_letchains(&trace.cause, span)); - } - } - (ty::Array(_, _), ty::Array(_, _)) => { - suggestions.extend(self.suggest_specify_actual_length(terr, trace, span)) - } - _ => {} - } - } - let code = trace.cause.code(); - if let &(ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { - source, - .. - }) - | ObligationCauseCode::BlockTailExpression(.., source)) = code - && let hir::MatchSource::TryDesugar(_) = source - && let Some((expected_ty, found_ty, _)) = self.values_str(trace.values) - { - suggestions.push(TypeErrorAdditionalDiags::TryCannotConvert { - found: found_ty.content(), - expected: expected_ty.content(), - }); - } - suggestions - } - - fn suggest_specify_actual_length( - &self, - terr: TypeError<'_>, - trace: &TypeTrace<'_>, - span: Span, - ) -> Option<TypeErrorAdditionalDiags> { - let hir = self.tcx.hir(); - let TypeError::FixedArraySize(sz) = terr else { - return None; - }; - let tykind = match self.tcx.hir_node_by_def_id(trace.cause.body_id) { - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) => { - let body = hir.body(*body_id); - struct LetVisitor { - span: Span, - } - impl<'v> Visitor<'v> for LetVisitor { - type Result = ControlFlow<&'v hir::TyKind<'v>>; - fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) -> Self::Result { - // Find a local statement where the initializer has - // the same span as the error and the type is specified. - if let hir::Stmt { - kind: - hir::StmtKind::Let(hir::LetStmt { - init: Some(hir::Expr { span: init_span, .. }), - ty: Some(array_ty), - .. - }), - .. - } = s - && init_span == &self.span - { - ControlFlow::Break(&array_ty.peel_refs().kind) - } else { - ControlFlow::Continue(()) - } - } - } - LetVisitor { span }.visit_body(body).break_value() - } - hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. }) => { - Some(&ty.peel_refs().kind) - } - _ => None, - }; - if let Some(tykind) = tykind - && let hir::TyKind::Array(_, length) = tykind - && let hir::ArrayLen::Body(ct) = length - { - let span = ct.span(); - Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { span, length: sz.found }) - } else { - None - } - } - - pub fn report_and_explain_type_error( - &self, - trace: TypeTrace<'tcx>, - terr: TypeError<'tcx>, - ) -> Diag<'a> { - debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr); - - let span = trace.cause.span(); - let failure_code = trace.cause.as_failure_code_diag( - terr, - span, - self.type_error_additional_suggestions(&trace, terr), - ); - let mut diag = self.dcx().create_err(failure_code); - self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false, false); - diag - } - - fn suggest_wrap_to_build_a_tuple( - &self, - span: Span, - found: Ty<'tcx>, - expected_fields: &List<Ty<'tcx>>, - ) -> Option<TypeErrorAdditionalDiags> { - let [expected_tup_elem] = expected_fields[..] else { return None }; - - if !self.same_type_modulo_infer(expected_tup_elem, found) { - return None; - } - - let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) else { return None }; - - let sugg = if code.starts_with('(') && code.ends_with(')') { - let before_close = span.hi() - BytePos::from_u32(1); - TypeErrorAdditionalDiags::TupleOnlyComma { - span: span.with_hi(before_close).shrink_to_hi(), - } - } else { - TypeErrorAdditionalDiags::TupleAlsoParentheses { - span_low: span.shrink_to_lo(), - span_high: span.shrink_to_hi(), - } - }; - Some(sugg) - } - - fn values_str( - &self, - values: ValuePairs<'tcx>, - ) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> { - match values { - ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found), - ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found), - ValuePairs::Aliases(exp_found) => self.expected_found_str(exp_found), - ValuePairs::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found), - ValuePairs::ExistentialProjection(exp_found) => self.expected_found_str(exp_found), - ValuePairs::TraitRefs(exp_found) => { - let pretty_exp_found = ty::error::ExpectedFound { - expected: exp_found.expected.print_trait_sugared(), - found: exp_found.found.print_trait_sugared(), - }; - match self.expected_found_str(pretty_exp_found) { - Some((expected, found, _)) if expected == found => { - self.expected_found_str(exp_found) - } - ret => ret, - } - } - ValuePairs::PolySigs(exp_found) => { - let exp_found = self.resolve_vars_if_possible(exp_found); - if exp_found.references_error() { - return None; - } - let (exp, fnd) = self.cmp_fn_sig(&exp_found.expected, &exp_found.found); - Some((exp, fnd, None)) - } - ValuePairs::Dummy => { - bug!("do not expect to report a type error from a ValuePairs::Dummy") - } - } - } - - fn expected_found_str_term( - &self, - exp_found: ty::error::ExpectedFound<ty::Term<'tcx>>, - ) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> { - let exp_found = self.resolve_vars_if_possible(exp_found); - if exp_found.references_error() { - return None; - } - - Some(match (exp_found.expected.unpack(), exp_found.found.unpack()) { - (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => { - let (mut exp, mut fnd) = self.cmp(expected, found); - // Use the terminal width as the basis to determine when to compress the printed - // out type, but give ourselves some leeway to avoid ending up creating a file for - // a type that is somewhat shorter than the path we'd write to. - let len = self.tcx.sess().diagnostic_width() + 40; - let exp_s = exp.content(); - let fnd_s = fnd.content(); - let mut path = None; - if exp_s.len() > len { - let exp_s = self.tcx.short_ty_string(expected, &mut path); - exp = DiagStyledString::highlighted(exp_s); - } - if fnd_s.len() > len { - let fnd_s = self.tcx.short_ty_string(found, &mut path); - fnd = DiagStyledString::highlighted(fnd_s); - } - (exp, fnd, path) - } - _ => ( - DiagStyledString::highlighted(exp_found.expected.to_string()), - DiagStyledString::highlighted(exp_found.found.to_string()), - None, - ), - }) - } - - /// Returns a string of the form "expected `{}`, found `{}`". - fn expected_found_str<T: fmt::Display + TypeFoldable<TyCtxt<'tcx>>>( - &self, - exp_found: ty::error::ExpectedFound<T>, - ) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> { - let exp_found = self.resolve_vars_if_possible(exp_found); - if exp_found.references_error() { - return None; - } - - Some(( - DiagStyledString::highlighted(exp_found.expected.to_string()), - DiagStyledString::highlighted(exp_found.found.to_string()), - None, - )) - } - - /// Determine whether an error associated with the given span and definition - /// should be treated as being caused by the implicit `From` conversion - /// within `?` desugaring. - pub fn is_try_conversion(&self, span: Span, trait_def_id: DefId) -> bool { - span.is_desugaring(DesugaringKind::QuestionMark) - && self.tcx.is_diagnostic_item(sym::From, trait_def_id) - } - - /// Structurally compares two types, modulo any inference variables. - /// - /// Returns `true` if two types are equal, or if one type is an inference variable compatible - /// with the other type. A TyVar inference type is compatible with any type, and an IntVar or - /// FloatVar inference type are compatible with themselves or their concrete types (Int and - /// Float types, respectively). When comparing two ADTs, these rules apply recursively. - pub fn same_type_modulo_infer<T: relate::Relate<TyCtxt<'tcx>>>(&self, a: T, b: T) -> bool { - let (a, b) = self.resolve_vars_if_possible((a, b)); - SameTypeModuloInfer(self).relate(a, b).is_ok() - } -} - -struct SameTypeModuloInfer<'a, 'tcx>(&'a InferCtxt<'tcx>); - -impl<'tcx> TypeRelation<TyCtxt<'tcx>> for SameTypeModuloInfer<'_, 'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.0.tcx - } - - fn relate_with_variance<T: relate::Relate<TyCtxt<'tcx>>>( - &mut self, - _variance: ty::Variance, - _info: ty::VarianceDiagInfo<TyCtxt<'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::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(_))) => Ok(a), - (ty::Infer(_), _) | (_, ty::Infer(_)) => Err(TypeError::Mismatch), - _ => relate::structurally_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()) - || (b.is_var() && a.is_free()) - || (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<TyCtxt<'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) - } -} - -pub enum FailureCode { - Error0317, - Error0580, - Error0308, - Error0644, -} - -#[extension(pub trait ObligationCauseExt<'tcx>)] -impl<'tcx> ObligationCause<'tcx> { - fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode { - match self.code() { - ObligationCauseCode::IfExpressionWithNoElse => FailureCode::Error0317, - ObligationCauseCode::MainFunctionType => FailureCode::Error0580, - ObligationCauseCode::CompareImplItem { .. } - | ObligationCauseCode::MatchExpressionArm(_) - | ObligationCauseCode::IfExpression { .. } - | ObligationCauseCode::LetElse - | ObligationCauseCode::StartFunctionType - | ObligationCauseCode::LangFunctionType(_) - | ObligationCauseCode::IntrinsicType - | ObligationCauseCode::MethodReceiver => FailureCode::Error0308, - - // In the case where we have no more specific thing to - // say, also take a look at the error code, maybe we can - // tailor to that. - _ => match terr { - TypeError::CyclicTy(ty) - if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() => - { - FailureCode::Error0644 - } - TypeError::IntrinsicCast => FailureCode::Error0308, - _ => FailureCode::Error0308, - }, - } - } - fn as_failure_code_diag( - &self, - terr: TypeError<'tcx>, - span: Span, - subdiags: Vec<TypeErrorAdditionalDiags>, - ) -> ObligationCauseFailureCode { - match self.code() { - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Fn, .. } => { - ObligationCauseFailureCode::MethodCompat { span, subdiags } - } - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Type, .. } => { - ObligationCauseFailureCode::TypeCompat { span, subdiags } - } - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Const, .. } => { - ObligationCauseFailureCode::ConstCompat { span, subdiags } - } - ObligationCauseCode::BlockTailExpression(.., hir::MatchSource::TryDesugar(_)) => { - ObligationCauseFailureCode::TryCompat { span, subdiags } - } - ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { - source, .. - }) => match source { - hir::MatchSource::TryDesugar(_) => { - ObligationCauseFailureCode::TryCompat { span, subdiags } - } - _ => ObligationCauseFailureCode::MatchCompat { span, subdiags }, - }, - ObligationCauseCode::IfExpression { .. } => { - ObligationCauseFailureCode::IfElseDifferent { span, subdiags } - } - ObligationCauseCode::IfExpressionWithNoElse => { - ObligationCauseFailureCode::NoElse { span } - } - ObligationCauseCode::LetElse => { - ObligationCauseFailureCode::NoDiverge { span, subdiags } - } - ObligationCauseCode::MainFunctionType => { - ObligationCauseFailureCode::FnMainCorrectType { span } - } - ObligationCauseCode::StartFunctionType => { - ObligationCauseFailureCode::FnStartCorrectType { span, subdiags } - } - &ObligationCauseCode::LangFunctionType(lang_item_name) => { - ObligationCauseFailureCode::FnLangCorrectType { span, subdiags, lang_item_name } - } - ObligationCauseCode::IntrinsicType => { - ObligationCauseFailureCode::IntrinsicCorrectType { span, subdiags } - } - ObligationCauseCode::MethodReceiver => { - ObligationCauseFailureCode::MethodCorrectType { span, subdiags } - } - - // In the case where we have no more specific thing to - // say, also take a look at the error code, maybe we can - // tailor to that. - _ => match terr { - TypeError::CyclicTy(ty) - if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() => - { - ObligationCauseFailureCode::ClosureSelfref { span } - } - TypeError::IntrinsicCast => { - ObligationCauseFailureCode::CantCoerce { span, subdiags } - } - _ => ObligationCauseFailureCode::Generic { span, subdiags }, - }, - } - } - - fn as_requirement_str(&self) -> &'static str { - match self.code() { - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Fn, .. } => { - "method type is compatible with trait" - } - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Type, .. } => { - "associated type is compatible with trait" - } - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Const, .. } => { - "const is compatible with trait" - } - ObligationCauseCode::MainFunctionType => "`main` function has the correct type", - ObligationCauseCode::StartFunctionType => "`#[start]` function has the correct type", - ObligationCauseCode::LangFunctionType(_) => "lang item function has the correct type", - ObligationCauseCode::IntrinsicType => "intrinsic has the correct type", - ObligationCauseCode::MethodReceiver => "method receiver has the correct type", - _ => "types are compatible", - } - } -} - -/// Newtype to allow implementing IntoDiagArg -pub struct ObligationCauseAsDiagArg<'tcx>(pub ObligationCause<'tcx>); - -impl IntoDiagArg for ObligationCauseAsDiagArg<'_> { - fn into_diag_arg(self) -> rustc_errors::DiagArgValue { - let kind = match self.0.code() { - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Fn, .. } => "method_compat", - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Type, .. } => "type_compat", - ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Const, .. } => { - "const_compat" - } - ObligationCauseCode::MainFunctionType => "fn_main_correct_type", - ObligationCauseCode::StartFunctionType => "fn_start_correct_type", - ObligationCauseCode::LangFunctionType(_) => "fn_lang_correct_type", - ObligationCauseCode::IntrinsicType => "intrinsic_correct_type", - ObligationCauseCode::MethodReceiver => "method_correct_type", - _ => "other", - } - .into(); - rustc_errors::DiagArgValue::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)] -pub enum TyCategory { - Closure, - Opaque, - OpaqueFuture, - Coroutine(hir::CoroutineKind), - Foreign, -} - -impl fmt::Display for TyCategory { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Closure => "closure".fmt(f), - Self::Opaque => "opaque type".fmt(f), - Self::OpaqueFuture => "future".fmt(f), - Self::Coroutine(gk) => gk.fmt(f), - Self::Foreign => "foreign type".fmt(f), - } - } -} - -impl TyCategory { - pub fn from_ty(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<(Self, DefId)> { - match *ty.kind() { - ty::Closure(def_id, _) => Some((Self::Closure, def_id)), - ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { - let kind = - if tcx.ty_is_opaque_future(ty) { Self::OpaqueFuture } else { Self::Opaque }; - Some((kind, def_id)) - } - ty::Coroutine(def_id, ..) => { - Some((Self::Coroutine(tcx.coroutine_kind(def_id).unwrap()), def_id)) - } - ty::Foreign(def_id) => Some((Self::Foreign, def_id)), - _ => None, - } - } -} - -impl<'tcx> InferCtxt<'tcx> { - /// Given a [`hir::Block`], get the span of its last expression or - /// statement, peeling off any inner blocks. - pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span { - let block = block.innermost_block(); - if let Some(expr) = &block.expr { - expr.span - } else if let Some(stmt) = block.stmts.last() { - // possibly incorrect trailing `;` in the else arm - stmt.span - } else { - // empty block; point at its entirety - block.span - } - } - - /// Given a [`hir::HirId`] for a block, get the span of its last expression - /// or statement, peeling off any inner blocks. - pub fn find_block_span_from_hir_id(&self, hir_id: hir::HirId) -> Span { - match self.tcx.hir_node(hir_id) { - hir::Node::Block(blk) => self.find_block_span(blk), - // The parser was in a weird state if either of these happen, but - // it's better not to panic. - hir::Node::Expr(e) => e.span, - _ => rustc_span::DUMMY_SP, - } - } -} diff --git a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/different_lifetimes.rs deleted file mode 100644 index 74dcde03639..00000000000 --- a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/different_lifetimes.rs +++ /dev/null @@ -1,163 +0,0 @@ -//! Error Reporting for Anonymous Region Lifetime Errors -//! where both the regions are anonymous. - -use crate::error_reporting::infer::nice_region_error::find_anon_type::find_anon_type; -use crate::error_reporting::infer::nice_region_error::util::AnonymousParamInfo; -use crate::error_reporting::infer::nice_region_error::NiceRegionError; -use crate::errors::AddLifetimeParamsSuggestion; -use crate::errors::LifetimeMismatch; -use crate::errors::LifetimeMismatchLabels; -use crate::infer::RegionResolutionError; -use crate::infer::SubregionOrigin; - -use rustc_errors::Subdiagnostic; -use rustc_errors::{Diag, ErrorGuaranteed}; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::Ty; -use rustc_middle::ty::{Region, TyCtxt}; - -impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { - /// Print the error message for lifetime errors when both the concerned regions are anonymous. - /// - /// Consider a case where we have - /// - /// ```compile_fail - /// fn foo(x: &mut Vec<&u8>, y: &u8) { - /// x.push(y); - /// } - /// ``` - /// - /// The example gives - /// - /// ```text - /// fn foo(x: &mut Vec<&u8>, y: &u8) { - /// --- --- these references are declared with different lifetimes... - /// x.push(y); - /// ^ ...but data from `y` flows into `x` here - /// ``` - /// - /// It has been extended for the case of structs too. - /// - /// Consider the example - /// - /// ```no_run - /// struct Ref<'a> { x: &'a u32 } - /// ``` - /// - /// ```text - /// fn foo(mut x: Vec<Ref>, y: Ref) { - /// --- --- these structs are declared with different lifetimes... - /// x.push(y); - /// ^ ...but data from `y` flows into `x` here - /// } - /// ``` - /// - /// It will later be extended to trait objects. - pub(super) fn try_report_anon_anon_conflict(&self) -> Option<ErrorGuaranteed> { - let (span, sub, sup) = self.regions()?; - - if let Some(RegionResolutionError::ConcreteFailure( - SubregionOrigin::ReferenceOutlivesReferent(..), - .., - )) = self.error - { - // This error doesn't make much sense in this case. - return None; - } - - // Determine whether the sub and sup consist of both anonymous (elided) regions. - let anon_reg_sup = self.tcx().is_suitable_region(self.generic_param_scope, sup)?; - - let anon_reg_sub = self.tcx().is_suitable_region(self.generic_param_scope, sub)?; - let scope_def_id_sup = anon_reg_sup.def_id; - let bregion_sup = anon_reg_sup.bound_region; - let scope_def_id_sub = anon_reg_sub.def_id; - let bregion_sub = anon_reg_sub.bound_region; - - let ty_sup = find_anon_type(self.tcx(), self.generic_param_scope, sup, &bregion_sup)?; - - let ty_sub = find_anon_type(self.tcx(), self.generic_param_scope, sub, &bregion_sub)?; - - debug!( - "try_report_anon_anon_conflict: found_param1={:?} sup={:?} br1={:?}", - ty_sub, sup, bregion_sup - ); - debug!( - "try_report_anon_anon_conflict: found_param2={:?} sub={:?} br2={:?}", - ty_sup, sub, bregion_sub - ); - - let (ty_sup, ty_fndecl_sup) = ty_sup; - let (ty_sub, ty_fndecl_sub) = ty_sub; - - let AnonymousParamInfo { param: anon_param_sup, .. } = - self.find_param_with_region(sup, sup)?; - let AnonymousParamInfo { param: anon_param_sub, .. } = - self.find_param_with_region(sub, sub)?; - - let sup_is_ret_type = - self.is_return_type_anon(scope_def_id_sup, bregion_sup, ty_fndecl_sup); - let sub_is_ret_type = - self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub); - - debug!( - "try_report_anon_anon_conflict: sub_is_ret_type={:?} sup_is_ret_type={:?}", - sub_is_ret_type, sup_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 }; - LifetimeMismatchLabels::InRet { - param_span, - ret_span, - span, - label_var1: anon_param_sup.pat.simple_ident(), - } - } - - (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 suggestion = AddLifetimeParamsSuggestion { - tcx: self.tcx(), - sub, - ty_sup, - ty_sub, - add_note: true, - generic_param_scope: self.generic_param_scope, - }; - let err = LifetimeMismatch { span, labels, suggestion }; - let reported = self.tcx().dcx().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>, - err: &mut Diag<'_>, - generic_param_scope: LocalDefId, - sub: Region<'tcx>, - ty_sup: &'tcx Ty<'_>, - ty_sub: &'tcx Ty<'_>, -) { - let suggestion = AddLifetimeParamsSuggestion { - tcx, - sub, - ty_sup, - ty_sub, - add_note: false, - generic_param_scope, - }; - suggestion.add_to_diag(err); -} diff --git a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/find_anon_type.rs deleted file mode 100644 index b91b755d683..00000000000 --- a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/find_anon_type.rs +++ /dev/null @@ -1,236 +0,0 @@ -use core::ops::ControlFlow; -use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_middle::hir::map::Map; -use rustc_middle::hir::nested_filter; -use rustc_middle::middle::resolve_bound_vars as rbv; -use rustc_middle::ty::{self, Region, TyCtxt}; - -/// This function calls the `visit_ty` method for the parameters -/// corresponding to the anonymous regions. The `nested_visitor.found_type` -/// contains the anonymous type. -/// -/// # Arguments -/// region - the anonymous region corresponding to the anon_anon conflict -/// br - the bound region corresponding to the above region which is of type `BrAnon(_)` -/// -/// # Example -/// ```compile_fail -/// fn foo(x: &mut Vec<&u8>, y: &u8) -/// { x.push(y); } -/// ``` -/// The function returns the nested type corresponding to the anonymous region -/// for e.g., `&u8` and `Vec<&u8>`. -pub fn find_anon_type<'tcx>( - tcx: TyCtxt<'tcx>, - generic_param_scope: LocalDefId, - region: Region<'tcx>, - br: &ty::BoundRegionKind, -) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnSig<'tcx>)> { - let anon_reg = tcx.is_suitable_region(generic_param_scope, region)?; - let fn_sig = tcx.hir_node_by_def_id(anon_reg.def_id).fn_sig()?; - - fn_sig - .decl - .inputs - .iter() - .find_map(|arg| find_component_for_bound_region(tcx, arg, br)) - .map(|ty| (ty, fn_sig)) -} - -// This method creates a FindNestedTypeVisitor which returns the type corresponding -// to the anonymous region. -fn find_component_for_bound_region<'tcx>( - tcx: TyCtxt<'tcx>, - arg: &'tcx hir::Ty<'tcx>, - br: &ty::BoundRegionKind, -) -> Option<&'tcx hir::Ty<'tcx>> { - FindNestedTypeVisitor { tcx, bound_region: *br, current_index: ty::INNERMOST } - .visit_ty(arg) - .break_value() -} - -// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the -// anonymous region. The example above would lead to a conflict between -// the two anonymous lifetimes for &u8 in x and y respectively. This visitor -// would be invoked twice, once for each lifetime, and would -// walk the types like &mut Vec<&u8> and &u8 looking for the HIR -// where that lifetime appears. This allows us to highlight the -// specific part of the type in the error message. -struct FindNestedTypeVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - // The bound_region corresponding to the Refree(freeregion) - // associated with the anonymous region we are looking for. - bound_region: ty::BoundRegionKind, - current_index: ty::DebruijnIndex, -} - -impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { - type Result = ControlFlow<&'tcx hir::Ty<'tcx>>; - type NestedFilter = nested_filter::OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - - fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result { - match arg.kind { - hir::TyKind::BareFn(_) => { - self.current_index.shift_in(1); - intravisit::walk_ty(self, arg); - self.current_index.shift_out(1); - return ControlFlow::Continue(()); - } - - hir::TyKind::TraitObject(bounds, ..) => { - for bound in bounds { - self.current_index.shift_in(1); - self.visit_poly_trait_ref(bound); - self.current_index.shift_out(1); - } - } - - hir::TyKind::Ref(lifetime, _) => { - // the lifetime of the Ref - let hir_id = lifetime.hir_id; - match (self.tcx.named_bound_var(hir_id), self.bound_region) { - // 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(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => { - debug!("EarlyBound id={:?} def_id={:?}", id, def_id); - if id == def_id { - return ControlFlow::Break(arg); - } - } - - // 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(rbv::ResolvedArg::LateBound(debruijn_index, _, id)), - ty::BrNamed(def_id, _), - ) => { - debug!( - "FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", - debruijn_index - ); - debug!("LateBound id={:?} def_id={:?}", id, def_id); - if debruijn_index == self.current_index && id == def_id { - return ControlFlow::Break(arg); - } - } - - ( - Some( - rbv::ResolvedArg::StaticLifetime - | rbv::ResolvedArg::Free(_, _) - | rbv::ResolvedArg::EarlyBound(_) - | rbv::ResolvedArg::LateBound(_, _, _) - | rbv::ResolvedArg::Error(_), - ) - | None, - _, - ) => { - debug!("no arg found"); - } - } - } - // Checks if it is of type `hir::TyKind::Path` which corresponds to a struct. - hir::TyKind::Path(_) => { - // Prefer using the lifetime in type arguments rather than lifetime arguments. - intravisit::walk_ty(self, arg)?; - - // Call `walk_ty` as `visit_ty` is empty. - return if intravisit::walk_ty( - &mut TyPathVisitor { - tcx: self.tcx, - bound_region: self.bound_region, - current_index: self.current_index, - }, - arg, - ) - .is_break() - { - ControlFlow::Break(arg) - } else { - ControlFlow::Continue(()) - }; - } - _ => {} - } - // walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`, - // go on to visit `&Foo` - intravisit::walk_ty(self, arg) - } -} - -// The visitor captures the corresponding `hir::Ty` of the anonymous region -// in the case of structs ie. `hir::TyKind::Path`. -// This visitor would be invoked for each lifetime corresponding to a struct, -// and would walk the types like Vec<Ref> in the above example and Ref looking for the HIR -// where that lifetime appears. This allows us to highlight the -// specific part of the type in the error message. -struct TyPathVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - bound_region: ty::BoundRegionKind, - current_index: ty::DebruijnIndex, -} - -impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { - type Result = ControlFlow<()>; - type NestedFilter = nested_filter::OnlyBodies; - - fn nested_visit_map(&mut self) -> Map<'tcx> { - self.tcx.hir() - } - - fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) -> Self::Result { - match (self.tcx.named_bound_var(lifetime.hir_id), self.bound_region) { - // the lifetime of the TyPath! - (Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => { - debug!("EarlyBound id={:?} def_id={:?}", id, def_id); - if id == def_id { - return ControlFlow::Break(()); - } - } - - (Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)), ty::BrNamed(def_id, _)) => { - debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index,); - debug!("id={:?}", id); - debug!("def_id={:?}", def_id); - if debruijn_index == self.current_index && id == def_id { - return ControlFlow::Break(()); - } - } - - ( - Some( - rbv::ResolvedArg::StaticLifetime - | rbv::ResolvedArg::EarlyBound(_) - | rbv::ResolvedArg::LateBound(_, _, _) - | rbv::ResolvedArg::Free(_, _) - | rbv::ResolvedArg::Error(_), - ) - | None, - _, - ) => { - debug!("no arg found"); - } - } - ControlFlow::Continue(()) - } - - fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result { - // ignore nested types - // - // If you have a type like `Foo<'a, &Ty>` we - // are only interested in the immediate lifetimes ('a). - // - // Making `visit_ty` empty will ignore the `&Ty` embedded - // inside, it will get reached by the outer visitor. - debug!("`Ty` corresponding to a struct is {:?}", arg); - ControlFlow::Continue(()) - } -} diff --git a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs deleted file mode 100644 index 550cc455e01..00000000000 --- a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs +++ /dev/null @@ -1,127 +0,0 @@ -//! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate -//! to hold. - -use crate::error_reporting::infer::nice_region_error::NiceRegionError; -use crate::errors::{note_and_explain, IntroducesStaticBecauseUnmetLifetimeReq}; -use crate::errors::{ - DoesNotOutliveStaticFromImpl, ImplicitStaticLifetimeSubdiag, MismatchedStaticLifetime, -}; -use crate::infer::RegionResolutionError; -use crate::infer::{SubregionOrigin, TypeTrace}; -use crate::traits::ObligationCauseCode; -use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{ErrorGuaranteed, MultiSpan}; -use rustc_hir as hir; -use rustc_hir::intravisit::Visitor; -use rustc_middle::bug; -use rustc_middle::ty::TypeVisitor; - -impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { - pub(super) fn try_report_mismatched_static_lifetime(&self) -> Option<ErrorGuaranteed> { - let error = self.error.as_ref()?; - debug!("try_report_mismatched_static_lifetime {:?}", error); - - let RegionResolutionError::ConcreteFailure(origin, sub, sup) = error.clone() else { - return None; - }; - if !sub.is_static() { - return None; - } - let SubregionOrigin::Subtype(box TypeTrace { ref cause, .. }) = origin else { - return None; - }; - // If we added a "points at argument expression" obligation, we remove it here, we care - // about the original obligation only. - let code = match cause.code() { - ObligationCauseCode::FunctionArg { parent_code, .. } => &*parent_code, - code => code, - }; - let ObligationCauseCode::MatchImpl(parent, impl_def_id) = code else { - return None; - }; - let (ObligationCauseCode::WhereClause(_, binding_span) - | ObligationCauseCode::WhereClauseInExpr(_, binding_span, ..)) = *parent.code() - else { - return None; - }; - if binding_span.is_dummy() { - return None; - } - - // FIXME: we should point at the lifetime - 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(), - self.generic_param_scope, - sup, - Some(binding_span), - note_and_explain::PrefixKind::Empty, - note_and_explain::SuffixKind::Continues, - ); - let mut impl_span = None; - let mut implicit_static_lifetimes = 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. - - // First, let's get the hir self type of the impl - let hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, .. }), - .. - }) = impl_node - else { - bug!("Node not an impl."); - }; - - // Next, let's figure out the set of trait objects with implicit static bounds - let ty = self.tcx().type_of(*impl_def_id).instantiate_identity(); - let mut v = super::static_impl_trait::TraitObjectVisitor(FxIndexSet::default()); - v.visit_ty(ty); - let mut traits = vec![]; - for matching_def_id in v.0 { - let mut hir_v = - super::static_impl_trait::HirTraitObjectVisitor(&mut traits, matching_def_id); - hir_v.visit_ty(impl_self_ty); - } - - if traits.is_empty() { - // If there are no trait object traits to point at, either because - // there aren't trait objects or because none are implicit, then just - // write a single note on the impl itself. - - impl_span = Some(self.tcx().def_span(*impl_def_id)); - } else { - // Otherwise, point at all implicit static lifetimes - - for span in &traits { - implicit_static_lifetimes - .push(ImplicitStaticLifetimeSubdiag::Note { span: *span }); - // It would be nice to put this immediately under the above note, but they get - // pushed to the end. - implicit_static_lifetimes - .push(ImplicitStaticLifetimeSubdiag::Sugg { span: span.shrink_to_hi() }); - } - } - } else { - // Otherwise just point out the impl. - - impl_span = Some(self.tcx().def_span(*impl_def_id)); - } - let err = MismatchedStaticLifetime { - cause_span: cause.span, - unmet_lifetime_reqs: multispan_subdiag, - expl, - does_not_outlive_static_from_impl: impl_span - .map(|span| DoesNotOutliveStaticFromImpl::Spanned { span }) - .unwrap_or(DoesNotOutliveStaticFromImpl::Unspanned), - implicit_static_lifetimes, - }; - let reported = self.tcx().dcx().emit_err(err); - Some(reported) - } -} diff --git a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/mod.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/mod.rs deleted file mode 100644 index ced4c384f02..00000000000 --- a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/mod.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::error_reporting::infer::TypeErrCtxt; -use crate::infer::RegionResolutionError; -use crate::infer::RegionResolutionError::*; -use rustc_errors::{Diag, ErrorGuaranteed}; -use rustc_hir::def_id::LocalDefId; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::Span; - -mod different_lifetimes; -pub mod find_anon_type; -mod mismatched_static_lifetime; -mod named_anon_conflict; -pub(crate) mod placeholder_error; -mod placeholder_relation; -mod static_impl_trait; -mod trait_impl_difference; -mod util; - -pub use different_lifetimes::suggest_adding_lifetime_params; -pub use find_anon_type::find_anon_type; -pub use static_impl_trait::{suggest_new_region_bound, HirTraitObjectVisitor, TraitObjectVisitor}; -pub use util::find_param_with_region; - -impl<'cx, 'tcx> TypeErrCtxt<'cx, 'tcx> { - pub fn try_report_nice_region_error( - &'cx self, - generic_param_scope: LocalDefId, - error: &RegionResolutionError<'tcx>, - ) -> Option<ErrorGuaranteed> { - NiceRegionError::new(self, generic_param_scope, error.clone()).try_report() - } -} - -pub struct NiceRegionError<'cx, 'tcx> { - cx: &'cx TypeErrCtxt<'cx, 'tcx>, - /// The innermost definition that introduces generic parameters that may be involved in - /// the region errors we are dealing with. - generic_param_scope: LocalDefId, - error: Option<RegionResolutionError<'tcx>>, - regions: Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)>, -} - -impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> { - pub fn new( - cx: &'cx TypeErrCtxt<'cx, 'tcx>, - generic_param_scope: LocalDefId, - error: RegionResolutionError<'tcx>, - ) -> Self { - Self { cx, error: Some(error), regions: None, generic_param_scope } - } - - pub fn new_from_span( - cx: &'cx TypeErrCtxt<'cx, 'tcx>, - generic_param_scope: LocalDefId, - span: Span, - sub: ty::Region<'tcx>, - sup: ty::Region<'tcx>, - ) -> Self { - Self { cx, error: None, regions: Some((span, sub, sup)), generic_param_scope } - } - - fn tcx(&self) -> TyCtxt<'tcx> { - self.cx.tcx - } - - pub fn try_report_from_nll(&self) -> Option<Diag<'tcx>> { - // Due to the improved diagnostics returned by the MIR borrow checker, only a subset of - // the nice region errors are required when running under the MIR borrow checker. - self.try_report_named_anon_conflict() - .or_else(|| self.try_report_placeholder_conflict()) - .or_else(|| self.try_report_placeholder_relation()) - } - - pub fn try_report(&self) -> Option<ErrorGuaranteed> { - self.try_report_from_nll() - .map(|diag| diag.emit()) - .or_else(|| self.try_report_impl_not_conforming_to_trait()) - .or_else(|| self.try_report_anon_anon_conflict()) - .or_else(|| self.try_report_static_impl_trait()) - .or_else(|| self.try_report_mismatched_static_lifetime()) - } - - pub(super) fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> { - match (&self.error, self.regions) { - (Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), *sub, *sup)), - (Some(SubSupConflict(_, _, origin, sub, _, sup, _)), None) => { - Some((origin.span(), *sub, *sup)) - } - (None, Some((span, sub, sup))) => Some((span, sub, sup)), - _ => None, - } - } -} diff --git a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs deleted file mode 100644 index d1802d2f5ee..00000000000 --- a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs +++ /dev/null @@ -1,92 +0,0 @@ -//! Error Reporting for Anonymous Region Lifetime Errors -//! where one region is named and the other is anonymous. - -use crate::error_reporting::infer::nice_region_error::find_anon_type::find_anon_type; -use crate::error_reporting::infer::nice_region_error::NiceRegionError; -use crate::errors::ExplicitLifetimeRequired; -use rustc_errors::Diag; -use rustc_middle::ty; -use rustc_span::symbol::kw; - -impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { - /// When given a `ConcreteFailure` for a function with parameters containing a named region and - /// an anonymous region, emit an descriptive diagnostic error. - pub(super) fn try_report_named_anon_conflict(&self) -> Option<Diag<'tcx>> { - let (span, sub, sup) = self.regions()?; - - debug!( - "try_report_named_anon_conflict(sub={:?}, sup={:?}, error={:?})", - sub, sup, self.error, - ); - - // Determine whether the sub and sup consist of one named region ('a) - // and one anonymous (elided) region. If so, find the parameter arg - // where the anonymous region appears (there must always be one; we - // only introduced anonymous regions in parameters) as well as a - // version new_ty of its type where the anonymous region is replaced - // with the named one. - let (named, anon, anon_param_info, region_info) = if sub.has_name() - && let Some(region_info) = self.tcx().is_suitable_region(self.generic_param_scope, sup) - && let Some(anon_param_info) = self.find_param_with_region(sup, sub) - { - (sub, sup, anon_param_info, region_info) - } else if sup.has_name() - && let Some(region_info) = self.tcx().is_suitable_region(self.generic_param_scope, sub) - && let Some(anon_param_info) = self.find_param_with_region(sub, sup) - { - (sup, sub, anon_param_info, region_info) - } else { - return None; // inapplicable - }; - - // Suggesting to add a `'static` lifetime to a parameter is nearly always incorrect, - // and can steer users down the wrong path. - if named.is_static() { - return None; - } - - debug!("try_report_named_anon_conflict: named = {:?}", named); - debug!("try_report_named_anon_conflict: anon_param_info = {:?}", anon_param_info); - debug!("try_report_named_anon_conflict: region_info = {:?}", region_info); - - let param = anon_param_info.param; - let new_ty = anon_param_info.param_ty; - let new_ty_span = anon_param_info.param_ty_span; - let br = anon_param_info.bound_region; - let is_first = anon_param_info.is_first; - let scope_def_id = region_info.def_id; - let is_impl_item = region_info.is_impl_item; - - match br { - ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon => {} - _ => { - /* not an anonymous region */ - debug!("try_report_named_anon_conflict: not an anonymous region"); - return None; - } - } - - if is_impl_item { - debug!("try_report_named_anon_conflict: impl item, bail out"); - return None; - } - - if find_anon_type(self.tcx(), self.generic_param_scope, anon, &br).is_some() - && self.is_self_anon(is_first, scope_def_id) - { - return None; - } - let named = named.to_string(); - let err = match param.pat.simple_ident() { - Some(simple_ident) => ExplicitLifetimeRequired::WithIdent { - span, - simple_ident, - named, - new_ty_span, - new_ty, - }, - None => ExplicitLifetimeRequired::WithParamType { span, named, new_ty_span, new_ty }, - }; - Some(self.tcx().sess.dcx().create_err(err)) - } -} diff --git a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/placeholder_error.rs deleted file mode 100644 index 476ac3f1720..00000000000 --- a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/placeholder_error.rs +++ /dev/null @@ -1,496 +0,0 @@ -use crate::error_reporting::infer::nice_region_error::NiceRegionError; -use crate::errors::{ - ActualImplExpectedKind, ActualImplExpectedLifetimeKind, ActualImplExplNotes, - TraitPlaceholderMismatch, TyOrSig, -}; -use crate::infer::RegionResolutionError; -use crate::infer::ValuePairs; -use crate::infer::{SubregionOrigin, TypeTrace}; -use crate::traits::{ObligationCause, ObligationCauseCode}; -use rustc_data_structures::intern::Interned; -use rustc_errors::{Diag, IntoDiagArg}; -use rustc_hir::def::Namespace; -use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; -use rustc_middle::bug; -use rustc_middle::ty::error::ExpectedFound; -use rustc_middle::ty::print::{FmtPrinter, Print, PrintTraitRefExt as _, RegionHighlightMode}; -use rustc_middle::ty::GenericArgsRef; -use rustc_middle::ty::{self, RePlaceholder, Region, TyCtxt}; - -use std::fmt; - -// HACK(eddyb) maybe move this in a more central location. -#[derive(Copy, Clone)] -pub struct Highlighted<'tcx, T> { - tcx: TyCtxt<'tcx>, - highlight: RegionHighlightMode<'tcx>, - value: T, -} - -impl<'tcx, T> IntoDiagArg for Highlighted<'tcx, T> -where - T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>>, -{ - fn into_diag_arg(self) -> rustc_errors::DiagArgValue { - rustc_errors::DiagArgValue::Str(self.to_string().into()) - } -} - -impl<'tcx, T> Highlighted<'tcx, T> { - fn map<U>(self, f: impl FnOnce(T) -> U) -> Highlighted<'tcx, U> { - Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value) } - } -} - -impl<'tcx, T> fmt::Display for Highlighted<'tcx, T> -where - T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>>, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS); - printer.region_highlight_mode = self.highlight; - - self.value.print(&mut printer)?; - f.write_str(&printer.into_buffer()) - } -} - -impl<'tcx> NiceRegionError<'_, 'tcx> { - /// When given a `ConcreteFailure` for a function with arguments containing a named region and - /// an anonymous region, emit a descriptive diagnostic error. - pub(super) fn try_report_placeholder_conflict(&self) -> Option<Diag<'tcx>> { - match &self.error { - /////////////////////////////////////////////////////////////////////////// - // NB. The ordering of cases in this match is very - // sensitive, because we are often matching against - // specific cases and then using an `_` to match all - // others. - - /////////////////////////////////////////////////////////////////////////// - // Check for errors from comparing trait failures -- first - // with two placeholders, then with one. - Some(RegionResolutionError::SubSupConflict( - vid, - _, - SubregionOrigin::Subtype(box TypeTrace { cause, values }), - sub_placeholder @ Region(Interned(RePlaceholder(_), _)), - _, - sup_placeholder @ Region(Interned(RePlaceholder(_), _)), - _, - )) => self.try_report_trait_placeholder_mismatch( - Some(ty::Region::new_var(self.tcx(), *vid)), - cause, - Some(*sub_placeholder), - Some(*sup_placeholder), - values, - ), - - Some(RegionResolutionError::SubSupConflict( - vid, - _, - SubregionOrigin::Subtype(box TypeTrace { cause, values }), - sub_placeholder @ Region(Interned(RePlaceholder(_), _)), - _, - _, - _, - )) => self.try_report_trait_placeholder_mismatch( - Some(ty::Region::new_var(self.tcx(), *vid)), - cause, - Some(*sub_placeholder), - None, - values, - ), - - Some(RegionResolutionError::SubSupConflict( - vid, - _, - SubregionOrigin::Subtype(box TypeTrace { cause, values }), - _, - _, - sup_placeholder @ Region(Interned(RePlaceholder(_), _)), - _, - )) => self.try_report_trait_placeholder_mismatch( - Some(ty::Region::new_var(self.tcx(), *vid)), - cause, - None, - Some(*sup_placeholder), - values, - ), - - Some(RegionResolutionError::SubSupConflict( - vid, - _, - _, - _, - SubregionOrigin::Subtype(box TypeTrace { cause, values }), - sup_placeholder @ Region(Interned(RePlaceholder(_), _)), - _, - )) => self.try_report_trait_placeholder_mismatch( - Some(ty::Region::new_var(self.tcx(), *vid)), - cause, - None, - Some(*sup_placeholder), - values, - ), - - Some(RegionResolutionError::UpperBoundUniverseConflict( - vid, - _, - _, - SubregionOrigin::Subtype(box TypeTrace { cause, values }), - sup_placeholder @ Region(Interned(RePlaceholder(_), _)), - )) => self.try_report_trait_placeholder_mismatch( - Some(ty::Region::new_var(self.tcx(), *vid)), - cause, - None, - Some(*sup_placeholder), - values, - ), - - Some(RegionResolutionError::ConcreteFailure( - SubregionOrigin::Subtype(box TypeTrace { cause, values }), - sub_region @ Region(Interned(RePlaceholder(_), _)), - sup_region @ Region(Interned(RePlaceholder(_), _)), - )) => self.try_report_trait_placeholder_mismatch( - None, - cause, - Some(*sub_region), - Some(*sup_region), - values, - ), - - Some(RegionResolutionError::ConcreteFailure( - SubregionOrigin::Subtype(box TypeTrace { cause, values }), - sub_region @ Region(Interned(RePlaceholder(_), _)), - sup_region, - )) => self.try_report_trait_placeholder_mismatch( - (!sup_region.has_name()).then_some(*sup_region), - cause, - Some(*sub_region), - None, - values, - ), - - Some(RegionResolutionError::ConcreteFailure( - SubregionOrigin::Subtype(box TypeTrace { cause, values }), - sub_region, - sup_region @ Region(Interned(RePlaceholder(_), _)), - )) => self.try_report_trait_placeholder_mismatch( - (!sub_region.has_name()).then_some(*sub_region), - cause, - None, - Some(*sup_region), - values, - ), - - _ => None, - } - } - - fn try_report_trait_placeholder_mismatch( - &self, - vid: Option<Region<'tcx>>, - cause: &ObligationCause<'tcx>, - sub_placeholder: Option<Region<'tcx>>, - sup_placeholder: Option<Region<'tcx>>, - value_pairs: &ValuePairs<'tcx>, - ) -> Option<Diag<'tcx>> { - let (expected_args, found_args, trait_def_id) = match value_pairs { - ValuePairs::TraitRefs(ExpectedFound { expected, found }) - if expected.def_id == found.def_id => - { - // It's possible that the placeholders come from a binder - // outside of this value pair. Use `no_bound_vars` as a - // simple heuristic for that. - (expected.args, found.args, expected.def_id) - } - _ => return None, - }; - - Some(self.report_trait_placeholder_mismatch( - vid, - cause, - sub_placeholder, - sup_placeholder, - trait_def_id, - expected_args, - found_args, - )) - } - - // error[E0308]: implementation of `Foo` does not apply to enough lifetimes - // --> /home/nmatsakis/tmp/foo.rs:12:5 - // | - // 12 | all::<&'static u32>(); - // | ^^^^^^^^^^^^^^^^^^^ lifetime mismatch - // | - // = note: Due to a where-clause on the function `all`, - // = note: `T` must implement `...` for any two lifetimes `'1` and `'2`. - // = note: However, the type `T` only implements `...` for some specific lifetime `'2`. - #[instrument(level = "debug", skip(self))] - fn report_trait_placeholder_mismatch( - &self, - vid: Option<Region<'tcx>>, - cause: &ObligationCause<'tcx>, - sub_placeholder: Option<Region<'tcx>>, - sup_placeholder: Option<Region<'tcx>>, - trait_def_id: DefId, - expected_args: GenericArgsRef<'tcx>, - actual_args: GenericArgsRef<'tcx>, - ) -> Diag<'tcx> { - let span = cause.span(); - - let (leading_ellipsis, satisfy_span, where_span, dup_span, def_id) = - if let ObligationCauseCode::WhereClause(def_id, span) - | ObligationCauseCode::WhereClauseInExpr(def_id, span, ..) = *cause.code() - && def_id != CRATE_DEF_ID.to_def_id() - { - ( - true, - Some(span), - Some(self.tcx().def_span(def_id)), - None, - self.tcx().def_path_str(def_id), - ) - } else { - (false, None, None, Some(span), String::new()) - }; - - let expected_trait_ref = self.cx.resolve_vars_if_possible(ty::TraitRef::new_from_args( - self.cx.tcx, - trait_def_id, - expected_args, - )); - let actual_trait_ref = self.cx.resolve_vars_if_possible(ty::TraitRef::new_from_args( - self.cx.tcx, - trait_def_id, - actual_args, - )); - - // Search the expected and actual trait references to see (a) - // whether the sub/sup placeholders appear in them (sometimes - // you have a trait ref like `T: Foo<fn(&u8)>`, where the - // placeholder was created as part of an inner type) and (b) - // whether the inference variable appears. In each case, - // assign a counter value in each case if so. - let mut counter = 0; - let mut has_sub = None; - let mut has_sup = None; - - let mut actual_has_vid = None; - let mut expected_has_vid = None; - - self.tcx().for_each_free_region(&expected_trait_ref, |r| { - if Some(r) == sub_placeholder && has_sub.is_none() { - has_sub = Some(counter); - counter += 1; - } else if Some(r) == sup_placeholder && has_sup.is_none() { - has_sup = Some(counter); - counter += 1; - } - - if Some(r) == vid && expected_has_vid.is_none() { - expected_has_vid = Some(counter); - counter += 1; - } - }); - - self.tcx().for_each_free_region(&actual_trait_ref, |r| { - if Some(r) == vid && actual_has_vid.is_none() { - actual_has_vid = Some(counter); - counter += 1; - } - }); - - let actual_self_ty_has_vid = - self.tcx().any_free_region_meets(&actual_trait_ref.self_ty(), |r| Some(r) == vid); - - let expected_self_ty_has_vid = - self.tcx().any_free_region_meets(&expected_trait_ref.self_ty(), |r| Some(r) == vid); - - let any_self_ty_has_vid = actual_self_ty_has_vid || expected_self_ty_has_vid; - - debug!( - ?actual_has_vid, - ?expected_has_vid, - ?has_sub, - ?has_sup, - ?actual_self_ty_has_vid, - ?expected_self_ty_has_vid, - ); - - let actual_impl_expl_notes = self.explain_actual_impl_that_was_found( - sub_placeholder, - sup_placeholder, - has_sub, - has_sup, - expected_trait_ref, - actual_trait_ref, - vid, - expected_has_vid, - actual_has_vid, - any_self_ty_has_vid, - leading_ellipsis, - ); - - self.tcx().dcx().create_err(TraitPlaceholderMismatch { - span, - satisfy_span, - where_span, - dup_span, - def_id, - trait_def_id: self.tcx().def_path_str(trait_def_id), - actual_impl_expl_notes, - }) - } - - /// Add notes with details about the expected and actual trait refs, with attention to cases - /// when placeholder regions are involved: either the trait or the self type containing - /// them needs to be mentioned the closest to the placeholders. - /// This makes the error messages read better, however at the cost of some complexity - /// due to the number of combinations we have to deal with. - fn explain_actual_impl_that_was_found( - &self, - sub_placeholder: Option<Region<'tcx>>, - sup_placeholder: Option<Region<'tcx>>, - has_sub: Option<usize>, - has_sup: Option<usize>, - expected_trait_ref: ty::TraitRef<'tcx>, - actual_trait_ref: ty::TraitRef<'tcx>, - vid: Option<Region<'tcx>>, - expected_has_vid: Option<usize>, - actual_has_vid: Option<usize>, - any_self_ty_has_vid: bool, - leading_ellipsis: bool, - ) -> Vec<ActualImplExplNotes<'tcx>> { - // The weird thing here with the `maybe_highlighting_region` calls and the - // the match inside is meant to be like this: - // - // - The match checks whether the given things (placeholders, etc) appear - // in the types are about to print - // - Meanwhile, the `maybe_highlighting_region` calls set up - // highlights so that, if they do appear, we will replace - // them `'0` and whatever. (This replacement takes place - // inside the closure given to `maybe_highlighting_region`.) - // - // There is some duplication between the calls -- i.e., the - // `maybe_highlighting_region` checks if (e.g.) `has_sub` is - // None, an then we check again inside the closure, but this - // setup sort of minimized the number of calls and so form. - - let highlight_trait_ref = |trait_ref| Highlighted { - tcx: self.tcx(), - highlight: RegionHighlightMode::default(), - value: trait_ref, - }; - - let same_self_type = actual_trait_ref.self_ty() == expected_trait_ref.self_ty(); - - let mut expected_trait_ref = highlight_trait_ref(expected_trait_ref); - expected_trait_ref.highlight.maybe_highlighting_region(sub_placeholder, has_sub); - expected_trait_ref.highlight.maybe_highlighting_region(sup_placeholder, has_sup); - - let passive_voice = match (has_sub, has_sup) { - (Some(_), _) | (_, Some(_)) => any_self_ty_has_vid, - (None, None) => { - expected_trait_ref.highlight.maybe_highlighting_region(vid, expected_has_vid); - match expected_has_vid { - Some(_) => true, - None => any_self_ty_has_vid, - } - } - }; - - let (kind, ty_or_sig, trait_path) = if same_self_type { - let mut self_ty = expected_trait_ref.map(|tr| tr.self_ty()); - self_ty.highlight.maybe_highlighting_region(vid, actual_has_vid); - - if self_ty.value.is_closure() && self.tcx().is_fn_trait(expected_trait_ref.value.def_id) - { - let closure_sig = self_ty.map(|closure| { - if let ty::Closure(_, args) = closure.kind() { - self.tcx() - .signature_unclosure(args.as_closure().sig(), rustc_hir::Safety::Safe) - } else { - bug!("type is not longer closure"); - } - }); - ( - ActualImplExpectedKind::Signature, - TyOrSig::ClosureSig(closure_sig), - expected_trait_ref.map(|tr| tr.print_only_trait_path()), - ) - } else { - ( - ActualImplExpectedKind::Other, - TyOrSig::Ty(self_ty), - expected_trait_ref.map(|tr| tr.print_only_trait_path()), - ) - } - } else if passive_voice { - ( - ActualImplExpectedKind::Passive, - TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())), - expected_trait_ref.map(|tr| tr.print_only_trait_path()), - ) - } else { - ( - ActualImplExpectedKind::Other, - TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())), - expected_trait_ref.map(|tr| tr.print_only_trait_path()), - ) - }; - - let (lt_kind, lifetime_1, lifetime_2) = match (has_sub, has_sup) { - (Some(n1), Some(n2)) => { - (ActualImplExpectedLifetimeKind::Two, std::cmp::min(n1, n2), std::cmp::max(n1, n2)) - } - (Some(n), _) | (_, Some(n)) => (ActualImplExpectedLifetimeKind::Any, n, 0), - (None, None) => { - if let Some(n) = expected_has_vid { - (ActualImplExpectedLifetimeKind::Some, n, 0) - } else { - (ActualImplExpectedLifetimeKind::Nothing, 0, 0) - } - } - }; - - let note_1 = ActualImplExplNotes::new_expected( - kind, - lt_kind, - leading_ellipsis, - ty_or_sig, - trait_path, - lifetime_1, - lifetime_2, - ); - - let mut actual_trait_ref = highlight_trait_ref(actual_trait_ref); - actual_trait_ref.highlight.maybe_highlighting_region(vid, actual_has_vid); - - let passive_voice = match actual_has_vid { - Some(_) => any_self_ty_has_vid, - None => true, - }; - - let trait_path = actual_trait_ref.map(|tr| tr.print_only_trait_path()); - let ty = actual_trait_ref.map(|tr| tr.self_ty()).to_string(); - let has_lifetime = actual_has_vid.is_some(); - let lifetime = actual_has_vid.unwrap_or_default(); - - let note_2 = if same_self_type { - ActualImplExplNotes::ButActuallyImplementsTrait { trait_path, has_lifetime, lifetime } - } else if passive_voice { - ActualImplExplNotes::ButActuallyImplementedForTy { - trait_path, - ty, - has_lifetime, - lifetime, - } - } else { - ActualImplExplNotes::ButActuallyTyImplements { trait_path, ty, has_lifetime, lifetime } - }; - - vec![note_1, note_2] - } -} diff --git a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/placeholder_relation.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/placeholder_relation.rs deleted file mode 100644 index e9f17a3e3e2..00000000000 --- a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/placeholder_relation.rs +++ /dev/null @@ -1,86 +0,0 @@ -use crate::error_reporting::infer::nice_region_error::NiceRegionError; -use crate::errors::PlaceholderRelationLfNotSatisfied; -use crate::infer::{RegionResolutionError, SubregionOrigin}; -use rustc_data_structures::intern::Interned; -use rustc_errors::Diag; -use rustc_middle::ty::{self, RePlaceholder, Region}; - -impl<'tcx> NiceRegionError<'_, 'tcx> { - /// Emitted wwhen given a `ConcreteFailure` when relating two placeholders. - pub(super) fn try_report_placeholder_relation(&self) -> Option<Diag<'tcx>> { - match &self.error { - Some(RegionResolutionError::ConcreteFailure( - SubregionOrigin::RelateRegionParamBound(span), - Region(Interned( - RePlaceholder(ty::Placeholder { - bound: ty::BoundRegion { kind: sub_name, .. }, - .. - }), - _, - )), - Region(Interned( - RePlaceholder(ty::Placeholder { - bound: ty::BoundRegion { kind: sup_name, .. }, - .. - }), - _, - )), - )) => { - let span = *span; - let (sub_span, sub_symbol) = match sub_name { - ty::BrNamed(def_id, symbol) => { - (Some(self.tcx().def_span(def_id)), Some(symbol)) - } - ty::BrAnon | ty::BrEnv => (None, None), - }; - let (sup_span, sup_symbol) = match sup_name { - ty::BrNamed(def_id, symbol) => { - (Some(self.tcx().def_span(def_id)), Some(symbol)) - } - ty::BrAnon | ty::BrEnv => (None, None), - }; - let diag = match (sub_span, sup_span, sub_symbol, sup_symbol) { - (Some(sub_span), Some(sup_span), Some(&sub_symbol), Some(&sup_symbol)) => { - PlaceholderRelationLfNotSatisfied::HasBoth { - span, - sub_span, - sup_span, - sub_symbol, - sup_symbol, - note: (), - } - } - (Some(sub_span), Some(sup_span), _, Some(&sup_symbol)) => { - PlaceholderRelationLfNotSatisfied::HasSup { - span, - sub_span, - sup_span, - sup_symbol, - note: (), - } - } - (Some(sub_span), Some(sup_span), Some(&sub_symbol), _) => { - PlaceholderRelationLfNotSatisfied::HasSub { - span, - sub_span, - sup_span, - sub_symbol, - note: (), - } - } - (Some(sub_span), Some(sup_span), _, _) => { - PlaceholderRelationLfNotSatisfied::HasNone { - span, - sub_span, - sup_span, - note: (), - } - } - _ => PlaceholderRelationLfNotSatisfied::OnlyPrimarySpan { span, note: () }, - }; - Some(self.tcx().dcx().create_err(diag)) - } - _ => None, - } - } -} diff --git a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/static_impl_trait.rs deleted file mode 100644 index ce157ff3dc8..00000000000 --- a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ /dev/null @@ -1,618 +0,0 @@ -//! Error Reporting for static impl Traits. - -use crate::error_reporting::infer::nice_region_error::NiceRegionError; -use crate::errors::{ - ButCallingIntroduces, ButNeedsToSatisfy, DynTraitConstraintSuggestion, MoreTargeted, - ReqIntroducedLocations, -}; -use crate::infer::RegionResolutionError; -use crate::infer::{SubregionOrigin, TypeTrace}; -use crate::traits::{ObligationCauseCode, UnifyReceiverContext}; -use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, Subdiagnostic}; -use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{walk_ty, Visitor}; -use rustc_hir::{ - self as hir, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime, - LifetimeName, LifetimeParamKind, MissingLifetimeKind, Node, TyKind, -}; -use rustc_middle::ty::{ - self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, -}; -use rustc_span::symbol::Ident; -use rustc_span::Span; - -use rustc_span::def_id::LocalDefId; - -impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { - /// Print the error message for lifetime errors when the return type is a static `impl Trait`, - /// `dyn Trait` or if a method call on a trait object introduces a static requirement. - pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> { - debug!("try_report_static_impl_trait(error={:?})", self.error); - let tcx = self.tcx(); - let (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) = match self.error.as_ref()? { - RegionResolutionError::SubSupConflict( - _, - var_origin, - sub_origin, - sub_r, - sup_origin, - sup_r, - spans, - ) if sub_r.is_static() => (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans), - RegionResolutionError::ConcreteFailure( - SubregionOrigin::Subtype(box TypeTrace { cause, .. }), - sub_r, - sup_r, - ) if sub_r.is_static() => { - // This is for an implicit `'static` requirement coming from `impl dyn Trait {}`. - if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() { - // This may have a closure and it would cause ICE - // through `find_param_with_region` (#78262). - let anon_reg_sup = tcx.is_suitable_region(self.generic_param_scope, *sup_r)?; - let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); - if fn_returns.is_empty() { - return None; - } - - let param = self.find_param_with_region(*sup_r, *sub_r)?; - let simple_ident = param.param.pat.simple_ident(); - - let (has_impl_path, impl_path) = match ctxt.assoc_item.container { - AssocItemContainer::TraitContainer => { - let id = ctxt.assoc_item.container_id(tcx); - (true, tcx.def_path_str(id)) - } - AssocItemContainer::ImplContainer => (false, String::new()), - }; - - let mut err = self.tcx().dcx().create_err(ButCallingIntroduces { - param_ty_span: param.param_ty_span, - cause_span: cause.span, - has_param_name: simple_ident.is_some(), - param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(), - has_lifetime: sup_r.has_name(), - lifetime: sup_r.to_string(), - assoc_item: ctxt.assoc_item.name, - has_impl_path, - impl_path, - }); - if self.find_impl_on_dyn_trait(&mut err, param.param_ty, ctxt) { - let reported = err.emit(); - return Some(reported); - } else { - err.cancel() - } - } - return None; - } - _ => return None, - }; - debug!( - "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})", - var_origin, sub_origin, sub_r, sup_origin, sup_r - ); - let anon_reg_sup = tcx.is_suitable_region(self.generic_param_scope, *sup_r)?; - debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup); - let sp = var_origin.span(); - let return_sp = sub_origin.span(); - let param = self.find_param_with_region(*sup_r, *sub_r)?; - let simple_ident = param.param.pat.simple_ident(); - let lifetime_name = if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() }; - - let (mention_influencer, influencer_point) = - if sup_origin.span().overlaps(param.param_ty_span) { - // Account for `async fn` like in `async-await/issues/issue-62097.rs`. - // The desugaring of `async fn`s causes `sup_origin` and `param` to point at the same - // place (but with different `ctxt`, hence `overlaps` instead of `==` above). - // - // This avoids the following: - // - // LL | pub async fn run_dummy_fn(&self) { - // | ^^^^^ - // | | - // | this data with an anonymous lifetime `'_`... - // | ...is captured here... - (false, sup_origin.span()) - } else { - (!sup_origin.span().overlaps(return_sp), param.param_ty_span) - }; - - debug!("try_report_static_impl_trait: param_info={:?}", param); - - let mut spans = spans.clone(); - - if mention_influencer { - spans.push(sup_origin.span()); - } - // We dedup the spans *ignoring* expansion context. - spans.sort(); - spans.dedup_by_key(|span| (span.lo(), span.hi())); - - // We try to make the output have fewer overlapping spans if possible. - let require_span = - if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp }; - - let spans_empty = spans.is_empty(); - let require_as_note = spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp); - let bound = if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin { - Some(*bound) - } else { - None - }; - - let mut subdiag = None; - - if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = sub_origin { - if let ObligationCauseCode::ReturnValue(hir_id) - | ObligationCauseCode::BlockTailExpression(hir_id, ..) = cause.code() - { - let parent_id = tcx.hir().get_parent_item(*hir_id); - if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id.into()) { - let mut span: MultiSpan = fn_decl.output.span().into(); - let mut spans = Vec::new(); - let mut add_label = true; - if let hir::FnRetTy::Return(ty) = fn_decl.output { - let mut v = StaticLifetimeVisitor(vec![], tcx.hir()); - v.visit_ty(ty); - if !v.0.is_empty() { - span = v.0.clone().into(); - spans = v.0; - add_label = false; - } - } - let fn_decl_span = fn_decl.output.span(); - - subdiag = Some(ReqIntroducedLocations { - span, - spans, - fn_decl_span, - cause_span: cause.span, - add_label, - }); - } - } - } - - let diag = ButNeedsToSatisfy { - sp, - influencer_point, - spans: spans.clone(), - // If any of the "captured here" labels appears on the same line or after - // `require_span`, we put it on a note to ensure the text flows by appearing - // always at the end. - require_span_as_note: require_as_note.then_some(require_span), - // We don't need a note, it's already at the end, it can be shown as a `span_label`. - require_span_as_label: (!require_as_note).then_some(require_span), - req_introduces_loc: subdiag, - - has_lifetime: sup_r.has_name(), - lifetime: lifetime_name.clone(), - has_param_name: simple_ident.is_some(), - param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(), - spans_empty, - bound, - }; - - let mut err = self.tcx().dcx().create_err(diag); - - let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); - - let mut override_error_code = None; - if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin - && let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() - // Handle case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a - // `'static` lifetime when called as a method on a binding: `bar.qux()`. - && self.find_impl_on_dyn_trait(&mut err, param.param_ty, ctxt) - { - override_error_code = Some(ctxt.assoc_item.name); - } - - if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin - && let code = match cause.code() { - ObligationCauseCode::MatchImpl(parent, ..) => parent.code(), - _ => cause.code(), - } - && let ( - &ObligationCauseCode::WhereClause(item_def_id, _) - | &ObligationCauseCode::WhereClauseInExpr(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: - // `Foo::qux(bar)`. - let mut v = TraitObjectVisitor(FxIndexSet::default()); - v.visit_ty(param.param_ty); - if let Some((ident, self_ty)) = - NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, item_def_id, &v.0) - && self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty) - { - override_error_code = Some(ident.name); - } - } - if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) { - // Provide a more targeted error code and description. - let retarget_subdiag = MoreTargeted { ident }; - retarget_subdiag.add_to_diag(&mut err); - } - - let arg = match param.param.pat.simple_ident() { - Some(simple_ident) => format!("argument `{simple_ident}`"), - None => "the argument".to_string(), - }; - let captures = format!("captures data from {arg}"); - suggest_new_region_bound( - tcx, - &mut err, - fn_returns, - lifetime_name, - Some(arg), - captures, - Some((param.param_ty_span, param.param_ty.to_string())), - Some(anon_reg_sup.def_id), - ); - - let reported = err.emit(); - Some(reported) - } -} - -pub fn suggest_new_region_bound( - tcx: TyCtxt<'_>, - err: &mut Diag<'_>, - fn_returns: Vec<&rustc_hir::Ty<'_>>, - lifetime_name: String, - arg: Option<String>, - captures: String, - param: Option<(Span, String)>, - scope_def_id: Option<LocalDefId>, -) { - debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns); - // FIXME: account for the need of parens in `&(dyn Trait + '_)` - let consider = "consider changing"; - let declare = "to declare that"; - let explicit = format!("you can add an explicit `{lifetime_name}` lifetime bound"); - let explicit_static = - arg.map(|arg| format!("explicit `'static` bound to the lifetime of {arg}")); - let add_static_bound = "alternatively, add an explicit `'static` bound to this reference"; - let plus_lt = format!(" + {lifetime_name}"); - for fn_return in fn_returns { - if fn_return.span.desugaring_kind().is_some() { - // Skip `async` desugaring `impl Future`. - continue; - } - match fn_return.kind { - // FIXME(precise_captures): Suggest adding to `use<...>` list instead. - TyKind::OpaqueDef(item_id, _, _) => { - let item = tcx.hir().item(item_id); - let ItemKind::OpaqueTy(opaque) = &item.kind else { - return; - }; - - // Get the identity type for this RPIT - let did = item_id.owner_id.to_def_id(); - let ty = Ty::new_opaque(tcx, did, ty::GenericArgs::identity_for_item(tcx, did)); - - if let Some(span) = opaque.bounds.iter().find_map(|arg| match arg { - GenericBound::Outlives(Lifetime { - res: LifetimeName::Static, ident, .. - }) => Some(ident.span), - _ => None, - }) { - if let Some(explicit_static) = &explicit_static { - err.span_suggestion_verbose( - span, - format!("{consider} `{ty}`'s {explicit_static}"), - &lifetime_name, - Applicability::MaybeIncorrect, - ); - } - if let Some((param_span, ref param_ty)) = param { - err.span_suggestion_verbose( - param_span, - add_static_bound, - param_ty, - Applicability::MaybeIncorrect, - ); - } - } else if opaque.bounds.iter().any(|arg| { - matches!(arg, - GenericBound::Outlives(Lifetime { ident, .. }) - if ident.name.to_string() == lifetime_name ) - }) { - } else { - // get a lifetime name of existing named lifetimes if any - let existing_lt_name = if let Some(id) = scope_def_id - && let Some(generics) = tcx.hir().get_generics(id) - && let named_lifetimes = generics - .params - .iter() - .filter(|p| { - matches!( - p.kind, - GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Explicit - } - ) - }) - .map(|p| { - if let hir::ParamName::Plain(name) = p.name { - Some(name.to_string()) - } else { - None - } - }) - .filter(|n| !matches!(n, None)) - .collect::<Vec<_>>() - && named_lifetimes.len() > 0 - { - named_lifetimes[0].clone() - } else { - None - }; - let name = if let Some(name) = &existing_lt_name { name } else { "'a" }; - // if there are more than one elided lifetimes in inputs, the explicit `'_` lifetime cannot be used. - // introducing a new lifetime `'a` or making use of one from existing named lifetimes if any - if let Some(id) = scope_def_id - && let Some(generics) = tcx.hir().get_generics(id) - && let mut spans_suggs = - make_elided_region_spans_suggs(name, generics.params.iter()) - && spans_suggs.len() > 1 - { - let use_lt = if existing_lt_name == None { - spans_suggs.push((generics.span.shrink_to_hi(), format!("<{name}>"))); - format!("you can introduce a named lifetime parameter `{name}`") - } else { - // make use the existing named lifetime - format!("you can use the named lifetime parameter `{name}`") - }; - spans_suggs.push((fn_return.span.shrink_to_hi(), format!(" + {name} "))); - err.multipart_suggestion_verbose( - format!("{declare} `{ty}` {captures}, {use_lt}",), - spans_suggs, - Applicability::MaybeIncorrect, - ); - } else { - err.span_suggestion_verbose( - fn_return.span.shrink_to_hi(), - format!("{declare} `{ty}` {captures}, {explicit}",), - &plus_lt, - Applicability::MaybeIncorrect, - ); - } - } - } - TyKind::TraitObject(_, lt, _) => { - if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res { - err.span_suggestion_verbose( - fn_return.span.shrink_to_hi(), - format!("{declare} the trait object {captures}, {explicit}",), - &plus_lt, - Applicability::MaybeIncorrect, - ); - } else if lt.ident.name.to_string() != lifetime_name { - // With this check we avoid suggesting redundant bounds. This - // would happen if there are nested impl/dyn traits and only - // one of them has the bound we'd suggest already there, like - // in `impl Foo<X = dyn Bar> + '_`. - if let Some(explicit_static) = &explicit_static { - err.span_suggestion_verbose( - lt.ident.span, - format!("{consider} the trait object's {explicit_static}"), - &lifetime_name, - Applicability::MaybeIncorrect, - ); - } - if let Some((param_span, param_ty)) = param.clone() { - err.span_suggestion_verbose( - param_span, - add_static_bound, - param_ty, - Applicability::MaybeIncorrect, - ); - } - } - } - _ => {} - } - } -} - -fn make_elided_region_spans_suggs<'a>( - name: &str, - generic_params: impl Iterator<Item = &'a GenericParam<'a>>, -) -> Vec<(Span, String)> { - let mut spans_suggs = Vec::new(); - let mut bracket_span = None; - let mut consecutive_brackets = 0; - - let mut process_consecutive_brackets = - |span: Option<Span>, spans_suggs: &mut Vec<(Span, String)>| { - if span - .is_some_and(|span| bracket_span.map_or(true, |bracket_span| span == bracket_span)) - { - consecutive_brackets += 1; - } else if let Some(bracket_span) = bracket_span.take() { - let sugg = std::iter::once("<") - .chain(std::iter::repeat(name).take(consecutive_brackets).intersperse(", ")) - .chain([">"]) - .collect(); - spans_suggs.push((bracket_span.shrink_to_hi(), sugg)); - consecutive_brackets = 0; - } - bracket_span = span; - }; - - for p in generic_params { - if let GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided(kind) } = p.kind { - match kind { - MissingLifetimeKind::Underscore => { - process_consecutive_brackets(None, &mut spans_suggs); - spans_suggs.push((p.span, name.to_string())) - } - MissingLifetimeKind::Ampersand => { - process_consecutive_brackets(None, &mut spans_suggs); - spans_suggs.push((p.span.shrink_to_hi(), format!("{name} "))); - } - MissingLifetimeKind::Comma => { - process_consecutive_brackets(None, &mut spans_suggs); - spans_suggs.push((p.span.shrink_to_hi(), format!("{name}, "))); - } - MissingLifetimeKind::Brackets => { - process_consecutive_brackets(Some(p.span), &mut spans_suggs); - } - } - } - } - process_consecutive_brackets(None, &mut spans_suggs); - - spans_suggs -} - -impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { - pub fn get_impl_ident_and_self_ty_from_trait( - tcx: TyCtxt<'tcx>, - def_id: DefId, - trait_objects: &FxIndexSet<DefId>, - ) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> { - match tcx.hir().get_if_local(def_id)? { - Node::ImplItem(impl_item) => { - let impl_did = tcx.hir().get_parent_item(impl_item.hir_id()); - if let hir::OwnerNode::Item(Item { - kind: ItemKind::Impl(hir::Impl { self_ty, .. }), - .. - }) = tcx.hir_owner_node(impl_did) - { - Some((impl_item.ident, self_ty)) - } else { - None - } - } - Node::TraitItem(trait_item) => { - let trait_id = tcx.hir().get_parent_item(trait_item.hir_id()); - debug_assert_eq!(tcx.def_kind(trait_id.def_id), hir::def::DefKind::Trait); - // The method being called is defined in the `trait`, but the `'static` - // obligation comes from the `impl`. Find that `impl` so that we can point - // at it in the suggestion. - let trait_did = trait_id.to_def_id(); - tcx.hir().trait_impls(trait_did).iter().find_map(|&impl_did| { - if let Node::Item(Item { - kind: ItemKind::Impl(hir::Impl { self_ty, .. }), .. - }) = tcx.hir_node_by_def_id(impl_did) - && trait_objects.iter().all(|did| { - // FIXME: we should check `self_ty` against the receiver - // type in the `UnifyReceiver` context, but for now, use - // this imperfect proxy. This will fail if there are - // multiple `impl`s for the same trait like - // `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`. - // In that case, only the first one will get suggestions. - let mut traits = vec![]; - let mut hir_v = HirTraitObjectVisitor(&mut traits, *did); - hir_v.visit_ty(self_ty); - !traits.is_empty() - }) - { - Some((trait_item.ident, *self_ty)) - } else { - None - } - }) - } - _ => None, - } - } - - /// When we call a method coming from an `impl Foo for dyn Bar`, `dyn Bar` introduces a default - /// `'static` obligation. Suggest relaxing that implicit bound. - fn find_impl_on_dyn_trait( - &self, - err: &mut Diag<'_>, - ty: Ty<'_>, - ctxt: &UnifyReceiverContext<'tcx>, - ) -> bool { - let tcx = self.tcx(); - - // Find the method being called. - let Ok(Some(instance)) = ty::Instance::try_resolve( - tcx, - ctxt.param_env, - ctxt.assoc_item.def_id, - self.cx.resolve_vars_if_possible(ctxt.args), - ) else { - return false; - }; - - let mut v = TraitObjectVisitor(FxIndexSet::default()); - v.visit_ty(ty); - - // Get the `Ident` of the method being called and the corresponding `impl` (to point at - // `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called). - let Some((ident, self_ty)) = - NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &v.0) - else { - return false; - }; - - // Find the trait object types in the argument, so we point at *only* the trait object. - self.suggest_constrain_dyn_trait_in_impl(err, &v.0, ident, self_ty) - } - - fn suggest_constrain_dyn_trait_in_impl( - &self, - err: &mut Diag<'_>, - found_dids: &FxIndexSet<DefId>, - ident: Ident, - self_ty: &hir::Ty<'_>, - ) -> bool { - let mut suggested = false; - for found_did in found_dids { - let mut traits = vec![]; - let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did); - hir_v.visit_ty(self_ty); - for &span in &traits { - let subdiag = DynTraitConstraintSuggestion { span, ident }; - subdiag.add_to_diag(err); - suggested = true; - } - } - suggested - } -} - -/// Collect all the trait objects in a type that could have received an implicit `'static` lifetime. -pub struct TraitObjectVisitor(pub FxIndexSet<DefId>); - -impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for TraitObjectVisitor { - fn visit_ty(&mut self, t: Ty<'tcx>) { - match t.kind() { - ty::Dynamic(preds, re, _) if re.is_static() => { - if let Some(def_id) = preds.principal_def_id() { - self.0.insert(def_id); - } - } - _ => t.super_visit_with(self), - } - } -} - -/// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime. -pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec<Span>, pub DefId); - -impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { - if let TyKind::TraitObject( - poly_trait_refs, - Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. }, - _, - ) = t.kind - { - for ptr in poly_trait_refs { - if Some(self.1) == ptr.trait_ref.trait_def_id() { - self.0.push(ptr.span); - } - } - } - walk_ty(self, t); - } -} diff --git a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs deleted file mode 100644 index c58c7e13551..00000000000 --- a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs +++ /dev/null @@ -1,162 +0,0 @@ -//! Error Reporting for `impl` items that do not match the obligations from their `trait`. - -use crate::error_reporting::infer::nice_region_error::NiceRegionError; -use crate::errors::{ConsiderBorrowingParamHelp, RelationshipHelp, TraitImplDiff}; -use crate::infer::RegionResolutionError; -use crate::infer::{Subtype, ValuePairs}; -use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; -use rustc_hir::def::Res; -use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; -use rustc_middle::hir::nested_filter; -use rustc_middle::traits::ObligationCauseCode; -use rustc_middle::ty::error::ExpectedFound; -use rustc_middle::ty::print::RegionHighlightMode; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor}; -use rustc_span::Span; - -impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { - /// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`. - pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorGuaranteed> { - let error = self.error.as_ref()?; - debug!("try_report_impl_not_conforming_to_trait {:?}", error); - if let RegionResolutionError::SubSupConflict( - _, - var_origin, - sub_origin, - _sub, - sup_origin, - _sup, - _, - ) = error.clone() - && let (Subtype(sup_trace), Subtype(sub_trace)) = (&sup_origin, &sub_origin) - && let ObligationCauseCode::CompareImplItem { trait_item_def_id, .. } = - sub_trace.cause.code() - && sub_trace.values == sup_trace.values - && let ValuePairs::PolySigs(ExpectedFound { expected, found }) = sub_trace.values - { - // FIXME(compiler-errors): Don't like that this needs `Ty`s, but - // all of the region highlighting machinery only deals with those. - let guar = self.emit_err( - var_origin.span(), - Ty::new_fn_ptr(self.cx.tcx, expected), - Ty::new_fn_ptr(self.cx.tcx, found), - *trait_item_def_id, - ); - return Some(guar); - } - None - } - - fn emit_err( - &self, - sp: Span, - expected: Ty<'tcx>, - found: Ty<'tcx>, - trait_def_id: DefId, - ) -> ErrorGuaranteed { - let trait_sp = self.tcx().def_span(trait_def_id); - - // Mark all unnamed regions in the type with a number. - // This diagnostic is called in response to lifetime errors, so be informative. - struct HighlightBuilder<'tcx> { - highlight: RegionHighlightMode<'tcx>, - counter: usize, - } - - impl<'tcx> HighlightBuilder<'tcx> { - fn build(ty: Ty<'tcx>) -> RegionHighlightMode<'tcx> { - let mut builder = - HighlightBuilder { highlight: RegionHighlightMode::default(), counter: 1 }; - builder.visit_ty(ty); - builder.highlight - } - } - - impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for HighlightBuilder<'tcx> { - fn visit_region(&mut self, r: ty::Region<'tcx>) { - if !r.has_name() && self.counter <= 3 { - self.highlight.highlighting_region(r, self.counter); - self.counter += 1; - } - } - } - - let expected_highlight = HighlightBuilder::build(expected); - let expected = self - .cx - .extract_inference_diagnostics_data(expected.into(), Some(expected_highlight)) - .name; - let found_highlight = HighlightBuilder::build(found); - let found = - self.cx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name; - - // Get the span of all the used type parameters in the method. - let assoc_item = self.tcx().associated_item(trait_def_id); - let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] }; - match assoc_item.kind { - ty::AssocKind::Fn => { - let hir = self.tcx().hir(); - if let Some(hir_id) = - assoc_item.def_id.as_local().map(|id| self.tcx().local_def_id_to_hir_id(id)) - { - if let Some(decl) = hir.fn_decl_by_hir_id(hir_id) { - visitor.visit_fn_decl(decl); - } - } - } - _ => {} - } - - let diag = TraitImplDiff { - sp, - trait_sp, - note: (), - param_help: ConsiderBorrowingParamHelp { spans: visitor.types.to_vec() }, - rel_help: visitor.types.is_empty().then_some(RelationshipHelp), - expected, - found, - }; - - self.tcx().dcx().emit_err(diag) - } -} - -struct TypeParamSpanVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - types: Vec<Span>, -} - -impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { - type NestedFilter = nested_filter::OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - - fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { - match arg.kind { - hir::TyKind::Ref(_, ref mut_ty) => { - // We don't want to suggest looking into borrowing `&T` or `&Self`. - hir::intravisit::walk_ty(self, mut_ty.ty); - return; - } - hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments { - [segment] - if matches!( - segment.res, - Res::SelfTyParam { .. } - | Res::SelfTyAlias { .. } - | Res::Def(hir::def::DefKind::TyParam, _) - ) => - { - self.types.push(path.span); - } - _ => {} - }, - _ => {} - } - hir::intravisit::walk_ty(self, arg); - } -} diff --git a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/util.rs b/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/util.rs deleted file mode 100644 index 30fa98c5526..00000000000 --- a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/util.rs +++ /dev/null @@ -1,166 +0,0 @@ -//! Helper functions corresponding to lifetime errors due to -//! anonymous regions. - -use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; -use rustc_middle::ty::{self, Binder, Region, Ty, TyCtxt, TypeFoldable}; -use rustc_span::Span; - -use crate::error_reporting::infer::nice_region_error::NiceRegionError; - -/// Information about the anonymous region we are searching for. -#[derive(Debug)] -pub struct AnonymousParamInfo<'tcx> { - /// The parameter corresponding to the anonymous region. - pub param: &'tcx hir::Param<'tcx>, - /// The type corresponding to the anonymous region parameter. - pub param_ty: Ty<'tcx>, - /// The ty::BoundRegionKind corresponding to the anonymous region. - pub bound_region: ty::BoundRegionKind, - /// The `Span` of the parameter type. - pub param_ty_span: Span, - /// Signals that the argument is the first parameter in the declaration. - pub is_first: bool, -} - -// This method walks the Type of the function body parameters using -// `fold_regions()` function and returns the -// &hir::Param of the function parameter corresponding to the anonymous -// region and the Ty corresponding to the named region. -// Currently only the case where the function declaration consists of -// one named region and one anonymous region is handled. -// Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32` -// Here, we would return the hir::Param for y, we return the type &'a -// i32, which is the type of y but with the anonymous region replaced -// with 'a, the corresponding bound region and is_first which is true if -// the hir::Param is the first parameter in the function declaration. -#[instrument(skip(tcx), level = "debug")] -pub fn find_param_with_region<'tcx>( - tcx: TyCtxt<'tcx>, - generic_param_scope: LocalDefId, - anon_region: Region<'tcx>, - replace_region: Region<'tcx>, -) -> Option<AnonymousParamInfo<'tcx>> { - let (id, bound_region) = match *anon_region { - ty::ReLateParam(late_param) => (late_param.scope, late_param.bound_region), - ty::ReEarlyParam(ebr) => { - let region_def = tcx.generics_of(generic_param_scope).region_param(ebr, tcx).def_id; - (tcx.parent(region_def), ty::BoundRegionKind::BrNamed(region_def, ebr.name)) - } - _ => return None, // not a free region - }; - - let hir = &tcx.hir(); - let def_id = id.as_local()?; - - // FIXME: use def_kind - // Don't perform this on closures - match tcx.hir_node_by_def_id(generic_param_scope) { - hir::Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => { - return None; - } - _ => {} - } - - let body = hir.maybe_body_owned_by(def_id)?; - - let owner_id = hir.body_owner(body.id()); - let fn_decl = hir.fn_decl_by_hir_id(owner_id)?; - let poly_fn_sig = tcx.fn_sig(id).instantiate_identity(); - - let fn_sig = tcx.liberate_late_bound_regions(id, poly_fn_sig); - body.params - .iter() - .take(if fn_sig.c_variadic { - fn_sig.inputs().len() - } else { - assert_eq!(fn_sig.inputs().len(), body.params.len()); - body.params.len() - }) - .enumerate() - .find_map(|(index, param)| { - // May return None; sometimes the tables are not yet populated. - let ty = fn_sig.inputs()[index]; - let mut found_anon_region = false; - let new_param_ty = tcx.fold_regions(ty, |r, _| { - if r == anon_region { - found_anon_region = true; - replace_region - } else { - r - } - }); - found_anon_region.then(|| { - let ty_hir_id = fn_decl.inputs[index].hir_id; - let param_ty_span = hir.span(ty_hir_id); - let is_first = index == 0; - AnonymousParamInfo { - param, - param_ty: new_param_ty, - param_ty_span, - bound_region, - is_first, - } - }) - }) -} - -impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { - pub(super) fn find_param_with_region( - &self, - anon_region: Region<'tcx>, - replace_region: Region<'tcx>, - ) -> Option<AnonymousParamInfo<'tcx>> { - find_param_with_region(self.tcx(), self.generic_param_scope, anon_region, replace_region) - } - - // Here, we check for the case where the anonymous region - // is in the return type as written by the user. - // FIXME(#42703) - Need to handle certain cases here. - pub(super) fn is_return_type_anon( - &self, - scope_def_id: LocalDefId, - br: ty::BoundRegionKind, - hir_sig: &hir::FnSig<'_>, - ) -> Option<Span> { - let fn_ty = self.tcx().type_of(scope_def_id).instantiate_identity(); - if let ty::FnDef(_, _) = fn_ty.kind() { - let ret_ty = fn_ty.fn_sig(self.tcx()).output(); - let span = hir_sig.decl.output.span(); - let future_output = if hir_sig.header.is_async() { - ret_ty.map_bound(|ty| self.cx.get_impl_future_output_ty(ty)).transpose() - } else { - None - }; - return match future_output { - Some(output) if self.includes_region(output, br) => Some(span), - None if self.includes_region(ret_ty, br) => Some(span), - _ => None, - }; - } - None - } - - fn includes_region( - &self, - ty: Binder<'tcx, impl TypeFoldable<TyCtxt<'tcx>>>, - region: ty::BoundRegionKind, - ) -> bool { - let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(ty); - // We are only checking is any region meets the condition so order doesn't matter - #[allow(rustc::potential_query_instability)] - late_bound_regions.iter().any(|r| *r == region) - } - - // Here we check for the case where anonymous region - // corresponds to self and if yes, we display E0312. - // FIXME(#42700) - Need to format self properly to - // enable E0621 for it. - pub(super) fn is_self_anon(&self, is_first: bool, scope_def_id: LocalDefId) -> bool { - is_first - && self - .tcx() - .opt_associated_item(scope_def_id.to_def_id()) - .is_some_and(|i| i.fn_has_self_parameter) - } -} diff --git a/compiler/rustc_infer/src/error_reporting/infer/note.rs b/compiler/rustc_infer/src/error_reporting/infer/note.rs deleted file mode 100644 index aeb3049c2ae..00000000000 --- a/compiler/rustc_infer/src/error_reporting/infer/note.rs +++ /dev/null @@ -1,421 +0,0 @@ -use crate::error_reporting::infer::{note_and_explain_region, TypeErrCtxt}; -use crate::errors::{ - note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent, - RefLongerThanData, RegionOriginNote, WhereClauseSuggestions, -}; -use crate::fluent_generated as fluent; -use crate::infer::{self, SubregionOrigin}; -use rustc_errors::{Diag, Subdiagnostic}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::traits::ObligationCauseCode; -use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::{self, IsSuggestable, Region, Ty}; -use rustc_span::symbol::kw; - -use super::ObligationCauseAsDiagArg; - -impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) { - match *origin { - infer::Subtype(ref trace) => RegionOriginNote::WithRequirement { - span: trace.cause.span, - requirement: ObligationCauseAsDiagArg(trace.cause.clone()), - expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)), - } - .add_to_diag(err), - infer::Reborrow(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err) - } - infer::RelateObjectBound(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound } - .add_to_diag(err); - } - infer::ReferenceOutlivesReferent(ty, span) => { - RegionOriginNote::WithName { - span, - msg: fluent::infer_reference_outlives_referent, - name: &self.ty_to_string(ty), - continues: false, - } - .add_to_diag(err); - } - infer::RelateParamBound(span, ty, opt_span) => { - RegionOriginNote::WithName { - span, - msg: fluent::infer_relate_param_bound, - name: &self.ty_to_string(ty), - continues: opt_span.is_some(), - } - .add_to_diag(err); - if let Some(span) = opt_span { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 } - .add_to_diag(err); - } - } - infer::RelateRegionParamBound(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound } - .add_to_diag(err); - } - infer::CompareImplItemObligation { span, .. } => { - RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation } - .add_to_diag(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_diag(err); - } - } - } - - pub(super) fn report_concrete_failure( - &self, - generic_param_scope: LocalDefId, - origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>, - ) -> Diag<'a> { - let mut err = match origin { - infer::Subtype(box trace) => { - let terr = TypeError::RegionsDoesNotOutlive(sup, sub); - let mut err = self.report_and_explain_type_error(trace, terr); - match (*sub, *sup) { - (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {} - (ty::RePlaceholder(_), _) => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "", - sup, - " doesn't meet the lifetime requirements", - None, - ); - } - (_, ty::RePlaceholder(_)) => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "the required lifetime does not necessarily outlive ", - sub, - "", - None, - ); - } - _ => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "", - sup, - "...", - None, - ); - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "...does not necessarily outlive ", - sub, - "", - None, - ); - } - } - err - } - infer::Reborrow(span) => { - let reference_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::RefValidFor, - note_and_explain::SuffixKind::Continues, - ); - let content_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::ContentValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(OutlivesContent { - span, - notes: reference_valid.into_iter().chain(content_valid).collect(), - }) - } - infer::RelateObjectBound(span) => { - let object_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::TypeObjValidFor, - note_and_explain::SuffixKind::Empty, - ); - let pointer_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::SourcePointerValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(OutlivesBound { - span, - notes: object_valid.into_iter().chain(pointer_valid).collect(), - }) - } - infer::RelateParamBound(span, ty, opt_span) => { - let prefix = match *sub { - ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy, - _ => note_and_explain::PrefixKind::TypeOutlive, - }; - let suffix = if opt_span.is_some() { - note_and_explain::SuffixKind::ReqByBinding - } else { - note_and_explain::SuffixKind::Empty - }; - let note = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - opt_span, - prefix, - suffix, - ); - self.dcx().create_err(FulfillReqLifetime { - span, - ty: self.resolve_vars_if_possible(ty), - note, - }) - } - infer::RelateRegionParamBound(span) => { - let param_instantiated = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::LfParamInstantiatedWith, - note_and_explain::SuffixKind::Empty, - ); - let param_must_outlive = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::LfParamMustOutlive, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(LfBoundNotSatisfied { - span, - notes: param_instantiated.into_iter().chain(param_must_outlive).collect(), - }) - } - infer::ReferenceOutlivesReferent(ty, span) => { - let pointer_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::PointerValidFor, - note_and_explain::SuffixKind::Empty, - ); - let data_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::DataValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(RefLongerThanData { - span, - ty: self.resolve_vars_if_possible(ty), - notes: pointer_valid.into_iter().chain(data_valid).collect(), - }) - } - infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => { - let mut err = self.infcx.report_extra_impl_obligation( - span, - impl_item_def_id, - trait_item_def_id, - &format!("`{sup}: {sub}`"), - ); - // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause - if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) - && generics.where_clause_span.contains(span) - { - self.suggest_copy_trait_method_bounds( - trait_item_def_id, - impl_item_def_id, - &mut err, - ); - } - err - } - infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => { - let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup); - - // Don't mention the item name if it's an RPITIT, since that'll just confuse - // folks. - if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) { - let trait_item_span = self.tcx.def_span(trait_item_def_id); - let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); - err.span_label( - trait_item_span, - format!("definition of `{item_name}` from trait"), - ); - } - - self.suggest_copy_trait_method_bounds( - trait_item_def_id, - impl_item_def_id, - &mut err, - ); - err - } - infer::AscribeUserTypeProvePredicate(span) => { - let instantiated = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::LfInstantiatedWith, - note_and_explain::SuffixKind::Empty, - ); - let must_outlive = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::LfMustOutlive, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(LfBoundNotSatisfied { - span, - notes: instantiated.into_iter().chain(must_outlive).collect(), - }) - } - }; - if sub.is_error() || sup.is_error() { - err.downgrade_to_delayed_bug(); - } - err - } - - pub fn suggest_copy_trait_method_bounds( - &self, - trait_item_def_id: DefId, - impl_item_def_id: LocalDefId, - err: &mut Diag<'_>, - ) { - // FIXME(compiler-errors): Right now this is only being used for region - // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches, - // but right now it's not really very smart when it comes to implicit `Sized` - // predicates and bounds on the trait itself. - - let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx) - else { - return; - }; - let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else { - return; - }; - let trait_args = trait_ref - .instantiate_identity() - // Replace the explicit self type with `Self` for better suggestion rendering - .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper)) - .args; - let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id) - .rebase_onto(self.tcx, impl_def_id, trait_args); - - let Ok(trait_predicates) = - self.tcx - .explicit_predicates_of(trait_item_def_id) - .instantiate_own(self.tcx, trait_item_args) - .map(|(pred, _)| { - if pred.is_suggestable(self.tcx, false) { - Ok(pred.to_string()) - } else { - Err(()) - } - }) - .collect::<Result<Vec<_>, ()>>() - else { - return; - }; - - let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else { - return; - }; - - let suggestion = if trait_predicates.is_empty() { - WhereClauseSuggestions::Remove { span: generics.where_clause_span } - } else { - let space = if generics.where_clause_span.is_empty() { " " } else { "" }; - WhereClauseSuggestions::CopyPredicates { - span: generics.where_clause_span, - space, - trait_predicates: trait_predicates.join(", "), - } - }; - err.subdiagnostic(suggestion); - } - - pub(super) fn report_placeholder_failure( - &self, - generic_param_scope: LocalDefId, - placeholder_origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>, - ) -> Diag<'a> { - // I can't think how to do better than this right now. -nikomatsakis - debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure"); - match placeholder_origin { - infer::Subtype(box ref trace) - if matches!( - &trace.cause.code().peel_derives(), - ObligationCauseCode::WhereClause(..) - | ObligationCauseCode::WhereClauseInExpr(..) - ) => - { - // Hack to get around the borrow checker because trace.cause has an `Rc`. - if let ObligationCauseCode::WhereClause(_, span) - | ObligationCauseCode::WhereClauseInExpr(_, span, ..) = - &trace.cause.code().peel_derives() - && !span.is_dummy() - { - let span = *span; - self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup) - .with_span_note(span, "the lifetime requirement is introduced here") - } else { - unreachable!( - "control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..." - ) - } - } - infer::Subtype(box trace) => { - let terr = TypeError::RegionsPlaceholderMismatch; - return self.report_and_explain_type_error(trace, terr); - } - _ => { - return self.report_concrete_failure( - generic_param_scope, - placeholder_origin, - sub, - sup, - ); - } - } - } -} diff --git a/compiler/rustc_infer/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_infer/src/error_reporting/infer/note_and_explain.rs deleted file mode 100644 index d5e7de897d0..00000000000 --- a/compiler/rustc_infer/src/error_reporting/infer/note_and_explain.rs +++ /dev/null @@ -1,940 +0,0 @@ -use super::TypeErrCtxt; -use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; -use rustc_errors::{pluralize, Diag, MultiSpan}; -use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_middle::traits::ObligationCauseCode; -use rustc_middle::ty::error::ExpectedFound; -use rustc_middle::ty::print::Printer; -use rustc_middle::{ - traits::ObligationCause, - ty::{self, error::TypeError, print::FmtPrinter, suggest_constraining_type_param, Ty}, -}; -use rustc_span::{def_id::DefId, sym, BytePos, Span, Symbol}; - -impl<'tcx> TypeErrCtxt<'_, 'tcx> { - pub fn note_and_explain_type_err( - &self, - diag: &mut Diag<'_>, - err: TypeError<'tcx>, - cause: &ObligationCause<'tcx>, - sp: Span, - body_owner_def_id: DefId, - ) { - debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause); - - let tcx = self.tcx; - - match err { - TypeError::ArgumentSorts(values, _) | TypeError::Sorts(values) => { - match (*values.expected.kind(), *values.found.kind()) { - (ty::Closure(..), ty::Closure(..)) => { - diag.note("no two closures, even if identical, have the same type"); - diag.help("consider boxing your closure and/or using it as a trait object"); - } - (ty::Coroutine(def_id1, ..), ty::Coroutine(def_id2, ..)) - if self.tcx.coroutine_is_async(def_id1) - && self.tcx.coroutine_is_async(def_id2) => - { - diag.note("no two async blocks, even if identical, have the same type"); - diag.help( - "consider pinning your async block and casting it to a trait object", - ); - } - (ty::Alias(ty::Opaque, ..), ty::Alias(ty::Opaque, ..)) => { - // Issue #63167 - diag.note("distinct uses of `impl Trait` result in different opaque types"); - } - (ty::Float(_), ty::Infer(ty::IntVar(_))) - if let Ok( - // Issue #53280 - snippet, - ) = tcx.sess.source_map().span_to_snippet(sp) => - { - if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') { - diag.span_suggestion_verbose( - sp.shrink_to_hi(), - "use a float literal", - ".0", - MachineApplicable, - ); - } - } - (ty::Param(expected), ty::Param(found)) => { - let generics = tcx.generics_of(body_owner_def_id); - let e_span = tcx.def_span(generics.type_param(expected, tcx).def_id); - if !sp.contains(e_span) { - diag.span_label(e_span, "expected type parameter"); - } - let f_span = tcx.def_span(generics.type_param(found, tcx).def_id); - if !sp.contains(f_span) { - diag.span_label(f_span, "found type parameter"); - } - diag.note( - "a type parameter was expected, but a different one was found; \ - you might be missing a type parameter or trait bound", - ); - diag.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch10-02-traits.html\ - #traits-as-parameters", - ); - } - ( - ty::Alias(ty::Projection | ty::Inherent, _), - ty::Alias(ty::Projection | ty::Inherent, _), - ) => { - diag.note("an associated type was expected, but a different one was found"); - } - // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too. - (ty::Param(p), ty::Alias(ty::Projection, proj)) - | (ty::Alias(ty::Projection, proj), ty::Param(p)) - if !tcx.is_impl_trait_in_trait(proj.def_id) => - { - let param = tcx.generics_of(body_owner_def_id).type_param(p, tcx); - let p_def_id = param.def_id; - let p_span = tcx.def_span(p_def_id); - let expected = match (values.expected.kind(), values.found.kind()) { - (ty::Param(_), _) => "expected ", - (_, ty::Param(_)) => "found ", - _ => "", - }; - if !sp.contains(p_span) { - diag.span_label(p_span, format!("{expected}this type parameter")); - } - let parent = p_def_id.as_local().and_then(|id| { - let local_id = tcx.local_def_id_to_hir_id(id); - let generics = tcx.parent_hir_node(local_id).generics()?; - Some((id, generics)) - }); - let mut note = true; - if let Some((local_id, generics)) = parent { - // Synthesize the associated type restriction `Add<Output = Expected>`. - // FIXME: extract this logic for use in other diagnostics. - let (trait_ref, assoc_args) = proj.trait_ref_and_own_args(tcx); - let item_name = tcx.item_name(proj.def_id); - let item_args = self.format_generic_args(assoc_args); - - // Here, we try to see if there's an existing - // trait implementation that matches the one that - // we're suggesting to restrict. If so, find the - // "end", whether it be at the end of the trait - // or the end of the generic arguments. - let mut matching_span = None; - let mut matched_end_of_args = false; - for bound in generics.bounds_for_param(local_id) { - let potential_spans = bound.bounds.iter().find_map(|bound| { - let bound_trait_path = bound.trait_ref()?.path; - let def_id = bound_trait_path.res.opt_def_id()?; - let generic_args = bound_trait_path - .segments - .iter() - .last() - .map(|path| path.args()); - (def_id == trait_ref.def_id) - .then_some((bound_trait_path.span, generic_args)) - }); - - if let Some((end_of_trait, end_of_args)) = potential_spans { - let args_span = end_of_args.and_then(|args| args.span()); - matched_end_of_args = args_span.is_some(); - matching_span = args_span - .or_else(|| Some(end_of_trait)) - .map(|span| span.shrink_to_hi()); - break; - } - } - - if matched_end_of_args { - // Append suggestion to the end of our args - let path = format!(", {item_name}{item_args} = {p}"); - note = !suggest_constraining_type_param( - tcx, - generics, - diag, - &proj.self_ty().to_string(), - &path, - None, - matching_span, - ); - } else { - // Suggest adding a bound to an existing trait - // or if the trait doesn't exist, add the trait - // and the suggested bounds. - let path = format!("<{item_name}{item_args} = {p}>"); - note = !suggest_constraining_type_param( - tcx, - generics, - diag, - &proj.self_ty().to_string(), - &path, - None, - matching_span, - ); - } - } - if note { - diag.note("you might be missing a type parameter or trait bound"); - } - } - (ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..)) - | (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => { - let generics = tcx.generics_of(body_owner_def_id); - let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); - let expected = match (values.expected.kind(), values.found.kind()) { - (ty::Param(_), _) => "expected ", - (_, ty::Param(_)) => "found ", - _ => "", - }; - if !sp.contains(p_span) { - diag.span_label(p_span, format!("{expected}this type parameter")); - } - diag.help("type parameters must be constrained to match other types"); - if diag.code.is_some_and(|code| tcx.sess.teach(code)) { - diag.help( - "given a type parameter `T` and a method `foo`: -``` -trait Trait<T> { fn foo(&self) -> T; } -``` -the only ways to implement method `foo` are: -- constrain `T` with an explicit type: -``` -impl Trait<String> for X { - fn foo(&self) -> String { String::new() } -} -``` -- add a trait bound to `T` and call a method on that trait that returns `Self`: -``` -impl<T: std::default::Default> Trait<T> for X { - fn foo(&self) -> T { <T as std::default::Default>::default() } -} -``` -- change `foo` to return an argument of type `T`: -``` -impl<T> Trait<T> for X { - fn foo(&self, x: T) -> T { x } -} -```", - ); - } - diag.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch10-02-traits.html\ - #traits-as-parameters", - ); - } - ( - ty::Param(p), - ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..), - ) => { - let generics = tcx.generics_of(body_owner_def_id); - let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); - if !sp.contains(p_span) { - diag.span_label(p_span, "expected this type parameter"); - } - diag.help(format!( - "every closure has a distinct type and so could not always match the \ - caller-chosen type of parameter `{p}`" - )); - } - (ty::Param(p), _) | (_, ty::Param(p)) => { - let generics = tcx.generics_of(body_owner_def_id); - let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); - let expected = match (values.expected.kind(), values.found.kind()) { - (ty::Param(_), _) => "expected ", - (_, ty::Param(_)) => "found ", - _ => "", - }; - if !sp.contains(p_span) { - diag.span_label(p_span, format!("{expected}this type parameter")); - } - } - (ty::Alias(ty::Projection | ty::Inherent, proj_ty), _) - if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => - { - self.expected_projection( - diag, - proj_ty, - values, - body_owner_def_id, - cause.code(), - ); - } - (_, ty::Alias(ty::Projection | ty::Inherent, proj_ty)) - if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => - { - let msg = || { - format!( - "consider constraining the associated type `{}` to `{}`", - values.found, values.expected, - ) - }; - if !(self.suggest_constraining_opaque_associated_type( - diag, - msg, - proj_ty, - values.expected, - ) || self.suggest_constraint( - diag, - &msg, - body_owner_def_id, - proj_ty, - values.expected, - )) { - diag.help(msg()); - diag.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", - ); - } - } - (ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias)) - if let Some(def_id) = t.principal_def_id() - && tcx - .explicit_item_super_predicates(alias.def_id) - .skip_binder() - .iter() - .any(|(pred, _span)| match pred.kind().skip_binder() { - ty::ClauseKind::Trait(trait_predicate) - if trait_predicate.polarity - == ty::PredicatePolarity::Positive => - { - trait_predicate.def_id() == def_id - } - _ => false, - }) => - { - diag.help(format!( - "you can box the `{}` to coerce it to `Box<{}>`, but you'll have to \ - change the expected type as well", - values.found, values.expected, - )); - } - (ty::Dynamic(t, _, ty::DynKind::Dyn), _) - if let Some(def_id) = t.principal_def_id() => - { - let mut impl_def_ids = vec![]; - tcx.for_each_relevant_impl(def_id, values.found, |did| { - impl_def_ids.push(did) - }); - if let [_] = &impl_def_ids[..] { - let trait_name = tcx.item_name(def_id); - diag.help(format!( - "`{}` implements `{trait_name}` so you could box the found value \ - and coerce it to the trait object `Box<dyn {trait_name}>`, you \ - will have to change the expected type as well", - values.found, - )); - } - } - (_, ty::Dynamic(t, _, ty::DynKind::Dyn)) - if let Some(def_id) = t.principal_def_id() => - { - let mut impl_def_ids = vec![]; - tcx.for_each_relevant_impl(def_id, values.expected, |did| { - impl_def_ids.push(did) - }); - if let [_] = &impl_def_ids[..] { - let trait_name = tcx.item_name(def_id); - diag.help(format!( - "`{}` implements `{trait_name}` so you could change the expected \ - type to `Box<dyn {trait_name}>`", - values.expected, - )); - } - } - (ty::Dynamic(t, _, ty::DynKind::DynStar), _) - if let Some(def_id) = t.principal_def_id() => - { - let mut impl_def_ids = vec![]; - tcx.for_each_relevant_impl(def_id, values.found, |did| { - impl_def_ids.push(did) - }); - if let [_] = &impl_def_ids[..] { - let trait_name = tcx.item_name(def_id); - diag.help(format!( - "`{}` implements `{trait_name}`, `#[feature(dyn_star)]` is likely \ - not enabled; that feature it is currently incomplete", - values.found, - )); - } - } - (_, ty::Alias(ty::Opaque, opaque_ty)) - | (ty::Alias(ty::Opaque, opaque_ty), _) => { - if opaque_ty.def_id.is_local() - && matches!( - tcx.def_kind(body_owner_def_id), - DefKind::Fn - | DefKind::Static { .. } - | DefKind::Const - | DefKind::AssocFn - | DefKind::AssocConst - ) - && tcx.is_type_alias_impl_trait(opaque_ty.def_id) - && !tcx - .opaque_types_defined_by(body_owner_def_id.expect_local()) - .contains(&opaque_ty.def_id.expect_local()) - { - let sp = tcx - .def_ident_span(body_owner_def_id) - .unwrap_or_else(|| tcx.def_span(body_owner_def_id)); - diag.span_note( - sp, - "this item must have the opaque type in its signature in order to \ - be able to register hidden types", - ); - } - // If two if arms can be coerced to a trait object, provide a structured - // suggestion. - let ObligationCauseCode::IfExpression(cause) = cause.code() else { - return; - }; - let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id) else { - return; - }; - let Some(then) = blk.expr else { - return; - }; - let hir::Node::Block(blk) = self.tcx.hir_node(cause.else_id) else { - return; - }; - let Some(else_) = blk.expr else { - return; - }; - let expected = match values.found.kind() { - ty::Alias(..) => values.expected, - _ => values.found, - }; - let preds = tcx.explicit_item_super_predicates(opaque_ty.def_id); - for (pred, _span) in preds.skip_binder() { - let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder() - else { - continue; - }; - if trait_predicate.polarity != ty::PredicatePolarity::Positive { - continue; - } - let def_id = trait_predicate.def_id(); - let mut impl_def_ids = vec![]; - tcx.for_each_relevant_impl(def_id, expected, |did| { - impl_def_ids.push(did) - }); - if let [_] = &impl_def_ids[..] { - let trait_name = tcx.item_name(def_id); - diag.multipart_suggestion( - format!( - "`{expected}` implements `{trait_name}` so you can box \ - both arms and coerce to the trait object \ - `Box<dyn {trait_name}>`", - ), - vec![ - (then.span.shrink_to_lo(), "Box::new(".to_string()), - ( - then.span.shrink_to_hi(), - format!(") as Box<dyn {}>", tcx.def_path_str(def_id)), - ), - (else_.span.shrink_to_lo(), "Box::new(".to_string()), - (else_.span.shrink_to_hi(), ")".to_string()), - ], - MachineApplicable, - ); - } - } - } - (ty::FnPtr(sig), ty::FnDef(def_id, _)) - | (ty::FnDef(def_id, _), ty::FnPtr(sig)) => { - if tcx.fn_sig(def_id).skip_binder().safety() < sig.safety() { - diag.note( - "unsafe functions cannot be coerced into safe function pointers", - ); - } - } - (ty::Adt(_, _), ty::Adt(def, args)) - if let ObligationCauseCode::IfExpression(cause) = cause.code() - && let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id) - && let Some(then) = blk.expr - && def.is_box() - && let boxed_ty = args.type_at(0) - && let ty::Dynamic(t, _, _) = boxed_ty.kind() - && let Some(def_id) = t.principal_def_id() - && let mut impl_def_ids = vec![] - && let _ = - tcx.for_each_relevant_impl(def_id, values.expected, |did| { - impl_def_ids.push(did) - }) - && let [_] = &impl_def_ids[..] => - { - // We have divergent if/else arms where the expected value is a type that - // implements the trait of the found boxed trait object. - diag.multipart_suggestion( - format!( - "`{}` implements `{}` so you can box it to coerce to the trait \ - object `{}`", - values.expected, - tcx.item_name(def_id), - values.found, - ), - vec![ - (then.span.shrink_to_lo(), "Box::new(".to_string()), - (then.span.shrink_to_hi(), ")".to_string()), - ], - MachineApplicable, - ); - } - _ => {} - } - debug!( - "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})", - values.expected, - values.expected.kind(), - values.found, - values.found.kind(), - ); - } - TypeError::CyclicTy(ty) => { - // Watch out for various cases of cyclic types and try to explain. - if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() { - diag.note( - "closures cannot capture themselves or take themselves as argument;\n\ - this error may be the result of a recent compiler bug-fix,\n\ - see issue #46062 <https://github.com/rust-lang/rust/issues/46062>\n\ - for more information", - ); - } - } - TypeError::TargetFeatureCast(def_id) => { - let target_spans = tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span); - diag.note( - "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" - ); - diag.span_labels(target_spans, "`#[target_feature]` added here"); - } - _ => {} - } - } - - fn suggest_constraint( - &self, - diag: &mut Diag<'_>, - msg: impl Fn() -> String, - body_owner_def_id: DefId, - proj_ty: ty::AliasTy<'tcx>, - ty: Ty<'tcx>, - ) -> bool { - let tcx = self.tcx; - let assoc = tcx.associated_item(proj_ty.def_id); - let (trait_ref, assoc_args) = proj_ty.trait_ref_and_own_args(tcx); - let Some(item) = tcx.hir().get_if_local(body_owner_def_id) else { - return false; - }; - let Some(hir_generics) = item.generics() else { - return false; - }; - // Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`. - // This will also work for `impl Trait`. - let ty::Param(param_ty) = *proj_ty.self_ty().kind() else { - return false; - }; - let generics = tcx.generics_of(body_owner_def_id); - let def_id = generics.type_param(param_ty, tcx).def_id; - let Some(def_id) = def_id.as_local() else { - return false; - }; - - // First look in the `where` clause, as this might be - // `fn foo<T>(x: T) where T: Trait`. - for pred in hir_generics.bounds_for_param(def_id) { - if self.constrain_generic_bound_associated_type_structured_suggestion( - diag, - trait_ref, - pred.bounds, - assoc, - assoc_args, - ty, - &msg, - false, - ) { - return true; - } - } - if (param_ty.index as usize) >= generics.parent_count { - // The param comes from the current item, do not look at the parent. (#117209) - return false; - } - // If associated item, look to constrain the params of the trait/impl. - let hir_id = match item { - hir::Node::ImplItem(item) => item.hir_id(), - hir::Node::TraitItem(item) => item.hir_id(), - _ => return false, - }; - let parent = tcx.hir().get_parent_item(hir_id).def_id; - self.suggest_constraint(diag, msg, parent.into(), proj_ty, ty) - } - - /// An associated type was expected and a different type was found. - /// - /// We perform a few different checks to see what we can suggest: - /// - /// - In the current item, look for associated functions that return the expected type and - /// suggest calling them. (Not a structured suggestion.) - /// - If any of the item's generic bounds can be constrained, we suggest constraining the - /// associated type to the found type. - /// - If the associated type has a default type and was expected inside of a `trait`, we - /// mention that this is disallowed. - /// - If all other things fail, and the error is not because of a mismatch between the `trait` - /// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc - /// fn that returns the type. - fn expected_projection( - &self, - diag: &mut Diag<'_>, - proj_ty: ty::AliasTy<'tcx>, - values: ExpectedFound<Ty<'tcx>>, - body_owner_def_id: DefId, - cause_code: &ObligationCauseCode<'_>, - ) { - let tcx = self.tcx; - - // Don't suggest constraining a projection to something containing itself - if self.tcx.erase_regions(values.found).contains(self.tcx.erase_regions(values.expected)) { - return; - } - - let msg = || { - format!( - "consider constraining the associated type `{}` to `{}`", - values.expected, values.found - ) - }; - - let body_owner = tcx.hir().get_if_local(body_owner_def_id); - let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); - - // We don't want to suggest calling an assoc fn in a scope where that isn't feasible. - let callable_scope = matches!( - body_owner, - Some( - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) - | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) - | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }), - ) - ); - let impl_comparison = matches!(cause_code, ObligationCauseCode::CompareImplItem { .. }); - let assoc = tcx.associated_item(proj_ty.def_id); - if impl_comparison { - // We do not want to suggest calling functions when the reason of the - // type error is a comparison of an `impl` with its `trait`. - } else { - let point_at_assoc_fn = if callable_scope - && self.point_at_methods_that_satisfy_associated_type( - diag, - assoc.container_id(tcx), - current_method_ident, - proj_ty.def_id, - values.expected, - ) { - // If we find a suitable associated function that returns the expected type, we - // don't want the more general suggestion later in this method about "consider - // constraining the associated type or calling a method that returns the associated - // type". - true - } else { - false - }; - // Possibly suggest constraining the associated type to conform to the - // found type. - if self.suggest_constraint(diag, &msg, body_owner_def_id, proj_ty, values.found) - || point_at_assoc_fn - { - return; - } - } - - self.suggest_constraining_opaque_associated_type(diag, &msg, proj_ty, values.found); - - if self.point_at_associated_type(diag, body_owner_def_id, values.found) { - return; - } - - if !impl_comparison { - // Generic suggestion when we can't be more specific. - if callable_scope { - diag.help(format!( - "{} or calling a method that returns `{}`", - msg(), - values.expected - )); - } else { - diag.help(msg()); - } - diag.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", - ); - } - if diag.code.is_some_and(|code| tcx.sess.teach(code)) { - diag.help( - "given an associated type `T` and a method `foo`: -``` -trait Trait { -type T; -fn foo(&self) -> Self::T; -} -``` -the only way of implementing method `foo` is to constrain `T` with an explicit associated type: -``` -impl Trait for X { -type T = String; -fn foo(&self) -> Self::T { String::new() } -} -```", - ); - } - } - - /// When the expected `impl Trait` is not defined in the current item, it will come from - /// a return type. This can occur when dealing with `TryStream` (#71035). - fn suggest_constraining_opaque_associated_type( - &self, - diag: &mut Diag<'_>, - msg: impl Fn() -> String, - proj_ty: ty::AliasTy<'tcx>, - ty: Ty<'tcx>, - ) -> bool { - let tcx = self.tcx; - - let assoc = tcx.associated_item(proj_ty.def_id); - if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *proj_ty.self_ty().kind() { - let opaque_local_def_id = def_id.as_local(); - let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id { - tcx.hir().expect_item(opaque_local_def_id).expect_opaque_ty() - } else { - return false; - }; - - let (trait_ref, assoc_args) = proj_ty.trait_ref_and_own_args(tcx); - - self.constrain_generic_bound_associated_type_structured_suggestion( - diag, - trait_ref, - opaque_hir_ty.bounds, - assoc, - assoc_args, - ty, - msg, - true, - ) - } else { - false - } - } - - fn point_at_methods_that_satisfy_associated_type( - &self, - diag: &mut Diag<'_>, - assoc_container_id: DefId, - current_method_ident: Option<Symbol>, - proj_ty_item_def_id: DefId, - expected: Ty<'tcx>, - ) -> bool { - let tcx = self.tcx; - - let items = tcx.associated_items(assoc_container_id); - // Find all the methods in the trait that could be called to construct the - // expected associated type. - // FIXME: consider suggesting the use of associated `const`s. - let methods: Vec<(Span, String)> = items - .in_definition_order() - .filter(|item| { - ty::AssocKind::Fn == item.kind - && Some(item.name) != current_method_ident - && !tcx.is_doc_hidden(item.def_id) - }) - .filter_map(|item| { - let method = tcx.fn_sig(item.def_id).instantiate_identity(); - match *method.output().skip_binder().kind() { - ty::Alias(ty::Projection, ty::AliasTy { def_id: item_def_id, .. }) - if item_def_id == proj_ty_item_def_id => - { - Some(( - tcx.def_span(item.def_id), - format!("consider calling `{}`", tcx.def_path_str(item.def_id)), - )) - } - _ => None, - } - }) - .collect(); - if !methods.is_empty() { - // Use a single `help:` to show all the methods in the trait that can - // be used to construct the expected associated type. - let mut span: MultiSpan = - methods.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into(); - let msg = format!( - "{some} method{s} {are} available that return{r} `{ty}`", - some = if methods.len() == 1 { "a" } else { "some" }, - s = pluralize!(methods.len()), - are = pluralize!("is", methods.len()), - r = if methods.len() == 1 { "s" } else { "" }, - ty = expected - ); - for (sp, label) in methods.into_iter() { - span.push_span_label(sp, label); - } - diag.span_help(span, msg); - return true; - } - false - } - - fn point_at_associated_type( - &self, - diag: &mut Diag<'_>, - body_owner_def_id: DefId, - found: Ty<'tcx>, - ) -> bool { - let tcx = self.tcx; - - let Some(def_id) = body_owner_def_id.as_local() else { - return false; - }; - - // When `body_owner` is an `impl` or `trait` item, look in its associated types for - // `expected` and point at it. - let hir_id = tcx.local_def_id_to_hir_id(def_id); - let parent_id = tcx.hir().get_parent_item(hir_id); - let item = tcx.hir_node_by_def_id(parent_id.def_id); - - debug!("expected_projection parent item {:?}", item); - - let param_env = tcx.param_env(body_owner_def_id); - - match item { - hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. }) => { - // FIXME: account for `#![feature(specialization)]` - for item in &items[..] { - match item.kind { - hir::AssocItemKind::Type => { - // FIXME: account for returning some type in a trait fn impl that has - // an assoc type as a return type (#72076). - if let hir::Defaultness::Default { has_value: true } = - tcx.defaultness(item.id.owner_id) - { - let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity(); - if self.infcx.can_eq_shallow(param_env, assoc_ty, found) { - diag.span_label( - item.span, - "associated type defaults can't be assumed inside the \ - trait defining them", - ); - return true; - } - } - } - _ => {} - } - } - } - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { items, .. }), - .. - }) => { - for item in &items[..] { - if let hir::AssocItemKind::Type = item.kind { - let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity(); - if let hir::Defaultness::Default { has_value: true } = - tcx.defaultness(item.id.owner_id) - && self.infcx.can_eq_shallow(param_env, assoc_ty, found) - { - diag.span_label( - item.span, - "associated type is `default` and may be overridden", - ); - return true; - } - } - } - } - _ => {} - } - false - } - - /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref` - /// requirement, provide a structured suggestion to constrain it to a given type `ty`. - /// - /// `is_bound_surely_present` indicates whether we know the bound we're looking for is - /// inside `bounds`. If that's the case then we can consider `bounds` containing only one - /// trait bound as the one we're looking for. This can help in cases where the associated - /// type is defined on a supertrait of the one present in the bounds. - fn constrain_generic_bound_associated_type_structured_suggestion( - &self, - diag: &mut Diag<'_>, - trait_ref: ty::TraitRef<'tcx>, - bounds: hir::GenericBounds<'_>, - assoc: ty::AssocItem, - assoc_args: &[ty::GenericArg<'tcx>], - ty: Ty<'tcx>, - msg: impl Fn() -> String, - is_bound_surely_present: bool, - ) -> bool { - // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting. - - let trait_bounds = bounds.iter().filter_map(|bound| match bound { - hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => Some(ptr), - _ => None, - }); - - let matching_trait_bounds = trait_bounds - .clone() - .filter(|ptr| ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id)) - .collect::<Vec<_>>(); - - let span = match &matching_trait_bounds[..] { - &[ptr] => ptr.span, - &[] if is_bound_surely_present => match &trait_bounds.collect::<Vec<_>>()[..] { - &[ptr] => ptr.span, - _ => return false, - }, - _ => return false, - }; - - self.constrain_associated_type_structured_suggestion(diag, span, assoc, assoc_args, ty, msg) - } - - /// Given a span corresponding to a bound, provide a structured suggestion to set an - /// associated type to a given type `ty`. - fn constrain_associated_type_structured_suggestion( - &self, - diag: &mut Diag<'_>, - span: Span, - assoc: ty::AssocItem, - assoc_args: &[ty::GenericArg<'tcx>], - ty: Ty<'tcx>, - msg: impl Fn() -> String, - ) -> bool { - let tcx = self.tcx; - - if let Ok(has_params) = - tcx.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>')) - { - let (span, sugg) = if has_params { - let pos = span.hi() - BytePos(1); - let span = Span::new(pos, pos, span.ctxt(), span.parent()); - (span, format!(", {} = {}", assoc.ident(tcx), ty)) - } else { - let item_args = self.format_generic_args(assoc_args); - (span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident(tcx), item_args, ty)) - }; - diag.span_suggestion_verbose(span, msg(), sugg, MaybeIncorrect); - return true; - } - false - } - - pub fn format_generic_args(&self, args: &[ty::GenericArg<'tcx>]) -> String { - FmtPrinter::print_string(self.tcx, hir::def::Namespace::TypeNS, |cx| { - cx.path_generic_args(|_| Ok(()), args) - }) - .expect("could not write to `String`.") - } -} diff --git a/compiler/rustc_infer/src/error_reporting/infer/region.rs b/compiler/rustc_infer/src/error_reporting/infer/region.rs deleted file mode 100644 index 5d41bb5d271..00000000000 --- a/compiler/rustc_infer/src/error_reporting/infer/region.rs +++ /dev/null @@ -1,1428 +0,0 @@ -use std::iter; - -use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{ - struct_span_code_err, Applicability, Diag, Subdiagnostic, E0309, E0310, E0311, E0495, -}; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::Visitor; -use rustc_hir::{self as hir, ParamName}; -use rustc_middle::bug; -use rustc_middle::traits::ObligationCauseCode; -use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::{self, IsSuggestable, Region, Ty, TyCtxt, TypeVisitableExt as _}; -use rustc_span::symbol::kw; -use rustc_span::{BytePos, ErrorGuaranteed, Span, Symbol}; -use rustc_type_ir::Upcast as _; - -use super::nice_region_error::find_anon_type; -use super::{nice_region_error, ObligationCauseAsDiagArg}; -use crate::error_reporting::infer::{ObligationCauseExt as _, TypeErrCtxt}; -use crate::errors::{ - self, note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, - OutlivesContent, RefLongerThanData, RegionOriginNote, WhereClauseSuggestions, -}; -use crate::fluent_generated as fluent; -use crate::infer::region_constraints::GenericKind; -use crate::infer::{self, InferCtxt, RegionResolutionError, RegionVariableOrigin, SubregionOrigin}; - -impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - pub fn report_region_errors( - &self, - generic_param_scope: LocalDefId, - errors: &[RegionResolutionError<'tcx>], - ) -> ErrorGuaranteed { - assert!(!errors.is_empty()); - - if let Some(guaranteed) = self.infcx.tainted_by_errors() { - return guaranteed; - } - - debug!("report_region_errors(): {} errors to start", errors.len()); - - // try to pre-process the errors, which will group some of them - // together into a `ProcessedErrors` group: - let errors = self.process_errors(errors); - - debug!("report_region_errors: {} errors after preprocessing", errors.len()); - - let mut guar = None; - for error in errors { - debug!("report_region_errors: error = {:?}", error); - - let e = if let Some(guar) = - self.try_report_nice_region_error(generic_param_scope, &error) - { - guar - } else { - match error.clone() { - // These errors could indicate all manner of different - // problems with many different solutions. Rather - // than generate a "one size fits all" error, what we - // attempt to do is go through a number of specific - // scenarios and try to find the best way to present - // the error. If all of these fails, we fall back to a rather - // general bit of code that displays the error information - RegionResolutionError::ConcreteFailure(origin, sub, sup) => { - if sub.is_placeholder() || sup.is_placeholder() { - self.report_placeholder_failure(generic_param_scope, origin, sub, sup) - .emit() - } else { - self.report_concrete_failure(generic_param_scope, origin, sub, sup) - .emit() - } - } - - RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => self - .report_generic_bound_failure( - generic_param_scope, - origin.span(), - Some(origin), - param_ty, - sub, - ), - - RegionResolutionError::SubSupConflict( - _, - var_origin, - sub_origin, - sub_r, - sup_origin, - sup_r, - _, - ) => { - if sub_r.is_placeholder() { - self.report_placeholder_failure( - generic_param_scope, - sub_origin, - sub_r, - sup_r, - ) - .emit() - } else if sup_r.is_placeholder() { - self.report_placeholder_failure( - generic_param_scope, - sup_origin, - sub_r, - sup_r, - ) - .emit() - } else { - self.report_sub_sup_conflict( - generic_param_scope, - var_origin, - sub_origin, - sub_r, - sup_origin, - sup_r, - ) - } - } - - RegionResolutionError::UpperBoundUniverseConflict( - _, - _, - _, - sup_origin, - sup_r, - ) => { - assert!(sup_r.is_placeholder()); - - // Make a dummy value for the "sub region" -- - // this is the initial value of the - // placeholder. In practice, we expect more - // tailored errors that don't really use this - // value. - let sub_r = self.tcx.lifetimes.re_erased; - - self.report_placeholder_failure( - generic_param_scope, - sup_origin, - sub_r, - sup_r, - ) - .emit() - } - - RegionResolutionError::CannotNormalize(clause, origin) => { - let clause: ty::Clause<'tcx> = - clause.map_bound(ty::ClauseKind::TypeOutlives).upcast(self.tcx); - self.tcx - .dcx() - .struct_span_err(origin.span(), format!("cannot normalize `{clause}`")) - .emit() - } - } - }; - - guar = Some(e) - } - - guar.unwrap() - } - - // This method goes through all the errors and try to group certain types - // of error together, for the purpose of suggesting explicit lifetime - // parameters to the user. This is done so that we can have a more - // complete view of what lifetimes should be the same. - // If the return value is an empty vector, it means that processing - // failed (so the return value of this method should not be used). - // - // The method also attempts to weed out messages that seem like - // duplicates that will be unhelpful to the end-user. But - // obviously it never weeds out ALL errors. - fn process_errors( - &self, - errors: &[RegionResolutionError<'tcx>], - ) -> Vec<RegionResolutionError<'tcx>> { - debug!("process_errors()"); - - // We want to avoid reporting generic-bound failures if we can - // avoid it: these have a very high rate of being unhelpful in - // practice. This is because they are basically secondary - // checks that test the state of the region graph after the - // rest of inference is done, and the other kinds of errors - // indicate that the region constraint graph is internally - // inconsistent, so these test results are likely to be - // meaningless. - // - // Therefore, we filter them out of the list unless they are - // the only thing in the list. - - let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e { - RegionResolutionError::GenericBoundFailure(..) => true, - RegionResolutionError::ConcreteFailure(..) - | RegionResolutionError::SubSupConflict(..) - | RegionResolutionError::UpperBoundUniverseConflict(..) - | RegionResolutionError::CannotNormalize(..) => false, - }; - - let mut errors = if errors.iter().all(|e| is_bound_failure(e)) { - errors.to_owned() - } else { - errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect() - }; - - // sort the errors by span, for better error message stability. - errors.sort_by_key(|u| match *u { - RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(), - RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), - RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(), - RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(), - RegionResolutionError::CannotNormalize(_, ref sro) => sro.span(), - }); - errors - } - - pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) { - match *origin { - infer::Subtype(ref trace) => RegionOriginNote::WithRequirement { - span: trace.cause.span, - requirement: ObligationCauseAsDiagArg(trace.cause.clone()), - expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)), - } - .add_to_diag(err), - infer::Reborrow(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err) - } - infer::RelateObjectBound(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound } - .add_to_diag(err); - } - infer::ReferenceOutlivesReferent(ty, span) => { - RegionOriginNote::WithName { - span, - msg: fluent::infer_reference_outlives_referent, - name: &self.ty_to_string(ty), - continues: false, - } - .add_to_diag(err); - } - infer::RelateParamBound(span, ty, opt_span) => { - RegionOriginNote::WithName { - span, - msg: fluent::infer_relate_param_bound, - name: &self.ty_to_string(ty), - continues: opt_span.is_some(), - } - .add_to_diag(err); - if let Some(span) = opt_span { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 } - .add_to_diag(err); - } - } - infer::RelateRegionParamBound(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound } - .add_to_diag(err); - } - infer::CompareImplItemObligation { span, .. } => { - RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation } - .add_to_diag(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_diag(err); - } - } - } - - pub(super) fn report_concrete_failure( - &self, - generic_param_scope: LocalDefId, - origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>, - ) -> Diag<'a> { - let mut err = match origin { - infer::Subtype(box trace) => { - let terr = TypeError::RegionsDoesNotOutlive(sup, sub); - let mut err = self.report_and_explain_type_error(trace, terr); - match (*sub, *sup) { - (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {} - (ty::RePlaceholder(_), _) => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "", - sup, - " doesn't meet the lifetime requirements", - None, - ); - } - (_, ty::RePlaceholder(_)) => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "the required lifetime does not necessarily outlive ", - sub, - "", - None, - ); - } - _ => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "", - sup, - "...", - None, - ); - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "...does not necessarily outlive ", - sub, - "", - None, - ); - } - } - err - } - infer::Reborrow(span) => { - let reference_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::RefValidFor, - note_and_explain::SuffixKind::Continues, - ); - let content_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::ContentValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(OutlivesContent { - span, - notes: reference_valid.into_iter().chain(content_valid).collect(), - }) - } - infer::RelateObjectBound(span) => { - let object_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::TypeObjValidFor, - note_and_explain::SuffixKind::Empty, - ); - let pointer_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::SourcePointerValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(OutlivesBound { - span, - notes: object_valid.into_iter().chain(pointer_valid).collect(), - }) - } - infer::RelateParamBound(span, ty, opt_span) => { - let prefix = match *sub { - ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy, - _ => note_and_explain::PrefixKind::TypeOutlive, - }; - let suffix = if opt_span.is_some() { - note_and_explain::SuffixKind::ReqByBinding - } else { - note_and_explain::SuffixKind::Empty - }; - let note = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - opt_span, - prefix, - suffix, - ); - self.dcx().create_err(FulfillReqLifetime { - span, - ty: self.resolve_vars_if_possible(ty), - note, - }) - } - infer::RelateRegionParamBound(span) => { - let param_instantiated = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::LfParamInstantiatedWith, - note_and_explain::SuffixKind::Empty, - ); - let param_must_outlive = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::LfParamMustOutlive, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(LfBoundNotSatisfied { - span, - notes: param_instantiated.into_iter().chain(param_must_outlive).collect(), - }) - } - infer::ReferenceOutlivesReferent(ty, span) => { - let pointer_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::PointerValidFor, - note_and_explain::SuffixKind::Empty, - ); - let data_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::DataValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(RefLongerThanData { - span, - ty: self.resolve_vars_if_possible(ty), - notes: pointer_valid.into_iter().chain(data_valid).collect(), - }) - } - infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => { - let mut err = self.infcx.report_extra_impl_obligation( - span, - impl_item_def_id, - trait_item_def_id, - &format!("`{sup}: {sub}`"), - ); - // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause - if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) - && generics.where_clause_span.contains(span) - { - self.suggest_copy_trait_method_bounds( - trait_item_def_id, - impl_item_def_id, - &mut err, - ); - } - err - } - infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => { - let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup); - - // Don't mention the item name if it's an RPITIT, since that'll just confuse - // folks. - if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) { - let trait_item_span = self.tcx.def_span(trait_item_def_id); - let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); - err.span_label( - trait_item_span, - format!("definition of `{item_name}` from trait"), - ); - } - - self.suggest_copy_trait_method_bounds( - trait_item_def_id, - impl_item_def_id, - &mut err, - ); - err - } - infer::AscribeUserTypeProvePredicate(span) => { - let instantiated = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::LfInstantiatedWith, - note_and_explain::SuffixKind::Empty, - ); - let must_outlive = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::LfMustOutlive, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(LfBoundNotSatisfied { - span, - notes: instantiated.into_iter().chain(must_outlive).collect(), - }) - } - }; - if sub.is_error() || sup.is_error() { - err.downgrade_to_delayed_bug(); - } - err - } - - pub fn suggest_copy_trait_method_bounds( - &self, - trait_item_def_id: DefId, - impl_item_def_id: LocalDefId, - err: &mut Diag<'_>, - ) { - // FIXME(compiler-errors): Right now this is only being used for region - // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches, - // but right now it's not really very smart when it comes to implicit `Sized` - // predicates and bounds on the trait itself. - - let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx) - else { - return; - }; - let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else { - return; - }; - let trait_args = trait_ref - .instantiate_identity() - // Replace the explicit self type with `Self` for better suggestion rendering - .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper)) - .args; - let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id) - .rebase_onto(self.tcx, impl_def_id, trait_args); - - let Ok(trait_predicates) = - self.tcx - .explicit_predicates_of(trait_item_def_id) - .instantiate_own(self.tcx, trait_item_args) - .map(|(pred, _)| { - if pred.is_suggestable(self.tcx, false) { - Ok(pred.to_string()) - } else { - Err(()) - } - }) - .collect::<Result<Vec<_>, ()>>() - else { - return; - }; - - let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else { - return; - }; - - let suggestion = if trait_predicates.is_empty() { - WhereClauseSuggestions::Remove { span: generics.where_clause_span } - } else { - let space = if generics.where_clause_span.is_empty() { " " } else { "" }; - WhereClauseSuggestions::CopyPredicates { - span: generics.where_clause_span, - space, - trait_predicates: trait_predicates.join(", "), - } - }; - err.subdiagnostic(suggestion); - } - - pub(super) fn report_placeholder_failure( - &self, - generic_param_scope: LocalDefId, - placeholder_origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>, - ) -> Diag<'a> { - // I can't think how to do better than this right now. -nikomatsakis - debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure"); - match placeholder_origin { - infer::Subtype(box ref trace) - if matches!( - &trace.cause.code().peel_derives(), - ObligationCauseCode::WhereClause(..) - | ObligationCauseCode::WhereClauseInExpr(..) - ) => - { - // Hack to get around the borrow checker because trace.cause has an `Rc`. - if let ObligationCauseCode::WhereClause(_, span) - | ObligationCauseCode::WhereClauseInExpr(_, span, ..) = - &trace.cause.code().peel_derives() - && !span.is_dummy() - { - let span = *span; - self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup) - .with_span_note(span, "the lifetime requirement is introduced here") - } else { - unreachable!( - "control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..." - ) - } - } - infer::Subtype(box trace) => { - let terr = TypeError::RegionsPlaceholderMismatch; - return self.report_and_explain_type_error(trace, terr); - } - _ => { - return self.report_concrete_failure( - generic_param_scope, - placeholder_origin, - sub, - sup, - ); - } - } - } - - pub fn report_generic_bound_failure( - &self, - generic_param_scope: LocalDefId, - span: Span, - origin: Option<SubregionOrigin<'tcx>>, - bound_kind: GenericKind<'tcx>, - sub: Region<'tcx>, - ) -> ErrorGuaranteed { - self.construct_generic_bound_failure(generic_param_scope, span, origin, bound_kind, sub) - .emit() - } - - pub fn construct_generic_bound_failure( - &self, - generic_param_scope: LocalDefId, - span: Span, - origin: Option<SubregionOrigin<'tcx>>, - bound_kind: GenericKind<'tcx>, - sub: Region<'tcx>, - ) -> Diag<'a> { - if let Some(SubregionOrigin::CompareImplItemObligation { - span, - impl_item_def_id, - trait_item_def_id, - }) = origin - { - return self.infcx.report_extra_impl_obligation( - span, - impl_item_def_id, - trait_item_def_id, - &format!("`{bound_kind}: {sub}`"), - ); - } - - let labeled_user_string = match bound_kind { - GenericKind::Param(ref p) => format!("the parameter type `{p}`"), - GenericKind::Placeholder(ref p) => format!("the placeholder type `{p:?}`"), - GenericKind::Alias(ref p) => match p.kind(self.tcx) { - ty::Projection | ty::Inherent => { - format!("the associated type `{p}`") - } - ty::Weak => format!("the type alias `{p}`"), - ty::Opaque => format!("the opaque type `{p}`"), - }, - }; - - let mut err = self - .tcx - .dcx() - .struct_span_err(span, format!("{labeled_user_string} may not live long enough")); - err.code(match sub.kind() { - ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => E0309, - ty::ReStatic => E0310, - _ => E0311, - }); - - '_explain: { - let (description, span) = match sub.kind() { - ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => { - msg_span_from_named_region(self.tcx, generic_param_scope, sub, Some(span)) - } - _ => (format!("lifetime `{sub}`"), Some(span)), - }; - let prefix = format!("{labeled_user_string} must be valid for "); - label_msg_span(&mut err, &prefix, description, span, "..."); - if let Some(origin) = origin { - self.note_region_origin(&mut err, &origin); - } - } - - 'suggestion: { - let msg = "consider adding an explicit lifetime bound"; - - if (bound_kind, sub).has_infer_regions() - || (bound_kind, sub).has_placeholders() - || !bound_kind.is_suggestable(self.tcx, false) - { - let lt_name = sub.get_name_or_anon().to_string(); - err.help(format!("{msg} `{bound_kind}: {lt_name}`...")); - break 'suggestion; - } - - let mut generic_param_scope = generic_param_scope; - while self.tcx.def_kind(generic_param_scope) == DefKind::OpaqueTy { - generic_param_scope = self.tcx.local_parent(generic_param_scope); - } - - // type_param_sugg_span is (span, has_bounds, needs_parentheses) - let (type_scope, type_param_sugg_span) = match bound_kind { - GenericKind::Param(param) => { - let generics = self.tcx.generics_of(generic_param_scope); - let type_param = generics.type_param(param, self.tcx); - let def_id = type_param.def_id.expect_local(); - let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id; - // Get the `hir::Param` to verify whether it already has any bounds. - // We do this to avoid suggesting code that ends up as `T: 'a'b`, - // instead we suggest `T: 'a + 'b` in that case. - let hir_generics = self.tcx.hir().get_generics(scope).unwrap(); - let sugg_span = match hir_generics.bounds_span_for_suggestions(def_id) { - Some((span, open_paren_sp)) => Some((span, true, open_paren_sp)), - // If `param` corresponds to `Self`, no usable suggestion span. - None if generics.has_self && param.index == 0 => None, - None => { - let span = if let Some(param) = - hir_generics.params.iter().find(|param| param.def_id == def_id) - && let ParamName::Plain(ident) = param.name - { - ident.span.shrink_to_hi() - } else { - let span = self.tcx.def_span(def_id); - span.shrink_to_hi() - }; - Some((span, false, None)) - } - }; - (scope, sugg_span) - } - _ => (generic_param_scope, None), - }; - let suggestion_scope = { - let lifetime_scope = match sub.kind() { - ty::ReStatic => hir::def_id::CRATE_DEF_ID, - _ => match self.tcx.is_suitable_region(generic_param_scope, sub) { - Some(info) => info.def_id, - None => generic_param_scope, - }, - }; - match self.tcx.is_descendant_of(type_scope.into(), lifetime_scope.into()) { - true => type_scope, - false => lifetime_scope, - } - }; - - let mut suggs = vec![]; - let lt_name = self.suggest_name_region(generic_param_scope, sub, &mut suggs); - - if let Some((sp, has_lifetimes, open_paren_sp)) = type_param_sugg_span - && suggestion_scope == type_scope - { - let suggestion = - if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") }; - - if let Some(open_paren_sp) = open_paren_sp { - suggs.push((open_paren_sp, "(".to_string())); - suggs.push((sp, format!("){suggestion}"))); - } else { - suggs.push((sp, suggestion)) - } - } else if let GenericKind::Alias(ref p) = bound_kind - && let ty::Projection = p.kind(self.tcx) - && let DefKind::AssocTy = self.tcx.def_kind(p.def_id) - && let Some(ty::ImplTraitInTraitData::Trait { .. }) = - self.tcx.opt_rpitit_info(p.def_id) - { - // The lifetime found in the `impl` is longer than the one on the RPITIT. - // Do not suggest `<Type as Trait>::{opaque}: 'static`. - } else if let Some(generics) = self.tcx.hir().get_generics(suggestion_scope) { - let pred = format!("{bound_kind}: {lt_name}"); - let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred); - suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion)) - } else { - let consider = format!("{msg} `{bound_kind}: {sub}`..."); - err.help(consider); - } - - if !suggs.is_empty() { - err.multipart_suggestion_verbose( - msg, - suggs, - Applicability::MaybeIncorrect, // Issue #41966 - ); - } - } - - err - } - - pub fn suggest_name_region( - &self, - generic_param_scope: LocalDefId, - lifetime: Region<'tcx>, - add_lt_suggs: &mut Vec<(Span, String)>, - ) -> String { - struct LifetimeReplaceVisitor<'tcx, 'a> { - tcx: TyCtxt<'tcx>, - needle: hir::LifetimeName, - new_lt: &'a str, - add_lt_suggs: &'a mut Vec<(Span, String)>, - } - - impl<'hir, 'tcx> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'tcx, '_> { - fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) { - if lt.res == self.needle { - let (pos, span) = lt.suggestion_position(); - let new_lt = &self.new_lt; - let sugg = match pos { - hir::LifetimeSuggestionPosition::Normal => format!("{new_lt}"), - hir::LifetimeSuggestionPosition::Ampersand => format!("{new_lt} "), - hir::LifetimeSuggestionPosition::ElidedPath => format!("<{new_lt}>"), - hir::LifetimeSuggestionPosition::ElidedPathArgument => { - format!("{new_lt}, ") - } - hir::LifetimeSuggestionPosition::ObjectDefault => format!("+ {new_lt}"), - }; - self.add_lt_suggs.push((span, sugg)); - } - } - - fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) { - let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind else { - return hir::intravisit::walk_ty(self, ty); - }; - let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty(); - if let Some(&(_, b)) = - opaque_ty.lifetime_mapping.iter().find(|&(a, _)| a.res == self.needle) - { - let prev_needle = - std::mem::replace(&mut self.needle, hir::LifetimeName::Param(b)); - for bound in opaque_ty.bounds { - self.visit_param_bound(bound); - } - self.needle = prev_needle; - } - } - } - - let (lifetime_def_id, lifetime_scope) = - match self.tcx.is_suitable_region(generic_param_scope, lifetime) { - Some(info) if !lifetime.has_name() => { - (info.bound_region.get_id().unwrap().expect_local(), info.def_id) - } - _ => return lifetime.get_name_or_anon().to_string(), - }; - - let new_lt = { - let generics = self.tcx.generics_of(lifetime_scope); - let mut used_names = - iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p))) - .flat_map(|g| &g.own_params) - .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) - .map(|p| p.name) - .collect::<Vec<_>>(); - let hir_id = self.tcx.local_def_id_to_hir_id(lifetime_scope); - // consider late-bound lifetimes ... - used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map( - |p| match p { - ty::BoundVariableKind::Region(lt) => lt.get_name(), - _ => None, - }, - )); - (b'a'..=b'z') - .map(|c| format!("'{}", c as char)) - .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate)) - .unwrap_or("'lt".to_string()) - }; - - let mut visitor = LifetimeReplaceVisitor { - tcx: self.tcx, - needle: hir::LifetimeName::Param(lifetime_def_id), - add_lt_suggs, - new_lt: &new_lt, - }; - match self.tcx.expect_hir_owner_node(lifetime_scope) { - hir::OwnerNode::Item(i) => visitor.visit_item(i), - hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i), - hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i), - hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i), - hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"), - hir::OwnerNode::Synthetic => unreachable!(), - } - - let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap(); - let sugg = ast_generics - .span_for_lifetime_suggestion() - .map(|span| (span, format!("{new_lt}, "))) - .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>"))); - add_lt_suggs.push(sugg); - - new_lt - } - - fn report_sub_sup_conflict( - &self, - generic_param_scope: LocalDefId, - var_origin: RegionVariableOrigin, - sub_origin: SubregionOrigin<'tcx>, - sub_region: Region<'tcx>, - sup_origin: SubregionOrigin<'tcx>, - sup_region: Region<'tcx>, - ) -> ErrorGuaranteed { - let mut err = self.report_inference_failure(var_origin); - - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "first, the lifetime cannot outlive ", - sup_region, - "...", - None, - ); - - debug!("report_sub_sup_conflict: var_origin={:?}", var_origin); - debug!("report_sub_sup_conflict: sub_region={:?}", sub_region); - debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin); - debug!("report_sub_sup_conflict: sup_region={:?}", sup_region); - debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin); - - if let infer::Subtype(ref sup_trace) = sup_origin - && let infer::Subtype(ref sub_trace) = sub_origin - && let Some((sup_expected, sup_found, _)) = self.values_str(sup_trace.values) - && let Some((sub_expected, sub_found, _)) = self.values_str(sub_trace.values) - && sub_expected == sup_expected - && sub_found == sup_found - { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "...but the lifetime must also be valid for ", - sub_region, - "...", - None, - ); - err.span_note( - sup_trace.cause.span, - format!("...so that the {}", sup_trace.cause.as_requirement_str()), - ); - - err.note_expected_found(&"", sup_expected, &"", sup_found); - return if sub_region.is_error() | sup_region.is_error() { - err.delay_as_bug() - } else { - err.emit() - }; - } - - self.note_region_origin(&mut err, &sup_origin); - - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "but, the lifetime must be valid for ", - sub_region, - "...", - None, - ); - - self.note_region_origin(&mut err, &sub_origin); - if sub_region.is_error() | sup_region.is_error() { err.delay_as_bug() } else { err.emit() } - } - - fn report_inference_failure(&self, var_origin: RegionVariableOrigin) -> Diag<'_> { - let br_string = |br: ty::BoundRegionKind| { - let mut s = match br { - ty::BrNamed(_, name) => name.to_string(), - _ => String::new(), - }; - if !s.is_empty() { - s.push(' '); - } - s - }; - let var_description = match var_origin { - infer::MiscVariable(_) => String::new(), - infer::PatternRegion(_) => " for pattern".to_string(), - infer::AddrOfRegion(_) => " for borrow expression".to_string(), - infer::Autoref(_) => " for autoref".to_string(), - infer::Coercion(_) => " for automatic coercion".to_string(), - infer::BoundRegion(_, br, infer::FnCall) => { - format!(" for lifetime parameter {}in function call", br_string(br)) - } - infer::BoundRegion(_, br, infer::HigherRankedType) => { - format!(" for lifetime parameter {}in generic type", br_string(br)) - } - infer::BoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!( - " for lifetime parameter {}in trait containing associated type `{}`", - br_string(br), - self.tcx.associated_item(def_id).name - ), - infer::RegionParameterDefinition(_, name) => { - format!(" for lifetime parameter `{name}`") - } - infer::UpvarRegion(ref upvar_id, _) => { - let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); - format!(" for capture of `{var_name}` by closure") - } - infer::Nll(..) => bug!("NLL variable found in lexical phase"), - }; - - struct_span_code_err!( - self.dcx(), - var_origin.span(), - E0495, - "cannot infer an appropriate lifetime{} due to conflicting requirements", - var_description - ) - } -} - -pub(super) fn note_and_explain_region<'tcx>( - tcx: TyCtxt<'tcx>, - err: &mut Diag<'_>, - generic_param_scope: LocalDefId, - prefix: &str, - region: ty::Region<'tcx>, - suffix: &str, - alt_span: Option<Span>, -) { - let (description, span) = match *region { - ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReStatic => { - msg_span_from_named_region(tcx, generic_param_scope, region, alt_span) - } - - ty::ReError(_) => return, - - // FIXME(#125431): `ReVar` shouldn't reach here. - ty::ReVar(_) => (format!("lifetime `{region}`"), alt_span), - - ty::ReBound(..) | ty::ReErased => { - bug!("unexpected region for note_and_explain_region: {:?}", region); - } - }; - - emit_msg_span(err, prefix, description, span, suffix); -} - -fn explain_free_region<'tcx>( - tcx: TyCtxt<'tcx>, - err: &mut Diag<'_>, - generic_param_scope: LocalDefId, - prefix: &str, - region: ty::Region<'tcx>, - suffix: &str, -) { - let (description, span) = msg_span_from_named_region(tcx, generic_param_scope, region, None); - - label_msg_span(err, prefix, description, span, suffix); -} - -fn msg_span_from_named_region<'tcx>( - tcx: TyCtxt<'tcx>, - generic_param_scope: LocalDefId, - region: ty::Region<'tcx>, - alt_span: Option<Span>, -) -> (String, Option<Span>) { - match *region { - ty::ReEarlyParam(br) => { - let scope = tcx - .parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id) - .expect_local(); - let span = if let Some(param) = - tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) - { - param.span - } else { - tcx.def_span(scope) - }; - let text = if br.has_name() { - format!("the lifetime `{}` as defined here", br.name) - } else { - "the anonymous lifetime as defined here".to_string() - }; - (text, Some(span)) - } - ty::ReLateParam(ref fr) => { - if !fr.bound_region.is_named() - && let Some((ty, _)) = - find_anon_type(tcx, generic_param_scope, region, &fr.bound_region) - { - ("the anonymous lifetime defined here".to_string(), Some(ty.span)) - } else { - match fr.bound_region { - ty::BoundRegionKind::BrNamed(_, name) => { - let span = if let Some(param) = tcx - .hir() - .get_generics(generic_param_scope) - .and_then(|generics| generics.get_named(name)) - { - param.span - } else { - tcx.def_span(generic_param_scope) - }; - let text = if name == kw::UnderscoreLifetime { - "the anonymous lifetime as defined here".to_string() - } else { - format!("the lifetime `{name}` as defined here") - }; - (text, Some(span)) - } - ty::BrAnon => ( - "the anonymous lifetime as defined here".to_string(), - Some(tcx.def_span(generic_param_scope)), - ), - _ => ( - format!("the lifetime `{region}` as defined here"), - Some(tcx.def_span(generic_param_scope)), - ), - } - } - } - ty::ReStatic => ("the static lifetime".to_owned(), alt_span), - ty::RePlaceholder(ty::PlaceholderRegion { - bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, name), .. }, - .. - }) => (format!("the lifetime `{name}` as defined here"), Some(tcx.def_span(def_id))), - ty::RePlaceholder(ty::PlaceholderRegion { - bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon, .. }, - .. - }) => ("an anonymous lifetime".to_owned(), None), - _ => bug!("{:?}", region), - } -} - -fn emit_msg_span( - err: &mut Diag<'_>, - prefix: &str, - description: String, - span: Option<Span>, - suffix: &str, -) { - let message = format!("{prefix}{description}{suffix}"); - - if let Some(span) = span { - err.span_note(span, message); - } else { - err.note(message); - } -} - -fn label_msg_span( - err: &mut Diag<'_>, - prefix: &str, - description: String, - span: Option<Span>, - suffix: &str, -) { - let message = format!("{prefix}{description}{suffix}"); - - if let Some(span) = span { - err.span_label(span, message); - } else { - err.note(message); - } -} - -#[instrument(level = "trace", skip(infcx))] -pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>( - infcx: &'a InferCtxt<'tcx>, - generic_param_scope: LocalDefId, - span: Span, - hidden_ty: Ty<'tcx>, - hidden_region: ty::Region<'tcx>, - opaque_ty_key: ty::OpaqueTypeKey<'tcx>, -) -> Diag<'a> { - let tcx = infcx.tcx; - let mut err = infcx.dcx().create_err(errors::OpaqueCapturesLifetime { - span, - opaque_ty: Ty::new_opaque(tcx, opaque_ty_key.def_id.to_def_id(), opaque_ty_key.args), - opaque_ty_span: tcx.def_span(opaque_ty_key.def_id), - }); - - // Explain the region we are capturing. - match *hidden_region { - ty::ReEarlyParam(_) | ty::ReLateParam(_) | 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 - // down this path which gives a decent human readable - // explanation. - // - // (*) if not, the `tainted_by_errors` field would be set to - // `Some(ErrorGuaranteed)` in any case, so we wouldn't be here at all. - explain_free_region( - tcx, - &mut err, - generic_param_scope, - &format!("hidden type `{hidden_ty}` captures "), - hidden_region, - "", - ); - if let Some(reg_info) = tcx.is_suitable_region(generic_param_scope, hidden_region) { - if infcx.tcx.features().precise_capturing { - suggest_precise_capturing(tcx, opaque_ty_key.def_id, hidden_region, &mut err); - } else { - let fn_returns = tcx.return_type_impl_or_dyn_traits(reg_info.def_id); - nice_region_error::suggest_new_region_bound( - tcx, - &mut err, - fn_returns, - hidden_region.to_string(), - None, - format!("captures `{hidden_region}`"), - None, - Some(reg_info.def_id), - ) - } - } - } - ty::RePlaceholder(_) => { - explain_free_region( - tcx, - &mut err, - generic_param_scope, - &format!("hidden type `{}` captures ", hidden_ty), - hidden_region, - "", - ); - } - ty::ReError(_) => { - err.downgrade_to_delayed_bug(); - } - _ => { - // Ugh. This is a painful case: the hidden region is not one - // that we can easily summarize or explain. This can happen - // in a case like - // `tests/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`: - // - // ``` - // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> { - // if condition() { a } else { b } - // } - // ``` - // - // Here the captured lifetime is the intersection of `'a` and - // `'b`, which we can't quite express. - - // We can at least report a really cryptic error for now. - note_and_explain_region( - tcx, - &mut err, - generic_param_scope, - &format!("hidden type `{hidden_ty}` captures "), - hidden_region, - "", - None, - ); - } - } - - err -} - -fn suggest_precise_capturing<'tcx>( - tcx: TyCtxt<'tcx>, - opaque_def_id: LocalDefId, - captured_lifetime: ty::Region<'tcx>, - diag: &mut Diag<'_>, -) { - let hir::OpaqueTy { bounds, origin, .. } = - tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); - - let hir::OpaqueTyOrigin::FnReturn(fn_def_id) = *origin else { - return; - }; - - let new_lifetime = Symbol::intern(&captured_lifetime.to_string()); - - if let Some((args, span)) = bounds.iter().find_map(|bound| match bound { - hir::GenericBound::Use(args, span) => Some((args, span)), - _ => None, - }) { - let last_lifetime_span = args.iter().rev().find_map(|arg| match arg { - hir::PreciseCapturingArg::Lifetime(lt) => Some(lt.ident.span), - _ => None, - }); - - let first_param_span = args.iter().find_map(|arg| match arg { - hir::PreciseCapturingArg::Param(p) => Some(p.ident.span), - _ => None, - }); - - let (span, pre, post) = if let Some(last_lifetime_span) = last_lifetime_span { - (last_lifetime_span.shrink_to_hi(), ", ", "") - } else if let Some(first_param_span) = first_param_span { - (first_param_span.shrink_to_lo(), "", ", ") - } else { - // If we have no args, then have `use<>` and need to fall back to using - // span math. This sucks, but should be reliable due to the construction - // of the `use<>` span. - (span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(), "", "") - }; - - diag.subdiagnostic(errors::AddPreciseCapturing::Existing { span, new_lifetime, pre, post }); - } else { - let mut captured_lifetimes = FxIndexSet::default(); - let mut captured_non_lifetimes = FxIndexSet::default(); - - let variances = tcx.variances_of(opaque_def_id); - let mut generics = tcx.generics_of(opaque_def_id); - let mut synthetics = vec![]; - loop { - for param in &generics.own_params { - if variances[param.index as usize] == ty::Bivariant { - continue; - } - - match param.kind { - ty::GenericParamDefKind::Lifetime => { - captured_lifetimes.insert(param.name); - } - ty::GenericParamDefKind::Type { synthetic: true, .. } => { - synthetics.push((tcx.def_span(param.def_id), param.name)); - } - ty::GenericParamDefKind::Type { .. } - | ty::GenericParamDefKind::Const { .. } => { - captured_non_lifetimes.insert(param.name); - } - } - } - - if let Some(parent) = generics.parent { - generics = tcx.generics_of(parent); - } else { - break; - } - } - - if !captured_lifetimes.insert(new_lifetime) { - // Uh, strange. This lifetime appears to already be captured... - return; - } - - if synthetics.is_empty() { - let concatenated_bounds = captured_lifetimes - .into_iter() - .chain(captured_non_lifetimes) - .map(|sym| sym.to_string()) - .collect::<Vec<_>>() - .join(", "); - - diag.subdiagnostic(errors::AddPreciseCapturing::New { - span: tcx.def_span(opaque_def_id).shrink_to_hi(), - new_lifetime, - concatenated_bounds, - }); - } else { - let mut next_fresh_param = || { - ["T", "U", "V", "W", "X", "Y", "A", "B", "C"] - .into_iter() - .map(Symbol::intern) - .chain((0..).map(|i| Symbol::intern(&format!("T{i}")))) - .find(|s| captured_non_lifetimes.insert(*s)) - .unwrap() - }; - - let mut new_params = String::new(); - let mut suggs = vec![]; - let mut apit_spans = vec![]; - - for (i, (span, name)) in synthetics.into_iter().enumerate() { - apit_spans.push(span); - - let fresh_param = next_fresh_param(); - - // Suggest renaming. - suggs.push((span, fresh_param.to_string())); - - // Super jank. Turn `impl Trait` into `T: Trait`. - // - // This currently involves stripping the `impl` from the name of - // the parameter, since APITs are always named after how they are - // rendered in the AST. This sucks! But to recreate the bound list - // from the APIT itself would be miserable, so we're stuck with - // this for now! - if i > 0 { - new_params += ", "; - } - let name_as_bounds = name.as_str().trim_start_matches("impl").trim_start(); - new_params += fresh_param.as_str(); - new_params += ": "; - new_params += name_as_bounds; - } - - let Some(generics) = tcx.hir().get_generics(fn_def_id) else { - // This shouldn't happen, but don't ICE. - return; - }; - - // Add generics or concatenate to the end of the list. - suggs.push(if let Some(params_span) = generics.span_for_param_suggestion() { - (params_span, format!(", {new_params}")) - } else { - (generics.span, format!("<{new_params}>")) - }); - - let concatenated_bounds = captured_lifetimes - .into_iter() - .chain(captured_non_lifetimes) - .map(|sym| sym.to_string()) - .collect::<Vec<_>>() - .join(", "); - - suggs.push(( - tcx.def_span(opaque_def_id).shrink_to_hi(), - format!(" + use<{concatenated_bounds}>"), - )); - - diag.subdiagnostic(errors::AddPreciseCapturingAndParams { - suggs, - new_lifetime, - apit_spans, - }); - } - } -} diff --git a/compiler/rustc_infer/src/error_reporting/infer/sub_relations.rs b/compiler/rustc_infer/src/error_reporting/infer/sub_relations.rs deleted file mode 100644 index ef26a8ff7b8..00000000000 --- a/compiler/rustc_infer/src/error_reporting/infer/sub_relations.rs +++ /dev/null @@ -1,81 +0,0 @@ -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::undo_log::NoUndo; -use rustc_data_structures::unify as ut; -use rustc_middle::ty; - -use crate::infer::InferCtxt; - -#[derive(Debug, Copy, Clone, PartialEq)] -struct SubId(u32); -impl ut::UnifyKey for SubId { - type Value = (); - #[inline] - fn index(&self) -> u32 { - self.0 - } - #[inline] - fn from_index(i: u32) -> SubId { - SubId(i) - } - fn tag() -> &'static str { - "SubId" - } -} - -/// When reporting ambiguity errors, we sometimes want to -/// treat all inference vars which are subtypes of each -/// others as if they are equal. For this case we compute -/// the transitive closure of our subtype obligations here. -/// -/// E.g. when encountering ambiguity errors, we want to suggest -/// specifying some method argument or to add a type annotation -/// to a local variable. Because subtyping cannot change the -/// shape of a type, it's fine if the cause of the ambiguity error -/// is only related to the suggested variable via subtyping. -/// -/// Even for something like `let x = returns_arg(); x.method();` the -/// type of `x` is only a supertype of the argument of `returns_arg`. We -/// still want to suggest specifying the type of the argument. -#[derive(Default)] -pub struct SubRelations { - map: FxHashMap<ty::TyVid, SubId>, - table: ut::UnificationTableStorage<SubId>, -} - -impl SubRelations { - fn get_id<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, vid: ty::TyVid) -> SubId { - let root_vid = infcx.root_var(vid); - *self.map.entry(root_vid).or_insert_with(|| self.table.with_log(&mut NoUndo).new_key(())) - } - - pub fn add_constraints<'tcx>( - &mut self, - infcx: &InferCtxt<'tcx>, - obls: impl IntoIterator<Item = ty::Predicate<'tcx>>, - ) { - for p in obls { - let (a, b) = match p.kind().skip_binder() { - ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => { - (a, b) - } - ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => (a, b), - _ => continue, - }; - - match (a.kind(), b.kind()) { - (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => { - let a = self.get_id(infcx, a_vid); - let b = self.get_id(infcx, b_vid); - self.table.with_log(&mut NoUndo).unify_var_var(a, b).unwrap(); - } - _ => continue, - } - } - } - - pub fn unified<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, a: ty::TyVid, b: ty::TyVid) -> bool { - let a = self.get_id(infcx, a); - let b = self.get_id(infcx, b); - self.table.with_log(&mut NoUndo).unioned(a, b) - } -} diff --git a/compiler/rustc_infer/src/error_reporting/infer/suggest.rs b/compiler/rustc_infer/src/error_reporting/infer/suggest.rs deleted file mode 100644 index 4d11ab9fac6..00000000000 --- a/compiler/rustc_infer/src/error_reporting/infer/suggest.rs +++ /dev/null @@ -1,899 +0,0 @@ -use crate::error_reporting::infer::hir::Path; -use core::ops::ControlFlow; -use hir::def::CtorKind; -use hir::intravisit::{walk_expr, walk_stmt, Visitor}; -use hir::{LetStmt, QPath}; -use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{Applicability, Diag}; -use rustc_hir as hir; -use rustc_hir::def::Res; -use rustc_hir::MatchSource; -use rustc_hir::Node; -use rustc_middle::traits::{ - IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, - StatementAsExpression, -}; -use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt}; -use rustc_span::{sym, Span}; - -use crate::errors::{ - ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, - FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding, - SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags, -}; - -use super::TypeErrCtxt; - -#[derive(Clone, Copy)] -pub enum SuggestAsRefKind { - Option, - Result, -} - -impl<'tcx> TypeErrCtxt<'_, 'tcx> { - pub(super) fn suggest_remove_semi_or_return_binding( - &self, - first_id: Option<hir::HirId>, - first_ty: Ty<'tcx>, - first_span: Span, - second_id: Option<hir::HirId>, - second_ty: Ty<'tcx>, - second_span: Span, - ) -> Option<SuggestRemoveSemiOrReturnBinding> { - let remove_semicolon = [ - (first_id, self.resolve_vars_if_possible(second_ty)), - (second_id, self.resolve_vars_if_possible(first_ty)), - ] - .into_iter() - .find_map(|(id, ty)| { - let hir::Node::Block(blk) = self.tcx.hir_node(id?) else { return None }; - self.could_remove_semicolon(blk, ty) - }); - match remove_semicolon { - Some((sp, StatementAsExpression::NeedsBoxing)) => { - Some(SuggestRemoveSemiOrReturnBinding::RemoveAndBox { - first_lo: first_span.shrink_to_lo(), - first_hi: first_span.shrink_to_hi(), - second_lo: second_span.shrink_to_lo(), - second_hi: second_span.shrink_to_hi(), - sp, - }) - } - Some((sp, StatementAsExpression::CorrectType)) => { - Some(SuggestRemoveSemiOrReturnBinding::Remove { sp }) - } - None => { - let mut ret = None; - for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] { - if let Some(id) = id - && let hir::Node::Block(blk) = self.tcx.hir_node(id) - && let Some(diag) = self.consider_returning_binding_diag(blk, ty) - { - ret = Some(diag); - break; - } - } - ret - } - } - } - - pub(super) fn suggest_tuple_pattern( - &self, - cause: &ObligationCause<'tcx>, - exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, - diag: &mut Diag<'_>, - ) { - // Heavily inspired by `FnCtxt::suggest_compatible_variants`, with - // some modifications due to that being in typeck and this being in infer. - if let ObligationCauseCode::Pattern { .. } = cause.code() { - if let ty::Adt(expected_adt, args) = exp_found.expected.kind() { - let compatible_variants: Vec<_> = expected_adt - .variants() - .iter() - .filter(|variant| { - variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn) - }) - .filter_map(|variant| { - let sole_field = &variant.single_field(); - let sole_field_ty = sole_field.ty(self.tcx, args); - if self.same_type_modulo_infer(sole_field_ty, exp_found.found) { - let variant_path = - with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id)); - // FIXME #56861: DRYer prelude filtering - if let Some(path) = variant_path.strip_prefix("std::prelude::") { - if let Some((_, path)) = path.split_once("::") { - return Some(path.to_string()); - } - } - Some(variant_path) - } else { - None - } - }) - .collect(); - match &compatible_variants[..] { - [] => {} - [variant] => { - let sugg = SuggestTuplePatternOne { - variant: variant.to_owned(), - span_low: cause.span.shrink_to_lo(), - span_high: cause.span.shrink_to_hi(), - }; - diag.subdiagnostic(sugg); - } - _ => { - // More than one matching variant. - let sugg = SuggestTuplePatternMany { - path: self.tcx.def_path_str(expected_adt.did()), - cause_span: cause.span, - compatible_variants, - }; - diag.subdiagnostic(sugg); - } - } - } - } - } - - /// A possible error is to forget to add `.await` when using futures: - /// - /// ```compile_fail,E0308 - /// async fn make_u32() -> u32 { - /// 22 - /// } - /// - /// fn take_u32(x: u32) {} - /// - /// async fn foo() { - /// let x = make_u32(); - /// take_u32(x); - /// } - /// ``` - /// - /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the - /// expected type. If this is the case, and we are inside of an async body, it suggests adding - /// `.await` to the tail of the expression. - pub(super) fn suggest_await_on_expect_found( - &self, - cause: &ObligationCause<'tcx>, - exp_span: Span, - exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, - diag: &mut Diag<'_>, - ) { - debug!( - "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}", - exp_span, exp_found.expected, exp_found.found, - ); - - if let ObligationCauseCode::CompareImplItem { .. } = cause.code() { - return; - } - - let subdiag = match ( - self.get_impl_future_output_ty(exp_found.expected), - self.get_impl_future_output_ty(exp_found.found), - ) { - (Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause - .code() - { - ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => { - let then_span = self.find_block_span_from_hir_id(*then_id); - Some(ConsiderAddingAwait::BothFuturesSugg { - first: then_span.shrink_to_hi(), - second: exp_span.shrink_to_hi(), - }) - } - ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { - prior_non_diverging_arms, - .. - }) => { - if let [.., arm_span] = &prior_non_diverging_arms[..] { - Some(ConsiderAddingAwait::BothFuturesSugg { - first: arm_span.shrink_to_hi(), - second: exp_span.shrink_to_hi(), - }) - } else { - Some(ConsiderAddingAwait::BothFuturesHelp) - } - } - _ => Some(ConsiderAddingAwait::BothFuturesHelp), - }, - (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => { - // FIXME: Seems like we can't have a suggestion and a note with different spans in a single subdiagnostic - diag.subdiagnostic(ConsiderAddingAwait::FutureSugg { - span: exp_span.shrink_to_hi(), - }); - Some(ConsiderAddingAwait::FutureSuggNote { span: exp_span }) - } - (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code() - { - ObligationCauseCode::Pattern { span: Some(then_span), origin_expr, .. } => { - origin_expr.then_some(ConsiderAddingAwait::FutureSugg { - span: then_span.shrink_to_hi(), - }) - } - ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => { - let then_span = self.find_block_span_from_hir_id(*then_id); - Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() }) - } - ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { - ref prior_non_diverging_arms, - .. - }) => Some({ - ConsiderAddingAwait::FutureSuggMultiple { - spans: prior_non_diverging_arms - .iter() - .map(|arm| arm.shrink_to_hi()) - .collect(), - } - }), - _ => None, - }, - _ => None, - }; - if let Some(subdiag) = subdiag { - diag.subdiagnostic(subdiag); - } - } - - pub(super) fn suggest_accessing_field_where_appropriate( - &self, - cause: &ObligationCause<'tcx>, - exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, - diag: &mut Diag<'_>, - ) { - debug!( - "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})", - cause, exp_found - ); - if let ty::Adt(expected_def, expected_args) = exp_found.expected.kind() { - if expected_def.is_enum() { - return; - } - - if let Some((name, ty)) = expected_def - .non_enum_variant() - .fields - .iter() - .filter(|field| field.vis.is_accessible_from(field.did, self.tcx)) - .map(|field| (field.name, field.ty(self.tcx, expected_args))) - .find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found)) - { - if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - let suggestion = if expected_def.is_struct() { - SuggestAccessingField::Safe { span, snippet, name, ty } - } else if expected_def.is_union() { - SuggestAccessingField::Unsafe { span, snippet, name, ty } - } else { - return; - }; - diag.subdiagnostic(suggestion); - } - } - } - } - } - - pub(super) fn suggest_turning_stmt_into_expr( - &self, - cause: &ObligationCause<'tcx>, - exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, - diag: &mut Diag<'_>, - ) { - let ty::error::ExpectedFound { expected, found } = exp_found; - if !found.peel_refs().is_unit() { - return; - } - - let ObligationCauseCode::BlockTailExpression(hir_id, MatchSource::Normal) = cause.code() - else { - return; - }; - - let node = self.tcx.hir_node(*hir_id); - let mut blocks = vec![]; - if let hir::Node::Block(block) = node - && let Some(expr) = block.expr - && let hir::ExprKind::Path(QPath::Resolved(_, Path { res, .. })) = expr.kind - && let Res::Local(local) = res - && let Node::LetStmt(LetStmt { init: Some(init), .. }) = - self.tcx.parent_hir_node(*local) - { - fn collect_blocks<'hir>(expr: &hir::Expr<'hir>, blocks: &mut Vec<&hir::Block<'hir>>) { - match expr.kind { - // `blk1` and `blk2` must be have the same types, it will be reported before reaching here - hir::ExprKind::If(_, blk1, Some(blk2)) => { - collect_blocks(blk1, blocks); - collect_blocks(blk2, blocks); - } - hir::ExprKind::Match(_, arms, _) => { - // all arms must have same types - for arm in arms.iter() { - collect_blocks(arm.body, blocks); - } - } - hir::ExprKind::Block(blk, _) => { - blocks.push(blk); - } - _ => {} - } - } - collect_blocks(init, &mut blocks); - } - - let expected_inner: Ty<'_> = expected.peel_refs(); - for block in blocks.iter() { - self.consider_removing_semicolon(block, expected_inner, diag); - } - } - - /// A common error is to add an extra semicolon: - /// - /// ```compile_fail,E0308 - /// fn foo() -> usize { - /// 22; - /// } - /// ``` - /// - /// This routine checks if the final statement in a block is an - /// expression with an explicit semicolon whose type is compatible - /// with `expected_ty`. If so, it suggests removing the semicolon. - pub fn consider_removing_semicolon( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - diag: &mut Diag<'_>, - ) -> bool { - if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) { - if let StatementAsExpression::NeedsBoxing = boxed { - diag.span_suggestion_verbose( - span_semi, - "consider removing this semicolon and boxing the expression", - "", - Applicability::HasPlaceholders, - ); - } else { - diag.span_suggestion_short( - span_semi, - "remove this semicolon to return this value", - "", - Applicability::MachineApplicable, - ); - } - true - } else { - false - } - } - - pub(super) fn suggest_function_pointers( - &self, - cause: &ObligationCause<'tcx>, - span: Span, - exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, - diag: &mut Diag<'_>, - ) { - debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found); - let ty::error::ExpectedFound { expected, found } = exp_found; - let expected_inner = expected.peel_refs(); - let found_inner = found.peel_refs(); - if !expected_inner.is_fn() || !found_inner.is_fn() { - return; - } - match (&expected_inner.kind(), &found_inner.kind()) { - (ty::FnPtr(sig), ty::FnDef(did, args)) => { - let expected_sig = &(self.normalize_fn_sig)(*sig); - let found_sig = - &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args)); - - let fn_name = self.tcx.def_path_str_with_args(*did, args); - - if !self.same_type_modulo_infer(*found_sig, *expected_sig) - || !sig.is_suggestable(self.tcx, true) - || self.tcx.intrinsic(*did).is_some() - { - return; - } - - let sugg = match (expected.is_ref(), found.is_ref()) { - (true, false) => FunctionPointerSuggestion::UseRef { span, fn_name }, - (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name }, - (true, true) => { - diag.subdiagnostic(FnItemsAreDistinct); - FunctionPointerSuggestion::CastRef { span, fn_name, sig: *sig } - } - (false, false) => { - diag.subdiagnostic(FnItemsAreDistinct); - FunctionPointerSuggestion::Cast { span, fn_name, sig: *sig } - } - }; - diag.subdiagnostic(sugg); - } - (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => { - let expected_sig = - &(self.normalize_fn_sig)(self.tcx.fn_sig(*did1).instantiate(self.tcx, args1)); - let found_sig = - &(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).instantiate(self.tcx, args2)); - - if self.same_type_modulo_infer(*expected_sig, *found_sig) { - diag.subdiagnostic(FnUniqTypes); - } - - if !self.same_type_modulo_infer(*found_sig, *expected_sig) - || !found_sig.is_suggestable(self.tcx, true) - || !expected_sig.is_suggestable(self.tcx, true) - || self.tcx.intrinsic(*did1).is_some() - || self.tcx.intrinsic(*did2).is_some() - { - return; - } - - let fn_name = self.tcx.def_path_str_with_args(*did2, args2); - let sug = if found.is_ref() { - FunctionPointerSuggestion::CastBothRef { - span, - fn_name, - found_sig: *found_sig, - expected_sig: *expected_sig, - } - } else { - FunctionPointerSuggestion::CastBoth { - span, - fn_name, - found_sig: *found_sig, - expected_sig: *expected_sig, - } - }; - - diag.subdiagnostic(sug); - } - (ty::FnDef(did, args), ty::FnPtr(sig)) => { - let expected_sig = - &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args)); - let found_sig = &(self.normalize_fn_sig)(*sig); - - if !self.same_type_modulo_infer(*found_sig, *expected_sig) { - return; - } - - let fn_name = self.tcx.def_path_str_with_args(*did, args); - - let casting = if expected.is_ref() { - format!("&({fn_name} as {found_sig})") - } else { - format!("{fn_name} as {found_sig}") - }; - - diag.subdiagnostic(FnConsiderCasting { casting }); - } - _ => { - return; - } - }; - } - - pub fn should_suggest_as_ref_kind( - &self, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) -> Option<SuggestAsRefKind> { - if let (ty::Adt(exp_def, exp_args), ty::Ref(_, found_ty, _)) = - (expected.kind(), found.kind()) - { - if let ty::Adt(found_def, found_args) = *found_ty.kind() { - if exp_def == &found_def { - let have_as_ref = &[ - (sym::Option, SuggestAsRefKind::Option), - (sym::Result, SuggestAsRefKind::Result), - ]; - 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 - std::iter::zip(exp_args.types(), found_args.types()) - { - match *exp_ty.kind() { - ty::Ref(_, exp_ty, _) => { - match (exp_ty.kind(), found_ty.kind()) { - (_, ty::Param(_)) - | (_, ty::Infer(_)) - | (ty::Param(_), _) - | (ty::Infer(_), _) => {} - _ if self.same_type_modulo_infer(exp_ty, found_ty) => {} - _ => show_suggestion = false, - }; - } - ty::Param(_) | ty::Infer(_) => {} - _ => show_suggestion = false, - } - } - if show_suggestion { - return Some(*msg); - } - } - } - } - } - None - } - - // FIXME: Remove once `rustc_hir_typeck` is migrated to diagnostic structs - pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> { - match self.should_suggest_as_ref_kind(expected, found) { - Some(SuggestAsRefKind::Option) => Some( - "you can convert from `&Option<T>` to `Option<&T>` using \ - `.as_ref()`", - ), - Some(SuggestAsRefKind::Result) => Some( - "you can convert from `&Result<T, E>` to \ - `Result<&T, &E>` using `.as_ref()`", - ), - None => None, - } - } - /// Try to find code with pattern `if Some(..) = expr` - /// use a `visitor` to mark the `if` which its span contains given error span, - /// and then try to find a assignment in the `cond` part, which span is equal with error span - pub(super) fn suggest_let_for_letchains( - &self, - cause: &ObligationCause<'_>, - span: Span, - ) -> Option<TypeErrorAdditionalDiags> { - /// Find the if expression with given span - struct IfVisitor { - pub found_if: bool, - pub err_span: Span, - } - - impl<'v> Visitor<'v> for IfVisitor { - type Result = ControlFlow<()>; - fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result { - match ex.kind { - hir::ExprKind::If(cond, _, _) => { - self.found_if = true; - walk_expr(self, cond)?; - self.found_if = false; - ControlFlow::Continue(()) - } - _ => walk_expr(self, ex), - } - } - - fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { - if let hir::StmtKind::Let(LetStmt { - span, - pat: hir::Pat { .. }, - ty: None, - init: Some(_), - .. - }) = &ex.kind - && self.found_if - && span.eq(&self.err_span) - { - ControlFlow::Break(()) - } else { - walk_stmt(self, ex) - } - } - } - - self.tcx.hir().maybe_body_owned_by(cause.body_id).and_then(|body| { - IfVisitor { err_span: span, found_if: false } - .visit_body(&body) - .is_break() - .then(|| TypeErrorAdditionalDiags::AddLetForLetChains { span: span.shrink_to_lo() }) - }) - } - - /// For "one type is more general than the other" errors on closures, suggest changing the lifetime - /// of the parameters to accept all lifetimes. - pub(super) fn suggest_for_all_lifetime_closure( - &self, - span: Span, - hir: hir::Node<'_>, - exp_found: &ty::error::ExpectedFound<ty::TraitRef<'tcx>>, - diag: &mut Diag<'_>, - ) { - // 0. Extract fn_decl from hir - let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(hir::Closure { body, fn_decl, .. }), - .. - }) = hir - else { - return; - }; - let hir::Body { params, .. } = self.tcx.hir().body(*body); - - // 1. Get the args of the closure. - // 2. Assume exp_found is FnOnce / FnMut / Fn, we can extract function parameters from [1]. - let Some(expected) = exp_found.expected.args.get(1) else { - return; - }; - let Some(found) = exp_found.found.args.get(1) else { - return; - }; - let expected = expected.unpack(); - let found = found.unpack(); - // 3. Extract the tuple type from Fn trait and suggest the change. - if let GenericArgKind::Type(expected) = expected - && let GenericArgKind::Type(found) = found - && let ty::Tuple(expected) = expected.kind() - && let ty::Tuple(found) = found.kind() - && expected.len() == found.len() - { - let mut suggestion = "|".to_string(); - let mut is_first = true; - let mut has_suggestion = false; - - for (((expected, found), param_hir), arg_hir) in - expected.iter().zip(found.iter()).zip(params.iter()).zip(fn_decl.inputs.iter()) - { - if is_first { - is_first = false; - } else { - suggestion += ", "; - } - - if let ty::Ref(expected_region, _, _) = expected.kind() - && let ty::Ref(found_region, _, _) = found.kind() - && expected_region.is_bound() - && !found_region.is_bound() - && let hir::TyKind::Infer = arg_hir.kind - { - // If the expected region is late bound, the found region is not, and users are asking compiler - // to infer the type, we can suggest adding `: &_`. - if param_hir.pat.span == param_hir.ty_span { - // for `|x|`, `|_|`, `|x: impl Foo|` - let Ok(pat) = - self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span) - else { - return; - }; - suggestion += &format!("{pat}: &_"); - } else { - // for `|x: ty|`, `|_: ty|` - let Ok(pat) = - self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span) - else { - return; - }; - let Ok(ty) = self.tcx.sess.source_map().span_to_snippet(param_hir.ty_span) - else { - return; - }; - suggestion += &format!("{pat}: &{ty}"); - } - has_suggestion = true; - } else { - let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(param_hir.span) else { - return; - }; - // Otherwise, keep it as-is. - suggestion += &arg; - } - } - suggestion += "|"; - - if has_suggestion { - diag.span_suggestion_verbose( - span, - "consider specifying the type of the closure parameters", - suggestion, - Applicability::MaybeIncorrect, - ); - } - } - } -} - -impl<'tcx> TypeErrCtxt<'_, 'tcx> { - /// Be helpful when the user wrote `{... expr; }` and taking the `;` off - /// is enough to fix the error. - pub fn could_remove_semicolon( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - ) -> Option<(Span, StatementAsExpression)> { - let blk = blk.innermost_block(); - // Do not suggest if we have a tail expr. - if blk.expr.is_some() { - return None; - } - let last_stmt = blk.stmts.last()?; - let hir::StmtKind::Semi(last_expr) = last_stmt.kind else { - return None; - }; - let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(last_expr)?; - let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) { - _ if last_expr_ty.references_error() => return None, - _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => { - StatementAsExpression::CorrectType - } - ( - ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, .. }), - ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, .. }), - ) if last_def_id == exp_def_id => StatementAsExpression::CorrectType, - ( - ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, args: last_bounds, .. }), - ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, args: exp_bounds, .. }), - ) => { - debug!( - "both opaque, likely future {:?} {:?} {:?} {:?}", - last_def_id, last_bounds, exp_def_id, exp_bounds - ); - - let last_local_id = last_def_id.as_local()?; - let exp_local_id = exp_def_id.as_local()?; - - match ( - &self.tcx.hir().expect_item(last_local_id).kind, - &self.tcx.hir().expect_item(exp_local_id).kind, - ) { - ( - hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }), - hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }), - ) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| match ( - left, right, - ) { - (hir::GenericBound::Trait(tl, ml), hir::GenericBound::Trait(tr, mr)) - if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id() - && ml == mr => - { - true - } - _ => false, - }) => - { - StatementAsExpression::NeedsBoxing - } - _ => StatementAsExpression::CorrectType, - } - } - _ => return None, - }; - let span = if last_stmt.span.from_expansion() { - let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span); - self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)? - } else { - self.tcx - .sess - .source_map() - .span_extend_while_whitespace(last_expr.span) - .shrink_to_hi() - .with_hi(last_stmt.span.hi()) - }; - - Some((span, needs_box)) - } - - /// Suggest returning a local binding with a compatible type if the block - /// has no return expression. - pub fn consider_returning_binding_diag( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - ) -> Option<SuggestRemoveSemiOrReturnBinding> { - let blk = blk.innermost_block(); - // Do not suggest if we have a tail expr. - if blk.expr.is_some() { - return None; - } - let mut shadowed = FxIndexSet::default(); - let mut candidate_idents = vec![]; - let mut find_compatible_candidates = |pat: &hir::Pat<'_>| { - if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind - && let Some(pat_ty) = self - .typeck_results - .as_ref() - .and_then(|typeck_results| typeck_results.node_type_opt(*hir_id)) - { - let pat_ty = self.resolve_vars_if_possible(pat_ty); - if self.same_type_modulo_infer(pat_ty, expected_ty) - && !(pat_ty, expected_ty).references_error() - && shadowed.insert(ident.name) - { - candidate_idents.push((*ident, pat_ty)); - } - } - true - }; - - let hir = self.tcx.hir(); - for stmt in blk.stmts.iter().rev() { - let hir::StmtKind::Let(local) = &stmt.kind else { - continue; - }; - local.pat.walk(&mut find_compatible_candidates); - } - match self.tcx.parent_hir_node(blk.hir_id) { - hir::Node::Expr(hir::Expr { hir_id, .. }) => match self.tcx.parent_hir_node(*hir_id) { - hir::Node::Arm(hir::Arm { pat, .. }) => { - pat.walk(&mut find_compatible_candidates); - } - - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. }) - | hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(_, body), .. - }) - | hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)), - .. - }) - | hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(hir::Closure { body, .. }), - .. - }) => { - for param in hir.body(*body).params { - param.pat.walk(&mut find_compatible_candidates); - } - } - hir::Node::Expr(hir::Expr { - kind: - hir::ExprKind::If( - hir::Expr { kind: hir::ExprKind::Let(let_), .. }, - then_block, - _, - ), - .. - }) if then_block.hir_id == *hir_id => { - let_.pat.walk(&mut find_compatible_candidates); - } - _ => {} - }, - _ => {} - } - - match &candidate_idents[..] { - [(ident, _ty)] => { - let sm = self.tcx.sess.source_map(); - let (span, sugg) = if let Some(stmt) = blk.stmts.last() { - let stmt_span = sm.stmt_span(stmt.span, blk.span); - let sugg = if sm.is_multiline(blk.span) - && let Some(spacing) = sm.indentation_before(stmt_span) - { - format!("\n{spacing}{ident}") - } else { - format!(" {ident}") - }; - (stmt_span.shrink_to_hi(), sugg) - } else { - let sugg = if sm.is_multiline(blk.span) - && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo()) - { - format!("\n{spacing} {ident}\n{spacing}") - } else { - format!(" {ident} ") - }; - let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi(); - (sm.span_extend_while_whitespace(left_span), sugg) - }; - Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident }) - } - values if (1..3).contains(&values.len()) => { - let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>(); - Some(SuggestRemoveSemiOrReturnBinding::AddOne { spans: spans.into() }) - } - _ => None, - } - } - - pub fn consider_returning_binding( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - err: &mut Diag<'_>, - ) -> bool { - let diag = self.consider_returning_binding_diag(blk, expected_ty); - match diag { - Some(diag) => { - err.subdiagnostic(diag); - true - } - None => false, - } - } -} diff --git a/compiler/rustc_infer/src/error_reporting/mod.rs b/compiler/rustc_infer/src/error_reporting/mod.rs deleted file mode 100644 index 132485ec661..00000000000 --- a/compiler/rustc_infer/src/error_reporting/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod infer; diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 2ce712e0bff..1a5c0137219 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1,28 +1,5 @@ -use hir::GenericParamKind; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{ - codes::*, Applicability, Diag, DiagMessage, DiagStyledString, EmissionGuarantee, IntoDiagArg, - MultiSpan, SubdiagMessageOp, Subdiagnostic, -}; -use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{walk_ty, Visitor}; -use rustc_hir::FnRetTy; -use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_middle::ty::print::TraitRefPrintOnlyTraitPath; -use rustc_middle::ty::{Binder, FnSig, Region, Ty, TyCtxt}; -use rustc_span::symbol::kw; -use rustc_span::Symbol; -use rustc_span::{symbol::Ident, BytePos, Span}; - -use crate::error_reporting::infer::nice_region_error::placeholder_error::Highlighted; -use crate::error_reporting::infer::ObligationCauseAsDiagArg; -use crate::fluent_generated as fluent; -use crate::infer::need_type_info::UnderspecifiedArgKind; - -use std::path::PathBuf; - -pub mod note_and_explain; +use rustc_macros::Diagnostic; +use rustc_span::Span; #[derive(Diagnostic)] #[diag(infer_opaque_hidden_type)] @@ -35,1599 +12,3 @@ pub struct OpaqueHiddenTypeDiag { #[note(infer_hidden_type)] pub hidden_type: Span, } - -#[derive(Diagnostic)] -#[diag(infer_type_annotations_needed, code = E0282)] -pub struct AnnotationRequired<'a> { - #[primary_span] - pub span: Span, - pub source_kind: &'static str, - pub source_name: &'a str, - #[label] - pub failure_span: Option<Span>, - #[subdiagnostic] - pub bad_label: Option<InferenceBadError<'a>>, - #[subdiagnostic] - pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, - #[subdiagnostic] - pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, - #[note(infer_full_type_written)] - pub was_written: Option<()>, - pub path: PathBuf, -} - -// Copy of `AnnotationRequired` for E0283 -#[derive(Diagnostic)] -#[diag(infer_type_annotations_needed, code = E0283)] -pub struct AmbiguousImpl<'a> { - #[primary_span] - pub span: Span, - pub source_kind: &'static str, - pub source_name: &'a str, - #[label] - pub failure_span: Option<Span>, - #[subdiagnostic] - pub bad_label: Option<InferenceBadError<'a>>, - #[subdiagnostic] - pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, - #[subdiagnostic] - pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, - #[note(infer_full_type_written)] - pub was_written: Option<()>, - pub path: PathBuf, -} - -// Copy of `AnnotationRequired` for E0284 -#[derive(Diagnostic)] -#[diag(infer_type_annotations_needed, code = E0284)] -pub struct AmbiguousReturn<'a> { - #[primary_span] - pub span: Span, - pub source_kind: &'static str, - pub source_name: &'a str, - #[label] - pub failure_span: Option<Span>, - #[subdiagnostic] - pub bad_label: Option<InferenceBadError<'a>>, - #[subdiagnostic] - pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, - #[subdiagnostic] - pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, - #[note(infer_full_type_written)] - pub was_written: Option<()>, - pub path: PathBuf, -} - -// Used when a better one isn't available -#[derive(Subdiagnostic)] -#[label(infer_label_bad)] -pub struct InferenceBadError<'a> { - #[primary_span] - pub span: Span, - pub bad_kind: &'static str, - pub prefix_kind: UnderspecifiedArgKind, - pub has_parent: bool, - pub prefix: &'a str, - pub parent_prefix: &'a str, - pub parent_name: String, - pub name: String, -} - -#[derive(Subdiagnostic)] -pub enum SourceKindSubdiag<'a> { - #[suggestion( - infer_source_kind_subdiag_let, - style = "verbose", - code = ": {type_name}", - applicability = "has-placeholders" - )] - LetLike { - #[primary_span] - span: Span, - name: String, - type_name: String, - kind: &'static str, - x_kind: &'static str, - prefix_kind: UnderspecifiedArgKind, - prefix: &'a str, - arg_name: String, - }, - #[label(infer_source_kind_subdiag_generic_label)] - GenericLabel { - #[primary_span] - span: Span, - is_type: bool, - param_name: String, - parent_exists: bool, - parent_prefix: String, - parent_name: String, - }, - #[suggestion( - infer_source_kind_subdiag_generic_suggestion, - style = "verbose", - code = "::<{args}>", - applicability = "has-placeholders" - )] - GenericSuggestion { - #[primary_span] - span: Span, - arg_count: usize, - args: String, - }, -} - -#[derive(Subdiagnostic)] -pub enum SourceKindMultiSuggestion<'a> { - #[multipart_suggestion( - infer_source_kind_fully_qualified, - style = "verbose", - applicability = "has-placeholders" - )] - FullyQualified { - #[suggestion_part(code = "{def_path}({adjustment}")] - span_lo: Span, - #[suggestion_part(code = "{successor_pos}")] - span_hi: Span, - def_path: String, - adjustment: &'a str, - successor_pos: &'a str, - }, - #[multipart_suggestion( - infer_source_kind_closure_return, - style = "verbose", - applicability = "has-placeholders" - )] - ClosureReturn { - #[suggestion_part(code = "{start_span_code}")] - start_span: Span, - start_span_code: String, - #[suggestion_part(code = " }}")] - end_span: Option<Span>, - }, -} - -impl<'a> SourceKindMultiSuggestion<'a> { - pub fn new_fully_qualified( - span: Span, - def_path: String, - adjustment: &'a str, - successor: (&'a str, BytePos), - ) -> Self { - Self::FullyQualified { - span_lo: span.shrink_to_lo(), - span_hi: span.shrink_to_hi().with_hi(successor.1), - def_path, - adjustment, - successor_pos: successor.0, - } - } - - pub fn new_closure_return( - ty_info: String, - data: &'a FnRetTy<'a>, - should_wrap_expr: Option<Span>, - ) -> Self { - let arrow = match data { - FnRetTy::DefaultReturn(_) => " -> ", - _ => "", - }; - let (start_span, start_span_code, end_span) = match should_wrap_expr { - Some(end_span) => (data.span(), format!("{arrow}{ty_info} {{"), Some(end_span)), - None => (data.span(), format!("{arrow}{ty_info}"), None), - }; - Self::ClosureReturn { start_span, start_span_code, end_span } - } -} - -pub enum RegionOriginNote<'a> { - Plain { - span: Span, - msg: DiagMessage, - }, - WithName { - span: Span, - msg: DiagMessage, - name: &'a str, - continues: bool, - }, - WithRequirement { - span: Span, - requirement: ObligationCauseAsDiagArg<'a>, - expected_found: Option<(DiagStyledString, DiagStyledString)>, - }, -} - -impl Subdiagnostic for RegionOriginNote<'_> { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { - let mut label_or_note = |span, msg: DiagMessage| { - let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count(); - let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count(); - let span_is_primary = diag.span.primary_spans().iter().all(|&sp| sp == span); - if span_is_primary && sub_count == 0 && expanded_sub_count == 0 { - diag.span_label(span, msg); - } else if span_is_primary && expanded_sub_count == 0 { - diag.note(msg); - } else { - diag.span_note(span, msg); - } - }; - match self { - RegionOriginNote::Plain { span, msg } => { - label_or_note(span, msg); - } - RegionOriginNote::WithName { span, msg, name, continues } => { - label_or_note(span, msg); - diag.arg("name", name); - diag.arg("continues", continues); - } - RegionOriginNote::WithRequirement { - span, - requirement, - expected_found: Some((expected, found)), - } => { - label_or_note(span, fluent::infer_subtype); - diag.arg("requirement", requirement); - - diag.note_expected_found(&"", expected, &"", found); - } - RegionOriginNote::WithRequirement { span, requirement, expected_found: None } => { - // 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(span, fluent::infer_subtype_2); - diag.arg("requirement", requirement); - } - }; - } -} - -pub enum LifetimeMismatchLabels { - InRet { - param_span: Span, - ret_span: Span, - span: Span, - label_var1: Option<Ident>, - }, - Normal { - hir_equal: bool, - ty_sup: Span, - ty_sub: Span, - span: Span, - sup: Option<Ident>, - sub: Option<Ident>, - }, -} - -impl Subdiagnostic for LifetimeMismatchLabels { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { - match self { - LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => { - diag.span_label(param_span, fluent::infer_declared_different); - diag.span_label(ret_span, fluent::infer_nothing); - diag.span_label(span, fluent::infer_data_returned); - diag.arg("label_var1_exists", label_var1.is_some()); - diag.arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default()); - } - LifetimeMismatchLabels::Normal { - hir_equal, - ty_sup, - ty_sub, - span, - sup: label_var1, - sub: label_var2, - } => { - if hir_equal { - diag.span_label(ty_sup, fluent::infer_declared_multiple); - diag.span_label(ty_sub, fluent::infer_nothing); - diag.span_label(span, fluent::infer_data_lifetime_flow); - } else { - diag.span_label(ty_sup, fluent::infer_types_declared_different); - diag.span_label(ty_sub, fluent::infer_nothing); - diag.span_label(span, fluent::infer_data_flows); - diag.arg("label_var1_exists", label_var1.is_some()); - diag.arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default()); - diag.arg("label_var2_exists", label_var2.is_some()); - diag.arg("label_var2", label_var2.map(|x| x.to_string()).unwrap_or_default()); - } - } - } - } -} - -pub struct AddLifetimeParamsSuggestion<'a> { - pub tcx: TyCtxt<'a>, - pub generic_param_scope: LocalDefId, - pub sub: Region<'a>, - pub ty_sup: &'a hir::Ty<'a>, - pub ty_sub: &'a hir::Ty<'a>, - pub add_note: bool, -} - -impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { - let mut mk_suggestion = || { - let Some(anon_reg) = self.tcx.is_suitable_region(self.generic_param_scope, self.sub) - else { - return false; - }; - - let node = self.tcx.hir_node_by_def_id(anon_reg.def_id); - let is_impl = matches!(&node, hir::Node::ImplItem(_)); - let (generics, parent_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, - match self.tcx.parent_hir_node(self.tcx.local_def_id_to_hir_id(anon_reg.def_id)) - { - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Trait(_, _, ref generics, ..), - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { ref generics, .. }), - .. - }) => Some(generics), - _ => None, - }, - ), - _ => 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 mut default = "'a".to_string(); - if let Some(parent_generics) = parent_generics { - let used: FxHashSet<_> = parent_generics - .params - .iter() - .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) - .map(|p| p.name.ident().name) - .filter(|i| *i != kw::UnderscoreLifetime) - .map(|l| l.to_string()) - .collect(); - if let Some(lt) = - ('a'..='z').map(|it| format!("'{it}")).find(|it| !used.contains(it)) - { - // We want a lifetime that *isn't* present in the `trait` or `impl` that assoc - // `fn` belongs to. We could suggest reusing one of their lifetimes, but it is - // likely to be an over-constraining lifetime requirement, so we always add a - // lifetime to the `fn`. - default = lt; - } - } - let suggestion_param_name = - suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| default); - - struct ImplicitLifetimeFinder { - suggestions: Vec<(Span, String)>, - suggestion_param_name: String, - } - - impl<'v> Visitor<'v> for ImplicitLifetimeFinder { - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { - let make_suggestion = |ident: Ident| { - if ident.name == kw::Empty && ident.span.is_empty() { - format!("{}, ", self.suggestion_param_name) - } else if ident.name == kw::UnderscoreLifetime && ident.span.is_empty() { - format!("{} ", self.suggestion_param_name) - } else { - self.suggestion_param_name.clone() - } - }; - match ty.kind { - hir::TyKind::Path(hir::QPath::Resolved(_, path)) => { - for segment in path.segments { - if let Some(args) = segment.args { - if args.args.iter().all(|arg| { - matches!( - arg, - hir::GenericArg::Lifetime(lifetime) - if lifetime.ident.name == kw::Empty - ) - }) { - self.suggestions.push(( - segment.ident.span.shrink_to_hi(), - format!( - "<{}>", - args.args - .iter() - .map(|_| self.suggestion_param_name.clone()) - .collect::<Vec<_>>() - .join(", ") - ), - )); - } else { - for arg in args.args { - if let hir::GenericArg::Lifetime(lifetime) = arg - && lifetime.is_anonymous() - { - self.suggestions.push(( - lifetime.ident.span, - make_suggestion(lifetime.ident), - )); - } - } - } - } - } - } - hir::TyKind::Ref(lifetime, ..) if lifetime.is_anonymous() => { - self.suggestions - .push((lifetime.ident.span, make_suggestion(lifetime.ident))); - } - _ => {} - } - walk_ty(self, ty); - } - } - let mut visitor = ImplicitLifetimeFinder { - suggestions: vec![], - suggestion_param_name: suggestion_param_name.clone(), - }; - if let Some(fn_decl) = node.fn_decl() - && let hir::FnRetTy::Return(ty) = fn_decl.output - { - visitor.visit_ty(ty); - } - if visitor.suggestions.is_empty() { - // Do not suggest constraining the `&self` param, but rather the return type. - // If that is wrong (because it is not sufficient), a follow up error will tell the - // user to fix it. This way we lower the chances of *over* constraining, but still - // get the cake of "correctly" contrained in two steps. - visitor.visit_ty(self.ty_sup); - } - visitor.visit_ty(self.ty_sub); - if visitor.suggestions.is_empty() { - return false; - } - 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}>")) - }; - - visitor.suggestions.push(new_param_suggestion); - } - diag.multipart_suggestion_verbose( - fluent::infer_lifetime_param_suggestion, - visitor.suggestions, - Applicability::MaybeIncorrect, - ); - diag.arg("is_impl", is_impl); - diag.arg("is_reuse", !introduce_new); - - true - }; - if mk_suggestion() && self.add_note { - diag.note(fluent::infer_lifetime_param_suggestion_elided); - } - } -} - -#[derive(Diagnostic)] -#[diag(infer_lifetime_mismatch, code = E0623)] -pub struct LifetimeMismatch<'a> { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub labels: LifetimeMismatchLabels, - #[subdiagnostic] - pub suggestion: AddLifetimeParamsSuggestion<'a>, -} - -pub struct IntroducesStaticBecauseUnmetLifetimeReq { - pub unmet_requirements: MultiSpan, - pub binding_span: Span, -} - -impl Subdiagnostic for IntroducesStaticBecauseUnmetLifetimeReq { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - mut self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { - self.unmet_requirements - .push_span_label(self.binding_span, fluent::infer_msl_introduces_static); - diag.span_note(self.unmet_requirements, fluent::infer_msl_unmet_req); - } -} - -// FIXME(#100717): replace with a `Option<Span>` when subdiagnostic supports that -#[derive(Subdiagnostic)] -pub enum DoesNotOutliveStaticFromImpl { - #[note(infer_does_not_outlive_static_from_impl)] - Spanned { - #[primary_span] - span: Span, - }, - #[note(infer_does_not_outlive_static_from_impl)] - Unspanned, -} - -#[derive(Subdiagnostic)] -pub enum ImplicitStaticLifetimeSubdiag { - #[note(infer_implicit_static_lifetime_note)] - Note { - #[primary_span] - span: Span, - }, - #[suggestion( - infer_implicit_static_lifetime_suggestion, - style = "verbose", - code = " + '_", - applicability = "maybe-incorrect" - )] - Sugg { - #[primary_span] - span: Span, - }, -} - -#[derive(Diagnostic)] -#[diag(infer_mismatched_static_lifetime)] -pub struct MismatchedStaticLifetime<'a> { - #[primary_span] - pub cause_span: Span, - #[subdiagnostic] - pub unmet_lifetime_reqs: IntroducesStaticBecauseUnmetLifetimeReq, - #[subdiagnostic] - pub expl: Option<note_and_explain::RegionExplanation<'a>>, - #[subdiagnostic] - pub does_not_outlive_static_from_impl: DoesNotOutliveStaticFromImpl, - #[subdiagnostic] - pub implicit_static_lifetimes: Vec<ImplicitStaticLifetimeSubdiag>, -} - -#[derive(Diagnostic)] -pub enum ExplicitLifetimeRequired<'a> { - #[diag(infer_explicit_lifetime_required_with_ident, code = E0621)] - WithIdent { - #[primary_span] - #[label] - span: Span, - simple_ident: Ident, - named: String, - #[suggestion( - infer_explicit_lifetime_required_sugg_with_ident, - code = "{new_ty}", - applicability = "unspecified" - )] - new_ty_span: Span, - #[skip_arg] - new_ty: Ty<'a>, - }, - #[diag(infer_explicit_lifetime_required_with_param_type, code = E0621)] - WithParamType { - #[primary_span] - #[label] - span: Span, - named: String, - #[suggestion( - infer_explicit_lifetime_required_sugg_with_param_type, - code = "{new_ty}", - applicability = "unspecified" - )] - new_ty_span: Span, - #[skip_arg] - new_ty: Ty<'a>, - }, -} - -pub enum TyOrSig<'tcx> { - Ty(Highlighted<'tcx, Ty<'tcx>>), - ClosureSig(Highlighted<'tcx, Binder<'tcx, FnSig<'tcx>>>), -} - -impl IntoDiagArg for TyOrSig<'_> { - fn into_diag_arg(self) -> rustc_errors::DiagArgValue { - match self { - TyOrSig::Ty(ty) => ty.into_diag_arg(), - TyOrSig::ClosureSig(sig) => sig.into_diag_arg(), - } - } -} - -#[derive(Subdiagnostic)] -pub enum ActualImplExplNotes<'tcx> { - #[note(infer_actual_impl_expl_expected_signature_two)] - ExpectedSignatureTwo { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - lifetime_2: usize, - }, - #[note(infer_actual_impl_expl_expected_signature_any)] - ExpectedSignatureAny { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - }, - #[note(infer_actual_impl_expl_expected_signature_some)] - ExpectedSignatureSome { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - }, - #[note(infer_actual_impl_expl_expected_signature_nothing)] - ExpectedSignatureNothing { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - }, - #[note(infer_actual_impl_expl_expected_passive_two)] - ExpectedPassiveTwo { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - lifetime_2: usize, - }, - #[note(infer_actual_impl_expl_expected_passive_any)] - ExpectedPassiveAny { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - }, - #[note(infer_actual_impl_expl_expected_passive_some)] - ExpectedPassiveSome { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - }, - #[note(infer_actual_impl_expl_expected_passive_nothing)] - ExpectedPassiveNothing { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - }, - #[note(infer_actual_impl_expl_expected_other_two)] - ExpectedOtherTwo { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - lifetime_2: usize, - }, - #[note(infer_actual_impl_expl_expected_other_any)] - ExpectedOtherAny { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - }, - #[note(infer_actual_impl_expl_expected_other_some)] - ExpectedOtherSome { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - }, - #[note(infer_actual_impl_expl_expected_other_nothing)] - ExpectedOtherNothing { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - }, - #[note(infer_actual_impl_expl_but_actually_implements_trait)] - ButActuallyImplementsTrait { - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - has_lifetime: bool, - lifetime: usize, - }, - #[note(infer_actual_impl_expl_but_actually_implemented_for_ty)] - ButActuallyImplementedForTy { - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - has_lifetime: bool, - lifetime: usize, - ty: String, - }, - #[note(infer_actual_impl_expl_but_actually_ty_implements)] - ButActuallyTyImplements { - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - has_lifetime: bool, - lifetime: usize, - ty: String, - }, -} - -pub enum ActualImplExpectedKind { - Signature, - Passive, - Other, -} - -pub enum ActualImplExpectedLifetimeKind { - Two, - Any, - Some, - Nothing, -} - -impl<'tcx> ActualImplExplNotes<'tcx> { - pub fn new_expected( - kind: ActualImplExpectedKind, - lt_kind: ActualImplExpectedLifetimeKind, - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - lifetime_2: usize, - ) -> Self { - match (kind, lt_kind) { - (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Two) => { - Self::ExpectedSignatureTwo { - leading_ellipsis, - ty_or_sig, - trait_path, - lifetime_1, - lifetime_2, - } - } - (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Any) => { - Self::ExpectedSignatureAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } - } - (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Some) => { - Self::ExpectedSignatureSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } - } - (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Nothing) => { - Self::ExpectedSignatureNothing { leading_ellipsis, ty_or_sig, trait_path } - } - (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Two) => { - Self::ExpectedPassiveTwo { - leading_ellipsis, - ty_or_sig, - trait_path, - lifetime_1, - lifetime_2, - } - } - (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Any) => { - Self::ExpectedPassiveAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } - } - (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Some) => { - Self::ExpectedPassiveSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } - } - (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Nothing) => { - Self::ExpectedPassiveNothing { leading_ellipsis, ty_or_sig, trait_path } - } - (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Two) => { - Self::ExpectedOtherTwo { - leading_ellipsis, - ty_or_sig, - trait_path, - lifetime_1, - lifetime_2, - } - } - (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Any) => { - Self::ExpectedOtherAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } - } - (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Some) => { - Self::ExpectedOtherSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } - } - (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Nothing) => { - Self::ExpectedOtherNothing { leading_ellipsis, ty_or_sig, trait_path } - } - } - } -} - -#[derive(Diagnostic)] -#[diag(infer_trait_placeholder_mismatch)] -pub struct TraitPlaceholderMismatch<'tcx> { - #[primary_span] - pub span: Span, - #[label(infer_label_satisfy)] - pub satisfy_span: Option<Span>, - #[label(infer_label_where)] - pub where_span: Option<Span>, - #[label(infer_label_dup)] - pub dup_span: Option<Span>, - pub def_id: String, - pub trait_def_id: String, - - #[subdiagnostic] - pub actual_impl_expl_notes: Vec<ActualImplExplNotes<'tcx>>, -} - -pub struct ConsiderBorrowingParamHelp { - pub spans: Vec<Span>, -} - -impl Subdiagnostic for ConsiderBorrowingParamHelp { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - f: &F, - ) { - let mut type_param_span: MultiSpan = self.spans.clone().into(); - for &span in &self.spans { - // Seems like we can't call f() here as Into<DiagMessage> is required - type_param_span.push_span_label(span, fluent::infer_tid_consider_borrowing); - } - let msg = f(diag, fluent::infer_tid_param_help.into()); - diag.span_help(type_param_span, msg); - } -} - -#[derive(Subdiagnostic)] -#[help(infer_tid_rel_help)] -pub struct RelationshipHelp; - -#[derive(Diagnostic)] -#[diag(infer_trait_impl_diff)] -pub struct TraitImplDiff { - #[primary_span] - #[label(infer_found)] - pub sp: Span, - #[label(infer_expected)] - pub trait_sp: Span, - #[note(infer_expected_found)] - pub note: (), - #[subdiagnostic] - pub param_help: ConsiderBorrowingParamHelp, - #[subdiagnostic] - // Seems like subdiagnostics are always pushed to the end, so this one - // also has to be a subdiagnostic to maintain order. - pub rel_help: Option<RelationshipHelp>, - pub expected: String, - pub found: String, -} - -pub struct DynTraitConstraintSuggestion { - pub span: Span, - pub ident: Ident, -} - -impl Subdiagnostic for DynTraitConstraintSuggestion { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - f: &F, - ) { - let mut multi_span: MultiSpan = vec![self.span].into(); - multi_span.push_span_label(self.span, fluent::infer_dtcs_has_lifetime_req_label); - multi_span.push_span_label(self.ident.span, fluent::infer_dtcs_introduces_requirement); - let msg = f(diag, fluent::infer_dtcs_has_req_note.into()); - diag.span_note(multi_span, msg); - let msg = f(diag, fluent::infer_dtcs_suggestion.into()); - diag.span_suggestion_verbose( - self.span.shrink_to_hi(), - msg, - " + '_", - Applicability::MaybeIncorrect, - ); - } -} - -#[derive(Diagnostic)] -#[diag(infer_but_calling_introduces, code = E0772)] -pub struct ButCallingIntroduces { - #[label(infer_label1)] - pub param_ty_span: Span, - #[primary_span] - #[label(infer_label2)] - pub cause_span: Span, - - pub has_param_name: bool, - pub param_name: String, - pub has_lifetime: bool, - pub lifetime: String, - pub assoc_item: Symbol, - pub has_impl_path: bool, - pub impl_path: String, -} - -pub struct ReqIntroducedLocations { - pub span: MultiSpan, - pub spans: Vec<Span>, - pub fn_decl_span: Span, - pub cause_span: Span, - pub add_label: bool, -} - -impl Subdiagnostic for ReqIntroducedLocations { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - mut self, - diag: &mut Diag<'_, G>, - f: &F, - ) { - for sp in self.spans { - self.span.push_span_label(sp, fluent::infer_ril_introduced_here); - } - - if self.add_label { - self.span.push_span_label(self.fn_decl_span, fluent::infer_ril_introduced_by); - } - self.span.push_span_label(self.cause_span, fluent::infer_ril_because_of); - let msg = f(diag, fluent::infer_ril_static_introduced_by.into()); - diag.span_note(self.span, msg); - } -} - -pub struct MoreTargeted { - pub ident: Symbol, -} - -impl Subdiagnostic for MoreTargeted { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { - diag.code(E0772); - diag.primary_message(fluent::infer_more_targeted); - diag.arg("ident", self.ident); - } -} - -#[derive(Diagnostic)] -#[diag(infer_but_needs_to_satisfy, code = E0759)] -pub struct ButNeedsToSatisfy { - #[primary_span] - pub sp: Span, - #[label(infer_influencer)] - pub influencer_point: Span, - #[label(infer_used_here)] - pub spans: Vec<Span>, - #[label(infer_require)] - pub require_span_as_label: Option<Span>, - #[note(infer_require)] - pub require_span_as_note: Option<Span>, - #[note(infer_introduced_by_bound)] - pub bound: Option<Span>, - - #[subdiagnostic] - pub req_introduces_loc: Option<ReqIntroducedLocations>, - - pub has_param_name: bool, - pub param_name: String, - pub spans_empty: bool, - pub has_lifetime: bool, - pub lifetime: String, -} - -#[derive(Diagnostic)] -#[diag(infer_outlives_content, code = E0312)] -pub struct OutlivesContent<'a> { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub notes: Vec<note_and_explain::RegionExplanation<'a>>, -} - -#[derive(Diagnostic)] -#[diag(infer_outlives_bound, code = E0476)] -pub struct OutlivesBound<'a> { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub notes: Vec<note_and_explain::RegionExplanation<'a>>, -} - -#[derive(Diagnostic)] -#[diag(infer_fulfill_req_lifetime, code = E0477)] -pub struct FulfillReqLifetime<'a> { - #[primary_span] - pub span: Span, - pub ty: Ty<'a>, - #[subdiagnostic] - pub note: Option<note_and_explain::RegionExplanation<'a>>, -} - -#[derive(Diagnostic)] -#[diag(infer_lf_bound_not_satisfied, code = E0478)] -pub struct LfBoundNotSatisfied<'a> { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub notes: Vec<note_and_explain::RegionExplanation<'a>>, -} - -#[derive(Diagnostic)] -#[diag(infer_ref_longer_than_data, code = E0491)] -pub struct RefLongerThanData<'a> { - #[primary_span] - pub span: Span, - pub ty: Ty<'a>, - #[subdiagnostic] - pub notes: Vec<note_and_explain::RegionExplanation<'a>>, -} - -#[derive(Subdiagnostic)] -pub enum WhereClauseSuggestions { - #[suggestion( - infer_where_remove, - code = "", - applicability = "machine-applicable", - style = "verbose" - )] - Remove { - #[primary_span] - span: Span, - }, - #[suggestion( - infer_where_copy_predicates, - code = "{space}where {trait_predicates}", - applicability = "machine-applicable", - style = "verbose" - )] - CopyPredicates { - #[primary_span] - span: Span, - space: &'static str, - trait_predicates: String, - }, -} - -#[derive(Subdiagnostic)] -pub enum SuggestRemoveSemiOrReturnBinding { - #[multipart_suggestion(infer_srs_remove_and_box, applicability = "machine-applicable")] - RemoveAndBox { - #[suggestion_part(code = "Box::new(")] - first_lo: Span, - #[suggestion_part(code = ")")] - first_hi: Span, - #[suggestion_part(code = "Box::new(")] - second_lo: Span, - #[suggestion_part(code = ")")] - second_hi: Span, - #[suggestion_part(code = "")] - sp: Span, - }, - #[suggestion( - infer_srs_remove, - style = "short", - code = "", - applicability = "machine-applicable" - )] - Remove { - #[primary_span] - sp: Span, - }, - #[suggestion( - infer_srs_add, - style = "verbose", - code = "{code}", - applicability = "maybe-incorrect" - )] - Add { - #[primary_span] - sp: Span, - code: String, - ident: Ident, - }, - #[note(infer_srs_add_one)] - AddOne { - #[primary_span] - spans: MultiSpan, - }, -} - -#[derive(Subdiagnostic)] -pub enum ConsiderAddingAwait { - #[help(infer_await_both_futures)] - BothFuturesHelp, - #[multipart_suggestion(infer_await_both_futures, applicability = "maybe-incorrect")] - BothFuturesSugg { - #[suggestion_part(code = ".await")] - first: Span, - #[suggestion_part(code = ".await")] - second: Span, - }, - #[suggestion( - infer_await_future, - code = ".await", - style = "verbose", - applicability = "maybe-incorrect" - )] - FutureSugg { - #[primary_span] - span: Span, - }, - #[note(infer_await_note)] - FutureSuggNote { - #[primary_span] - span: Span, - }, - #[multipart_suggestion( - infer_await_future, - style = "verbose", - applicability = "maybe-incorrect" - )] - FutureSuggMultiple { - #[suggestion_part(code = ".await")] - spans: Vec<Span>, - }, -} - -#[derive(Diagnostic)] -pub enum PlaceholderRelationLfNotSatisfied { - #[diag(infer_lf_bound_not_satisfied)] - HasBoth { - #[primary_span] - span: Span, - #[note(infer_prlf_defined_with_sub)] - sub_span: Span, - #[note(infer_prlf_must_outlive_with_sup)] - sup_span: Span, - sub_symbol: Symbol, - sup_symbol: Symbol, - #[note(infer_prlf_known_limitation)] - note: (), - }, - #[diag(infer_lf_bound_not_satisfied)] - HasSub { - #[primary_span] - span: Span, - #[note(infer_prlf_defined_with_sub)] - sub_span: Span, - #[note(infer_prlf_must_outlive_without_sup)] - sup_span: Span, - sub_symbol: Symbol, - #[note(infer_prlf_known_limitation)] - note: (), - }, - #[diag(infer_lf_bound_not_satisfied)] - HasSup { - #[primary_span] - span: Span, - #[note(infer_prlf_defined_without_sub)] - sub_span: Span, - #[note(infer_prlf_must_outlive_with_sup)] - sup_span: Span, - sup_symbol: Symbol, - #[note(infer_prlf_known_limitation)] - note: (), - }, - #[diag(infer_lf_bound_not_satisfied)] - HasNone { - #[primary_span] - span: Span, - #[note(infer_prlf_defined_without_sub)] - sub_span: Span, - #[note(infer_prlf_must_outlive_without_sup)] - sup_span: Span, - #[note(infer_prlf_known_limitation)] - note: (), - }, - #[diag(infer_lf_bound_not_satisfied)] - OnlyPrimarySpan { - #[primary_span] - span: Span, - #[note(infer_prlf_known_limitation)] - note: (), - }, -} - -#[derive(Diagnostic)] -#[diag(infer_opaque_captures_lifetime, code = E0700)] -pub struct OpaqueCapturesLifetime<'tcx> { - #[primary_span] - pub span: Span, - #[label] - pub opaque_ty_span: Span, - pub opaque_ty: Ty<'tcx>, -} - -#[derive(Subdiagnostic)] -pub enum FunctionPointerSuggestion<'a> { - #[suggestion( - infer_fps_use_ref, - code = "&{fn_name}", - style = "verbose", - applicability = "maybe-incorrect" - )] - UseRef { - #[primary_span] - span: Span, - #[skip_arg] - fn_name: String, - }, - #[suggestion( - infer_fps_remove_ref, - code = "{fn_name}", - style = "verbose", - applicability = "maybe-incorrect" - )] - RemoveRef { - #[primary_span] - span: Span, - #[skip_arg] - fn_name: String, - }, - #[suggestion( - infer_fps_cast, - code = "&({fn_name} as {sig})", - style = "verbose", - applicability = "maybe-incorrect" - )] - CastRef { - #[primary_span] - span: Span, - #[skip_arg] - fn_name: String, - #[skip_arg] - sig: Binder<'a, FnSig<'a>>, - }, - #[suggestion( - infer_fps_cast, - code = "{fn_name} as {sig}", - style = "verbose", - applicability = "maybe-incorrect" - )] - Cast { - #[primary_span] - span: Span, - #[skip_arg] - fn_name: String, - #[skip_arg] - sig: Binder<'a, FnSig<'a>>, - }, - #[suggestion( - infer_fps_cast_both, - code = "{fn_name} as {found_sig}", - style = "hidden", - applicability = "maybe-incorrect" - )] - CastBoth { - #[primary_span] - span: Span, - #[skip_arg] - fn_name: String, - #[skip_arg] - found_sig: Binder<'a, FnSig<'a>>, - expected_sig: Binder<'a, FnSig<'a>>, - }, - #[suggestion( - infer_fps_cast_both, - code = "&({fn_name} as {found_sig})", - style = "hidden", - applicability = "maybe-incorrect" - )] - CastBothRef { - #[primary_span] - span: Span, - #[skip_arg] - fn_name: String, - #[skip_arg] - found_sig: Binder<'a, FnSig<'a>>, - expected_sig: Binder<'a, FnSig<'a>>, - }, -} - -#[derive(Subdiagnostic)] -#[note(infer_fps_items_are_distinct)] -pub struct FnItemsAreDistinct; - -#[derive(Subdiagnostic)] -#[note(infer_fn_uniq_types)] -pub struct FnUniqTypes; - -#[derive(Subdiagnostic)] -#[help(infer_fn_consider_casting)] -pub struct FnConsiderCasting { - pub casting: String, -} - -#[derive(Subdiagnostic)] -pub enum SuggestAccessingField<'a> { - #[suggestion( - infer_suggest_accessing_field, - code = "{snippet}.{name}", - applicability = "maybe-incorrect" - )] - Safe { - #[primary_span] - span: Span, - snippet: String, - name: Symbol, - ty: Ty<'a>, - }, - #[suggestion( - infer_suggest_accessing_field, - code = "unsafe {{ {snippet}.{name} }}", - applicability = "maybe-incorrect" - )] - Unsafe { - #[primary_span] - span: Span, - snippet: String, - name: Symbol, - ty: Ty<'a>, - }, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion(infer_stp_wrap_one, applicability = "maybe-incorrect")] -pub struct SuggestTuplePatternOne { - pub variant: String, - #[suggestion_part(code = "{variant}(")] - pub span_low: Span, - #[suggestion_part(code = ")")] - pub span_high: Span, -} - -pub struct SuggestTuplePatternMany { - pub path: String, - pub cause_span: Span, - pub compatible_variants: Vec<String>, -} - -impl Subdiagnostic for SuggestTuplePatternMany { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - f: &F, - ) { - diag.arg("path", self.path); - let message = f(diag, crate::fluent_generated::infer_stp_wrap_many.into()); - diag.multipart_suggestions( - message, - self.compatible_variants.into_iter().map(|variant| { - vec![ - (self.cause_span.shrink_to_lo(), format!("{variant}(")), - (self.cause_span.shrink_to_hi(), ")".to_string()), - ] - }), - rustc_errors::Applicability::MaybeIncorrect, - ); - } -} - -#[derive(Subdiagnostic)] -pub enum TypeErrorAdditionalDiags { - #[suggestion( - infer_meant_byte_literal, - code = "b'{code}'", - applicability = "machine-applicable" - )] - MeantByteLiteral { - #[primary_span] - span: Span, - code: String, - }, - #[suggestion( - infer_meant_char_literal, - code = "'{code}'", - applicability = "machine-applicable" - )] - MeantCharLiteral { - #[primary_span] - span: Span, - code: String, - }, - #[multipart_suggestion(infer_meant_str_literal, applicability = "machine-applicable")] - MeantStrLiteral { - #[suggestion_part(code = "\"")] - start: Span, - #[suggestion_part(code = "\"")] - end: Span, - }, - #[suggestion( - infer_consider_specifying_length, - code = "{length}", - applicability = "maybe-incorrect" - )] - ConsiderSpecifyingLength { - #[primary_span] - span: Span, - length: u64, - }, - #[note(infer_try_cannot_convert)] - TryCannotConvert { found: String, expected: String }, - #[suggestion(infer_tuple_trailing_comma, code = ",", applicability = "machine-applicable")] - TupleOnlyComma { - #[primary_span] - span: Span, - }, - #[multipart_suggestion(infer_tuple_trailing_comma, applicability = "machine-applicable")] - TupleAlsoParentheses { - #[suggestion_part(code = "(")] - span_low: Span, - #[suggestion_part(code = ",)")] - span_high: Span, - }, - #[suggestion( - infer_suggest_add_let_for_letchains, - style = "verbose", - applicability = "machine-applicable", - code = "let " - )] - AddLetForLetChains { - #[primary_span] - span: Span, - }, -} - -#[derive(Diagnostic)] -pub enum ObligationCauseFailureCode { - #[diag(infer_oc_method_compat, code = E0308)] - MethodCompat { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_type_compat, code = E0308)] - TypeCompat { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_const_compat, code = E0308)] - ConstCompat { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_try_compat, code = E0308)] - TryCompat { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_match_compat, code = E0308)] - MatchCompat { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_if_else_different, code = E0308)] - IfElseDifferent { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_no_else, code = E0317)] - NoElse { - #[primary_span] - span: Span, - }, - #[diag(infer_oc_no_diverge, code = E0308)] - NoDiverge { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_fn_main_correct_type, code = E0580)] - FnMainCorrectType { - #[primary_span] - span: Span, - }, - #[diag(infer_oc_fn_start_correct_type, code = E0308)] - FnStartCorrectType { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_fn_lang_correct_type, code = E0308)] - FnLangCorrectType { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - lang_item_name: Symbol, - }, - #[diag(infer_oc_intrinsic_correct_type, code = E0308)] - IntrinsicCorrectType { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_method_correct_type, code = E0308)] - MethodCorrectType { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_closure_selfref, code = E0644)] - ClosureSelfref { - #[primary_span] - span: Span, - }, - #[diag(infer_oc_cant_coerce, code = E0308)] - CantCoerce { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_generic, code = E0308)] - Generic { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, -} - -#[derive(Subdiagnostic)] -pub enum AddPreciseCapturing { - #[suggestion( - infer_precise_capturing_new, - style = "verbose", - code = " + use<{concatenated_bounds}>", - applicability = "machine-applicable" - )] - New { - #[primary_span] - span: Span, - new_lifetime: Symbol, - concatenated_bounds: String, - }, - #[suggestion( - infer_precise_capturing_existing, - style = "verbose", - code = "{pre}{new_lifetime}{post}", - applicability = "machine-applicable" - )] - Existing { - #[primary_span] - span: Span, - new_lifetime: Symbol, - pre: &'static str, - post: &'static str, - }, -} - -pub struct AddPreciseCapturingAndParams { - pub suggs: Vec<(Span, String)>, - pub new_lifetime: Symbol, - pub apit_spans: Vec<Span>, -} - -impl Subdiagnostic for AddPreciseCapturingAndParams { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { - diag.arg("new_lifetime", self.new_lifetime); - diag.multipart_suggestion_verbose( - fluent::infer_precise_capturing_new_but_apit, - self.suggs, - Applicability::MaybeIncorrect, - ); - diag.span_note(self.apit_spans, fluent::infer_warn_removing_apit_params); - } -} diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs deleted file mode 100644 index d71b7f3c264..00000000000 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ /dev/null @@ -1,183 +0,0 @@ -use crate::error_reporting::infer::nice_region_error::find_anon_type; -use crate::fluent_generated as fluent; -use rustc_errors::{Diag, EmissionGuarantee, IntoDiagArg, SubdiagMessageOp, Subdiagnostic}; -use rustc_hir::def_id::LocalDefId; -use rustc_middle::bug; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::{symbol::kw, Span}; - -struct DescriptionCtx<'a> { - span: Option<Span>, - kind: &'a str, - arg: String, -} - -impl<'a> DescriptionCtx<'a> { - fn new<'tcx>( - tcx: TyCtxt<'tcx>, - generic_param_scope: LocalDefId, - region: ty::Region<'tcx>, - alt_span: Option<Span>, - ) -> Option<Self> { - let (span, kind, arg) = match *region { - ty::ReEarlyParam(br) => { - let scope = tcx - .parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id) - .expect_local(); - let span = if let Some(param) = - tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) - { - param.span - } else { - tcx.def_span(scope) - }; - if br.has_name() { - (Some(span), "as_defined", br.name.to_string()) - } else { - (Some(span), "as_defined_anon", String::new()) - } - } - ty::ReLateParam(ref fr) => { - if !fr.bound_region.is_named() - && let Some((ty, _)) = - find_anon_type(tcx, generic_param_scope, region, &fr.bound_region) - { - (Some(ty.span), "defined_here", String::new()) - } else { - let scope = fr.scope.expect_local(); - match fr.bound_region { - ty::BoundRegionKind::BrNamed(_, name) => { - let span = if let Some(param) = tcx - .hir() - .get_generics(scope) - .and_then(|generics| generics.get_named(name)) - { - param.span - } else { - tcx.def_span(scope) - }; - if name == kw::UnderscoreLifetime { - (Some(span), "as_defined_anon", String::new()) - } else { - (Some(span), "as_defined", name.to_string()) - } - } - ty::BrAnon => { - let span = Some(tcx.def_span(scope)); - (span, "defined_here", String::new()) - } - _ => (Some(tcx.def_span(scope)), "defined_here_reg", region.to_string()), - } - } - } - - ty::ReStatic => (alt_span, "restatic", String::new()), - - ty::RePlaceholder(_) | ty::ReError(_) => return None, - - ty::ReVar(_) | ty::ReBound(..) | ty::ReErased => { - bug!("unexpected region for DescriptionCtx: {:?}", region); - } - }; - Some(DescriptionCtx { span, kind, arg }) - } -} - -pub enum PrefixKind { - Empty, - RefValidFor, - ContentValidFor, - TypeObjValidFor, - SourcePointerValidFor, - TypeSatisfy, - TypeOutlive, - LfParamInstantiatedWith, - LfParamMustOutlive, - LfInstantiatedWith, - LfMustOutlive, - PointerValidFor, - DataValidFor, -} - -pub enum SuffixKind { - Empty, - Continues, - ReqByBinding, -} - -impl IntoDiagArg for PrefixKind { - fn into_diag_arg(self) -> rustc_errors::DiagArgValue { - let kind = match self { - Self::Empty => "empty", - Self::RefValidFor => "ref_valid_for", - Self::ContentValidFor => "content_valid_for", - Self::TypeObjValidFor => "type_obj_valid_for", - Self::SourcePointerValidFor => "source_pointer_valid_for", - Self::TypeSatisfy => "type_satisfy", - Self::TypeOutlive => "type_outlive", - Self::LfParamInstantiatedWith => "lf_param_instantiated_with", - Self::LfParamMustOutlive => "lf_param_must_outlive", - Self::LfInstantiatedWith => "lf_instantiated_with", - Self::LfMustOutlive => "lf_must_outlive", - Self::PointerValidFor => "pointer_valid_for", - Self::DataValidFor => "data_valid_for", - } - .into(); - rustc_errors::DiagArgValue::Str(kind) - } -} - -impl IntoDiagArg for SuffixKind { - fn into_diag_arg(self) -> rustc_errors::DiagArgValue { - let kind = match self { - Self::Empty => "empty", - Self::Continues => "continues", - Self::ReqByBinding => "req_by_binding", - } - .into(); - rustc_errors::DiagArgValue::Str(kind) - } -} - -pub struct RegionExplanation<'a> { - desc: DescriptionCtx<'a>, - prefix: PrefixKind, - suffix: SuffixKind, -} - -impl RegionExplanation<'_> { - pub fn new<'tcx>( - tcx: TyCtxt<'tcx>, - generic_param_scope: LocalDefId, - region: ty::Region<'tcx>, - alt_span: Option<Span>, - prefix: PrefixKind, - suffix: SuffixKind, - ) -> Option<Self> { - Some(Self { - desc: DescriptionCtx::new(tcx, generic_param_scope, region, alt_span)?, - prefix, - suffix, - }) - } -} - -impl Subdiagnostic for RegionExplanation<'_> { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - f: &F, - ) { - diag.arg("pref_kind", self.prefix); - diag.arg("suff_kind", self.suffix); - diag.arg("desc_kind", self.desc.kind); - diag.arg("desc_arg", self.desc.arg); - - let msg = f(diag, fluent::infer_region_explanation.into()); - if let Some(span) = self.desc.span { - diag.span_note(span, msg); - } else { - diag.note(msg); - } - } -} diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index c9073d8c23e..7fc4e36d752 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -11,7 +11,6 @@ pub use BoundRegionConversionTime::*; pub use RegionVariableOrigin::*; pub use SubregionOrigin::*; -use crate::error_reporting::infer::TypeErrCtxt; use crate::infer::relate::RelateResult; use crate::traits::{self, ObligationCause, ObligationInspector, PredicateObligation, TraitEngine}; use free_regions::RegionRelations; @@ -24,7 +23,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::Rollback; use rustc_data_structures::unify as ut; -use rustc_errors::{Diag, ErrorGuaranteed}; +use rustc_errors::ErrorGuaranteed; +use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_macros::extension; use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; @@ -65,8 +65,6 @@ pub mod relate; pub mod resolve; pub(crate) mod snapshot; pub mod type_variable; -// FIXME(error_reporting): Where should we put this? -pub mod need_type_info; #[must_use] #[derive(Debug)] @@ -698,36 +696,24 @@ impl<'tcx> InferCtxt<'tcx> { self.next_trait_solver } - /// Creates a `TypeErrCtxt` for emitting various inference errors. - /// During typeck, use `FnCtxt::err_ctxt` instead. - pub fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> { - TypeErrCtxt { - infcx: self, - sub_relations: Default::default(), - typeck_results: None, - fallback_has_occurred: false, - normalize_fn_sig: Box::new(|fn_sig| fn_sig), - autoderef_steps: Box::new(|ty| { - debug_assert!(false, "shouldn't be using autoderef_steps outside of typeck"); - vec![(ty, vec![])] - }), - } - } - pub fn freshen<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T { t.fold_with(&mut self.freshener()) } - /// Returns the origin of the type variable identified by `vid`, or `None` - /// if this is not a type variable. + /// Returns the origin of the type variable identified by `vid`. /// - /// No attempt is made to resolve `ty`. - pub fn type_var_origin(&self, ty: Ty<'tcx>) -> Option<TypeVariableOrigin> { - match *ty.kind() { - ty::Infer(ty::TyVar(vid)) => { - Some(self.inner.borrow_mut().type_variables().var_origin(vid)) - } - _ => None, + /// No attempt is made to resolve `vid` to its root variable. + pub fn type_var_origin(&self, vid: TyVid) -> TypeVariableOrigin { + self.inner.borrow_mut().type_variables().var_origin(vid) + } + + /// Returns the origin of the const variable identified by `vid` + // FIXME: We should store origins separately from the unification table + // so this doesn't need to be optional. + pub fn const_var_origin(&self, vid: ConstVid) -> Option<ConstVariableOrigin> { + match self.inner.borrow_mut().const_unification_table().probe_value(vid) { + ConstVariableValue::Known { .. } => None, + ConstVariableValue::Unknown { origin, .. } => Some(origin), } } @@ -1589,60 +1575,6 @@ impl<'tcx> InferCtxt<'tcx> { } } -impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - // [Note-Type-error-reporting] - // An invariant is that anytime the expected or actual type is Error (the special - // error type, meaning that an error occurred when typechecking this expression), - // this is a derived error. The error cascaded from another error (that was already - // reported), so it's not useful to display it to the user. - // The following methods implement this logic. - // They check if either the actual or expected type is Error, and don't print the error - // in this case. The typechecker should only ever report type errors involving mismatched - // types using one of these methods, and should not call span_err directly for such - // errors. - pub fn type_error_struct_with_diag<M>( - &self, - sp: Span, - mk_diag: M, - actual_ty: Ty<'tcx>, - ) -> Diag<'a> - where - M: FnOnce(String) -> Diag<'a>, - { - let actual_ty = self.resolve_vars_if_possible(actual_ty); - debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty); - - let mut err = mk_diag(self.ty_to_string(actual_ty)); - - // Don't report an error if actual type is `Error`. - if actual_ty.references_error() { - err.downgrade_to_delayed_bug(); - } - - err - } - - pub fn report_mismatched_types( - &self, - cause: &ObligationCause<'tcx>, - expected: Ty<'tcx>, - actual: Ty<'tcx>, - err: TypeError<'tcx>, - ) -> Diag<'a> { - self.report_and_explain_type_error(TypeTrace::types(cause, true, expected, actual), err) - } - - pub fn report_mismatched_consts( - &self, - cause: &ObligationCause<'tcx>, - expected: ty::Const<'tcx>, - actual: ty::Const<'tcx>, - err: TypeError<'tcx>, - ) -> Diag<'a> { - self.report_and_explain_type_error(TypeTrace::consts(cause, true, expected, actual), err) - } -} - /// Helper for [InferCtxt::ty_or_const_infer_var_changed] (see comment on that), currently /// used only for `traits::fulfill`'s list of `stalled_on` inference variables. #[derive(Copy, Clone, Debug)] @@ -1888,3 +1820,32 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( args.fold_with(&mut ReplaceParamAndInferWithPlaceholder { tcx, idx: 0 }) } + +impl<'tcx> InferCtxt<'tcx> { + /// Given a [`hir::Block`], get the span of its last expression or + /// statement, peeling off any inner blocks. + pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span { + let block = block.innermost_block(); + if let Some(expr) = &block.expr { + expr.span + } else if let Some(stmt) = block.stmts.last() { + // possibly incorrect trailing `;` in the else arm + stmt.span + } else { + // empty block; point at its entirety + block.span + } + } + + /// Given a [`hir::HirId`] for a block, get the span of its last expression + /// or statement, peeling off any inner blocks. + pub fn find_block_span_from_hir_id(&self, hir_id: hir::HirId) -> Span { + match self.tcx.hir_node(hir_id) { + hir::Node::Block(blk) => self.find_block_span(blk), + // The parser was in a weird state if either of these happen, but + // it's better not to panic. + hir::Node::Expr(e) => e.span, + _ => rustc_span::DUMMY_SP, + } + } +} diff --git a/compiler/rustc_infer/src/infer/need_type_info.rs b/compiler/rustc_infer/src/infer/need_type_info.rs deleted file mode 100644 index 4f3dcd9043f..00000000000 --- a/compiler/rustc_infer/src/infer/need_type_info.rs +++ /dev/null @@ -1,1298 +0,0 @@ -use crate::error_reporting::infer::TypeErrCtxt; -use crate::errors::{ - AmbiguousImpl, AmbiguousReturn, AnnotationRequired, InferenceBadError, - SourceKindMultiSuggestion, SourceKindSubdiag, -}; -use crate::infer::InferCtxt; -use rustc_errors::{codes::*, Diag, IntoDiagArg}; -use rustc_hir as hir; -use rustc_hir::def::Res; -use rustc_hir::def::{CtorOf, DefKind, Namespace}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource}; -use rustc_middle::bug; -use rustc_middle::hir::nested_filter; -use rustc_middle::infer::unify_key::ConstVariableValue; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; -use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; -use rustc_middle::ty::{ - self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Ty, TyCtxt, - TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults, -}; -use rustc_span::symbol::{sym, Ident}; -use rustc_span::{BytePos, Span, DUMMY_SP}; -use std::borrow::Cow; -use std::iter; -use std::path::PathBuf; - -pub enum TypeAnnotationNeeded { - /// ```compile_fail,E0282 - /// let x; - /// ``` - E0282, - /// An implementation cannot be chosen unambiguously because of lack of information. - /// ```compile_fail,E0790 - /// let _ = Default::default(); - /// ``` - E0283, - /// ```compile_fail,E0284 - /// let mut d: u64 = 2; - /// d = d % 1u32.into(); - /// ``` - E0284, -} - -impl Into<ErrCode> for TypeAnnotationNeeded { - fn into(self) -> ErrCode { - match self { - Self::E0282 => E0282, - Self::E0283 => E0283, - Self::E0284 => E0284, - } - } -} - -/// Information about a constant or a type containing inference variables. -pub struct InferenceDiagnosticsData { - pub name: String, - pub span: Option<Span>, - pub kind: UnderspecifiedArgKind, - pub parent: Option<InferenceDiagnosticsParentData>, -} - -/// Data on the parent definition where a generic argument was declared. -pub struct InferenceDiagnosticsParentData { - prefix: &'static str, - name: String, -} - -#[derive(Clone)] -pub enum UnderspecifiedArgKind { - Type { prefix: Cow<'static, str> }, - Const { is_parameter: bool }, -} - -impl InferenceDiagnosticsData { - fn can_add_more_info(&self) -> bool { - !(self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. })) - } - - fn where_x_is_kind(&self, in_type: Ty<'_>) -> &'static str { - if in_type.is_ty_or_numeric_infer() { - "" - } else if self.name == "_" { - // FIXME: Consider specializing this message if there is a single `_` - // in the type. - "underscore" - } else { - "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, - } - } -} - -impl InferenceDiagnosticsParentData { - fn for_parent_def_id( - tcx: TyCtxt<'_>, - parent_def_id: DefId, - ) -> Option<InferenceDiagnosticsParentData> { - let parent_name = - tcx.def_key(parent_def_id).disambiguated_data.data.get_opt_name()?.to_string(); - - Some(InferenceDiagnosticsParentData { - prefix: tcx.def_descr(parent_def_id), - name: parent_name, - }) - } - - fn for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<InferenceDiagnosticsParentData> { - Self::for_parent_def_id(tcx, tcx.parent(def_id)) - } -} - -impl IntoDiagArg for UnderspecifiedArgKind { - fn into_diag_arg(self) -> rustc_errors::DiagArgValue { - let kind = match self { - Self::Type { .. } => "type", - Self::Const { is_parameter: true } => "const_with_param", - Self::Const { is_parameter: false } => "const", - }; - rustc_errors::DiagArgValue::Str(kind.into()) - } -} - -impl UnderspecifiedArgKind { - fn try_get_prefix(&self) -> Option<&str> { - match self { - Self::Type { prefix } => Some(prefix.as_ref()), - Self::Const { .. } => None, - } - } -} - -struct ClosureEraser<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ClosureEraser<'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind() { - ty::Closure(_, args) => { - let closure_sig = args.as_closure().sig(); - Ty::new_fn_ptr( - self.tcx, - self.tcx.signature_unclosure(closure_sig, hir::Safety::Safe), - ) - } - _ => ty.super_fold_with(self), - } - } -} - -fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinter<'a, 'tcx> { - let mut printer = FmtPrinter::new(infcx.tcx, ns); - let ty_getter = move |ty_vid| { - if infcx.probe_ty_var(ty_vid).is_ok() { - warn!("resolved ty var in error message"); - } - - let mut infcx_inner = infcx.inner.borrow_mut(); - let ty_vars = infcx_inner.type_variables(); - let var_origin = ty_vars.var_origin(ty_vid); - if let Some(def_id) = var_origin.param_def_id - // The `Self` param of a trait has the def-id of the trait, - // since it's a synthetic parameter. - && infcx.tcx.def_kind(def_id) == DefKind::TyParam - && let name = infcx.tcx.item_name(def_id) - && !var_origin.span.from_expansion() - { - let generics = infcx.tcx.generics_of(infcx.tcx.parent(def_id)); - let idx = generics.param_def_id_to_index(infcx.tcx, def_id).unwrap(); - let generic_param_def = generics.param_at(idx as usize, infcx.tcx); - if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param_def.kind { - None - } else { - Some(name) - } - } else { - None - } - }; - printer.ty_infer_name_resolver = Some(Box::new(ty_getter)); - let const_getter = move |ct_vid| match infcx - .inner - .borrow_mut() - .const_unification_table() - .probe_value(ct_vid) - { - ConstVariableValue::Known { value: _ } => { - warn!("resolved const var in error message"); - None - } - ConstVariableValue::Unknown { origin, universe: _ } => { - if let Some(def_id) = origin.param_def_id { - Some(infcx.tcx.item_name(def_id)) - } else { - None - } - } - }; - printer.const_infer_name_resolver = Some(Box::new(const_getter)); - printer -} - -fn ty_to_string<'tcx>( - infcx: &InferCtxt<'tcx>, - ty: Ty<'tcx>, - called_method_def_id: Option<DefId>, -) -> String { - let mut printer = fmt_printer(infcx, Namespace::TypeNS); - let ty = infcx.resolve_vars_if_possible(ty); - // We use `fn` ptr syntax for closures, but this only works when the closure - // does not capture anything. - let ty = ty.fold_with(&mut ClosureEraser { tcx: infcx.tcx }); - - match (ty.kind(), called_method_def_id) { - // We don't want the regular output for `fn`s because it includes its path in - // invalid pseudo-syntax, we want the `fn`-pointer output instead. - (ty::FnDef(..), _) => { - ty.fn_sig(infcx.tcx).print(&mut printer).unwrap(); - printer.into_buffer() - } - (_, Some(def_id)) - if ty.is_ty_or_numeric_infer() - && infcx.tcx.get_diagnostic_item(sym::iterator_collect_fn) == Some(def_id) => - { - "Vec<_>".to_string() - } - _ if ty.is_ty_or_numeric_infer() => "/* Type */".to_string(), - _ => { - ty.print(&mut printer).unwrap(); - printer.into_buffer() - } - } -} - -/// We don't want to directly use `ty_to_string` for closures as their type isn't really -/// 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(_, args) = ty.kind() else { - bug!("cannot convert non-closure to fn str in `closure_as_fn_str`") - }; - let fn_sig = args.as_closure().sig(); - let args = fn_sig - .inputs() - .skip_binder() - .iter() - .next() - .map(|args| { - args.tuple_fields() - .iter() - .map(|arg| ty_to_string(infcx, arg, None)) - .collect::<Vec<_>>() - .join(", ") - }) - .unwrap_or_default(); - let ret = if fn_sig.output().skip_binder().is_unit() { - String::new() - } else { - format!(" -> {}", ty_to_string(infcx, fn_sig.output().skip_binder(), None)) - }; - format!("fn({args}){ret}") -} - -impl<'tcx> InferCtxt<'tcx> { - /// Extracts data used by diagnostic for either types or constants - /// which were stuck during inference. - pub fn extract_inference_diagnostics_data( - &self, - arg: GenericArg<'tcx>, - highlight: Option<ty::print::RegionHighlightMode<'tcx>>, - ) -> InferenceDiagnosticsData { - match arg.unpack() { - GenericArgKind::Type(ty) => { - if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() { - let mut inner = self.inner.borrow_mut(); - let ty_vars = &inner.type_variables(); - let var_origin = ty_vars.var_origin(ty_vid); - if let Some(def_id) = var_origin.param_def_id - // The `Self` param of a trait has the def-id of the trait, - // since it's a synthetic parameter. - && self.tcx.def_kind(def_id) == DefKind::TyParam - && !var_origin.span.from_expansion() - { - return InferenceDiagnosticsData { - name: self.tcx.item_name(def_id).to_string(), - span: Some(var_origin.span), - kind: UnderspecifiedArgKind::Type { prefix: "type parameter".into() }, - parent: InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id), - }; - } - } - - let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS); - if let Some(highlight) = highlight { - printer.region_highlight_mode = highlight; - } - ty.print(&mut printer).unwrap(); - InferenceDiagnosticsData { - name: printer.into_buffer(), - span: None, - kind: UnderspecifiedArgKind::Type { prefix: ty.prefix_string(self.tcx) }, - parent: None, - } - } - GenericArgKind::Const(ct) => { - if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() { - let origin = - match self.inner.borrow_mut().const_unification_table().probe_value(vid) { - ConstVariableValue::Known { value } => { - bug!("resolved infer var: {vid:?} {value}") - } - ConstVariableValue::Unknown { origin, universe: _ } => origin, - }; - if let Some(def_id) = origin.param_def_id { - return InferenceDiagnosticsData { - name: self.tcx.item_name(def_id).to_string(), - span: Some(origin.span), - kind: UnderspecifiedArgKind::Const { is_parameter: true }, - parent: InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id), - }; - } - - debug_assert!(!origin.span.is_dummy()); - let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::ValueNS); - if let Some(highlight) = highlight { - printer.region_highlight_mode = highlight; - } - ct.print(&mut printer).unwrap(); - InferenceDiagnosticsData { - name: printer.into_buffer(), - span: Some(origin.span), - kind: UnderspecifiedArgKind::Const { is_parameter: false }, - parent: None, - } - } else { - // If we end up here the `FindInferSourceVisitor` - // won't work, as its expected argument isn't an inference variable. - // - // FIXME: Ideally we should look into the generic constant - // to figure out which inference var is actually unresolved so that - // this path is unreachable. - let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::ValueNS); - if let Some(highlight) = highlight { - printer.region_highlight_mode = highlight; - } - ct.print(&mut printer).unwrap(); - InferenceDiagnosticsData { - name: printer.into_buffer(), - span: None, - kind: UnderspecifiedArgKind::Const { is_parameter: false }, - parent: None, - } - } - } - GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), - } - } - - /// Used as a fallback in [TypeErrCtxt::emit_inference_failure_err] - /// in case we weren't able to get a better error. - fn bad_inference_failure_err( - &self, - span: Span, - arg_data: InferenceDiagnosticsData, - error_code: TypeAnnotationNeeded, - ) -> Diag<'_> { - 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 => self.dcx().create_err(AnnotationRequired { - span, - source_kind, - source_name, - failure_span, - infer_subdiags, - multi_suggestions, - bad_label, - was_written: None, - path: Default::default(), - }), - TypeAnnotationNeeded::E0283 => self.dcx().create_err(AmbiguousImpl { - span, - source_kind, - source_name, - failure_span, - infer_subdiags, - multi_suggestions, - bad_label, - was_written: None, - path: Default::default(), - }), - TypeAnnotationNeeded::E0284 => self.dcx().create_err(AmbiguousReturn { - span, - source_kind, - source_name, - failure_span, - infer_subdiags, - multi_suggestions, - bad_label, - was_written: None, - path: Default::default(), - }), - } - } -} - -impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - #[instrument(level = "debug", skip(self, error_code))] - pub fn emit_inference_failure_err( - &self, - body_def_id: LocalDefId, - failure_span: Span, - arg: GenericArg<'tcx>, - error_code: TypeAnnotationNeeded, - should_label_span: bool, - ) -> Diag<'a> { - let arg = self.resolve_vars_if_possible(arg); - let arg_data = self.extract_inference_diagnostics_data(arg, None); - - let Some(typeck_results) = &self.typeck_results else { - // If we don't have any typeck results we're outside - // of a body, so we won't be able to get better info - // here. - return self.infcx.bad_inference_failure_err(failure_span, arg_data, error_code); - }; - - let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, arg); - if let Some(body) = self.tcx.hir().maybe_body_owned_by( - self.tcx.typeck_root_def_id(body_def_id.to_def_id()).expect_local(), - ) { - let expr = body.value; - local_visitor.visit_expr(expr); - } - - let Some(InferSource { span, kind }) = local_visitor.infer_source else { - return self.infcx.bad_inference_failure_err(failure_span, arg_data, error_code); - }; - - let (source_kind, name, path) = 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, def_id } => { - 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, def_id), - }); - } - InferSourceKind::ClosureArg { insert_span, ty } => { - 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, None), - }); - } - InferSourceKind::GenericArg { - insert_span, - argument_index, - generics_def_id, - def_id: _, - generic_args, - have_turbofish, - } => { - let generics = self.tcx.generics_of(generics_def_id); - let is_type = matches!(arg.unpack(), GenericArgKind::Type(_)); - - let (parent_exists, parent_prefix, parent_name) = - InferenceDiagnosticsParentData::for_parent_def_id(self.tcx, generics_def_id) - .map_or((false, String::new(), String::new()), |parent| { - (true, parent.prefix.to_string(), parent.name) - }); - - infer_subdiags.push(SourceKindSubdiag::GenericLabel { - span, - is_type, - param_name: generics.own_params[argument_index].name.to_string(), - parent_exists, - parent_prefix, - parent_name, - }); - - let args = if self.tcx.get_diagnostic_item(sym::iterator_collect_fn) - == Some(generics_def_id) - { - "Vec<_>".to_string() - } else { - let mut printer = fmt_printer(self, Namespace::TypeNS); - printer - .comma_sep(generic_args.iter().copied().map(|arg| { - if arg.is_suggestable(self.tcx, true) { - return arg; - } - - match arg.unpack() { - GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), - GenericArgKind::Type(_) => self.next_ty_var(DUMMY_SP).into(), - GenericArgKind::Const(_) => self.next_const_var(DUMMY_SP).into(), - } - })) - .unwrap(); - printer.into_buffer() - }; - - if !have_turbofish { - infer_subdiags.push(SourceKindSubdiag::GenericSuggestion { - span: insert_span, - arg_count: generic_args.len(), - args, - }); - } - } - InferSourceKind::FullyQualifiedMethodCall { receiver, successor, args, def_id } => { - let placeholder = Some(self.next_ty_var(DUMMY_SP)); - if let Some(args) = args.make_suggestable(self.infcx.tcx, true, placeholder) { - let mut printer = fmt_printer(self, Namespace::ValueNS); - printer.print_def_path(def_id, args).unwrap(); - let def_path = printer.into_buffer(); - - // We only care about whether we have to add `&` or `&mut ` for now. - // This is the case if the last adjustment is a borrow and the - // first adjustment was not a builtin deref. - let adjustment = match typeck_results.expr_adjustments(receiver) { - [ - Adjustment { kind: Adjust::Deref(None), target: _ }, - .., - Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ }, - ] => "", - [ - .., - Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(_, mut_)), - target: _, - }, - ] => hir::Mutability::from(*mut_).ref_prefix_str(), - _ => "", - }; - - multi_suggestions.push(SourceKindMultiSuggestion::new_fully_qualified( - receiver.span, - def_path, - adjustment, - successor, - )); - } - } - InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => { - let placeholder = Some(self.next_ty_var(DUMMY_SP)); - if let Some(ty) = ty.make_suggestable(self.infcx.tcx, true, placeholder) { - let ty_info = ty_to_string(self, ty, None); - multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return( - ty_info, - data, - should_wrap_expr, - )); - } - } - } - match error_code { - TypeAnnotationNeeded::E0282 => self.dcx().create_err(AnnotationRequired { - span, - source_kind, - source_name: &name, - failure_span, - infer_subdiags, - multi_suggestions, - bad_label: None, - was_written: path.as_ref().map(|_| ()), - path: path.unwrap_or_default(), - }), - TypeAnnotationNeeded::E0283 => self.dcx().create_err(AmbiguousImpl { - span, - source_kind, - source_name: &name, - failure_span, - infer_subdiags, - multi_suggestions, - bad_label: None, - was_written: path.as_ref().map(|_| ()), - path: path.unwrap_or_default(), - }), - TypeAnnotationNeeded::E0284 => self.dcx().create_err(AmbiguousReturn { - span, - source_kind, - source_name: &name, - failure_span, - infer_subdiags, - multi_suggestions, - bad_label: None, - was_written: path.as_ref().map(|_| ()), - path: path.unwrap_or_default(), - }), - } - } -} - -#[derive(Debug)] -struct InferSource<'tcx> { - span: Span, - kind: InferSourceKind<'tcx>, -} - -#[derive(Debug)] -enum InferSourceKind<'tcx> { - LetBinding { - insert_span: Span, - pattern_name: Option<Ident>, - ty: Ty<'tcx>, - def_id: Option<DefId>, - }, - ClosureArg { - insert_span: Span, - ty: Ty<'tcx>, - }, - GenericArg { - insert_span: Span, - argument_index: usize, - generics_def_id: DefId, - def_id: DefId, - generic_args: &'tcx [GenericArg<'tcx>], - have_turbofish: bool, - }, - FullyQualifiedMethodCall { - receiver: &'tcx Expr<'tcx>, - /// If the method has other arguments, this is ", " and the start of the first argument, - /// while for methods without arguments this is ")" and the end of the method call. - successor: (&'static str, BytePos), - args: GenericArgsRef<'tcx>, - def_id: DefId, - }, - ClosureReturn { - ty: Ty<'tcx>, - data: &'tcx FnRetTy<'tcx>, - should_wrap_expr: Option<Span>, - }, -} - -impl<'tcx> InferSource<'tcx> { - fn from_expansion(&self) -> bool { - let source_from_expansion = match self.kind { - InferSourceKind::LetBinding { insert_span, .. } - | InferSourceKind::ClosureArg { insert_span, .. } - | InferSourceKind::GenericArg { insert_span, .. } => insert_span.from_expansion(), - InferSourceKind::FullyQualifiedMethodCall { receiver, .. } => { - receiver.span.from_expansion() - } - InferSourceKind::ClosureReturn { data, should_wrap_expr, .. } => { - data.span().from_expansion() || should_wrap_expr.is_some_and(Span::from_expansion) - } - }; - source_from_expansion || self.span.from_expansion() - } -} - -impl<'tcx> InferSourceKind<'tcx> { - fn ty_localized_msg(&self, infcx: &InferCtxt<'tcx>) -> (&'static str, String, Option<PathBuf>) { - let mut path = None; - match *self { - InferSourceKind::LetBinding { ty, .. } - | InferSourceKind::ClosureArg { ty, .. } - | InferSourceKind::ClosureReturn { ty, .. } => { - if ty.is_closure() { - ("closure", closure_as_fn_str(infcx, ty), path) - } else if !ty.is_ty_or_numeric_infer() { - ("normal", infcx.tcx.short_ty_string(ty, &mut path), path) - } else { - ("other", String::new(), path) - } - } - // FIXME: We should be able to add some additional info here. - InferSourceKind::GenericArg { .. } - | InferSourceKind::FullyQualifiedMethodCall { .. } => ("other", String::new(), path), - } - } -} - -#[derive(Debug)] -struct InsertableGenericArgs<'tcx> { - insert_span: Span, - args: GenericArgsRef<'tcx>, - generics_def_id: DefId, - def_id: DefId, - have_turbofish: bool, -} - -/// A visitor which searches for the "best" spot to use in the inference error. -/// -/// For this it walks over the hir body and tries to check all places where -/// inference variables could be bound. -/// -/// While doing so, the currently best spot is stored in `infer_source`. -/// For details on how we rank spots, see [Self::source_cost] -struct FindInferSourceVisitor<'a, 'tcx> { - tecx: &'a TypeErrCtxt<'a, 'tcx>, - typeck_results: &'a TypeckResults<'tcx>, - - target: GenericArg<'tcx>, - - attempt: usize, - infer_source_cost: usize, - infer_source: Option<InferSource<'tcx>>, -} - -impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { - fn new( - tecx: &'a TypeErrCtxt<'a, 'tcx>, - typeck_results: &'a TypeckResults<'tcx>, - target: GenericArg<'tcx>, - ) -> Self { - FindInferSourceVisitor { - tecx, - typeck_results, - - target, - - attempt: 0, - infer_source_cost: usize::MAX, - infer_source: None, - } - } - - /// Computes cost for the given source. - /// - /// Sources with a small cost are prefer and should result - /// in a clearer and idiomatic suggestion. - fn source_cost(&self, source: &InferSource<'tcx>) -> usize { - #[derive(Clone, Copy)] - struct CostCtxt<'tcx> { - tcx: TyCtxt<'tcx>, - } - impl<'tcx> CostCtxt<'tcx> { - fn arg_cost(self, arg: GenericArg<'tcx>) -> usize { - match arg.unpack() { - GenericArgKind::Lifetime(_) => 0, // erased - GenericArgKind::Type(ty) => self.ty_cost(ty), - GenericArgKind::Const(_) => 3, // some non-zero value - } - } - fn ty_cost(self, ty: Ty<'tcx>) -> usize { - match *ty.kind() { - ty::Closure(..) => 1000, - ty::FnDef(..) => 150, - ty::FnPtr(..) => 30, - ty::Adt(def, args) => { - 5 + self - .tcx - .generics_of(def.did()) - .own_args_no_defaults(self.tcx, args) - .iter() - .map(|&arg| self.arg_cost(arg)) - .sum::<usize>() - } - ty::Tuple(args) => 5 + args.iter().map(|arg| self.ty_cost(arg)).sum::<usize>(), - ty::Ref(_, ty, _) => 2 + self.ty_cost(ty), - ty::Infer(..) => 0, - _ => 1, - } - } - } - - // The sources are listed in order of preference here. - let tcx = self.tecx.tcx; - let ctx = CostCtxt { tcx }; - match source.kind { - InferSourceKind::LetBinding { ty, .. } => ctx.ty_cost(ty), - InferSourceKind::ClosureArg { ty, .. } => ctx.ty_cost(ty), - InferSourceKind::GenericArg { def_id, generic_args, .. } => { - let variant_cost = match tcx.def_kind(def_id) { - // `None::<u32>` and friends are ugly. - DefKind::Variant | DefKind::Ctor(CtorOf::Variant, _) => 15, - _ => 10, - }; - variant_cost + generic_args.iter().map(|&arg| ctx.arg_cost(arg)).sum::<usize>() - } - InferSourceKind::FullyQualifiedMethodCall { args, .. } => { - 20 + args.iter().map(|arg| ctx.arg_cost(arg)).sum::<usize>() - } - InferSourceKind::ClosureReturn { ty, should_wrap_expr, .. } => { - 30 + ctx.ty_cost(ty) + if should_wrap_expr.is_some() { 10 } else { 0 } - } - } - } - - /// Uses `fn source_cost` to determine whether this inference source is preferable to - /// previous sources. We generally prefer earlier sources. - #[instrument(level = "debug", skip(self))] - fn update_infer_source(&mut self, mut new_source: InferSource<'tcx>) { - if new_source.from_expansion() { - return; - } - - let cost = self.source_cost(&new_source) + self.attempt; - debug!(?cost); - self.attempt += 1; - if let Some(InferSource { kind: InferSourceKind::GenericArg { def_id: did, .. }, .. }) = - self.infer_source - && let InferSourceKind::LetBinding { ref ty, ref mut def_id, .. } = new_source.kind - && ty.is_ty_or_numeric_infer() - { - // Customize the output so we talk about `let x: Vec<_> = iter.collect();` instead of - // `let x: _ = iter.collect();`, as this is a very common case. - *def_id = Some(did); - } - - if cost < self.infer_source_cost { - self.infer_source_cost = cost; - self.infer_source = Some(new_source); - } - } - - fn node_args_opt(&self, hir_id: HirId) -> Option<GenericArgsRef<'tcx>> { - let args = self.typeck_results.node_args_opt(hir_id); - self.tecx.resolve_vars_if_possible(args) - } - - fn opt_node_type(&self, hir_id: HirId) -> Option<Ty<'tcx>> { - let ty = self.typeck_results.node_type_opt(hir_id); - self.tecx.resolve_vars_if_possible(ty) - } - - // Check whether this generic argument is the inference variable we - // are looking for. - fn generic_arg_is_target(&self, arg: GenericArg<'tcx>) -> bool { - if arg == self.target { - return true; - } - - match (arg.unpack(), self.target.unpack()) { - (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => { - use ty::{Infer, TyVar}; - match (inner_ty.kind(), target_ty.kind()) { - (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => { - self.tecx.sub_relations.borrow_mut().unified(self.tecx, a_vid, b_vid) - } - _ => false, - } - } - (GenericArgKind::Const(inner_ct), GenericArgKind::Const(target_ct)) => { - use ty::InferConst::*; - match (inner_ct.kind(), target_ct.kind()) { - (ty::ConstKind::Infer(Var(a_vid)), ty::ConstKind::Infer(Var(b_vid))) => { - self.tecx.inner.borrow_mut().const_unification_table().unioned(a_vid, b_vid) - } - _ => false, - } - } - _ => false, - } - } - - /// Does this generic argument contain our target inference variable - /// in a way which can be written by the user. - fn generic_arg_contains_target(&self, arg: GenericArg<'tcx>) -> bool { - let mut walker = arg.walk(); - while let Some(inner) = walker.next() { - if self.generic_arg_is_target(inner) { - return true; - } - match inner.unpack() { - GenericArgKind::Lifetime(_) => {} - GenericArgKind::Type(ty) => { - if matches!( - ty.kind(), - ty::Alias(ty::Opaque, ..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - ) { - // Opaque types can't be named by the user right now. - // - // Both the generic arguments of closures and coroutines can - // also not be named. We may want to only look into the closure - // signature in case it has no captures, as that can be represented - // using `fn(T) -> R`. - - // FIXME(type_alias_impl_trait): These opaque types - // can actually be named, so it would make sense to - // adjust this case and add a test for it. - walker.skip_current_subtree(); - } - } - GenericArgKind::Const(ct) => { - if matches!(ct.kind(), ty::ConstKind::Unevaluated(..)) { - // You can't write the generic arguments for - // unevaluated constants. - walker.skip_current_subtree(); - } - } - } - } - false - } - - fn expr_inferred_arg_iter( - &self, - expr: &'tcx hir::Expr<'tcx>, - ) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> { - let tcx = self.tecx.tcx; - match expr.kind { - hir::ExprKind::Path(ref path) => { - if let Some(args) = self.node_args_opt(expr.hir_id) { - return self.path_inferred_arg_iter(expr.hir_id, args, path); - } - } - // FIXME(#98711): Ideally we would also deal with type relative - // paths here, even if that is quite rare. - // - // See the `need_type_info/expr-struct-type-relative-gat.rs` test - // for an example where that would be needed. - // - // However, the `type_dependent_def_id` for `Self::Output` in an - // impl is currently the `DefId` of `Output` in the trait definition - // which makes this somewhat difficult and prevents us from just - // using `self.path_inferred_arg_iter` here. - hir::ExprKind::Struct(&hir::QPath::Resolved(_self_ty, path), _, _) - // FIXME(TaKO8Ki): Ideally we should support this. For that - // we have to map back from the self type to the - // type alias though. That's difficult. - // - // See the `need_type_info/issue-103053.rs` test for - // a example. - if !matches!(path.res, Res::Def(DefKind::TyAlias, _)) => { - if let Some(ty) = self.opt_node_type(expr.hir_id) - && let ty::Adt(_, args) = ty.kind() - { - return Box::new(self.resolved_path_inferred_arg_iter(path, args)); - } - } - 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 { - if generics.has_impl_trait() { - None? - } - let args = self.node_args_opt(expr.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, - args, - generics_def_id: def_id, - def_id, - have_turbofish: false, - } - }; - return Box::new(insertable.into_iter()); - } - } - _ => {} - } - - Box::new(iter::empty()) - } - - fn resolved_path_inferred_arg_iter( - &self, - path: &'tcx hir::Path<'tcx>, - args: GenericArgsRef<'tcx>, - ) -> impl Iterator<Item = InsertableGenericArgs<'tcx>> + 'a { - let tcx = self.tecx.tcx; - let have_turbofish = path.segments.iter().any(|segment| { - segment.args.is_some_and(|args| args.args.iter().any(|arg| arg.is_ty_or_const())) - }); - // The last segment of a path often has `Res::Err` and the - // correct `Res` is the one of the whole path. - // - // FIXME: We deal with that one separately for now, - // would be good to remove this special case. - let last_segment_using_path_data: Option<_> = try { - let generics_def_id = tcx.res_generics_def_id(path.res)?; - let generics = tcx.generics_of(generics_def_id); - if generics.has_impl_trait() { - do yeet (); - } - let insert_span = - path.segments.last().unwrap().ident.span.shrink_to_hi().with_hi(path.span.hi()); - InsertableGenericArgs { - insert_span, - args, - generics_def_id, - def_id: path.res.def_id(), - have_turbofish, - } - }; - - path.segments - .iter() - .filter_map(move |segment| { - 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 insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); - Some(InsertableGenericArgs { - insert_span, - args, - generics_def_id, - def_id: res.def_id(), - have_turbofish, - }) - }) - .chain(last_segment_using_path_data) - } - - fn path_inferred_arg_iter( - &self, - hir_id: HirId, - args: GenericArgsRef<'tcx>, - qpath: &'tcx hir::QPath<'tcx>, - ) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> { - let tcx = self.tecx.tcx; - match qpath { - hir::QPath::Resolved(_self_ty, path) => { - Box::new(self.resolved_path_inferred_arg_iter(path, args)) - } - hir::QPath::TypeRelative(ty, segment) => { - let Some(def_id) = self.typeck_results.type_dependent_def_id(hir_id) else { - return Box::new(iter::empty()); - }; - - let generics = tcx.generics_of(def_id); - let segment: Option<_> = try { - if !segment.infer_args || generics.has_impl_trait() { - do yeet (); - } - let span = tcx.hir().span(segment.hir_id); - let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); - InsertableGenericArgs { - insert_span, - args, - generics_def_id: def_id, - def_id, - have_turbofish: false, - } - }; - - let parent_def_id = generics.parent.unwrap(); - if let DefKind::Impl { .. } = tcx.def_kind(parent_def_id) { - let parent_ty = tcx.type_of(parent_def_id).instantiate(tcx, args); - match (parent_ty.kind(), &ty.kind) { - ( - ty::Adt(def, args), - hir::TyKind::Path(hir::QPath::Resolved(_self_ty, path)), - ) => { - if tcx.res_generics_def_id(path.res) != Some(def.did()) { - match path.res { - Res::Def(DefKind::TyAlias, _) => { - // FIXME: Ideally we should support this. For that - // we have to map back from the self type to the - // type alias though. That's difficult. - // - // See the `need_type_info/type-alias.rs` test for - // some examples. - } - // There cannot be inference variables in the self type, - // so there's nothing for us to do here. - Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => {} - _ => warn!( - "unexpected path: def={:?} args={:?} path={:?}", - def, args, path, - ), - } - } else { - return Box::new( - self.resolved_path_inferred_arg_iter(path, args).chain(segment), - ); - } - } - _ => (), - } - } - - Box::new(segment.into_iter()) - } - hir::QPath::LangItem(_, _) => Box::new(iter::empty()), - } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { - type NestedFilter = nested_filter::OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tecx.tcx.hir() - } - - fn visit_local(&mut self, local: &'tcx LetStmt<'tcx>) { - intravisit::walk_local(self, local); - - if let Some(ty) = self.opt_node_type(local.hir_id) { - if self.generic_arg_contains_target(ty.into()) { - match local.source { - LocalSource::Normal if local.ty.is_none() => { - self.update_infer_source(InferSource { - span: local.pat.span, - kind: InferSourceKind::LetBinding { - insert_span: local.pat.span.shrink_to_hi(), - pattern_name: local.pat.simple_ident(), - ty, - def_id: None, - }, - }) - } - _ => {} - } - } - } - } - - /// For closures, we first visit the parameters and then the content, - /// as we prefer those. - fn visit_body(&mut self, body: &Body<'tcx>) { - for param in body.params { - debug!( - "param: span {:?}, ty_span {:?}, pat.span {:?}", - param.span, param.ty_span, param.pat.span - ); - if param.ty_span != param.pat.span { - debug!("skipping param: has explicit type"); - continue; - } - - let Some(param_ty) = self.opt_node_type(param.hir_id) else { continue }; - - if self.generic_arg_contains_target(param_ty.into()) { - self.update_infer_source(InferSource { - span: param.pat.span, - kind: InferSourceKind::ClosureArg { - insert_span: param.pat.span.shrink_to_hi(), - ty: param_ty, - }, - }) - } - } - intravisit::walk_body(self, body); - } - - #[instrument(level = "debug", skip(self))] - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - let tcx = self.tecx.tcx; - match expr.kind { - // When encountering `func(arg)` first look into `arg` and then `func`, - // as `arg` is "more specific". - ExprKind::Call(func, args) => { - for arg in args { - self.visit_expr(arg); - } - self.visit_expr(func); - } - _ => intravisit::walk_expr(self, expr), - } - - for args in self.expr_inferred_arg_iter(expr) { - debug!(?args); - let InsertableGenericArgs { - insert_span, - args, - generics_def_id, - def_id, - have_turbofish, - } = args; - let generics = tcx.generics_of(generics_def_id); - if let Some(mut argument_index) = generics - .own_args(args) - .iter() - .position(|&arg| self.generic_arg_contains_target(arg)) - { - if generics.parent.is_none() && generics.has_self { - argument_index += 1; - } - let args = self.tecx.resolve_vars_if_possible(args); - let generic_args = - &generics.own_args_no_defaults(tcx, args)[generics.own_counts().lifetimes..]; - let span = match expr.kind { - ExprKind::MethodCall(path, ..) => path.ident.span, - _ => expr.span, - }; - - self.update_infer_source(InferSource { - span, - kind: InferSourceKind::GenericArg { - insert_span, - argument_index, - generics_def_id, - def_id, - generic_args, - have_turbofish, - }, - }); - } - } - - if let Some(node_ty) = self.opt_node_type(expr.hir_id) { - if let ( - &ExprKind::Closure(&Closure { fn_decl, body, fn_decl_span, .. }), - ty::Closure(_, args), - ) = (&expr.kind, node_ty.kind()) - { - let output = args.as_closure().sig().output().skip_binder(); - if self.generic_arg_contains_target(output.into()) { - let body = self.tecx.tcx.hir().body(body); - let should_wrap_expr = if matches!(body.value.kind, ExprKind::Block(..)) { - None - } else { - Some(body.value.span.shrink_to_hi()) - }; - self.update_infer_source(InferSource { - span: fn_decl_span, - kind: InferSourceKind::ClosureReturn { - ty: output, - data: &fn_decl.output, - should_wrap_expr, - }, - }) - } - } - } - - let has_impl_trait = |def_id| { - iter::successors(Some(tcx.generics_of(def_id)), |generics| { - generics.parent.map(|def_id| tcx.generics_of(def_id)) - }) - .any(|generics| generics.has_impl_trait()) - }; - if let ExprKind::MethodCall(path, receiver, method_args, span) = expr.kind - && let Some(args) = self.node_args_opt(expr.hir_id) - && args.iter().any(|arg| self.generic_arg_contains_target(arg)) - && let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) - && self.tecx.tcx.trait_of_item(def_id).is_some() - && !has_impl_trait(def_id) - { - let successor = - method_args.get(0).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo())); - let args = self.tecx.resolve_vars_if_possible(args); - self.update_infer_source(InferSource { - span: path.ident.span, - kind: InferSourceKind::FullyQualifiedMethodCall { - receiver, - successor, - args, - def_id, - }, - }) - } - } -} diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 02ebf933f53..b65ac859667 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -34,7 +34,6 @@ #[macro_use] extern crate tracing; -pub mod error_reporting; mod errors; pub mod infer; pub mod traits; diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs deleted file mode 100644 index 83df1fd6510..00000000000 --- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs +++ /dev/null @@ -1,203 +0,0 @@ -use super::ObjectSafetyViolation; - -use crate::infer::InferCtxt; -use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag, MultiSpan}; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::Span; -use std::fmt; -use std::iter; - -impl<'tcx> InferCtxt<'tcx> { - pub fn report_extra_impl_obligation<'a>( - &'a self, - error_span: Span, - impl_item_def_id: LocalDefId, - trait_item_def_id: DefId, - requirement: &dyn fmt::Display, - ) -> Diag<'a> { - let mut err = struct_span_code_err!( - self.dcx(), - error_span, - E0276, - "impl has stricter requirements than trait" - ); - - if !self.tcx.is_impl_trait_in_trait(trait_item_def_id) { - if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) { - let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); - err.span_label(span, format!("definition of `{item_name}` from trait")); - } - } - - err.span_label(error_span, format!("impl has extra requirement {requirement}")); - - err - } -} - -pub fn report_object_safety_error<'tcx>( - tcx: TyCtxt<'tcx>, - span: Span, - hir_id: Option<hir::HirId>, - trait_def_id: DefId, - violations: &[ObjectSafetyViolation], -) -> Diag<'tcx> { - let trait_str = tcx.def_path_str(trait_def_id); - let trait_span = tcx.hir().get_if_local(trait_def_id).and_then(|node| match node { - hir::Node::Item(item) => Some(item.ident.span), - _ => None, - }); - let mut err = struct_span_code_err!( - tcx.dcx(), - span, - E0038, - "the trait `{}` cannot be made into an object", - trait_str - ); - err.span_label(span, format!("`{trait_str}` cannot be made into an object")); - - if let Some(hir_id) = hir_id - && let hir::Node::Ty(ty) = tcx.hir_node(hir_id) - && let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind - { - let mut hir_id = hir_id; - while let hir::Node::Ty(ty) = tcx.parent_hir_node(hir_id) { - hir_id = ty.hir_id; - } - if tcx.parent_hir_node(hir_id).fn_sig().is_some() { - // Do not suggest `impl Trait` when dealing with things like super-traits. - err.span_suggestion_verbose( - ty.span.until(trait_ref.span), - "consider using an opaque type instead", - "impl ", - Applicability::MaybeIncorrect, - ); - } - } - let mut reported_violations = FxIndexSet::default(); - let mut multi_span = vec![]; - let mut messages = vec![]; - for violation in violations { - if let ObjectSafetyViolation::SizedSelf(sp) = &violation - && !sp.is_empty() - { - // Do not report `SizedSelf` without spans pointing at `SizedSelf` obligations - // with a `Span`. - reported_violations.insert(ObjectSafetyViolation::SizedSelf(vec![].into())); - } - if reported_violations.insert(violation.clone()) { - let spans = violation.spans(); - let msg = if trait_span.is_none() || spans.is_empty() { - format!("the trait cannot be made into an object because {}", violation.error_msg()) - } else { - format!("...because {}", violation.error_msg()) - }; - if spans.is_empty() { - err.note(msg); - } else { - for span in spans { - multi_span.push(span); - messages.push(msg.clone()); - } - } - } - } - let has_multi_span = !multi_span.is_empty(); - let mut note_span = MultiSpan::from_spans(multi_span.clone()); - if let (Some(trait_span), true) = (trait_span, has_multi_span) { - note_span.push_span_label(trait_span, "this trait cannot be made into an object..."); - } - for (span, msg) in iter::zip(multi_span, messages) { - note_span.push_span_label(span, msg); - } - err.span_note( - note_span, - "for a trait to be \"object safe\" it needs to allow building a vtable to allow the call \ - to be resolvable dynamically; for more information visit \ - <https://doc.rust-lang.org/reference/items/traits.html#object-safety>", - ); - - // Only provide the help if its a local trait, otherwise it's not actionable. - if trait_span.is_some() { - let mut reported_violations: Vec<_> = reported_violations.into_iter().collect(); - reported_violations.sort(); - - let mut potential_solutions: Vec<_> = - reported_violations.into_iter().map(|violation| violation.solution()).collect(); - potential_solutions.sort(); - // Allows us to skip suggesting that the same item should be moved to another trait multiple times. - potential_solutions.dedup(); - for solution in potential_solutions { - solution.add_to(&mut err); - } - } - - let impls_of = tcx.trait_impls_of(trait_def_id); - let impls = if impls_of.blanket_impls().is_empty() { - impls_of - .non_blanket_impls() - .values() - .flatten() - .filter(|def_id| { - !matches!(tcx.type_of(*def_id).instantiate_identity().kind(), ty::Dynamic(..)) - }) - .collect::<Vec<_>>() - } else { - vec![] - }; - let externally_visible = if !impls.is_empty() - && let Some(def_id) = trait_def_id.as_local() - // We may be executing this during typeck, which would result in cycle - // if we used effective_visibilities query, which looks into opaque types - // (and therefore calls typeck). - && tcx.resolutions(()).effective_visibilities.is_exported(def_id) - { - true - } else { - false - }; - match &impls[..] { - [] => {} - _ if impls.len() > 9 => {} - [only] if externally_visible => { - err.help(with_no_trimmed_paths!(format!( - "only type `{}` is seen to implement the trait in this crate, consider using it \ - directly instead", - tcx.type_of(*only).instantiate_identity(), - ))); - } - [only] => { - err.help(with_no_trimmed_paths!(format!( - "only type `{}` implements the trait, consider using it directly instead", - tcx.type_of(*only).instantiate_identity(), - ))); - } - impls => { - let types = impls - .iter() - .map(|t| { - with_no_trimmed_paths!(format!(" {}", tcx.type_of(*t).instantiate_identity(),)) - }) - .collect::<Vec<_>>(); - err.help(format!( - "the following types implement the trait, consider defining an enum where each \ - variant holds one of these types, implementing `{}` for this new enum and using \ - it instead:\n{}", - trait_str, - types.join("\n"), - )); - } - } - if externally_visible { - err.note(format!( - "`{trait_str}` can be implemented in other crates; if you want to support your users \ - passing their own types here, you can't refer to a specific type", - )); - } - - err -} diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 556b3bd063d..7bc3af374fc 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -3,7 +3,6 @@ //! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html mod engine; -pub mod error_reporting; mod project; mod structural_impls; pub mod util; |
