diff options
Diffstat (limited to 'compiler/rustc_trait_selection/src')
66 files changed, 13200 insertions, 1414 deletions
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs new file mode 100644 index 00000000000..bff2a184b19 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -0,0 +1,2201 @@ +//! 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; +use std::path::PathBuf; +use std::{cmp, fmt, iter}; + +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_errors::{pluralize, Applicability, Diag, 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, TypeError, TypeErrorToStringExt}; +use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError, PrintTraitRefExt as _}; +use rustc_middle::ty::{ + self, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, +}; +use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span}; +use rustc_target::spec::abi; +use tracing::{debug, instrument}; + +use crate::error_reporting::TypeErrCtxt; +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, +}; + +mod note_and_explain; +mod suggest; + +pub mod need_type_info; +pub mod nice_region_error; +pub mod region; +pub mod sub_relations; + +/// 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 +} + +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) + } + + 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() + }) + } + + /// 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(sig_tys2, hdr2)) => { + let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1); + let mut values = self.cmp_fn_sig(&sig1, &sig_tys2.with(*hdr2)); + values.0.push_highlighted(format!( + " {{{}}}", + self.tcx.def_path_str_with_args(*did1, args1) + )); + values + } + + (ty::FnPtr(sig_tys1, hdr1), ty::FnDef(did2, args2)) => { + let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2); + let mut values = self.cmp_fn_sig(&sig_tys1.with(*hdr1), &sig2); + values + .1 + .push_normal(format!(" {{{}}}", self.tcx.def_path_str_with_args(*did2, args2))); + values + } + + (ty::FnPtr(sig_tys1, hdr1), ty::FnPtr(sig_tys2, hdr2)) => { + self.cmp_fn_sig(&sig_tys1.with(*hdr1), &sig_tys2.with(*hdr2)) + } + + _ => { + 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, + } + } +} diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs new file mode 100644 index 00000000000..a93b633ce1a --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -0,0 +1,1314 @@ +use std::borrow::Cow; +use std::iter; +use std::path::PathBuf; + +use rustc_errors::codes::*; +use rustc_errors::{Diag, IntoDiagArg}; +use rustc_hir as hir; +use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; +use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; +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::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, FileName, Span, DUMMY_SP}; +use tracing::{debug, instrument, warn}; + +use crate::error_reporting::TypeErrCtxt; +use crate::errors::{ + AmbiguousImpl, AmbiguousReturn, AnnotationRequired, InferenceBadError, + SourceKindMultiSuggestion, SourceKindSubdiag, +}; +use crate::infer::InferCtxt; + +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 var_origin = infcx.type_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| Some(infcx.tcx.item_name(infcx.const_var_origin(ct_vid)?.param_def_id?)); + 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<'a, 'tcx> TypeErrCtxt<'a, '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 var_origin = self.infcx.type_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 = self.const_var_origin(vid).expect("expected unresolved const var"); + 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<'a> { + 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: false, + path: Default::default(), + time_version: false, + }), + TypeAnnotationNeeded::E0283 => self.dcx().create_err(AmbiguousImpl { + span, + source_kind, + source_name, + failure_span, + infer_subdiags, + multi_suggestions, + bad_label, + was_written: false, + 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: false, + path: Default::default(), + }), + } + } + + #[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.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.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, + )); + } + } + } + + let time_version = + self.detect_old_time_crate_version(failure_span, &kind, &mut infer_subdiags); + + 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.is_some(), + path: path.unwrap_or_default(), + time_version, + }), + 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.is_some(), + 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.is_some(), + path: path.unwrap_or_default(), + }), + } + } + + /// Detect the inference regression on crate `time` <= 0.3.35 and emit a more targeted error. + /// <https://github.com/rust-lang/rust/issues/127343> + // FIXME: we should figure out a more generic version of doing this, ideally in cargo itself. + fn detect_old_time_crate_version( + &self, + span: Option<Span>, + kind: &InferSourceKind<'_>, + // We will clear the non-actionable suggestion from the error to reduce noise. + infer_subdiags: &mut Vec<SourceKindSubdiag<'_>>, + ) -> bool { + // FIXME(#129461): We are time-boxing this code in the compiler. It'll start failing + // compilation once we promote 1.89 to beta, which will happen in 9 months from now. + #[cfg(not(version("1.89")))] + const fn version_check() {} + #[cfg(version("1.89"))] + const fn version_check() { + panic!("remove this check as presumably the ecosystem has moved from needing it"); + } + const { version_check() }; + // Only relevant when building the `time` crate. + if self.infcx.tcx.crate_name(LOCAL_CRATE) == sym::time + && let Some(span) = span + && let InferSourceKind::LetBinding { pattern_name, .. } = kind + && let Some(name) = pattern_name + && name.as_str() == "items" + && let FileName::Real(file) = self.infcx.tcx.sess.source_map().span_to_filename(span) + { + let path = file.local_path_if_available().to_string_lossy(); + if path.contains("format_description") && path.contains("parse") { + infer_subdiags.clear(); + return true; + } + } + false + } +} + +#[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.root_const_var(a_vid) == self.tecx.root_const_var(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 other kinds, + // such as `TyAlias` or `AssocTy`. 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::Struct | DefKind::Enum | DefKind::Union, _)) => { + 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_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs new file mode 100644 index 00000000000..47e9a3755e8 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs @@ -0,0 +1,160 @@ +//! Error Reporting for Anonymous Region Lifetime Errors +//! where both the regions are anonymous. + +use rustc_errors::{Diag, ErrorGuaranteed, Subdiagnostic}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::Ty; +use rustc_middle::ty::{Region, TyCtxt}; +use tracing::debug; + +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, LifetimeMismatch, LifetimeMismatchLabels}; +use crate::infer::{RegionResolutionError, SubregionOrigin}; + +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_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs new file mode 100644 index 00000000000..7a44c2ad661 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs @@ -0,0 +1,238 @@ +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}; +use tracing::debug; + +/// 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.to_def_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.to_def_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.to_def_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.to_def_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_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs new file mode 100644 index 00000000000..4fe26fa81af --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs @@ -0,0 +1,128 @@ +//! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate +//! to hold. + +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; +use tracing::debug; + +use crate::error_reporting::infer::nice_region_error::NiceRegionError; +use crate::errors::{ + note_and_explain, DoesNotOutliveStaticFromImpl, ImplicitStaticLifetimeSubdiag, + IntroducesStaticBecauseUnmetLifetimeReq, MismatchedStaticLifetime, +}; +use crate::infer::{RegionResolutionError, SubregionOrigin, TypeTrace}; +use crate::traits::ObligationCauseCode; + +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_trait_selection/src/error_reporting/infer/nice_region_error/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mod.rs new file mode 100644 index 00000000000..79a770ac9b3 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mod.rs @@ -0,0 +1,94 @@ +use rustc_errors::{Diag, ErrorGuaranteed}; +use rustc_hir::def_id::LocalDefId; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::Span; + +use crate::error_reporting::TypeErrCtxt; +use crate::infer::RegionResolutionError; +use crate::infer::RegionResolutionError::*; + +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_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs new file mode 100644 index 00000000000..a7a1fa1c2b4 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs @@ -0,0 +1,94 @@ +//! Error Reporting for Anonymous Region Lifetime Errors +//! where one region is named and the other is anonymous. + +use rustc_errors::Diag; +use rustc_middle::ty; +use rustc_span::symbol::kw; +use tracing::debug; + +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; + +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_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs new file mode 100644 index 00000000000..2b7927367d8 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs @@ -0,0 +1,495 @@ +use std::fmt; + +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::{self, GenericArgsRef, RePlaceholder, Region, TyCtxt}; +use tracing::{debug, instrument}; + +use crate::error_reporting::infer::nice_region_error::NiceRegionError; +use crate::errors::{ + ActualImplExpectedKind, ActualImplExpectedLifetimeKind, ActualImplExplNotes, + TraitPlaceholderMismatch, TyOrSig, +}; +use crate::infer::{RegionResolutionError, SubregionOrigin, TypeTrace, ValuePairs}; +use crate::traits::{ObligationCause, ObligationCauseCode}; + +// 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_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs new file mode 100644 index 00000000000..f2a7da707b8 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs @@ -0,0 +1,87 @@ +use rustc_data_structures::intern::Interned; +use rustc_errors::Diag; +use rustc_middle::ty::{self, RePlaceholder, Region}; + +use crate::error_reporting::infer::nice_region_error::NiceRegionError; +use crate::errors::PlaceholderRelationLfNotSatisfied; +use crate::infer::{RegionResolutionError, SubregionOrigin}; + +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_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs new file mode 100644 index 00000000000..95ebeab13ef --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -0,0 +1,618 @@ +//! Error Reporting for static impl Traits. + +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::def_id::LocalDefId; +use rustc_span::symbol::Ident; +use rustc_span::Span; +use tracing::debug; + +use crate::error_reporting::infer::nice_region_error::NiceRegionError; +use crate::errors::{ + ButCallingIntroduces, ButNeedsToSatisfy, DynTraitConstraintSuggestion, MoreTargeted, + ReqIntroducedLocations, +}; +use crate::infer::{RegionResolutionError, SubregionOrigin, TypeTrace}; +use crate::traits::{ObligationCauseCode, UnifyReceiverContext}; + +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_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs new file mode 100644 index 00000000000..592ade8ede2 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs @@ -0,0 +1,163 @@ +//! Error Reporting for `impl` items that do not match the obligations from their `trait`. + +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; +use tracing::debug; + +use crate::error_reporting::infer::nice_region_error::NiceRegionError; +use crate::errors::{ConsiderBorrowingParamHelp, RelationshipHelp, TraitImplDiff}; +use crate::infer::{RegionResolutionError, Subtype, ValuePairs}; + +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_trait_selection/src/error_reporting/infer/nice_region_error/util.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs new file mode 100644 index 00000000000..90b354305ff --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs @@ -0,0 +1,167 @@ +//! 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 tracing::instrument; + +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_trait_selection/src/error_reporting/infer/note.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs new file mode 100644 index 00000000000..e38b8e2f3d6 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs @@ -0,0 +1,422 @@ +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 tracing::debug; + +use super::ObligationCauseAsDiagArg; +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}; + +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_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs new file mode 100644 index 00000000000..db71331d07f --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -0,0 +1,954 @@ +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::{ObligationCause, ObligationCauseCode}; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; +use rustc_middle::ty::print::{FmtPrinter, Printer}; +use rustc_middle::ty::{self, suggest_constraining_type_param, Ty}; +use rustc_span::def_id::DefId; +use rustc_span::{sym, BytePos, Span, Symbol}; +use tracing::debug; + +use crate::error_reporting::TypeErrCtxt; +use crate::infer::InferCtxtExt; + +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 has_matching_impl = false; + tcx.for_each_relevant_impl(def_id, values.found, |did| { + if DeepRejectCtxt::new(tcx, TreatParams::ForLookup) + .types_may_unify(values.found, tcx.type_of(did).skip_binder()) + { + has_matching_impl = true; + } + }); + if has_matching_impl { + 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 has_matching_impl = false; + tcx.for_each_relevant_impl(def_id, values.expected, |did| { + if DeepRejectCtxt::new(tcx, TreatParams::ForLookup) + .types_may_unify(values.expected, tcx.type_of(did).skip_binder()) + { + has_matching_impl = true; + } + }); + if has_matching_impl { + 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 has_matching_impl = false; + tcx.for_each_relevant_impl(def_id, values.found, |did| { + if DeepRejectCtxt::new(tcx, TreatParams::ForLookup) + .types_may_unify(values.found, tcx.type_of(did).skip_binder()) + { + has_matching_impl = true; + } + }); + if has_matching_impl { + 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(_, hdr), ty::FnDef(def_id, _)) + | (ty::FnDef(def_id, _), ty::FnPtr(_, hdr)) => { + if tcx.fn_sig(def_id).skip_binder().safety() < hdr.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(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(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_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs new file mode 100644 index 00000000000..e4a4ec125a5 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -0,0 +1,1430 @@ +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 tracing::{debug, instrument}; + +use super::nice_region_error::find_anon_type; +use super::ObligationCauseAsDiagArg; +use crate::error_reporting::infer::ObligationCauseExt; +use crate::error_reporting::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::trait_selection_reborrow } + .add_to_diag(err) + } + infer::RelateObjectBound(span) => { + RegionOriginNote::Plain { span, msg: fluent::trait_selection_relate_object_bound } + .add_to_diag(err); + } + infer::ReferenceOutlivesReferent(ty, span) => { + RegionOriginNote::WithName { + span, + msg: fluent::trait_selection_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::trait_selection_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::trait_selection_relate_param_bound_2, + } + .add_to_diag(err); + } + } + infer::RelateRegionParamBound(span, _) => { + RegionOriginNote::Plain { + span, + msg: fluent::trait_selection_relate_region_param_bound, + } + .add_to_diag(err); + } + infer::CompareImplItemObligation { span, .. } => { + RegionOriginNote::Plain { + span, + msg: fluent::trait_selection_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::trait_selection_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, ty) => { + 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 mut alt_span = None; + if let Some(ty) = ty + && sub.is_static() + && let ty::Dynamic(preds, _, ty::DynKind::Dyn) = ty.kind() + && let Some(def_id) = preds.principal_def_id() + { + for (clause, span) in + self.tcx.predicates_of(def_id).instantiate_identity(self.tcx) + { + if let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(a, b)) = + clause.kind().skip_binder() + && let ty::Param(param) = a.kind() + && param.name == kw::SelfUpper + && b.is_static() + { + // Point at explicit `'static` bound on the trait (`trait T: 'static`). + alt_span = Some(span); + } + } + } + let param_must_outlive = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + alt_span, + 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.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.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 param_def_id = tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id; + let span = tcx.def_span(param_def_id); + 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(param_def_id, name) => { + let span = tcx.def_span(param_def_id); + 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(_) = tcx.is_suitable_region(generic_param_scope, hidden_region) { + suggest_precise_capturing(tcx, opaque_ty_key.def_id, hidden_region, &mut err); + } + } + 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_trait_selection/src/error_reporting/infer/sub_relations.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/sub_relations.rs new file mode 100644 index 00000000000..ef26a8ff7b8 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/sub_relations.rs @@ -0,0 +1,81 @@ +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_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs new file mode 100644 index 00000000000..50cbdcc6151 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -0,0 +1,900 @@ +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, 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 tracing::debug; + +use crate::error_reporting::infer::hir::Path; +use crate::error_reporting::TypeErrCtxt; +use crate::errors::{ + ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, + FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding, + SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags, +}; + +#[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_tys, hdr), ty::FnDef(did, args)) => { + let sig = sig_tys.with(*hdr); + 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 } + } + (false, false) => { + diag.subdiagnostic(FnItemsAreDistinct); + FunctionPointerSuggestion::Cast { span, fn_name, 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_tys, hdr)) => { + let expected_sig = + &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args)); + let found_sig = &(self.normalize_fn_sig)(sig_tys.with(*hdr)); + + 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_trait_selection/src/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/mod.rs index f6ac8fc7b61..cb7efeaae0b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/mod.rs @@ -1 +1,73 @@ +use std::ops::Deref; + +use rustc_errors::DiagCtxtHandle; +use rustc_infer::infer::InferCtxt; +use rustc_infer::traits::PredicateObligation; +use rustc_macros::extension; +use rustc_middle::bug; +use rustc_middle::ty::{self, Ty}; + +use crate::error_reporting::infer::sub_relations; + +pub mod infer; pub mod traits; + +/// 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>, +} + +#[extension(pub trait InferCtxtErrorExt<'tcx>)] +impl<'tcx> InferCtxt<'tcx> { + /// Creates a `TypeErrCtxt` for emitting various inference errors. + /// During typeck, use `FnCtxt::err_ctxt` instead. + 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![])] + }), + } + } +} + +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 + } +} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index c301deac616..79c1f722280 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -8,20 +8,18 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor as _; use rustc_hir::LangItem; -use rustc_infer::infer::error_reporting::{TypeAnnotationNeeded, TypeErrCtxt}; use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt}; use rustc_infer::traits::util::elaborate; use rustc_infer::traits::{ Obligation, ObligationCause, ObligationCauseCode, PolyTraitObligation, PredicateObligation, }; -use rustc_macros::extension; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable as _, TypeVisitableExt as _}; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; +use tracing::{debug, instrument}; -use crate::error_reporting::traits::suggestions::TypeErrCtxtExt as _; -use crate::error_reporting::traits::{ - to_pretty_impl_header, FindExprBySpan, InferCtxtPrivExt as _, -}; +use crate::error_reporting::infer::need_type_info::TypeAnnotationNeeded; +use crate::error_reporting::traits::{to_pretty_impl_header, FindExprBySpan}; +use crate::error_reporting::TypeErrCtxt; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::ObligationCtxt; @@ -152,10 +150,12 @@ pub fn compute_applicable_impls_for_diagnostics<'tcx>( ambiguities } -#[extension(pub trait TypeErrCtxtAmbiguityExt<'a, 'tcx>)] impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug")] - fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) -> ErrorGuaranteed { + pub(super) fn maybe_report_ambiguity( + &self, + obligation: &PredicateObligation<'tcx>, + ) -> ErrorGuaranteed { // Unable to successfully determine, probably means // insufficient type information, but could mean // ambiguous impls. The latter *ought* to be a @@ -389,39 +389,67 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_impls.non_blanket_impls().values().flatten().count(); // If there is only one implementation of the trait, suggest using it. // Otherwise, use a placeholder comment for the implementation. - let (message, self_type) = if non_blanket_impl_count == 1 { + let (message, self_types) = if non_blanket_impl_count == 1 { ( "use the fully-qualified path to the only available \ implementation", - format!( + vec![format!( "{}", self.tcx.type_of(impl_def_id).instantiate_identity() - ), + )], + ) + } else if non_blanket_impl_count < 20 { + ( + "use a fully-qualified path to one of the available \ + implementations", + trait_impls + .non_blanket_impls() + .values() + .flatten() + .map(|id| { + format!( + "{}", + self.tcx.type_of(id).instantiate_identity() + ) + }) + .collect::<Vec<String>>(), ) } else { ( "use a fully-qualified path to a specific available \ implementation", - "/* self type */".to_string(), + vec!["/* self type */".to_string()], ) }; - let mut suggestions = - vec![(path.span.shrink_to_lo(), format!("<{self_type} as "))]; - if let Some(generic_arg) = trait_path_segment.args { - let between_span = - trait_path_segment.ident.span.between(generic_arg.span_ext); - // get rid of :: between Trait and <type> - // must be '::' between them, otherwise the parser won't accept the code - suggestions.push((between_span, "".to_string())); - suggestions - .push((generic_arg.span_ext.shrink_to_hi(), ">".to_string())); - } else { - suggestions.push(( - trait_path_segment.ident.span.shrink_to_hi(), - ">".to_string(), - )); - } - err.multipart_suggestion( + let suggestions: Vec<_> = self_types + .into_iter() + .map(|self_type| { + let mut suggestions = vec![( + path.span.shrink_to_lo(), + format!("<{self_type} as "), + )]; + if let Some(generic_arg) = trait_path_segment.args { + let between_span = trait_path_segment + .ident + .span + .between(generic_arg.span_ext); + // get rid of :: between Trait and <type> + // must be '::' between them, otherwise the parser won't accept the code + suggestions.push((between_span, "".to_string())); + suggestions.push(( + generic_arg.span_ext.shrink_to_hi(), + ">".to_string(), + )); + } else { + suggestions.push(( + trait_path_segment.ident.span.shrink_to_hi(), + ">".to_string(), + )); + } + suggestions + }) + .collect(); + err.multipart_suggestions( message, suggestions, Applicability::MaybeIncorrect, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 2e6247b4640..5918686213a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1,33 +1,18 @@ -use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _}; -use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _}; -use crate::error_reporting::traits::infer_ctxt_ext::InferCtxtExt; -use crate::errors::{ - AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, -}; -use crate::infer::error_reporting::TyCategory; -use crate::infer::InferCtxtExt as _; -use crate::infer::{self, InferCtxt}; -use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use crate::traits::NormalizeExt; -use crate::traits::{ - elaborate, MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, - ObligationCtxt, Overflow, PredicateObligation, SelectionError, SignatureMismatch, - TraitNotObjectSafe, -}; use core::ops::ControlFlow; +use std::borrow::Cow; + use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; -use rustc_errors::{pluralize, struct_span_code_err, Applicability, StringPart}; -use rustc_errors::{Diag, ErrorGuaranteed, StashKey}; +use rustc_errors::{ + pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, + StringPart, +}; use rustc_hir::def::Namespace; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::intravisit::Visitor; -use rustc_hir::Node; -use rustc_hir::{self as hir, LangItem}; -use rustc_infer::infer::error_reporting::TypeErrCtxt; +use rustc_hir::{self as hir, LangItem, Node}; use rustc_infer::infer::{InferOk, TypeTrace}; -use rustc_macros::extension; use rustc_middle::traits::select::OverflowError; use rustc_middle::traits::SignatureMismatchData; use rustc_middle::ty::abstract_const::NotConstEvaluatable; @@ -43,20 +28,32 @@ use rustc_middle::ty::{ use rustc_middle::{bug, span_bug}; use rustc_span::symbol::sym; use rustc_span::{BytePos, Span, Symbol, DUMMY_SP}; -use std::borrow::Cow; +use tracing::{debug, instrument}; +use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote}; +use super::suggestions::get_explanation_based_on_obligation; use super::{ ArgKind, CandidateSimilarity, GetSafeTransmuteErrorAndReason, ImplCandidate, UnsatisfiedConst, }; +use crate::error_reporting::infer::TyCategory; +use crate::error_reporting::traits::report_object_safety_error; +use crate::error_reporting::TypeErrCtxt; +use crate::errors::{ + AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, +}; +use crate::infer::{self, InferCtxt, InferCtxtExt as _}; +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use crate::traits::{ + elaborate, MismatchedProjectionTypes, NormalizeExt, Obligation, ObligationCause, + ObligationCauseCode, ObligationCtxt, Overflow, PredicateObligation, SelectionError, + SignatureMismatch, TraitNotObjectSafe, +}; -pub use rustc_infer::traits::error_reporting::*; - -#[extension(pub trait TypeErrCtxtSelectionErrExt<'a, 'tcx>)] impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// The `root_obligation` parameter should be the `root_obligation` field /// from a `FulfillmentError`. If no `FulfillmentError` is available, /// then it should be the same as `obligation`. - fn report_selection_error( + pub fn report_selection_error( &self, mut obligation: PredicateObligation<'tcx>, root_obligation: &PredicateObligation<'tcx>, @@ -173,7 +170,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { return guar; } - // Silence redundant errors on binding acccess that are already + // Silence redundant errors on binding access that are already // reported on the binding definition (#56607). if let Err(guar) = self.fn_arg_obligation(&obligation) { return guar; @@ -234,8 +231,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { post_message, ); - let (err_msg, safe_transmute_explanation) = if Some(main_trait_ref.def_id()) - == self.tcx.lang_items().transmute_trait() + let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(main_trait_ref.def_id(), LangItem::TransmuteTrait) { // Recompute the safe transmute reason and use that for the error reporting match self.get_safe_transmute_error_and_reason( @@ -379,7 +375,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let impl_candidates = self.find_similar_impl_candidates(leaf_trait_predicate); suggested = if let &[cand] = &impl_candidates[..] { let cand = cand.trait_ref; - if let (ty::FnPtr(_), ty::FnDef(..)) = + if let (ty::FnPtr(..), ty::FnDef(..)) = (cand.self_ty().kind(), main_trait_ref.self_ty().skip_binder().kind()) { err.span_suggestion( @@ -682,17 +678,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } -#[extension(pub(super) trait TypeErrCtxtExt<'a, 'tcx>)] impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - fn apply_do_not_recommend(&self, obligation: &mut PredicateObligation<'tcx>) -> bool { + pub(super) fn apply_do_not_recommend( + &self, + obligation: &mut PredicateObligation<'tcx>, + ) -> bool { let mut base_cause = obligation.cause.code().clone(); let mut applied_do_not_recommend = false; loop { if let ObligationCauseCode::ImplDerived(ref c) = base_cause { - if self.tcx.has_attrs_with_path( - c.impl_or_alias_def_id, - &[sym::diagnostic, sym::do_not_recommend], - ) { + if self.tcx.do_not_recommend_impl(c.impl_or_alias_def_id) { let code = (*c.derived.parent_code).clone(); obligation.cause.map_code(|_| code); obligation.predicate = c.derived.parent_trait_pred.upcast(self.tcx); @@ -795,8 +790,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // is unimplemented is because async closures don't implement `Fn`/`FnMut` // if they have captures. if let Some(by_ref_captures) = by_ref_captures - && let ty::FnPtr(sig) = by_ref_captures.kind() - && !sig.skip_binder().output().is_unit() + && let ty::FnPtr(sig_tys, _) = by_ref_captures.kind() + && !sig_tys.skip_binder().output().is_unit() { let mut err = self.dcx().create_err(AsyncClosureNotFn { span: self.tcx.def_span(closure_def_id), @@ -903,7 +898,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut suggested = false; let mut chain = vec![]; - // The following logic is simlar to `point_at_chain`, but that's focused on associated types + // The following logic is similar to `point_at_chain`, but that's focused on associated types let mut expr = expr; while let hir::ExprKind::MethodCall(path_segment, rcvr_expr, args, span) = expr.kind { // Point at every method call in the chain with the `Result` type. @@ -946,8 +941,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // The current method call returns `Result<_, ()>` && self.can_eq(obligation.param_env, ty, found_ty) // There's a single argument in the method call and it is a closure - && args.len() == 1 - && let Some(arg) = args.get(0) + && let [arg] = args && let hir::ExprKind::Closure(closure) = arg.kind // The closure has a block for its body with no tail expression && let body = self.tcx.hir().body(closure.body) @@ -1063,7 +1057,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { "`{ty}` is forbidden as the type of a const generic parameter", ) } - ty::FnPtr(_) => { + ty::FnPtr(..) => { struct_span_code_err!( self.dcx(), span, @@ -1142,7 +1136,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } -#[extension(pub(super) trait InferCtxtPrivExt<'a, 'tcx>)] impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn can_match_trait( &self, @@ -1182,7 +1175,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // returns if `cond` not occurring implies that `error` does not occur - i.e., that // `error` occurring implies that `cond` occurs. #[instrument(level = "debug", skip(self), ret)] - fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool { + pub(super) fn error_implies( + &self, + cond: ty::Predicate<'tcx>, + error: ty::Predicate<'tcx>, + ) -> bool { if cond == error { return true; } @@ -1205,7 +1202,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip_all)] - fn report_projection_error( + pub(super) fn report_projection_error( &self, obligation: &PredicateObligation<'tcx>, error: &MismatchedProjectionTypes<'tcx>, @@ -1455,7 +1452,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn fuzzy_match_tys( + pub fn fuzzy_match_tys( &self, mut a: Ty<'tcx>, mut b: Ty<'tcx>, @@ -1535,7 +1532,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str { + pub(super) fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str { match kind { hir::ClosureKind::Closure => "a closure", hir::ClosureKind::Coroutine(hir::CoroutineKind::Coroutine(_)) => "a coroutine", @@ -1585,7 +1582,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn find_similar_impl_candidates( + pub(super) fn find_similar_impl_candidates( &self, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Vec<ImplCandidate<'tcx>> { @@ -1615,7 +1612,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { candidates } - fn report_similar_impl_candidates( + pub(super) fn report_similar_impl_candidates( &self, impl_candidates: &[ImplCandidate<'tcx>], trait_ref: ty::PolyTraitRef<'tcx>, @@ -1624,9 +1621,127 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { other: bool, param_env: ty::ParamEnv<'tcx>, ) -> bool { - // If we have a single implementation, try to unify it with the trait ref - // that failed. This should uncover a better hint for what *is* implemented. + let alternative_candidates = |def_id: DefId| { + let mut impl_candidates: Vec<_> = self + .tcx + .all_impls(def_id) + // ignore `do_not_recommend` items + .filter(|def_id| !self.tcx.do_not_recommend_impl(*def_id)) + // Ignore automatically derived impls and `!Trait` impls. + .filter_map(|def_id| self.tcx.impl_trait_header(def_id)) + .filter_map(|header| { + (header.polarity != ty::ImplPolarity::Negative + || self.tcx.is_automatically_derived(def_id)) + .then(|| header.trait_ref.instantiate_identity()) + }) + .filter(|trait_ref| { + let self_ty = trait_ref.self_ty(); + // Avoid mentioning type parameters. + if let ty::Param(_) = self_ty.kind() { + false + } + // Avoid mentioning types that are private to another crate + else if let ty::Adt(def, _) = self_ty.peel_refs().kind() { + // FIXME(compiler-errors): This could be generalized, both to + // be more granular, and probably look past other `#[fundamental]` + // types, too. + self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx) + } else { + true + } + }) + .collect(); + + impl_candidates.sort_by_key(|tr| tr.to_string()); + impl_candidates.dedup(); + impl_candidates + }; + + // We'll check for the case where the reason for the mismatch is that the trait comes from + // one crate version and the type comes from another crate version, even though they both + // are from the same crate. + let trait_def_id = trait_ref.def_id(); + if let ty::Adt(def, _) = trait_ref.self_ty().skip_binder().peel_refs().kind() + && let found_type = def.did() + && trait_def_id.krate != found_type.krate + && self.tcx.crate_name(trait_def_id.krate) == self.tcx.crate_name(found_type.krate) + { + let name = self.tcx.crate_name(trait_def_id.krate); + let spans: Vec<_> = [trait_def_id, found_type] + .into_iter() + .filter_map(|def_id| self.tcx.extern_crate(def_id.krate)) + .map(|data| { + let dependency = if data.dependency_of == LOCAL_CRATE { + "direct dependency of the current crate".to_string() + } else { + let dep = self.tcx.crate_name(data.dependency_of); + format!("dependency of crate `{dep}`") + }; + ( + data.span, + format!("one version of crate `{name}` is used here, as a {dependency}"), + ) + }) + .collect(); + let mut span: MultiSpan = spans.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into(); + for (sp, label) in spans.into_iter() { + span.push_span_label(sp, label); + } + err.highlighted_span_help( + span, + vec![ + StringPart::normal("there are ".to_string()), + StringPart::highlighted("multiple different versions".to_string()), + StringPart::normal(" of crate `".to_string()), + StringPart::highlighted(format!("{name}")), + StringPart::normal("` in the dependency graph".to_string()), + ], + ); + let candidates = if impl_candidates.is_empty() { + alternative_candidates(trait_def_id) + } else { + impl_candidates.into_iter().map(|cand| cand.trait_ref).collect() + }; + if let Some((sp_candidate, sp_found)) = candidates.iter().find_map(|trait_ref| { + if let ty::Adt(def, _) = trait_ref.self_ty().peel_refs().kind() + && let candidate_def_id = def.did() + && let Some(name) = self.tcx.opt_item_name(candidate_def_id) + && let Some(found) = self.tcx.opt_item_name(found_type) + && name == found + && candidate_def_id.krate != found_type.krate + && self.tcx.crate_name(candidate_def_id.krate) + == self.tcx.crate_name(found_type.krate) + { + // A candidate was found of an item with the same name, from two separate + // versions of the same crate, let's clarify. + Some((self.tcx.def_span(candidate_def_id), self.tcx.def_span(found_type))) + } else { + None + } + }) { + let mut span: MultiSpan = vec![sp_candidate, sp_found].into(); + span.push_span_label(self.tcx.def_span(trait_def_id), "this is the required trait"); + span.push_span_label(sp_candidate, "this type implements the required trait"); + span.push_span_label(sp_found, "this type doesn't implement the required trait"); + err.highlighted_span_note( + span, + vec![ + StringPart::normal( + "two types coming from two different versions of the same crate are \ + different types " + .to_string(), + ), + StringPart::highlighted("even if they look the same".to_string()), + ], + ); + } + err.help("you can use `cargo tree` to explore your dependency tree"); + return true; + } + if let [single] = &impl_candidates { + // If we have a single implementation, try to unify it with the trait ref + // that failed. This should uncover a better hint for what *is* implemented. if self.probe(|_| { let ocx = ObligationCtxt::new(self); @@ -1721,10 +1836,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let &[cand] = &candidates[..] { let (desc, mention_castable) = match (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) { - (ty::FnPtr(_), ty::FnDef(..)) => { + (ty::FnPtr(..), ty::FnDef(..)) => { (" implemented for fn pointer `", ", cast using `as`") } - (ty::FnPtr(_), _) => (" implemented for fn pointer `", ""), + (ty::FnPtr(..), _) => (" implemented for fn pointer `", ""), _ => (" implemented for `", ""), }; err.highlighted_help(vec![ @@ -1781,12 +1896,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let impl_candidates = impl_candidates .into_iter() .cloned() - .filter(|cand| { - !self.tcx.has_attrs_with_path( - cand.impl_def_id, - &[sym::diagnostic, sym::do_not_recommend], - ) - }) + .filter(|cand| !self.tcx.do_not_recommend_impl(cand.impl_def_id)) .collect::<Vec<_>>(); let def_id = trait_ref.def_id(); @@ -1798,43 +1908,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Mentioning implementers of `Copy`, `Debug` and friends is not useful. return false; } - let mut impl_candidates: Vec<_> = self - .tcx - .all_impls(def_id) - // ignore `do_not_recommend` items - .filter(|def_id| { - !self - .tcx - .has_attrs_with_path(*def_id, &[sym::diagnostic, sym::do_not_recommend]) - }) - // Ignore automatically derived impls and `!Trait` impls. - .filter_map(|def_id| self.tcx.impl_trait_header(def_id)) - .filter_map(|header| { - (header.polarity != ty::ImplPolarity::Negative - || self.tcx.is_automatically_derived(def_id)) - .then(|| header.trait_ref.instantiate_identity()) - }) - .filter(|trait_ref| { - let self_ty = trait_ref.self_ty(); - // Avoid mentioning type parameters. - if let ty::Param(_) = self_ty.kind() { - false - } - // Avoid mentioning types that are private to another crate - else if let ty::Adt(def, _) = self_ty.peel_refs().kind() { - // FIXME(compiler-errors): This could be generalized, both to - // be more granular, and probably look past other `#[fundamental]` - // types, too. - self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx) - } else { - true - } - }) - .collect(); - - impl_candidates.sort_by_key(|tr| tr.to_string()); - impl_candidates.dedup(); - return report(impl_candidates, err); + return report(alternative_candidates(def_id), err); } // Sort impl candidates so that ordering is consistent for UI tests. @@ -1989,7 +2063,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// `trait_ref`. /// /// For this to work, `new_self_ty` must have no escaping bound variables. - fn mk_trait_obligation_with_new_self_ty( + pub(super) fn mk_trait_obligation_with_new_self_ty( &self, param_env: ty::ParamEnv<'tcx>, trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>, @@ -2041,7 +2115,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) } - fn note_obligation_cause(&self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>) { + pub fn note_obligation_cause( + &self, + err: &mut Diag<'_>, + obligation: &PredicateObligation<'tcx>, + ) { // First, attempt to add note to this error with an async-await-specific // message, and fall back to regular note otherwise. if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { @@ -2067,7 +2145,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn is_recursive_obligation( + pub(super) fn is_recursive_obligation( &self, obligated_types: &mut Vec<Ty<'tcx>>, cause_code: &ObligationCauseCode<'tcx>, @@ -2596,7 +2674,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) .unwrap_or((found_span, None, found)); - self.infcx.report_arg_count_mismatch( + self.report_arg_count_mismatch( span, closure_span, expected, @@ -2608,6 +2686,242 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) } + /// Given some node representing a fn-like thing in the HIR map, + /// returns a span and `ArgKind` information that describes the + /// arguments it expects. This can be supplied to + /// `report_arg_count_mismatch`. + pub fn get_fn_like_arguments( + &self, + node: Node<'_>, + ) -> Option<(Span, Option<Span>, Vec<ArgKind>)> { + let sm = self.tcx.sess.source_map(); + let hir = self.tcx.hir(); + Some(match node { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }), + .. + }) => ( + fn_decl_span, + fn_arg_span, + hir.body(body) + .params + .iter() + .map(|arg| { + if let hir::Pat { kind: hir::PatKind::Tuple(args, _), span, .. } = *arg.pat + { + Some(ArgKind::Tuple( + Some(span), + args.iter() + .map(|pat| { + sm.span_to_snippet(pat.span) + .ok() + .map(|snippet| (snippet, "_".to_owned())) + }) + .collect::<Option<Vec<_>>>()?, + )) + } else { + let name = sm.span_to_snippet(arg.pat.span).ok()?; + Some(ArgKind::Arg(name, "_".to_owned())) + } + }) + .collect::<Option<Vec<ArgKind>>>()?, + ), + Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. }) + | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. }) + | Node::TraitItem(&hir::TraitItem { + kind: hir::TraitItemKind::Fn(ref sig, _), .. + }) + | Node::ForeignItem(&hir::ForeignItem { + kind: hir::ForeignItemKind::Fn(ref sig, _, _), + .. + }) => ( + sig.span, + None, + sig.decl + .inputs + .iter() + .map(|arg| match arg.kind { + hir::TyKind::Tup(tys) => ArgKind::Tuple( + Some(arg.span), + vec![("_".to_owned(), "_".to_owned()); tys.len()], + ), + _ => ArgKind::empty(), + }) + .collect::<Vec<ArgKind>>(), + ), + Node::Ctor(variant_data) => { + let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); + (span, None, vec![ArgKind::empty(); variant_data.fields().len()]) + } + _ => panic!("non-FnLike node found: {node:?}"), + }) + } + + /// Reports an error when the number of arguments needed by a + /// trait match doesn't match the number that the expression + /// provides. + pub fn report_arg_count_mismatch( + &self, + span: Span, + found_span: Option<Span>, + expected_args: Vec<ArgKind>, + found_args: Vec<ArgKind>, + is_closure: bool, + closure_arg_span: Option<Span>, + ) -> Diag<'a> { + let kind = if is_closure { "closure" } else { "function" }; + + let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { + let arg_length = arguments.len(); + let distinct = matches!(other, &[ArgKind::Tuple(..)]); + match (arg_length, arguments.get(0)) { + (1, Some(ArgKind::Tuple(_, fields))) => { + format!("a single {}-tuple as argument", fields.len()) + } + _ => format!( + "{} {}argument{}", + arg_length, + if distinct && arg_length > 1 { "distinct " } else { "" }, + pluralize!(arg_length) + ), + } + }; + + let expected_str = args_str(&expected_args, &found_args); + let found_str = args_str(&found_args, &expected_args); + + let mut err = struct_span_code_err!( + self.dcx(), + span, + E0593, + "{} is expected to take {}, but it takes {}", + kind, + expected_str, + found_str, + ); + + err.span_label(span, format!("expected {kind} that takes {expected_str}")); + + if let Some(found_span) = found_span { + err.span_label(found_span, format!("takes {found_str}")); + + // Suggest to take and ignore the arguments with expected_args_length `_`s if + // found arguments is empty (assume the user just wants to ignore args in this case). + // For example, if `expected_args_length` is 2, suggest `|_, _|`. + if found_args.is_empty() && is_closure { + let underscores = vec!["_"; expected_args.len()].join(", "); + err.span_suggestion_verbose( + closure_arg_span.unwrap_or(found_span), + format!( + "consider changing the closure to take and ignore the expected argument{}", + pluralize!(expected_args.len()) + ), + format!("|{underscores}|"), + Applicability::MachineApplicable, + ); + } + + if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { + if fields.len() == expected_args.len() { + let sugg = fields + .iter() + .map(|(name, _)| name.to_owned()) + .collect::<Vec<String>>() + .join(", "); + err.span_suggestion_verbose( + found_span, + "change the closure to take multiple arguments instead of a single tuple", + format!("|{sugg}|"), + Applicability::MachineApplicable, + ); + } + } + if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] + && fields.len() == found_args.len() + && is_closure + { + let sugg = format!( + "|({}){}|", + found_args + .iter() + .map(|arg| match arg { + ArgKind::Arg(name, _) => name.to_owned(), + _ => "_".to_owned(), + }) + .collect::<Vec<String>>() + .join(", "), + // add type annotations if available + if found_args.iter().any(|arg| match arg { + ArgKind::Arg(_, ty) => ty != "_", + _ => false, + }) { + format!( + ": ({})", + fields + .iter() + .map(|(_, ty)| ty.to_owned()) + .collect::<Vec<String>>() + .join(", ") + ) + } else { + String::new() + }, + ); + err.span_suggestion_verbose( + found_span, + "change the closure to accept a tuple instead of individual arguments", + sugg, + Applicability::MachineApplicable, + ); + } + } + + err + } + + /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` + /// in that order, and returns the generic type corresponding to the + /// argument of that trait (corresponding to the closure arguments). + pub fn type_implements_fn_trait( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: ty::Binder<'tcx, Ty<'tcx>>, + polarity: ty::PredicatePolarity, + ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { + self.commit_if_ok(|_| { + for trait_def_id in [ + self.tcx.lang_items().fn_trait(), + self.tcx.lang_items().fn_mut_trait(), + self.tcx.lang_items().fn_once_trait(), + ] { + let Some(trait_def_id) = trait_def_id else { continue }; + // Make a fresh inference variable so we can determine what the generic parameters + // of the trait are. + let var = self.next_ty_var(DUMMY_SP); + // FIXME(effects) + let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]); + let obligation = Obligation::new( + self.tcx, + ObligationCause::dummy(), + param_env, + ty.rebind(ty::TraitPredicate { trait_ref, polarity }), + ); + let ocx = ObligationCtxt::new(self); + ocx.register_obligation(obligation); + if ocx.select_all_or_error().is_empty() { + return Ok(( + self.tcx + .fn_trait_kind_from_def_id(trait_def_id) + .expect("expected to map DefId to ClosureKind"), + ty.rebind(self.resolve_vars_if_possible(var)), + )); + } + } + + Err(()) + }) + } + fn report_not_const_evaluatable_error( &self, obligation: &PredicateObligation<'tcx>, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs deleted file mode 100644 index e8d7e80ac56..00000000000 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs +++ /dev/null @@ -1,244 +0,0 @@ -// FIXME(error_reporting): This should be made into private methods on `TypeErrCtxt`. - -use crate::infer::InferCtxt; -use crate::traits::{Obligation, ObligationCause, ObligationCtxt}; -use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, Diag}; -use rustc_hir as hir; -use rustc_hir::Node; -use rustc_macros::extension; -use rustc_middle::ty::{self, Ty}; -use rustc_span::{Span, DUMMY_SP}; - -use super::ArgKind; - -#[extension(pub trait InferCtxtExt<'tcx>)] -impl<'tcx> InferCtxt<'tcx> { - /// Given some node representing a fn-like thing in the HIR map, - /// returns a span and `ArgKind` information that describes the - /// arguments it expects. This can be supplied to - /// `report_arg_count_mismatch`. - fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)> { - let sm = self.tcx.sess.source_map(); - let hir = self.tcx.hir(); - Some(match node { - Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }), - .. - }) => ( - fn_decl_span, - fn_arg_span, - hir.body(body) - .params - .iter() - .map(|arg| { - if let hir::Pat { kind: hir::PatKind::Tuple(args, _), span, .. } = *arg.pat - { - Some(ArgKind::Tuple( - Some(span), - args.iter() - .map(|pat| { - sm.span_to_snippet(pat.span) - .ok() - .map(|snippet| (snippet, "_".to_owned())) - }) - .collect::<Option<Vec<_>>>()?, - )) - } else { - let name = sm.span_to_snippet(arg.pat.span).ok()?; - Some(ArgKind::Arg(name, "_".to_owned())) - } - }) - .collect::<Option<Vec<ArgKind>>>()?, - ), - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. }) - | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. }) - | Node::TraitItem(&hir::TraitItem { - kind: hir::TraitItemKind::Fn(ref sig, _), .. - }) => ( - sig.span, - None, - sig.decl - .inputs - .iter() - .map(|arg| match arg.kind { - hir::TyKind::Tup(tys) => ArgKind::Tuple( - Some(arg.span), - vec![("_".to_owned(), "_".to_owned()); tys.len()], - ), - _ => ArgKind::empty(), - }) - .collect::<Vec<ArgKind>>(), - ), - Node::Ctor(variant_data) => { - let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); - (span, None, vec![ArgKind::empty(); variant_data.fields().len()]) - } - _ => panic!("non-FnLike node found: {node:?}"), - }) - } - - /// Reports an error when the number of arguments needed by a - /// trait match doesn't match the number that the expression - /// provides. - fn report_arg_count_mismatch( - &self, - span: Span, - found_span: Option<Span>, - expected_args: Vec<ArgKind>, - found_args: Vec<ArgKind>, - is_closure: bool, - closure_arg_span: Option<Span>, - ) -> Diag<'_> { - let kind = if is_closure { "closure" } else { "function" }; - - let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { - let arg_length = arguments.len(); - let distinct = matches!(other, &[ArgKind::Tuple(..)]); - match (arg_length, arguments.get(0)) { - (1, Some(ArgKind::Tuple(_, fields))) => { - format!("a single {}-tuple as argument", fields.len()) - } - _ => format!( - "{} {}argument{}", - arg_length, - if distinct && arg_length > 1 { "distinct " } else { "" }, - pluralize!(arg_length) - ), - } - }; - - let expected_str = args_str(&expected_args, &found_args); - let found_str = args_str(&found_args, &expected_args); - - let mut err = struct_span_code_err!( - self.dcx(), - span, - E0593, - "{} is expected to take {}, but it takes {}", - kind, - expected_str, - found_str, - ); - - err.span_label(span, format!("expected {kind} that takes {expected_str}")); - - if let Some(found_span) = found_span { - err.span_label(found_span, format!("takes {found_str}")); - - // Suggest to take and ignore the arguments with expected_args_length `_`s if - // found arguments is empty (assume the user just wants to ignore args in this case). - // For example, if `expected_args_length` is 2, suggest `|_, _|`. - if found_args.is_empty() && is_closure { - let underscores = vec!["_"; expected_args.len()].join(", "); - err.span_suggestion_verbose( - closure_arg_span.unwrap_or(found_span), - format!( - "consider changing the closure to take and ignore the expected argument{}", - pluralize!(expected_args.len()) - ), - format!("|{underscores}|"), - Applicability::MachineApplicable, - ); - } - - if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { - if fields.len() == expected_args.len() { - let sugg = fields - .iter() - .map(|(name, _)| name.to_owned()) - .collect::<Vec<String>>() - .join(", "); - err.span_suggestion_verbose( - found_span, - "change the closure to take multiple arguments instead of a single tuple", - format!("|{sugg}|"), - Applicability::MachineApplicable, - ); - } - } - if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] - && fields.len() == found_args.len() - && is_closure - { - let sugg = format!( - "|({}){}|", - found_args - .iter() - .map(|arg| match arg { - ArgKind::Arg(name, _) => name.to_owned(), - _ => "_".to_owned(), - }) - .collect::<Vec<String>>() - .join(", "), - // add type annotations if available - if found_args.iter().any(|arg| match arg { - ArgKind::Arg(_, ty) => ty != "_", - _ => false, - }) { - format!( - ": ({})", - fields - .iter() - .map(|(_, ty)| ty.to_owned()) - .collect::<Vec<String>>() - .join(", ") - ) - } else { - String::new() - }, - ); - err.span_suggestion_verbose( - found_span, - "change the closure to accept a tuple instead of individual arguments", - sugg, - Applicability::MachineApplicable, - ); - } - } - - err - } - - /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` - /// in that order, and returns the generic type corresponding to the - /// argument of that trait (corresponding to the closure arguments). - fn type_implements_fn_trait( - &self, - param_env: ty::ParamEnv<'tcx>, - ty: ty::Binder<'tcx, Ty<'tcx>>, - polarity: ty::PredicatePolarity, - ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { - self.commit_if_ok(|_| { - for trait_def_id in [ - self.tcx.lang_items().fn_trait(), - self.tcx.lang_items().fn_mut_trait(), - self.tcx.lang_items().fn_once_trait(), - ] { - let Some(trait_def_id) = trait_def_id else { continue }; - // Make a fresh inference variable so we can determine what the generic parameters - // of the trait are. - let var = self.next_ty_var(DUMMY_SP); - // FIXME(effects) - let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]); - let obligation = Obligation::new( - self.tcx, - ObligationCause::dummy(), - param_env, - ty.rebind(ty::TraitPredicate { trait_ref, polarity }), - ); - let ocx = ObligationCtxt::new(self); - ocx.register_obligation(obligation); - if ocx.select_all_or_error().is_empty() { - return Ok(( - self.tcx - .fn_trait_kind_from_def_id(trait_def_id) - .expect("expected to map DefId to ClosureKind"), - ty.rebind(self.resolve_vars_if_possible(var)), - )); - } - } - - Err(()) - }) - } -} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index 2131e236401..9aa6d1f3d46 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -1,34 +1,28 @@ pub mod ambiguity; mod fulfillment_errors; -mod infer_ctxt_ext; pub mod on_unimplemented; mod overflow; pub mod suggestions; -use std::iter; +use std::{fmt, iter}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_hir::def_id::DefId; +use rustc_errors::{struct_span_code_err, Applicability, Diag, MultiSpan, E0038, E0276}; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, LangItem}; -use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::traits::{ - Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError, + ObjectSafetyViolation, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, + SelectionError, }; -use rustc_macros::extension; -use rustc_middle::ty::print::PrintTraitRefExt as _; +use rustc_middle::ty::print::{with_no_trimmed_paths, PrintTraitRefExt as _}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::{ErrorGuaranteed, ExpnKind, Span}; +use tracing::{info, instrument}; -use ambiguity::TypeErrCtxtAmbiguityExt as _; -use fulfillment_errors::TypeErrCtxtExt as _; -use suggestions::TypeErrCtxtExt as _; - -use crate::traits::{FulfillmentError, FulfillmentErrorCode}; - -pub use self::fulfillment_errors::*; -pub use self::infer_ctxt_ext::*; pub use self::overflow::*; +use crate::error_reporting::TypeErrCtxt; +use crate::traits::{FulfillmentError, FulfillmentErrorCode}; // When outputting impl candidates, prefer showing those that are more similar. // @@ -137,9 +131,8 @@ pub enum DefIdOrName { Name(&'static str), } -#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)] impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - fn report_fulfillment_errors( + pub fn report_fulfillment_errors( &self, mut errors: Vec<FulfillmentError<'tcx>>, ) -> ErrorGuaranteed { @@ -383,3 +376,194 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti w.push(';'); Some(w) } + +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + pub fn report_extra_impl_obligation( + &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.0.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_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index e90fe8fb94d..41b0ee56a4c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -1,30 +1,28 @@ -use super::{ObligationCauseCode, PredicateObligation}; -use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt; -use crate::errors::{ - EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented, -}; -use crate::infer::error_reporting::TypeErrCtxt; -use crate::infer::InferCtxtExt; -use rustc_ast::AttrArgs; -use rustc_ast::AttrArgsEq; -use rustc_ast::AttrKind; -use rustc_ast::{Attribute, MetaItem, NestedMetaItem}; -use rustc_attr as attr; +use std::iter; +use std::path::PathBuf; + +use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, Attribute, MetaItem, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{codes::*, struct_span_code_err, ErrorGuaranteed}; -use rustc_hir as hir; +use rustc_errors::codes::*; +use rustc_errors::{struct_span_code_err, ErrorGuaranteed}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_macros::{extension, LintDiagnostic}; +use rustc_macros::LintDiagnostic; use rustc_middle::bug; use rustc_middle::ty::print::PrintTraitRefExt as _; -use rustc_middle::ty::GenericArgsRef; -use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; +use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, TyCtxt}; use rustc_parse_format::{ParseMode, Parser, Piece, Position}; use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; -use std::iter; -use std::path::PathBuf; +use tracing::{debug, info}; +use {rustc_attr as attr, rustc_hir as hir}; + +use super::{ObligationCauseCode, PredicateObligation}; +use crate::error_reporting::TypeErrCtxt; +use crate::errors::{ + EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented, +}; +use crate::infer::InferCtxtExt; /// The symbols which are always allowed in a format string static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[ @@ -41,7 +39,6 @@ static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[ sym::Trait, ]; -#[extension(pub trait TypeErrCtxtExt<'tcx>)] impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn impl_similar_to( &self, @@ -77,10 +74,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } }); - let impl_def_id_and_args = if self_match_impls.len() == 1 { - self_match_impls[0] - } else if fuzzy_match_impls.len() == 1 { - fuzzy_match_impls[0] + let impl_def_id_and_args = if let [impl_] = self_match_impls[..] { + impl_ + } else if let [impl_] = fuzzy_match_impls[..] { + impl_ } else { return None; }; @@ -109,7 +106,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - fn on_unimplemented_note( + pub fn on_unimplemented_note( &self, trait_ref: ty::PolyTraitRef<'tcx>, obligation: &PredicateObligation<'tcx>, @@ -881,7 +878,7 @@ impl<'tcx> OnUnimplementedFormatString { } } // we cannot return errors from processing the format string as hard error here - // as the diagnostic namespace gurantees that malformed input cannot cause an error + // as the diagnostic namespace guarantees that malformed input cannot cause an error // // if we encounter any error while processing we nevertheless want to show it as warning // so that users are aware that something is not correct @@ -989,10 +986,10 @@ impl<'tcx> OnUnimplementedFormatString { }) .collect(); // we cannot return errors from processing the format string as hard error here - // as the diagnostic namespace gurantees that malformed input cannot cause an error + // as the diagnostic namespace guarantees that malformed input cannot cause an error // // if we encounter any error while processing the format string - // we don't want to show the potentially half assembled formated string, + // we don't want to show the potentially half assembled formatted string, // therefore we fall back to just showing the input string in this case // // The actual parser errors are emitted earlier diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs index 061a5a4be20..51fb9f3c622 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs @@ -5,17 +5,15 @@ use rustc_errors::{ }; use rustc_hir::def::Namespace; use rustc_hir::def_id::LOCAL_CRATE; -use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::traits::{Obligation, PredicateObligation}; -use rustc_macros::extension; use rustc_middle::ty::print::{FmtPrinter, Print}; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::Limit; use rustc_span::Span; use rustc_type_ir::Upcast; +use tracing::debug; -use super::InferCtxtPrivExt; -use crate::error_reporting::traits::suggestions::TypeErrCtxtExt; +use crate::error_reporting::TypeErrCtxt; pub enum OverflowCause<'tcx> { DeeplyNormalize(ty::AliasTerm<'tcx>), @@ -38,7 +36,6 @@ pub fn suggest_new_overflow_limit<'tcx, G: EmissionGuarantee>( )); } -#[extension(pub trait TypeErrCtxtOverflowExt<'a, 'tcx>)] impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// Reports that an overflow has occurred and halts compilation. We /// halt compilation unconditionally because it is important that @@ -46,7 +43,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// whose result could not be truly determined and thus we can't say /// if the program type checks or not -- and they are unusual /// occurrences in any case. - fn report_overflow_error( + pub fn report_overflow_error( &self, cause: OverflowCause<'tcx>, span: Span, @@ -59,7 +56,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { FatalError.raise(); } - fn build_overflow_error( + pub fn build_overflow_error( &self, cause: OverflowCause<'tcx>, span: Span, @@ -132,7 +129,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// whose result could not be truly determined and thus we can't say /// if the program type checks or not -- and they are unusual /// occurrences in any case. - fn report_overflow_obligation<T>( + pub fn report_overflow_obligation<T>( &self, obligation: &Obligation<'tcx, T>, suggest_increasing_limit: bool, @@ -165,7 +162,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// that we can give a more helpful error message (and, in particular, /// we do not suggest increasing the overflow limit, which is not /// going to help). - fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { + pub fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { let cycle = self.resolve_vars_if_possible(cycle.to_owned()); assert!(!cycle.is_empty()); @@ -179,7 +176,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } - fn report_overflow_no_abort( + pub fn report_overflow_no_abort( &self, obligation: PredicateObligation<'tcx>, suggest_increasing_limit: bool, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 43bc925d09b..45e157b1080 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1,35 +1,33 @@ // ignore-tidy-filelength -use super::{ - DefIdOrName, FindExprBySpan, ImplCandidate, Obligation, ObligationCause, ObligationCauseCode, - PredicateObligation, -}; - -use crate::errors; -use crate::infer::InferCtxt; -use crate::traits::{ImplDerivedCause, NormalizeExt, ObligationCtxt}; +use std::assert_matches::debug_assert_matches; +use std::borrow::Cow; +use std::iter; -use hir::def::CtorOf; +use itertools::{EitherOrBoth, Itertools}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_errors::codes::*; use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, Applicability, Diag, EmissionGuarantee, MultiSpan, - Style, SuggestionStyle, + pluralize, struct_span_code_err, Applicability, Diag, EmissionGuarantee, MultiSpan, Style, + SuggestionStyle, }; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_hir::is_range_literal; use rustc_hir::lang_items::LangItem; -use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node}; -use rustc_infer::infer::error_reporting::TypeErrCtxt; -use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk}; -use rustc_macros::extension; +use rustc_hir::{ + is_range_literal, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node, +}; +use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_middle::hir::map; use rustc_middle::traits::IsConstable; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::print::PrintPolyTraitRefExt; +use rustc_middle::ty::print::{ + with_forced_trimmed_paths, with_no_trimmed_paths, PrintPolyTraitPredicateExt as _, + PrintPolyTraitRefExt, PrintTraitPredicateExt as _, +}; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GenericArgs, InferTy, IsSuggestable, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, @@ -40,20 +38,17 @@ use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP}; use rustc_target::spec::abi; -use std::assert_matches::debug_assert_matches; -use std::borrow::Cow; -use std::iter; +use tracing::{debug, instrument}; -use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt; +use super::{ + DefIdOrName, FindExprBySpan, ImplCandidate, Obligation, ObligationCause, ObligationCauseCode, + PredicateObligation, +}; +use crate::error_reporting::TypeErrCtxt; +use crate::errors; use crate::infer::InferCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_middle::ty::print::{ - with_forced_trimmed_paths, with_no_trimmed_paths, PrintPolyTraitPredicateExt as _, - PrintTraitPredicateExt as _, -}; - -use itertools::EitherOrBoth; -use itertools::Itertools; +use crate::traits::{ImplDerivedCause, NormalizeExt, ObligationCtxt}; #[derive(Debug)] pub enum CoroutineInteriorOrUpvar { @@ -241,9 +236,8 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( } } -#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)] impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - fn suggest_restricting_param_bound( + pub fn suggest_restricting_param_bound( &self, err: &mut Diag<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, @@ -453,7 +447,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// When after several dereferencing, the reference satisfies the trait /// bound. This function provides dereference suggestion for this /// specific situation. - fn suggest_dereferences( + pub(super) fn suggest_dereferences( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -466,7 +460,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr) { // Suggest dereferencing the argument to a function/method call if possible - let mut real_trait_pred = trait_pred; while let Some((parent_code, parent_trait_pred)) = code.parent() { code = parent_code; @@ -553,6 +546,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); if self.predicate_may_hold(&obligation) && self.predicate_must_hold_modulo_regions(&sized_obligation) + // Do not suggest * if it is already a reference, + // will suggest removing the borrow instead in that case. + && !matches!(expr.kind, hir::ExprKind::AddrOf(..)) { let call_node = self.tcx.hir_node(*call_hir_id); let msg = "consider dereferencing here"; @@ -780,7 +776,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// We tried to apply the bound to an `fn` or closure. Check whether calling it would /// evaluate to a type that *would* satisfy the trait bound. If it would, suggest calling /// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`. - fn suggest_fn_call( + pub(super) fn suggest_fn_call( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -896,7 +892,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { true } - fn check_for_binding_assigned_block_without_tail_expression( + pub(super) fn check_for_binding_assigned_block_without_tail_expression( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -972,7 +968,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn suggest_add_clone_to_arg( + pub(super) fn suggest_add_clone_to_arg( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -1072,7 +1068,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// Extracts information about a callable type for diagnostics. This is a /// heuristic -- it doesn't necessarily mean that a type is always callable, /// because the callable type must also be well-formed to be called. - fn extract_callable_info( + pub fn extract_callable_info( &self, body_id: LocalDefId, param_env: ty::ParamEnv<'tcx>, @@ -1082,10 +1078,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let Some((def_id_or_name, output, inputs)) = (self.autoderef_steps)(found).into_iter().find_map(|(found, _)| { match *found.kind() { - ty::FnPtr(fn_sig) => Some(( + ty::FnPtr(sig_tys, _) => Some(( DefIdOrName::Name("function pointer"), - fn_sig.output(), - fn_sig.inputs(), + sig_tys.output(), + sig_tys.inputs(), )), ty::FnDef(def_id, _) => { let fn_sig = found.fn_sig(self.tcx); @@ -1198,7 +1194,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) } } - fn suggest_add_reference_to_arg( + pub(super) fn suggest_add_reference_to_arg( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -1420,7 +1416,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } // Suggest borrowing the type - fn suggest_borrowing_for_object_cast( + pub(super) fn suggest_borrowing_for_object_cast( &self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>, @@ -1455,7 +1451,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`, /// suggest removing these references until we reach a type that implements the trait. - fn suggest_remove_reference( + pub(super) fn suggest_remove_reference( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -1576,7 +1572,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { false } - fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>) { + pub(super) fn suggest_remove_await( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diag<'_>, + ) { let hir = self.tcx.hir(); if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives() && let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id) @@ -1642,7 +1642,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// Check if the trait bound is implemented for a different mutability and note it in the /// final error. - fn suggest_change_mut( + pub(super) fn suggest_change_mut( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -1718,7 +1718,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn suggest_semicolon_removal( + pub(super) fn suggest_semicolon_removal( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -1760,7 +1760,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { false } - fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> { + pub(super) fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> { let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. }) = self.tcx.hir_node_by_def_id(obligation.cause.body_id) else { @@ -1773,7 +1773,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// If all conditions are met to identify a returned `dyn Trait`, suggest using `impl Trait` if /// applicable and signal that the error has been expanded appropriately and needs to be /// emitted. - fn suggest_impl_trait( + pub(super) fn suggest_impl_trait( &self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>, @@ -1791,25 +1791,42 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.children.clear(); let span = obligation.cause.span; - if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) + let body = self.tcx.hir().body_owned_by(obligation.cause.body_id); + + let mut visitor = ReturnsVisitor::default(); + visitor.visit_body(&body); + + let (pre, impl_span) = if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) && snip.starts_with("dyn ") { - err.span_suggestion( - span.with_hi(span.lo() + BytePos(4)), - "return an `impl Trait` instead of a `dyn Trait`, \ - if all returned values are the same type", + ("", span.with_hi(span.lo() + BytePos(4))) + } else { + ("dyn ", span.shrink_to_lo()) + }; + let alternatively = if visitor + .returns + .iter() + .map(|expr| self.typeck_results.as_ref().unwrap().expr_ty_adjusted_opt(expr)) + .collect::<FxHashSet<_>>() + .len() + <= 1 + { + err.span_suggestion_verbose( + impl_span, + "consider returning an `impl Trait` instead of a `dyn Trait`", "impl ", Applicability::MaybeIncorrect, ); - } - - let body = self.tcx.hir().body_owned_by(obligation.cause.body_id); - - let mut visitor = ReturnsVisitor::default(); - visitor.visit_body(&body); + "alternatively, " + } else { + err.help("if there were a single returned type, you could use `impl Trait` instead"); + "" + }; - let mut sugg = - vec![(span.shrink_to_lo(), "Box<".to_string()), (span.shrink_to_hi(), ">".to_string())]; + let mut sugg = vec![ + (span.shrink_to_lo(), format!("Box<{pre}")), + (span.shrink_to_hi(), ">".to_string()), + ]; sugg.extend(visitor.returns.into_iter().flat_map(|expr| { let span = expr.span.find_ancestor_in_same_ctxt(obligation.cause.span).unwrap_or(expr.span); @@ -1835,7 +1852,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { })); err.multipart_suggestion( - "box the return type, and wrap all of the returned values in `Box::new`", + format!( + "{alternatively}box the return type, and wrap all of the returned values in \ + `Box::new`", + ), sugg, Applicability::MaybeIncorrect, ); @@ -1843,7 +1863,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { true } - fn point_at_returns_when_relevant( + pub(super) fn point_at_returns_when_relevant( &self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>, @@ -1875,7 +1895,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn report_closure_arg_mismatch( + pub(super) fn report_closure_arg_mismatch( &self, span: Span, found_span: Option<Span>, @@ -1958,20 +1978,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = cause else { return; }; - let ty::FnPtr(expected) = expected.kind() else { + let ty::FnPtr(sig_tys, hdr) = expected.kind() else { return; }; - let ty::FnPtr(found) = found.kind() else { + let expected = sig_tys.with(*hdr); + let ty::FnPtr(sig_tys, hdr) = found.kind() else { return; }; + let found = sig_tys.with(*hdr); let Node::Expr(arg) = self.tcx.hir_node(*arg_hir_id) else { return; }; let hir::ExprKind::Path(path) = arg.kind else { return; }; - let expected_inputs = self.tcx.instantiate_bound_regions_with_erased(*expected).inputs(); - let found_inputs = self.tcx.instantiate_bound_regions_with_erased(*found).inputs(); + let expected_inputs = self.tcx.instantiate_bound_regions_with_erased(expected).inputs(); + let found_inputs = self.tcx.instantiate_bound_regions_with_erased(found).inputs(); let both_tys = expected_inputs.iter().copied().zip(found_inputs.iter().copied()); let arg_expr = |infcx: &InferCtxt<'tcx>, name, expected: Ty<'tcx>, found: Ty<'tcx>| { @@ -2154,7 +2176,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn suggest_fully_qualified_path( + pub(super) fn suggest_fully_qualified_path( &self, err: &mut Diag<'_>, item_def_id: DefId, @@ -2221,7 +2243,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// /// Returns `true` if an async-await specific note was added to the diagnostic. #[instrument(level = "debug", skip_all, fields(?obligation.predicate, ?obligation.cause.span))] - fn maybe_note_obligation_cause_for_async_await<G: EmissionGuarantee>( + pub fn maybe_note_obligation_cause_for_async_await<G: EmissionGuarantee>( &self, err: &mut Diag<'_, G>, obligation: &PredicateObligation<'tcx>, @@ -2690,7 +2712,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } - fn note_obligation_cause_code<G: EmissionGuarantee, T>( + pub(super) fn note_obligation_cause_code<G: EmissionGuarantee, T>( &self, body_id: LocalDefId, err: &mut Diag<'_, G>, @@ -2706,6 +2728,20 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let tcx = self.tcx; let predicate = predicate.upcast(tcx); + let suggest_remove_deref = |err: &mut Diag<'_, G>, expr: &hir::Expr<'_>| { + if let Some(pred) = predicate.as_trait_clause() + && tcx.is_lang_item(pred.def_id(), LangItem::Sized) + && let hir::ExprKind::Unary(hir::UnOp::Deref, inner) = expr.kind + { + err.span_suggestion_verbose( + expr.span.until(inner.span), + "references are always `Sized`, even if they point to unsized data; consider \ + not dereferencing the expression", + String::new(), + Applicability::MaybeIncorrect, + ); + } + }; match *cause_code { ObligationCauseCode::ExprAssignable | ObligationCauseCode::MatchExpressionArm { .. } @@ -2752,6 +2788,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | ObligationCauseCode::WhereClauseInExpr(item_def_id, span, ..) if !span.is_dummy() => { + if let ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, pos) = &cause_code { + if let Node::Expr(expr) = tcx.parent_hir_node(*hir_id) + && let hir::ExprKind::Call(_, args) = expr.kind + && let Some(expr) = args.get(*pos) + { + suggest_remove_deref(err, &expr); + } else if let Node::Expr(expr) = self.tcx.hir_node(*hir_id) + && let hir::ExprKind::MethodCall(_, _, args, _) = expr.kind + && let Some(expr) = args.get(*pos) + { + suggest_remove_deref(err, &expr); + } + } let item_name = tcx.def_path_str(item_def_id); let short_item_name = with_forced_trimmed_paths!(tcx.def_path_str(item_def_id)); let mut multispan = MultiSpan::from(span); @@ -2810,7 +2859,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Do not suggest relaxing if there is an explicit `Sized` obligation. && !bounds.iter() .filter_map(|bound| bound.trait_ref()) - .any(|tr| tr.trait_def_id() == tcx.lang_items().sized_trait()) + .any(|tr| tr.trait_def_id().is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized))) { let (span, separator) = if let [.., last] = bounds { (last.span().shrink_to_hi(), " +") @@ -2949,6 +2998,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { )); err.downgrade_to_delayed_bug(); } + let mut local = true; match tcx.parent_hir_node(hir_id) { Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }) => { err.span_suggestion_verbose( @@ -2957,7 +3007,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { "&", Applicability::MachineApplicable, ); - err.note("all local variables must have a statically known size"); } Node::LetStmt(hir::LetStmt { init: Some(hir::Expr { kind: hir::ExprKind::Index(..), span, .. }), @@ -2972,7 +3021,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { "&", Applicability::MachineApplicable, ); - err.note("all local variables must have a statically known size"); + } + Node::LetStmt(hir::LetStmt { init: Some(expr), .. }) => { + // When encountering an assignment of an unsized trait, like `let x = *"";`, + // we check if the RHS is a deref operation, to suggest removing it. + suggest_remove_deref(err, &expr); } Node::Param(param) => { err.span_suggestion_verbose( @@ -2982,10 +3035,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { "&", Applicability::MachineApplicable, ); + local = false; } - _ => { - err.note("all local variables must have a statically known size"); - } + _ => {} + } + if local { + err.note("all local variables must have a statically known size"); } if !tcx.features().unsized_locals { err.help("unsized locals are gated as an unstable feature"); @@ -3017,11 +3072,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { match ty.kind { hir::TyKind::TraitObject(traits, _, _) => { let (span, kw) = match traits { - [first, ..] if first.span.lo() == ty.span.lo() => { + [(first, _), ..] if first.span.lo() == ty.span.lo() => { // Missing `dyn` in front of trait object. (ty.span.shrink_to_lo(), "dyn ") } - [first, ..] => (ty.span.until(first.span), ""), + [(first, _), ..] => (ty.span.until(first.span), ""), [] => span_bug!(ty.span, "trait object with no traits: {ty:?}"), }; let needs_parens = traits.len() != 1; @@ -3508,14 +3563,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } ObligationCauseCode::OpaqueReturnType(expr_info) => { - if let Some((expr_ty, expr_span)) = expr_info { + if let Some((expr_ty, hir_id)) = expr_info { let expr_ty = self.tcx.short_ty_string(expr_ty, &mut long_ty_file); + let expr = self.infcx.tcx.hir().expect_expr(hir_id); err.span_label( - expr_span, + expr.span, with_forced_trimmed_paths!(format!( "return type was inferred to be `{expr_ty}` here", )), ); + suggest_remove_deref(err, &expr); } } } @@ -3532,7 +3589,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { #[instrument( level = "debug", skip(self, err), fields(trait_pred.self_ty = ?trait_pred.self_ty()) )] - fn suggest_await_before_try( + pub(super) fn suggest_await_before_try( &self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>, @@ -3589,7 +3646,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn suggest_floating_point_literal( + pub(super) fn suggest_floating_point_literal( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -3613,7 +3670,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn suggest_derive( + pub fn suggest_derive( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -3679,7 +3736,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn suggest_dereferencing_index( + pub(super) fn suggest_dereferencing_index( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -3810,6 +3867,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { if let Some(where_pred) = where_pred.as_trait_clause() && let Some(failed_pred) = failed_pred.as_trait_clause() + && where_pred.def_id() == failed_pred.def_id() { self.enter_forall(where_pred, |where_pred| { let failed_pred = self.instantiate_binder_with_fresh_vars( @@ -4300,7 +4358,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// If the type that failed selection is an array or a reference to an array, /// but the trait is implemented for slices, suggest that the user converts /// the array into a slice. - fn suggest_convert_to_slice( + pub(super) fn suggest_convert_to_slice( &self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>, @@ -4372,7 +4430,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn explain_hrtb_projection( + pub(super) fn explain_hrtb_projection( &self, diag: &mut Diag<'_>, pred: ty::PolyTraitPredicate<'tcx>, @@ -4438,7 +4496,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn suggest_desugaring_async_fn_in_trait( + pub(super) fn suggest_desugaring_async_fn_in_trait( &self, err: &mut Diag<'_>, trait_ref: ty::PolyTraitRef<'tcx>, @@ -4522,7 +4580,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } - fn ty_kind_suggestion(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> Option<String> { + pub fn ty_kind_suggestion( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + ) -> Option<String> { let tcx = self.infcx.tcx; let implements_default = |ty| { let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else { @@ -4584,7 +4646,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) } - fn suggest_add_result_as_return_type( + // For E0277 when use `?` operator, suggest adding + // a suitable return type in `FnSig`, and a default + // return value at the end of the function's body. + pub(super) fn suggest_add_result_as_return_type( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -4594,26 +4659,59 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return; } + // Only suggest for local function and associated method, + // because this suggest adding both return type in + // the `FnSig` and a default return value in the body, so it + // is not suitable for foreign function without a local body, + // and neither for trait method which may be also implemented + // in other place, so shouldn't change it's FnSig. + fn choose_suggest_items<'tcx, 'hir>( + tcx: TyCtxt<'tcx>, + node: hir::Node<'hir>, + ) -> Option<(&'hir hir::FnDecl<'hir>, hir::BodyId)> { + match node { + hir::Node::Item(item) if let hir::ItemKind::Fn(sig, _, body_id) = item.kind => { + Some((sig.decl, body_id)) + } + hir::Node::ImplItem(item) + if let hir::ImplItemKind::Fn(sig, body_id) = item.kind => + { + let parent = tcx.parent_hir_node(item.hir_id()); + if let hir::Node::Item(item) = parent + && let hir::ItemKind::Impl(imp) = item.kind + && imp.of_trait.is_none() + { + return Some((sig.decl, body_id)); + } + None + } + _ => None, + } + } + let node = self.tcx.hir_node_by_def_id(obligation.cause.body_id); - if let hir::Node::Item(item) = node - && let hir::ItemKind::Fn(sig, _, body_id) = item.kind - && let hir::FnRetTy::DefaultReturn(ret_span) = sig.decl.output + if let Some((fn_decl, body_id)) = choose_suggest_items(self.tcx, node) + && let hir::FnRetTy::DefaultReturn(ret_span) = fn_decl.output && self.tcx.is_diagnostic_item(sym::FromResidual, trait_pred.def_id()) && trait_pred.skip_binder().trait_ref.args.type_at(0).is_unit() && let ty::Adt(def, _) = trait_pred.skip_binder().trait_ref.args.type_at(1).kind() && self.tcx.is_diagnostic_item(sym::Result, def.did()) { - let body = self.tcx.hir().body(body_id); let mut sugg_spans = vec![(ret_span, " -> Result<(), Box<dyn std::error::Error>>".to_string())]; - + let body = self.tcx.hir().body(body_id); if let hir::ExprKind::Block(b, _) = body.value.kind && b.expr.is_none() { + // The span of '}' in the end of block. + let span = self.tcx.sess.source_map().end_point(b.span); sugg_spans.push(( - // The span will point to the closing curly brace `}` of the block. - b.span.shrink_to_hi().with_lo(b.span.hi() - BytePos(1)), - "\n Ok(())\n}".to_string(), + span.shrink_to_lo(), + format!( + "{}{}", + " Ok(())\n", + self.tcx.sess.source_map().indentation_before(span).unwrap_or_default(), + ), )); } err.multipart_suggestion_verbose( @@ -4625,7 +4723,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip_all)] - fn suggest_unsized_bound_if_applicable( + pub(super) fn suggest_unsized_bound_if_applicable( &self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>, @@ -4766,13 +4864,13 @@ fn hint_missing_borrow<'tcx>( } let found_args = match found.kind() { - ty::FnPtr(f) => infcx.enter_forall(*f, |f| f.inputs().iter()), + ty::FnPtr(sig_tys, _) => infcx.enter_forall(*sig_tys, |sig_tys| sig_tys.inputs().iter()), kind => { span_bug!(span, "found was converted to a FnPtr above but is now {:?}", kind) } }; let expected_args = match expected.kind() { - ty::FnPtr(f) => infcx.enter_forall(*f, |f| f.inputs().iter()), + ty::FnPtr(sig_tys, _) => infcx.enter_forall(*sig_tys, |sig_tys| sig_tys.inputs().iter()), kind => { span_bug!(span, "expected was converted to a FnPtr above but is now {:?}", kind) } @@ -5284,7 +5382,8 @@ impl<'v> Visitor<'v> for FindTypeParam { match ty.kind { hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {} hir::TyKind::Path(hir::QPath::Resolved(None, path)) - if path.segments.len() == 1 && path.segments[0].ident.name == self.param => + if let [segment] = path.segments + && segment.ident.name == self.param => { if !self.nested { debug!(?ty, "FindTypeParam::visit_ty"); diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index a46cba35b2d..ebaec0b9059 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,11 +1,27 @@ -use crate::fluent_generated as fluent; +use std::path::PathBuf; + +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::codes::*; use rustc_errors::{ - codes::*, Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, - SubdiagMessageOp, Subdiagnostic, + Applicability, Diag, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic, + EmissionGuarantee, IntoDiagArg, Level, 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, GenericParamKind}; use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_middle::ty::{self, print::PrintTraitRefExt as _, ClosureKind, PolyTraitRef, Ty}; -use rustc_span::{Span, Symbol}; +use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath}; +use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, PolyTraitRef, Region, Ty, TyCtxt}; +use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_span::{BytePos, Span}; + +use crate::error_reporting::infer::need_type_info::UnderspecifiedArgKind; +use crate::error_reporting::infer::nice_region_error::placeholder_error::Highlighted; +use crate::error_reporting::infer::ObligationCauseAsDiagArg; +use crate::fluent_generated as fluent; + +pub mod note_and_explain; #[derive(Diagnostic)] #[diag(trait_selection_dump_vtable_entries)] @@ -170,3 +186,1612 @@ pub(crate) struct AsyncClosureNotFn { pub span: Span, pub kind: &'static str, } + +#[derive(Diagnostic)] +#[diag(trait_selection_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(trait_selection_full_type_written)] + pub was_written: bool, + pub path: PathBuf, + #[note(trait_selection_type_annotations_needed_error_time)] + pub time_version: bool, +} + +// Copy of `AnnotationRequired` for E0283 +#[derive(Diagnostic)] +#[diag(trait_selection_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(trait_selection_full_type_written)] + pub was_written: bool, + pub path: PathBuf, +} + +// Copy of `AnnotationRequired` for E0284 +#[derive(Diagnostic)] +#[diag(trait_selection_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(trait_selection_full_type_written)] + pub was_written: bool, + pub path: PathBuf, +} + +// Used when a better one isn't available +#[derive(Subdiagnostic)] +#[label(trait_selection_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( + trait_selection_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(trait_selection_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( + trait_selection_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( + trait_selection_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( + trait_selection_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::trait_selection_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::trait_selection_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::trait_selection_declared_different); + diag.span_label(ret_span, fluent::trait_selection_nothing); + diag.span_label(span, fluent::trait_selection_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::trait_selection_declared_multiple); + diag.span_label(ty_sub, fluent::trait_selection_nothing); + diag.span_label(span, fluent::trait_selection_data_lifetime_flow); + } else { + diag.span_label(ty_sup, fluent::trait_selection_types_declared_different); + diag.span_label(ty_sub, fluent::trait_selection_nothing); + diag.span_label(span, fluent::trait_selection_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::trait_selection_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::trait_selection_lifetime_param_suggestion_elided); + } + } +} + +#[derive(Diagnostic)] +#[diag(trait_selection_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::trait_selection_msl_introduces_static); + diag.span_note(self.unmet_requirements, fluent::trait_selection_msl_unmet_req); + } +} + +// FIXME(#100717): replace with a `Option<Span>` when subdiagnostic supports that +#[derive(Subdiagnostic)] +pub enum DoesNotOutliveStaticFromImpl { + #[note(trait_selection_does_not_outlive_static_from_impl)] + Spanned { + #[primary_span] + span: Span, + }, + #[note(trait_selection_does_not_outlive_static_from_impl)] + Unspanned, +} + +#[derive(Subdiagnostic)] +pub enum ImplicitStaticLifetimeSubdiag { + #[note(trait_selection_implicit_static_lifetime_note)] + Note { + #[primary_span] + span: Span, + }, + #[suggestion( + trait_selection_implicit_static_lifetime_suggestion, + style = "verbose", + code = " + '_", + applicability = "maybe-incorrect" + )] + Sugg { + #[primary_span] + span: Span, + }, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_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(trait_selection_explicit_lifetime_required_with_ident, code = E0621)] + WithIdent { + #[primary_span] + #[label] + span: Span, + simple_ident: Ident, + named: String, + #[suggestion( + trait_selection_explicit_lifetime_required_sugg_with_ident, + code = "{new_ty}", + applicability = "unspecified" + )] + new_ty_span: Span, + #[skip_arg] + new_ty: Ty<'a>, + }, + #[diag(trait_selection_explicit_lifetime_required_with_param_type, code = E0621)] + WithParamType { + #[primary_span] + #[label] + span: Span, + named: String, + #[suggestion( + trait_selection_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(trait_selection_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(trait_selection_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(trait_selection_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(trait_selection_actual_impl_expl_expected_signature_nothing)] + ExpectedSignatureNothing { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + }, + #[note(trait_selection_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(trait_selection_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(trait_selection_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(trait_selection_actual_impl_expl_expected_passive_nothing)] + ExpectedPassiveNothing { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + }, + #[note(trait_selection_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(trait_selection_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(trait_selection_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(trait_selection_actual_impl_expl_expected_other_nothing)] + ExpectedOtherNothing { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + }, + #[note(trait_selection_actual_impl_expl_but_actually_implements_trait)] + ButActuallyImplementsTrait { + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + has_lifetime: bool, + lifetime: usize, + }, + #[note(trait_selection_actual_impl_expl_but_actually_implemented_for_ty)] + ButActuallyImplementedForTy { + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + has_lifetime: bool, + lifetime: usize, + ty: String, + }, + #[note(trait_selection_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(trait_selection_trait_placeholder_mismatch)] +pub struct TraitPlaceholderMismatch<'tcx> { + #[primary_span] + pub span: Span, + #[label(trait_selection_label_satisfy)] + pub satisfy_span: Option<Span>, + #[label(trait_selection_label_where)] + pub where_span: Option<Span>, + #[label(trait_selection_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::trait_selection_tid_consider_borrowing); + } + let msg = f(diag, fluent::trait_selection_tid_param_help.into()); + diag.span_help(type_param_span, msg); + } +} + +#[derive(Subdiagnostic)] +#[help(trait_selection_tid_rel_help)] +pub struct RelationshipHelp; + +#[derive(Diagnostic)] +#[diag(trait_selection_trait_impl_diff)] +pub struct TraitImplDiff { + #[primary_span] + #[label(trait_selection_found)] + pub sp: Span, + #[label(trait_selection_expected)] + pub trait_sp: Span, + #[note(trait_selection_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::trait_selection_dtcs_has_lifetime_req_label); + multi_span + .push_span_label(self.ident.span, fluent::trait_selection_dtcs_introduces_requirement); + let msg = f(diag, fluent::trait_selection_dtcs_has_req_note.into()); + diag.span_note(multi_span, msg); + let msg = f(diag, fluent::trait_selection_dtcs_suggestion.into()); + diag.span_suggestion_verbose( + self.span.shrink_to_hi(), + msg, + " + '_", + Applicability::MaybeIncorrect, + ); + } +} + +#[derive(Diagnostic)] +#[diag(trait_selection_but_calling_introduces, code = E0772)] +pub struct ButCallingIntroduces { + #[label(trait_selection_label1)] + pub param_ty_span: Span, + #[primary_span] + #[label(trait_selection_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::trait_selection_ril_introduced_here); + } + + if self.add_label { + self.span.push_span_label(self.fn_decl_span, fluent::trait_selection_ril_introduced_by); + } + self.span.push_span_label(self.cause_span, fluent::trait_selection_ril_because_of); + let msg = f(diag, fluent::trait_selection_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::trait_selection_more_targeted); + diag.arg("ident", self.ident); + } +} + +#[derive(Diagnostic)] +#[diag(trait_selection_but_needs_to_satisfy, code = E0759)] +pub struct ButNeedsToSatisfy { + #[primary_span] + pub sp: Span, + #[label(trait_selection_influencer)] + pub influencer_point: Span, + #[label(trait_selection_used_here)] + pub spans: Vec<Span>, + #[label(trait_selection_require)] + pub require_span_as_label: Option<Span>, + #[note(trait_selection_require)] + pub require_span_as_note: Option<Span>, + #[note(trait_selection_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(trait_selection_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(trait_selection_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(trait_selection_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(trait_selection_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(trait_selection_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( + trait_selection_where_remove, + code = "", + applicability = "machine-applicable", + style = "verbose" + )] + Remove { + #[primary_span] + span: Span, + }, + #[suggestion( + trait_selection_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( + trait_selection_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( + trait_selection_srs_remove, + style = "short", + code = "", + applicability = "machine-applicable" + )] + Remove { + #[primary_span] + sp: Span, + }, + #[suggestion( + trait_selection_srs_add, + style = "verbose", + code = "{code}", + applicability = "maybe-incorrect" + )] + Add { + #[primary_span] + sp: Span, + code: String, + ident: Ident, + }, + #[note(trait_selection_srs_add_one)] + AddOne { + #[primary_span] + spans: MultiSpan, + }, +} + +#[derive(Subdiagnostic)] +pub enum ConsiderAddingAwait { + #[help(trait_selection_await_both_futures)] + BothFuturesHelp, + #[multipart_suggestion(trait_selection_await_both_futures, applicability = "maybe-incorrect")] + BothFuturesSugg { + #[suggestion_part(code = ".await")] + first: Span, + #[suggestion_part(code = ".await")] + second: Span, + }, + #[suggestion( + trait_selection_await_future, + code = ".await", + style = "verbose", + applicability = "maybe-incorrect" + )] + FutureSugg { + #[primary_span] + span: Span, + }, + #[note(trait_selection_await_note)] + FutureSuggNote { + #[primary_span] + span: Span, + }, + #[multipart_suggestion( + trait_selection_await_future, + style = "verbose", + applicability = "maybe-incorrect" + )] + FutureSuggMultiple { + #[suggestion_part(code = ".await")] + spans: Vec<Span>, + }, +} + +#[derive(Diagnostic)] +pub enum PlaceholderRelationLfNotSatisfied { + #[diag(trait_selection_lf_bound_not_satisfied)] + HasBoth { + #[primary_span] + span: Span, + #[note(trait_selection_prlf_defined_with_sub)] + sub_span: Span, + #[note(trait_selection_prlf_must_outlive_with_sup)] + sup_span: Span, + sub_symbol: Symbol, + sup_symbol: Symbol, + #[note(trait_selection_prlf_known_limitation)] + note: (), + }, + #[diag(trait_selection_lf_bound_not_satisfied)] + HasSub { + #[primary_span] + span: Span, + #[note(trait_selection_prlf_defined_with_sub)] + sub_span: Span, + #[note(trait_selection_prlf_must_outlive_without_sup)] + sup_span: Span, + sub_symbol: Symbol, + #[note(trait_selection_prlf_known_limitation)] + note: (), + }, + #[diag(trait_selection_lf_bound_not_satisfied)] + HasSup { + #[primary_span] + span: Span, + #[note(trait_selection_prlf_defined_without_sub)] + sub_span: Span, + #[note(trait_selection_prlf_must_outlive_with_sup)] + sup_span: Span, + sup_symbol: Symbol, + #[note(trait_selection_prlf_known_limitation)] + note: (), + }, + #[diag(trait_selection_lf_bound_not_satisfied)] + HasNone { + #[primary_span] + span: Span, + #[note(trait_selection_prlf_defined_without_sub)] + sub_span: Span, + #[note(trait_selection_prlf_must_outlive_without_sup)] + sup_span: Span, + #[note(trait_selection_prlf_known_limitation)] + note: (), + }, + #[diag(trait_selection_lf_bound_not_satisfied)] + OnlyPrimarySpan { + #[primary_span] + span: Span, + #[note(trait_selection_prlf_known_limitation)] + note: (), + }, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_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( + trait_selection_fps_use_ref, + code = "&{fn_name}", + style = "verbose", + applicability = "maybe-incorrect" + )] + UseRef { + #[primary_span] + span: Span, + #[skip_arg] + fn_name: String, + }, + #[suggestion( + trait_selection_fps_remove_ref, + code = "{fn_name}", + style = "verbose", + applicability = "maybe-incorrect" + )] + RemoveRef { + #[primary_span] + span: Span, + #[skip_arg] + fn_name: String, + }, + #[suggestion( + trait_selection_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( + trait_selection_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( + trait_selection_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( + trait_selection_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(trait_selection_fps_items_are_distinct)] +pub struct FnItemsAreDistinct; + +#[derive(Subdiagnostic)] +#[note(trait_selection_fn_uniq_types)] +pub struct FnUniqTypes; + +#[derive(Subdiagnostic)] +#[help(trait_selection_fn_consider_casting)] +pub struct FnConsiderCasting { + pub casting: String, +} + +#[derive(Subdiagnostic)] +pub enum SuggestAccessingField<'a> { + #[suggestion( + trait_selection_suggest_accessing_field, + code = "{snippet}.{name}", + applicability = "maybe-incorrect" + )] + Safe { + #[primary_span] + span: Span, + snippet: String, + name: Symbol, + ty: Ty<'a>, + }, + #[suggestion( + trait_selection_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(trait_selection_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::trait_selection_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( + trait_selection_meant_byte_literal, + code = "b'{code}'", + applicability = "machine-applicable" + )] + MeantByteLiteral { + #[primary_span] + span: Span, + code: String, + }, + #[suggestion( + trait_selection_meant_char_literal, + code = "'{code}'", + applicability = "machine-applicable" + )] + MeantCharLiteral { + #[primary_span] + span: Span, + code: String, + }, + #[multipart_suggestion(trait_selection_meant_str_literal, applicability = "machine-applicable")] + MeantStrLiteral { + #[suggestion_part(code = "\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, + }, + #[suggestion( + trait_selection_consider_specifying_length, + code = "{length}", + applicability = "maybe-incorrect" + )] + ConsiderSpecifyingLength { + #[primary_span] + span: Span, + length: u64, + }, + #[note(trait_selection_try_cannot_convert)] + TryCannotConvert { found: String, expected: String }, + #[suggestion( + trait_selection_tuple_trailing_comma, + code = ",", + applicability = "machine-applicable" + )] + TupleOnlyComma { + #[primary_span] + span: Span, + }, + #[multipart_suggestion( + trait_selection_tuple_trailing_comma, + applicability = "machine-applicable" + )] + TupleAlsoParentheses { + #[suggestion_part(code = "(")] + span_low: Span, + #[suggestion_part(code = ",)")] + span_high: Span, + }, + #[suggestion( + trait_selection_suggest_add_let_for_letchains, + style = "verbose", + applicability = "machine-applicable", + code = "let " + )] + AddLetForLetChains { + #[primary_span] + span: Span, + }, +} + +#[derive(Diagnostic)] +pub enum ObligationCauseFailureCode { + #[diag(trait_selection_oc_method_compat, code = E0308)] + MethodCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_type_compat, code = E0308)] + TypeCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_const_compat, code = E0308)] + ConstCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_try_compat, code = E0308)] + TryCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_match_compat, code = E0308)] + MatchCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_if_else_different, code = E0308)] + IfElseDifferent { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_no_else, code = E0317)] + NoElse { + #[primary_span] + span: Span, + }, + #[diag(trait_selection_oc_no_diverge, code = E0308)] + NoDiverge { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_fn_main_correct_type, code = E0580)] + FnMainCorrectType { + #[primary_span] + span: Span, + }, + #[diag(trait_selection_oc_fn_start_correct_type, code = E0308)] + FnStartCorrectType { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_fn_lang_correct_type, code = E0308)] + FnLangCorrectType { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + lang_item_name: Symbol, + }, + #[diag(trait_selection_oc_intrinsic_correct_type, code = E0308)] + IntrinsicCorrectType { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_method_correct_type, code = E0308)] + MethodCorrectType { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_closure_selfref, code = E0644)] + ClosureSelfref { + #[primary_span] + span: Span, + }, + #[diag(trait_selection_oc_cant_coerce, code = E0308)] + CantCoerce { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_generic, code = E0308)] + Generic { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, +} + +#[derive(Subdiagnostic)] +pub enum AddPreciseCapturing { + #[suggestion( + trait_selection_precise_capturing_new, + style = "verbose", + code = " + use<{concatenated_bounds}>", + applicability = "machine-applicable" + )] + New { + #[primary_span] + span: Span, + new_lifetime: Symbol, + concatenated_bounds: String, + }, + #[suggestion( + trait_selection_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::trait_selection_precise_capturing_new_but_apit, + self.suggs, + Applicability::MaybeIncorrect, + ); + diag.span_note(self.apit_spans, fluent::trait_selection_warn_removing_apit_params); + } +} diff --git a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs new file mode 100644 index 00000000000..b1477763028 --- /dev/null +++ b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs @@ -0,0 +1,185 @@ +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; +use rustc_span::Span; + +use crate::error_reporting::infer::nice_region_error::find_anon_type; +use crate::fluent_generated as fluent; + +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::trait_selection_region_explanation.into()); + if let Some(span) = self.desc.span { + diag.span_note(span, msg); + } else { + diag.note(msg); + } + } +} diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index ad087620ae0..f232a896f96 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -1,20 +1,19 @@ -use crate::infer::at::ToTrace; -use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use crate::traits::{self, Obligation, ObligationCause, ObligationCtxt, SelectionContext}; +use std::fmt::Debug; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; +pub use rustc_infer::infer::*; use rustc_macros::extension; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse}; use rustc_middle::traits::query::NoSolution; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; -use rustc_middle::ty::{GenericArg, Upcast}; +use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast}; use rustc_span::DUMMY_SP; +use tracing::instrument; -use std::fmt::Debug; - -pub use rustc_infer::infer::*; +use crate::infer::at::ToTrace; +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use crate::traits::{self, Obligation, ObligationCause, ObligationCtxt, SelectionContext}; #[extension(pub trait InferCtxtExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index d0a12d73941..a17c007debd 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -19,20 +19,22 @@ #![feature(assert_matches)] #![feature(associated_type_defaults)] #![feature(box_patterns)] +#![feature(cfg_version)] #![feature(control_flow_enum)] #![feature(extract_if)] #![feature(if_let_guard)] +#![feature(iter_intersperse)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustdoc_internals)] +#![feature(try_blocks)] #![feature(type_alias_impl_trait)] #![feature(unwrap_infallible)] +#![feature(yeet_expr)] #![recursion_limit = "512"] // For rustdoc +#![warn(unreachable_pub)] // For rustdoc // tidy-alphabetical-end -#[macro_use] -extern crate tracing; - pub mod error_reporting; pub mod errors; pub mod infer; diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index 5f986e22f51..65762cfcd2e 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -1,10 +1,11 @@ -use crate::traits::ScrubbedTraitError; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{InferCtxt, RegionResolutionError}; use rustc_macros::extension; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::ObligationCause; +use crate::traits::ScrubbedTraitError; + #[extension(pub trait InferCtxtRegionExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { /// Resolve regions, using the deep normalizer to normalize any type-outlives diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index f67518a577e..a7b0719d8d4 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -13,6 +13,7 @@ use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt as _}; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; use rustc_type_ir::solve::{Certainty, NoSolution, SolverMode}; +use tracing::trace; use crate::traits::specialization_graph; @@ -87,12 +88,12 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< ) -> Option<ty::Const<'tcx>> { use rustc_middle::mir::interpret::ErrorHandled; match self.const_eval_resolve(param_env, unevaluated, DUMMY_SP) { - Ok(Some(val)) => Some(ty::Const::new_value( + Ok(Ok(val)) => Some(ty::Const::new_value( self.tcx, val, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args), )), - Ok(None) | Err(ErrorHandled::TooGeneric(_)) => None, + Ok(Err(_)) | Err(ErrorHandled::TooGeneric(_)) => None, Err(ErrorHandled::Reported(e, _)) => Some(ty::Const::new_error(self.tcx, e.into())), } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 76b88aeb0f7..f5f36f40f7e 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -13,13 +13,12 @@ use rustc_middle::bug; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, TyCtxt}; use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _}; -use rustc_span::symbol::sym; - -use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError}; +use tracing::instrument; use super::delegate::SolverDelegate; use super::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor}; use super::Certainty; +use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError}; /// A trait engine using the new trait solver. /// @@ -441,10 +440,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { source: CandidateSource::Impl(impl_def_id), result: _, } = candidate.kind() - && goal - .infcx() - .tcx - .has_attrs_with_path(impl_def_id, &[sym::diagnostic, sym::do_not_recommend]) + && goal.infcx().tcx.do_not_recommend_impl(impl_def_id) { return ControlFlow::Break(self.obligation.clone()); } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index e8de8457440..49c37a684b5 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -9,6 +9,8 @@ //! coherence right now and was annoying to implement, so I am leaving it //! as is until we start using it for something else. +use std::assert_matches::assert_matches; + use rustc_ast_ir::try_visit; use rustc_ast_ir::visit::VisitorResult; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; @@ -21,6 +23,7 @@ use rustc_next_trait_solver::resolve::EagerResolver; use rustc_next_trait_solver::solve::inspect::{self, instantiate_canonical_state}; use rustc_next_trait_solver::solve::{GenerateProofTree, MaybeCause, SolverDelegateEvalExt as _}; use rustc_span::{Span, DUMMY_SP}; +use tracing::instrument; use crate::solve::delegate::SolverDelegate; use crate::traits::ObligationCtxt; @@ -273,10 +276,10 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { steps.push(step) } inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => { - assert!(matches!( + assert_matches!( shallow_certainty.replace(c), None | Some(Certainty::Maybe(MaybeCause::Ambiguity)) - )); + ); } inspect::ProbeStep::NestedProbe(ref probe) => { match probe.kind { @@ -332,13 +335,9 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { pub fn candidates(&'a self) -> Vec<InspectCandidate<'a, 'tcx>> { let mut candidates = vec![]; - let last_eval_step = match self.evaluation_kind { - inspect::CanonicalGoalEvaluationKind::Overflow - | inspect::CanonicalGoalEvaluationKind::CycleInStack - | inspect::CanonicalGoalEvaluationKind::ProvisionalCacheHit => { - warn!("unexpected root evaluation: {:?}", self.evaluation_kind); - return vec![]; - } + let last_eval_step = match &self.evaluation_kind { + // An annoying edge case in case the recursion limit is 0. + inspect::CanonicalGoalEvaluationKind::Overflow => return vec![], inspect::CanonicalGoalEvaluationKind::Evaluation { final_revision } => final_revision, }; diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index ca313590265..938ba2dde84 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -1,19 +1,23 @@ +use std::assert_matches::assert_matches; use std::fmt::Debug; use std::marker::PhantomData; -use crate::error_reporting::traits::{OverflowCause, TypeErrCtxtOverflowExt}; -use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::infer::at::At; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::{FromSolverError, Obligation, TraitEngine}; use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::{self, Ty, TyCtxt, UniverseIndex}; -use rustc_middle::ty::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::{TypeFoldable, TypeVisitableExt}; +use rustc_middle::ty::{ + self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, UniverseIndex, +}; +use tracing::instrument; use super::{FulfillmentCtxt, NextSolverError}; +use crate::error_reporting::traits::OverflowCause; +use crate::error_reporting::InferCtxtErrorExt; +use crate::traits::query::evaluate_obligation::InferCtxtExt; +use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError}; /// Deeply normalize all aliases in `value`. This does not handle inference and expects /// its input to be already fully resolved. @@ -61,7 +65,7 @@ where E: FromSolverError<'tcx, NextSolverError<'tcx>>, { fn normalize_alias_ty(&mut self, alias_ty: Ty<'tcx>) -> Result<Ty<'tcx>, Vec<E>> { - assert!(matches!(alias_ty.kind(), ty::Alias(..))); + assert_matches!(alias_ty.kind(), ty::Alias(..)); let infcx = self.at.infcx; let tcx = infcx.tcx; diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 1d32ef2ccd9..f68e0583307 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -1,20 +1,20 @@ //! Support code for rustdoc and external tools. //! You really don't want to be using this unless you need to. -use super::*; - -use crate::errors::UnableToConstructConstantValue; -use crate::infer::region_constraints::{Constraint, RegionConstraintData}; -use crate::traits::project::ProjectAndUnifyResult; +use std::collections::VecDeque; +use std::iter; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; use rustc_data_structures::unord::UnordSet; use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::{Region, RegionVid}; +use tracing::debug; -use std::collections::VecDeque; -use std::iter; +use super::*; +use crate::errors::UnableToConstructConstantValue; +use crate::infer::region_constraints::{Constraint, RegionConstraintData}; +use crate::traits::project::ProjectAndUnifyResult; // FIXME(twk): this is obviously not nice to duplicate like that #[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)] @@ -765,13 +765,13 @@ impl<'tcx> AutoTraitFinder<'tcx> { unevaluated, obligation.cause.span, ) { - Ok(Some(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args))), - Ok(None) => { + Ok(Ok(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args))), + Ok(Err(_)) => { let tcx = self.tcx; let reported = tcx.dcx().emit_err(UnableToConstructConstantValue { span: tcx.def_span(unevaluated.def), - unevaluated: unevaluated, + unevaluated, }); Err(ErrorHandled::Reported(reported.into(), tcx.def_span(unevaluated.def))) } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 7e996c5c5ef..8558520897b 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -4,15 +4,8 @@ //! [trait-resolution]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html //! [trait-specialization]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html -use crate::infer::outlives::env::OutlivesEnvironment; -use crate::infer::InferOk; -use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor}; -use crate::solve::{deeply_normalize_for_diagnostics, inspect}; -use crate::traits::select::IntercrateAmbiguityCause; -use crate::traits::NormalizeExt; -use crate::traits::SkipLeakCheck; -use crate::traits::{util, FulfillmentErrorCode}; -use crate::traits::{Obligation, ObligationCause, PredicateObligation, SelectionContext}; +use std::fmt::Debug; + use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir::def::DefKind; @@ -28,10 +21,19 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; pub use rustc_next_trait_solver::coherence::*; use rustc_span::symbol::sym; use rustc_span::{Span, DUMMY_SP}; -use std::fmt::Debug; +use tracing::{debug, instrument, warn}; use super::ObligationCtxt; use crate::error_reporting::traits::suggest_new_overflow_limit; +use crate::infer::outlives::env::OutlivesEnvironment; +use crate::infer::InferOk; +use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor}; +use crate::solve::{deeply_normalize_for_diagnostics, inspect}; +use crate::traits::select::IntercrateAmbiguityCause; +use crate::traits::{ + util, FulfillmentErrorCode, NormalizeExt, Obligation, ObligationCause, PredicateObligation, + SelectionContext, SkipLeakCheck, +}; pub struct OverlapResult<'tcx> { pub impl_header: ty::ImplHeader<'tcx>, @@ -52,7 +54,7 @@ pub fn add_placeholder_note<G: EmissionGuarantee>(err: &mut Diag<'_, G>) { ); } -pub fn suggest_increasing_recursion_limit<'tcx, G: EmissionGuarantee>( +pub(crate) fn suggest_increasing_recursion_limit<'tcx, G: EmissionGuarantee>( tcx: TyCtxt<'tcx>, err: &mut Diag<'_, G>, overflowing_predicates: &[ty::Predicate<'tcx>], @@ -721,7 +723,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { // FIXME: While this matches the behavior of the // old solver, it is not the only way in which the unknowable // candidates *weaken* coherence, they can also force otherwise - // sucessful normalization to be ambiguous. + // successful normalization to be ambiguous. Ok(Certainty::Maybe(_) | Certainty::Yes) => { ambiguity_cause = None; break; diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index f93bd0a396d..4289384725f 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -17,6 +17,7 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitableExt, TypeVisitor}; use rustc_span::{Span, DUMMY_SP}; +use tracing::{debug, instrument}; use crate::traits::ObligationCtxt; diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index bdc27e734f9..de1d4ef15ac 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -1,16 +1,6 @@ use std::cell::RefCell; use std::fmt::Debug; -use super::{FromSolverError, TraitEngine}; -use super::{FulfillmentContext, ScrubbedTraitError}; -use crate::error_reporting::traits::TypeErrCtxtExt; -use crate::regions::InferCtxtRegionExt; -use crate::solve::FulfillmentCtxt as NextFulfillmentCtxt; -use crate::solve::NextSolverError; -use crate::traits::fulfill::OldSolverError; -use crate::traits::NormalizeExt; -use crate::traits::StructurallyNormalizeExt; -use crate::traits::{FulfillmentError, Obligation, ObligationCause, PredicateObligation}; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -19,16 +9,22 @@ use rustc_infer::infer::canonical::{ Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse, }; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::RegionResolutionError; -use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError}; use rustc_macros::extension; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::TypeFoldable; -use rustc_middle::ty::Upcast; -use rustc_middle::ty::Variance; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast, Variance}; + +use super::{FromSolverError, FulfillmentContext, ScrubbedTraitError, TraitEngine}; +use crate::error_reporting::InferCtxtErrorExt; +use crate::regions::InferCtxtRegionExt; +use crate::solve::{FulfillmentCtxt as NextFulfillmentCtxt, NextSolverError}; +use crate::traits::fulfill::OldSolverError; +use crate::traits::{ + FulfillmentError, NormalizeExt, Obligation, ObligationCause, PredicateObligation, + StructurallyNormalizeExt, +}; #[extension(pub trait TraitEngineExt<'tcx, E>)] impl<'tcx, E> dyn TraitEngine<'tcx, E> diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 5597c8be592..16ba06f8667 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -1,32 +1,30 @@ -use crate::error_reporting::traits::TypeErrCtxtOverflowExt; -use crate::infer::{InferCtxt, TyOrConstInferVar}; -use crate::traits::normalize::normalize_with_depth_to; +use std::marker::PhantomData; + use rustc_data_structures::captures::Captures; -use rustc_data_structures::obligation_forest::ProcessResult; -use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome}; -use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; +use rustc_data_structures::obligation_forest::{ + Error, ForestObligation, ObligationForest, ObligationProcessor, Outcome, ProcessResult, +}; use rustc_infer::infer::DefineOpaqueTypes; -use rustc_infer::traits::{FromSolverError, ProjectionCacheKey}; -use rustc_infer::traits::{PolyTraitObligation, SelectionError, TraitEngine}; +use rustc_infer::traits::{ + FromSolverError, PolyTraitObligation, ProjectionCacheKey, SelectionError, TraitEngine, +}; use rustc_middle::bug; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::GenericArgsRef; -use rustc_middle::ty::{self, Binder, Const, TypeVisitableExt}; -use std::marker::PhantomData; +use rustc_middle::ty::{self, Binder, Const, GenericArgsRef, TypeVisitableExt}; +use tracing::{debug, debug_span, instrument}; use super::project::{self, ProjectAndUnifyResult}; use super::select::SelectionContext; -use super::wf; -use super::EvaluationResult; -use super::PredicateObligation; -use super::Unimplemented; -use super::{const_evaluatable, ScrubbedTraitError}; -use super::{FulfillmentError, FulfillmentErrorCode}; - -use crate::traits::project::PolyProjectionObligation; -use crate::traits::project::ProjectionCacheKeyExt as _; +use super::{ + const_evaluatable, wf, EvaluationResult, FulfillmentError, FulfillmentErrorCode, + PredicateObligation, ScrubbedTraitError, Unimplemented, +}; +use crate::error_reporting::InferCtxtErrorExt; +use crate::infer::{InferCtxt, TyOrConstInferVar}; +use crate::traits::normalize::normalize_with_depth_to; +use crate::traits::project::{PolyProjectionObligation, ProjectionCacheKeyExt as _}; use crate::traits::query::evaluate_obligation::InferCtxtExt; impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> { @@ -474,7 +472,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { }; match infcx.at(&obligation.cause, obligation.param_env).eq( - // Only really excercised by generic_const_exprs + // Only really exercised by generic_const_exprs DefineOpaqueTypes::Yes, ct_ty, ty, diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index baec2268629..3e65194577e 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -1,9 +1,9 @@ //! Miscellaneous type-system utilities that are too small to deserve their own modules. -use crate::regions::InferCtxtRegionExt; -use crate::traits::{self, FulfillmentError, ObligationCause}; +use std::assert_matches::assert_matches; use hir::LangItem; +use rustc_ast::Mutability; use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_infer::infer::outlives::env::OutlivesEnvironment; @@ -11,6 +11,8 @@ use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt}; use super::outlives_bounds::InferCtxtExt; +use crate::regions::InferCtxtRegionExt; +use crate::traits::{self, FulfillmentError, ObligationCause}; pub enum CopyImplementationError<'tcx> { InfringingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>), @@ -19,6 +21,8 @@ pub enum CopyImplementationError<'tcx> { } pub enum ConstParamTyImplementationError<'tcx> { + UnsizedConstParamsFeatureRequired, + InvalidInnerTyOfBuiltinTy(Vec<(Ty<'tcx>, InfringingFieldsReason<'tcx>)>), InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>), NotAnAdtOrBuiltinAllowed, } @@ -77,9 +81,9 @@ pub fn type_allowed_to_implement_copy<'tcx>( Ok(()) } -/// Checks that the fields of the type (an ADT) all implement `ConstParamTy`. +/// Checks that the fields of the type (an ADT) all implement `(Unsized?)ConstParamTy`. /// -/// If fields don't implement `ConstParamTy`, return an error containing a list of +/// If fields don't implement `(Unsized?)ConstParamTy`, return an error containing a list of /// those violating fields. /// /// If it's not an ADT, int ty, `bool` or `char`, returns `Err(NotAnAdtOrBuiltinAllowed)`. @@ -87,35 +91,95 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, self_type: Ty<'tcx>, + lang_item: LangItem, parent_cause: ObligationCause<'tcx>, ) -> Result<(), ConstParamTyImplementationError<'tcx>> { - let (adt, args) = match self_type.kind() { - // `core` provides these impls. - ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Char - | ty::Str - | ty::Array(..) - | ty::Slice(_) - | ty::Ref(.., hir::Mutability::Not) - | ty::Tuple(_) => return Ok(()), + assert_matches!(lang_item, LangItem::ConstParamTy | LangItem::UnsizedConstParamTy); - &ty::Adt(adt, args) => (adt, args), + let inner_tys: Vec<_> = match *self_type.kind() { + // Trivially okay as these types are all: + // - Sized + // - Contain no nested types + // - Have structural equality + ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => return Ok(()), + + // Handle types gated under `feature(unsized_const_params)` + // FIXME(unsized_const_params): Make `const N: [u8]` work then forbid references + ty::Slice(inner_ty) | ty::Ref(_, inner_ty, Mutability::Not) + if lang_item == LangItem::UnsizedConstParamTy => + { + vec![inner_ty] + } + ty::Str if lang_item == LangItem::UnsizedConstParamTy => { + vec![Ty::new_slice(tcx, tcx.types.u8)] + } + ty::Str | ty::Slice(..) | ty::Ref(_, _, Mutability::Not) => { + return Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired); + } + + ty::Array(inner_ty, _) => vec![inner_ty], + + // `str` morally acts like a newtype around `[u8]` + ty::Tuple(inner_tys) => inner_tys.into_iter().collect(), + + ty::Adt(adt, args) if adt.is_enum() || adt.is_struct() => { + all_fields_implement_trait( + tcx, + param_env, + self_type, + adt, + args, + parent_cause.clone(), + lang_item, + ) + .map_err(ConstParamTyImplementationError::InfrigingFields)?; + + vec![] + } _ => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed), }; - all_fields_implement_trait( - tcx, - param_env, - self_type, - adt, - args, - parent_cause, - hir::LangItem::ConstParamTy, - ) - .map_err(ConstParamTyImplementationError::InfrigingFields)?; + let mut infringing_inner_tys = vec![]; + for inner_ty in inner_tys { + // We use an ocx per inner ty for better diagnostics + let infcx = tcx.infer_ctxt().build(); + let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx); + + ocx.register_bound( + parent_cause.clone(), + param_env, + inner_ty, + tcx.require_lang_item(lang_item, Some(parent_cause.span)), + ); + + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Fulfill(errors))); + continue; + } + + // Check regions assuming the self type of the impl is WF + let outlives_env = OutlivesEnvironment::with_bounds( + param_env, + infcx.implied_bounds_tys( + param_env, + parent_cause.body_id, + &FxIndexSet::from_iter([self_type]), + ), + ); + let errors = infcx.resolve_regions(&outlives_env); + if !errors.is_empty() { + infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Regions(errors))); + continue; + } + } + + if !infringing_inner_tys.is_empty() { + return Err(ConstParamTyImplementationError::InvalidInnerTyOfBuiltinTy( + infringing_inner_tys, + )); + } Ok(()) } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index d28982ed849..c82eaa5143d 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -16,59 +16,62 @@ pub mod query; #[allow(hidden_glob_reexports)] mod select; mod specialize; -mod structural_match; mod structural_normalize; #[allow(hidden_glob_reexports)] mod util; pub mod vtable; pub mod wf; -use crate::error_reporting::traits::TypeErrCtxtExt as _; -use crate::infer::outlives::env::OutlivesEnvironment; -use crate::infer::{InferCtxt, TyCtxtInferExt}; -use crate::regions::InferCtxtRegionExt; -use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use std::fmt::Debug; +use std::ops::ControlFlow; + use rustc_errors::ErrorGuaranteed; +pub use rustc_infer::traits::*; use rustc_middle::query::Providers; use rustc_middle::span_bug; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFolder, TypeSuperVisitable, Upcast}; -use rustc_middle::ty::{GenericArgs, GenericArgsRef}; +use rustc_middle::ty::{ + self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFolder, TypeSuperVisitable, Upcast, +}; use rustc_span::def_id::DefId; use rustc_span::Span; +use tracing::{debug, instrument}; -use std::fmt::Debug; -use std::ops::ControlFlow; - -pub use self::coherence::{add_placeholder_note, orphan_check_trait_ref, overlapping_impls}; -pub use self::coherence::{InCrate, IsFirstInputType, UncoveredTyParams}; -pub use self::coherence::{OrphanCheckErr, OrphanCheckMode, OverlapResult}; +pub use self::coherence::{ + add_placeholder_note, orphan_check_trait_ref, overlapping_impls, InCrate, IsFirstInputType, + OrphanCheckErr, OrphanCheckMode, OverlapResult, UncoveredTyParams, +}; pub use self::engine::{ObligationCtxt, TraitEngineExt}; pub use self::fulfill::{FulfillmentContext, OldSolverError, PendingPredicateObligation}; pub use self::normalize::NormalizeExt; -pub use self::object_safety::hir_ty_lowering_object_safety_violations; -pub use self::object_safety::is_vtable_safe_method; -pub use self::object_safety::object_safety_violations_for_assoc_item; -pub use self::object_safety::ObjectSafetyViolation; +pub use self::object_safety::{ + hir_ty_lowering_object_safety_violations, is_vtable_safe_method, + object_safety_violations_for_assoc_item, ObjectSafetyViolation, +}; pub use self::project::{normalize_inherent_projection, normalize_projection_ty}; -pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; -pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; -pub use self::specialize::specialization_graph::FutureCompatOverlapError; -pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; +pub use self::select::{ + EvaluationCache, EvaluationResult, IntercrateAmbiguityCause, OverflowError, SelectionCache, + SelectionContext, +}; +pub use self::specialize::specialization_graph::{ + FutureCompatOverlapError, FutureCompatOverlapErrorKind, +}; pub use self::specialize::{ specialization_graph, translate_args, translate_args_with_cause, OverlapError, }; -pub use self::structural_match::search_for_structural_match_violation; pub use self::structural_normalize::StructurallyNormalizeExt; -pub use self::util::elaborate; -pub use self::util::{expand_trait_aliases, TraitAliasExpander, TraitAliasExpansionInfo}; -pub use self::util::{impl_item_is_final, upcast_choices}; -pub use self::util::{supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item}; -pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer}; - -pub use rustc_infer::traits::*; +pub use self::util::{ + elaborate, expand_trait_aliases, impl_item_is_final, supertraits, + transitive_bounds_that_define_assoc_item, upcast_choices, with_replaced_escaping_bound_vars, + BoundVarReplacer, PlaceholderReplacer, TraitAliasExpander, TraitAliasExpansionInfo, +}; +use crate::error_reporting::InferCtxtErrorExt; +use crate::infer::outlives::env::OutlivesEnvironment; +use crate::infer::{InferCtxt, TyCtxtInferExt}; +use crate::regions::InferCtxtRegionExt; +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; pub struct FulfillmentError<'tcx> { pub obligation: PredicateObligation<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 01ba8c02ea6..aad47df7369 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -1,20 +1,25 @@ //! Deeply normalize types using the old trait solver. -use super::SelectionContext; -use super::{project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer}; -use crate::error_reporting::traits::OverflowCause; -use crate::error_reporting::traits::TypeErrCtxtOverflowExt; -use crate::solve::NextSolverError; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::infer::at::At; use rustc_infer::infer::InferOk; -use rustc_infer::traits::FromSolverError; -use rustc_infer::traits::PredicateObligation; -use rustc_infer::traits::{Normalized, Obligation, TraitEngine}; +use rustc_infer::traits::{ + FromSolverError, Normalized, Obligation, PredicateObligation, TraitEngine, +}; use rustc_macros::extension; use rustc_middle::traits::{ObligationCause, ObligationCauseCode, Reveal}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFolder}; -use rustc_middle::ty::{TypeFoldable, TypeSuperFoldable, TypeVisitable, TypeVisitableExt}; +use rustc_middle::ty::{ + self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, TypeVisitableExt, +}; +use tracing::{debug, instrument}; + +use super::{ + project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer, + SelectionContext, +}; +use crate::error_reporting::traits::OverflowCause; +use crate::error_reporting::InferCtxtErrorExt; +use crate::solve::NextSolverError; #[extension(pub trait NormalizeExt<'tcx>)] impl<'tcx> At<'_, 'tcx> { diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 1c6993bdd37..a3d5c530797 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -8,30 +8,29 @@ //! - not reference the erased type `Self` except for in this receiver; //! - not have generic type parameters. -use super::elaborate; +use std::iter; +use std::ops::ControlFlow; -use crate::infer::TyCtxtInferExt; -use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{self, Obligation, ObligationCause}; use rustc_errors::FatalError; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::query::Providers; use rustc_middle::ty::{ - self, EarlyBinder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitor, + self, EarlyBinder, ExistentialPredicateStableCmpExt as _, GenericArgs, Ty, TyCtxt, + TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, Upcast, }; -use rustc_middle::ty::{GenericArg, GenericArgs}; -use rustc_middle::ty::{TypeVisitableExt, Upcast}; use rustc_span::symbol::Symbol; use rustc_span::Span; use rustc_target::abi::Abi; use smallvec::SmallVec; +use tracing::{debug, instrument}; -use std::iter; -use std::ops::ControlFlow; - -pub use crate::traits::{MethodViolationCode, ObjectSafetyViolation}; +use super::elaborate; +use crate::infer::TyCtxtInferExt; +use crate::traits::query::evaluate_obligation::InferCtxtExt; +pub use crate::traits::ObjectSafetyViolation; +use crate::traits::{util, MethodViolationCode, Obligation, ObligationCause}; /// Returns the object safety violations that affect HIR ty lowering. /// @@ -187,15 +186,20 @@ fn predicates_reference_self( ) -> SmallVec<[Span; 1]> { let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id)); let predicates = if supertraits_only { - tcx.explicit_super_predicates_of(trait_def_id) + tcx.explicit_super_predicates_of(trait_def_id).skip_binder() } else { - tcx.predicates_of(trait_def_id) + tcx.predicates_of(trait_def_id).predicates }; predicates - .predicates .iter() .map(|&(predicate, sp)| (predicate.instantiate_supertrait(tcx, trait_ref), sp)) - .filter_map(|predicate| predicate_references_self(tcx, predicate)) + .filter_map(|(clause, sp)| { + // Super predicates cannot allow self projections, since they're + // impossible to make into existential bounds without eager resolution + // or something. + // e.g. `trait A: B<Item = Self::Assoc>`. + predicate_references_self(tcx, trait_def_id, clause, sp, AllowSelfProjections::No) + }) .collect() } @@ -204,20 +208,25 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) .flat_map(|item| tcx.explicit_item_bounds(item.def_id).iter_identity_copied()) - .filter_map(|c| predicate_references_self(tcx, c)) + .filter_map(|(clause, sp)| { + // Item bounds *can* have self projections, since they never get + // their self type erased. + predicate_references_self(tcx, trait_def_id, clause, sp, AllowSelfProjections::Yes) + }) .collect() } fn predicate_references_self<'tcx>( tcx: TyCtxt<'tcx>, - (predicate, sp): (ty::Clause<'tcx>, Span), + trait_def_id: DefId, + predicate: ty::Clause<'tcx>, + sp: Span, + allow_self_projections: AllowSelfProjections, ) -> Option<Span> { - let self_ty = tcx.types.self_param; - let has_self_ty = |arg: &GenericArg<'tcx>| arg.walk().any(|arg| arg == self_ty.into()); match predicate.kind().skip_binder() { ty::ClauseKind::Trait(ref data) => { // In the case of a trait predicate, we can skip the "self" type. - data.trait_ref.args[1..].iter().any(has_self_ty).then_some(sp) + data.trait_ref.args[1..].iter().any(|&arg| contains_illegal_self_type_reference(tcx, trait_def_id, arg, allow_self_projections)).then_some(sp) } ty::ClauseKind::Projection(ref data) => { // And similarly for projections. This should be redundant with @@ -235,9 +244,9 @@ fn predicate_references_self<'tcx>( // // This is ALT2 in issue #56288, see that for discussion of the // possible alternatives. - data.projection_term.args[1..].iter().any(has_self_ty).then_some(sp) + data.projection_term.args[1..].iter().any(|&arg| contains_illegal_self_type_reference(tcx, trait_def_id, arg, allow_self_projections)).then_some(sp) } - ty::ClauseKind::ConstArgHasType(_ct, ty) => has_self_ty(&ty.into()).then_some(sp), + ty::ClauseKind::ConstArgHasType(_ct, ty) => contains_illegal_self_type_reference(tcx, trait_def_id, ty, allow_self_projections).then_some(sp), ty::ClauseKind::WellFormed(..) | ty::ClauseKind::TypeOutlives(..) @@ -257,9 +266,8 @@ fn super_predicates_have_non_lifetime_binders( return SmallVec::new(); } tcx.explicit_super_predicates_of(trait_def_id) - .predicates - .iter() - .filter_map(|(pred, span)| pred.has_non_region_bound_vars().then_some(*span)) + .iter_identity_copied() + .filter_map(|(pred, span)| pred.has_non_region_bound_vars().then_some(span)) .collect() } @@ -383,7 +391,12 @@ fn virtual_call_violations_for_method<'tcx>( let mut errors = Vec::new(); for (i, &input_ty) in sig.skip_binder().inputs().iter().enumerate().skip(1) { - if contains_illegal_self_type_reference(tcx, trait_def_id, sig.rebind(input_ty)) { + if contains_illegal_self_type_reference( + tcx, + trait_def_id, + sig.rebind(input_ty), + AllowSelfProjections::Yes, + ) { let span = if let Some(hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(sig, _), .. @@ -396,7 +409,12 @@ fn virtual_call_violations_for_method<'tcx>( errors.push(MethodViolationCode::ReferencesSelfInput(span)); } } - if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) { + if contains_illegal_self_type_reference( + tcx, + trait_def_id, + sig.output(), + AllowSelfProjections::Yes, + ) { errors.push(MethodViolationCode::ReferencesSelfOutput); } if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) { @@ -482,7 +500,7 @@ fn virtual_call_violations_for_method<'tcx>( return false; } - contains_illegal_self_type_reference(tcx, trait_def_id, pred) + contains_illegal_self_type_reference(tcx, trait_def_id, pred, AllowSelfProjections::Yes) }) { errors.push(MethodViolationCode::WhereClauseReferencesSelf); } @@ -494,7 +512,7 @@ fn virtual_call_violations_for_method<'tcx>( /// /// This check is outlined from the object safety check to avoid cycles with /// layout computation, which relies on knowing whether methods are object safe. -pub fn check_receiver_correct<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, method: ty::AssocItem) { +fn check_receiver_correct<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, method: ty::AssocItem) { if !is_vtable_safe_method(tcx, trait_def_id, method) { return; } @@ -711,124 +729,184 @@ fn receiver_is_dispatchable<'tcx>( infcx.predicate_must_hold_modulo_regions(&obligation) } +#[derive(Copy, Clone)] +enum AllowSelfProjections { + Yes, + No, +} + +/// This is somewhat subtle. In general, we want to forbid +/// references to `Self` in the argument and return types, +/// since the value of `Self` is erased. However, there is one +/// exception: it is ok to reference `Self` in order to access +/// an associated type of the current trait, since we retain +/// the value of those associated types in the object type +/// itself. +/// +/// ```rust,ignore (example) +/// trait SuperTrait { +/// type X; +/// } +/// +/// trait Trait : SuperTrait { +/// type Y; +/// fn foo(&self, x: Self) // bad +/// fn foo(&self) -> Self // bad +/// fn foo(&self) -> Option<Self> // bad +/// fn foo(&self) -> Self::Y // OK, desugars to next example +/// fn foo(&self) -> <Self as Trait>::Y // OK +/// fn foo(&self) -> Self::X // OK, desugars to next example +/// fn foo(&self) -> <Self as SuperTrait>::X // OK +/// } +/// ``` +/// +/// However, it is not as simple as allowing `Self` in a projected +/// type, because there are illegal ways to use `Self` as well: +/// +/// ```rust,ignore (example) +/// trait Trait : SuperTrait { +/// ... +/// fn foo(&self) -> <Self as SomeOtherTrait>::X; +/// } +/// ``` +/// +/// Here we will not have the type of `X` recorded in the +/// object type, and we cannot resolve `Self as SomeOtherTrait` +/// without knowing what `Self` is. fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>( tcx: TyCtxt<'tcx>, trait_def_id: DefId, value: T, + allow_self_projections: AllowSelfProjections, ) -> bool { - // This is somewhat subtle. In general, we want to forbid - // references to `Self` in the argument and return types, - // since the value of `Self` is erased. However, there is one - // exception: it is ok to reference `Self` in order to access - // an associated type of the current trait, since we retain - // the value of those associated types in the object type - // itself. - // - // ```rust - // trait SuperTrait { - // type X; - // } - // - // trait Trait : SuperTrait { - // type Y; - // fn foo(&self, x: Self) // bad - // fn foo(&self) -> Self // bad - // fn foo(&self) -> Option<Self> // bad - // fn foo(&self) -> Self::Y // OK, desugars to next example - // fn foo(&self) -> <Self as Trait>::Y // OK - // fn foo(&self) -> Self::X // OK, desugars to next example - // fn foo(&self) -> <Self as SuperTrait>::X // OK - // } - // ``` - // - // However, it is not as simple as allowing `Self` in a projected - // type, because there are illegal ways to use `Self` as well: - // - // ```rust - // trait Trait : SuperTrait { - // ... - // fn foo(&self) -> <Self as SomeOtherTrait>::X; - // } - // ``` - // - // Here we will not have the type of `X` recorded in the - // object type, and we cannot resolve `Self as SomeOtherTrait` - // without knowing what `Self` is. - - struct IllegalSelfTypeVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - supertraits: Option<Vec<DefId>>, - } + value + .visit_with(&mut IllegalSelfTypeVisitor { + tcx, + trait_def_id, + supertraits: None, + allow_self_projections, + }) + .is_break() +} + +struct IllegalSelfTypeVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + supertraits: Option<Vec<ty::TraitRef<'tcx>>>, + allow_self_projections: AllowSelfProjections, +} - impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalSelfTypeVisitor<'tcx> { - type Result = ControlFlow<()>; +impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalSelfTypeVisitor<'tcx> { + type Result = ControlFlow<()>; - fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { - match t.kind() { - ty::Param(_) => { - if t == self.tcx.types.self_param { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - } - ty::Alias(ty::Projection, ref data) - if self.tcx.is_impl_trait_in_trait(data.def_id) => - { - // We'll deny these later in their own pass + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + match t.kind() { + ty::Param(_) => { + if t == self.tcx.types.self_param { + ControlFlow::Break(()) + } else { ControlFlow::Continue(()) } - ty::Alias(ty::Projection, ref data) => { - // This is a projected type `<Foo as SomeTrait>::X`. - - // Compute supertraits of current trait lazily. - if self.supertraits.is_none() { - let trait_ref = - ty::Binder::dummy(ty::TraitRef::identity(self.tcx, self.trait_def_id)); - self.supertraits = Some( - traits::supertraits(self.tcx, trait_ref).map(|t| t.def_id()).collect(), - ); - } + } + ty::Alias(ty::Projection, ref data) if self.tcx.is_impl_trait_in_trait(data.def_id) => { + // We'll deny these later in their own pass + ControlFlow::Continue(()) + } + ty::Alias(ty::Projection, ref data) => { + match self.allow_self_projections { + AllowSelfProjections::Yes => { + // This is a projected type `<Foo as SomeTrait>::X`. + + // Compute supertraits of current trait lazily. + if self.supertraits.is_none() { + self.supertraits = Some( + util::supertraits( + self.tcx, + ty::Binder::dummy(ty::TraitRef::identity( + self.tcx, + self.trait_def_id, + )), + ) + .map(|trait_ref| { + self.tcx.erase_regions( + self.tcx.instantiate_bound_regions_with_erased(trait_ref), + ) + }) + .collect(), + ); + } - // Determine whether the trait reference `Foo as - // SomeTrait` is in fact a supertrait of the - // current trait. In that case, this type is - // legal, because the type `X` will be specified - // in the object type. Note that we can just use - // direct equality here because all of these types - // are part of the formal parameter listing, and - // hence there should be no inference variables. - let is_supertrait_of_current_trait = self - .supertraits - .as_ref() - .unwrap() - .contains(&data.trait_ref(self.tcx).def_id); - - // only walk contained types if it's not a super trait - if is_supertrait_of_current_trait { - ControlFlow::Continue(()) - } else { - t.super_visit_with(self) // POSSIBLY reporting an error + // Determine whether the trait reference `Foo as + // SomeTrait` is in fact a supertrait of the + // current trait. In that case, this type is + // legal, because the type `X` will be specified + // in the object type. Note that we can just use + // direct equality here because all of these types + // are part of the formal parameter listing, and + // hence there should be no inference variables. + let is_supertrait_of_current_trait = + self.supertraits.as_ref().unwrap().contains( + &data.trait_ref(self.tcx).fold_with( + &mut EraseEscapingBoundRegions { + tcx: self.tcx, + binder: ty::INNERMOST, + }, + ), + ); + + // only walk contained types if it's not a super trait + if is_supertrait_of_current_trait { + ControlFlow::Continue(()) + } else { + t.super_visit_with(self) // POSSIBLY reporting an error + } } + AllowSelfProjections::No => t.super_visit_with(self), } - _ => t.super_visit_with(self), // walk contained types, if any } + _ => t.super_visit_with(self), } + } - fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result { - // Constants can only influence object safety if they are generic and reference `Self`. - // This is only possible for unevaluated constants, so we walk these here. - self.tcx.expand_abstract_consts(ct).super_visit_with(self) - } + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result { + // Constants can only influence object safety if they are generic and reference `Self`. + // This is only possible for unevaluated constants, so we walk these here. + self.tcx.expand_abstract_consts(ct).super_visit_with(self) } +} - value - .visit_with(&mut IllegalSelfTypeVisitor { tcx, trait_def_id, supertraits: None }) - .is_break() +struct EraseEscapingBoundRegions<'tcx> { + tcx: TyCtxt<'tcx>, + binder: ty::DebruijnIndex, +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EraseEscapingBoundRegions<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_binder<T>(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T> + where + T: TypeFoldable<TyCtxt<'tcx>>, + { + self.binder.shift_in(1); + let result = t.super_fold_with(self); + self.binder.shift_out(1); + result + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + if let ty::ReBound(debruijn, _) = *r + && debruijn < self.binder + { + r + } else { + self.tcx.lifetimes.re_erased + } + } } -pub fn contains_illegal_impl_trait_in_trait<'tcx>( +fn contains_illegal_impl_trait_in_trait<'tcx>( tcx: TyCtxt<'tcx>, fn_def_id: DefId, ty: ty::Binder<'tcx, Ty<'tcx>>, @@ -852,7 +930,7 @@ pub fn contains_illegal_impl_trait_in_trait<'tcx>( }) } -pub fn provide(providers: &mut Providers) { +pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { object_safety_violations, is_object_safe, diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 1dc2ebfaa7a..ee1b0fc6f2e 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -1,15 +1,16 @@ -use crate::infer::InferCtxt; -use crate::traits::{ObligationCause, ObligationCtxt}; use rustc_data_structures::fx::FxIndexSet; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::infer::InferOk; use rustc_macros::extension; use rustc_middle::infer::canonical::{OriginalQueryValues, QueryRegionConstraints}; use rustc_middle::span_bug; +pub use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty::{self, ParamEnv, Ty, TypeFolder, TypeVisitableExt}; use rustc_span::def_id::LocalDefId; +use tracing::{debug, instrument}; -pub use rustc_middle::traits::query::OutlivesBound; +use crate::infer::InferCtxt; +use crate::traits::{ObligationCause, ObligationCtxt}; pub type BoundsCompat<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a; pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a; diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 1d7a0515044..4702fd866c1 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -2,29 +2,6 @@ use std::ops::ControlFlow; -use super::specialization_graph; -use super::translate_args; -use super::util; -use super::MismatchedProjectionTypes; -use super::Obligation; -use super::ObligationCause; -use super::PredicateObligation; -use super::Selection; -use super::SelectionContext; -use super::SelectionError; -use super::{Normalized, NormalizedTerm, ProjectionCacheEntry, ProjectionCacheKey}; -use rustc_infer::traits::ObligationCauseCode; -use rustc_middle::traits::BuiltinImplSource; -use rustc_middle::traits::ImplSource; -use rustc_middle::traits::ImplSourceUserDefinedData; -use rustc_middle::{bug, span_bug}; - -use crate::errors::InherentProjectionNormalizationOverflow; -use crate::infer::{BoundRegionConversionTime, InferOk}; -use crate::traits::normalize::normalize_with_depth; -use crate::traits::normalize::normalize_with_depth_to; -use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use crate::traits::select::ProjectionMatchesProjection; use rustc_data_structures::sso::SsoHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; @@ -32,13 +9,27 @@ use rustc_hir::def::DefKind; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::infer::DefineOpaqueTypes; +use rustc_infer::traits::ObligationCauseCode; use rustc_middle::traits::select::OverflowError; +pub use rustc_middle::traits::Reveal; +use rustc_middle::traits::{BuiltinImplSource, ImplSource, ImplSourceUserDefinedData}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{self, Term, Ty, TyCtxt, Upcast}; +use rustc_middle::{bug, span_bug}; use rustc_span::symbol::sym; +use tracing::{debug, instrument}; -pub use rustc_middle::traits::Reveal; +use super::{ + specialization_graph, translate_args, util, MismatchedProjectionTypes, Normalized, + NormalizedTerm, Obligation, ObligationCause, PredicateObligation, ProjectionCacheEntry, + ProjectionCacheKey, Selection, SelectionContext, SelectionError, +}; +use crate::errors::InherentProjectionNormalizationOverflow; +use crate::infer::{BoundRegionConversionTime, InferOk}; +use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use crate::traits::select::ProjectionMatchesProjection; pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>; @@ -1120,7 +1111,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Error(_) => false, } } else if tcx.is_lang_item(trait_ref.def_id, LangItem::PointeeTrait) { - let tail = selcx.tcx().struct_tail_with_normalize( + let tail = selcx.tcx().struct_tail_raw( self_ty, |ty| { // We throw away any obligations we get from this, since we normalize @@ -1159,10 +1150,10 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Never // Extern types have unit metadata, according to RFC 2850 | ty::Foreign(_) - // If returned by `struct_tail_without_normalization` this is a unit struct + // If returned by `struct_tail` this is a unit struct // without any fields, or not a struct, and therefore is Sized. | ty::Adt(..) - // If returned by `struct_tail_without_normalization` this is the empty tuple. + // If returned by `struct_tail` this is the empty tuple. | ty::Tuple(..) // Integers and floats are always Sized, and so have unit type metadata. | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, @@ -1202,6 +1193,12 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( false } } + } else if tcx.trait_is_auto(trait_ref.def_id) { + tcx.dcx().span_delayed_bug( + tcx.def_span(obligation.predicate.def_id), + "associated types not allowed on auto traits", + ); + false } else { bug!("unexpected builtin trait with associated type: {trait_ref:?}") } @@ -1640,7 +1637,7 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>( .generics_of(def_id) .host_effect_index .map_or(tcx.consts.true_, |idx| args.const_at(idx)), - ty::FnPtr(_) => tcx.consts.true_, + ty::FnPtr(..) => tcx.consts.true_, _ => unreachable!("only expected FnPtr or FnDef in `confirm_fn_pointer_candidate`"), }; diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 7dc051e6fe9..7036df02465 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -1,11 +1,12 @@ -use crate::traits::query::normalize::QueryNormalizeExt; -use crate::traits::query::NoSolution; -use crate::traits::{Normalized, ObligationCause, ObligationCtxt}; - use rustc_data_structures::fx::FxHashSet; use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult}; use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, Ty, TyCtxt}; use rustc_span::{Span, DUMMY_SP}; +use tracing::{debug, instrument}; + +use crate::traits::query::normalize::QueryNormalizeExt; +use crate::traits::query::NoSolution; +use crate::traits::{Normalized, ObligationCause, ObligationCtxt}; /// This returns true if the type `ty` is "trivial" for /// dropck-outlives -- that is, if it doesn't require any types to @@ -33,7 +34,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | ty::Float(_) | ty::Never | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Char | ty::CoroutineWitness(..) | ty::RawPtr(_, _) @@ -42,8 +43,15 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | ty::Foreign(..) | ty::Error(_) => true, - // `T is PAT`, `[T; N]`, and `[T]` have same properties as T. - ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty), + // `T is PAT` and `[T]` have same properties as T. + ty::Pat(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty), + ty::Array(ty, size) => { + // Empty array never has a dtor. See issue #110288. + match size.try_to_target_usize(tcx) { + Some(0) => true, + _ => trivial_dropck_outlives(tcx, *ty), + } + } // (T1..Tn) and closures have same properties as T1..Tn -- // check if *all* of them are trivial. @@ -217,7 +225,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::CoroutineWitness(..) => { // these types never have a destructor } diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index c11e86abef8..f1b524d1325 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -2,26 +2,27 @@ //! which folds deeply, invoking the underlying //! `normalize_canonicalized_projection_ty` query when it encounters projections. -use crate::error_reporting::traits::OverflowCause; -use crate::error_reporting::traits::TypeErrCtxtOverflowExt; -use crate::infer::at::At; -use crate::infer::canonical::OriginalQueryValues; -use crate::infer::{InferCtxt, InferOk}; -use crate::traits::normalize::needs_normalization; -use crate::traits::Normalized; -use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError}; -use crate::traits::{ObligationCause, PredicateObligation, Reveal}; use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_macros::extension; +pub use rustc_middle::traits::query::NormalizationResult; use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor}; use rustc_span::DUMMY_SP; +use tracing::{debug, info, instrument}; use super::NoSolution; - -pub use rustc_middle::traits::query::NormalizationResult; +use crate::error_reporting::traits::OverflowCause; +use crate::error_reporting::InferCtxtErrorExt; +use crate::infer::at::At; +use crate::infer::canonical::OriginalQueryValues; +use crate::infer::{InferCtxt, InferOk}; +use crate::traits::normalize::needs_normalization; +use crate::traits::{ + BoundVarReplacer, Normalized, ObligationCause, PlaceholderReplacer, PredicateObligation, + Reveal, ScrubbedTraitError, +}; #[extension(pub trait QueryNormalizeExt<'tcx>)] impl<'cx, 'tcx> At<'cx, 'tcx> { @@ -333,14 +334,14 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> return Ok(constant); } - let constant = constant.try_super_fold_with(self)?; - debug!(?constant, ?self.param_env); - Ok(crate::traits::with_replaced_escaping_bound_vars( + let constant = crate::traits::with_replaced_escaping_bound_vars( self.infcx, &mut self.universes, constant, |constant| constant.normalize(self.infcx.tcx, self.param_env), - )) + ); + debug!(?constant, ?self.param_env); + constant.try_super_fold_with(self) } #[inline] diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index aca16950223..2b3c11d4c48 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -1,13 +1,14 @@ -use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; -use crate::traits::ObligationCtxt; use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; use rustc_infer::traits::Obligation; +pub use rustc_middle::traits::query::type_op::AscribeUserType; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, UserArgs, UserSelfTy, UserType}; - -pub use rustc_middle::traits::query::type_op::AscribeUserType; use rustc_span::{Span, DUMMY_SP}; +use tracing::{debug, instrument}; + +use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::traits::ObligationCtxt; impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { type QueryResponse = (); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index d533e69a4fa..6efc2d07843 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -1,14 +1,16 @@ -use crate::infer::canonical::query_response; -use crate::infer::InferCtxt; -use crate::traits::query::type_op::TypeOpOutput; -use crate::traits::ObligationCtxt; +use std::fmt; + use rustc_errors::ErrorGuaranteed; use rustc_infer::infer::region_constraints::RegionConstraintData; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{TyCtxt, TypeFoldable}; use rustc_span::Span; +use tracing::info; -use std::fmt; +use crate::infer::canonical::query_response; +use crate::infer::InferCtxt; +use crate::traits::query::type_op::TypeOpOutput; +use crate::traits::ObligationCtxt; pub struct CustomTypeOp<F> { closure: F, diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs deleted file mode 100644 index 57e649f3e43..00000000000 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; -use crate::traits::ObligationCtxt; -use rustc_middle::traits::query::NoSolution; -use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; - -pub use rustc_middle::traits::query::type_op::Eq; - -impl<'tcx> super::QueryTypeOp<'tcx> for Eq<'tcx> { - type QueryResponse = (); - - fn try_fast_path( - _tcx: TyCtxt<'tcx>, - key: &ParamEnvAnd<'tcx, Eq<'tcx>>, - ) -> Option<Self::QueryResponse> { - if key.value.a == key.value.b { Some(()) } else { None } - } - - fn perform_query( - tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> { - tcx.type_op_eq(canonicalized) - } - - fn perform_locally_with_next_solver( - ocx: &ObligationCtxt<'_, 'tcx>, - key: ParamEnvAnd<'tcx, Self>, - ) -> Result<Self::QueryResponse, NoSolution> { - ocx.eq(&ObligationCause::dummy(), key.param_env, key.value.a, key.value.b)?; - Ok(()) - } -} diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index 8525215a3bc..a493615a1df 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -1,7 +1,3 @@ -use crate::traits::query::NoSolution; -use crate::traits::wf; -use crate::traits::ObligationCtxt; - use rustc_infer::infer::canonical::Canonical; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::query::OutlivesBound; @@ -13,6 +9,10 @@ use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::DUMMY_SP; use rustc_type_ir::outlives::{push_outlives_components, Component}; use smallvec::{smallvec, SmallVec}; +use tracing::debug; + +use crate::traits::query::NoSolution; +use crate::traits::{wf, ObligationCtxt}; #[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct ImpliedOutlivesBounds<'tcx> { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index c1b1bfd300b..a765de92afd 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -1,8 +1,5 @@ -use crate::infer::canonical::{ - Canonical, CanonicalQueryResponse, OriginalQueryValues, QueryRegionConstraints, -}; -use crate::infer::{InferCtxt, InferOk}; -use crate::traits::{ObligationCause, ObligationCtxt}; +use std::fmt; + use rustc_errors::ErrorGuaranteed; use rustc_infer::infer::canonical::Certainty; use rustc_infer::traits::PredicateObligation; @@ -10,16 +7,19 @@ use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; use rustc_span::Span; -use std::fmt; + +use crate::infer::canonical::{ + Canonical, CanonicalQueryResponse, OriginalQueryValues, QueryRegionConstraints, +}; +use crate::infer::{InferCtxt, InferOk}; +use crate::traits::{ObligationCause, ObligationCtxt}; pub mod ascribe_user_type; pub mod custom; -pub mod eq; pub mod implied_outlives_bounds; pub mod normalize; pub mod outlives; pub mod prove_predicate; -pub mod subtype; pub use rustc_middle::traits::query::type_op::*; @@ -168,44 +168,12 @@ where // collecting region constraints via `region_constraints`. let (mut output, _) = scrape_region_constraints( infcx, - |_ocx| { - let (output, ei, mut obligations, _) = + |ocx| { + let (output, ei, obligations, _) = Q::fully_perform_into(self, infcx, &mut region_constraints, span)?; error_info = ei; - // Typically, instantiating NLL query results does not - // create obligations. However, in some cases there - // are unresolved type variables, and unify them *can* - // create obligations. In that case, we have to go - // fulfill them. We do this via a (recursive) query. - while !obligations.is_empty() { - trace!("{:#?}", obligations); - let mut progress = false; - for obligation in std::mem::take(&mut obligations) { - let obligation = infcx.resolve_vars_if_possible(obligation); - match ProvePredicate::fully_perform_into( - obligation.param_env.and(ProvePredicate::new(obligation.predicate)), - infcx, - &mut region_constraints, - span, - ) { - Ok(((), _, new, certainty)) => { - obligations.extend(new); - progress = true; - if let Certainty::Ambiguous = certainty { - obligations.push(obligation); - } - } - Err(_) => obligations.push(obligation), - } - } - if !progress { - infcx.dcx().span_bug( - span, - format!("ambiguity processing {obligations:?} from {self:?}"), - ); - } - } + ocx.register_obligations(obligations); Ok(output) }, "fully_perform", diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index e9948bf1f71..41c34f6da29 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -1,12 +1,13 @@ -use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; -use crate::traits::ObligationCtxt; +use std::fmt; + +pub use rustc_middle::traits::query::type_op::Normalize; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; -use std::fmt; -pub use rustc_middle::traits::query::type_op::Normalize; +use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::traits::ObligationCtxt; impl<'tcx, T> super::QueryTypeOp<'tcx> for Normalize<T> where diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs index 3e7aa52dcfe..49d324fa62e 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs @@ -1,11 +1,12 @@ +use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; +use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution}; +use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt}; + use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; use crate::traits::query::dropck_outlives::{ compute_dropck_outlives_inner, trivial_dropck_outlives, }; use crate::traits::ObligationCtxt; -use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; -use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution}; -use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt}; #[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct DropckOutlives<'tcx> { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index 63289746f5e..d6687c762c3 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -1,11 +1,12 @@ -use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; -use crate::traits::ObligationCtxt; +use rustc_hir::LangItem; use rustc_infer::traits::Obligation; +pub use rustc_middle::traits::query::type_op::ProvePredicate; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt}; -pub use rustc_middle::traits::query::type_op::ProvePredicate; +use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::traits::ObligationCtxt; impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { type QueryResponse = (); @@ -20,8 +21,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { // such cases. if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) = key.value.predicate.kind().skip_binder() - && let Some(sized_def_id) = tcx.lang_items().sized_trait() - && trait_ref.def_id() == sized_def_id + && tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) && trait_ref.self_ty().is_trivially_sized(tcx) { return Some(()); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs deleted file mode 100644 index ae11b0825bd..00000000000 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; -use crate::traits::ObligationCtxt; -use rustc_middle::traits::query::NoSolution; -use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; - -pub use rustc_middle::traits::query::type_op::Subtype; - -impl<'tcx> super::QueryTypeOp<'tcx> for Subtype<'tcx> { - type QueryResponse = (); - - fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<()> { - if key.value.sub == key.value.sup { Some(()) } else { None } - } - - fn perform_query( - tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> { - tcx.type_op_subtype(canonicalized) - } - - fn perform_locally_with_next_solver( - ocx: &ObligationCtxt<'_, 'tcx>, - key: ParamEnvAnd<'tcx, Self>, - ) -> Result<Self::QueryResponse, NoSolution> { - ocx.sub(&ObligationCause::dummy(), key.param_env, key.value.sub, key.value.sup)?; - Ok(()) - } -} diff --git a/compiler/rustc_trait_selection/src/traits/select/_match.rs b/compiler/rustc_trait_selection/src/traits/select/_match.rs index 50d8e96aaf9..f77f83ed447 100644 --- a/compiler/rustc_trait_selection/src/traits/select/_match.rs +++ b/compiler/rustc_trait_selection/src/traits/select/_match.rs @@ -3,7 +3,7 @@ use rustc_infer::infer::relate::{ }; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; -use tracing::{debug, instrument}; +use tracing::instrument; /// A type "A" *matches* "B" if the fresh types in B could be /// instantiated with values so as to make it equal to A. Matching is @@ -21,22 +21,18 @@ use tracing::{debug, instrument}; /// Like subtyping, matching is really a binary relation, so the only /// important thing about the result is Ok/Err. Also, matching never /// affects any type variables or unification state. -pub struct MatchAgainstFreshVars<'tcx> { +pub(crate) struct MatchAgainstFreshVars<'tcx> { tcx: TyCtxt<'tcx>, } impl<'tcx> MatchAgainstFreshVars<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>) -> MatchAgainstFreshVars<'tcx> { + pub(crate) fn new(tcx: TyCtxt<'tcx>) -> MatchAgainstFreshVars<'tcx> { MatchAgainstFreshVars { tcx } } } impl<'tcx> TypeRelation<TyCtxt<'tcx>> for MatchAgainstFreshVars<'tcx> { - fn tag(&self) -> &'static str { - "MatchAgainstFreshVars" - } - - fn tcx(&self) -> TyCtxt<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -50,7 +46,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for MatchAgainstFreshVars<'tcx> { self.relate(a, b) } - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "trace")] fn regions( &mut self, a: ty::Region<'tcx>, @@ -59,7 +55,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for MatchAgainstFreshVars<'tcx> { Ok(a) } - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "trace")] fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { if a == b { return Ok(a); @@ -77,18 +73,18 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for MatchAgainstFreshVars<'tcx> { Err(TypeError::Sorts(ExpectedFound::new(true, a, b))) } - (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(self.tcx(), guar)), + (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(self.cx(), guar)), _ => structurally_relate_tys(self, a, b), } } + #[instrument(skip(self), level = "trace")] fn consts( &mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>, ) -> RelateResult<'tcx, ty::Const<'tcx>> { - debug!("{}.consts({:?}, {:?})", self.tag(), a, b); if a == b { return Ok(a); } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 4c3d833b0f9..96faa5236b1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -12,20 +12,18 @@ use hir::def_id::DefId; use hir::LangItem; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_hir as hir; -use rustc_infer::traits::ObligationCause; -use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError}; +use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation, SelectionError}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::{self, ToPolyTraitRef, Ty, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; +use tracing::{debug, instrument, trace}; +use super::SelectionCandidate::*; +use super::{BuiltinImplConditions, SelectionCandidateSet, SelectionContext, TraitObligationStack}; use crate::traits; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::util; -use super::BuiltinImplConditions; -use super::SelectionCandidate::*; -use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack}; - impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { #[instrument(skip(self, stack), level = "debug")] pub(super) fn assemble_candidates<'o>( @@ -470,8 +468,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } candidates.vec.push(AsyncClosureCandidate); } - ty::FnDef(..) | ty::FnPtr(..) => { - candidates.vec.push(AsyncClosureCandidate); + // Provide an impl, but only for suitable `fn` pointers. + ty::FnPtr(sig_tys, hdr) => { + if sig_tys.with(hdr).is_fn_trait_compatible() { + candidates.vec.push(AsyncClosureCandidate); + } + } + // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). + ty::FnDef(def_id, _) => { + let tcx = self.tcx(); + if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible() + && tcx.codegen_fn_attrs(def_id).target_features.is_empty() + { + candidates.vec.push(AsyncClosureCandidate); + } } _ => {} } @@ -526,8 +536,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates.ambiguous = true; // Could wind up being a fn() type. } // Provide an impl, but only for suitable `fn` pointers. - ty::FnPtr(sig) => { - if sig.is_fn_trait_compatible() { + ty::FnPtr(sig_tys, hdr) => { + if sig_tys.with(hdr).is_fn_trait_compatible() { candidates .vec .push(FnPointerCandidate { fn_host_effect: self.tcx().consts.true_ }); @@ -772,7 +782,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); } - ty::Alias(ty::Opaque, _) => { + ty::Alias(ty::Opaque, alias) => { if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate(_))) { // We do not generate an auto impl candidate for `impl Trait`s which already // reference our auto trait. @@ -787,6 +797,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // We do not emit auto trait candidates for opaque types in coherence. // Doing so can result in weird dependency cycles. candidates.ambiguous = true; + } else if self.infcx.can_define_opaque_ty(alias.def_id) { + // We do not emit auto trait candidates for opaque types in their defining scope, as + // we need to know the hidden type first, which we can't reliably know within the defining + // scope. + candidates.ambiguous = true; } else { candidates.vec.push(AutoImplCandidate) } @@ -805,7 +820,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) @@ -1193,7 +1208,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Never | ty::Foreign(_) | ty::Array(..) @@ -1276,7 +1291,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::Pat(_, _) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Dynamic(_, _, _) | ty::Closure(..) | ty::CoroutineClosure(..) @@ -1325,7 +1340,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let self_ty = self.infcx.resolve_vars_if_possible(obligation.self_ty()); match self_ty.skip_binder().kind() { - ty::FnPtr(_) => candidates.vec.push(BuiltinCandidate { has_nested: false }), + ty::FnPtr(..) => candidates.vec.push(BuiltinCandidate { has_nested: false }), ty::Bool | ty::Char | ty::Int(_) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 9508a3e8e15..daf0700ec0c 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -7,20 +7,24 @@ //! [rustc dev guide]: //! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation +use std::iter; +use std::ops::ControlFlow; + use rustc_ast::Mutability; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::HigherRankedType; -use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; +use rustc_infer::infer::{DefineOpaqueTypes, HigherRankedType, InferOk}; use rustc_infer::traits::ObligationCauseCode; use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; use rustc_middle::ty::{ - self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, TraitPredicate, Ty, - TyCtxt, Upcast, + self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, Ty, TyCtxt, Upcast, }; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; +use tracing::{debug, instrument}; +use super::SelectionCandidate::{self, *}; +use super::{BuiltinImplConditions, SelectionContext}; use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::util::{self, closure_trait_ref_and_return_type}; use crate::traits::{ @@ -29,13 +33,6 @@ use crate::traits::{ SignatureMismatch, TraitNotObjectSafe, TraitObligation, Unimplemented, }; -use super::BuiltinImplConditions; -use super::SelectionCandidate::{self, *}; -use super::SelectionContext; - -use std::iter; -use std::ops::ControlFlow; - impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { #[instrument(level = "debug", skip(self))] pub(super) fn confirm_candidate( @@ -295,90 +292,120 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { - use rustc_transmute::{Answer, Condition}; - #[instrument(level = "debug", skip(tcx, obligation, predicate))] + use rustc_transmute::{Answer, Assume, Condition}; + + /// Generate sub-obligations for reference-to-reference transmutations. + fn reference_obligations<'tcx>( + tcx: TyCtxt<'tcx>, + obligation: &PolyTraitObligation<'tcx>, + (src_lifetime, src_ty, src_mut): (ty::Region<'tcx>, Ty<'tcx>, Mutability), + (dst_lifetime, dst_ty, dst_mut): (ty::Region<'tcx>, Ty<'tcx>, Mutability), + assume: Assume, + ) -> Vec<PredicateObligation<'tcx>> { + let make_transmute_obl = |src, dst| { + let transmute_trait = obligation.predicate.def_id(); + let assume = obligation.predicate.skip_binder().trait_ref.args.const_at(2); + let trait_ref = ty::TraitRef::new( + tcx, + transmute_trait, + [ + ty::GenericArg::from(dst), + ty::GenericArg::from(src), + ty::GenericArg::from(assume), + ], + ); + Obligation::with_depth( + tcx, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + obligation.predicate.rebind(trait_ref), + ) + }; + + let make_freeze_obl = |ty| { + let trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Freeze, None), + [ty::GenericArg::from(ty)], + ); + Obligation::with_depth( + tcx, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + trait_ref, + ) + }; + + let make_outlives_obl = |target, region| { + let outlives = ty::OutlivesPredicate(target, region); + Obligation::with_depth( + tcx, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + obligation.predicate.rebind(outlives), + ) + }; + + // Given a transmutation from `&'a (mut) Src` and `&'dst (mut) Dst`, + // it is always the case that `Src` must be transmutable into `Dst`, + // and that that `'src` must outlive `'dst`. + let mut obls = vec![make_transmute_obl(src_ty, dst_ty)]; + if !assume.lifetimes { + obls.push(make_outlives_obl(src_lifetime, dst_lifetime)); + } + + // Given a transmutation from `&Src`, both `Src` and `Dst` must be + // `Freeze`, otherwise, using the transmuted value could lead to + // data races. + if src_mut == Mutability::Not { + obls.extend([make_freeze_obl(src_ty), make_freeze_obl(dst_ty)]) + } + + // Given a transmutation into `&'dst mut Dst`, it also must be the + // case that `Dst` is transmutable into `Src`. For example, + // transmuting bool -> u8 is OK as long as you can't update that u8 + // to be > 1, because you could later transmute the u8 back to a + // bool and get undefined behavior. It also must be the case that + // `'dst` lives exactly as long as `'src`. + if dst_mut == Mutability::Mut { + obls.push(make_transmute_obl(dst_ty, src_ty)); + if !assume.lifetimes { + obls.push(make_outlives_obl(dst_lifetime, src_lifetime)); + } + } + + obls + } + + /// Flatten the `Condition` tree into a conjunction of obligations. + #[instrument(level = "debug", skip(tcx, obligation))] fn flatten_answer_tree<'tcx>( tcx: TyCtxt<'tcx>, obligation: &PolyTraitObligation<'tcx>, - predicate: TraitPredicate<'tcx>, cond: Condition<rustc_transmute::layout::rustc::Ref<'tcx>>, + assume: Assume, ) -> Vec<PredicateObligation<'tcx>> { match cond { // FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll` // Not possible until the trait solver supports disjunctions of obligations Condition::IfAll(conds) | Condition::IfAny(conds) => conds .into_iter() - .flat_map(|cond| flatten_answer_tree(tcx, obligation, predicate, cond)) + .flat_map(|cond| flatten_answer_tree(tcx, obligation, cond, assume)) .collect(), - Condition::IfTransmutable { src, dst } => { - let transmute_trait = obligation.predicate.def_id(); - let assume_const = predicate.trait_ref.args.const_at(2); - let make_transmute_obl = |from_ty, to_ty| { - let trait_ref = ty::TraitRef::new( - tcx, - transmute_trait, - [ - ty::GenericArg::from(to_ty), - ty::GenericArg::from(from_ty), - ty::GenericArg::from(assume_const), - ], - ); - Obligation::with_depth( - tcx, - obligation.cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - trait_ref, - ) - }; - - let make_freeze_obl = |ty| { - let trait_ref = ty::TraitRef::new( - tcx, - tcx.require_lang_item(LangItem::Freeze, None), - [ty::GenericArg::from(ty)], - ); - Obligation::with_depth( - tcx, - obligation.cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - trait_ref, - ) - }; - - let mut obls = vec![]; - - // If the source is a shared reference, it must be `Freeze`; - // otherwise, transmuting could lead to data races. - if src.mutability == Mutability::Not { - obls.extend([make_freeze_obl(src.ty), make_freeze_obl(dst.ty)]) - } - - // If Dst is mutable, check bidirectionally. - // For example, transmuting bool -> u8 is OK as long as you can't update that u8 - // to be > 1, because you could later transmute the u8 back to a bool and get UB. - match dst.mutability { - Mutability::Not => obls.push(make_transmute_obl(src.ty, dst.ty)), - Mutability::Mut => obls.extend([ - make_transmute_obl(src.ty, dst.ty), - make_transmute_obl(dst.ty, src.ty), - ]), - } - - obls - } + Condition::IfTransmutable { src, dst } => reference_obligations( + tcx, + obligation, + (src.lifetime, src.ty, src.mutability), + (dst.lifetime, dst.ty, dst.mutability), + assume, + ), } } - // We erase regions here because transmutability calls layout queries, - // which does not handle inference regions and doesn't particularly - // care about other regions. Erasing late-bound regions is equivalent - // to instantiating the binder with placeholders then erasing those - // placeholder regions. - let predicate = self - .tcx() - .erase_regions(self.tcx().instantiate_bound_regions_with_erased(obligation.predicate)); + let predicate = obligation.predicate.skip_binder(); let Some(assume) = rustc_transmute::Assume::from_const( self.infcx.tcx, @@ -390,6 +417,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let dst = predicate.trait_ref.args.type_at(0); let src = predicate.trait_ref.args.type_at(1); + debug!(?src, ?dst); let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx); let maybe_transmutable = transmute_env.is_transmutable( @@ -400,7 +428,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let fully_flattened = match maybe_transmutable { Answer::No(_) => Err(Unimplemented)?, - Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, predicate, cond), + Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, cond, assume), Answer::Yes => vec![], }; @@ -573,21 +601,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Check supertraits hold. This is so that their associated type bounds // will be checked in the code below. - for super_trait in tcx + for (supertrait, _) in tcx .explicit_super_predicates_of(trait_predicate.def_id()) - .instantiate(tcx, trait_predicate.trait_ref.args) - .predicates - .into_iter() + .iter_instantiated_copied(tcx, trait_predicate.trait_ref.args) { - let normalized_super_trait = normalize_with_depth_to( + let normalized_supertrait = normalize_with_depth_to( self, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, - super_trait, + supertrait, &mut nested, ); - nested.push(obligation.with(tcx, normalized_super_trait)); + nested.push(obligation.with(tcx, normalized_supertrait)); } let assoc_types: Vec<_> = tcx @@ -1401,7 +1427,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Never | ty::Foreign(_) => {} diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 7a93f59f163..241c3a3d141 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2,31 +2,12 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html#selection -use self::EvaluationResult::*; -use self::SelectionCandidate::*; - -use super::coherence::{self, Conflict}; -use super::const_evaluatable; -use super::project; -use super::project::ProjectionTermObligation; -use super::util; -use super::util::closure_trait_ref_and_return_type; -use super::wf; -use super::{ - ImplDerivedCause, Normalized, Obligation, ObligationCause, ObligationCauseCode, Overflow, - PolyTraitObligation, PredicateObligation, Selection, SelectionError, SelectionResult, - TraitQueryMode, -}; +use std::cell::{Cell, RefCell}; +use std::fmt::{self, Display}; +use std::ops::ControlFlow; +use std::{cmp, iter}; -use crate::error_reporting::traits::TypeErrCtxtOverflowExt; -use crate::infer::{InferCtxt, InferCtxtExt, InferOk, TypeFreshener}; -use crate::solve::InferCtxtSelectExt as _; -use crate::traits::normalize::normalize_with_depth; -use crate::traits::normalize::normalize_with_depth_to; -use crate::traits::project::ProjectAndUnifyResult; -use crate::traits::project::ProjectionCacheKeyExt; -use crate::traits::ProjectionCacheKey; -use crate::traits::Unimplemented; +use hir::def::DefKind; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Diag, EmissionGuarantee}; @@ -34,31 +15,40 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; use rustc_infer::infer::relate::TypeRelation; -use rustc_infer::infer::BoundRegionConversionTime; -use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType; +use rustc_infer::infer::BoundRegionConversionTime::{self, HigherRankedType}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::traits::TraitObligation; use rustc_middle::bug; -use rustc_middle::dep_graph::dep_kinds; -use rustc_middle::dep_graph::DepNodeIndex; +use rustc_middle::dep_graph::{dep_kinds, DepNodeIndex}; use rustc_middle::mir::interpret::ErrorHandled; +pub use rustc_middle::traits::select::*; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::TypeErrorToStringExt; -use rustc_middle::ty::print::PrintTraitRefExt as _; -use rustc_middle::ty::GenericArgsRef; -use rustc_middle::ty::{self, PolyProjectionPredicate, Upcast}; -use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; +use rustc_middle::ty::print::{with_no_trimmed_paths, PrintTraitRefExt as _}; +use rustc_middle::ty::{ + self, GenericArgsRef, PolyProjectionPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, + Upcast, +}; use rustc_span::symbol::sym; use rustc_span::Symbol; +use tracing::{debug, instrument, trace}; -use std::cell::{Cell, RefCell}; -use std::cmp; -use std::fmt::{self, Display}; -use std::iter; -use std::ops::ControlFlow; - -pub use rustc_middle::traits::select::*; -use rustc_middle::ty::print::with_no_trimmed_paths; +use self::EvaluationResult::*; +use self::SelectionCandidate::*; +use super::coherence::{self, Conflict}; +use super::project::ProjectionTermObligation; +use super::util::closure_trait_ref_and_return_type; +use super::{ + const_evaluatable, project, util, wf, ImplDerivedCause, Normalized, Obligation, + ObligationCause, ObligationCauseCode, Overflow, PolyTraitObligation, PredicateObligation, + Selection, SelectionError, SelectionResult, TraitQueryMode, +}; +use crate::error_reporting::InferCtxtErrorExt; +use crate::infer::{InferCtxt, InferCtxtExt, InferOk, TypeFreshener}; +use crate::solve::InferCtxtSelectExt as _; +use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; +use crate::traits::project::{ProjectAndUnifyResult, ProjectionCacheKeyExt}; +use crate::traits::{ProjectionCacheKey, Unimplemented}; mod _match; mod candidate_assembly; @@ -1001,7 +991,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; match self.infcx.at(&obligation.cause, obligation.param_env).eq( - // Only really excercised by generic_const_exprs + // Only really exercised by generic_const_exprs DefineOpaqueTypes::Yes, ct_ty, ty, @@ -1498,7 +1488,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return false; } - // Avoid using the master cache during coherence and just rely + // Avoid using the global cache during coherence and just rely // on the local cache. This effectively disables caching // during coherence. It is really just a simplification to // avoid us having to fear that coherence results "pollute" @@ -1509,6 +1499,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return false; } + // Avoid using the global cache when we're defining opaque types + // as their hidden type may impact the result of candidate selection. + if !self.infcx.defining_opaque_types().is_empty() { + return false; + } + // Otherwise, we can use the global cache. true } @@ -2118,7 +2114,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Bool | ty::Float(_) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::RawPtr(..) | ty::Char | ty::Ref(..) @@ -2175,7 +2171,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { use self::BuiltinImplConditions::{Ambiguous, None, Where}; match *self_ty.kind() { - ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())), + ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())), ty::Uint(_) | ty::Int(_) @@ -2256,8 +2252,21 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } - // FIXME(async_closures): These are never clone, for now. - ty::CoroutineClosure(_, _) => None, + ty::CoroutineClosure(_, args) => { + // (*) binder moved here + let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty()); + if let ty::Infer(ty::TyVar(_)) = ty.kind() { + // Not yet resolved. + Ambiguous + } else { + Where( + obligation + .predicate + .rebind(args.as_coroutine_closure().upvar_tys().to_vec()), + ) + } + } + // `Copy` and `Clone` are automatically implemented for an anonymous adt // if all of its fields are `Copy` and `Clone` ty::Adt(adt, args) if adt.is_anonymous() => { @@ -2324,7 +2333,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Bool | ty::Float(_) | ty::FnDef(..) - | ty::FnPtr(_) + | ty::FnPtr(..) | ty::Error(_) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Never @@ -2380,13 +2389,17 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { - // We can resolve the `impl Trait` to its concrete type, - // which enforces a DAG between the functions requiring - // the auto trait bounds in question. - match self.tcx().type_of_opaque(def_id) { - Ok(ty) => t.rebind(vec![ty.instantiate(self.tcx(), args)]), - Err(_) => { - return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id)); + if self.infcx.can_define_opaque_ty(def_id) { + unreachable!() + } else { + // We can resolve the `impl Trait` to its concrete type, + // which enforces a DAG between the functions requiring + // the auto trait bounds in question. + match self.tcx().type_of_opaque(def_id) { + Ok(ty) => t.rebind(vec![ty.instantiate(self.tcx(), args)]), + Err(_) => { + return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id)); + } } } } @@ -2787,6 +2800,26 @@ impl<'tcx> SelectionContext<'_, 'tcx> { }); } + // Register any outlives obligations from the trait here, cc #124336. + if matches!(tcx.def_kind(def_id), DefKind::Impl { of_trait: true }) { + for clause in tcx.impl_super_outlives(def_id).iter_instantiated(tcx, args) { + let clause = normalize_with_depth_to( + self, + param_env, + cause.clone(), + recursion_depth, + clause, + &mut obligations, + ); + obligations.push(Obligation { + cause: cause.clone(), + recursion_depth, + param_env, + predicate: clause.as_predicate(), + }); + } + } + obligations } } @@ -3094,7 +3127,7 @@ impl<'o, 'tcx> fmt::Debug for TraitObligationStack<'o, 'tcx> { } } -pub enum ProjectionMatchesProjection { +pub(crate) enum ProjectionMatchesProjection { Yes, Ambiguous, No, diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 3c33d13567d..7337b59f870 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -10,28 +10,27 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html pub mod specialization_graph; + +use rustc_data_structures::fx::FxIndexSet; +use rustc_errors::codes::*; +use rustc_errors::{Diag, EmissionGuarantee}; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::DefineOpaqueTypes; +use rustc_middle::bug; +use rustc_middle::query::LocalCrate; use rustc_middle::ty::print::PrintTraitRefExt as _; +use rustc_middle::ty::{self, GenericArgsRef, ImplSubject, Ty, TyCtxt, TypeVisitableExt}; +use rustc_session::lint::builtin::{COHERENCE_LEAK_CHECK, ORDER_DEPENDENT_TRAIT_OBJECTS}; +use rustc_span::{sym, ErrorGuaranteed, Span, DUMMY_SP}; use specialization_graph::GraphExt; +use tracing::{debug, instrument}; +use super::{util, SelectionContext}; use crate::error_reporting::traits::to_pretty_impl_header; use crate::errors::NegativePositiveConflict; use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt}; use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::{coherence, FutureCompatOverlapErrorKind, ObligationCause, ObligationCtxt}; -use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{codes::*, Diag, EmissionGuarantee}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::bug; -use rustc_middle::query::LocalCrate; -use rustc_middle::ty::GenericArgsRef; -use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt}; -use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; -use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; -use rustc_span::{sym, ErrorGuaranteed, Span, DUMMY_SP}; - -use super::util; -use super::SelectionContext; /// Information pertinent to an overlapping impl error. #[derive(Debug)] diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 90f2c7ad213..0fdaf40b136 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -1,14 +1,14 @@ -use super::OverlapError; - -use crate::traits; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; use rustc_macros::extension; use rustc_middle::bug; +pub use rustc_middle::traits::specialization_graph::*; use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams}; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; +use tracing::{debug, instrument}; -pub use rustc_middle::traits::specialization_graph::*; +use super::OverlapError; +use crate::traits; #[derive(Copy, Clone, Debug)] pub enum FutureCompatOverlapErrorKind { diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs deleted file mode 100644 index d4535db951e..00000000000 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ /dev/null @@ -1,173 +0,0 @@ -use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_middle::bug; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; -use std::ops::ControlFlow; - -/// This method traverses the structure of `ty`, trying to find an -/// instance of an ADT (i.e. struct or enum) that doesn't implement -/// the structural-match traits, or a generic type parameter -/// (which cannot be determined to be structural-match). -/// -/// The "structure of a type" includes all components that would be -/// considered when doing a pattern match on a constant of that -/// type. -/// -/// * This means this method descends into fields of structs/enums, -/// and also descends into the inner type `T` of `&T` and `&mut T` -/// -/// * The traversal doesn't dereference unsafe pointers (`*const T`, -/// `*mut T`), and it does not visit the type arguments of an -/// instantiated generic like `PhantomData<T>`. -/// -/// The reason we do this search is Rust currently require all ADTs -/// reachable from a constant's type to implement the -/// structural-match traits, which essentially say that -/// the implementation of `PartialEq::eq` behaves *equivalently* to a -/// comparison against the unfolded structure. -/// -/// For more background on why Rust has this requirement, and issues -/// that arose when the requirement was not enforced completely, see -/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. -pub fn search_for_structural_match_violation<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, -) -> Option<Ty<'tcx>> { - ty.visit_with(&mut Search { tcx, seen: FxHashSet::default() }).break_value() -} - -/// This implements the traversal over the structure of a given type to try to -/// find instances of ADTs (specifically structs or enums) that do not implement -/// `StructuralPartialEq`. -struct Search<'tcx> { - tcx: TyCtxt<'tcx>, - - /// Tracks ADTs previously encountered during search, so that - /// we will not recur on them again. - seen: FxHashSet<hir::def_id::DefId>, -} - -impl<'tcx> Search<'tcx> { - fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool { - adt_ty.is_structural_eq_shallow(self.tcx) - } -} - -impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Search<'tcx> { - type Result = ControlFlow<Ty<'tcx>>; - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - debug!("Search visiting ty: {:?}", ty); - - let (adt_def, args) = match *ty.kind() { - ty::Adt(adt_def, args) => (adt_def, args), - ty::Param(_) => { - return ControlFlow::Break(ty); - } - ty::Dynamic(..) => { - return ControlFlow::Break(ty); - } - ty::Foreign(_) => { - return ControlFlow::Break(ty); - } - ty::Alias(..) => { - return ControlFlow::Break(ty); - } - ty::Closure(..) => { - return ControlFlow::Break(ty); - } - ty::CoroutineClosure(..) => { - return ControlFlow::Break(ty); - } - ty::Coroutine(..) | ty::CoroutineWitness(..) => { - return ControlFlow::Break(ty); - } - ty::FnDef(..) => { - // Types of formals and return in `fn(_) -> _` are also irrelevant; - // so we do not recur into them via `super_visit_with` - return ControlFlow::Continue(()); - } - ty::Array(_, n) - if { n.try_eval_target_usize(self.tcx, ty::ParamEnv::reveal_all()) == Some(0) } => - { - // rust-lang/rust#62336: ignore type of contents - // for empty array. - return ControlFlow::Continue(()); - } - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::Never => { - // These primitive types are always structural match. - // - // `Never` is kind of special here, but as it is not inhabitable, this should be fine. - return ControlFlow::Continue(()); - } - - ty::FnPtr(..) => { - return ControlFlow::Continue(()); - } - - ty::RawPtr(..) => { - // structural-match ignores substructure of - // `*const _`/`*mut _`, so skip `super_visit_with`. - // - // For example, if you have: - // ``` - // struct NonStructural; - // #[derive(PartialEq, Eq)] - // struct T(*const NonStructural); - // const C: T = T(std::ptr::null()); - // ``` - // - // Even though `NonStructural` does not implement `PartialEq`, - // structural equality on `T` does not recur into the raw - // pointer. Therefore, one can still use `C` in a pattern. - return ControlFlow::Continue(()); - } - - ty::Float(_) => { - return ControlFlow::Continue(()); - } - - ty::Pat(..) | ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => { - // First check all contained types and then tell the caller to continue searching. - return ty.super_visit_with(self); - } - ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => { - bug!("unexpected type during structural-match checking: {:?}", ty); - } - ty::Error(_) => { - // We still want to check other types after encountering an error, - // as this may still emit relevant errors. - return ControlFlow::Continue(()); - } - }; - - if !self.seen.insert(adt_def.did()) { - debug!("Search already seen adt_def: {:?}", adt_def); - return ControlFlow::Continue(()); - } - - if !self.type_marked_structural(ty) { - debug!("Search found ty: {:?}", ty); - return ControlFlow::Break(ty); - } - - // structural-match does not care about the - // instantiation of the generics in an ADT (it - // instead looks directly at its fields outside - // this match), so we skip super_visit_with. - // - // (Must not recur on args for `PhantomData<T>` cf - // rust-lang/rust#55028 and rust-lang/rust#55837; but also - // want to skip args when only uses of generic are - // behind unsafe pointers `*const T`/`*mut T`.) - - // even though we skip super_visit_with, we must recur on - // fields of ADT. - let tcx = self.tcx; - adt_def.all_fields().map(|field| field.ty(tcx, args)).try_for_each(|field_ty| { - let ty = self.tcx.normalize_erasing_regions(ty::ParamEnv::empty(), field_ty); - debug!("structural-match ADT: field_ty={:?}, ty={:?}", field_ty, ty); - ty.visit_with(self) - }) - } -} diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 951af4b0920..0fb13799e67 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -1,19 +1,20 @@ use std::collections::BTreeMap; -use super::NormalizeExt; -use super::{ObligationCause, PredicateObligation, SelectionContext}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Diag; use rustc_hir::def_id::DefId; use rustc_infer::infer::{InferCtxt, InferOk}; +pub use rustc_infer::traits::util::*; use rustc_middle::bug; -use rustc_middle::ty::GenericArgsRef; -use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt, Upcast}; -use rustc_middle::ty::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::{ + self, GenericArgsRef, ImplSubject, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, Upcast, +}; use rustc_span::Span; use smallvec::{smallvec, SmallVec}; +use tracing::debug; -pub use rustc_infer::traits::util::*; +use super::{NormalizeExt, ObligationCause, PredicateObligation, SelectionContext}; /////////////////////////////////////////////////////////////////////////// // `TraitAliasExpander` iterator @@ -131,7 +132,7 @@ impl<'tcx> TraitAliasExpander<'tcx> { let predicates = tcx.explicit_super_predicates_of(trait_ref.def_id()); debug!(?predicates); - let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| { + let items = predicates.skip_binder().iter().rev().filter_map(|(pred, span)| { pred.instantiate_supertrait(tcx, trait_ref) .as_trait_clause() .map(|trait_ref| item.clone_and_push(trait_ref.map_bound(|t| t.trait_ref), *span)) @@ -168,7 +169,7 @@ impl<'tcx> Iterator for TraitAliasExpander<'tcx> { /// Instantiate all bound parameters of the impl subject with the given args, /// returning the resulting subject and all obligations that arise. /// The obligations are closed under normalization. -pub fn impl_subject_and_oblig<'a, 'tcx>( +pub(crate) fn impl_subject_and_oblig<'a, 'tcx>( selcx: &SelectionContext<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, impl_def_id: DefId, @@ -208,7 +209,7 @@ pub fn upcast_choices<'tcx>( supertraits(tcx, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect() } -pub fn closure_trait_ref_and_return_type<'tcx>( +pub(crate) fn closure_trait_ref_and_return_type<'tcx>( tcx: TyCtxt<'tcx>, fn_trait_def_id: DefId, self_ty: Ty<'tcx>, @@ -237,7 +238,7 @@ pub fn closure_trait_ref_and_return_type<'tcx>( sig.map_bound(|sig| (trait_ref, sig.output())) } -pub fn coroutine_trait_ref_and_outputs<'tcx>( +pub(crate) fn coroutine_trait_ref_and_outputs<'tcx>( tcx: TyCtxt<'tcx>, fn_trait_def_id: DefId, self_ty: Ty<'tcx>, @@ -248,7 +249,7 @@ pub fn coroutine_trait_ref_and_outputs<'tcx>( (trait_ref, sig.yield_ty, sig.return_ty) } -pub fn future_trait_ref_and_outputs<'tcx>( +pub(crate) fn future_trait_ref_and_outputs<'tcx>( tcx: TyCtxt<'tcx>, fn_trait_def_id: DefId, self_ty: Ty<'tcx>, @@ -259,7 +260,7 @@ pub fn future_trait_ref_and_outputs<'tcx>( (trait_ref, sig.return_ty) } -pub fn iterator_trait_ref_and_outputs<'tcx>( +pub(crate) fn iterator_trait_ref_and_outputs<'tcx>( tcx: TyCtxt<'tcx>, iterator_def_id: DefId, self_ty: Ty<'tcx>, @@ -270,7 +271,7 @@ pub fn iterator_trait_ref_and_outputs<'tcx>( (trait_ref, sig.yield_ty) } -pub fn async_iterator_trait_ref_and_outputs<'tcx>( +pub(crate) fn async_iterator_trait_ref_and_outputs<'tcx>( tcx: TyCtxt<'tcx>, async_iterator_def_id: DefId, self_ty: Ty<'tcx>, @@ -286,7 +287,7 @@ pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool { && tcx.defaultness(assoc_item.container_id(tcx)).is_final() } -pub enum TupleArgumentsFlag { +pub(crate) enum TupleArgumentsFlag { Yes, No, } diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 8f56f9c0f3e..f525c17e053 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -1,16 +1,19 @@ -use crate::errors::DumpVTableEntries; -use crate::traits::{impossible_predicates, is_vtable_safe_method}; +use std::fmt::Debug; +use std::ops::ControlFlow; + use rustc_hir::def_id::DefId; use rustc_infer::traits::util::PredicateSet; use rustc_middle::bug; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, GenericParamDefKind, Ty, TyCtxt, Upcast, VtblEntry}; -use rustc_middle::ty::{GenericArgs, TypeVisitableExt}; +use rustc_middle::ty::{ + self, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, Upcast, VtblEntry, +}; use rustc_span::{sym, Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; +use tracing::debug; -use std::fmt::Debug; -use std::ops::ControlFlow; +use crate::errors::DumpVTableEntries; +use crate::traits::{impossible_predicates, is_vtable_safe_method}; #[derive(Clone, Debug)] pub enum VtblSegment<'tcx> { @@ -118,8 +121,7 @@ fn prepare_vtable_segments_inner<'tcx, T>( let mut direct_super_traits_iter = tcx .explicit_super_predicates_of(inner_most_trait_ref.def_id()) - .predicates - .into_iter() + .iter_identity_copied() .filter_map(move |(pred, _)| { pred.instantiate_supertrait(tcx, inner_most_trait_ref).as_trait_clause() }); @@ -364,7 +366,9 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe } /// Given a `dyn Subtrait` and `dyn Supertrait` trait object, find the slot of -/// // the trait vptr in the subtrait's vtable. +/// the trait vptr in the subtrait's vtable. +/// +/// A return value of `None` means that the original vtable can be reused. pub(crate) fn supertrait_vtable_slot<'tcx>( tcx: TyCtxt<'tcx>, key: ( @@ -373,20 +377,22 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( ), ) -> Option<usize> { debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param()); - let (source, target) = key; - let ty::Dynamic(source, _, _) = *source.kind() else { + + // If the target principal is `None`, we can just return `None`. + let ty::Dynamic(target, _, _) = *target.kind() else { bug!(); }; - let source_principal = tcx - .normalize_erasing_regions(ty::ParamEnv::reveal_all(), source.principal().unwrap()) + let target_principal = tcx + .normalize_erasing_regions(ty::ParamEnv::reveal_all(), target.principal()?) .with_self_ty(tcx, tcx.types.trait_object_dummy_self); - let ty::Dynamic(target, _, _) = *target.kind() else { + // Given that we have a target principal, it is a bug for there not to be a source principal. + let ty::Dynamic(source, _, _) = *source.kind() else { bug!(); }; - let target_principal = tcx - .normalize_erasing_regions(ty::ParamEnv::reveal_all(), target.principal().unwrap()) + let source_principal = tcx + .normalize_erasing_regions(ty::ParamEnv::reveal_all(), source.principal().unwrap()) .with_self_ty(tcx, tcx.types.trait_object_dummy_self); let vtable_segment_callback = { diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index f071dc6c784..889e4ed7fcc 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1,17 +1,19 @@ -use crate::infer::InferCtxt; -use crate::traits; +use std::iter; + use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_infer::traits::ObligationCauseCode; use rustc_middle::bug; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + self, GenericArg, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, }; -use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_span::{Span, DUMMY_SP}; +use tracing::{debug, instrument, trace}; -use std::iter; +use crate::infer::InferCtxt; +use crate::traits; /// Returns the set of obligations needed to make `arg` well-formed. /// If `arg` contains unresolved inference variables, this may include /// further WF obligations. However, if `arg` IS an unresolved @@ -672,9 +674,21 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { self.require_sized(subty, ObligationCauseCode::SliceOrArrayElem); } - ty::Array(subty, _) => { + ty::Array(subty, len) => { self.require_sized(subty, ObligationCauseCode::SliceOrArrayElem); - // Note that we handle the len is implicitly checked while walking `arg`. + // Note that the len being WF is implicitly checked while visiting. + // Here we just check that it's of type usize. + let cause = self.cause(ObligationCauseCode::Misc); + self.out.push(traits::Obligation::with_depth( + tcx, + cause, + self.recursion_depth, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType( + len, + tcx.types.usize, + ))), + )); } ty::Pat(subty, _) => { @@ -799,7 +813,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { return upvars.visit_with(self); } - ty::FnPtr(_) => { + ty::FnPtr(..) => { // Let the visitor iterate into the argument/return // types appearing in the fn signature. } |
