X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=compiler%2Frustc_trait_selection%2Fsrc%2Ftraits%2Ferror_reporting%2Fmod.rs;h=dda7b2b2fa5b0b392ce3d19f080c236152a8284e;hb=db416ea195e46fa8f7c8adabc9e9b3e0b71f5c90;hp=049b24b39975dd3ff7e21978d667a4d40d324ee1;hpb=3d64420fa9442d50c6b51c7587c9310a88163ba8;p=rust.git diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 049b24b3997..dda7b2b2fa5 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -9,7 +9,7 @@ }; use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use crate::infer::{self, InferCtxt, TyCtxtInferExt}; +use crate::infer::{self, InferCtxt}; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::query::normalize::QueryNormalizeExt as _; use crate::traits::specialize::to_pretty_impl_header; @@ -71,7 +71,7 @@ pub trait InferCtxtExt<'tcx> { /// returns a span and `ArgKind` information that describes the /// arguments it expects. This can be supplied to /// `report_arg_count_mismatch`. - fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec)>; + fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option, Vec)>; /// Reports an error when the number of arguments needed by a /// trait match doesn't match the number that the expression @@ -83,6 +83,7 @@ fn report_arg_count_mismatch( expected_args: Vec, found_args: Vec, is_closure: bool, + closure_pipe_span: Option, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` @@ -98,26 +99,36 @@ fn type_implements_fn_trait( } pub trait TypeErrCtxtExt<'tcx> { + fn report_overflow_error( + &self, + predicate: &T, + span: Span, + suggest_increasing_limit: bool, + mutate: impl FnOnce(&mut Diagnostic), + ) -> ! + where + T: fmt::Display + + TypeFoldable<'tcx> + + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>, + >>::Error: std::fmt::Debug; + fn report_fulfillment_errors( &self, errors: &[FulfillmentError<'tcx>], body_id: Option, ) -> ErrorGuaranteed; - fn report_overflow_error( + fn report_overflow_obligation( &self, obligation: &Obligation<'tcx, T>, suggest_increasing_limit: bool, ) -> ! where - T: fmt::Display - + TypeFoldable<'tcx> - + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>, - >>::Error: std::fmt::Debug; + T: ToPredicate<'tcx> + Clone; fn suggest_new_overflow_limit(&self, err: &mut Diagnostic); - fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !; + fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !; /// The `root_obligation` parameter should be the `root_obligation` field /// from a `FulfillmentError`. If no `FulfillmentError` is available, @@ -135,15 +146,16 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { /// returns a span and `ArgKind` information that describes the /// arguments it expects. This can be supplied to /// `report_arg_count_mismatch`. - fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec)> { + fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option, Vec)> { let sm = self.tcx.sess.source_map(); let hir = self.tcx.hir(); Some(match node { Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }), + kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }), .. }) => ( fn_decl_span, + fn_arg_span, hir.body(body) .params .iter() @@ -174,6 +186,7 @@ fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec)> kind: hir::TraitItemKind::Fn(ref sig, _), .. }) => ( sig.span, + None, sig.decl .inputs .iter() @@ -188,7 +201,7 @@ fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec)> ), Node::Ctor(ref variant_data) => { let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); - (span, vec![ArgKind::empty(); variant_data.fields().len()]) + (span, None, vec![ArgKind::empty(); variant_data.fields().len()]) } _ => panic!("non-FnLike node found: {:?}", node), }) @@ -204,6 +217,7 @@ fn report_arg_count_mismatch( expected_args: Vec, found_args: Vec, is_closure: bool, + closure_arg_span: Option, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { let kind = if is_closure { "closure" } else { "function" }; @@ -241,24 +255,13 @@ fn report_arg_count_mismatch( if let Some(found_span) = found_span { err.span_label(found_span, format!("takes {}", found_str)); - // move |_| { ... } - // ^^^^^^^^-- def_span - // - // move |_| { ... } - // ^^^^^-- prefix - let prefix_span = self.tcx.sess.source_map().span_until_non_whitespace(found_span); - // move |_| { ... } - // ^^^-- pipe_span - let pipe_span = - if let Some(span) = found_span.trim_start(prefix_span) { span } else { found_span }; - // Suggest to take and ignore the arguments with expected_args_length `_`s if // found arguments is empty (assume the user just wants to ignore args in this case). // For example, if `expected_args_length` is 2, suggest `|_, _|`. if found_args.is_empty() && is_closure { let underscores = vec!["_"; expected_args.len()].join(", "); err.span_suggestion_verbose( - pipe_span, + closure_arg_span.unwrap_or(found_span), &format!( "consider changing the closure to take and ignore the expected argument{}", pluralize!(expected_args.len()) @@ -465,8 +468,10 @@ struct ErrorDescriptor<'tcx> { /// occurrences in any case. fn report_overflow_error( &self, - obligation: &Obligation<'tcx, T>, + predicate: &T, + span: Span, suggest_increasing_limit: bool, + mutate: impl FnOnce(&mut Diagnostic), ) -> ! where T: fmt::Display @@ -474,8 +479,9 @@ fn report_overflow_error( + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>, >>::Error: std::fmt::Debug, { - let predicate = self.resolve_vars_if_possible(obligation.predicate.clone()); + let predicate = self.resolve_vars_if_possible(predicate.clone()); let mut pred_str = predicate.to_string(); + if pred_str.len() > 50 { // We don't need to save the type to a file, we will be talking about this type already // in a separate note when we explain the obligation, so it will be available that way. @@ -490,7 +496,7 @@ fn report_overflow_error( } let mut err = struct_span_err!( self.tcx.sess, - obligation.cause.span, + span, E0275, "overflow evaluating the requirement `{}`", pred_str, @@ -500,20 +506,46 @@ fn report_overflow_error( self.suggest_new_overflow_limit(&mut err); } - self.note_obligation_cause_code( - &mut err, - &obligation.predicate, - obligation.param_env, - obligation.cause.code(), - &mut vec![], - &mut Default::default(), - ); + mutate(&mut err); err.emit(); self.tcx.sess.abort_if_errors(); bug!(); } + /// Reports that an overflow has occurred and halts compilation. We + /// halt compilation unconditionally because it is important that + /// overflows never be masked -- they basically represent computations + /// whose result could not be truly determined and thus we can't say + /// if the program type checks or not -- and they are unusual + /// occurrences in any case. + fn report_overflow_obligation( + &self, + obligation: &Obligation<'tcx, T>, + suggest_increasing_limit: bool, + ) -> ! + where + T: ToPredicate<'tcx> + Clone, + { + let predicate = obligation.predicate.clone().to_predicate(self.tcx); + let predicate = self.resolve_vars_if_possible(predicate); + self.report_overflow_error( + &predicate, + obligation.cause.span, + suggest_increasing_limit, + |err| { + self.note_obligation_cause_code( + err, + &predicate, + obligation.param_env, + obligation.cause.code(), + &mut vec![], + &mut Default::default(), + ); + }, + ); + } + fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) { let suggested_limit = match self.tcx.recursion_limit() { Limit(0) => Limit(2), @@ -528,11 +560,11 @@ fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) { } /// Reports that a cycle was detected which led to overflow and halts - /// compilation. This is equivalent to `report_overflow_error` except + /// compilation. This is equivalent to `report_overflow_obligation` except /// that we can give a more helpful error message (and, in particular, /// we do not suggest increasing the overflow limit, which is not /// going to help). - fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { + fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { let cycle = self.resolve_vars_if_possible(cycle.to_owned()); assert!(!cycle.is_empty()); @@ -540,7 +572,10 @@ fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! // The 'deepest' obligation is most likely to have a useful // cause 'backtrace' - self.report_overflow_error(cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(), false); + self.report_overflow_obligation( + cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(), + false, + ); } fn report_selection_error( @@ -1252,13 +1287,14 @@ fn report_selection_error( obligation.cause.code(), ) } else { - let (closure_span, found) = found_did + let (closure_span, closure_arg_span, found) = found_did .and_then(|did| { let node = self.tcx.hir().get_if_local(did)?; - let (found_span, found) = self.get_fn_like_arguments(node)?; - Some((Some(found_span), found)) + let (found_span, closure_arg_span, found) = + self.get_fn_like_arguments(node)?; + Some((Some(found_span), closure_arg_span, found)) }) - .unwrap_or((found_span, found)); + .unwrap_or((found_span, None, found)); self.report_arg_count_mismatch( span, @@ -1266,6 +1302,7 @@ fn report_selection_error( expected, found, found_trait_ty.is_closure(), + closure_arg_span, ) } } @@ -1559,7 +1596,7 @@ fn report_fulfillment_error( diag.emit(); } FulfillmentErrorCode::CodeCycle(ref cycle) => { - self.report_overflow_error_cycle(cycle); + self.report_overflow_obligation_cycle(cycle); } } } @@ -1577,32 +1614,26 @@ fn report_projection_error( } self.probe(|_| { - let mut err = error.err; - let mut values = None; + let ocx = ObligationCtxt::new_in_snapshot(self); // try to find the mismatched types to report the error with. // // this can fail if the problem was higher-ranked, in which // cause I have no idea for a good error message. let bound_predicate = predicate.kind(); - if let ty::PredicateKind::Clause(ty::Clause::Projection(data)) = + let (values, err) = if let ty::PredicateKind::Clause(ty::Clause::Projection(data)) = bound_predicate.skip_binder() { - let mut selcx = SelectionContext::new(self); let data = self.replace_bound_vars_with_fresh_vars( obligation.cause.span, infer::LateBoundRegionConversionTime::HigherRankedType, bound_predicate.rebind(data), ); - let mut obligations = vec![]; - // FIXME(normalization): Change this to use `At::normalize` - let normalized_ty = super::normalize_projection_type( - &mut selcx, + let normalized_ty = ocx.normalize( + &obligation.cause, obligation.param_env, - data.projection_ty, - obligation.cause.clone(), - 0, - &mut obligations, + self.tcx + .mk_projection(data.projection_ty.item_def_id, data.projection_ty.substs), ); debug!(?obligation.cause, ?obligation.param_env); @@ -1618,19 +1649,34 @@ fn report_projection_error( | ObligationCauseCode::ObjectCastObligation(..) | ObligationCauseCode::OpaqueType ); - if let Err(new_err) = self.at(&obligation.cause, obligation.param_env).eq_exp( + let expected_ty = data.term.ty().unwrap_or_else(|| self.tcx.ty_error()); + + // constrain inference variables a bit more to nested obligations from normalize so + // we can have more helpful errors. + ocx.select_where_possible(); + + if let Err(new_err) = ocx.eq_exp( + &obligation.cause, + obligation.param_env, is_normalized_ty_expected, normalized_ty, - data.term, + expected_ty, ) { - values = Some((data, is_normalized_ty_expected, normalized_ty, data.term)); - err = new_err; + (Some((data, is_normalized_ty_expected, normalized_ty, expected_ty)), new_err) + } else { + (None, error.err) } - } + } else { + (None, error.err) + }; let msg = values .and_then(|(predicate, _, normalized_ty, expected_ty)| { - self.maybe_detailed_projection_msg(predicate, normalized_ty, expected_ty) + self.maybe_detailed_projection_msg( + predicate, + normalized_ty.into(), + expected_ty.into(), + ) }) .unwrap_or_else(|| format!("type mismatch resolving `{}`", predicate)); let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}"); @@ -1672,11 +1718,11 @@ fn report_projection_error( &mut diag, &obligation.cause, secondary_span, - values.map(|(_, is_normalized_ty_expected, normalized_ty, term)| { + values.map(|(_, is_normalized_ty_expected, normalized_ty, expected_ty)| { infer::ValuePairs::Terms(ExpectedFound::new( is_normalized_ty_expected, - normalized_ty, - term, + normalized_ty.into(), + expected_ty.into(), )) }), err, @@ -1806,7 +1852,8 @@ fn find_similar_impl_candidates( &self, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Vec> { - self.tcx + let mut candidates: Vec<_> = self + .tcx .all_impls(trait_pred.def_id()) .filter_map(|def_id| { if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative @@ -1822,7 +1869,14 @@ fn find_similar_impl_candidates( self.fuzzy_match_tys(trait_pred.skip_binder().self_ty(), imp.self_ty(), false) .map(|similarity| ImplCandidate { trait_ref: imp, similarity }) }) - .collect() + .collect(); + if candidates.iter().any(|c| matches!(c.similarity, CandidateSimilarity::Exact { .. })) { + // If any of the candidates is a perfect match, we don't want to show all of them. + // This is particularly relevant for the case of numeric types (as they all have the + // same cathegory). + candidates.retain(|c| matches!(c.similarity, CandidateSimilarity::Exact { .. })); + } + candidates } fn report_similar_impl_candidates( @@ -1930,14 +1984,6 @@ fn report_similar_impl_candidates( return report(normalized_impl_candidates, err); } - let normalize = |candidate| { - let infcx = self.tcx.infer_ctxt().build(); - infcx - .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) - .query_normalize(candidate) - .map_or(candidate, |normalized| normalized.value) - }; - // Sort impl candidates so that ordering is consistent for UI tests. // because the ordering of `impl_candidates` may not be deterministic: // https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507 @@ -1947,7 +1993,11 @@ fn report_similar_impl_candidates( let mut normalized_impl_candidates_and_similarities = impl_candidates .into_iter() .map(|ImplCandidate { trait_ref, similarity }| { - let normalized = normalize(trait_ref); + // FIXME(compiler-errors): This should be using `NormalizeExt::normalize` + let normalized = self + .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) + .query_normalize(trait_ref) + .map_or(trait_ref, |normalized| normalized.value); (similarity, normalized) }) .collect::>();