diff options
Diffstat (limited to 'compiler/rustc_infer')
40 files changed, 1046 insertions, 740 deletions
| diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 934ada9932e..9d40b3cba29 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -134,7 +134,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// response*, then we don't typically replace free regions, as they /// must have been introduced from other parts of the system. trait CanonicalizeRegionMode { - fn canonicalize_free_region( + fn canonicalize_free_region<'tcx>( &self, canonicalizer: &mut Canonicalizer<'_, 'tcx>, r: ty::Region<'tcx>, @@ -146,7 +146,7 @@ trait CanonicalizeRegionMode { struct CanonicalizeQueryResponse; impl CanonicalizeRegionMode for CanonicalizeQueryResponse { - fn canonicalize_free_region( + fn canonicalize_free_region<'tcx>( &self, canonicalizer: &mut Canonicalizer<'_, 'tcx>, r: ty::Region<'tcx>, @@ -203,7 +203,7 @@ impl CanonicalizeRegionMode for CanonicalizeQueryResponse { struct CanonicalizeUserTypeAnnotation; impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation { - fn canonicalize_free_region( + fn canonicalize_free_region<'tcx>( &self, canonicalizer: &mut Canonicalizer<'_, 'tcx>, r: ty::Region<'tcx>, @@ -226,7 +226,7 @@ impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation { struct CanonicalizeAllFreeRegions; impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions { - fn canonicalize_free_region( + fn canonicalize_free_region<'tcx>( &self, canonicalizer: &mut Canonicalizer<'_, 'tcx>, r: ty::Region<'tcx>, @@ -242,7 +242,7 @@ impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions { struct CanonicalizeFreeRegionsOtherThanStatic; impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic { - fn canonicalize_free_region( + fn canonicalize_free_region<'tcx>( &self, canonicalizer: &mut Canonicalizer<'_, 'tcx>, r: ty::Region<'tcx>, @@ -425,7 +425,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { // FIXME: perf problem described in #55921. ui = ty::UniverseIndex::ROOT; return self.canonicalize_const_var( - CanonicalVarInfo { kind: CanonicalVarKind::Const(ui) }, + CanonicalVarInfo { kind: CanonicalVarKind::Const(ui, ct.ty) }, ct, ); } @@ -470,7 +470,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { { let needs_canonical_flags = if canonicalize_region_mode.any() { TypeFlags::NEEDS_INFER | - TypeFlags::HAS_POTENTIAL_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_POTENTIAL_FREE_REGIONS` + TypeFlags::HAS_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_FREE_REGIONS` TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER } else { diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index 0c26639e9b0..2d2edb07d9e 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -137,12 +137,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { self.tcx.mk_region(ty::RePlaceholder(placeholder_mapped)).into() } - CanonicalVarKind::Const(ui) => self + CanonicalVarKind::Const(ui, ty) => self .next_const_var_in_universe( - self.next_ty_var_in_universe( - TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }, - universe_map(ui), - ), + ty, ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span }, universe_map(ui), ) diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 2296cc6129a..5b4a9d9dfad 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -108,7 +108,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { let tcx = self.tcx; // Select everything, returning errors. - let true_errors = fulfill_cx.select_where_possible(self).err().unwrap_or_else(Vec::new); + let true_errors = fulfill_cx.select_where_possible(self); debug!("true_errors = {:#?}", true_errors); if !true_errors.is_empty() { @@ -118,7 +118,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { } // Anything left unselected *now* must be an ambiguity. - let ambig_errors = fulfill_cx.select_all_or_error(self).err().unwrap_or_else(Vec::new); + let ambig_errors = fulfill_cx.select_all_or_error(self); debug!("ambig_errors = {:#?}", ambig_errors); let region_obligations = self.take_registered_region_obligations(); diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 09bfb3290f4..a77fd8fae8d 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -37,7 +37,7 @@ use crate::traits::{Obligation, PredicateObligations}; use rustc_data_structures::sso::SsoHashMap; use rustc_hir::def_id::DefId; use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeFoldable}; @@ -129,8 +129,6 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { where R: ConstEquateRelation<'tcx>, { - let a = self.tcx.expose_default_const_substs(a); - let b = self.tcx.expose_default_const_substs(b); debug!("{}.consts({:?}, {:?})", relation.tag(), a, b); if a == b { return Ok(a); @@ -533,7 +531,7 @@ struct Generalization<'tcx> { needs_wf: bool, } -impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { +impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } @@ -572,8 +570,9 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { // (e.g., #41849). relate::relate_substs(self, None, a_subst, b_subst) } else { - let opt_variances = self.tcx().variances_of(item_def_id); - relate::relate_substs(self, Some(&opt_variances), a_subst, b_subst) + let tcx = self.tcx(); + let opt_variances = tcx.variances_of(item_def_id); + relate::relate_substs(self, Some((item_def_id, &opt_variances)), a_subst, b_subst) } } @@ -745,9 +744,10 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { } } } - ty::ConstKind::Unevaluated(uv) if self.tcx().lazy_normalization() => { - assert_eq!(uv.promoted, None); - let substs = uv.substs(self.tcx()); + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) + if self.tcx().lazy_normalization() => + { + assert_eq!(promoted, None); let substs = self.relate_with_variance( ty::Variance::Invariant, ty::VarianceDiagInfo::default(), @@ -756,7 +756,7 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { )?; Ok(self.tcx().mk_const(ty::Const { ty: c.ty, - val: ty::ConstKind::Unevaluated(ty::Unevaluated::new(uv.def, substs)), + val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }), })) } _ => relate::super_relate_consts(self, c, c), @@ -790,7 +790,7 @@ pub fn const_unification_error<'tcx>( a_is_expected: bool, (a, b): (&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>), ) -> TypeError<'tcx> { - TypeError::ConstMismatch(ty::relate::expected_found_bool(a_is_expected, a, b)) + TypeError::ConstMismatch(ExpectedFound::new(a_is_expected, a, b)) } fn int_unification_error<'tcx>( @@ -798,7 +798,7 @@ fn int_unification_error<'tcx>( v: (ty::IntVarValue, ty::IntVarValue), ) -> TypeError<'tcx> { let (a, b) = v; - TypeError::IntMismatch(ty::relate::expected_found_bool(a_is_expected, a, b)) + TypeError::IntMismatch(ExpectedFound::new(a_is_expected, a, b)) } fn float_unification_error<'tcx>( @@ -806,7 +806,7 @@ fn float_unification_error<'tcx>( v: (ty::FloatVarValue, ty::FloatVarValue), ) -> TypeError<'tcx> { let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v; - TypeError::FloatMismatch(ty::relate::expected_found_bool(a_is_expected, a, b)) + TypeError::FloatMismatch(ExpectedFound::new(a_is_expected, a, b)) } struct ConstInferUnifier<'cx, 'tcx> { @@ -827,7 +827,7 @@ struct ConstInferUnifier<'cx, 'tcx> { // We use `TypeRelation` here to propagate `RelateResult` upwards. // // Both inputs are expected to be the same. -impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { +impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } @@ -990,9 +990,10 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { } } } - ty::ConstKind::Unevaluated(uv) if self.tcx().lazy_normalization() => { - assert_eq!(uv.promoted, None); - let substs = uv.substs(self.tcx()); + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) + if self.tcx().lazy_normalization() => + { + assert_eq!(promoted, None); let substs = self.relate_with_variance( ty::Variance::Invariant, ty::VarianceDiagInfo::default(), @@ -1001,7 +1002,7 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { )?; Ok(self.tcx().mk_const(ty::Const { ty: c.ty, - val: ty::ConstKind::Unevaluated(ty::Unevaluated::new(uv.def, substs)), + val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }), })) } _ => relate::super_relate_consts(self, c, c), diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs index cbc735c98a6..90c0ff9226f 100644 --- a/compiler/rustc_infer/src/infer/equate.rs +++ b/compiler/rustc_infer/src/infer/equate.rs @@ -23,7 +23,7 @@ impl<'combine, 'infcx, 'tcx> Equate<'combine, 'infcx, 'tcx> { } } -impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> { +impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> { fn tag(&self) -> &'static str { "Equate" } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 2173ff1f9ab..1eb8190bd7d 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -65,11 +65,11 @@ use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{Item, ItemKind, Node}; use rustc_middle::dep_graph::DepContext; -use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{ self, + error::TypeError, subst::{GenericArgKind, Subst, SubstsRef}, - Region, Ty, TyCtxt, TypeFoldable, + Binder, Region, Ty, TyCtxt, TypeFoldable, }; use rustc_span::{sym, BytePos, DesugaringKind, MultiSpan, Pos, Span}; use rustc_target::spec::abi; @@ -83,7 +83,7 @@ pub use need_type_info::TypeAnnotationNeeded; pub mod nice_region_error; -pub(super) fn note_and_explain_region( +pub(super) fn note_and_explain_region<'tcx>( tcx: TyCtxt<'tcx>, err: &mut DiagnosticBuilder<'_>, prefix: &str, @@ -116,7 +116,7 @@ pub(super) fn note_and_explain_region( emit_msg_span(err, prefix, description, span, suffix); } -fn explain_free_region( +fn explain_free_region<'tcx>( tcx: TyCtxt<'tcx>, err: &mut DiagnosticBuilder<'_>, prefix: &str, @@ -128,7 +128,7 @@ fn explain_free_region( label_msg_span(err, prefix, description, span, suffix); } -fn msg_span_from_free_region( +fn msg_span_from_free_region<'tcx>( tcx: TyCtxt<'tcx>, region: ty::Region<'tcx>, alt_span: Option<Span>, @@ -145,17 +145,16 @@ fn msg_span_from_free_region( } } -fn msg_span_from_early_bound_and_free_regions( +fn msg_span_from_early_bound_and_free_regions<'tcx>( tcx: TyCtxt<'tcx>, region: ty::Region<'tcx>, ) -> (String, Span) { let sm = tcx.sess.source_map(); - let scope = region.free_region_binding_scope(tcx); - let node = tcx.hir().local_def_id_to_hir_id(scope.expect_local()); + let scope = region.free_region_binding_scope(tcx).expect_local(); match *region { ty::ReEarlyBound(ref br) => { - let mut sp = sm.guess_head_span(tcx.hir().span(node)); + let mut sp = sm.guess_head_span(tcx.def_span(scope)); if let Some(param) = tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) { @@ -166,7 +165,7 @@ fn msg_span_from_early_bound_and_free_regions( ty::ReFree(ty::FreeRegion { bound_region: ty::BoundRegionKind::BrNamed(_, name), .. }) => { - let mut sp = sm.guess_head_span(tcx.hir().span(node)); + let mut sp = sm.guess_head_span(tcx.def_span(scope)); if let Some(param) = tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name)) { @@ -181,13 +180,13 @@ fn msg_span_from_early_bound_and_free_regions( } else { ( format!("the anonymous lifetime #{} defined here", idx + 1), - tcx.hir().span(node), + tcx.def_span(scope), ) } } _ => ( format!("the lifetime `{}` as defined here", region), - sm.guess_head_span(tcx.hir().span(node)), + sm.guess_head_span(tcx.def_span(scope)), ), }, _ => bug!(), @@ -226,7 +225,7 @@ fn label_msg_span( } } -pub fn unexpected_hidden_region_diagnostic( +pub fn unexpected_hidden_region_diagnostic<'tcx>( tcx: TyCtxt<'tcx>, span: Span, hidden_ty: Ty<'tcx>, @@ -275,7 +274,7 @@ pub fn unexpected_hidden_region_diagnostic( fn_returns, hidden_region.to_string(), None, - format!("captures {}", hidden_region), + format!("captures `{}`", hidden_region), None, ) } @@ -310,6 +309,34 @@ pub fn unexpected_hidden_region_diagnostic( err } +/// Structurally compares two types, modulo any inference variables. +/// +/// Returns `true` if two types are equal, or if one type is an inference variable compatible +/// with the other type. A TyVar inference type is compatible with any type, and an IntVar or +/// FloatVar inference type are compatible with themselves or their concrete types (Int and +/// Float types, respectively). When comparing two ADTs, these rules apply recursively. +pub fn same_type_modulo_infer<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { + match (&a.kind(), &b.kind()) { + (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => { + if did_a != did_b { + return false; + } + + substs_a.types().zip(substs_b.types()).all(|(a, b)| same_type_modulo_infer(a, b)) + } + (&ty::Int(_), &ty::Infer(ty::InferTy::IntVar(_))) + | (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Int(_) | &ty::Infer(ty::InferTy::IntVar(_))) + | (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_))) + | ( + &ty::Infer(ty::InferTy::FloatVar(_)), + &ty::Float(_) | &ty::Infer(ty::InferTy::FloatVar(_)), + ) + | (&ty::Infer(ty::InferTy::TyVar(_)), _) + | (_, &ty::Infer(ty::InferTy::TyVar(_))) => true, + _ => a == b, + } +} + impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn report_region_errors(&self, errors: &Vec<RegionResolutionError<'tcx>>) { debug!("report_region_errors(): {} errors to start", errors.len()); @@ -356,6 +383,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { sub_r, sup_origin, sup_r, + _, ) => { if sub_r.is_placeholder() { self.report_placeholder_failure(sub_origin, sub_r, sup_r).emit(); @@ -436,7 +464,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { errors.sort_by_key(|u| match *u { RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(), RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), - RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _) => rvo.span(), + RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(), RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(), }); errors @@ -575,7 +603,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>, terr: &TypeError<'tcx>, ) { - match cause.code { + match *cause.code() { ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => { let ty = self.resolve_vars_if_possible(root_ty); if ty.is_suggestable() { @@ -742,7 +770,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.suggest_boxing_for_return_impl_trait( err, ret_sp, - vec![then, else_sp].into_iter(), + [then, else_sp].into_iter(), ); } } @@ -752,7 +780,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } _ => { if let ObligationCauseCode::BindingObligation(_, binding_span) = - cause.code.peel_derives() + cause.code().peel_derives() { if matches!(terr, TypeError::RegionsPlaceholderMismatch) { err.span_note(*binding_span, "the lifetime requirement is introduced here"); @@ -778,11 +806,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); let sugg = arm_spans .flat_map(|sp| { - vec![ - (sp.shrink_to_lo(), "Box::new(".to_string()), - (sp.shrink_to_hi(), ")".to_string()), - ] - .into_iter() + [(sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string())] + .into_iter() }) .collect::<Vec<_>>(); err.multipart_suggestion( @@ -1401,6 +1426,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + /// Extend a type error with extra labels pointing at "non-trivial" types, like closures and + /// the return type of `async fn`s. + /// + /// `secondary_span` gives the caller the opportunity to expand `diag` with a `span_label`. + /// + /// `swap_secondary_and_primary` is used to make projection errors in particular nicer by using + /// the message in `secondary_span` as the primary label, and apply the message that would + /// otherwise be used for the primary label on the `secondary_span` `Span`. This applies on + /// E0271, like `src/test/ui/issues/issue-39970.stderr`. pub fn note_type_err( &self, diag: &mut DiagnosticBuilder<'tcx>, @@ -1408,6 +1442,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { secondary_span: Option<(Span, String)>, mut values: Option<ValuePairs<'tcx>>, terr: &TypeError<'tcx>, + swap_secondary_and_primary: bool, ) { let span = cause.span(self.tcx); debug!("note_type_err cause={:?} values={:?}, terr={:?}", cause, values, terr); @@ -1466,7 +1501,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let mut returned_async_output_error = false; for &sp in values { if sp.is_desugaring(DesugaringKind::Async) && !returned_async_output_error { - if &[sp] != err.span.primary_spans() { + if [sp] != err.span.primary_spans() { let mut span: MultiSpan = sp.into(); span.push_span_label( sp, @@ -1514,10 +1549,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } impl<'tcx> ty::fold::TypeVisitor<'tcx> for OpaqueTypesVisitor<'tcx> { - fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> { - Some(self.tcx) - } - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { if let Some((kind, def_id)) = TyCategory::from_ty(self.tcx, t) { let span = self.tcx.def_span(def_id); @@ -1584,9 +1615,32 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { match terr { TypeError::ObjectUnsafeCoercion(_) => {} _ => { - diag.span_label(span, terr.to_string()); + let mut label_or_note = |span: Span, msg: &str| { + if &[span] == diag.span.primary_spans() { + diag.span_label(span, msg); + } else { + diag.span_note(span, msg); + } + }; if let Some((sp, msg)) = secondary_span { - diag.span_label(sp, msg); + if swap_secondary_and_primary { + let terr = if let Some(infer::ValuePairs::Types(infer::ExpectedFound { + expected, + .. + })) = values + { + format!("expected this to be `{}`", expected) + } else { + terr.to_string() + }; + label_or_note(sp, &terr); + label_or_note(span, &msg); + } else { + label_or_note(span, &terr.to_string()); + label_or_note(sp, &msg); + } + } else { + label_or_note(span, &terr.to_string()); } } }; @@ -1667,11 +1721,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } _ => exp_found, }; - debug!("exp_found {:?} terr {:?}", exp_found, terr); + debug!("exp_found {:?} terr {:?} cause.code {:?}", exp_found, terr, cause.code()); if let Some(exp_found) = exp_found { - self.suggest_as_ref_where_appropriate(span, &exp_found, diag); - self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); - self.suggest_await_on_expect_found(cause, span, &exp_found, diag); + let should_suggest_fixes = if let ObligationCauseCode::Pattern { root_ty, .. } = + cause.code() + { + // Skip if the root_ty of the pattern is not the same as the expected_ty. + // If these types aren't equal then we've probably peeled off a layer of arrays. + same_type_modulo_infer(self.resolve_vars_if_possible(*root_ty), exp_found.expected) + } else { + true + }; + + if should_suggest_fixes { + self.suggest_as_ref_where_appropriate(span, &exp_found, diag); + self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); + self.suggest_await_on_expect_found(cause, span, &exp_found, diag); + } } // In some (most?) cases cause.body_id points to actual body, but in some cases @@ -1688,8 +1754,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values { if let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind() { if let Some(def_id) = def_id.as_local() { - let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); - let span = self.tcx.hir().span(hir_id); + let span = self.tcx.def_span(def_id); diag.span_note(span, "this closure does not fulfill the lifetime requirements"); } } @@ -1700,29 +1765,30 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.note_error_origin(diag, cause, exp_found, terr); } - pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { + pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Binder<'tcx, Ty<'tcx>>> { 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_items(future_trait) - .in_definition_order() - .next() - .unwrap() - .def_id; + let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; let bounds = self.tcx.explicit_item_bounds(*def_id); for (predicate, _) in bounds { let predicate = predicate.subst(self.tcx, substs); - if let ty::PredicateKind::Projection(projection_predicate) = - predicate.kind().skip_binder() - { - if projection_predicate.projection_ty.item_def_id == item_def_id { - // We don't account for multiple `Future::Output = Ty` contraints. - return Some(projection_predicate.ty); - } + 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` contraints. + return output; } } } @@ -1759,15 +1825,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { exp_span, exp_found.expected, exp_found.found, ); - if let ObligationCauseCode::CompareImplMethodObligation { .. } = &cause.code { + if let ObligationCauseCode::CompareImplMethodObligation { .. } = cause.code() { return; } match ( - self.get_impl_future_output_ty(exp_found.expected), - self.get_impl_future_output_ty(exp_found.found), + self.get_impl_future_output_ty(exp_found.expected).map(Binder::skip_binder), + self.get_impl_future_output_ty(exp_found.found).map(Binder::skip_binder), ) { - (Some(exp), Some(found)) if ty::TyS::same_type(exp, found) => match &cause.code { + (Some(exp), Some(found)) if same_type_modulo_infer(exp, found) => match cause.code() { ObligationCauseCode::IfExpression(box IfExpressionCause { then, .. }) => { diag.multipart_suggestion( "consider `await`ing on both `Future`s", @@ -1799,32 +1865,39 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { diag.help("consider `await`ing on both `Future`s"); } }, - (_, Some(ty)) if ty::TyS::same_type(exp_found.expected, ty) => { - let span = match cause.code { - // scrutinee's span - ObligationCauseCode::Pattern { span: Some(span), .. } => span, - _ => exp_span, - }; - diag.span_suggestion_verbose( - span.shrink_to_hi(), - "consider `await`ing on the `Future`", - ".await".to_string(), - Applicability::MaybeIncorrect, - ); - } - (Some(ty), _) if ty::TyS::same_type(ty, exp_found.found) => { - let span = match cause.code { - // scrutinee's span - ObligationCauseCode::Pattern { span: Some(span), .. } => span, - _ => exp_span, - }; + (_, Some(ty)) if same_type_modulo_infer(exp_found.expected, ty) => { diag.span_suggestion_verbose( - span.shrink_to_hi(), + exp_span.shrink_to_hi(), "consider `await`ing on the `Future`", ".await".to_string(), Applicability::MaybeIncorrect, ); } + (Some(ty), _) if same_type_modulo_infer(ty, exp_found.found) => match cause.code() { + ObligationCauseCode::Pattern { span: Some(span), .. } + | ObligationCauseCode::IfExpression(box IfExpressionCause { then: span, .. }) => { + diag.span_suggestion_verbose( + span.shrink_to_hi(), + "consider `await`ing on the `Future`", + ".await".to_string(), + Applicability::MaybeIncorrect, + ); + } + ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { + ref prior_arms, + .. + }) => { + diag.multipart_suggestion_verbose( + "consider `await`ing on the `Future`", + prior_arms + .iter() + .map(|arm| (arm.shrink_to_hi(), ".await".to_string())) + .collect(), + Applicability::MaybeIncorrect, + ); + } + _ => {} + }, _ => {} } } @@ -1849,10 +1922,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .fields .iter() .filter(|field| field.vis.is_accessible_from(field.did, self.tcx)) - .map(|field| (field.ident.name, field.ty(self.tcx, expected_substs))) - .find(|(_, ty)| ty::TyS::same_type(ty, exp_found.found)) + .map(|field| (field.name, field.ty(self.tcx, expected_substs))) + .find(|(_, ty)| same_type_modulo_infer(ty, exp_found.found)) { - if let ObligationCauseCode::Pattern { span: Some(span), .. } = cause.code { + if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { let suggestion = if expected_def.is_struct() { format!("{}.{}", snippet, name) @@ -1915,7 +1988,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { | (_, ty::Infer(_)) | (ty::Param(_), _) | (ty::Infer(_), _) => {} - _ if ty::TyS::same_type(exp_ty, found_ty) => {} + _ if same_type_modulo_infer(exp_ty, found_ty) => {} _ => show_suggestion = false, }; } @@ -1966,11 +2039,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let ValuePairs::Types(ty::error::ExpectedFound { expected, found }) = trace.values { - // If a tuple of length one was expected and the found expression has - // parentheses around it, perhaps the user meant to write `(expr,)` to - // build a tuple (issue #86100) match (expected.kind(), found.kind()) { (ty::Tuple(_), ty::Tuple(_)) => {} + // If a tuple of length one was expected and the found expression has + // parentheses around it, perhaps the user meant to write `(expr,)` to + // build a tuple (issue #86100) (ty::Tuple(_), _) if expected.tuple_fields().count() == 1 => { if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) { if let Some(code) = @@ -1985,11 +2058,46 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } } + // If a character was expected and the found expression is a string literal + // containing a single character, perhaps the user meant to write `'c'` to + // specify a character literal (issue #92479) + (ty::Char, ty::Ref(_, r, _)) if r.is_str() => { + if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) { + if let Some(code) = + code.strip_prefix('"').and_then(|s| s.strip_suffix('"')) + { + if code.chars().nth(1).is_none() { + err.span_suggestion( + span, + "if you meant to write a `char` literal, use single quotes", + format!("'{}'", code), + Applicability::MachineApplicable, + ); + } + } + } + } + // If a string was expected and the found expression is a character literal, + // perhaps the user meant to write `"s"` to specify a string literal. + (ty::Ref(_, r, _), ty::Char) if r.is_str() => { + if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) { + if let Some(code) = + code.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) + { + err.span_suggestion( + span, + "if you meant to write a `str` literal, use double quotes", + format!("\"{}\"", code), + Applicability::MachineApplicable, + ); + } + } + } _ => {} } } if let MatchExpressionArm(box MatchExpressionArmCause { source, .. }) = - trace.cause.code + *trace.cause.code() { if let hir::MatchSource::TryDesugar = source { if let Some((expected_ty, found_ty)) = self.values_str(trace.values) { @@ -2007,7 +2115,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str) } }; - self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr); + self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false); diag } @@ -2105,17 +2213,26 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let Some(Node::Item(Item { kind: ItemKind::Trait(..) | ItemKind::Impl { .. }, .. - })) = hir.find(parent_id) + })) = hir.find_by_def_id(parent_id) { - Some(self.tcx.generics_of(hir.local_def_id(parent_id).to_def_id())) + Some(self.tcx.generics_of(parent_id)) } else { None }, self.tcx.generics_of(owner.to_def_id()), + hir.span(hir_id), ) }); + + let span = match generics { + // This is to get around the trait identity obligation, that has a `DUMMY_SP` as signal + // for other diagnostics, so we need to recover it here. + Some((_, _, node)) if span.is_dummy() => node, + _ => span, + }; + let type_param_span = match (generics, bound_kind) { - (Some((_, ref generics)), GenericKind::Param(ref param)) => { + (Some((_, ref generics, _)), GenericKind::Param(ref param)) => { // Account for the case where `param` corresponds to `Self`, // which doesn't have the expected type argument. if !(generics.has_self && param.index == 0) { @@ -2129,7 +2246,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let Node::GenericParam(param) = hir.get(id) { has_bounds = !param.bounds.is_empty(); } - let sp = hir.span(id); + let sp = self.tcx.def_span(def_id); // `sp` only covers `T`, change it so that it covers // `T:` when appropriate let is_impl_trait = bound_kind.to_string().starts_with("impl "); @@ -2152,7 +2269,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }; let new_lt = generics .as_ref() - .and_then(|(parent_g, g)| { + .and_then(|(parent_g, g, _)| { let mut possible = (b'a'..=b'z').map(|c| format!("'{}", c as char)); let mut lts_names = g .params @@ -2168,20 +2285,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .map(|p| p.name.as_str()), ); } - let lts = lts_names.iter().map(|s| -> &str { &*s }).collect::<Vec<_>>(); - possible.find(|candidate| !lts.contains(&candidate.as_str())) + possible.find(|candidate| !lts_names.contains(&&candidate[..])) }) .unwrap_or("'lt".to_string()); let add_lt_sugg = generics .as_ref() - .and_then(|(_, g)| g.params.first()) + .and_then(|(_, g, _)| g.params.first()) .and_then(|param| param.def_id.as_local()) - .map(|def_id| { - ( - hir.span(hir.local_def_id_to_hir_id(def_id)).shrink_to_lo(), - format!("{}, ", new_lt), - ) - }); + .map(|def_id| (self.tcx.def_span(def_id).shrink_to_lo(), format!("{}, ", new_lt))); let labeled_user_string = match bound_kind { GenericKind::Param(ref p) => format!("the parameter type `{}`", p), @@ -2190,14 +2301,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let Some(SubregionOrigin::CompareImplMethodObligation { span, - item_name, impl_item_def_id, trait_item_def_id, }) = origin { return self.report_extra_impl_obligation( span, - item_name, impl_item_def_id, trait_item_def_id, &format!("`{}: {}`", bound_kind, sub), @@ -2530,7 +2639,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { infer::MiscVariable(_) => String::new(), infer::PatternRegion(_) => " for pattern".to_string(), infer::AddrOfRegion(_) => " for borrow expression".to_string(), - infer::Autoref(_, _) => " for autoref".to_string(), + infer::Autoref(_) => " for autoref".to_string(), infer::Coercion(_) => " for automatic coercion".to_string(), infer::LateBoundRegion(_, br, infer::FnCall) => { format!(" for lifetime parameter {}in function call", br_string(br)) @@ -2541,7 +2650,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { infer::LateBoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!( " for lifetime parameter {}in trait containing associated type `{}`", br_string(br), - self.tcx.associated_item(def_id).ident + self.tcx.associated_item(def_id).name ), infer::EarlyBoundRegion(_, name) => format!(" for lifetime parameter `{}`", name), infer::UpvarRegion(ref upvar_id, _) => { @@ -2578,7 +2687,7 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> { fn as_failure_code(&self, terr: &TypeError<'tcx>) -> FailureCode { use self::FailureCode::*; use crate::traits::ObligationCauseCode::*; - match self.code { + match self.code() { CompareImplMethodObligation { .. } => Error0308("method not compatible with trait"), CompareImplTypeObligation { .. } => Error0308("type not compatible with trait"), MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => { @@ -2613,7 +2722,7 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> { fn as_requirement_str(&self) -> &'static str { use crate::traits::ObligationCauseCode::*; - match self.code { + match self.code() { CompareImplMethodObligation { .. } => "method type is compatible with trait", CompareImplTypeObligation { .. } => "associated type is compatible with trait", ExprAssignable => "expression is assignable", diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index e00003face9..4c93ec7ab18 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -1,19 +1,18 @@ use crate::infer::type_variable::TypeVariableOriginKind; -use crate::infer::InferCtxt; +use crate::infer::{InferCtxt, Symbol}; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, Pat}; -use rustc_middle::hir::map::Map; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, MatchSource, Pat}; +use rustc_middle::hir::nested_filter; use rustc_middle::infer::unify_key::ConstVariableOriginKind; use rustc_middle::ty::print::Print; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, DefIdTree, InferConst, Ty, TyCtxt}; -use rustc_span::source_map::DesugaringKind; +use rustc_middle::ty::{self, Const, DefIdTree, InferConst, Ty, TyCtxt, TypeFoldable, TypeFolder}; use rustc_span::symbol::kw; -use rustc_span::Span; +use rustc_span::{sym, Span}; use std::borrow::Cow; struct FindHirNodeVisitor<'a, 'tcx> { @@ -26,6 +25,7 @@ struct FindHirNodeVisitor<'a, 'tcx> { found_closure: Option<&'tcx Expr<'tcx>>, found_method_call: Option<&'tcx Expr<'tcx>>, found_exact_method_call: Option<&'tcx Expr<'tcx>>, + found_for_loop_iter: Option<&'tcx Expr<'tcx>>, found_use_diagnostic: Option<UseDiagnostic<'tcx>>, } @@ -41,6 +41,7 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> { found_closure: None, found_method_call: None, found_exact_method_call: None, + found_for_loop_iter: None, found_use_diagnostic: None, } } @@ -51,7 +52,7 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> { fn node_ty_contains_target(&self, hir_id: HirId) -> Option<Ty<'tcx>> { self.node_type_opt(hir_id).map(|ty| self.infcx.resolve_vars_if_possible(ty)).filter(|ty| { - ty.walk(self.infcx.tcx).any(|inner| { + ty.walk().any(|inner| { inner == self.target || match (inner.unpack(), self.target.unpack()) { (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => { @@ -82,10 +83,10 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> { } impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> { - type Map = Map<'tcx>; + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::OnlyBodies(self.infcx.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.infcx.tcx.hir() } fn visit_local(&mut self, local: &'tcx Local<'tcx>) { @@ -111,8 +112,17 @@ impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> { } fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if let ExprKind::MethodCall(_, call_span, exprs, _) = expr.kind { - if call_span == self.target_span + if let ExprKind::Match(scrutinee, [_, arm], MatchSource::ForLoopDesugar) = expr.kind { + if let Some(pat) = arm.pat.for_loop_some() { + if let Some(ty) = self.node_ty_contains_target(pat.hir_id) { + self.found_for_loop_iter = Some(scrutinee); + self.found_node_ty = Some(ty); + return; + } + } + } + if let ExprKind::MethodCall(segment, exprs, _) = expr.kind { + if segment.ident.span == self.target_span && Some(self.target) == self.infcx.in_progress_typeck_results.and_then(|typeck_results| { typeck_results @@ -295,6 +305,15 @@ pub enum UnderspecifiedArgKind { Const { is_parameter: bool }, } +impl UnderspecifiedArgKind { + fn descr(&self) -> &'static str { + match self { + Self::Type { .. } => "type", + Self::Const { .. } => "const", + } + } +} + impl InferenceDiagnosticsData { /// Generate a label for a generic argument which can't be inferred. When not /// much is known about the argument, `use_diag` may be used to describe the @@ -390,36 +409,73 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } GenericArgKind::Const(ct) => { - if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.val { - let origin = - self.inner.borrow_mut().const_unification_table().probe_value(vid).origin; - if let ConstVariableOriginKind::ConstParameterDefinition(name, def_id) = - origin.kind - { - return InferenceDiagnosticsData { - name: name.to_string(), + match ct.val { + ty::ConstKind::Infer(InferConst::Var(vid)) => { + let origin = self + .inner + .borrow_mut() + .const_unification_table() + .probe_value(vid) + .origin; + if let ConstVariableOriginKind::ConstParameterDefinition(name, def_id) = + origin.kind + { + return InferenceDiagnosticsData { + name: name.to_string(), + span: Some(origin.span), + kind: UnderspecifiedArgKind::Const { is_parameter: true }, + parent: InferenceDiagnosticsParentData::for_def_id( + self.tcx, def_id, + ), + }; + } + + debug_assert!(!origin.span.is_dummy()); + let mut s = String::new(); + let mut printer = + ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::ValueNS); + if let Some(highlight) = highlight { + printer.region_highlight_mode = highlight; + } + let _ = ct.print(printer); + InferenceDiagnosticsData { + name: s, span: Some(origin.span), - kind: UnderspecifiedArgKind::Const { is_parameter: true }, - parent: InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id), - }; + kind: UnderspecifiedArgKind::Const { is_parameter: false }, + parent: None, + } } + ty::ConstKind::Unevaluated(ty::Unevaluated { substs, .. }) => { + assert!(substs.has_infer_types_or_consts()); - debug_assert!(!origin.span.is_dummy()); - let mut s = String::new(); - let mut printer = - ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::ValueNS); - if let Some(highlight) = highlight { - printer.region_highlight_mode = highlight; + // FIXME: We only use the first inference variable we encounter in + // `substs` here, this gives insufficiently informative diagnostics + // in case there are multiple inference variables + for s in substs.iter() { + match s.unpack() { + GenericArgKind::Type(t) => match t.kind() { + ty::Infer(_) => { + return self.extract_inference_diagnostics_data(s, None); + } + _ => {} + }, + GenericArgKind::Const(c) => match c.val { + ty::ConstKind::Infer(InferConst::Var(_)) => { + return self.extract_inference_diagnostics_data(s, None); + } + _ => {} + }, + _ => {} + } + } + bug!( + "expected an inference variable in substs of unevaluated const {:?}", + ct + ); } - let _ = ct.print(printer); - InferenceDiagnosticsData { - name: s, - span: Some(origin.span), - kind: UnderspecifiedArgKind::Const { is_parameter: false }, - parent: None, + _ => { + bug!("unexpect const: {:?}", ct); } - } else { - bug!("unexpect const: {:?}", ct); } } GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), @@ -475,18 +531,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // 3 | let _ = x.sum() as f64; // | ^^^ cannot infer type for `S` span - } else if let Some(ExprKind::MethodCall(_, call_span, _, _)) = + } else if let Some(ExprKind::MethodCall(segment, ..)) = local_visitor.found_method_call.map(|e| &e.kind) { // Point at the call instead of the whole expression: // error[E0284]: type annotations needed // --> file.rs:2:5 // | - // 2 | vec![Ok(2)].into_iter().collect()?; - // | ^^^^^^^ cannot infer type + // 2 | [Ok(2)].into_iter().collect()?; + // | ^^^^^^^ cannot infer type // | // = note: cannot resolve `<_ as std::ops::Try>::Ok == _` - if span.contains(*call_span) { *call_span } else { span } + if span.contains(segment.ident.span) { segment.ident.span } else { span } } else { span }; @@ -538,6 +594,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + let param_type = arg_data.kind.descr(); let suffix = match local_visitor.found_node_ty { Some(ty) if ty.is_closure() => { let substs = @@ -576,13 +633,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } Some(ty) if is_named_and_not_impl_trait(ty) && arg_data.name == "_" => { let ty = ty_to_string(ty); - format!("the explicit type `{}`, with the type parameters specified", ty) + format!("the explicit type `{}`, with the {} parameters specified", ty, param_type) } Some(ty) if is_named_and_not_impl_trait(ty) && ty.to_string() != arg_data.name => { + let ty = ResolvedTypeParamEraser::new(self.tcx).fold_ty(ty); + let ty = ErrTypeParamEraser(self.tcx).fold_ty(ty); let ty = ty_to_string(ty); format!( - "the explicit type `{}`, where the type parameter `{}` is specified", - ty, arg_data.name, + "the explicit type `{}`, where the {} parameter `{}` is specified", + ty, param_type, arg_data.name, ) } _ => "a type".to_string(), @@ -643,17 +702,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let msg = if let Some(simple_ident) = pattern.simple_ident() { match pattern.span.desugaring_kind() { None => format!("consider giving `{}` {}", simple_ident, suffix), - Some(DesugaringKind::ForLoop(_)) => { - "the element type for this iterator is not specified".to_string() - } - _ => format!("this needs {}", suffix), + Some(_) => format!("this needs {}", suffix), } } else { format!("consider giving this pattern {}", suffix) }; err.span_label(pattern.span, msg); } else if let Some(e) = local_visitor.found_method_call { - if let ExprKind::MethodCall(segment, _, exprs, _) = &e.kind { + if let ExprKind::MethodCall(segment, exprs, _) = &e.kind { // Suggest impl candidates: // // error[E0283]: type annotations needed @@ -719,6 +775,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // = note: type must be known at this point self.annotate_method_call(segment, e, &mut err); } + } else if let Some(scrutinee) = local_visitor.found_for_loop_iter { + err.span_label( + scrutinee.span, + "the element type for this iterator is not specified".to_string(), + ); } // Instead of the following: // error[E0282]: type annotations needed @@ -856,3 +917,117 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err } } + +/// Turn *resolved* type params into `[type error]` to signal we don't want to display them. After +/// performing that replacement, we'll turn all remaining infer type params to use their name from +/// their definition, and replace all the `[type error]`s back to being infer so they display in +/// the output as `_`. If we didn't go through `[type error]`, we would either show all type params +/// by their name *or* `_`, neither of which is desireable: we want to show all types that we could +/// infer as `_` to reduce verbosity and avoid telling the user about unnecessary type annotations. +struct ResolvedTypeParamEraser<'tcx> { + tcx: TyCtxt<'tcx>, + level: usize, +} + +impl<'tcx> ResolvedTypeParamEraser<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> Self { + ResolvedTypeParamEraser { tcx, level: 0 } + } + + /// Replace not yet inferred const params with their def name. + fn replace_infers(&self, c: &'tcx Const<'tcx>, index: u32, name: Symbol) -> &'tcx Const<'tcx> { + match c.val { + ty::ConstKind::Infer(..) => self.tcx().mk_const_param(index, name, c.ty), + _ => c, + } + } +} + +impl<'tcx> TypeFolder<'tcx> for ResolvedTypeParamEraser<'tcx> { + fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { + self.tcx + } + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + self.level += 1; + let t = match t.kind() { + // We'll hide this type only if all its type params are hidden as well. + ty::Adt(def, substs) => { + let generics = self.tcx().generics_of(def.did); + // Account for params with default values, like `Vec`, where we + // want to show `Vec<T>`, not `Vec<T, _>`. If we replaced that + // subst, then we'd get the incorrect output, so we passthrough. + let substs: Vec<_> = substs + .iter() + .zip(generics.params.iter()) + .map(|(subst, param)| match &(subst.unpack(), ¶m.kind) { + (_, ty::GenericParamDefKind::Type { has_default: true, .. }) => subst, + (crate::infer::GenericArgKind::Const(c), _) => { + self.replace_infers(c, param.index, param.name).into() + } + _ => subst.super_fold_with(self), + }) + .collect(); + let should_keep = |subst: &GenericArg<'_>| match subst.unpack() { + ty::subst::GenericArgKind::Type(t) => match t.kind() { + ty::Error(_) => false, + _ => true, + }, + // Account for `const` params here, otherwise `doesnt_infer.rs` + // shows `_` instead of `Foo<{ _: u32 }>` + ty::subst::GenericArgKind::Const(_) => true, + _ => false, + }; + if self.level == 1 || substs.iter().any(should_keep) { + let substs = self.tcx().intern_substs(&substs[..]); + self.tcx().mk_ty(ty::Adt(def, substs)) + } else { + self.tcx().ty_error() + } + } + ty::Ref(_, ty, _) => { + let ty = self.fold_ty(ty); + match ty.kind() { + // Avoid `&_`, these can be safely presented as `_`. + ty::Error(_) => self.tcx().ty_error(), + _ => t.super_fold_with(self), + } + } + // We could account for `()` if we wanted to replace it, but it's assured to be short. + ty::Tuple(_) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Opaque(..) + | ty::Projection(_) + | ty::Never => t.super_fold_with(self), + ty::Array(ty, c) => { + self.tcx().mk_ty(ty::Array(self.fold_ty(ty), self.replace_infers(c, 0, sym::N))) + } + // We don't want to hide type params that haven't been resolved yet. + // This would be the type that will be written out with the type param + // name in the output. + ty::Infer(_) => t, + // We don't want to hide the outermost type, only its type params. + _ if self.level == 1 => t.super_fold_with(self), + // Hide this type + _ => self.tcx().ty_error(), + }; + self.level -= 1; + t + } +} + +/// Replace `[type error]` with `ty::Infer(ty::Var)` to display `_`. +struct ErrTypeParamEraser<'tcx>(TyCtxt<'tcx>); +impl<'tcx> TypeFolder<'tcx> for ErrTypeParamEraser<'tcx> { + fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { + self.0 + } + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match t.kind() { + ty::Error(_) => self.tcx().mk_ty_var(ty::TyVid::from_u32(0)), + _ => t.super_fold_with(self), + } + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs index ac57796763f..4eec492b3ae 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -106,90 +106,47 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { None => String::new(), }; - let (span_1, span_2, main_label, span_label, future_return_type) = - match (sup_is_ret_type, sub_is_ret_type) { - (None, None) => { - let (main_label_1, span_label_1) = if ty_sup.hir_id == ty_sub.hir_id { - ( - "this type is declared with multiple lifetimes...".to_owned(), - "...but data with one lifetime flows into the other here".to_owned(), - ) - } else { - ( - "these two types are declared with different lifetimes...".to_owned(), - format!("...but data{} flows{} here", span_label_var1, span_label_var2), - ) - }; - (ty_sup.span, ty_sub.span, main_label_1, span_label_1, None) - } + debug!( + "try_report_anon_anon_conflict: sub_is_ret_type={:?} sup_is_ret_type={:?}", + sub_is_ret_type, sup_is_ret_type + ); - (Some(ret_span), _) => { - let sup_future = self.future_return_type(scope_def_id_sup); - let (return_type, action) = if sup_future.is_some() { - ("returned future", "held across an await point") - } else { - ("return type", "returned") - }; + let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch"); - ( - ty_sub.span, - ret_span, - format!( - "this parameter and the {} are declared with different lifetimes...", - return_type - ), - format!("...but data{} is {} here", span_label_var1, action), - sup_future, - ) - } - (_, Some(ret_span)) => { - let sub_future = self.future_return_type(scope_def_id_sub); - let (return_type, action) = if sub_future.is_some() { - ("returned future", "held across an await point") - } else { - ("return type", "returned") - }; + match (sup_is_ret_type, sub_is_ret_type) { + (ret_capture @ Some(ret_span), _) | (_, ret_capture @ Some(ret_span)) => { + let param_span = + if sup_is_ret_type == ret_capture { ty_sub.span } else { ty_sup.span }; + + err.span_label( + param_span, + "this parameter and the return type are declared with different lifetimes...", + ); + err.span_label(ret_span, ""); + err.span_label(span, format!("...but data{} is returned here", span_label_var1)); + } - ( + (None, None) => { + if ty_sup.hir_id == ty_sub.hir_id { + err.span_label(ty_sup.span, "this type is declared with multiple lifetimes..."); + err.span_label(ty_sub.span, ""); + err.span_label(span, "...but data with one lifetime flows into the other here"); + } else { + err.span_label( ty_sup.span, - ret_span, - format!( - "this parameter and the {} are declared with different lifetimes...", - return_type - ), - format!("...but data{} is {} here", span_label_var1, action), - sub_future, - ) + "these two types are declared with different lifetimes...", + ); + err.span_label(ty_sub.span, ""); + err.span_label( + span, + format!("...but data{} flows{} here", span_label_var1, span_label_var2), + ); } - }; - - let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch"); - - err.span_label(span_1, main_label); - err.span_label(span_2, String::new()); - err.span_label(span, span_label); + } + } self.suggest_adding_lifetime_params(sub, ty_sup, ty_sub, &mut err); - if let Some(t) = future_return_type { - let snip = self - .tcx() - .sess - .source_map() - .span_to_snippet(t.span) - .ok() - .and_then(|s| match (&t.kind, s.as_str()) { - (rustc_hir::TyKind::Tup(&[]), "") => Some("()".to_string()), - (_, "") => None, - _ => Some(s), - }) - .unwrap_or_else(|| "{unnamed_type}".to_string()); - - err.span_label( - t.span, - &format!("this `async fn` implicitly returns an `impl Future<Output = {}>`", snip), - ); - } err.emit(); Some(ErrorReported) } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs index 58eb1e9aa12..b1535701bb3 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -1,7 +1,7 @@ use rustc_hir as hir; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::Node; +use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_lifetime as rl; use rustc_middle::ty::{self, Region, TyCtxt}; @@ -20,29 +20,23 @@ use rustc_middle::ty::{self, Region, TyCtxt}; /// ``` /// The function returns the nested type corresponding to the anonymous region /// for e.g., `&u8` and `Vec<&u8>`. -pub(crate) fn find_anon_type( +pub(crate) fn find_anon_type<'tcx>( tcx: TyCtxt<'tcx>, region: Region<'tcx>, br: &ty::BoundRegionKind, -) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnDecl<'tcx>)> { +) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnSig<'tcx>)> { if let Some(anon_reg) = tcx.is_suitable_region(region) { let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id); - let fndecl = match tcx.hir().get(hir_id) { - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref m, ..), .. }) - | Node::TraitItem(&hir::TraitItem { - kind: hir::TraitItemKind::Fn(ref m, ..), .. - }) - | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref m, ..), .. }) => { - &m.decl - } - _ => return None, + let Some(fn_sig) = tcx.hir().get(hir_id).fn_sig() else { + return None }; - fndecl + fn_sig + .decl .inputs .iter() .find_map(|arg| find_component_for_bound_region(tcx, arg, br)) - .map(|ty| (ty, &**fndecl)) + .map(|ty| (ty, fn_sig)) } else { None } @@ -50,7 +44,7 @@ pub(crate) fn find_anon_type( // This method creates a FindNestedTypeVisitor which returns the type corresponding // to the anonymous region. -fn find_component_for_bound_region( +fn find_component_for_bound_region<'tcx>( tcx: TyCtxt<'tcx>, arg: &'tcx hir::Ty<'tcx>, br: &ty::BoundRegionKind, @@ -83,11 +77,11 @@ struct FindNestedTypeVisitor<'tcx> { current_index: ty::DebruijnIndex, } -impl Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { - type Map = Map<'tcx>; +impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { @@ -207,11 +201,11 @@ struct TyPathVisitor<'tcx> { current_index: ty::DebruijnIndex, } -impl Visitor<'tcx> for TyPathVisitor<'tcx> { - type Map = Map<'tcx>; +impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> NestedVisitorMap<Map<'tcx>> { - NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Map<'tcx> { + self.tcx.hir() } fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs index 35c8786dcd3..d3b47e396ec 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs @@ -31,15 +31,15 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { }; // If we added a "points at argument expression" obligation, we remove it here, we care // about the original obligation only. - let code = match &cause.code { + let code = match cause.code() { ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => &*parent_code, - _ => &cause.code, + _ => cause.code(), }; let (parent, impl_def_id) = match code { ObligationCauseCode::MatchImpl(parent, impl_def_id) => (parent, impl_def_id), _ => return None, }; - let binding_span = match parent.code { + let binding_span = match *parent.code() { ObligationCauseCode::BindingObligation(_def_id, binding_span) => binding_span, _ => return None, }; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs index 6a330977002..fd295b74342 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs @@ -67,7 +67,7 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> { pub fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> { match (&self.error, self.regions) { (Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), sub, sup)), - (Some(SubSupConflict(_, _, origin, sub, _, sup)), None) => { + (Some(SubSupConflict(_, _, origin, sub, _, sup, _)), None) => { Some((origin.span(), sub, sup)) } (None, Some((span, sub, sup))) => Some((span, sub, sup)), diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs index 0878f8550da..eb1c80ecb01 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs @@ -3,8 +3,6 @@ use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::error_reporting::nice_region_error::NiceRegionError; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; -use rustc_hir::intravisit::Visitor; -use rustc_hir::FnRetTy; use rustc_middle::ty; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { @@ -48,19 +46,24 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { return None; // inapplicable }; + // Suggesting to add a `'static` lifetime to a parameter is nearly always incorrect, + // and can steer users down the wrong path. + if *named == ty::ReStatic { + return None; + } + debug!("try_report_named_anon_conflict: named = {:?}", named); debug!("try_report_named_anon_conflict: anon_param_info = {:?}", anon_param_info); debug!("try_report_named_anon_conflict: region_info = {:?}", region_info); - let (param, new_ty, new_ty_span, br, is_first, scope_def_id, is_impl_item) = ( - anon_param_info.param, - anon_param_info.param_ty, - anon_param_info.param_ty_span, - anon_param_info.bound_region, - anon_param_info.is_first, - region_info.def_id, - region_info.is_impl_item, - ); + let param = anon_param_info.param; + let new_ty = anon_param_info.param_ty; + let new_ty_span = anon_param_info.param_ty_span; + let br = anon_param_info.bound_region; + let is_first = anon_param_info.is_first; + let scope_def_id = region_info.def_id; + let is_impl_item = region_info.is_impl_item; + match br { ty::BrAnon(_) => {} _ => { @@ -75,26 +78,10 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { return None; } - if let Some((_, fndecl)) = find_anon_type(self.tcx(), anon, &br) { - if self.is_self_anon(is_first, scope_def_id) { - return None; - } - - if let FnRetTy::Return(ty) = &fndecl.output { - let mut v = ty::TraitObjectVisitor(vec![], self.tcx().hir()); - v.visit_ty(ty); - - debug!("try_report_named_anon_conflict: ret ty {:?}", ty); - if sub == &ty::ReStatic - && v.0.into_iter().any(|t| t.span.desugaring_kind().is_none()) - { - // If the failure is due to a `'static` requirement coming from a `dyn` or - // `impl` Trait that *isn't* caused by `async fn` desugaring, handle this case - // better in `static_impl_trait`. - debug!("try_report_named_anon_conflict: impl Trait + 'static"); - return None; - } - } + if find_anon_type(self.tcx(), anon, &br).is_some() + && self.is_self_anon(is_first, scope_def_id) + { + return None; } let (error_var, span_label_var) = match param.pat.simple_ident() { @@ -114,16 +101,12 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ); diag.span_label(span, format!("lifetime `{}` required", named)); - // Suggesting `'static` is nearly always incorrect, and can steer users - // down the wrong path. - if *named != ty::ReStatic { - diag.span_suggestion( - new_ty_span, - &format!("add explicit lifetime `{}` to {}", named, span_label_var), - new_ty.to_string(), - Applicability::Unspecified, - ); - } + diag.span_suggestion( + new_ty_span, + &format!("add explicit lifetime `{}` to {}", named, span_label_var), + new_ty.to_string(), + Applicability::Unspecified, + ); Some(diag) } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs index 4aecc2f40b8..7178bfa525b 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs @@ -13,7 +13,7 @@ use rustc_middle::ty::{self, TyCtxt}; use std::fmt::{self, Write}; -impl NiceRegionError<'me, 'tcx> { +impl<'tcx> NiceRegionError<'_, 'tcx> { /// When given a `ConcreteFailure` for a function with arguments containing a named region and /// an anonymous region, emit a descriptive diagnostic error. pub(super) fn try_report_placeholder_conflict(&self) -> Option<DiagnosticBuilder<'tcx>> { @@ -34,6 +34,7 @@ impl NiceRegionError<'me, 'tcx> { sub_placeholder @ ty::RePlaceholder(_), _, sup_placeholder @ ty::RePlaceholder(_), + _, )) => self.try_report_trait_placeholder_mismatch( Some(self.tcx().mk_region(ty::ReVar(*vid))), cause, @@ -49,6 +50,7 @@ impl NiceRegionError<'me, 'tcx> { sub_placeholder @ ty::RePlaceholder(_), _, _, + _, )) => self.try_report_trait_placeholder_mismatch( Some(self.tcx().mk_region(ty::ReVar(*vid))), cause, @@ -64,6 +66,7 @@ impl NiceRegionError<'me, 'tcx> { _, _, sup_placeholder @ ty::RePlaceholder(_), + _, )) => self.try_report_trait_placeholder_mismatch( Some(self.tcx().mk_region(ty::ReVar(*vid))), cause, @@ -79,6 +82,7 @@ impl NiceRegionError<'me, 'tcx> { _, SubregionOrigin::Subtype(box TypeTrace { cause, values }), sup_placeholder @ ty::RePlaceholder(_), + _, )) => self.try_report_trait_placeholder_mismatch( Some(self.tcx().mk_region(ty::ReVar(*vid))), cause, @@ -204,7 +208,7 @@ impl NiceRegionError<'me, 'tcx> { ); let mut err = self.tcx().sess.struct_span_err(span, &msg); - let leading_ellipsis = if let ObligationCauseCode::ItemObligation(def_id) = cause.code { + let leading_ellipsis = if let ObligationCauseCode::ItemObligation(def_id) = *cause.code() { err.span_label(span, "doesn't satisfy where-clause"); err.span_label( self.tcx().def_span(def_id), diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index c6ccd9b60a9..0a9f59fbc97 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -7,10 +7,11 @@ use crate::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_data_structures::stable_set::FxHashSet; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{walk_ty, ErasedMap, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind}; use rustc_middle::ty::{ - self, AssocItemContainer, RegionKind, Ty, TyCtxt, TypeFoldable, TypeVisitor, + self, AssocItemContainer, RegionKind, StaticLifetimeVisitor, Ty, TyCtxt, TypeFoldable, + TypeVisitor, }; use rustc_span::symbol::Ident; use rustc_span::{MultiSpan, Span}; @@ -23,7 +24,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorReported> { debug!("try_report_static_impl_trait(error={:?})", self.error); let tcx = self.tcx(); - let (var_origin, sub_origin, sub_r, sup_origin, sup_r) = match self.error.as_ref()? { + let (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) = match self.error.as_ref()? { RegionResolutionError::SubSupConflict( _, var_origin, @@ -31,8 +32,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { sub_r, sup_origin, sup_r, + spans, ) if **sub_r == RegionKind::ReStatic => { - (var_origin, sub_origin, sub_r, sup_origin, sup_r) + (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) } RegionResolutionError::ConcreteFailure( SubregionOrigin::Subtype(box TypeTrace { cause, .. }), @@ -40,7 +42,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { sup_r, ) if **sub_r == RegionKind::ReStatic => { // This is for an implicit `'static` requirement coming from `impl dyn Trait {}`. - if let ObligationCauseCode::UnifyReceiver(ctxt) = &cause.code { + if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() { // This may have a closure and it would cause ICE // through `find_param_with_region` (#78262). let anon_reg_sup = tcx.is_suitable_region(sup_r)?; @@ -68,13 +70,13 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { .map(|s| format!("`{}`", s)) .unwrap_or_else(|| "`fn` parameter".to_string()), lifetime, - ctxt.assoc_item.ident, + ctxt.assoc_item.name, ); err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime)); err.span_label( cause.span, &format!( - "...is captured and required to live as long as `'static` here \ + "...is used and required to live as long as `'static` here \ because of an implicit lifetime bound on the {}", match ctxt.assoc_item.container { AssocItemContainer::TraitContainer(id) => @@ -123,86 +125,134 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { param_name, lifetime, ); - err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime)); - debug!("try_report_static_impl_trait: param_info={:?}", param); - // We try to make the output have fewer overlapping spans if possible. - if (sp == sup_origin.span() || !return_sp.overlaps(sup_origin.span())) - && sup_origin.span() != return_sp - { - // FIXME: account for `async fn` like in `async-await/issues/issue-62097.rs` - - // Customize the spans and labels depending on their relative order so - // that split sentences flow correctly. - if sup_origin.span().overlaps(return_sp) && sp == sup_origin.span() { - // Avoid the following: - // - // error: cannot infer an appropriate lifetime - // --> $DIR/must_outlive_least_region_or_bound.rs:18:50 - // | - // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) } - // | ---- ---------^- + let (mention_influencer, influencer_point) = + if sup_origin.span().overlaps(param.param_ty_span) { + // Account for `async fn` like in `async-await/issues/issue-62097.rs`. + // The desugaring of `async `fn`s causes `sup_origin` and `param` to point at the same + // place (but with different `ctxt`, hence `overlaps` instead of `==` above). // - // and instead show: + // This avoids the following: // - // error: cannot infer an appropriate lifetime - // --> $DIR/must_outlive_least_region_or_bound.rs:18:50 - // | - // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) } - // | ---- ^ - err.span_label( - sup_origin.span(), - "...is captured here, requiring it to live as long as `'static`", - ); + // LL | pub async fn run_dummy_fn(&self) { + // | ^^^^^ + // | | + // | this data with an anonymous lifetime `'_`... + // | ...is captured here... + (false, sup_origin.span()) } else { - err.span_label(sup_origin.span(), "...is captured here..."); - if return_sp < sup_origin.span() { - err.span_note( - return_sp, - "...and is required to live as long as `'static` here", + (!sup_origin.span().overlaps(return_sp), param.param_ty_span) + }; + err.span_label(influencer_point, &format!("this data with {}...", lifetime)); + + debug!("try_report_static_impl_trait: param_info={:?}", param); + + let mut spans = spans.clone(); + + if mention_influencer { + spans.push(sup_origin.span()); + } + // We dedup the spans *ignoring* expansion context. + spans.sort(); + spans.dedup_by_key(|span| (span.lo(), span.hi())); + + // We try to make the output have fewer overlapping spans if possible. + let require_msg = if spans.is_empty() { + "...is used and required to live as long as `'static` here" + } else { + "...and is required to live as long as `'static` here" + }; + let require_span = + if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp }; + + for span in &spans { + err.span_label(*span, "...is used here..."); + } + + if spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp) { + // If any of the "captured here" labels appears on the same line or after + // `require_span`, we put it on a note to ensure the text flows by appearing + // always at the end. + err.span_note(require_span, require_msg); + } else { + // We don't need a note, it's already at the end, it can be shown as a `span_label`. + err.span_label(require_span, require_msg); + } + + if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin { + err.span_note(*bound, "`'static` lifetime requirement introduced by this bound"); + } + if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = sub_origin { + if let ObligationCauseCode::ReturnValue(hir_id) + | ObligationCauseCode::BlockTailExpression(hir_id) = cause.code() + { + let parent_id = tcx.hir().get_parent_item(*hir_id); + let parent_id = tcx.hir().local_def_id_to_hir_id(parent_id); + if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id) { + let mut span: MultiSpan = fn_decl.output.span().into(); + let mut add_label = true; + if let hir::FnRetTy::Return(ty) = fn_decl.output { + let mut v = StaticLifetimeVisitor(vec![], tcx.hir()); + v.visit_ty(ty); + if !v.0.is_empty() { + span = v.0.clone().into(); + for sp in v.0 { + span.push_span_label( + sp, + "`'static` requirement introduced here".to_string(), + ); + } + add_label = false; + } + } + if add_label { + span.push_span_label( + fn_decl.output.span(), + "requirement introduced by this return type".to_string(), + ); + } + span.push_span_label( + cause.span, + "because of this returned expression".to_string(), ); - } else { - err.span_label( - return_sp, - "...and is required to live as long as `'static` here", + err.span_note( + span, + "`'static` lifetime requirement introduced by the return type", ); } } - } else { - err.span_label( - return_sp, - "...is captured and required to live as long as `'static` here", - ); } let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); let mut override_error_code = None; if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin { - if let ObligationCauseCode::UnifyReceiver(ctxt) = &cause.code { + if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() { // Handle case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a // `'static` lifetime when called as a method on a binding: `bar.qux()`. if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) { - override_error_code = Some(ctxt.assoc_item.ident); + override_error_code = Some(ctxt.assoc_item.name); } } } if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin { - let code = match &cause.code { - ObligationCauseCode::MatchImpl(parent, ..) => &parent.code, - _ => &cause.code, + let code = match cause.code() { + ObligationCauseCode::MatchImpl(parent, ..) => parent.code(), + _ => cause.code(), }; - if let ObligationCauseCode::ItemObligation(item_def_id) = *code { + if let (ObligationCauseCode::ItemObligation(item_def_id), None) = + (code, override_error_code) + { // Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static` // lifetime as above, but called using a fully-qualified path to the method: // `Foo::qux(bar)`. let mut v = TraitObjectVisitor(FxHashSet::default()); v.visit_ty(param.param_ty); if let Some((ident, self_ty)) = - self.get_impl_ident_and_self_ty_from_trait(item_def_id, &v.0) + self.get_impl_ident_and_self_ty_from_trait(*item_def_id, &v.0) { if self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty) { - override_error_code = Some(ident); + override_error_code = Some(ident.name); } } } @@ -238,7 +288,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } pub fn suggest_new_region_bound( - tcx: TyCtxt<'tcx>, + tcx: TyCtxt<'_>, err: &mut DiagnosticBuilder<'_>, fn_returns: Vec<&rustc_hir::Ty<'_>>, lifetime_name: String, @@ -376,7 +426,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let tcx = self.tcx(); match tcx.hir().get_if_local(def_id) { Some(Node::ImplItem(impl_item)) => { - match tcx.hir().find(tcx.hir().get_parent_item(impl_item.hir_id())) { + match tcx.hir().find_by_def_id(tcx.hir().get_parent_item(impl_item.hir_id())) { Some(Node::Item(Item { kind: ItemKind::Impl(hir::Impl { self_ty, .. }), .. @@ -385,13 +435,13 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } } Some(Node::TraitItem(trait_item)) => { - let parent_id = tcx.hir().get_parent_item(trait_item.hir_id()); - match tcx.hir().find(parent_id) { + let trait_did = tcx.hir().get_parent_item(trait_item.hir_id()); + match tcx.hir().find_by_def_id(trait_did) { Some(Node::Item(Item { kind: ItemKind::Trait(..), .. })) => { // The method being called is defined in the `trait`, but the `'static` // obligation comes from the `impl`. Find that `impl` so that we can point // at it in the suggestion. - let trait_did = tcx.hir().local_def_id(parent_id).to_def_id(); + let trait_did = trait_did.to_def_id(); match tcx .hir() .trait_impls(trait_did) @@ -508,12 +558,6 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { pub(super) struct TraitObjectVisitor(pub(super) FxHashSet<DefId>); impl<'tcx> TypeVisitor<'tcx> for TraitObjectVisitor { - fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> { - // The default anon const substs cannot include - // trait objects, so we don't have to bother looking. - None - } - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { match t.kind() { ty::Dynamic(preds, RegionKind::ReStatic) => { @@ -531,12 +575,6 @@ impl<'tcx> TypeVisitor<'tcx> for TraitObjectVisitor { pub(super) struct HirTraitObjectVisitor<'a>(pub(super) &'a mut Vec<Span>, pub(super) DefId); impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { - type Map = ErasedMap<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { - NestedVisitorMap::None - } - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { if let TyKind::TraitObject( poly_trait_refs, diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs index b9e7ee12bc8..bbea450a769 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -9,6 +9,7 @@ use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; +use rustc_middle::hir::nested_filter; use rustc_middle::ty::print::RegionHighlightMode; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor}; @@ -28,6 +29,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { _sub, sup_origin, _sup, + _, ) = error.clone() { if let (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) = (&sup_origin, &sub_origin) { @@ -35,7 +37,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ValuePairs::Types(sub_expected_found), ValuePairs::Types(sup_expected_found), CompareImplMethodObligation { trait_item_def_id, .. }, - ) = (&sub_trace.values, &sup_trace.values, &sub_trace.cause.code) + ) = (&sub_trace.values, &sup_trace.values, sub_trace.cause.code()) { if sup_expected_found == sub_expected_found { self.emit_err( @@ -54,12 +56,16 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { { if let SubregionOrigin::CompareImplTypeObligation { span, - item_name, impl_item_def_id, trait_item_def_id, } = origin { - self.emit_associated_type_err(span, item_name, impl_item_def_id, trait_item_def_id); + self.emit_associated_type_err( + span, + self.infcx.tcx.item_name(impl_item_def_id), + impl_item_def_id, + trait_item_def_id, + ); return Some(ErrorReported); } } @@ -75,26 +81,21 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // Mark all unnamed regions in the type with a number. // This diagnostic is called in response to lifetime errors, so be informative. - struct HighlightBuilder<'tcx> { + struct HighlightBuilder { highlight: RegionHighlightMode, - tcx: TyCtxt<'tcx>, counter: usize, } - impl HighlightBuilder<'tcx> { - fn build(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> RegionHighlightMode { + impl HighlightBuilder { + fn build(ty: Ty<'_>) -> RegionHighlightMode { let mut builder = - HighlightBuilder { highlight: RegionHighlightMode::default(), counter: 1, tcx }; + HighlightBuilder { highlight: RegionHighlightMode::default(), counter: 1 }; builder.visit_ty(ty); builder.highlight } } - impl<'tcx> ty::fold::TypeVisitor<'tcx> for HighlightBuilder<'tcx> { - fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> { - Some(self.tcx) - } - + impl<'tcx> ty::fold::TypeVisitor<'tcx> for HighlightBuilder { fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { if !r.has_name() && self.counter <= 3 { self.highlight.highlighting_region(r, self.counter); @@ -104,12 +105,12 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } } - let expected_highlight = HighlightBuilder::build(self.tcx(), expected); + let expected_highlight = HighlightBuilder::build(expected); let expected = self .infcx .extract_inference_diagnostics_data(expected.into(), Some(expected_highlight)) .name; - let found_highlight = HighlightBuilder::build(self.tcx(), found); + let found_highlight = HighlightBuilder::build(found); let found = self.infcx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name; @@ -181,11 +182,11 @@ struct TypeParamSpanVisitor<'tcx> { types: Vec<Span>, } -impl Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { - type Map = rustc_middle::hir::map::Map<'tcx>; +impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; - fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> { - hir::intravisit::NestedVisitorMap::OnlyBodies(self.tcx.hir()) + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() } fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs index 90bc5b3b2fe..6d71d702cc8 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs @@ -4,7 +4,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_middle::ty::{self, DefIdTree, Region, Ty}; +use rustc_middle::ty::{self, Binder, DefIdTree, Region, Ty, TypeFoldable}; use rustc_span::Span; /// Information about the anonymous region we are searching for. @@ -94,80 +94,42 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { }) } - pub(super) fn future_return_type( - &self, - local_def_id: LocalDefId, - ) -> Option<&rustc_hir::Ty<'_>> { - if let Some(hir::IsAsync::Async) = self.asyncness(local_def_id) { - if let rustc_middle::ty::Opaque(def_id, _) = - self.tcx().type_of(local_def_id).fn_sig(self.tcx()).output().skip_binder().kind() - { - match self.tcx().hir().get_if_local(*def_id) { - Some(hir::Node::Item(hir::Item { - kind: - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - bounds, - origin: hir::OpaqueTyOrigin::AsyncFn, - .. - }), - .. - })) => { - for b in bounds.iter() { - if let hir::GenericBound::LangItemTrait( - hir::LangItem::Future, - _span, - _hir_id, - generic_args, - ) = b - { - for type_binding in generic_args.bindings.iter() { - if type_binding.ident.name == rustc_span::sym::Output { - if let hir::TypeBindingKind::Equality { ty } = - type_binding.kind - { - return Some(ty); - } - } - } - } - } - } - _ => {} - } - } - } - None - } - - pub(super) fn asyncness(&self, local_def_id: LocalDefId) -> Option<hir::IsAsync> { - // similar to the asyncness fn in rustc_ty_utils::ty - let hir_id = self.tcx().hir().local_def_id_to_hir_id(local_def_id); - let node = self.tcx().hir().get(hir_id); - let fn_kind = node.fn_kind()?; - Some(fn_kind.asyncness()) - } - // Here, we check for the case where the anonymous region - // is in the return type. + // is in the return type as written by the user. // FIXME(#42703) - Need to handle certain cases here. pub(super) fn is_return_type_anon( &self, scope_def_id: LocalDefId, br: ty::BoundRegionKind, - decl: &hir::FnDecl<'_>, + hir_sig: &hir::FnSig<'_>, ) -> Option<Span> { - let ret_ty = self.tcx().type_of(scope_def_id); - if let ty::FnDef(_, _) = ret_ty.kind() { - let sig = ret_ty.fn_sig(self.tcx()); - let late_bound_regions = - self.tcx().collect_referenced_late_bound_regions(&sig.output()); - if late_bound_regions.iter().any(|r| *r == br) { - return Some(decl.output.span()); - } + let fn_ty = self.tcx().type_of(scope_def_id); + if let ty::FnDef(_, _) = fn_ty.kind() { + let ret_ty = fn_ty.fn_sig(self.tcx()).output(); + let span = hir_sig.decl.output.span(); + let future_output = if hir_sig.header.is_async() { + ret_ty.map_bound(|ty| self.infcx.get_impl_future_output_ty(ty)).transpose() + } else { + None + }; + return match future_output { + Some(output) if self.includes_region(output, br) => Some(span), + None if self.includes_region(ret_ty, br) => Some(span), + _ => None, + }; } None } + fn includes_region( + &self, + ty: Binder<'tcx, impl TypeFoldable<'tcx>>, + region: ty::BoundRegionKind, + ) -> bool { + let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(&ty); + late_bound_regions.iter().any(|r| *r == region) + } + // Here we check for the case where anonymous region // corresponds to self and if yes, we display E0312. // FIXME(#42700) - Need to format self properly to diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index 167a8893a11..82bd8890dda 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -330,30 +330,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); err } - infer::CompareImplMethodObligation { - span, - item_name, - impl_item_def_id, - trait_item_def_id, - } => self.report_extra_impl_obligation( - span, - item_name, - impl_item_def_id, - trait_item_def_id, - &format!("`{}: {}`", sup, sub), - ), - infer::CompareImplTypeObligation { - span, - item_name, - impl_item_def_id, - trait_item_def_id, - } => self.report_extra_impl_obligation( - span, - item_name, - impl_item_def_id, - trait_item_def_id, - &format!("`{}: {}`", sup, sub), - ), + infer::CompareImplMethodObligation { span, impl_item_def_id, trait_item_def_id } => { + self.report_extra_impl_obligation( + span, + impl_item_def_id, + trait_item_def_id, + &format!("`{}: {}`", sup, sub), + ) + } + infer::CompareImplTypeObligation { span, impl_item_def_id, trait_item_def_id } => self + .report_extra_impl_obligation( + span, + impl_item_def_id, + trait_item_def_id, + &format!("`{}: {}`", sup, sub), + ), } } @@ -368,13 +359,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { match placeholder_origin { infer::Subtype(box ref trace) if matches!( - &trace.cause.code.peel_derives(), + &trace.cause.code().peel_derives(), ObligationCauseCode::BindingObligation(..) ) => { // Hack to get around the borrow checker because trace.cause has an `Rc`. if let ObligationCauseCode::BindingObligation(_, span) = - &trace.cause.code.peel_derives() + &trace.cause.code().peel_derives() { let span = *span; let mut err = self.report_concrete_failure(placeholder_origin, sub, sup); diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs index 4814b65e320..e93cdf79421 100644 --- a/compiler/rustc_infer/src/infer/free_regions.rs +++ b/compiler/rustc_infer/src/infer/free_regions.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::{self, Lift, Region, TyCtxt}; /// /// This stuff is a bit convoluted and should be refactored, but as we /// transition to NLL, it'll all go away anyhow. -pub struct RegionRelations<'a, 'tcx> { +pub(crate) struct RegionRelations<'a, 'tcx> { pub tcx: TyCtxt<'tcx>, /// The context used for debug messages diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index c40e409891b..4af1bdf97a7 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -146,7 +146,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if !t.needs_infer() && !t.has_erasable_regions(self.tcx()) { + if !t.needs_infer() && !t.has_erasable_regions() { return t; } diff --git a/compiler/rustc_infer/src/infer/glb.rs b/compiler/rustc_infer/src/infer/glb.rs index d769667c2fb..862f5a5fbb8 100644 --- a/compiler/rustc_infer/src/infer/glb.rs +++ b/compiler/rustc_infer/src/infer/glb.rs @@ -23,7 +23,7 @@ impl<'combine, 'infcx, 'tcx> Glb<'combine, 'infcx, 'tcx> { } } -impl TypeRelation<'tcx> for Glb<'combine, 'infcx, 'tcx> { +impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> { fn tag(&self) -> &'static str { "Glb" } diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 4c9dcab26b1..a5ec84a4f14 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -19,6 +19,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; use rustc_middle::ty::{ReLateBound, RePlaceholder, ReVar}; use rustc_middle::ty::{Region, RegionVid}; +use rustc_span::Span; use std::fmt; /// This function performs lexical region resolution given a complete @@ -27,7 +28,7 @@ use std::fmt; /// assuming such values can be found. It returns the final values of /// all the variables as well as a set of errors that must be reported. #[instrument(level = "debug", skip(region_rels, var_infos, data))] -pub fn resolve<'tcx>( +pub(crate) fn resolve<'tcx>( region_rels: &RegionRelations<'_, 'tcx>, var_infos: VarInfos, data: RegionConstraintData<'tcx>, @@ -96,6 +97,7 @@ pub enum RegionResolutionError<'tcx> { Region<'tcx>, SubregionOrigin<'tcx>, Region<'tcx>, + Vec<Span>, // All the influences on a given value that didn't meet its constraints. ), /// Indicates a `'b: 'a` constraint where `'a` is in a universe that @@ -567,7 +569,30 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // if this rule starts to create problems we'll // have to revisit this portion of the code and // think hard about it. =) -- nikomatsakis - self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors); + + // Obtain the spans for all the places that can + // influence the constraints on this value for + // richer diagnostics in `static_impl_trait`. + let influences: Vec<Span> = self + .data + .constraints + .iter() + .filter_map(|(constraint, origin)| match (constraint, origin) { + ( + Constraint::VarSubVar(_, sup), + SubregionOrigin::DataBorrowed(_, sp), + ) if sup == &node_vid => Some(*sp), + _ => None, + }) + .collect(); + + self.collect_error_for_expanding_node( + graph, + &mut dup_vec, + node_vid, + errors, + influences, + ); } } } @@ -621,6 +646,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { dup_vec: &mut IndexVec<RegionVid, Option<RegionVid>>, node_idx: RegionVid, errors: &mut Vec<RegionResolutionError<'tcx>>, + influences: Vec<Span>, ) { // Errors in expanding nodes result from a lower-bound that is // not contained by an upper-bound. @@ -667,6 +693,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { sup: {:?}", origin, node_idx, lower_bound.region, upper_bound.region ); + errors.push(RegionResolutionError::SubSupConflict( node_idx, origin, @@ -674,6 +701,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { lower_bound.region, upper_bound.origin.clone(), upper_bound.region, + influences, )); return; } diff --git a/compiler/rustc_infer/src/infer/lub.rs b/compiler/rustc_infer/src/infer/lub.rs index cbad66397fd..5191d1c1cc1 100644 --- a/compiler/rustc_infer/src/infer/lub.rs +++ b/compiler/rustc_infer/src/infer/lub.rs @@ -23,7 +23,7 @@ impl<'combine, 'infcx, 'tcx> Lub<'combine, 'infcx, 'tcx> { } } -impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> { +impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> { fn tag(&self) -> &'static str { "Lub" } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index d6d1dfb7113..d1b24b332bd 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -10,7 +10,6 @@ pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog}; use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine}; -use hir::def_id::CRATE_DEF_ID; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::Rollback; @@ -21,6 +20,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; +use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::mir::interpret::EvalToConstValueResult; use rustc_middle::traits::select; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -95,9 +95,10 @@ pub(crate) type UnificationTable<'a, 'tcx, T> = ut::UnificationTable< /// This is used so that the region values inferred by HIR region solving are /// not exposed, and so that we can avoid doing work in HIR typeck that MIR /// typeck will also do. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Default)] pub enum RegionckMode { /// The default mode: report region errors, don't erase regions. + #[default] Solve, /// Erase the results of region after solving. Erase { @@ -108,12 +109,6 @@ pub enum RegionckMode { }, } -impl Default for RegionckMode { - fn default() -> Self { - RegionckMode::Solve - } -} - impl RegionckMode { /// Indicates that the MIR borrowck will repeat these region /// checks, so we should ignore errors if NLL is (unconditionally) @@ -295,7 +290,12 @@ pub struct InferCtxt<'a, 'tcx> { /// The `DefId` of the item in whose context we are performing inference or typeck. /// It is used to check whether an opaque type use is a defining use. - pub defining_use_anchor: LocalDefId, + /// + /// If it is `None`, we can't resolve opaque types here and need to bubble up + /// the obligation. This frequently happens for + /// short lived InferCtxt within queries. The opaque type obligations are forwarded + /// to the outside until the end up in an `InferCtxt` for typeck or borrowck. + pub defining_use_anchor: Option<LocalDefId>, /// During type-checking/inference of a body, `in_progress_typeck_results` /// contains a reference to the typeck results being built up, which are @@ -419,21 +419,11 @@ pub enum SubregionOrigin<'tcx> { /// Comparing the signature and requirements of an impl method against /// the containing trait. - CompareImplMethodObligation { - span: Span, - item_name: Symbol, - impl_item_def_id: DefId, - trait_item_def_id: DefId, - }, + CompareImplMethodObligation { span: Span, impl_item_def_id: DefId, trait_item_def_id: DefId }, /// Comparing the signature and requirements of an impl associated type /// against the containing trait - CompareImplTypeObligation { - span: Span, - item_name: Symbol, - impl_item_def_id: DefId, - trait_item_def_id: DefId, - }, + CompareImplTypeObligation { span: Span, impl_item_def_id: DefId, trait_item_def_id: DefId }, } // `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger. @@ -469,7 +459,7 @@ pub enum RegionVariableOrigin { AddrOfRegion(Span), /// Regions created as part of an autoref of a method receiver - Autoref(Span, ty::AssocItem), + Autoref(Span), /// Regions created as part of an automatic coercion Coercion(Span), @@ -561,20 +551,16 @@ impl<'tcx> fmt::Display for FixupError<'tcx> { pub struct InferCtxtBuilder<'tcx> { tcx: TyCtxt<'tcx>, fresh_typeck_results: Option<RefCell<ty::TypeckResults<'tcx>>>, - defining_use_anchor: LocalDefId, + defining_use_anchor: Option<LocalDefId>, } pub trait TyCtxtInferExt<'tcx> { fn infer_ctxt(self) -> InferCtxtBuilder<'tcx>; } -impl TyCtxtInferExt<'tcx> for TyCtxt<'tcx> { +impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> { fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> { - InferCtxtBuilder { - tcx: self, - defining_use_anchor: CRATE_DEF_ID, - fresh_typeck_results: None, - } + InferCtxtBuilder { tcx: self, defining_use_anchor: None, fresh_typeck_results: None } } } @@ -594,7 +580,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// (via `with_fresh_in_progress_typeck_results`) and for the inference context used /// in mir borrowck. pub fn with_opaque_type_inference(mut self, defining_use_anchor: LocalDefId) -> Self { - self.defining_use_anchor = defining_use_anchor; + self.defining_use_anchor = Some(defining_use_anchor); self } @@ -1599,13 +1585,26 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { unevaluated: ty::Unevaluated<'tcx>, span: Option<Span>, ) -> EvalToConstValueResult<'tcx> { - let mut original_values = OriginalQueryValues::default(); - let canonical = self.canonicalize_query((param_env, unevaluated), &mut original_values); + let substs = self.resolve_vars_if_possible(unevaluated.substs); + + // Postpone the evaluation of constants whose substs depend on inference + // variables + if substs.has_infer_types_or_consts() { + return Err(ErrorHandled::TooGeneric); + } + + let param_env_erased = self.tcx.erase_regions(param_env); + let substs_erased = self.tcx.erase_regions(substs); + + let unevaluated = ty::Unevaluated { + def: unevaluated.def, + substs: substs_erased, + promoted: unevaluated.promoted, + }; - let (param_env, unevaluated) = canonical.value; // The return value is the evaluated value which doesn't contain any reference to inference // variables, thus we don't need to substitute back the original values. - self.tcx.const_eval_resolve(param_env, unevaluated, span) + self.tcx.const_eval_resolve(param_env_erased, unevaluated, span) } /// If `typ` is a type variable of some kind, resolve it one level @@ -1718,7 +1717,7 @@ pub enum TyOrConstInferVar<'tcx> { Const(ConstVid<'tcx>), } -impl TyOrConstInferVar<'tcx> { +impl<'tcx> TyOrConstInferVar<'tcx> { /// Tries to extract an inference variable from a type or a constant, returns `None` /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`) and /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`). @@ -1824,29 +1823,25 @@ impl<'tcx> SubregionOrigin<'tcx> { where F: FnOnce() -> Self, { - match cause.code { + match *cause.code() { traits::ObligationCauseCode::ReferenceOutlivesReferent(ref_type) => { SubregionOrigin::ReferenceOutlivesReferent(ref_type, cause.span) } traits::ObligationCauseCode::CompareImplMethodObligation { - item_name, impl_item_def_id, trait_item_def_id, } => SubregionOrigin::CompareImplMethodObligation { span: cause.span, - item_name, impl_item_def_id, trait_item_def_id, }, traits::ObligationCauseCode::CompareImplTypeObligation { - item_name, impl_item_def_id, trait_item_def_id, } => SubregionOrigin::CompareImplTypeObligation { span: cause.span, - item_name, impl_item_def_id, trait_item_def_id, }, @@ -1862,7 +1857,7 @@ impl RegionVariableOrigin { MiscVariable(a) | PatternRegion(a) | AddrOfRegion(a) - | Autoref(a, _) + | Autoref(a) | Coercion(a) | EarlyBoundRegion(a, ..) | LateBoundRegion(a, ..) diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index 29a9cbc7a99..0a210ed053c 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -201,7 +201,6 @@ where }; value.skip_binder().visit_with(&mut ScopeInstantiator { - tcx: self.infcx.tcx, next_region: &mut next_region, target_index: ty::INNERMOST, bound_region_scope: &mut scope, @@ -407,7 +406,7 @@ trait VidValuePair<'tcx>: Debug { /// Extract the scopes that apply to whichever side of the tuple /// the vid was found on. See the comment where this is called /// for more details on why we want them. - fn vid_scopes<D: TypeRelatingDelegate<'tcx>>( + fn vid_scopes<'r, D: TypeRelatingDelegate<'tcx>>( &self, relate: &'r mut TypeRelating<'_, 'tcx, D>, ) -> &'r mut Vec<BoundRegionScope<'tcx>>; @@ -424,7 +423,7 @@ trait VidValuePair<'tcx>: Debug { D: TypeRelatingDelegate<'tcx>; } -impl VidValuePair<'tcx> for (ty::TyVid, Ty<'tcx>) { +impl<'tcx> VidValuePair<'tcx> for (ty::TyVid, Ty<'tcx>) { fn vid(&self) -> ty::TyVid { self.0 } @@ -433,7 +432,7 @@ impl VidValuePair<'tcx> for (ty::TyVid, Ty<'tcx>) { self.1 } - fn vid_scopes<D>( + fn vid_scopes<'r, D>( &self, relate: &'r mut TypeRelating<'_, 'tcx, D>, ) -> &'r mut Vec<BoundRegionScope<'tcx>> @@ -456,7 +455,7 @@ impl VidValuePair<'tcx> for (ty::TyVid, Ty<'tcx>) { } // In this case, the "vid" is the "b" type. -impl VidValuePair<'tcx> for (Ty<'tcx>, ty::TyVid) { +impl<'tcx> VidValuePair<'tcx> for (Ty<'tcx>, ty::TyVid) { fn vid(&self) -> ty::TyVid { self.1 } @@ -465,7 +464,7 @@ impl VidValuePair<'tcx> for (Ty<'tcx>, ty::TyVid) { self.0 } - fn vid_scopes<D>( + fn vid_scopes<'r, D>( &self, relate: &'r mut TypeRelating<'_, 'tcx, D>, ) -> &'r mut Vec<BoundRegionScope<'tcx>> @@ -487,7 +486,7 @@ impl VidValuePair<'tcx> for (Ty<'tcx>, ty::TyVid) { } } -impl<D> TypeRelation<'tcx> for TypeRelating<'me, 'tcx, D> +impl<'tcx, D> TypeRelation<'tcx> for TypeRelating<'_, 'tcx, D> where D: TypeRelatingDelegate<'tcx>, { @@ -759,7 +758,6 @@ where /// `for<..`>. For each of those, it creates an entry in /// `bound_region_scope`. struct ScopeInstantiator<'me, 'tcx> { - tcx: TyCtxt<'tcx>, next_region: &'me mut dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx>, // The debruijn index of the scope we are instantiating. target_index: ty::DebruijnIndex, @@ -767,10 +765,6 @@ struct ScopeInstantiator<'me, 'tcx> { } impl<'me, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'tcx> { - fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> { - Some(self.tcx) - } - fn visit_binder<T: TypeFoldable<'tcx>>( &mut self, t: &ty::Binder<'tcx, T>, @@ -841,7 +835,7 @@ where universe: ty::UniverseIndex, } -impl<D> TypeRelation<'tcx> for TypeGeneralizer<'me, 'tcx, D> +impl<'tcx, D> TypeRelation<'tcx> for TypeGeneralizer<'_, 'tcx, D> where D: TypeRelatingDelegate<'tcx>, { diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index e2e07f2072e..e7dca94806c 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -276,7 +276,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { debug!(?concrete_ty); let first_own_region = match opaque_defn.origin { - hir::OpaqueTyOrigin::FnReturn | hir::OpaqueTyOrigin::AsyncFn => { + hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => { // We lower // // fn foo<'l0..'ln>() -> impl Trait<'l0..'lm> @@ -316,7 +316,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { - tcx: self.tcx, op: |r| { self.member_constraint( opaque_type_key.def_id, @@ -328,6 +327,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }, }); } + + fn opaque_type_origin(&self, def_id: LocalDefId) -> Option<hir::OpaqueTyOrigin> { + let tcx = self.tcx; + let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let parent_def_id = self.defining_use_anchor?; + let item_kind = &tcx.hir().expect_item(def_id).kind; + let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else { + span_bug!( + tcx.def_span(def_id), + "weird opaque type: {:#?}", + item_kind + ) + }; + let in_definition_scope = match *origin { + // Async `impl Trait` + hir::OpaqueTyOrigin::AsyncFn(parent) => parent == parent_def_id, + // Anonymous `impl Trait` + hir::OpaqueTyOrigin::FnReturn(parent) => parent == parent_def_id, + // Named `type Foo = impl Bar;` + hir::OpaqueTyOrigin::TyAlias => { + may_define_opaque_type(tcx, parent_def_id, opaque_hir_id) + } + }; + in_definition_scope.then_some(*origin) + } } // Visitor that requires that (almost) all regions in the type visited outlive @@ -343,19 +367,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // // We ignore any type parameters because impl trait values are assumed to // capture all the in-scope type parameters. -struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP> { - tcx: TyCtxt<'tcx>, +struct ConstrainOpaqueTypeRegionVisitor<OP> { op: OP, } -impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP> +impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<OP> where OP: FnMut(ty::Region<'tcx>), { - fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> { - Some(self.tcx) - } - fn visit_binder<T: TypeFoldable<'tcx>>( &mut self, t: &ty::Binder<'tcx, T>, @@ -377,7 +396,7 @@ where fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { // We're only interested in types involving regions - if !ty.flags().intersects(ty::TypeFlags::HAS_POTENTIAL_FREE_REGIONS) { + if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { return ControlFlow::CONTINUE; } @@ -459,32 +478,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { // } // ``` if let Some(def_id) = def_id.as_local() { - let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let parent_def_id = self.infcx.defining_use_anchor; - let def_scope_default = || { - let opaque_parent_hir_id = tcx.hir().get_parent_item(opaque_hir_id); - parent_def_id == tcx.hir().local_def_id(opaque_parent_hir_id) - }; - let (in_definition_scope, origin) = - match tcx.hir().expect_item(opaque_hir_id).kind { - // Anonymous `impl Trait` - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - impl_trait_fn: Some(parent), - origin, - .. - }) => (parent == parent_def_id.to_def_id(), origin), - // Named `type Foo = impl Bar;` - hir::ItemKind::OpaqueTy(hir::OpaqueTy { - impl_trait_fn: None, - origin, - .. - }) => ( - may_define_opaque_type(tcx, parent_def_id, opaque_hir_id), - origin, - ), - _ => (def_scope_default(), hir::OpaqueTyOrigin::TyAlias), - }; - if in_definition_scope { + if let Some(origin) = self.infcx.opaque_type_origin(def_id) { let opaque_type_key = OpaqueTypeKey { def_id: def_id.to_def_id(), substs }; return self.fold_opaque_ty(ty, opaque_type_key, origin); @@ -555,17 +549,35 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { let predicate = predicate.subst(tcx, substs); debug!(?predicate); + let predicate = predicate.fold_with(&mut BottomUpFolder { + tcx, + ty_op: |ty| match *ty.kind() { + // Replace all other mentions of the same opaque type with the hidden type, + // as the bounds must hold on the hidden type after all. + ty::Opaque(def_id2, substs2) if def_id == def_id2 && substs == substs2 => { + ty_var + } + // Instantiate nested instances of `impl Trait`. + ty::Opaque(..) => self.instantiate_opaque_types_in_map(ty), + _ => ty, + }, + lt_op: |lt| lt, + ct_op: |ct| ct, + }); + // We can't normalize associated types from `rustc_infer`, but we can eagerly register inference variables for them. let predicate = predicate.fold_with(&mut BottomUpFolder { tcx, ty_op: |ty| match ty.kind() { - ty::Projection(projection_ty) => infcx.infer_projection( - self.param_env, - *projection_ty, - traits::ObligationCause::misc(self.value_span, self.body_id), - 0, - &mut self.obligations, - ), + ty::Projection(projection_ty) if !projection_ty.has_escaping_bound_vars() => { + infcx.infer_projection( + self.param_env, + *projection_ty, + traits::ObligationCause::misc(self.value_span, self.body_id), + 0, + &mut self.obligations, + ) + } _ => ty, }, lt_op: |lt| lt, @@ -574,15 +586,10 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { debug!(?predicate); if let ty::PredicateKind::Projection(projection) = predicate.kind().skip_binder() { - if projection.ty.references_error() { - // No point on adding these obligations since there's a type error involved. + if projection.term.references_error() { return tcx.ty_error(); } } - // Change the predicate to refer to the type variable, - // which will be the concrete type instead of the opaque type. - // This also instantiates nested instances of `impl Trait`. - let predicate = self.instantiate_opaque_types_in_map(predicate); let cause = traits::ObligationCause::new(self.value_span, self.body_id, traits::OpaqueType); @@ -623,7 +630,7 @@ fn may_define_opaque_type(tcx: TyCtxt<'_>, def_id: LocalDefId, opaque_hir_id: hi let scope = tcx.hir().get_defining_scope(opaque_hir_id); // We walk up the node tree until we hit the root or the scope of the opaque type. while hir_id != scope && hir_id != hir::CRATE_HIR_ID { - hir_id = tcx.hir().get_parent_item(hir_id); + hir_id = tcx.hir().local_def_id_to_hir_id(tcx.hir().get_parent_item(hir_id)); } // Syntactically, we are allowed to define the concrete type if: let res = hir_id == scope; diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs index 98f926e9d76..fbf149a4788 100644 --- a/compiler/rustc_infer/src/infer/outlives/components.rs +++ b/compiler/rustc_infer/src/infer/outlives/components.rs @@ -49,7 +49,7 @@ pub enum Component<'tcx> { /// Push onto `out` all the things that must outlive `'a` for the condition /// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**. -pub fn push_outlives_components( +pub fn push_outlives_components<'tcx>( tcx: TyCtxt<'tcx>, ty0: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>, @@ -59,7 +59,7 @@ pub fn push_outlives_components( debug!("components({:?}) = {:?}", ty0, out); } -fn compute_components( +fn compute_components<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>, @@ -190,13 +190,13 @@ fn compute_components( } } -fn compute_components_recursive( +fn compute_components_recursive<'tcx>( tcx: TyCtxt<'tcx>, parent: GenericArg<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>, visited: &mut SsoHashSet<GenericArg<'tcx>>, ) { - for child in parent.walk_shallow(tcx, visited) { + for child in parent.walk_shallow(visited) { match child.unpack() { GenericArgKind::Type(ty) => { compute_components(tcx, ty, out, visited); diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index 9e04773c5fa..3947282aa62 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -99,7 +99,7 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { /// function. We can then add implied bounds and the like from the /// closure arguments into the environment -- these should only /// apply in the closure body, so once we exit, we invoke - /// `pop_snapshot_post_closure` to remove them. + /// `pop_snapshot_post_typeck_child` to remove them. /// /// Example: /// @@ -129,12 +129,12 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { /// seems like it'd be readily fixed if we wanted. There are /// similar leaks around givens that seem equally suspicious, to /// be honest. --nmatsakis - pub fn push_snapshot_pre_closure(&self) -> usize { + pub fn push_snapshot_pre_typeck_child(&self) -> usize { self.region_bound_pairs_accum.len() } - /// See `push_snapshot_pre_closure`. - pub fn pop_snapshot_post_closure(&mut self, len: usize) { + /// See `push_snapshot_pre_typeck_child`. + pub fn pop_snapshot_post_typeck_child(&mut self, len: usize) { self.region_bound_pairs_accum.truncate(len); } diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 91a22ecc5a9..a5276afc5bf 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -102,7 +102,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { infer::RelateParamBound( cause.span, sup_type, - match cause.code.peel_derives() { + match cause.code().peel_derives() { ObligationCauseCode::BindingObligation(_, span) => Some(*span), _ => None, }, @@ -164,7 +164,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { "cannot process registered region obligations in a snapshot" ); - debug!("process_registered_region_obligations()"); + debug!(?param_env, "process_registered_region_obligations()"); let my_region_obligations = self.take_registered_region_obligations(); @@ -356,6 +356,8 @@ where let trait_bounds: Vec<_> = self.verify_bound.projection_declared_bounds_from_trait(projection_ty).collect(); + debug!(?trait_bounds); + // Compute the bounds we can derive from the environment. This // is an "approximate" match -- in some cases, these bounds // may not apply. diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index dba73251b4f..f69212c599b 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -189,7 +189,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { visited: &mut SsoHashSet<GenericArg<'tcx>>, ) -> VerifyBound<'tcx> { let mut bounds = parent - .walk_shallow(self.tcx, visited) + .walk_shallow(visited) .filter_map(|child| match child.unpack() { GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)), GenericArgKind::Lifetime(lt) => { diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs index 9b53ab72b00..b45a6514d79 100644 --- a/compiler/rustc_infer/src/infer/projection.rs +++ b/compiler/rustc_infer/src/infer/projection.rs @@ -26,7 +26,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { kind: TypeVariableOriginKind::NormalizeProjectionType, span: self.tcx.def_span(def_id), }); - let projection = ty::Binder::dummy(ty::ProjectionPredicate { projection_ty, ty: ty_var }); + let projection = + ty::Binder::dummy(ty::ProjectionPredicate { projection_ty, term: ty_var.into() }); let obligation = Obligation::with_depth( cause, recursion_depth, diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index df4fdb3a982..29775a96685 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -65,7 +65,7 @@ pub struct RegionConstraintCollector<'a, 'tcx> { undo_log: &'a mut InferCtxtUndoLogs<'tcx>, } -impl std::ops::Deref for RegionConstraintCollector<'_, 'tcx> { +impl<'tcx> std::ops::Deref for RegionConstraintCollector<'_, 'tcx> { type Target = RegionConstraintStorage<'tcx>; #[inline] fn deref(&self) -> &RegionConstraintStorage<'tcx> { @@ -73,7 +73,7 @@ impl std::ops::Deref for RegionConstraintCollector<'_, 'tcx> { } } -impl std::ops::DerefMut for RegionConstraintCollector<'_, 'tcx> { +impl<'tcx> std::ops::DerefMut for RegionConstraintCollector<'_, 'tcx> { #[inline] fn deref_mut(&mut self) -> &mut RegionConstraintStorage<'tcx> { self.storage diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 4b08c2eb9c1..74459911384 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -1,7 +1,7 @@ use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use super::{FixupError, FixupResult, InferCtxt, Span}; use rustc_middle::mir; -use rustc_middle::ty::fold::{TypeFolder, TypeVisitor}; +use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFolder, TypeVisitor}; use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable}; use std::ops::ControlFlow; @@ -126,11 +126,6 @@ impl<'a, 'tcx> UnresolvedTypeFinder<'a, 'tcx> { impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'tcx> { type BreakTy = (Ty<'tcx>, Option<Span>); - - fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> { - Some(self.infcx.tcx) - } - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { let t = self.infcx.shallow_resolve(t); if t.has_infer_types() { @@ -175,81 +170,72 @@ pub fn fully_resolve<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>, value: T) -> Fixu where T: TypeFoldable<'tcx>, { - let mut full_resolver = FullTypeResolver { infcx, err: None }; - let result = value.fold_with(&mut full_resolver); - match full_resolver.err { - None => Ok(result), - Some(e) => Err(e), - } + value.try_fold_with(&mut FullTypeResolver { infcx }) } // N.B. This type is not public because the protocol around checking the // `err` field is not enforceable otherwise. struct FullTypeResolver<'a, 'tcx> { infcx: &'a InferCtxt<'a, 'tcx>, - err: Option<FixupError<'tcx>>, } impl<'a, 'tcx> TypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { + type Error = FixupError<'tcx>; + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { self.infcx.tcx } +} - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { +impl<'a, 'tcx> FallibleTypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { + fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> { if !t.needs_infer() { - t // micro-optimize -- if there is nothing in this type that this fold affects... + Ok(t) // micro-optimize -- if there is nothing in this type that this fold affects... } else { let t = self.infcx.shallow_resolve(t); match *t.kind() { - ty::Infer(ty::TyVar(vid)) => { - self.err = Some(FixupError::UnresolvedTy(vid)); - self.tcx().ty_error() - } - ty::Infer(ty::IntVar(vid)) => { - self.err = Some(FixupError::UnresolvedIntTy(vid)); - self.tcx().ty_error() - } - ty::Infer(ty::FloatVar(vid)) => { - self.err = Some(FixupError::UnresolvedFloatTy(vid)); - self.tcx().ty_error() - } + ty::Infer(ty::TyVar(vid)) => Err(FixupError::UnresolvedTy(vid)), + ty::Infer(ty::IntVar(vid)) => Err(FixupError::UnresolvedIntTy(vid)), + ty::Infer(ty::FloatVar(vid)) => Err(FixupError::UnresolvedFloatTy(vid)), ty::Infer(_) => { bug!("Unexpected type in full type resolver: {:?}", t); } - _ => t.super_fold_with(self), + _ => t.try_super_fold_with(self), } } } - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + fn try_fold_region(&mut self, r: ty::Region<'tcx>) -> Result<ty::Region<'tcx>, Self::Error> { match *r { - ty::ReVar(rid) => self + ty::ReVar(rid) => Ok(self .infcx .lexical_region_resolutions .borrow() .as_ref() .expect("region resolution not performed") - .resolve_var(rid), - _ => r, + .resolve_var(rid)), + _ => Ok(r), } } - fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + fn try_fold_const( + &mut self, + c: &'tcx ty::Const<'tcx>, + ) -> Result<&'tcx ty::Const<'tcx>, Self::Error> { if !c.needs_infer() { - c // micro-optimize -- if there is nothing in this const that this fold affects... + Ok(c) // micro-optimize -- if there is nothing in this const that this fold affects... } else { let c = self.infcx.shallow_resolve(c); match c.val { ty::ConstKind::Infer(InferConst::Var(vid)) => { - self.err = Some(FixupError::UnresolvedConst(vid)); - return self.tcx().const_error(c.ty); + return Err(FixupError::UnresolvedConst(vid)); } ty::ConstKind::Infer(InferConst::Fresh(_)) => { bug!("Unexpected const in full const resolver: {:?}", c); } _ => {} } - c.super_fold_with(self) + c.try_super_fold_with(self) } } } diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs index 8ef0d132cf0..ccac0efd6c9 100644 --- a/compiler/rustc_infer/src/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -31,7 +31,7 @@ impl<'combine, 'infcx, 'tcx> Sub<'combine, 'infcx, 'tcx> { } } -impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> { +impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { fn tag(&self) -> &'static str { "Sub" } diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index d0f1ff649d0..5f228d1e203 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -15,11 +15,10 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(bool_to_option)] #![feature(box_patterns)] +#![feature(derive_default_enum)] #![feature(extend_one)] -#![feature(iter_zip)] #![feature(let_else)] #![feature(never_type)] -#![feature(in_band_lifetimes)] #![feature(control_flow_enum)] #![feature(min_specialization)] #![feature(label_break_value)] diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index dce4a87b041..736278ba0d3 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -1,9 +1,8 @@ use crate::infer::InferCtxt; use crate::traits::Obligation; use rustc_data_structures::fx::FxHashMap; -use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_middle::ty::{self, ToPredicate, Ty, WithConstness}; +use rustc_middle::ty::{self, ToPredicate, Ty}; use super::FulfillmentError; use super::{ObligationCause, PredicateObligation}; @@ -46,32 +45,10 @@ pub trait TraitEngine<'tcx>: 'tcx { obligation: PredicateObligation<'tcx>, ); - fn select_all_or_error( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Result<(), Vec<FulfillmentError<'tcx>>>; - - fn select_all_with_constness_or_error( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - _constness: hir::Constness, - ) -> Result<(), Vec<FulfillmentError<'tcx>>> { - self.select_all_or_error(infcx) - } + fn select_all_or_error(&mut self, infcx: &InferCtxt<'_, 'tcx>) -> Vec<FulfillmentError<'tcx>>; - fn select_where_possible( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Result<(), Vec<FulfillmentError<'tcx>>>; - - // FIXME(fee1-dead) this should not provide a default body for chalk as chalk should be updated - fn select_with_constness_where_possible( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - _constness: hir::Constness, - ) -> Result<(), Vec<FulfillmentError<'tcx>>> { - self.select_where_possible(infcx) - } + fn select_where_possible(&mut self, infcx: &InferCtxt<'_, 'tcx>) + -> Vec<FulfillmentError<'tcx>>; fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>; @@ -86,7 +63,7 @@ pub trait TraitEngineExt<'tcx> { ); } -impl<T: ?Sized + TraitEngine<'tcx>> TraitEngineExt<'tcx> for T { +impl<'tcx, T: ?Sized + TraitEngine<'tcx>> TraitEngineExt<'tcx> for T { fn register_predicate_obligations( &mut self, infcx: &InferCtxt<'_, 'tcx>, diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs index 9dbfa3a850b..1a5ffd93701 100644 --- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs @@ -6,7 +6,6 @@ use rustc_errors::{struct_span_err, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::TyCtxt; -use rustc_span::symbol::Symbol; use rustc_span::{MultiSpan, Span}; use std::fmt; use std::iter; @@ -15,8 +14,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn report_extra_impl_obligation( &self, error_span: Span, - item_name: Symbol, - _impl_item_def_id: DefId, + impl_item_def_id: DefId, trait_item_def_id: DefId, requirement: &dyn fmt::Display, ) -> DiagnosticBuilder<'tcx> { @@ -27,6 +25,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let Some(trait_item_span) = self.tcx.hir().span_if_local(trait_item_def_id) { let span = self.tcx.sess.source_map().guess_head_span(trait_item_span); + let item_name = self.tcx.item_name(impl_item_def_id); err.span_label(span, format!("definition of `{}` from trait", item_name)); } @@ -36,7 +35,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } -pub fn report_object_safety_error( +pub fn report_object_safety_error<'tcx>( tcx: TyCtxt<'tcx>, span: Span, trait_def_id: DefId, diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index e8622b3c819..e1f3b548e97 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -55,7 +55,7 @@ pub struct Obligation<'tcx, T> { pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>; pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>; -impl PredicateObligation<'tcx> { +impl<'tcx> PredicateObligation<'tcx> { /// Flips the polarity of the inner predicate. /// /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. @@ -69,9 +69,19 @@ impl PredicateObligation<'tcx> { } } +impl TraitObligation<'_> { + /// Returns `true` if the trait predicate is considered `const` in its ParamEnv. + pub fn is_const(&self) -> bool { + match (self.predicate.skip_binder().constness, self.param_env.constness()) { + (ty::BoundConstness::ConstIfConst, hir::Constness::Const) => true, + _ => false, + } + } +} + // `PredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(PredicateObligation<'_>, 32); +static_assert_size!(PredicateObligation<'_>, 48); pub type PredicateObligations<'tcx> = Vec<PredicateObligation<'tcx>>; diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs index e2c13d20a9a..96af16c6687 100644 --- a/compiler/rustc_infer/src/traits/project.rs +++ b/compiler/rustc_infer/src/traits/project.rs @@ -10,7 +10,7 @@ use rustc_data_structures::{ }; use rustc_middle::ty::{self, Ty}; -pub use rustc_middle::traits::Reveal; +pub use rustc_middle::traits::{EvaluationResult, Reveal}; pub(crate) type UndoLog<'tcx> = snapshot_map::UndoLog<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>; @@ -80,7 +80,7 @@ pub struct ProjectionCacheKey<'tcx> { ty: ty::ProjectionTy<'tcx>, } -impl ProjectionCacheKey<'tcx> { +impl<'tcx> ProjectionCacheKey<'tcx> { pub fn new(ty: ty::ProjectionTy<'tcx>) -> Self { Self { ty } } @@ -92,7 +92,42 @@ pub enum ProjectionCacheEntry<'tcx> { Ambiguous, Recur, Error, - NormalizedTy(NormalizedTy<'tcx>), + NormalizedTy { + ty: NormalizedTy<'tcx>, + /// If we were able to successfully evaluate the + /// corresponding cache entry key during predicate + /// evaluation, then this field stores the final + /// result obtained from evaluating all of the projection + /// sub-obligations. During evaluation, we will skip + /// evaluating the cached sub-obligations in `ty` + /// if this field is set. Evaluation only + /// cares about the final result, so we don't + /// care about any region constraint side-effects + /// produced by evaluating the sub-boligations. + /// + /// Additionally, we will clear out the sub-obligations + /// entirely if we ever evaluate the cache entry (along + /// with all its sub obligations) to `EvaluatedToOk`. + /// This affects all users of the cache, not just evaluation. + /// Since a result of `EvaluatedToOk` means that there were + /// no region obligations that need to be tracked, it's + /// fine to forget about the sub-obligations - they + /// don't provide any additional information. However, + /// we do *not* discard any obligations when we see + /// `EvaluatedToOkModuloRegions` - we don't know + /// which sub-obligations may introduce region constraints, + /// so we keep them all to be safe. + /// + /// When we are not performing evaluation + /// (e.g. in `FulfillmentContext`), we ignore this field, + /// and always re-process the cached sub-obligations + /// (which may have been cleared out - see the above + /// paragraph). + /// This ensures that we do not lose any regions + /// constraints that arise from processing the + /// sub-obligations. + complete: Option<EvaluationResult>, + }, } impl<'tcx> ProjectionCacheStorage<'tcx> { @@ -149,10 +184,41 @@ impl<'tcx> ProjectionCache<'_, 'tcx> { debug!("Not overwriting Recur"); return; } - let fresh_key = map.insert(key, ProjectionCacheEntry::NormalizedTy(value)); + let fresh_key = + map.insert(key, ProjectionCacheEntry::NormalizedTy { ty: value, complete: None }); assert!(!fresh_key, "never started projecting `{:?}`", key); } + /// Mark the relevant projection cache key as having its derived obligations + /// complete, so they won't have to be re-computed (this is OK to do in a + /// snapshot - if the snapshot is rolled back, the obligations will be + /// marked as incomplete again). + pub fn complete(&mut self, key: ProjectionCacheKey<'tcx>, result: EvaluationResult) { + let mut map = self.map(); + match map.get(&key) { + Some(&ProjectionCacheEntry::NormalizedTy { ref ty, complete: _ }) => { + info!("ProjectionCacheEntry::complete({:?}) - completing {:?}", key, ty); + let mut ty = ty.clone(); + if result == EvaluationResult::EvaluatedToOk { + ty.obligations = vec![]; + } + map.insert(key, ProjectionCacheEntry::NormalizedTy { ty, complete: Some(result) }); + } + ref value => { + // Type inference could "strand behind" old cache entries. Leave + // them alone for now. + info!("ProjectionCacheEntry::complete({:?}) - ignoring {:?}", key, value); + } + }; + } + + pub fn is_complete(&mut self, key: ProjectionCacheKey<'tcx>) -> Option<EvaluationResult> { + self.map().get(&key).and_then(|res| match res { + ProjectionCacheEntry::NormalizedTy { ty: _, complete } => *complete, + _ => None, + }) + } + /// Indicates that trying to normalize `key` resulted in /// ambiguity. No point in trying it again then until we gain more /// type information (in which case, the "fully resolved" key will diff --git a/compiler/rustc_infer/src/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs index c4a2ecee096..20453eeb147 100644 --- a/compiler/rustc_infer/src/traits/structural_impls.rs +++ b/compiler/rustc_infer/src/traits/structural_impls.rs @@ -1,7 +1,7 @@ use crate::traits; use crate::traits::project::Normalized; use rustc_middle::ty; -use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; +use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeVisitor}; use std::fmt; use std::ops::ControlFlow; @@ -60,16 +60,20 @@ impl<'tcx> fmt::Debug for traits::MismatchedProjectionTypes<'tcx> { // TypeFoldable implementations. impl<'tcx, O: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Obligation<'tcx, O> { - fn super_fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self { - traits::Obligation { + fn try_super_fold_with<F: FallibleTypeFolder<'tcx>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + Ok(traits::Obligation { cause: self.cause, recursion_depth: self.recursion_depth, - predicate: self.predicate.fold_with(folder), - param_env: self.param_env.fold_with(folder), - } + predicate: self.predicate.try_fold_with(folder)?, + param_env: self.param_env.try_fold_with(folder)?, + }) } fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { - self.predicate.visit_with(visitor) + self.predicate.visit_with(visitor)?; + self.param_env.visit_with(visitor) } } diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index c839f824d1c..674c75fdee5 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -3,8 +3,9 @@ use smallvec::smallvec; use crate::infer::outlives::components::{push_outlives_components, Component}; use crate::traits::{Obligation, ObligationCause, PredicateObligation}; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; -use rustc_middle::ty::{self, ToPredicate, TyCtxt, WithConstness}; +use rustc_middle::ty::{self, ToPredicate, TyCtxt}; use rustc_span::symbol::Ident; +use rustc_span::Span; pub fn anonymize_predicate<'tcx>( tcx: TyCtxt<'tcx>, @@ -19,7 +20,7 @@ pub struct PredicateSet<'tcx> { set: FxHashSet<ty::Predicate<'tcx>>, } -impl PredicateSet<'tcx> { +impl<'tcx> PredicateSet<'tcx> { pub fn new(tcx: TyCtxt<'tcx>) -> Self { Self { tcx, set: Default::default() } } @@ -39,7 +40,7 @@ impl PredicateSet<'tcx> { } } -impl Extend<ty::Predicate<'tcx>> for PredicateSet<'tcx> { +impl<'tcx> Extend<ty::Predicate<'tcx>> for PredicateSet<'tcx> { fn extend<I: IntoIterator<Item = ty::Predicate<'tcx>>>(&mut self, iter: I) { for pred in iter { self.insert(pred); @@ -97,6 +98,22 @@ pub fn elaborate_predicates<'tcx>( elaborate_obligations(tcx, obligations) } +pub fn elaborate_predicates_with_span<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: impl Iterator<Item = (ty::Predicate<'tcx>, Span)>, +) -> Elaborator<'tcx> { + let obligations = predicates + .map(|(predicate, span)| { + predicate_obligation( + predicate, + ty::ParamEnv::empty(), + ObligationCause::dummy_with_span(span), + ) + }) + .collect(); + elaborate_obligations(tcx, obligations) +} + pub fn elaborate_obligations<'tcx>( tcx: TyCtxt<'tcx>, mut obligations: Vec<PredicateObligation<'tcx>>, @@ -114,7 +131,7 @@ fn predicate_obligation<'tcx>( Obligation { cause, param_env, recursion_depth: 0, predicate } } -impl Elaborator<'tcx> { +impl<'tcx> Elaborator<'tcx> { pub fn filter_to_traits(self) -> FilterToTraits<Self> { FilterToTraits::new(self) } @@ -135,7 +152,7 @@ impl Elaborator<'tcx> { obligation.cause.clone(), ) }); - debug!("super_predicates: data={:?}", data); + debug!(?data, ?obligations, "super_predicates"); // Only keep those bounds that we haven't already seen. // This is necessary to prevent infinite recursion in some @@ -224,10 +241,19 @@ impl Elaborator<'tcx> { Component::UnresolvedInferenceVariable(_) => None, - Component::Projection(_) | Component::EscapingProjection(_) => { - // We can probably do more here. This - // corresponds to a case like `<T as - // Foo<'a>>::U: 'b`. + Component::Projection(projection) => { + // We might end up here if we have `Foo<<Bar as Baz>::Assoc>: 'a`. + // With this, we can deduce that `<Bar as Baz>::Assoc: 'a`. + let ty = + tcx.mk_projection(projection.item_def_id, projection.substs); + Some(ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate( + ty, r_min, + ))) + } + + Component::EscapingProjection(_) => { + // We might be able to do more here, but we don't + // want to deal with escaping vars right now. None } }) @@ -250,7 +276,7 @@ impl Elaborator<'tcx> { } } -impl Iterator for Elaborator<'tcx> { +impl<'tcx> Iterator for Elaborator<'tcx> { type Item = PredicateObligation<'tcx>; fn size_hint(&self) -> (usize, Option<usize>) { @@ -311,8 +337,8 @@ pub fn transitive_bounds_that_define_assoc_type<'tcx>( )); for (super_predicate, _) in super_predicates.predicates { let subst_predicate = super_predicate.subst_supertrait(tcx, &trait_ref); - if let Some(binder) = subst_predicate.to_opt_poly_trait_ref() { - stack.push(binder.value); + if let Some(binder) = subst_predicate.to_opt_poly_trait_pred() { + stack.push(binder.map_bound(|t| t.trait_ref)); } } @@ -345,8 +371,8 @@ impl<'tcx, I: Iterator<Item = PredicateObligation<'tcx>>> Iterator for FilterToT fn next(&mut self) -> Option<ty::PolyTraitRef<'tcx>> { while let Some(obligation) = self.base_iterator.next() { - if let Some(data) = obligation.predicate.to_opt_poly_trait_ref() { - return Some(data.value); + if let Some(data) = obligation.predicate.to_opt_poly_trait_pred() { + return Some(data.map_bound(|t| t.trait_ref)); } } None | 
