diff options
Diffstat (limited to 'compiler/rustc_trait_selection/src/traits/error_reporting')
5 files changed, 910 insertions, 302 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs index 5bc5a12a8..b246e476b 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs @@ -1,5 +1,5 @@ use rustc_hir::def_id::DefId; -use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime}; +use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt}; use rustc_infer::traits::util::elaborate; use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation}; use rustc_middle::ty; @@ -53,7 +53,7 @@ pub fn recompute_applicable_impls<'tcx>( let param_env_predicate = infcx.instantiate_binder_with_fresh_vars( DUMMY_SP, - LateBoundRegionConversionTime::HigherRankedType, + BoundRegionConversionTime::HigherRankedType, poly_trait_predicate, ); let param_env_trait_ref = diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs index b4835b011..0190d5ab4 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs @@ -61,8 +61,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { .params .iter() .map(|arg| { - if let hir::Pat { kind: hir::PatKind::Tuple(ref args, _), span, .. } = - *arg.pat + if let hir::Pat { kind: hir::PatKind::Tuple(args, _), span, .. } = *arg.pat { Some(ArgKind::Tuple( Some(span), @@ -92,7 +91,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { .inputs .iter() .map(|arg| match arg.kind { - hir::TyKind::Tup(ref tys) => ArgKind::Tuple( + hir::TyKind::Tup(tys) => ArgKind::Tuple( Some(arg.span), vec![("_".to_owned(), "_".to_owned()); tys.len()], ), @@ -100,7 +99,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { }) .collect::<Vec<ArgKind>>(), ), - Node::Ctor(ref variant_data) => { + 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()]) } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 5fbfedd3e..61e97dde5 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -103,7 +103,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { /// to be the enclosing (async) block/function/closure fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> { let hir = self.tcx.hir(); - let node = hir.find(hir_id)?; + let node = self.tcx.opt_hir_node(hir_id)?; match &node { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => { self.describe_coroutine(*body_id).or_else(|| { @@ -184,18 +184,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { flags.push((sym::cause, Some("MainFunctionType".to_string()))); } - if let Some(kind) = self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id) - && let ty::Tuple(args) = trait_ref.args.type_at(1).kind() - { - let args = args - .iter() - .map(|ty| ty.to_string()) - .collect::<Vec<_>>() - .join(", "); - flags.push((sym::Trait, Some(format!("{}({args})", kind.as_str())))); - } else { - flags.push((sym::Trait, Some(trait_ref.print_only_trait_path().to_string()))); - } + flags.push((sym::Trait, Some(trait_ref.print_trait_sugared().to_string()))); // Add all types without trimmed paths or visible paths, ensuring they end up with // their "canonical" def path. @@ -325,7 +314,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } #[derive(Clone, Debug)] -pub struct OnUnimplementedFormatString(Symbol); +pub struct OnUnimplementedFormatString { + symbol: Symbol, + span: Span, + is_diagnostic_namespace_variant: bool, +} #[derive(Debug)] pub struct OnUnimplementedDirective { @@ -354,7 +347,7 @@ pub struct OnUnimplementedNote { pub enum AppendConstMessage { #[default] Default, - Custom(Symbol), + Custom(Symbol, Span), } #[derive(LintDiagnostic)] @@ -376,6 +369,43 @@ impl MalformedOnUnimplementedAttrLint { #[help] pub struct MissingOptionsForOnUnimplementedAttr; +#[derive(LintDiagnostic)] +#[diag(trait_selection_ignored_diagnostic_option)] +pub struct IgnoredDiagnosticOption { + pub option_name: &'static str, + #[label] + pub span: Span, + #[label(trait_selection_other_label)] + pub prev_span: Span, +} + +impl IgnoredDiagnosticOption { + fn maybe_emit_warning<'tcx>( + tcx: TyCtxt<'tcx>, + item_def_id: DefId, + new: Option<Span>, + old: Option<Span>, + option_name: &'static str, + ) { + if let (Some(new_item), Some(old_item)) = (new, old) { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + new_item, + IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name }, + ); + } + } +} + +#[derive(LintDiagnostic)] +#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)] +#[help] +pub struct UnknownFormatParameterForOnUnimplementedAttr { + argument_name: Symbol, + trait_name: Symbol, +} + impl<'tcx> OnUnimplementedDirective { fn parse( tcx: TyCtxt<'tcx>, @@ -388,8 +418,15 @@ impl<'tcx> OnUnimplementedDirective { let mut errored = None; let mut item_iter = items.iter(); - let parse_value = |value_str| { - OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span).map(Some) + let parse_value = |value_str, value_span| { + OnUnimplementedFormatString::try_parse( + tcx, + item_def_id, + value_str, + value_span, + is_diagnostic_namespace_variant, + ) + .map(Some) }; let condition = if is_root { @@ -402,7 +439,7 @@ impl<'tcx> OnUnimplementedDirective { .ok_or_else(|| tcx.sess.emit_err(InvalidOnClauseInOnUnimplemented { span }))?; attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| { if let Some(value) = cfg.value - && let Err(guar) = parse_value(value) + && let Err(guar) = parse_value(value, cfg.span) { errored = Some(guar); } @@ -421,17 +458,17 @@ impl<'tcx> OnUnimplementedDirective { for item in item_iter { if item.has_name(sym::message) && message.is_none() { if let Some(message_) = item.value_str() { - message = parse_value(message_)?; + message = parse_value(message_, item.span())?; continue; } } else if item.has_name(sym::label) && label.is_none() { if let Some(label_) = item.value_str() { - label = parse_value(label_)?; + label = parse_value(label_, item.span())?; continue; } } else if item.has_name(sym::note) { if let Some(note_) = item.value_str() { - if let Some(note) = parse_value(note_)? { + if let Some(note) = parse_value(note_, item.span())? { notes.push(note); continue; } @@ -441,7 +478,7 @@ impl<'tcx> OnUnimplementedDirective { && !is_diagnostic_namespace_variant { if let Some(parent_label_) = item.value_str() { - parent_label = parse_value(parent_label_)?; + parent_label = parse_value(parent_label_, item.span())?; continue; } } else if item.has_name(sym::on) @@ -456,7 +493,7 @@ impl<'tcx> OnUnimplementedDirective { match Self::parse( tcx, item_def_id, - &items, + items, item.span(), false, is_diagnostic_namespace_variant, @@ -474,7 +511,7 @@ impl<'tcx> OnUnimplementedDirective { && !is_diagnostic_namespace_variant { if let Some(msg) = item.value_str() { - append_const_msg = Some(AppendConstMessage::Custom(msg)); + append_const_msg = Some(AppendConstMessage::Custom(msg, item.span())); continue; } else if item.is_word() { append_const_msg = Some(AppendConstMessage::Default); @@ -485,7 +522,7 @@ impl<'tcx> OnUnimplementedDirective { if is_diagnostic_namespace_variant { tcx.emit_spanned_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), vec![item.span()], MalformedOnUnimplementedAttrLint::new(item.span()), ); @@ -523,6 +560,54 @@ impl<'tcx> OnUnimplementedDirective { subcommands.extend(directive.subcommands); let mut notes = aggr.notes; notes.extend(directive.notes); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.message.as_ref().map(|f| f.span), + aggr.message.as_ref().map(|f| f.span), + "message", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.label.as_ref().map(|f| f.span), + aggr.label.as_ref().map(|f| f.span), + "label", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.condition.as_ref().map(|i| i.span), + aggr.condition.as_ref().map(|i| i.span), + "condition", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.parent_label.as_ref().map(|f| f.span), + aggr.parent_label.as_ref().map(|f| f.span), + "parent_label", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.append_const_msg.as_ref().and_then(|c| { + if let AppendConstMessage::Custom(_, s) = c { + Some(*s) + } else { + None + } + }), + aggr.append_const_msg.as_ref().and_then(|c| { + if let AppendConstMessage::Custom(_, s) = c { + Some(*s) + } else { + None + } + }), + "append_const_msg", + ); + Ok(Some(Self { condition: aggr.condition.or(directive.condition), subcommands, @@ -560,6 +645,7 @@ impl<'tcx> OnUnimplementedDirective { item_def_id, value, attr.span, + is_diagnostic_namespace_variant, )?), notes: Vec::new(), parent_label: None, @@ -576,7 +662,7 @@ impl<'tcx> OnUnimplementedDirective { tcx.emit_spanned_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), report_span, MalformedOnUnimplementedAttrLint::new(report_span), ); @@ -587,14 +673,14 @@ impl<'tcx> OnUnimplementedDirective { AttrKind::Normal(p) if !matches!(p.item.args, AttrArgs::Empty) => { tcx.emit_spanned_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), attr.span, MalformedOnUnimplementedAttrLint::new(attr.span), ); } _ => tcx.emit_spanned_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), attr.span, MissingOptionsForOnUnimplementedAttr, ), @@ -602,8 +688,9 @@ impl<'tcx> OnUnimplementedDirective { Ok(None) } else { - let reported = - tcx.sess.delay_span_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str"); + let reported = tcx + .sess + .span_delayed_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str"); return Err(reported); }; debug!("of_item({:?}) = {:?}", item_def_id, result); @@ -636,7 +723,18 @@ impl<'tcx> OnUnimplementedDirective { let value = cfg.value.map(|v| { // `with_no_visible_paths` is also used when generating the options, // so we need to match it here. - ty::print::with_no_visible_paths!(OnUnimplementedFormatString(v).format(tcx, trait_ref, &options_map)) + ty::print::with_no_visible_paths!( + OnUnimplementedFormatString { + symbol: v, + span: cfg.span, + is_diagnostic_namespace_variant: false + } + .format( + tcx, + trait_ref, + &options_map + ) + ) }); options.contains(&(cfg.name, value)) @@ -679,19 +777,19 @@ impl<'tcx> OnUnimplementedFormatString { tcx: TyCtxt<'tcx>, item_def_id: DefId, from: Symbol, - err_sp: Span, + value_span: Span, + is_diagnostic_namespace_variant: bool, ) -> Result<Self, ErrorGuaranteed> { - let result = OnUnimplementedFormatString(from); - result.verify(tcx, item_def_id, err_sp)?; + let result = OnUnimplementedFormatString { + symbol: from, + span: value_span, + is_diagnostic_namespace_variant, + }; + result.verify(tcx, item_def_id)?; Ok(result) } - fn verify( - &self, - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - span: Span, - ) -> Result<(), ErrorGuaranteed> { + fn verify(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<(), ErrorGuaranteed> { let trait_def_id = if tcx.is_trait(item_def_id) { item_def_id } else { @@ -700,7 +798,7 @@ impl<'tcx> OnUnimplementedFormatString { }; let trait_name = tcx.item_name(trait_def_id); let generics = tcx.generics_of(item_def_id); - let s = self.0.as_str(); + let s = self.symbol.as_str(); let parser = Parser::new(s, None, None, false, ParseMode::Format); let mut result = Ok(()); for token in parser { @@ -710,24 +808,40 @@ impl<'tcx> OnUnimplementedFormatString { Position::ArgumentNamed(s) => { match Symbol::intern(s) { // `{ThisTraitsName}` is allowed - s if s == trait_name => (), - s if ALLOWED_FORMAT_SYMBOLS.contains(&s) => (), + s if s == trait_name && !self.is_diagnostic_namespace_variant => (), + s if ALLOWED_FORMAT_SYMBOLS.contains(&s) + && !self.is_diagnostic_namespace_variant => + { + () + } // So is `{A}` if A is a type parameter s if generics.params.iter().any(|param| param.name == s) => (), s => { - result = Err(struct_span_err!( - tcx.sess, - span, - E0230, - "there is no parameter `{}` on {}", - s, - if trait_def_id == item_def_id { - format!("trait `{trait_name}`") - } else { - "impl".to_string() - } - ) - .emit()); + if self.is_diagnostic_namespace_variant { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + self.span, + UnknownFormatParameterForOnUnimplementedAttr { + argument_name: s, + trait_name, + }, + ); + } else { + result = Err(struct_span_err!( + tcx.sess, + self.span, + E0230, + "there is no parameter `{}` on {}", + s, + if trait_def_id == item_def_id { + format!("trait `{trait_name}`") + } else { + "impl".to_string() + } + ) + .emit()); + } } } } @@ -735,7 +849,7 @@ impl<'tcx> OnUnimplementedFormatString { Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => { let reported = struct_span_err!( tcx.sess, - span, + self.span, E0231, "only named substitution parameters are allowed" ) @@ -774,37 +888,42 @@ impl<'tcx> OnUnimplementedFormatString { .collect::<FxHashMap<Symbol, String>>(); let empty_string = String::new(); - let s = self.0.as_str(); + let s = self.symbol.as_str(); let parser = Parser::new(s, None, None, false, ParseMode::Format); let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string); parser .map(|p| match p { - Piece::String(s) => s, + Piece::String(s) => s.to_owned(), Piece::NextArgument(a) => match a.position { - Position::ArgumentNamed(s) => { - let s = Symbol::intern(s); + Position::ArgumentNamed(arg) => { + let s = Symbol::intern(arg); match generic_map.get(&s) { - Some(val) => val, - None if s == name => &trait_str, + Some(val) => val.to_string(), + None if self.is_diagnostic_namespace_variant => { + format!("{{{arg}}}") + } + None if s == name => trait_str.clone(), None => { if let Some(val) = options.get(&s) { - val + val.clone() } else if s == sym::from_desugaring { // don't break messages using these two arguments incorrectly - &empty_string - } else if s == sym::ItemContext { - &item_context + String::new() + } else if s == sym::ItemContext + && !self.is_diagnostic_namespace_variant + { + item_context.clone() } else if s == sym::integral { - "{integral}" + String::from("{integral}") } else if s == sym::integer_ { - "{integer}" + String::from("{integer}") } else if s == sym::float { - "{float}" + String::from("{float}") } else { bug!( "broken on_unimplemented {:?} for {:?}: \ no argument matching {:?}", - self.0, + self.symbol, trait_ref, s ) @@ -812,7 +931,7 @@ impl<'tcx> OnUnimplementedFormatString { } } } - _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0), + _ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol), }, }) .collect() diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 6b09bc898..a1b896d22 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -17,7 +17,7 @@ use rustc_errors::{ ErrorGuaranteed, MultiSpan, Style, SuggestionStyle, }; use rustc_hir as hir; -use rustc_hir::def::DefKind; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::is_range_literal; @@ -26,7 +26,7 @@ use rustc_hir::{CoroutineKind, CoroutineSource, Node}; use rustc_hir::{Expr, HirId}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{DefineOpaqueTypes, InferOk, LateBoundRegionConversionTime}; +use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk}; use rustc_middle::hir::map; use rustc_middle::traits::IsConstable; use rustc_middle::ty::error::TypeError::{self, Sorts}; @@ -36,7 +36,7 @@ use rustc_middle::ty::{ TypeSuperFoldable, TypeVisitableExt, TypeckResults, }; use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::{sym, Ident, Symbol}; +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::borrow::Cow; @@ -99,7 +99,7 @@ impl<'tcx, 'a> CoroutineData<'tcx, 'a> { .awaits .into_iter() .map(|id| hir.expect_expr(id)) - .find(|await_expr| ty_matches(ty::Binder::dummy(self.0.expr_ty_adjusted(&await_expr)))) + .find(|await_expr| ty_matches(ty::Binder::dummy(self.0.expr_ty_adjusted(await_expr)))) .map(|expr| expr.span) } } @@ -222,6 +222,15 @@ pub trait TypeErrCtxtExt<'tcx> { param_env: ty::ParamEnv<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; + fn note_conflicting_fn_args( + &self, + err: &mut Diagnostic, + cause: &ObligationCauseCode<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ); + fn note_conflicting_closure_bounds( &self, cause: &ObligationCauseCode<'tcx>, @@ -501,7 +510,7 @@ pub fn suggest_restriction<'tcx>( impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn suggest_restricting_param_bound( &self, - mut err: &mut Diagnostic, + err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, associated_ty: Option<(&'static str, Ty<'tcx>)>, mut body_id: LocalDefId, @@ -521,7 +530,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we // don't suggest `T: Sized + ?Sized`. - while let Some(node) = self.tcx.hir().find_by_def_id(body_id) { + while let Some(node) = self.tcx.opt_hir_node_by_def_id(body_id) { match node { hir::Node::Item(hir::Item { ident, @@ -533,7 +542,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { suggest_restriction( self.tcx, body_id, - &generics, + generics, "`Self`", err, None, @@ -552,7 +561,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { assert!(param_ty); // Restricting `Self` for a single method. suggest_restriction( - self.tcx, body_id, &generics, "`Self`", err, None, projection, trait_pred, + self.tcx, body_id, generics, "`Self`", err, None, projection, trait_pred, None, ); return; @@ -575,7 +584,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { suggest_restriction( self.tcx, body_id, - &generics, + generics, "the associated type", err, Some(fn_sig), @@ -595,7 +604,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { suggest_restriction( self.tcx, body_id, - &generics, + generics, "the associated type", err, None, @@ -662,7 +671,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if suggest_constraining_type_param( self.tcx, generics, - &mut err, + err, ¶m_name, &constraint, Some(trait_pred.def_id()), @@ -690,7 +699,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if suggest_arbitrary_trait_bound( self.tcx, generics, - &mut err, + err, trait_pred, associated_ty, ) { @@ -723,7 +732,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let Some(typeck_results) = &self.typeck_results else { return false; }; - let hir::Node::Expr(expr) = self.tcx.hir().get(*arg_hir_id) else { + let hir::Node::Expr(expr) = self.tcx.hir_node(*arg_hir_id) else { return false; }; let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr) else { @@ -739,9 +748,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { real_trait_pred = parent_trait_pred; } - // We `erase_late_bound_regions` here because `make_subregion` does not handle - // `ReLateBound`, and we don't particularly care about the regions. - let real_ty = self.tcx.erase_late_bound_regions(real_trait_pred.self_ty()); + // We `instantiate_bound_regions_with_erased` here because `make_subregion` does not handle + // `ReBound`, and we don't particularly care about the regions. + let real_ty = self.tcx.instantiate_bound_regions_with_erased(real_trait_pred.self_ty()); if !self.can_eq(obligation.param_env, real_ty, arg_ty) { continue; } @@ -776,7 +785,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { kind: hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr), .. - })) = self.tcx.hir().find(*arg_hir_id) + })) = self.tcx.opt_hir_node(*arg_hir_id) { let derefs = "*".repeat(steps); err.span_suggestion_verbose( @@ -812,7 +821,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if self.predicate_may_hold(&obligation) && self.predicate_must_hold_modulo_regions(&sized_obligation) { - let call_node = self.tcx.hir().get(*call_hir_id); + let call_node = self.tcx.hir_node(*call_hir_id); let msg = "consider dereferencing here"; let is_receiver = matches!( call_node, @@ -871,7 +880,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { }; let hir = self.tcx.hir(); - let hir_id = hir.local_def_id_to_hir_id(def_id.as_local()?); + let hir_id = self.tcx.local_def_id_to_hir_id(def_id.as_local()?); match hir.find_parent(hir_id) { Some(hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local), .. })) => { get_name(err, &local.pat.kind) @@ -908,7 +917,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let self_ty = self.instantiate_binder_with_fresh_vars( DUMMY_SP, - LateBoundRegionConversionTime::FnCall, + BoundRegionConversionTime::FnCall, trait_pred.self_ty(), ); @@ -1034,10 +1043,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else { return; }; - let hir::def::Res::Local(hir_id) = path.res else { + let Res::Local(hir_id) = path.res else { return; }; - let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(hir_id) else { + let Some(hir::Node::Pat(pat)) = self.tcx.opt_hir_node(hir_id) else { return; }; let Some(hir::Node::Local(hir::Local { ty: None, init: Some(init), .. })) = @@ -1097,7 +1106,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { else { return false; }; - let arg_node = self.tcx.hir().get(*arg_hir_id); + let arg_node = self.tcx.hir_node(*arg_hir_id); let Node::Expr(Expr { kind: hir::ExprKind::Path(_), .. }) = arg_node else { return false }; let clone_trait = self.tcx.require_lang_item(LangItem::Clone, None); @@ -1237,7 +1246,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let output = self.instantiate_binder_with_fresh_vars( DUMMY_SP, - LateBoundRegionConversionTime::FnCall, + BoundRegionConversionTime::FnCall, output, ); let inputs = inputs @@ -1246,7 +1255,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .map(|ty| { self.instantiate_binder_with_fresh_vars( DUMMY_SP, - LateBoundRegionConversionTime::FnCall, + BoundRegionConversionTime::FnCall, inputs.rebind(*ty), ) }) @@ -1273,7 +1282,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let code = if let ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } = obligation.cause.code() { - &parent_code + parent_code } else if let ObligationCauseCode::ItemObligation(_) | ObligationCauseCode::ExprItemObligation(..) = obligation.cause.code() { @@ -1378,14 +1387,21 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.message = vec![(rustc_errors::DiagnosticMessage::from(msg), Style::NoStyle)]; } + let mut file = None; err.span_label( span, format!( "the trait `{}` is not implemented for `{}`", old_pred.print_modifiers_and_trait_path(), - old_pred.self_ty().skip_binder(), + self.tcx.short_ty_string(old_pred.self_ty().skip_binder(), &mut file), ), ); + if let Some(file) = file { + err.note(format!( + "the full type name has been written to '{}'", + file.display() + )); + } if imm_ref_self_ty_satisfies_pred && mut_ref_self_ty_satisfies_pred { err.span_suggestions( @@ -1618,8 +1634,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind - && let hir::def::Res::Local(hir_id) = path.res - && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(hir_id) + && let Res::Local(hir_id) = path.res + && let Some(hir::Node::Pat(binding)) = self.tcx.opt_hir_node(hir_id) && let Some(hir::Node::Local(local)) = self.tcx.hir().find_parent(binding.hir_id) && let None = local.ty && let Some(binding_expr) = local.init @@ -1634,9 +1650,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) { let hir = self.tcx.hir(); - if let ObligationCauseCode::AwaitableExpr(Some(hir_id)) = - obligation.cause.code().peel_derives() - && let hir::Node::Expr(expr) = hir.get(*hir_id) + if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives() + && let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id) { // FIXME: use `obligation.predicate.kind()...trait_ref.self_ty()` to see if we have `()` // and if not maybe suggest doing something else? If we kept the expression around we @@ -1786,7 +1801,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { let hir = self.tcx.hir(); - let node = hir.find_by_def_id(obligation.cause.body_id); + let node = self.tcx.opt_hir_node_by_def_id(obligation.cause.body_id); if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. })) = node && let hir::ExprKind::Block(blk, _) = &hir.body(*body_id).value.kind && sig.decl.output.span().overlaps(span) @@ -1821,9 +1836,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> { - let hir = self.tcx.hir(); let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. })) = - hir.find_by_def_id(obligation.cause.body_id) + self.tcx.opt_hir_node_by_def_id(obligation.cause.body_id) else { return None; }; @@ -1867,7 +1881,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let body = self.tcx.hir().body(self.tcx.hir().body_owned_by(obligation.cause.body_id)); let mut visitor = ReturnsVisitor::default(); - visitor.visit_body(&body); + visitor.visit_body(body); let mut sugg = vec![(span.shrink_to_lo(), "Box<".to_string()), (span.shrink_to_hi(), ">".to_string())]; @@ -1915,14 +1929,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } let hir = self.tcx.hir(); - let node = hir.find_by_def_id(obligation.cause.body_id); + let node = self.tcx.opt_hir_node_by_def_id(obligation.cause.body_id); if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) = node { let body = hir.body(*body_id); // Point at all the `return`s in the function as they have failed trait bounds. let mut visitor = ReturnsVisitor::default(); - visitor.visit_body(&body); + visitor.visit_body(body); let typeck_results = self.typeck_results.as_ref().unwrap(); for expr in &visitor.returns { if let Some(returned_ty) = typeck_results.node_type_opt(expr.hir_id) { @@ -2006,6 +2020,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let signature_kind = format!("{argument_kind} signature"); err.note_expected_found(&signature_kind, expected_str, &signature_kind, found_str); + self.note_conflicting_fn_args(&mut err, cause, expected, found, param_env); self.note_conflicting_closure_bounds(cause, &mut err); if let Some(found_node) = found_node { @@ -2015,6 +2030,158 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err } + fn note_conflicting_fn_args( + &self, + err: &mut Diagnostic, + cause: &ObligationCauseCode<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) { + let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = cause else { + return; + }; + let ty::FnPtr(expected) = expected.kind() else { + return; + }; + let ty::FnPtr(found) = found.kind() else { + return; + }; + let Some(Node::Expr(arg)) = self.tcx.opt_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 both_tys = expected_inputs.iter().copied().zip(found_inputs.iter().copied()); + + let arg_expr = |infcx: &InferCtxt<'tcx>, name, expected: Ty<'tcx>, found: Ty<'tcx>| { + let (expected_ty, expected_refs) = get_deref_type_and_refs(expected); + let (found_ty, found_refs) = get_deref_type_and_refs(found); + + if infcx.can_eq(param_env, found_ty, expected_ty) { + if found_refs.len() == expected_refs.len() + && found_refs.iter().eq(expected_refs.iter()) + { + name + } else if found_refs.len() > expected_refs.len() { + let refs = &found_refs[..found_refs.len() - expected_refs.len()]; + if found_refs[..expected_refs.len()].iter().eq(expected_refs.iter()) { + format!( + "{}{name}", + refs.iter() + .map(|mutbl| format!("&{}", mutbl.prefix_str())) + .collect::<Vec<_>>() + .join(""), + ) + } else { + // The refs have different mutability. + format!( + "{}*{name}", + refs.iter() + .map(|mutbl| format!("&{}", mutbl.prefix_str())) + .collect::<Vec<_>>() + .join(""), + ) + } + } else if expected_refs.len() > found_refs.len() { + format!( + "{}{name}", + (0..(expected_refs.len() - found_refs.len())) + .map(|_| "*") + .collect::<Vec<_>>() + .join(""), + ) + } else { + format!( + "{}{name}", + found_refs + .iter() + .map(|mutbl| format!("&{}", mutbl.prefix_str())) + .chain(found_refs.iter().map(|_| "*".to_string())) + .collect::<Vec<_>>() + .join(""), + ) + } + } else { + format!("/* {found} */") + } + }; + let args_have_same_underlying_type = both_tys.clone().all(|(expected, found)| { + let (expected_ty, _) = get_deref_type_and_refs(expected); + let (found_ty, _) = get_deref_type_and_refs(found); + self.can_eq(param_env, found_ty, expected_ty) + }); + let (closure_names, call_names): (Vec<_>, Vec<_>) = if args_have_same_underlying_type + && !expected_inputs.is_empty() + && expected_inputs.len() == found_inputs.len() + && let Some(typeck) = &self.typeck_results + && let Res::Def(res_kind, fn_def_id) = typeck.qpath_res(&path, *arg_hir_id) + && res_kind.is_fn_like() + { + let closure: Vec<_> = self + .tcx + .fn_arg_names(fn_def_id) + .iter() + .enumerate() + .map(|(i, ident)| { + if ident.name.is_empty() || ident.name == kw::SelfLower { + format!("arg{i}") + } else { + format!("{ident}") + } + }) + .collect(); + let args = closure + .iter() + .zip(both_tys) + .map(|(name, (expected, found))| { + arg_expr(self.infcx, name.to_owned(), expected, found) + }) + .collect(); + (closure, args) + } else { + let closure_args = expected_inputs + .iter() + .enumerate() + .map(|(i, _)| format!("arg{i}")) + .collect::<Vec<_>>(); + let call_args = both_tys + .enumerate() + .map(|(i, (expected, found))| { + arg_expr(self.infcx, format!("arg{i}"), expected, found) + }) + .collect::<Vec<_>>(); + (closure_args, call_args) + }; + let closure_names: Vec<_> = closure_names + .into_iter() + .zip(expected_inputs.iter()) + .map(|(name, ty)| { + format!( + "{name}{}", + if ty.has_infer_types() { + String::new() + } else if ty.references_error() { + ": /* type */".to_string() + } else { + format!(": {ty}") + } + ) + }) + .collect(); + err.multipart_suggestion( + "consider wrapping the function in a closure", + vec![ + (arg.span.shrink_to_lo(), format!("|{}| ", closure_names.join(", "))), + (arg.span.shrink_to_hi(), format!("({})", call_names.join(", "))), + ], + Applicability::MaybeIncorrect, + ); + } + // Add a note if there are two `Fn`-family bounds that have conflicting argument // requirements, which will always cause a closure to have a type error. fn note_conflicting_closure_bounds( @@ -2287,8 +2454,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // represent regions that are part of the suspended // coroutine frame. Bound regions are preserved by // `erase_regions` and so we must also call - // `erase_late_bound_regions`. - let ty_erased = self.tcx.erase_late_bound_regions(ty); + // `instantiate_bound_regions_with_erased`. + let ty_erased = self.tcx.instantiate_bound_regions_with_erased(ty); let ty_erased = self.tcx.erase_regions(ty_erased); let eq = ty_erased == target_ty_erased; debug!(?ty_erased, ?target_ty_erased, ?eq); @@ -2300,7 +2467,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // cycles. If we can't use resolved types because the coroutine comes from another crate, // we still provide a targeted error but without all the relevant spans. let coroutine_data = match &self.typeck_results { - Some(t) if t.hir_owner.to_def_id() == coroutine_did_root => CoroutineData(&t), + Some(t) if t.hir_owner.to_def_id() == coroutine_did_root => CoroutineData(t), _ if coroutine_did.is_local() => { CoroutineData(self.tcx.typeck(coroutine_did.expect_local())) } @@ -2344,7 +2511,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if interior_or_upvar_span.is_none() { interior_or_upvar_span = - coroutine_data.try_get_upvar_span(&self, coroutine_did, ty_matches); + coroutine_data.try_get_upvar_span(self, coroutine_did, ty_matches); } if interior_or_upvar_span.is_none() && !coroutine_did.is_local() { @@ -2415,7 +2582,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .tcx .parent(coroutine_did) .as_local() - .map(|parent_did| hir.local_def_id_to_hir_id(parent_did)) + .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did)) .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) .map(|name| { format!("future returned by `{name}` is not {trait_name}") @@ -2426,11 +2593,28 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { CoroutineKind::Async(CoroutineSource::Closure) => { format!("future created by async closure is not {trait_name}") } + CoroutineKind::AsyncGen(CoroutineSource::Fn) => self + .tcx + .parent(coroutine_did) + .as_local() + .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did)) + .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) + .map(|name| { + format!("async iterator returned by `{name}` is not {trait_name}") + })?, + CoroutineKind::AsyncGen(CoroutineSource::Block) => { + format!("async iterator created by async gen block is not {trait_name}") + } + CoroutineKind::AsyncGen(CoroutineSource::Closure) => { + format!( + "async iterator created by async gen closure is not {trait_name}" + ) + } CoroutineKind::Gen(CoroutineSource::Fn) => self .tcx .parent(coroutine_did) .as_local() - .map(|parent_did| hir.local_def_id_to_hir_id(parent_did)) + .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did)) .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) .map(|name| { format!("iterator returned by `{name}` is not {trait_name}") @@ -2517,7 +2701,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { CoroutineInteriorOrUpvar::Upvar(upvar_span) => { // `Some((ref_ty, is_mut))` if `target_ty` is `&T` or `&mut T` and fails to impl `Send` let non_send = match target_ty.kind() { - ty::Ref(_, ref_ty, mutability) => match self.evaluate_obligation(&obligation) { + ty::Ref(_, ref_ty, mutability) => match self.evaluate_obligation(obligation) { Ok(eval) if !eval.may_apply() => Some((ref_ty, mutability.is_mut())), _ => None, }, @@ -2593,11 +2777,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::MethodReceiver | ObligationCauseCode::ReturnNoExpression | ObligationCauseCode::UnifyReceiver(..) - | ObligationCauseCode::OpaqueType | ObligationCauseCode::MiscObligation | ObligationCauseCode::WellFormed(..) | ObligationCauseCode::MatchImpl(..) - | ObligationCauseCode::ReturnType | ObligationCauseCode::ReturnValue(_) | ObligationCauseCode::BlockTailExpression(..) | ObligationCauseCode::AwaitableExpr(_) @@ -2608,7 +2790,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::BinOp { .. } | ObligationCauseCode::AscribeUserTypeProvePredicate(..) | ObligationCauseCode::DropImpl - | ObligationCauseCode::ConstParam(_) => {} + | ObligationCauseCode::ConstParam(_) + | ObligationCauseCode::ReferenceOutlivesReferent(..) + | ObligationCauseCode::ObjectTypeBound(..) => {} ObligationCauseCode::RustCall => { if let Some(pred) = predicate.to_opt_poly_trait_pred() && Some(pred.def_id()) == self.tcx.lang_items().sized_trait() @@ -2622,19 +2806,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ObligationCauseCode::TupleElem => { err.note("only the last element of a tuple may have a dynamically sized type"); } - ObligationCauseCode::ProjectionWf(data) => { - err.note(format!("required so that the projection `{data}` is well-formed")); - } - ObligationCauseCode::ReferenceOutlivesReferent(ref_ty) => { - err.note(format!( - "required so that reference `{ref_ty}` does not outlive its referent" - )); - } - ObligationCauseCode::ObjectTypeBound(object_ty, region) => { - err.note(format!( - "required so that the lifetime bound of `{region}` for `{object_ty}` is satisfied", - )); - } ObligationCauseCode::ItemObligation(_) | ObligationCauseCode::ExprItemObligation(..) => { // We hold the `DefId` of the item introducing the obligation, but displaying it @@ -2723,11 +2894,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .collect::<Vec<_>>(); if !impls.is_empty() { let len = impls.len(); - let mut types = impls.iter() - .map(|t| with_no_trimmed_paths!(format!( - " {}", - tcx.type_of(*t).instantiate_identity(), - ))) + let mut types = impls + .iter() + .map(|t| { + with_no_trimmed_paths!(format!( + " {}", + tcx.type_of(*t).instantiate_identity(), + )) + }) .collect::<Vec<_>>(); let post = if types.len() > 9 { types.truncate(8); @@ -2749,50 +2923,62 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } ObligationCauseCode::Coercion { source, target } => { - let (source, source_file) = - self.tcx.short_ty_string(self.resolve_vars_if_possible(source)); - let (target, target_file) = - self.tcx.short_ty_string(self.resolve_vars_if_possible(target)); + let mut file = None; + let source = + self.tcx.short_ty_string(self.resolve_vars_if_possible(source), &mut file); + let target = + self.tcx.short_ty_string(self.resolve_vars_if_possible(target), &mut file); err.note(with_forced_trimmed_paths!(format!( "required for the cast from `{source}` to `{target}`", ))); - if let Some(file) = source_file { - err.note(format!( - "the full name for the source type has been written to '{}'", - file.display(), - )); - } - if let Some(file) = target_file { + if let Some(file) = file { err.note(format!( - "the full name for the target type has been written to '{}'", + "the full name for the type has been written to '{}'", file.display(), )); } } - ObligationCauseCode::RepeatElementCopy { is_constable, elt_type, elt_span, elt_stmt_span } => { + ObligationCauseCode::RepeatElementCopy { + is_constable, + elt_type, + elt_span, + elt_stmt_span, + } => { err.note( "the `Copy` trait is required because this value will be copied for each element of the array", ); let value_kind = match is_constable { IsConstable::Fn => Some("the result of the function call"), IsConstable::Ctor => Some("the result of the constructor"), - _ => None + _ => None, }; let sm = tcx.sess.source_map(); - if let Some(value_kind) = value_kind && - let Ok(snip) = sm.span_to_snippet(elt_span) + if let Some(value_kind) = value_kind + && let Ok(snip) = sm.span_to_snippet(elt_span) { let help_msg = format!( "consider creating a new `const` item and initializing it with {value_kind} \ - to be used in the repeat position"); + to be used in the repeat position" + ); let indentation = sm.indentation_before(elt_stmt_span).unwrap_or_default(); - err.multipart_suggestion(help_msg, vec![ - (elt_stmt_span.shrink_to_lo(), format!("const ARRAY_REPEAT_VALUE: {elt_type} = {snip};\n{indentation}")), - (elt_span, "ARRAY_REPEAT_VALUE".to_string()) - ], Applicability::MachineApplicable); + err.multipart_suggestion( + help_msg, + vec![ + ( + elt_stmt_span.shrink_to_lo(), + format!( + "const ARRAY_REPEAT_VALUE: {elt_type} = {snip};\n{indentation}" + ), + ), + (elt_span, "ARRAY_REPEAT_VALUE".to_string()), + ], + Applicability::MachineApplicable, + ); } - if self.tcx.sess.is_nightly_build() && matches!(is_constable, IsConstable::Fn|IsConstable::Ctor) { + if self.tcx.sess.is_nightly_build() + && matches!(is_constable, IsConstable::Fn | IsConstable::Ctor) + { err.help( "create an inline `const` block, see RFC #2920 \ <https://github.com/rust-lang/rfcs/pull/2920> for more information", @@ -2801,7 +2987,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ObligationCauseCode::VariableType(hir_id) => { let parent_node = self.tcx.hir().parent_id(hir_id); - match self.tcx.hir().find(parent_node) { + match self.tcx.opt_hir_node(parent_node) { Some(Node::Local(hir::Local { ty: Some(ty), .. })) => { err.span_suggestion_verbose( ty.span.shrink_to_lo(), @@ -2945,7 +3131,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "all values captured by value by a closure must have a statically known size", ); let hir::ExprKind::Closure(closure) = - self.tcx.hir().get_by_def_id(closure_def_id).expect_expr().kind + self.tcx.hir_node_by_def_id(closure_def_id).expect_expr().kind else { bug!("expected closure in SizedClosureCapture obligation"); }; @@ -2957,16 +3143,16 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ObligationCauseCode::SizedCoroutineInterior(coroutine_def_id) => { let what = match self.tcx.coroutine_kind(coroutine_def_id) { - None | Some(hir::CoroutineKind::Coroutine) | Some(hir::CoroutineKind::Gen(_)) => "yield", + None + | Some(hir::CoroutineKind::Coroutine) + | Some(hir::CoroutineKind::Gen(_)) => "yield", Some(hir::CoroutineKind::Async(..)) => "await", + Some(hir::CoroutineKind::AsyncGen(_)) => "yield`/`await", }; err.note(format!( "all values live across `{what}` must have a statically known size" )); } - ObligationCauseCode::ConstPatternStructural => { - err.note("constants used for pattern-matching must derive `PartialEq` and `Eq`"); - } ObligationCauseCode::SharedStatic => { err.note("shared static variables must have a type that implements `Sync`"); } @@ -3000,9 +3186,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Don't print the tuple of capture types 'print: { if !is_upvar_tys_infer_tuple { - let msg = with_forced_trimmed_paths!(format!( - "required because it appears within the type `{ty}`", - )); + let mut file = None; + let ty_str = self.tcx.short_ty_string(ty, &mut file); + let msg = format!("required because it appears within the type `{ty_str}`"); match ty.kind() { ty::Adt(def, _) => match self.tcx.opt_item_ident(def.did()) { Some(ident) => err.span_note(ident.span, msg), @@ -3099,8 +3285,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut parent_trait_pred = self.resolve_vars_if_possible(data.derived.parent_trait_pred); let parent_def_id = parent_trait_pred.def_id(); - let (self_ty, file) = - self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty()); + let mut file = None; + let self_ty = + self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file); let msg = format!( "required for `{self_ty}` to implement `{}`", parent_trait_pred.print_modifiers_and_trait_path() @@ -3197,8 +3384,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { count, pluralize!(count) )); - let (self_ty, file) = - self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty()); + let mut file = None; + let self_ty = self + .tcx + .short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file); err.note(format!( "required for `{self_ty}` to implement `{}`", parent_trait_pred.print_modifiers_and_trait_path() @@ -3283,7 +3472,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err, predicate, param_env, - &parent_code, + parent_code, obligated_types, seen_requirements, ) @@ -3352,7 +3541,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty()); let impls_future = self.type_implements_trait( future_trait, - [self.tcx.erase_late_bound_regions(self_ty)], + [self.tcx.instantiate_bound_regions_with_erased(self_ty)], obligation.param_env, ); if !impls_future.must_apply_modulo_regions() { @@ -3443,17 +3632,19 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { is_derivable_trait && // Ensure all fields impl the trait. adt.all_fields().all(|field| { - let field_ty = field.ty(self.tcx, args); + let field_ty = ty::GenericArg::from(field.ty(self.tcx, args)); let trait_args = match diagnostic_name { sym::PartialEq | sym::PartialOrd => { Some(field_ty) } _ => None, }; + // Also add host param, if present + let host = self.tcx.generics_of(trait_pred.def_id()).host_effect_index.map(|idx| trait_pred.skip_binder().trait_ref.args[idx]); let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate { trait_ref: ty::TraitRef::new(self.tcx, trait_pred.def_id(), - [field_ty].into_iter().chain(trait_args), + [field_ty].into_iter().chain(trait_args).chain(host), ), ..*tr }); @@ -3474,6 +3665,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { trait_pred.skip_binder().self_ty(), diagnostic_name, ), + // FIXME(effects, const_trait_impl) derive_const as suggestion? format!("#[derive({diagnostic_name})]\n"), Applicability::MaybeIncorrect, ); @@ -3513,13 +3705,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { call_hir_id: HirId, ) { let tcx = self.tcx; - let hir = tcx.hir(); - if let Some(Node::Expr(expr)) = hir.find(arg_hir_id) + if let Some(Node::Expr(expr)) = tcx.opt_hir_node(arg_hir_id) && let Some(typeck_results) = &self.typeck_results { if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr { let inner_expr = expr.peel_blocks(); - let ty = typeck_results.expr_ty_adjusted_opt(inner_expr) + let ty = typeck_results + .expr_ty_adjusted_opt(inner_expr) .unwrap_or(Ty::new_misc_error(tcx)); let span = inner_expr.span; if Some(span) != err.span.primary_span() { @@ -3538,16 +3730,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { tcx.lang_items().fn_once_trait(), tcx.lang_items().fn_mut_trait(), tcx.lang_items().fn_trait(), - ].contains(&Some(pred.def_id())) + ] + .contains(&Some(pred.def_id())) { if let [stmt, ..] = block.stmts && let hir::StmtKind::Semi(value) = stmt.kind && let hir::ExprKind::Closure(hir::Closure { - body, - fn_decl_span, - .. + body, fn_decl_span, .. }) = value.kind - && let body = hir.body(*body) + && let body = tcx.hir().body(*body) && !matches!(body.value.kind, hir::ExprKind::Block(..)) { // Check if the failed predicate was an expectation of a closure type @@ -3568,9 +3759,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "you might have meant to create the closure instead of a block", format!( "|{}| ", - (0..pred.trait_ref.args.len() - 1).map(|_| "_") + (0..pred.trait_ref.args.len() - 1) + .map(|_| "_") .collect::<Vec<_>>() - .join(", ")), + .join(", ") + ), Applicability::MaybeIncorrect, ); } @@ -3595,7 +3788,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let where_pred = self.instantiate_binder_with_placeholders(where_pred); let failed_pred = self.instantiate_binder_with_fresh_vars( expr.span, - LateBoundRegionConversionTime::FnCall, + BoundRegionConversionTime::FnCall, failed_pred, ); @@ -3627,21 +3820,21 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind - && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path - && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(*hir_id) + && let hir::Path { res: Res::Local(hir_id), .. } = path + && let Some(hir::Node::Pat(binding)) = self.tcx.opt_hir_node(*hir_id) && let parent_hir_id = self.tcx.hir().parent_id(binding.hir_id) - && let Some(hir::Node::Local(local)) = self.tcx.hir().find(parent_hir_id) + && let Some(hir::Node::Local(local)) = self.tcx.opt_hir_node(parent_hir_id) && let Some(binding_expr) = local.init { // If the expression we're calling on is a binding, we want to point at the // `let` when talking about the type. Otherwise we'll point at every part // of the method chain with the type. - self.point_at_chain(binding_expr, &typeck_results, type_diffs, param_env, err); + self.point_at_chain(binding_expr, typeck_results, type_diffs, param_env, err); } else { - self.point_at_chain(expr, &typeck_results, type_diffs, param_env, err); + self.point_at_chain(expr, typeck_results, type_diffs, param_env, err); } } - let call_node = hir.find(call_hir_id); + let call_node = tcx.opt_hir_node(call_hir_id); if let Some(Node::Expr(hir::Expr { kind: hir::ExprKind::MethodCall(path, rcvr, ..), .. })) = call_node @@ -3651,7 +3844,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } - if let Some(Node::Expr(expr)) = hir.find(call_hir_id) { + if let Some(Node::Expr(expr)) = tcx.opt_hir_node(call_hir_id) { if let hir::ExprKind::Call(hir::Expr { span, .. }, _) | hir::ExprKind::MethodCall( hir::PathSegment { ident: Ident { span, .. }, .. }, @@ -3812,7 +4005,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { continue; }; let hir = tcx.hir(); - let node = hir.get_by_def_id(hir.get_parent_item(expr.hir_id).def_id); + let node = tcx.hir_node_by_def_id(hir.get_parent_item(expr.hir_id).def_id); let pred = ty::Binder::dummy(ty::TraitPredicate { trait_ref: ty::TraitRef::from_lang_item( @@ -3832,7 +4025,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { suggest_restriction( tcx, hir.body_owner_def_id(body_id), - &generics, + generics, &format!("type parameter `{ty}`"), err, node.fn_sig(), @@ -3887,8 +4080,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind - && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path - && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(*hir_id) + && let hir::Path { res: Res::Local(hir_id), .. } = path + && let Some(hir::Node::Pat(binding)) = self.tcx.opt_hir_node(*hir_id) && let Some(parent) = self.tcx.hir().find_parent(binding.hir_id) { // We've reached the root of the method call chain... @@ -4267,7 +4460,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } return; }; - let Some(hir::Node::TraitItem(item)) = self.tcx.hir().find_by_def_id(fn_def_id) else { + let Some(hir::Node::TraitItem(item)) = self.tcx.opt_hir_node_by_def_id(fn_def_id) else { return; }; @@ -4342,17 +4535,6 @@ fn hint_missing_borrow<'tcx>( let args = fn_decl.inputs.iter(); - fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) { - let mut refs = vec![]; - - while let ty::Ref(_, new_ty, mutbl) = ty.kind() { - ty = *new_ty; - refs.push(*mutbl); - } - - (ty, refs) - } - let mut to_borrow = Vec::new(); let mut remove_borrow = Vec::new(); @@ -4483,7 +4665,7 @@ pub trait NextTypeParamName { impl NextTypeParamName for &[hir::GenericParam<'_>] { fn next_type_param_name(&self, name: Option<&str>) -> String { // This is the list of possible parameter names that we might suggest. - let name = name.and_then(|n| n.chars().next()).map(|c| c.to_string().to_uppercase()); + let name = name.and_then(|n| n.chars().next()).map(|c| c.to_uppercase().to_string()); let name = name.as_deref(); let possible_names = [name.unwrap_or("T"), "T", "U", "V", "X", "Y", "Z", "A", "B", "C"]; let used_names = self @@ -4512,7 +4694,7 @@ impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> { fn visit_ty(&mut self, t: &'hir hir::Ty<'hir>) { if let hir::TyKind::Path(hir::QPath::Resolved( None, - hir::Path { res: hir::def::Res::Def(_, segment_did), .. }, + hir::Path { res: Res::Def(_, segment_did), .. }, )) = t.kind { if self.param_did == *segment_did { @@ -4530,6 +4712,7 @@ impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> { } pub(super) fn get_explanation_based_on_obligation<'tcx>( + tcx: TyCtxt<'tcx>, obligation: &PredicateObligation<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, trait_predicate: &ty::PolyTraitPredicate<'tcx>, @@ -4550,13 +4733,13 @@ pub(super) fn get_explanation_based_on_obligation<'tcx>( pre_message, trait_predicate.print_modifiers_and_trait_path(), desc, - trait_ref.skip_binder().self_ty(), + tcx.short_ty_string(trait_ref.skip_binder().self_ty(), &mut None), ), None => format!( "{}the trait `{}` is not implemented for `{}`", pre_message, trait_predicate.print_modifiers_and_trait_path(), - trait_ref.skip_binder().self_ty(), + tcx.short_ty_string(trait_ref.skip_binder().self_ty(), &mut None), ), } } @@ -4599,9 +4782,15 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( return None; }; - let future = tcx.hir().get_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); - let Some(hir::GenericBound::LangItemTrait(_, _, _, generics)) = future.bounds.get(0) else { - // `async fn` should always lower to a lang item bound... but don't ICE. + let future = tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); + let [hir::GenericBound::Trait(trait_ref, _)] = future.bounds else { + // `async fn` should always lower to a single bound... but don't ICE. + return None; + }; + let Some(hir::PathSegment { args: Some(generics), .. }) = + trait_ref.trait_ref.path.segments.last() + else { + // desugaring to a single path segment for `Future<...>`. return None; }; let Some(hir::TypeBindingKind::Equality { term: hir::Term::Ty(future_output_ty) }) = @@ -4645,3 +4834,14 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( Some(sugg) } + +fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) { + let mut refs = vec![]; + + while let ty::Ref(_, new_ty, mutbl) = ty.kind() { + ty = *new_ty; + refs.push(*mutbl); + } + + (ty, refs) +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index ba2e3d1ae..9ee091bbd 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -3,6 +3,7 @@ use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch}; use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::InferCtxtExt as _; use crate::infer::{self, InferCtxt}; use crate::traits::error_reporting::infer_ctxt_ext::InferCtxtExt; use crate::traits::error_reporting::{ambiguity, ambiguity::Ambiguity::*}; @@ -36,11 +37,11 @@ use rustc_middle::ty::{ self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, }; -use rustc_session::config::{DumpSolverProofTree, TraitSolver}; +use rustc_session::config::DumpSolverProofTree; use rustc_session::Limit; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::sym; -use rustc_span::{ExpnKind, Span, DUMMY_SP}; +use rustc_span::{BytePos, ExpnKind, Span, Symbol, DUMMY_SP}; use std::borrow::Cow; use std::fmt; use std::iter; @@ -98,8 +99,21 @@ pub trait TypeErrCtxtExt<'tcx> { error: &SelectionError<'tcx>, ); + fn emit_specialized_closure_kind_error( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Option<ErrorGuaranteed>; + fn fn_arg_obligation(&self, obligation: &PredicateObligation<'tcx>) -> bool; + fn try_conversion_context( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + err: &mut Diagnostic, + ) -> bool; + fn report_const_param_not_wf( &self, ty: Ty<'tcx>, @@ -212,7 +226,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } - self.tcx.sess.delay_span_bug(DUMMY_SP, "expected fulfillment errors") + self.tcx.sess.span_delayed_bug(DUMMY_SP, "expected fulfillment errors") } /// Reports that an overflow has occurred and halts compilation. We @@ -236,7 +250,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.emit(); self.tcx.sess.abort_if_errors(); - bug!(); + // FIXME: this should be something like `build_overflow_error_fatal`, which returns + // `DiagnosticBuilder<', !>`. Then we don't even need anything after that `emit()`. + unreachable!( + "did not expect compilation to continue after `abort_if_errors`, \ + since an error was definitely emitted!" + ); } fn build_overflow_error<T>( @@ -356,14 +375,16 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) { let tcx = self.tcx; - if tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError { + if tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default() + == DumpSolverProofTree::OnError + { dump_proof_tree(root_obligation, self.infcx); } let mut span = obligation.cause.span; // FIXME: statically guarantee this by tainting after the diagnostic is emitted self.set_tainted_by_errors( - tcx.sess.delay_span_bug(span, "`report_selection_error` did not emit an error"), + tcx.sess.span_delayed_bug(span, "`report_selection_error` did not emit an error"), ); let mut err = match *error { @@ -411,6 +432,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => { let trait_predicate = bound_predicate.rebind(trait_predicate); let trait_predicate = self.resolve_vars_if_possible(trait_predicate); + let trait_ref = trait_predicate.to_poly_trait_ref(); + + if let Some(_guar) = self.emit_specialized_closure_kind_error(&obligation, trait_ref) { + return; + } // FIXME(effects) let predicate_is_const = false; @@ -425,22 +451,22 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // reported on the binding definition (#56607). return; } - let trait_ref = trait_predicate.to_poly_trait_ref(); - let (post_message, pre_message, type_def, file_note) = self + let mut file = None; + let (post_message, pre_message, type_def) = self .get_parent_trait_ref(obligation.cause.code()) .map(|(t, s)| { - let (t, file) = self.tcx.short_ty_string(t); + let t = self.tcx.short_ty_string(t, &mut file); ( format!(" in `{t}`"), format!("within `{t}`, "), s.map(|s| (format!("within this `{t}`"), s)), - file.map(|file| format!( - "the full trait has been written to '{}'", - file.display(), - )) ) }) .unwrap_or_default(); + let file_note = file.map(|file| format!( + "the full trait has been written to '{}'", + file.display(), + )); let OnUnimplementedNote { message, @@ -499,6 +525,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut err = struct_span_err!(self.tcx.sess, span, E0277, "{}", err_msg); + let mut suggested = false; + if is_try_conversion { + suggested = self.try_conversion_context(&obligation, trait_ref.skip_binder(), &mut err); + } + if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) { err.span_label( ret_span, @@ -511,7 +542,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if Some(trait_ref.def_id()) == tcx.lang_items().tuple_trait() { self.add_tuple_trait_message( - &obligation.cause.code().peel_derives(), + obligation.cause.code().peel_derives(), &mut err, ); } @@ -524,6 +555,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } let explanation = get_explanation_based_on_obligation( + self.tcx, &obligation, trait_ref, &trait_predicate, @@ -569,7 +601,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() { self.suggest_borrowing_for_object_cast( &mut err, - &root_obligation, + root_obligation, source, target, ); @@ -599,8 +631,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref); self.suggest_dereferencing_index(&obligation, &mut err, trait_predicate); - let mut suggested = - self.suggest_dereferences(&obligation, &mut err, trait_predicate); + suggested |= self.suggest_dereferences(&obligation, &mut err, trait_predicate); suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate); let impl_candidates = self.find_similar_impl_candidates(trait_predicate); suggested = if let &[cand] = &impl_candidates[..] { @@ -612,7 +643,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { span.shrink_to_hi(), format!( "the trait `{}` is implemented for fn pointer `{}`, try casting using `as`", - cand.print_only_trait_path(), + cand.print_trait_sugared(), cand.self_ty(), ), format!(" as {}", cand.self_ty()), @@ -786,30 +817,22 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { report_object_safety_error(self.tcx, span, trait_def_id, violations) } - ty::PredicateKind::ClosureKind(closure_def_id, closure_args, kind) => { - let found_kind = self.closure_kind(closure_args).unwrap(); - self.report_closure_error(&obligation, closure_def_id, found_kind, kind) - } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty)) => { let ty = self.resolve_vars_if_possible(ty); - match self.tcx.sess.opts.unstable_opts.trait_solver { - TraitSolver::Classic => { - // WF predicates cannot themselves make - // errors. They can only block due to - // ambiguity; otherwise, they always - // degenerate into other obligations - // (which may fail). - span_bug!(span, "WF predicate not satisfied for {:?}", ty); - } - TraitSolver::Next | TraitSolver::NextCoherence => { - // FIXME: we'll need a better message which takes into account - // which bounds actually failed to hold. - self.tcx.sess.struct_span_err( - span, - format!("the type `{ty}` is not well-formed"), - ) - } + if self.next_trait_solver() { + // FIXME: we'll need a better message which takes into account + // which bounds actually failed to hold. + self.tcx.sess.struct_span_err( + span, + format!("the type `{ty}` is not well-formed"), + ) + } else { + // WF predicates cannot themselves make + // errors. They can only block due to + // ambiguity; otherwise, they always + // degenerate into other obligations + // (which may fail). + span_bug!(span, "WF predicate not satisfied for {:?}", ty); } } @@ -837,6 +860,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ty::PredicateKind::Ambiguous => span_bug!(span, "ambiguous"), + ty::PredicateKind::NormalizesTo(..) => span_bug!( + span, + "NormalizesTo predicate should never be the predicate cause of a SelectionError" + ), + ty::PredicateKind::AliasRelate(..) => span_bug!( span, "AliasRelate predicate should never be the predicate cause of a SelectionError" @@ -927,18 +955,48 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.emit(); } + fn emit_specialized_closure_kind_error( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Option<ErrorGuaranteed> { + if let ty::Closure(closure_def_id, closure_args) = *trait_ref.self_ty().skip_binder().kind() + && let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id()) + && let Some(found_kind) = self.closure_kind(closure_args) + && !found_kind.extends(expected_kind) + && let sig = closure_args.as_closure().sig() + && self.can_sub( + obligation.param_env, + trait_ref, + sig.map_bound(|sig| { + ty::TraitRef::new( + self.tcx, + trait_ref.def_id(), + [trait_ref.self_ty().skip_binder(), sig.inputs()[0]], + ) + }), + ) + { + let mut err = + self.report_closure_error(&obligation, closure_def_id, found_kind, expected_kind); + self.note_obligation_cause(&mut err, &obligation); + self.point_at_returns_when_relevant(&mut err, &obligation); + Some(err.emit()) + } else { + None + } + } + fn fn_arg_obligation(&self, obligation: &PredicateObligation<'tcx>) -> bool { - if let ObligationCauseCode::FunctionArgumentObligation { - arg_hir_id, - .. - } = obligation.cause.code() - && let Some(Node::Expr(arg)) = self.tcx.hir().find(*arg_hir_id) + if let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = + obligation.cause.code() + && let Some(Node::Expr(arg)) = self.tcx.opt_hir_node(*arg_hir_id) && let arg = arg.peel_borrows() && let hir::ExprKind::Path(hir::QPath::Resolved( None, hir::Path { res: hir::def::Res::Local(hir_id), .. }, )) = arg.kind - && let Some(Node::Pat(pat)) = self.tcx.hir().find(*hir_id) + && let Some(Node::Pat(pat)) = self.tcx.opt_hir_node(*hir_id) && let Some(preds) = self.reported_trait_errors.borrow().get(&pat.span) && preds.contains(&obligation.predicate) { @@ -947,6 +1005,223 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { false } + /// When the `E` of the resulting `Result<T, E>` in an expression `foo().bar().baz()?`, + /// identify thoe method chain sub-expressions that could or could not have been annotated + /// with `?`. + fn try_conversion_context( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + err: &mut Diagnostic, + ) -> bool { + let span = obligation.cause.span; + struct V<'v> { + search_span: Span, + found: Option<&'v hir::Expr<'v>>, + } + impl<'v> Visitor<'v> for V<'v> { + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + if let hir::ExprKind::Match(expr, _arms, hir::MatchSource::TryDesugar(_)) = ex.kind + { + if ex.span.with_lo(ex.span.hi() - BytePos(1)).source_equal(self.search_span) { + if let hir::ExprKind::Call(_, [expr, ..]) = expr.kind { + self.found = Some(expr); + return; + } + } + } + hir::intravisit::walk_expr(self, ex); + } + } + let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id); + let body_id = match self.tcx.opt_hir_node(hir_id) { + Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) => { + body_id + } + _ => return false, + }; + let mut v = V { search_span: span, found: None }; + v.visit_body(self.tcx.hir().body(*body_id)); + let Some(expr) = v.found else { + return false; + }; + let Some(typeck) = &self.typeck_results else { + return false; + }; + let Some((ObligationCauseCode::QuestionMark, Some(y))) = obligation.cause.code().parent() + else { + return false; + }; + if !self.tcx.is_diagnostic_item(sym::FromResidual, y.def_id()) { + return false; + } + let self_ty = trait_ref.self_ty(); + let found_ty = trait_ref.args.get(1).and_then(|a| a.as_type()); + + let mut prev_ty = self.resolve_vars_if_possible( + typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), + ); + + // We always look at the `E` type, because that's the only one affected by `?`. If the + // incorrect `Result<T, E>` is because of the `T`, we'll get an E0308 on the whole + // expression, after the `?` has "unwrapped" the `T`. + let get_e_type = |prev_ty: Ty<'tcx>| -> Option<Ty<'tcx>> { + let ty::Adt(def, args) = prev_ty.kind() else { + return None; + }; + let Some(arg) = args.get(1) else { + return None; + }; + if !self.tcx.is_diagnostic_item(sym::Result, def.did()) { + return None; + } + arg.as_type() + }; + + let mut suggested = false; + let mut chain = vec![]; + + // The following logic is simlar 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. + // let foo = bar.iter().map(mapper)?; + // ------ ----------- + expr = rcvr_expr; + chain.push((span, prev_ty)); + + let next_ty = self.resolve_vars_if_possible( + typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), + ); + + let is_diagnostic_item = |symbol: Symbol, ty: Ty<'tcx>| { + let ty::Adt(def, _) = ty.kind() else { + return false; + }; + self.tcx.is_diagnostic_item(symbol, def.did()) + }; + // For each method in the chain, see if this is `Result::map_err` or + // `Option::ok_or_else` and if it is, see if the closure passed to it has an incorrect + // trailing `;`. + if let Some(ty) = get_e_type(prev_ty) + && let Some(found_ty) = found_ty + // Ideally we would instead use `FnCtxt::lookup_method_for_diagnostic` for 100% + // accurate check, but we are in the wrong stage to do that and looking for + // `Result::map_err` by checking the Self type and the path segment is enough. + // sym::ok_or_else + && ( + ( // Result::map_err + path_segment.ident.name == sym::map_err + && is_diagnostic_item(sym::Result, next_ty) + ) || ( // Option::ok_or_else + path_segment.ident.name == sym::ok_or_else + && is_diagnostic_item(sym::Option, next_ty) + ) + ) + // Found `Result<_, ()>?` + && let ty::Tuple(tys) = found_ty.kind() + && tys.is_empty() + // 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 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) + && let hir::ExprKind::Block(block, _) = body.value.kind + && let None = block.expr + // The last statement is of a type that can be converted to the return error type + && let [.., stmt] = block.stmts + && let hir::StmtKind::Semi(expr) = stmt.kind + && let expr_ty = self.resolve_vars_if_possible( + typeck.expr_ty_adjusted_opt(expr) + .unwrap_or(Ty::new_misc_error(self.tcx)), + ) + && self + .infcx + .type_implements_trait( + self.tcx.get_diagnostic_item(sym::From).unwrap(), + [self_ty, expr_ty], + obligation.param_env, + ) + .must_apply_modulo_regions() + { + suggested = true; + err.span_suggestion_short( + stmt.span.with_lo(expr.span.hi()), + "remove this semicolon", + String::new(), + Applicability::MachineApplicable, + ); + } + + prev_ty = next_ty; + + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind + && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path + && let Some(hir::Node::Pat(binding)) = self.tcx.opt_hir_node(*hir_id) + && let Some(parent) = self.tcx.hir().find_parent(binding.hir_id) + { + // We've reached the root of the method call chain... + if let hir::Node::Local(local) = parent + && let Some(binding_expr) = local.init + { + // ...and it is a binding. Get the binding creation and continue the chain. + expr = binding_expr; + } + if let hir::Node::Param(_param) = parent { + // ...and it is a an fn argument. + break; + } + } + } + // `expr` is now the "root" expression of the method call chain, which can be any + // expression kind, like a method call or a path. If this expression is `Result<T, E>` as + // well, then we also point at it. + prev_ty = self.resolve_vars_if_possible( + typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), + ); + chain.push((expr.span, prev_ty)); + + let mut prev = None; + for (span, err_ty) in chain.into_iter().rev() { + let err_ty = get_e_type(err_ty); + let err_ty = match (err_ty, prev) { + (Some(err_ty), Some(prev)) if !self.can_eq(obligation.param_env, err_ty, prev) => { + err_ty + } + (Some(err_ty), None) => err_ty, + _ => { + prev = err_ty; + continue; + } + }; + if self + .infcx + .type_implements_trait( + self.tcx.get_diagnostic_item(sym::From).unwrap(), + [self_ty, err_ty], + obligation.param_env, + ) + .must_apply_modulo_regions() + { + if !suggested { + err.span_label(span, format!("this has type `Result<_, {err_ty}>`")); + } + } else { + err.span_label( + span, + format!( + "this can't be annotated with `?` because it has type `Result<_, {err_ty}>`", + ), + ); + } + prev = Some(err_ty); + } + suggested + } + fn report_const_param_not_wf( &self, ty: Ty<'tcx>, @@ -1291,7 +1566,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { #[instrument(skip(self), level = "debug")] fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) { - if self.tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError { + if self.tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default() + == DumpSolverProofTree::OnError + { dump_proof_tree(&error.root_obligation, self.infcx); } @@ -1377,7 +1654,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { { let data = self.instantiate_binder_with_fresh_vars( obligation.cause.span, - infer::LateBoundRegionConversionTime::HigherRankedType, + infer::BoundRegionConversionTime::HigherRankedType, bound_predicate.rebind(data), ); let unnormalized_term = match data.term.unpack() { @@ -1397,7 +1674,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) .into(), }; - // FIXME(-Ztrait-solver=next): For diagnostic purposes, it would be nice + // FIXME(-Znext-solver): For diagnostic purposes, it would be nice // to deeply normalize this type. let normalized_term = ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term); @@ -1413,7 +1690,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::ExprItemObligation(..) | ObligationCauseCode::ExprBindingObligation(..) | ObligationCauseCode::Coercion { .. } - | ObligationCauseCode::OpaqueType ); // constrain inference variables a bit more to nested obligations from normalize so @@ -1653,6 +1929,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { hir::CoroutineKind::Async(hir::CoroutineSource::Block) => "an async block", hir::CoroutineKind::Async(hir::CoroutineSource::Fn) => "an async function", hir::CoroutineKind::Async(hir::CoroutineSource::Closure) => "an async closure", + hir::CoroutineKind::AsyncGen(hir::CoroutineSource::Block) => "an async gen block", + hir::CoroutineKind::AsyncGen(hir::CoroutineSource::Fn) => "an async gen function", + hir::CoroutineKind::AsyncGen(hir::CoroutineSource::Closure) => "an async gen closure", hir::CoroutineKind::Gen(hir::CoroutineSource::Block) => "a gen block", hir::CoroutineKind::Gen(hir::CoroutineSource::Fn) => "a gen function", hir::CoroutineKind::Gen(hir::CoroutineSource::Closure) => "a gen closure", @@ -1751,7 +2030,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ct_op: |ct| ct.normalize(self.tcx, ty::ParamEnv::empty()), }); err.highlighted_help(vec![ - (format!("the trait `{}` ", cand.print_only_trait_path()), Style::NoStyle), + (format!("the trait `{}` ", cand.print_trait_sugared()), Style::NoStyle), ("is".to_string(), Style::Highlight), (" implemented for `".to_string(), Style::NoStyle), (cand.self_ty().to_string(), Style::Highlight), @@ -1787,7 +2066,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { _ => (" implemented for `", ""), }; err.highlighted_help(vec![ - (format!("the trait `{}` ", cand.print_only_trait_path()), Style::NoStyle), + (format!("the trait `{}` ", cand.print_trait_sugared()), Style::NoStyle), ("is".to_string(), Style::Highlight), (desc.to_string(), Style::NoStyle), (cand.self_ty().to_string(), Style::Highlight), @@ -1820,7 +2099,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let end = if candidates.len() <= 9 { candidates.len() } else { 8 }; err.help(format!( "the following {other}types implement trait `{}`:{}{}", - trait_ref.print_only_trait_path(), + trait_ref.print_trait_sugared(), candidates[..end].join(""), if candidates.len() > 9 { format!("\nand {} others", candidates.len() - 8) @@ -1967,7 +2246,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => { - self.get_parent_trait_ref(&parent_code) + self.get_parent_trait_ref(parent_code) } _ => None, } @@ -2182,7 +2461,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) { let mut expr_finder = FindExprBySpan::new(span); - expr_finder.visit_expr(&self.tcx.hir().body(body_id).value); + expr_finder.visit_expr(self.tcx.hir().body(body_id).value); if let Some(hir::Expr { kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)), @@ -2228,7 +2507,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ident: trait_name, kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs), .. - })) = self.tcx.hir().find_by_def_id(local_def_id) + })) = self.tcx.opt_hir_node_by_def_id(local_def_id) && let Some(method_ref) = trait_item_refs .iter() .find(|item_ref| item_ref.ident == *assoc_item_name) @@ -2731,7 +3010,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { Some(format!("{cannot_do_this} in const contexts")) } // overridden post message - (true, Some(AppendConstMessage::Custom(custom_msg))) => { + (true, Some(AppendConstMessage::Custom(custom_msg, _))) => { Some(format!("{cannot_do_this}{custom_msg}")) } // fallback to generic message @@ -2752,7 +3031,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { use rustc_transmute::Answer; // Erase regions because layout code doesn't particularly care about regions. - let trait_ref = self.tcx.erase_regions(self.tcx.erase_late_bound_regions(trait_ref)); + let trait_ref = + self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref)); let src_and_dst = rustc_transmute::Types { dst: trait_ref.args.type_at(0), @@ -2933,7 +3213,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { obligation.param_env, ) { self.report_similar_impl_candidates_for_root_obligation( - &obligation, + obligation, *trait_predicate, body_def_id, err, @@ -3040,18 +3320,18 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Additional context information explaining why the closure only implements // a particular trait. if let Some(typeck_results) = &self.typeck_results { - let hir_id = self.tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); + let hir_id = self.tcx.local_def_id_to_hir_id(closure_def_id.expect_local()); match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) { (ty::ClosureKind::FnOnce, Some((span, place))) => { err.fn_once_label = Some(ClosureFnOnceLabel { span: *span, - place: ty::place_to_string_for_capture(self.tcx, &place), + place: ty::place_to_string_for_capture(self.tcx, place), }) } (ty::ClosureKind::FnMut, Some((span, place))) => { err.fn_mut_label = Some(ClosureFnMutLabel { span: *span, - place: ty::place_to_string_for_capture(self.tcx, &place), + place: ty::place_to_string_for_capture(self.tcx, place), }) } _ => {} @@ -3115,7 +3395,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { }; if let Some(diag) = - self.tcx.sess.diagnostic().steal_diagnostic(self.tcx.def_span(def_id), StashKey::Cycle) + self.tcx.sess.dcx().steal_diagnostic(self.tcx.def_span(def_id), StashKey::Cycle) { diag.cancel(); } @@ -3163,7 +3443,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut not_tupled = false; let found = match found_trait_ref.skip_binder().args.type_at(1).kind() { - ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()], + ty::Tuple(tys) => vec![ArgKind::empty(); tys.len()], _ => { not_tupled = true; vec![ArgKind::empty()] @@ -3172,7 +3452,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let expected_ty = expected_trait_ref.skip_binder().args.type_at(1); let expected = match expected_ty.kind() { - ty::Tuple(ref tys) => { + ty::Tuple(tys) => { tys.iter().map(|t| ArgKind::from_expected_ty(t, Some(span))).collect() } _ => { @@ -3253,20 +3533,30 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { - let ty::ConstKind::Unevaluated(uv) = ct.kind() else { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => match ct.kind() { + ty::ConstKind::Unevaluated(uv) => { + let mut err = + self.tcx.sess.struct_span_err(span, "unconstrained generic constant"); + let const_span = self.tcx.def_span(uv.def); + match self.tcx.sess.source_map().span_to_snippet(const_span) { + Ok(snippet) => err.help(format!( + "try adding a `where` bound using this expression: `where [(); {snippet}]:`" + )), + _ => err.help("consider adding a `where` bound using this expression"), + }; + Some(err) + } + ty::ConstKind::Expr(_) => { + let err = self + .tcx + .sess + .struct_span_err(span, format!("unconstrained generic constant `{ct}`")); + Some(err) + } + _ => { bug!("const evaluatable failed for non-unevaluated const `{ct:?}`"); - }; - let mut err = self.tcx.sess.struct_span_err(span, "unconstrained generic constant"); - let const_span = self.tcx.def_span(uv.def); - match self.tcx.sess.source_map().span_to_snippet(const_span) { - Ok(snippet) => err.help(format!( - "try adding a `where` bound using this expression: `where [(); {snippet}]:`" - )), - _ => err.help("consider adding a `where` bound using this expression"), - }; - Some(err) - } + } + }, _ => { span_bug!( span, |