From 20431706a863f92cb37dc512fef6e48d192aaf2c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:11:38 +0200 Subject: Merging upstream version 1.66.0+dfsg1. Signed-off-by: Daniel Baumann --- .../rustc_infer/src/infer/error_reporting/mod.rs | 304 ++++++++++++++++----- 1 file changed, 235 insertions(+), 69 deletions(-) (limited to 'compiler/rustc_infer/src/infer/error_reporting/mod.rs') diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index eb5afe828..9ff703e52 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -51,6 +51,7 @@ use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePa use crate::infer; use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; +use crate::infer::ExpectedFound; use crate::traits::error_reporting::report_object_safety_error; use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, @@ -69,12 +70,12 @@ use rustc_middle::dep_graph::DepContext; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::relate::{self, RelateResult, TypeRelation}; use rustc_middle::ty::{ - self, error::TypeError, Binder, List, Region, Subst, Ty, TyCtxt, TypeFoldable, - TypeSuperVisitable, TypeVisitable, + self, error::TypeError, Binder, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, + TypeVisitable, }; use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span}; use rustc_target::spec::abi; -use std::ops::ControlFlow; +use std::ops::{ControlFlow, Deref}; use std::{cmp, fmt, iter}; mod note; @@ -84,6 +85,31 @@ pub use need_type_info::TypeAnnotationNeeded; pub mod nice_region_error; +/// 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` or `FnCtxt::infer_err`. +pub struct TypeErrCtxt<'a, 'tcx> { + pub infcx: &'a InferCtxt<'tcx>, + pub typeck_results: Option>>, +} + +impl TypeErrCtxt<'_, '_> { + /// 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 + } +} + pub(super) fn note_and_explain_region<'tcx>( tcx: TyCtxt<'tcx>, err: &mut Diagnostic, @@ -303,7 +329,38 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( err } -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { + pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option>> { + if let ty::Opaque(def_id, substs) = ty.kind() { + let future_trait = self.tcx.require_lang_item(LangItem::Future, None); + // Future::Output + let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; + + let bounds = self.tcx.bound_explicit_item_bounds(*def_id); + + for (predicate, _) in bounds.subst_iter_copied(self.tcx, substs) { + let output = predicate + .kind() + .map_bound(|kind| match kind { + ty::PredicateKind::Projection(projection_predicate) + if projection_predicate.projection_ty.item_def_id == item_def_id => + { + projection_predicate.term.ty() + } + _ => None, + }) + .transpose(); + if output.is_some() { + // We don't account for multiple `Future::Output = Ty` constraints. + return output; + } + } + } + None + } +} + +impl<'tcx> TypeErrCtxt<'_, 'tcx> { pub fn report_region_errors( &self, generic_param_scope: LocalDefId, @@ -577,13 +634,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { { // don't show type `_` if span.desugaring_kind() == Some(DesugaringKind::ForLoop) - && let ty::Adt(def, substs) = ty.kind() - && Some(def.did()) == self.tcx.get_diagnostic_item(sym::Option) + && let ty::Adt(def, substs) = 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 `{}`", substs.type_at(0))); } else { - err.span_label(span, format!("this expression has type `{}`", ty)); - } + 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 @@ -619,8 +676,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { 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.in_progress_typeck_results.and_then(|typeck_results| { - typeck_results.borrow().expr_ty_opt(arg_expr) + 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"); @@ -726,10 +783,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { _ => { if let ObligationCauseCode::BindingObligation(_, span) | ObligationCauseCode::ExprBindingObligation(_, span, ..) - = cause.code().peel_derives() + = cause.code().peel_derives() && let TypeError::RegionsPlaceholderMismatch = terr { - err.span_note(*span, "the lifetime requirement is introduced here"); + err.span_note( * span, + "the lifetime requirement is introduced here"); } } } @@ -1653,8 +1711,114 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ), Mismatch::Fixed(s) => (s.into(), s.into(), None), }; - match (&terr, expected == found) { - (TypeError::Sorts(values), extra) => { + + 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>| { + 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, + diagnostic: &mut Diagnostic| { + let name = shadow.sort_string(self.tcx); + diagnostic.note(format!( + "{prim} and {name} have similar names, but are actually distinct types" + )); + diagnostic + .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}") + }; + diagnostic.span_note(def_span, msg); + }; + + let diagnose_adts = + |expected_adt : ty::AdtDef<'tcx>, + found_adt: ty::AdtDef<'tcx>, + diagnostic: &mut Diagnostic| { + 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(); + + diagnostic.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}`") + }; + diagnostic.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::Opaque(def_id, _)) => { let sm = self.tcx.sess.source_map(); @@ -1707,10 +1871,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); } } - (TypeError::ObjectUnsafeCoercion(_), _) => { + TypeError::ObjectUnsafeCoercion(_) => { diag.note_unsuccessful_coercion(found, expected); } - (_, _) => { + _ => { debug!( "note_type_err: exp_found={:?}, expected={:?} found={:?}", exp_found, expected, found @@ -1755,7 +1919,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // In some (most?) cases cause.body_id points to actual body, but in some cases // it's an actual definition. According to the comments (e.g. in - // librustc_typeck/check/compare_method.rs:compare_predicate_entailment) the latter + // rustc_hir_analysis/check/compare_method.rs:compare_predicate_entailment) the latter // is relied upon by some other code. This might (or might not) need cleanup. let body_owner_def_id = self.tcx.hir().opt_local_def_id(cause.body_id).unwrap_or_else(|| { @@ -1847,36 +2011,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option>> { - if let ty::Opaque(def_id, substs) = ty.kind() { - let future_trait = self.tcx.require_lang_item(LangItem::Future, None); - // Future::Output - let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; - - let bounds = self.tcx.bound_explicit_item_bounds(*def_id); - - for predicate in bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) { - let predicate = predicate.subst(self.tcx, substs); - let output = predicate - .kind() - .map_bound(|kind| match kind { - ty::PredicateKind::Projection(projection_predicate) - if projection_predicate.projection_ty.item_def_id == item_def_id => - { - projection_predicate.term.ty() - } - _ => None, - }) - .transpose(); - if output.is_some() { - // We don't account for multiple `Future::Output = Ty` constraints. - return output; - } - } - } - None - } - /// A possible error is to forget to add `.await` when using futures: /// /// ```compile_fail,E0308 @@ -2051,8 +2185,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { exp_found: &ty::error::ExpectedFound>, diag: &mut Diagnostic, ) { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) + && let Some(msg) = self.should_suggest_as_ref(exp_found.expected, exp_found.found) + { + diag.span_suggestion( + span, + msg, + // HACK: fix issue# 100605, suggesting convert from &Option to Option<&T>, remove the extra `&` + format!("{}.as_ref()", snippet.trim_start_matches('&')), + Applicability::MachineApplicable, + ); + } + } + + pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> { if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) = - (exp_found.expected.kind(), exp_found.found.kind()) + (expected.kind(), found.kind()) { if let ty::Adt(found_def, found_substs) = *found_ty.kind() { if exp_def == &found_def { @@ -2090,21 +2238,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { _ => show_suggestion = false, } } - if let (Ok(snippet), true) = - (self.tcx.sess.source_map().span_to_snippet(span), show_suggestion) - { - diag.span_suggestion( - span, - *msg, - // HACK: fix issue# 100605, suggesting convert from &Option to Option<&T>, remove the extra `&` - format!("{}.as_ref()", snippet.trim_start_matches('&')), - Applicability::MachineApplicable, - ); + if show_suggestion { + return Some(*msg); } } } } } + None } pub fn report_and_explain_type_error( @@ -2130,6 +2271,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { struct_span_err!(self.tcx.sess, span, E0580, "{}", failure_str) } FailureCode::Error0308(failure_str) => { + 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 + } let mut err = struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str); if let Some((expected, found)) = trace.values.ty() { match (expected.kind(), found.kind()) { @@ -2151,7 +2311,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err.span_suggestion( span, "if you meant to write a `char` literal, use single quotes", - format!("'{}'", code), + format!("'{}'", escape_literal(code)), Applicability::MachineApplicable, ); } @@ -2166,7 +2326,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err.span_suggestion( span, "if you meant to write a `str` literal, use double quotes", - format!("\"{}\"", code), + format!("\"{}\"", escape_literal(code)), Applicability::MachineApplicable, ); } @@ -2317,7 +2477,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { origin: Option>, bound_kind: GenericKind<'tcx>, sub: Region<'tcx>, - ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { // Attempt to obtain the span of the parameter so we can // suggest adding an explicit lifetime bound to it. let generics = self.tcx.generics_of(generic_param_scope); @@ -2333,7 +2493,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // 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_id = self.tcx.hir().local_def_id_to_hir_id(def_id); - let ast_generics = self.tcx.hir().get_generics(hir_id.owner); + let ast_generics = self.tcx.hir().get_generics(hir_id.owner.def_id); let bounds = ast_generics.and_then(|g| g.bounds_span_for_suggestions(def_id)); // `sp` only covers `T`, change it so that it covers @@ -2374,6 +2534,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let labeled_user_string = match bound_kind { GenericKind::Param(ref p) => format!("the parameter type `{}`", p), GenericKind::Projection(ref p) => format!("the associated type `{}`", p), + GenericKind::Opaque(def_id, substs) => { + format!("the opaque type `{}`", self.tcx.def_path_str_with_substs(def_id, substs)) + } }; if let Some(SubregionOrigin::CompareImplItemObligation { @@ -2453,7 +2616,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { for h in self.tcx.hir().parent_iter(param.hir_id) { break 'origin match h.1 { Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::TyAlias(..), + kind: hir::ImplItemKind::Type(..), generics, .. }) @@ -2692,7 +2855,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } -struct SameTypeModuloInfer<'a, 'tcx>(&'a InferCtxt<'a, 'tcx>); +struct SameTypeModuloInfer<'a, 'tcx>(&'a InferCtxt<'tcx>); impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { @@ -2779,7 +2942,7 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> { } } -impl<'a, 'tcx> InferCtxt<'a, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { fn report_inference_failure( &self, var_origin: RegionVariableOrigin, @@ -2967,7 +3130,7 @@ impl TyCategory { } } -impl<'tcx> InferCtxt<'_, 'tcx> { +impl<'tcx> InferCtxt<'tcx> { /// Given a [`hir::Block`], get the span of its last expression or /// statement, peeling off any inner blocks. pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span { @@ -2994,7 +3157,9 @@ impl<'tcx> InferCtxt<'_, 'tcx> { _ => rustc_span::DUMMY_SP, } } +} +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( @@ -3011,7 +3176,7 @@ impl<'tcx> InferCtxt<'_, 'tcx> { let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else { return None; }; - let last_expr_ty = self.in_progress_typeck_results?.borrow().expr_ty_opt(*last_expr)?; + 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) => { @@ -3094,8 +3259,9 @@ impl<'tcx> InferCtxt<'_, 'tcx> { let mut find_compatible_candidates = |pat: &hir::Pat<'_>| { if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind && let Some(pat_ty) = self - .in_progress_typeck_results - .and_then(|typeck_results| typeck_results.borrow().node_type_opt(*hir_id)) + .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) -- cgit v1.2.3