diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:18:21 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:18:21 +0000 |
commit | 4e8199b572f2035b7749cba276ece3a26630d23e (patch) | |
tree | f09feeed6a0fe39d027b1908aa63ea6b35e4b631 /compiler/rustc_hir_typeck/src/fn_ctxt | |
parent | Adding upstream version 1.66.0+dfsg1. (diff) | |
download | rustc-4e8199b572f2035b7749cba276ece3a26630d23e.tar.xz rustc-4e8199b572f2035b7749cba276ece3a26630d23e.zip |
Adding upstream version 1.67.1+dfsg1.upstream/1.67.1+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_hir_typeck/src/fn_ctxt')
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs | 249 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 86 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 66 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 149 |
4 files changed, 287 insertions, 263 deletions
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 6a1cffe3e..952d27262 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -16,13 +16,13 @@ use rustc_hir_analysis::astconv::{ }; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; -use rustc_infer::infer::{InferOk, InferResult}; +use rustc_infer::infer::InferResult; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, ToPolyTraitRef, - ToPredicate, Ty, UserType, + self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, Ty, UserType, }; use rustc_middle::ty::{GenericArgKind, InternalSubsts, SubstsRef, UserSelfTy, UserSubsts}; use rustc_session::lint; @@ -30,11 +30,8 @@ use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{Span, DUMMY_SP}; -use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; -use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, -}; +use rustc_trait_selection::traits::{self, NormalizeExt, ObligationCauseCode, ObligationCtxt}; use std::collections::hash_map::Entry; use std::slice; @@ -106,7 +103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // possible. This can help substantially when there are // indirect dependencies that don't seem worth tracking // precisely. - self.select_obligations_where_possible(false, mutate_fulfillment_errors); + self.select_obligations_where_possible(mutate_fulfillment_errors); self.resolve_vars_if_possible(ty) } @@ -142,9 +139,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("write_ty({:?}, {:?}) in fcx {}", id, self.resolve_vars_if_possible(ty), self.tag()); self.typeck_results.borrow_mut().node_types_mut().insert(id, ty); - if ty.references_error() { - self.has_errors.set(true); - self.set_tainted_by_errors(); + if let Err(e) = ty.error_reported() { + self.set_tainted_by_errors(e); } } @@ -346,7 +342,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { debug!("instantiate_type_scheme(value={:?}, substs={:?})", value, substs); let value = EarlyBinder(value).subst(self.tcx, substs); - let result = self.normalize_associated_types_in(span, value); + let result = self.normalize(span, value); debug!("instantiate_type_scheme = {:?}", result); result } @@ -362,7 +358,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let bounds = self.tcx.predicates_of(def_id); let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect(); let result = bounds.instantiate(self.tcx, substs); - let result = self.normalize_associated_types_in(span, result); + let result = self.normalize(span, result); debug!( "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}", bounds, substs, result, spans, @@ -370,50 +366,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (result, spans) } - pub(in super::super) fn normalize_associated_types_in<T>(&self, span: Span, value: T) -> T - where - T: TypeFoldable<'tcx>, - { - self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value) - } - - pub(in super::super) fn normalize_associated_types_in_as_infer_ok<T>( - &self, - span: Span, - value: T, - ) -> InferOk<'tcx, T> + pub(in super::super) fn normalize<T>(&self, span: Span, value: T) -> T where T: TypeFoldable<'tcx>, { - self.inh.partially_normalize_associated_types_in( - ObligationCause::misc(span, self.body_id), - self.param_env, - value, - ) - } - - pub(in super::super) fn normalize_op_associated_types_in_as_infer_ok<T>( - &self, - span: Span, - value: T, - opt_input_expr: Option<&hir::Expr<'_>>, - ) -> InferOk<'tcx, T> - where - T: TypeFoldable<'tcx>, - { - self.inh.partially_normalize_associated_types_in( - ObligationCause::new( - span, - self.body_id, - traits::BinOp { - rhs_span: opt_input_expr.map(|expr| expr.span), - is_lit: opt_input_expr - .map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))), - output_ty: None, - }, - ), - self.param_env, - value, + self.register_infer_ok_obligations( + self.at(&self.misc(span), self.param_env).normalize(value), ) } @@ -490,11 +448,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match length { &hir::ArrayLen::Infer(_, span) => self.ct_infer(self.tcx.types.usize, None, span), hir::ArrayLen::Body(anon_const) => { - let const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); - let span = self.tcx.hir().span(anon_const.hir_id); - let c = ty::Const::from_anon_const(self.tcx, const_def_id); + let span = self.tcx.def_span(anon_const.def_id); + let c = ty::Const::from_anon_const(self.tcx, anon_const.def_id); self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None)); - self.normalize_associated_types_in(span, c) + self.normalize(span, c) } } } @@ -504,10 +461,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ast_c: &hir::AnonConst, param_def_id: DefId, ) -> ty::Const<'tcx> { - let const_def = ty::WithOptConstParam { - did: self.tcx.hir().local_def_id(ast_c.hir_id), - const_param_did: Some(param_def_id), - }; + let const_def = + ty::WithOptConstParam { did: ast_c.def_id, const_param_did: Some(param_def_id) }; let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def); self.register_wf_obligation( c.into(), @@ -534,7 +489,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { match self.typeck_results.borrow().node_types().get(id) { Some(&t) => t, - None if self.is_tainted_by_errors() => self.tcx.ty_error(), + None if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error_with_guaranteed(e), None => { bug!( "no type for node {}: {} in fcx {}", @@ -549,7 +504,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn node_ty_opt(&self, id: hir::HirId) -> Option<Ty<'tcx>> { match self.typeck_results.borrow().node_types().get(id) { Some(&t) => Some(t), - None if self.is_tainted_by_errors() => Some(self.tcx.ty_error()), + None if let Some(e) = self.tainted_by_errors() => Some(self.tcx.ty_error_with_guaranteed(e)), None => None, } } @@ -564,9 +519,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // WF obligations never themselves fail, so no real need to give a detailed cause: let cause = traits::ObligationCause::new(span, self.body_id, code); self.register_predicate(traits::Obligation::new( + self.tcx, cause, self.param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(self.tcx), + ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)), )); } @@ -588,7 +544,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { field: &'tcx ty::FieldDef, substs: SubstsRef<'tcx>, ) -> Ty<'tcx> { - self.normalize_associated_types_in(span, field.ty(self.tcx, substs)) + self.normalize(span, field.ty(self.tcx, substs)) } pub(in super::super) fn resolve_rvalue_scopes(&self, def_id: DefId) { @@ -601,7 +557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) { let mut generators = self.deferred_generator_interiors.borrow_mut(); for (body_id, interior, kind) in generators.drain(..) { - self.select_obligations_where_possible(false, |_| {}); + self.select_obligations_where_possible(|_| {}); crate::generator_interior::resolve_interior(self, def_id, body_id, interior, kind); } } @@ -612,25 +568,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !errors.is_empty() { self.adjust_fulfillment_errors_for_expr_obligation(&mut errors); - self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id, false); + self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id); } } /// Select as many obligations as we can at present. pub(in super::super) fn select_obligations_where_possible( &self, - fallback_has_occurred: bool, mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>), ) { let mut result = self.fulfillment_cx.borrow_mut().select_where_possible(self); if !result.is_empty() { mutate_fulfillment_errors(&mut result); self.adjust_fulfillment_errors_for_expr_obligation(&mut result); - self.err_ctxt().report_fulfillment_errors( - &result, - self.inh.body_id, - fallback_has_occurred, - ); + self.err_ctxt().report_fulfillment_errors(&result, self.inh.body_id); } } @@ -650,12 +601,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(skip(self), level = "debug")] - fn self_type_matches_expected_vid( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - expected_vid: ty::TyVid, - ) -> bool { - let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty()); + fn self_type_matches_expected_vid(&self, self_ty: Ty<'tcx>, expected_vid: ty::TyVid) -> bool { + let self_ty = self.shallow_resolve(self_ty); debug!(?self_ty); match *self_ty.kind() { @@ -674,54 +621,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn obligations_for_self_ty<'b>( &'b self, self_ty: ty::TyVid, - ) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, traits::PredicateObligation<'tcx>)> - + Captures<'tcx> - + 'b { + ) -> impl DoubleEndedIterator<Item = traits::PredicateObligation<'tcx>> + Captures<'tcx> + 'b + { // FIXME: consider using `sub_root_var` here so we // can see through subtyping. let ty_var_root = self.root_var(self_ty); trace!("pending_obligations = {:#?}", self.fulfillment_cx.borrow().pending_obligations()); - self.fulfillment_cx - .borrow() - .pending_obligations() - .into_iter() - .filter_map(move |obligation| { - let bound_predicate = obligation.predicate.kind(); - match bound_predicate.skip_binder() { - ty::PredicateKind::Projection(data) => Some(( - bound_predicate.rebind(data).required_poly_trait_ref(self.tcx), - obligation, - )), - ty::PredicateKind::Trait(data) => { - Some((bound_predicate.rebind(data).to_poly_trait_ref(), obligation)) - } - ty::PredicateKind::Subtype(..) => None, - ty::PredicateKind::Coerce(..) => None, - ty::PredicateKind::RegionOutlives(..) => None, - ty::PredicateKind::TypeOutlives(..) => None, - ty::PredicateKind::WellFormed(..) => None, - ty::PredicateKind::ObjectSafe(..) => None, - ty::PredicateKind::ConstEvaluatable(..) => None, - ty::PredicateKind::ConstEquate(..) => None, - // N.B., this predicate is created by breaking down a - // `ClosureType: FnFoo()` predicate, where - // `ClosureType` represents some `Closure`. It can't - // possibly be referring to the current closure, - // because we haven't produced the `Closure` for - // this closure yet; this is exactly why the other - // code is looking for a self type of an unresolved - // inference variable. - ty::PredicateKind::ClosureKind(..) => None, - ty::PredicateKind::TypeWellFormedFromEnv(..) => None, + self.fulfillment_cx.borrow().pending_obligations().into_iter().filter_map( + move |obligation| match &obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Projection(data)) + if self.self_type_matches_expected_vid( + data.projection_ty.self_ty(), + ty_var_root, + ) => + { + Some(obligation) } - }) - .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root)) + ty::PredicateKind::Clause(ty::Clause::Trait(data)) + if self.self_type_matches_expected_vid(data.self_ty(), ty_var_root) => + { + Some(obligation) + } + + ty::PredicateKind::Clause(ty::Clause::Trait(..)) + | ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) + | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + // N.B., this predicate is created by breaking down a + // `ClosureType: FnFoo()` predicate, where + // `ClosureType` represents some `Closure`. It can't + // possibly be referring to the current closure, + // because we haven't produced the `Closure` for + // this closure yet; this is exactly why the other + // code is looking for a self type of an unresolved + // inference variable. + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Ambiguous + | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, + }, + ) } pub(in super::super) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool { - self.obligations_for_self_ty(self_ty) - .any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait()) + let sized_did = self.tcx.lang_items().sized_trait(); + self.obligations_for_self_ty(self_ty).any(|obligation| { + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Trait(data)) => { + Some(data.def_id()) == sized_did + } + _ => false, + } + }) } pub(in super::super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> { @@ -769,34 +726,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expect_args = self .fudge_inference_if_ok(|| { + let ocx = ObligationCtxt::new_in_snapshot(self); + // Attempt to apply a subtyping relationship between the formal // return type (likely containing type variables if the function // is polymorphic) and the expected return type. // No argument expectations are produced if unification fails. let origin = self.misc(call_span); - let ures = self.at(&origin, self.param_env).sup(ret_ty, formal_ret); - - // FIXME(#27336) can't use ? here, Try::from_error doesn't default - // to identity so the resulting type is not constrained. - match ures { - Ok(ok) => { - // Process any obligations locally as much as - // we can. We don't care if some things turn - // out unconstrained or ambiguous, as we're - // just trying to get hints here. - let errors = self.save_and_restore_in_snapshot_flag(|_| { - let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx); - for obligation in ok.obligations { - fulfill.register_predicate_obligation(self, obligation); - } - fulfill.select_where_possible(self) - }); - - if !errors.is_empty() { - return Err(()); - } - } - Err(_) => return Err(()), + ocx.sup(&origin, self.param_env, ret_ty, formal_ret)?; + if !ocx.select_where_possible().is_empty() { + return Err(TypeError::Mismatch); } // Record all the argument types, with the substitutions @@ -1132,7 +1071,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Res::Local(hid) = res { let ty = self.local_ty(span, hid).decl_ty; - let ty = self.normalize_associated_types_in(span, ty); + let ty = self.normalize(span, ty); self.write_ty(hir_id, ty); return (ty, res); } @@ -1173,9 +1112,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { explicit_late_bound = ExplicitLateBound::Yes; } - if let Err(GenericArgCountMismatch { reported: Some(_), .. }) = arg_count.correct { + if let Err(GenericArgCountMismatch { reported: Some(e), .. }) = arg_count.correct { infer_args_for_err.insert(index); - self.set_tainted_by_errors(); // See issue #53251. + self.set_tainted_by_errors(e); // See issue #53251. } } @@ -1189,11 +1128,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *ty.kind() { ty::Adt(adt_def, substs) if adt_def.has_ctor() => { let variant = adt_def.non_enum_variant(); - let ctor_def_id = variant.ctor_def_id.unwrap(); - ( - Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id), - Some(substs), - ) + let (ctor_kind, ctor_def_id) = variant.ctor.unwrap(); + (Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id), Some(substs)) } _ => { let mut err = tcx.sess.struct_span_err( @@ -1215,9 +1151,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - err.emit(); - - return (tcx.ty_error(), res); + let reported = err.emit(); + return (tcx.ty_error_with_guaranteed(reported), res); } } } else { @@ -1466,12 +1401,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !ty.is_ty_var() { ty } else { - if !self.is_tainted_by_errors() { + let e = self.tainted_by_errors().unwrap_or_else(|| { self.err_ctxt() .emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true) - .emit(); - } - let err = self.tcx.ty_error(); + .emit() + }); + let err = self.tcx.ty_error_with_guaranteed(e); self.demand_suptype(sp, err, ty); err } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 8e0fcb56c..60fec05d3 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -73,7 +73,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.typeck_results.borrow().expr_ty_adjusted(expr); let ty = self.resolve_vars_if_possible(ty); if ty.has_non_region_infer() { - assert!(self.is_tainted_by_errors()); self.tcx.ty_error() } else { self.tcx.erase_regions(ty) @@ -136,6 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_arguments, Some(method.def_id), ); + method.sig.output() } @@ -214,7 +214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "cannot use call notation; the first type parameter \ for the function trait is neither a tuple nor unit" ) - .emit(); + .delay_as_bug(); (self.err_args(provided_args.len()), None) } } @@ -344,7 +344,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // an "opportunistic" trait resolution of any trait bounds on // the call. This helps coercions. if check_closures { - self.select_obligations_where_possible(false, |_| {}) + self.select_obligations_where_possible(|_| {}) } // Check each argument, to satisfy the input it was provided for @@ -511,8 +511,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var()) } - self.set_tainted_by_errors(); let tcx = self.tcx; + // FIXME: taint after emitting errors and pass through an `ErrorGuaranteed` + self.set_tainted_by_errors( + tcx.sess.delay_span_bug(call_span, "no errors reported for args"), + ); // Get the argument span in the context of the call span so that // suggestions and labels are (more) correct when an arg is a @@ -596,6 +599,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; + let mk_trace = |span, (formal_ty, expected_ty), provided_ty| { + let mismatched_ty = if expected_ty == provided_ty { + // If expected == provided, then we must have failed to sup + // the formal type. Avoid printing out "expected Ty, found Ty" + // in that case. + formal_ty + } else { + expected_ty + }; + TypeTrace::types(&self.misc(span), true, mismatched_ty, provided_ty) + }; + // The algorithm here is inspired by levenshtein distance and longest common subsequence. // We'll try to detect 4 different types of mistakes: // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs @@ -660,10 +675,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // A tuple wrap suggestion actually occurs within, // so don't do anything special here. err = self.err_ctxt().report_and_explain_type_error( - TypeTrace::types( - &self.misc(*lo), - true, - formal_and_expected_inputs[mismatch_idx.into()].1, + mk_trace( + *lo, + formal_and_expected_inputs[mismatch_idx.into()], provided_arg_tys[mismatch_idx.into()].0, ), terr, @@ -747,9 +761,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { errors.drain_filter(|error| { let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = error else { return false }; let (provided_ty, provided_span) = provided_arg_tys[*provided_idx]; - let (expected_ty, _) = formal_and_expected_inputs[*expected_idx]; - let cause = &self.misc(provided_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let trace = mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty); if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) { self.err_ctxt().report_and_explain_type_error(trace, *e).emit(); return true; @@ -773,8 +785,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx]; let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx]; - let cause = &self.misc(provided_arg_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let trace = mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty); let mut err = self.err_ctxt().report_and_explain_type_error(trace, *err); self.emit_coerce_suggestions( &mut err, @@ -846,8 +857,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; let (provided_ty, provided_span) = provided_arg_tys[provided_idx]; if let Compatibility::Incompatible(error) = compatibility { - let cause = &self.misc(provided_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let trace = mk_trace(provided_span, (formal_ty, expected_ty), provided_ty); if let Some(e) = error { self.err_ctxt().note_type_err( &mut err, @@ -1200,7 +1210,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); let variant = match def { Res::Err => { - self.set_tainted_by_errors(); + self.set_tainted_by_errors( + self.tcx.sess.delay_span_bug(path_span, "`Res::Err` but no error emitted"), + ); return None; } Res::Def(DefKind::Variant, _) => match ty.kind() { @@ -1334,7 +1346,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Hide the outer diverging and `has_errors` flags. let old_diverges = self.diverges.replace(Diverges::Maybe); - let old_has_errors = self.has_errors.replace(false); match stmt.kind { hir::StmtKind::Local(l) => { @@ -1364,7 +1375,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Combine the diverging and `has_error` flags. self.diverges.set(self.diverges.get() | old_diverges); - self.has_errors.set(self.has_errors.get() | old_has_errors); } pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { @@ -1383,8 +1393,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { blk: &'tcx hir::Block<'tcx>, expected: Expectation<'tcx>, ) -> Ty<'tcx> { - let prev = self.ps.replace(self.ps.get().recurse(blk)); - // In some cases, blocks have just one exit, but other blocks // can be targeted by multiple breaks. This can happen both // with labeled blocks as well as when we desugar @@ -1544,15 +1552,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.diverges.set(prev_diverges); } - let mut ty = ctxt.coerce.unwrap().complete(self); - - if self.has_errors.get() || ty.references_error() { - ty = self.tcx.ty_error() - } + let ty = ctxt.coerce.unwrap().complete(self); self.write_ty(blk.hir_id, ty); - self.ps.set(prev); ty } @@ -1728,22 +1731,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir = self.tcx.hir(); let hir::Node::Expr(expr) = hir.get(hir_id) else { return false; }; - // Skip over mentioning async lang item - if Some(def_id) == self.tcx.lang_items().from_generator_fn() - && error.obligation.cause.span.desugaring_kind() - == Some(rustc_span::DesugaringKind::Async) - { - return false; - } - let Some(unsubstituted_pred) = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx).predicates.into_iter().nth(idx) else { return false; }; let generics = self.tcx.generics_of(def_id); let predicate_substs = match unsubstituted_pred.kind().skip_binder() { - ty::PredicateKind::Trait(pred) => pred.trait_ref.substs, - ty::PredicateKind::Projection(pred) => pred.projection_ty.substs, + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => pred.trait_ref.substs, + ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => pred.projection_ty.substs, _ => ty::List::empty(), }; @@ -1920,7 +1915,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { receiver: Option<&'tcx hir::Expr<'tcx>>, args: &'tcx [hir::Expr<'tcx>], ) -> bool { - let sig = self.tcx.fn_sig(def_id).skip_binder(); + let ty = self.tcx.type_of(def_id); + if !ty.is_fn() { + return false; + } + let sig = ty.fn_sig(self.tcx).skip_binder(); let args_referencing_param: Vec<_> = sig .inputs() .iter() @@ -2091,7 +2090,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let maybe_trait_item_def_id = assoc_item.trait_item_def_id.unwrap_or(def_id) && let maybe_trait_def_id = self.tcx.parent(maybe_trait_item_def_id) // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce" - && let Some(call_kind) = ty::ClosureKind::from_def_id(self.tcx, maybe_trait_def_id) + && let Some(call_kind) = self.tcx.fn_trait_kind_from_def_id(maybe_trait_def_id) && let Some(callee_ty) = callee_ty { let callee_ty = callee_ty.peel_refs(); @@ -2115,9 +2114,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for (predicate, span) in std::iter::zip(instantiated.predicates, instantiated.spans) { - if let ty::PredicateKind::Trait(pred) = predicate.kind().skip_binder() + if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = predicate.kind().skip_binder() && pred.self_ty().peel_refs() == callee_ty - && ty::ClosureKind::from_def_id(self.tcx, pred.def_id()).is_some() + && self.tcx.is_fn_trait(pred.def_id()) { err.span_note(span, "callable defined here"); return; @@ -2148,13 +2147,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ); let obligation = traits::Obligation::new( + self.tcx, traits::ObligationCause::dummy(), self.param_env, - ty::Binder::dummy(ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity: ty::ImplPolarity::Positive, - }), + ty::Binder::dummy(trait_ref), ); match SelectionContext::new(&self).select(&obligation) { Ok(Some(traits::ImplSource::UserDefined(impl_source))) => { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 0c600daf4..30b59da78 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -4,10 +4,11 @@ mod checks; mod suggestions; pub use _impl::*; +use rustc_errors::ErrorGuaranteed; pub use suggestions::*; use crate::coercion::DynamicCoerceMany; -use crate::{Diverges, EnclosingBreakables, Inherited, UnsafetyState}; +use crate::{Diverges, EnclosingBreakables, Inherited}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir_analysis::astconv::AstConv; @@ -21,7 +22,7 @@ use rustc_middle::ty::{self, Const, Ty, TyCtxt}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::{self, Span}; -use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; +use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; use std::cell::{Cell, RefCell}; use std::ops::Deref; @@ -68,17 +69,11 @@ pub struct FnCtxt<'a, 'tcx> { /// any). pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>, - /// Used exclusively to reduce cost of advanced evaluation used for - /// more helpful diagnostics. - pub(super) in_tail_expr: bool, - /// First span of a return site that we find. Used in error messages. pub(super) ret_coercion_span: Cell<Option<Span>>, pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>, - pub(super) ps: Cell<UnsafetyState>, - /// Whether the last checked node generates a divergence (e.g., /// `return` will set this to `Always`). In general, when entering /// an expression or other node in the tree, the initial value @@ -112,21 +107,11 @@ pub struct FnCtxt<'a, 'tcx> { /// the diverges flag is set to something other than `Maybe`. pub(super) diverges: Cell<Diverges>, - /// Whether any child nodes have any type errors. - pub(super) has_errors: Cell<bool>, - pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>, pub(super) inh: &'a Inherited<'tcx>, - /// True if the function or closure's return type is known before - /// entering the function/closure, i.e. if the return type is - /// either given explicitly or inferred from, say, an `Fn*` trait - /// bound. Used for diagnostic purposes only. - pub(super) return_type_pre_known: bool, - - /// True if the return type has an Opaque type - pub(super) return_type_has_opaque: bool, + pub(super) fallback_has_occurred: Cell<bool>, } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -140,19 +125,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { param_env, err_count_on_creation: inh.tcx.sess.err_count(), ret_coercion: None, - in_tail_expr: false, ret_coercion_span: Cell::new(None), resume_yield_tys: None, - ps: Cell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)), diverges: Cell::new(Diverges::Maybe), - has_errors: Cell::new(false), enclosing_breakables: RefCell::new(EnclosingBreakables { stack: Vec::new(), by_id: Default::default(), }), inh, - return_type_pre_known: true, - return_type_has_opaque: false, + fallback_has_occurred: Cell::new(false), } } @@ -174,7 +155,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// [`InferCtxt::err_ctxt`]: infer::InferCtxt::err_ctxt pub fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> { - TypeErrCtxt { infcx: &self.infcx, typeck_results: Some(self.typeck_results.borrow()) } + TypeErrCtxt { + infcx: &self.infcx, + typeck_results: Some(self.typeck_results.borrow()), + fallback_has_occurred: self.fallback_has_occurred.get(), + normalize_fn_sig: Box::new(|fn_sig| { + if fn_sig.has_escaping_bound_vars() { + return fn_sig; + } + self.probe(|_| { + let ocx = ObligationCtxt::new_in_snapshot(self); + let normalized_fn_sig = + ocx.normalize(&ObligationCause::dummy(), self.param_env, fn_sig); + if ocx.select_all_or_error().is_empty() { + let normalized_fn_sig = self.resolve_vars_if_possible(normalized_fn_sig); + if !normalized_fn_sig.needs_infer() { + return normalized_fn_sig; + } + } + fn_sig + }) + }), + } } pub fn errors_reported_since_creation(&self) -> bool { @@ -194,8 +196,8 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { self.tcx } - fn item_def_id(&self) -> Option<DefId> { - None + fn item_def_id(&self) -> DefId { + self.body_id.owner.to_def_id() } fn get_type_parameter_bounds( @@ -213,7 +215,9 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { predicates: tcx.arena.alloc_from_iter( self.param_env.caller_bounds().iter().filter_map(|predicate| { match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(data) if data.self_ty().is_param(index) => { + ty::PredicateKind::Clause(ty::Clause::Trait(data)) + if data.self_ty().is_param(index) => + { // HACK(eddyb) should get the original `Span`. let span = tcx.def_span(def_id); Some((predicate, span)) @@ -298,12 +302,12 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { if ty.has_escaping_bound_vars() { ty // FIXME: normalization and escaping regions } else { - self.normalize_associated_types_in(span, ty) + self.normalize(span, ty) } } - fn set_tainted_by_errors(&self) { - self.infcx.set_tainted_by_errors() + fn set_tainted_by_errors(&self, e: ErrorGuaranteed) { + self.infcx.set_tainted_by_errors(e) } fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 4db9c56f9..4f92477b5 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -10,24 +10,35 @@ use rustc_hir::{ Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, }; use rustc_hir_analysis::astconv::AstConv; -use rustc_infer::infer::{self, TyCtxtInferExt}; +use rustc_infer::infer; use rustc_infer::traits::{self, StatementAsExpression}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Binder, IsSuggestable, ToPredicate, Ty}; +use rustc_middle::ty::{self, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; +use rustc_trait_selection::traits::NormalizeExt; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub(crate) fn body_fn_sig(&self) -> Option<ty::FnSig<'tcx>> { + self.typeck_results + .borrow() + .liberated_fn_sigs() + .get(self.tcx.hir().get_parent_node(self.body_id)) + .copied() + } + pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) { + // This suggestion is incorrect for + // fn foo() -> bool { match () { () => true } || match () { () => true } } err.span_suggestion_short( span.shrink_to_hi(), "consider using a semicolon here", ";", - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); } @@ -165,7 +176,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ty::Opaque(def_id, substs) => { self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { - if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder() && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() // args tuple will always be substs[1] && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() @@ -200,7 +211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Param(param) => { let def_id = self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx).def_id; self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| { - if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder() && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() && proj.projection_ty.self_ty() == found // args tuple will always be substs[1] @@ -237,7 +248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // implied by wf, but also because that would possibly result in // erroneous errors later on. let infer::InferOk { value: output, obligations: _ } = - self.normalize_associated_types_in_as_infer_ok(expr.span, output); + self.at(&self.misc(expr.span), self.param_env).normalize(output); if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) } } @@ -337,8 +348,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if annotation { let suggest_annotation = match expr.peel_drop_temps().kind { - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, _) => "&", - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) => "&mut ", + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, _) => mutbl.ref_prefix_str(), _ => return true, }; let mut tuple_indexes = Vec::new(); @@ -366,7 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let annotation_span = ty.span; err.span_suggestion( annotation_span.with_hi(annotation_span.lo()), - format!("alternatively, consider changing the type annotation"), + "alternatively, consider changing the type annotation", suggest_annotation, Applicability::MaybeIncorrect, ); @@ -456,7 +466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ref_cnt += 1; } if let ty::Adt(adt, _) = peeled.kind() - && self.tcx.is_diagnostic_item(sym::String, adt.did()) + && Some(adt.did()) == self.tcx.lang_items().string() { err.span_suggestion_verbose( expr.span.shrink_to_hi(), @@ -752,7 +762,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("suggest_missing_return_type: expected type {:?}", ty); let bound_vars = self.tcx.late_bound_vars(fn_id); let ty = Binder::bind_with_vars(ty, bound_vars); - let ty = self.normalize_associated_types_in(span, ty); + let ty = self.normalize(span, ty); let ty = self.tcx.erase_late_bound_regions(ty); if self.can_coerce(expected, ty) { err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected }); @@ -913,22 +923,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty); let bound_vars = self.tcx.late_bound_vars(fn_id); let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); - let ty = self.normalize_associated_types_in(expr.span, ty); let ty = match self.tcx.asyncness(fn_id.owner) { - hir::IsAsync::Async => { - let infcx = self.tcx.infer_ctxt().build(); - infcx - .get_impl_future_output_ty(ty) - .unwrap_or_else(|| { - span_bug!( - fn_decl.output.span(), - "failed to get output type of async function" - ) - }) - .skip_binder() - } + hir::IsAsync::Async => self.get_impl_future_output_ty(ty).unwrap_or_else(|| { + span_bug!(fn_decl.output.span(), "failed to get output type of async function") + }), hir::IsAsync::NotAsync => ty, }; + let ty = self.normalize(expr.span, ty); if self.can_coerce(found, ty) { err.multipart_suggestion( "you might have meant to return this value", @@ -1082,14 +1083,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into) && self.predicate_must_hold_modulo_regions(&traits::Obligation::new( + self.tcx, self.misc(expr.span), self.param_env, - ty::Binder::dummy(ty::TraitRef { - def_id: into_def_id, - substs: self.tcx.mk_substs_trait(expr_ty, &[expected_ty.into()]), - }) - .to_poly_trait_predicate() - .to_predicate(self.tcx), + ty::Binder::dummy(self.tcx.mk_trait_ref( + into_def_id, + [expr_ty, expected_ty] + )), )) { let sugg = if expr.precedence().order() >= PREC_POSTFIX { @@ -1108,6 +1108,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + /// When expecting a `bool` and finding an `Option`, suggests using `let Some(..)` or `.is_some()` + pub(crate) fn suggest_option_to_bool( + &self, + diag: &mut Diagnostic, + expr: &hir::Expr<'_>, + expr_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) -> bool { + if !expected_ty.is_bool() { + return false; + } + + let ty::Adt(def, _) = expr_ty.peel_refs().kind() else { return false; }; + if !self.tcx.is_diagnostic_item(sym::Option, def.did()) { + return false; + } + + let hir = self.tcx.hir(); + let cond_parent = hir.parent_iter(expr.hir_id).skip_while(|(_, node)| { + matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And) + }).next(); + // Don't suggest: + // `let Some(_) = a.is_some() && b` + // ++++++++++ + // since the user probably just misunderstood how `let else` + // and `&&` work together. + if let Some((_, hir::Node::Local(local))) = cond_parent + && let hir::PatKind::Path(qpath) | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind + && let hir::QPath::Resolved(None, path) = qpath + && let Some(did) = path.res.opt_def_id() + .and_then(|did| self.tcx.opt_parent(did)) + .and_then(|did| self.tcx.opt_parent(did)) + && self.tcx.is_diagnostic_item(sym::Option, did) + { + return false; + } + + diag.span_suggestion( + expr.span.shrink_to_hi(), + "use `Option::is_some` to test if the `Option` has a value", + ".is_some()", + Applicability::MachineApplicable, + ); + + true + } + /// Suggest wrapping the block in square brackets instead of curly braces /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`. pub(crate) fn suggest_block_to_brackets( @@ -1149,6 +1196,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + #[instrument(skip(self, err))] + pub(crate) fn suggest_floating_point_literal( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + expected_ty: Ty<'tcx>, + ) -> bool { + if !expected_ty.is_floating_point() { + return false; + } + match expr.kind { + ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => { + err.span_suggestion_verbose( + start.span.shrink_to_hi().with_hi(end.span.lo()), + "remove the unnecessary `.` operator for a floating point literal", + '.', + Applicability::MaybeIncorrect, + ); + true + } + ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => { + err.span_suggestion_verbose( + expr.span.with_lo(start.span.hi()), + "remove the unnecessary `.` operator for a floating point literal", + '.', + Applicability::MaybeIncorrect, + ); + true + } + ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => { + err.span_suggestion_verbose( + expr.span.until(end.span), + "remove the unnecessary `.` operator and add an integer part for a floating point literal", + "0.", + Applicability::MaybeIncorrect, + ); + true + } + _ => false, + } + } + fn is_loop(&self, id: hir::HirId) -> bool { let node = self.tcx.hir().get(id); matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. })) |