diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 72eff72c7f9ed..24caafb423e25 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -478,14 +478,9 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { let errci = ErrorConstraintInfo { fr, outlived_fr, category, span }; let mut diag = match (category, fr_is_local, outlived_fr_is_local) { - (ConstraintCategory::SolverRegionConstraint(span), _, _) => { - let mut d = self.dcx().struct_span_err( - span, - "unsatisfied lifetime constraint from -Zassumptions-on-binders :3", - ); - d.note("meoow :c"); - d - } + (ConstraintCategory::SolverRegionConstraint(span), _, _) => self + .dcx() + .struct_span_err(span, "higher-ranked lifetime bound could not be satisfied"), (ConstraintCategory::Return(kind), true, false) if self.regioncx.is_closure_fn_mut(fr) => { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 370d43d35d3ac..953f785271e9a 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -189,7 +189,6 @@ pub(crate) fn type_check<'tcx>( &mut converter, typeck.known_type_outlives_obligations, universal_region_relations.outlives.clone(), - infcx.tcx.def_span(infcx.root_def_id), ); } diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 70f09b1003045..6b565ff49f87d 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -63,12 +63,13 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { fn get_solver_region_constraint( &self, ) -> rustc_type_ir::region_constraint::RegionConstraint> { - self.inner.borrow().solver_region_constraint_storage.get_constraint() + self.inner.borrow().solver_region_constraint_storage.get_unspanned_constraint() } fn overwrite_solver_region_constraint( &self, constraint: rustc_type_ir::region_constraint::RegionConstraint>, + span: Span, ) { let mut inner = self.inner.borrow_mut(); use rustc_data_structures::undo_log::UndoLogs; @@ -76,7 +77,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { use crate::infer::UndoLog; let old_constraint = inner.solver_region_constraint_storage.get_constraint(); inner.undo_log.push(UndoLog::OverwriteSolverRegionConstraint { old_constraint }); - inner.solver_region_constraint_storage.overwrite_solver_region_constraint(constraint); + inner.solver_region_constraint_storage.overwrite(constraint, span); } fn universe_of_ty(&self, vid: ty::TyVid) -> Option { @@ -367,13 +368,14 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { fn register_solver_region_constraint( &self, c: rustc_type_ir::region_constraint::RegionConstraint>, + span: Span, ) { let mut inner = self.inner.borrow_mut(); use rustc_data_structures::undo_log::UndoLogs; use crate::infer::UndoLog; inner.undo_log.push(UndoLog::PushSolverRegionConstraint); - inner.solver_region_constraint_storage.push(c); + inner.solver_region_constraint_storage.push(c, span); } fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>, span: Span) { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 953b0f1b9d464..3e92557ebff46 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -60,9 +60,13 @@ pub mod region_constraints; pub mod relate; pub mod resolve; pub(crate) mod snapshot; +mod solver_region_constraints; mod type_variable; mod unify_key; +pub(crate) use solver_region_constraints::SolverRegionConstraint; +use solver_region_constraints::SolverRegionConstraintStorage; + /// `InferOk<'tcx, ()>` is used a lot. It may seem like a useless wrapper /// around `PredicateObligations<'tcx>`, but it has one important property: /// because `InferOk` is marked with `#[must_use]`, if you have a method @@ -1844,55 +1848,3 @@ impl<'tcx> InferCtxt<'tcx> { } } } - -type SolverRegionConstraint<'tcx> = - rustc_type_ir::region_constraint::RegionConstraint>; - -#[derive(Clone, Debug)] -struct SolverRegionConstraintStorage<'tcx>(SolverRegionConstraint<'tcx>); - -impl<'tcx> SolverRegionConstraintStorage<'tcx> { - fn new() -> Self { - SolverRegionConstraintStorage(SolverRegionConstraint::And(Box::new([]))) - } - - fn get_constraint(&self) -> SolverRegionConstraint<'tcx> { - self.0.clone() - } - - fn pop(&mut self) -> Option> { - match &mut self.0 { - SolverRegionConstraint::And(and) => { - let mut and = core::mem::take(and).into_iter().collect::>(); - let popped = and.pop()?; - self.0 = SolverRegionConstraint::And(and.into_boxed_slice()); - Some(popped) - } - _ => unreachable!(), - } - } - - #[instrument(level = "debug")] - fn push(&mut self, constraint: SolverRegionConstraint<'tcx>) { - match &mut self.0 { - SolverRegionConstraint::And(and) => { - let and = core::mem::take(and) - .into_iter() - .chain([constraint]) - .collect::>() - .into_boxed_slice(); - self.0 = SolverRegionConstraint::And(and); - } - _ => unreachable!(), - } - } - - #[instrument(level = "debug", skip(self))] - fn overwrite_solver_region_constraint(&mut self, constraint: SolverRegionConstraint<'tcx>) { - if !constraint.is_and() { - self.0 = SolverRegionConstraint::And(vec![constraint].into_boxed_slice()) - } else { - self.0 = constraint; - } - } -} diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 058aaa017cad4..9abba72a046e3 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -204,13 +204,12 @@ impl<'tcx> InferCtxt<'tcx> { pub fn destructure_solver_region_constraints_for_regionck( &self, outlives_env: &OutlivesEnvironment<'tcx>, - span: Span, ) { let assumptions = rustc_type_ir::region_constraint::Assumptions::new( outlives_env.known_type_outlives().into_iter().cloned().collect(), outlives_env.free_region_map().relation.clone(), ); - self.destructure_solver_region_constraints(assumptions, self, span); + self.destructure_solver_region_constraints(assumptions, self); } pub fn destructure_solver_region_constraints_for_borrowck( @@ -219,13 +218,12 @@ impl<'tcx> InferCtxt<'tcx> { conversion: impl TypeOutlivesDelegate<'tcx>, known_type_outlives: &[PolyTypeOutlivesPredicate<'tcx>], region_outlives: TransitiveRelation, - span: Span, ) { let assumptions = rustc_type_ir::region_constraint::Assumptions::new( known_type_outlives.into_iter().cloned().collect(), region_outlives.maybe_map(|r| Some(Region::new_var(self.tcx, r))).unwrap(), ); - self.destructure_solver_region_constraints(assumptions, conversion, span); + self.destructure_solver_region_constraints(assumptions, conversion); } #[instrument(level = "debug", skip(self, conversion))] @@ -233,14 +231,10 @@ impl<'tcx> InferCtxt<'tcx> { &self, assumptions: rustc_type_ir::region_constraint::Assumptions>, mut conversion: impl TypeOutlivesDelegate<'tcx>, - span: Span, ) { assert!(self.tcx.assumptions_on_binders()); assert!(self.next_trait_solver()); - let origin = SubregionOrigin::SolverRegionConstraint(span); - let category = origin.to_constraint_category(); - let constraint = self.inner.borrow().solver_region_constraint_storage.get_constraint(); debug!(?constraint); let constraint = @@ -258,16 +252,20 @@ impl<'tcx> InferCtxt<'tcx> { use rustc_type_ir::region_constraint::RegionConstraint::*; match c { - Ambiguity => { - self.dcx().err("unable to satisfy constraints involving placeholders due to unknown implied bounds"); + Ambiguity(span) => { + self.dcx() + .struct_span_err( + span, + "unable to satisfy constraints involving placeholders due to unknown implied bounds", + ) + .emit(); } - RegionOutlives(a, b) => { + RegionOutlives(a, b, span) => { + let origin = SubregionOrigin::SolverRegionConstraint(span); + let category = origin.to_constraint_category(); conversion.push_sub_region_constraint( - origin.clone(), - // we flip these because regionck is silly :> - b, - a, - category, + origin, // we flip these because regionck is silly :> + b, a, category, ); } // FIXME(-Zassumptions-on-binders): actually implement OR as an OR @@ -295,7 +293,7 @@ impl<'tcx> InferCtxt<'tcx> { assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot"); if self.tcx.assumptions_on_binders() { - self.destructure_solver_region_constraints_for_regionck(outlives_env, span); + self.destructure_solver_region_constraints_for_regionck(outlives_env); } // Must loop since the process of normalizing may itself register region obligations. diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index 09d8eb3bf9232..8006981f8e203 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -88,8 +88,7 @@ impl<'tcx> Rollback> for InferCtxtInner<'tcx> { ); } UndoLog::OverwriteSolverRegionConstraint { old_constraint } => { - self.solver_region_constraint_storage - .overwrite_solver_region_constraint(old_constraint); + self.solver_region_constraint_storage.overwrite_spanned(old_constraint); } UndoLog::PushTypeOutlivesConstraint => { let popped = self.region_obligations.pop(); diff --git a/compiler/rustc_infer/src/infer/solver_region_constraints.rs b/compiler/rustc_infer/src/infer/solver_region_constraints.rs new file mode 100644 index 0000000000000..4512bb00ad6b9 --- /dev/null +++ b/compiler/rustc_infer/src/infer/solver_region_constraints.rs @@ -0,0 +1,73 @@ +use rustc_middle::ty::TyCtxt; +use rustc_span::Span; +use rustc_type_ir::region_constraint::{ + RegionConstraint as UnspannedRegionConstraint, SpannedRegionConstraint, +}; +use tracing::instrument; + +pub(crate) type SolverRegionConstraint<'tcx> = SpannedRegionConstraint>; + +#[derive(Clone, Debug)] +pub(crate) struct SolverRegionConstraintStorage<'tcx>(SolverRegionConstraint<'tcx>); + +impl<'tcx> SolverRegionConstraintStorage<'tcx> { + pub(crate) fn new() -> Self { + Self(SolverRegionConstraint::And(Box::new([]))) + } + + pub(crate) fn get_constraint(&self) -> SolverRegionConstraint<'tcx> { + self.0.clone() + } + + pub(crate) fn get_unspanned_constraint(&self) -> UnspannedRegionConstraint> { + self.0.clone().without_spans() + } + + pub(crate) fn pop(&mut self) -> Option> { + match &mut self.0 { + SolverRegionConstraint::And(and) => { + let mut and = core::mem::take(and).into_vec(); + let popped = and.pop()?; + self.0 = SolverRegionConstraint::And(and.into_boxed_slice()); + Some(popped) + } + _ => unreachable!(), + } + } + + #[instrument(level = "debug")] + pub(crate) fn push(&mut self, constraint: UnspannedRegionConstraint>, span: Span) { + let constraint = constraint.with_span(span); + match &mut self.0 { + SolverRegionConstraint::And(and) => { + let and = core::mem::take(and) + .into_iter() + .chain([constraint]) + .collect::>() + .into_boxed_slice(); + self.0 = SolverRegionConstraint::And(and); + } + _ => unreachable!(), + } + } + + #[instrument(level = "debug", skip(self))] + pub(crate) fn overwrite( + &mut self, + constraint: UnspannedRegionConstraint>, + span: Span, + ) { + self.overwrite_spanned(constraint.with_span(span)); + } + + pub(crate) fn overwrite_spanned(&mut self, constraint: SolverRegionConstraint<'tcx>) { + if matches!(constraint, SolverRegionConstraint::And(_)) { + self.0 = constraint; + } else { + self.0 = SolverRegionConstraint::And(vec![constraint].into_boxed_slice()); + } + } +} + +#[cfg(test)] +mod tests; diff --git a/compiler/rustc_infer/src/infer/solver_region_constraints/tests.rs b/compiler/rustc_infer/src/infer/solver_region_constraints/tests.rs new file mode 100644 index 0000000000000..339a454753f0b --- /dev/null +++ b/compiler/rustc_infer/src/infer/solver_region_constraints/tests.rs @@ -0,0 +1,58 @@ +use rustc_span::{BytePos, DUMMY_SP, Span}; +use rustc_type_ir::region_constraint::evaluate_solver_constraint; + +use super::SolverRegionConstraint; + +fn and(constraints: Vec>) -> SolverRegionConstraint<'static> { + SolverRegionConstraint::And(constraints.into_boxed_slice()) +} + +fn or(constraints: Vec>) -> SolverRegionConstraint<'static> { + SolverRegionConstraint::Or(constraints.into_boxed_slice()) +} + +fn ambiguity() -> SolverRegionConstraint<'static> { + SolverRegionConstraint::Ambiguity(DUMMY_SP) +} + +#[test] +fn evaluation_is_span_agnostic() { + let constraints = [ + ambiguity(), + and(vec![]), + or(vec![]), + and(vec![and(vec![]), ambiguity()]), + and(vec![ambiguity(), or(vec![])]), + or(vec![or(vec![]), ambiguity()]), + or(vec![ambiguity(), and(vec![])]), + and(vec![or(vec![or(vec![]), ambiguity()]), or(vec![ambiguity(), and(vec![])])]), + ]; + + for constraint in constraints { + let expected = evaluate_solver_constraint(&constraint.clone().without_spans()); + let actual = evaluate_solver_constraint(&constraint).without_spans(); + assert_eq!(actual, expected); + } +} + +#[test] +fn evaluation_preserves_first_ambiguity_span() { + let first = Span::with_root_ctxt(BytePos(1), BytePos(2)); + let second = Span::with_root_ctxt(BytePos(3), BytePos(4)); + + for constraint in [ + and(vec![ + SolverRegionConstraint::Ambiguity(first), + SolverRegionConstraint::Ambiguity(second), + ]), + or(vec![ + SolverRegionConstraint::Ambiguity(first), + SolverRegionConstraint::Ambiguity(second), + ]), + ] { + assert!(matches!( + evaluate_solver_constraint(&constraint), + SolverRegionConstraint::Ambiguity(span) if span == first + )); + } +} diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 5d54065260888..fde4ee583d835 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -133,7 +133,7 @@ where span, ), ExternalRegionConstraints::NextGen(r) => { - delegate.register_solver_region_constraint(r.clone()) + delegate.register_solver_region_constraint(r.clone(), span) } }; register_new_opaque_types(delegate, opaque_types, span); diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 2ec0b6fc9e2e8..ec631f8d58a0e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1332,7 +1332,7 @@ where } pub(super) fn register_solver_region_constraint(&self, c: RegionConstraint) { - self.delegate.register_solver_region_constraint(c); + self.delegate.register_solver_region_constraint(c, self.origin_span); } pub(super) fn register_ty_outlives(&self, ty: I::Ty, lt: Region) { diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs index f31f292240a56..b15f215c38dd0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs @@ -137,7 +137,7 @@ where eagerly_handle_placeholders_in_universe(&**self.delegate, constraint, u) }); - self.delegate.overwrite_solver_region_constraint(constraint.clone()); + self.delegate.overwrite_solver_region_constraint(constraint.clone(), self.origin_span); if constraint.is_false() { Err(NoSolution) @@ -175,13 +175,13 @@ where fn destructure_component(&mut self, c: &Component, r: Region) -> RegionConstraint { use Component::*; match c { - Region(c_r) => RegionConstraint::RegionOutlives(*c_r, r), + Region(c_r) => RegionConstraint::RegionOutlives(*c_r, r, ()), Placeholder(p) => { - RegionConstraint::PlaceholderTyOutlives(Ty::new_placeholder(self.cx(), *p), r) + RegionConstraint::PlaceholderTyOutlives(Ty::new_placeholder(self.cx(), *p), r, ()) } // The alias is either rigid or ambiguous in which case we'll return with ambiguity. Alias(_, alias) => self.destructure_alias_outlives(*alias, r), - UnresolvedInferenceVariable(_) => RegionConstraint::Ambiguity, + UnresolvedInferenceVariable(_) => RegionConstraint::Ambiguity(()), Param(_) => panic!("Params should have been canonicalized to placeholders"), EscapingAlias(components) => self.destructure_components(components, r), } @@ -202,11 +202,11 @@ where ) -> RegionConstraint { let item_bounds = rustc_type_ir::outlives::declared_bounds_from_definition(self.cx(), alias) - .map(|bound| RegionConstraint::RegionOutlives(bound, r)); + .map(|bound| RegionConstraint::RegionOutlives(bound, r, ())); let item_bound_outlives = RegionConstraint::Or(item_bounds.collect()); let where_clause_outlives = - RegionConstraint::AliasTyOutlivesViaEnv(Binder::dummy((alias, r))); + RegionConstraint::AliasTyOutlivesViaEnv(Binder::dummy((alias, r)), ()); let mut components = Default::default(); rustc_type_ir::outlives::compute_alias_components_recursive( diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 8a869df067301..1f9b2ad148744 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -120,7 +120,7 @@ where if self.cx().assumptions_on_binders() { let constraint = - rustc_type_ir::region_constraint::RegionConstraint::RegionOutlives(a, b); + rustc_type_ir::region_constraint::RegionConstraint::RegionOutlives(a, b, ()); self.register_solver_region_constraint(constraint); } else { self.register_region_outlives(a, b, VisibleForLeakCheck::Yes); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 2c959acbc2a86..c0b69b44f125b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -297,7 +297,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { SubregionOrigin::SolverRegionConstraint(span) => { RegionOriginNote::Plain { span, - msg: msg!("this diagnostic is currently WIP while -Zassumptions-on-binders is incomplete"), + msg: msg!("...so that a higher-ranked lifetime bound can be satisfied"), } .add_to_diag(err); } @@ -569,14 +569,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { notes: instantiated.into_iter().chain(must_outlive).collect(), }) } - SubregionOrigin::SolverRegionConstraint(span) => { - let mut d = self.dcx().struct_span_err( - span, - "unsatisfied lifetime constraint from -Zassumptions-on-binders :3", - ); - d.note("meoow :c"); - d - } + SubregionOrigin::SolverRegionConstraint(span) => self + .dcx() + .struct_span_err(span, "higher-ranked lifetime bound could not be satisfied"), }; if sub.is_error() || sup.is_error() { err.downgrade_to_delayed_bug(); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index b50ace11bbd75..bd45e2b8b1e46 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -312,6 +312,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { FulfillmentErrorCode::Project(ref e) => { self.report_projection_error(&error.obligation, e) } + FulfillmentErrorCode::Outlives => self + .dcx() + .struct_span_err( + error.obligation.cause.span, + "higher-ranked lifetime bound could not be satisfied", + ) + .emit(), FulfillmentErrorCode::Ambiguity { overflow: None } => { self.maybe_report_ambiguity(&error.obligation) } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index 0c365e9dea707..45fca9a64b8db 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -66,6 +66,9 @@ pub(super) fn fulfillment_error_for_no_solution<'tcx>( let expected_found = ExpectedFound::new(b, a); FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) } + ty::PredicateKind::Clause( + ty::ClauseKind::RegionOutlives(_) | ty::ClauseKind::TypeOutlives(_), + ) if infcx.tcx.assumptions_on_binders() => FulfillmentErrorCode::Outlives, ty::PredicateKind::Clause(_) | ty::PredicateKind::DynCompatible(_) | ty::PredicateKind::Ambiguous => { diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 8179b0f6f01a1..492becb45fa0b 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -1069,6 +1069,7 @@ impl<'tcx> FromSolverError<'tcx, OldSolverError<'tcx>> for ScrubbedTraitError<'t match error.0.error { FulfillmentErrorCode::Select(_) | FulfillmentErrorCode::Project(_) + | FulfillmentErrorCode::Outlives | FulfillmentErrorCode::Subtype(_, _) | FulfillmentErrorCode::ConstEquate(_, _) => ScrubbedTraitError::TrueError, FulfillmentErrorCode::Ambiguity { overflow: _ } => ScrubbedTraitError::Ambiguity, diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 919bc3cdcc3d9..cf3a581f28415 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -99,6 +99,7 @@ impl<'tcx> FulfillmentError<'tcx> { match self.code { FulfillmentErrorCode::Select(_) | FulfillmentErrorCode::Project(_) + | FulfillmentErrorCode::Outlives | FulfillmentErrorCode::Subtype(_, _) | FulfillmentErrorCode::ConstEquate(_, _) => true, FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => { @@ -115,6 +116,8 @@ pub enum FulfillmentErrorCode<'tcx> { Cycle(PredicateObligations<'tcx>), Select(SelectionError<'tcx>), Project(MismatchedProjectionTypes<'tcx>), + /// An outlives constraint emitted for `-Zassumptions-on-binders` was unsatisfiable. + Outlives, Subtype(ExpectedFound>, TypeError<'tcx>), // always comes from a SubtypePredicate ConstEquate(ExpectedFound>, TypeError<'tcx>), Ambiguity { @@ -130,6 +133,7 @@ impl<'tcx> Debug for FulfillmentErrorCode<'tcx> { match *self { FulfillmentErrorCode::Select(ref e) => write!(f, "{e:?}"), FulfillmentErrorCode::Project(ref e) => write!(f, "{e:?}"), + FulfillmentErrorCode::Outlives => write!(f, "CodeOutlivesError"), FulfillmentErrorCode::Subtype(ref a, ref b) => { write!(f, "CodeSubtypeError({a:?}, {b:?})") } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index d8c11542be4a3..91e475721421b 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -399,6 +399,7 @@ pub trait InferCtxtLike: Sized { fn overwrite_solver_region_constraint( &self, constraint: crate::region_constraint::RegionConstraint, + span: ::Span, ); fn universe_of_ty(&self, ty: ty::TyVid) -> Option; @@ -519,6 +520,7 @@ pub trait InferCtxtLike: Sized { fn register_solver_region_constraint( &self, c: crate::region_constraint::RegionConstraint, + span: ::Span, ); fn register_ty_outlives( diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index e42e96901b74d..361be4415acdc 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -90,10 +90,10 @@ impl Assumptions { } } -#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner)] -pub enum RegionConstraint { - Ambiguity, - RegionOutlives(Region, Region), +#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, S)] +pub enum RegionConstraint { + Ambiguity(S), + RegionOutlives(Region, Region, S), /// Requirement that a (potentially higher ranked) alias outlives some (potentially higher ranked) /// region due to an assumption in the environment. This cannot be satisfied via component outlives /// or item bounds. @@ -103,7 +103,7 @@ pub enum RegionConstraint { /// /// We eagerly destructure alias outlives requirements into region outlives requirements corresponding to /// component outlives & item bound outlives rules, leaving only param env candidates. - AliasTyOutlivesViaEnv(Binder, Region)>), + AliasTyOutlivesViaEnv(Binder, Region)>, S), /// This is an `I::Ty` for two reasons: /// 1. We need the type visitable impl to be able to `visit_ty` on this so canonicalization /// knows about the placeholder @@ -113,12 +113,19 @@ pub enum RegionConstraint { /// /// We cannot eagerly look at assumptions as we are usually working with an incomplete set of assumptions /// and there may wind up being assumptions we can use to prove this when we're in a smaller universe. - PlaceholderTyOutlives(I::Ty, Region), + PlaceholderTyOutlives(I::Ty, Region, S), - And(Box<[RegionConstraint]>), - Or(Box<[RegionConstraint]>), + And(Box<[RegionConstraint]>), + Or(Box<[RegionConstraint]>), } +/// A solver region constraint together with the span that caused each leaf constraint. +/// +/// Solver query responses use [`RegionConstraint`] so source locations do not participate in +/// candidate equality or caching. Spans are attached when responses are applied to an inference +/// context. +pub type SpannedRegionConstraint = RegionConstraint::Span>; + // This is not a derived impl because a perfect derive leads to inductive // cycle causing the trait to never actually be implemented. #[cfg(feature = "nightly")] @@ -139,15 +146,15 @@ where std::mem::discriminant(self).stable_hash(hcx, hasher); match self { - Ambiguity => (), - RegionOutlives(a, b) => { + Ambiguity(_) => (), + RegionOutlives(a, b, _) => { a.stable_hash(hcx, hasher); b.stable_hash(hcx, hasher); } - AliasTyOutlivesViaEnv(outlives) => { + AliasTyOutlivesViaEnv(outlives, _) => { outlives.stable_hash(hcx, hasher); } - PlaceholderTyOutlives(a, b) => { + PlaceholderTyOutlives(a, b, _) => { a.stable_hash(hcx, hasher); b.stable_hash(hcx, hasher); } @@ -165,15 +172,19 @@ where } } -impl TypeFoldable for RegionConstraint { +impl TypeFoldable for RegionConstraint { fn try_fold_with>(self, f: &mut F) -> Result { use RegionConstraint::*; Ok(match self { - Ambiguity => self, - RegionOutlives(a, b) => RegionOutlives(a.try_fold_with(f)?, b.try_fold_with(f)?), - AliasTyOutlivesViaEnv(outlives) => AliasTyOutlivesViaEnv(outlives.try_fold_with(f)?), - PlaceholderTyOutlives(a, b) => { - PlaceholderTyOutlives(a.try_fold_with(f)?, b.try_fold_with(f)?) + Ambiguity(_) => self, + RegionOutlives(a, b, span) => { + RegionOutlives(a.try_fold_with(f)?, b.try_fold_with(f)?, span) + } + AliasTyOutlivesViaEnv(outlives, span) => { + AliasTyOutlivesViaEnv(outlives.try_fold_with(f)?, span) + } + PlaceholderTyOutlives(a, b, span) => { + PlaceholderTyOutlives(a.try_fold_with(f)?, b.try_fold_with(f)?, span) } And(and) => { let mut new_and = Vec::new(); @@ -195,10 +206,14 @@ impl TypeFoldable for RegionConstraint { fn fold_with>(self, f: &mut F) -> Self { use RegionConstraint::*; match self { - Ambiguity => self, - RegionOutlives(a, b) => RegionOutlives(a.fold_with(f), b.fold_with(f)), - AliasTyOutlivesViaEnv(outlives) => AliasTyOutlivesViaEnv(outlives.fold_with(f)), - PlaceholderTyOutlives(a, b) => PlaceholderTyOutlives(a.fold_with(f), b.fold_with(f)), + Ambiguity(_) => self, + RegionOutlives(a, b, span) => RegionOutlives(a.fold_with(f), b.fold_with(f), span), + AliasTyOutlivesViaEnv(outlives, span) => { + AliasTyOutlivesViaEnv(outlives.fold_with(f), span) + } + PlaceholderTyOutlives(a, b, span) => { + PlaceholderTyOutlives(a.fold_with(f), b.fold_with(f), span) + } And(and) => { let mut new_and = Vec::new(); for a in and { @@ -217,15 +232,15 @@ impl TypeFoldable for RegionConstraint { } } -impl TypeVisitable for RegionConstraint { +impl TypeVisitable for RegionConstraint { fn visit_with>(&self, f: &mut F) -> F::Result { use core::ops::ControlFlow::*; use RegionConstraint::*; match self { - Ambiguity => (), - RegionOutlives(a, b) => { + Ambiguity(_) => (), + RegionOutlives(a, b, _) => { if let b @ Break(_) = a.visit_with(f).branch() { return F::Result::from_branch(b); }; @@ -233,10 +248,10 @@ impl TypeVisitable for RegionConstraint { return F::Result::from_branch(b); }; } - AliasTyOutlivesViaEnv(outlives) => { + AliasTyOutlivesViaEnv(outlives, _) => { return outlives.visit_with(f); } - PlaceholderTyOutlives(a, b) => { + PlaceholderTyOutlives(a, b, _) => { if let b @ Break(_) = a.visit_with(f).branch() { return F::Result::from_branch(b); }; @@ -264,13 +279,38 @@ impl TypeVisitable for RegionConstraint { } } -impl Default for RegionConstraint { +impl RegionConstraint { + fn map_spans(self, f: &mut impl FnMut(S) -> T) -> RegionConstraint { + use RegionConstraint::*; + + match self { + Ambiguity(span) => Ambiguity(f(span)), + RegionOutlives(a, b, span) => RegionOutlives(a, b, f(span)), + AliasTyOutlivesViaEnv(outlives, span) => AliasTyOutlivesViaEnv(outlives, f(span)), + PlaceholderTyOutlives(ty, region, span) => PlaceholderTyOutlives(ty, region, f(span)), + And(constraints) => And(constraints.into_iter().map(|c| c.map_spans(f)).collect()), + Or(constraints) => Or(constraints.into_iter().map(|c| c.map_spans(f)).collect()), + } + } + + pub fn without_spans(self) -> RegionConstraint { + self.map_spans(&mut |_| ()) + } +} + +impl RegionConstraint { + pub fn with_span(self, span: S) -> RegionConstraint { + self.map_spans(&mut |_| span.clone()) + } +} + +impl Default for RegionConstraint { fn default() -> Self { Self::new_true() } } -impl RegionConstraint { +impl RegionConstraint { pub fn new_true() -> Self { RegionConstraint::And(Box::new([])) } @@ -297,14 +337,14 @@ impl RegionConstraint { matches!(self, Self::Or(_)) } - pub fn unwrap_or(self) -> Box<[RegionConstraint]> { + pub fn unwrap_or(self) -> Box<[RegionConstraint]> { match self { Self::Or(ors) => ors, _ => panic!("`unwrap_or` on non-Or: {self:?}"), } } - pub fn unwrap_and(self) -> Box<[RegionConstraint]> { + pub fn unwrap_and(self) -> Box<[RegionConstraint]> { match self { Self::And(ands) => ands, _ => panic!("`unwrap_and` on non-And: {self:?}"), @@ -316,10 +356,10 @@ impl RegionConstraint { } pub fn is_ambig(&self) -> bool { - matches!(self, Self::Ambiguity) + matches!(self, Self::Ambiguity(_)) } - pub fn and(self, other: RegionConstraint) -> RegionConstraint { + pub fn and(self, other: RegionConstraint) -> RegionConstraint { use RegionConstraint::*; match (self, other) { @@ -341,9 +381,9 @@ impl RegionConstraint { pub fn canonical_form(self) -> Self { use RegionConstraint::*; - fn permutations( - ors: &[Vec>], - ) -> Vec>> { + fn permutations( + ors: &[Vec>], + ) -> Vec>> { match ors { [] => vec![vec![]], [or1] => { @@ -420,7 +460,7 @@ impl RegionConstraint { fn is_leaf_constraint(&self) -> bool { use RegionConstraint::*; match self { - Ambiguity + Ambiguity(_) | RegionOutlives(..) | AliasTyOutlivesViaEnv(..) | PlaceholderTyOutlives(..) => true, @@ -519,10 +559,10 @@ fn compute_new_region_constraints, I: Interne for c in constraints { match c { And(..) | Or(..) => unreachable!(), - Ambiguity | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => { + Ambiguity(_) | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => { new_constraints.push(c.clone()) } - RegionOutlives(r1, r2) => { + RegionOutlives(r1, r2, _) => { regions.insert(r1); regions.insert(r2); region_flows_builder.add(r2, r1); @@ -546,7 +586,7 @@ fn compute_new_region_constraints, I: Interne }; if is_placeholder_like(*r) && is_placeholder_like(*ub) { - new_constraints.push(RegionOutlives(*ub, *r)); + new_constraints.push(RegionOutlives(*ub, *r, ())); } } } @@ -556,57 +596,56 @@ fn compute_new_region_constraints, I: Interne /// Evaluate ANDs and ORs to true/false/ambiguous based on whether their arguments are true/false/ambiguous #[instrument(level = "debug", ret)] -pub fn evaluate_solver_constraint( - constraint: &RegionConstraint, -) -> RegionConstraint { +pub fn evaluate_solver_constraint( + constraint: &RegionConstraint, +) -> RegionConstraint { use RegionConstraint::*; match constraint { - Ambiguity | RegionOutlives(..) | AliasTyOutlivesViaEnv(..) | PlaceholderTyOutlives(..) => { - constraint.clone() - } + Ambiguity(_) + | RegionOutlives(..) + | AliasTyOutlivesViaEnv(..) + | PlaceholderTyOutlives(..) => constraint.clone(), And(and) => { let mut and_constraints = Vec::new(); - let mut is_ambiguous_constraint = false; + let mut ambiguity = None; for c in and.iter() { let evaluated_constraint = evaluate_solver_constraint(c); if evaluated_constraint.is_true() { // - do nothing } else if evaluated_constraint.is_false() { return RegionConstraint::new_false(); - } else if evaluated_constraint.is_ambig() { - is_ambiguous_constraint = true; + } else if let Ambiguity(span) = evaluated_constraint { + ambiguity.get_or_insert(span); } else { and_constraints.push(evaluated_constraint); } } - if is_ambiguous_constraint { - RegionConstraint::Ambiguity - } else { - RegionConstraint::And(and_constraints.into_boxed_slice()) - } + ambiguity.map_or_else( + || RegionConstraint::And(and_constraints.into_boxed_slice()), + RegionConstraint::Ambiguity, + ) } Or(or) => { let mut or_constraints = Vec::new(); - let mut is_ambiguous_constraint = false; + let mut ambiguity = None; for c in or.iter() { let evaluated_constraint = evaluate_solver_constraint(c); if evaluated_constraint.is_false() { // do nothing } else if evaluated_constraint.is_true() { return RegionConstraint::new_true(); - } else if evaluated_constraint.is_ambig() { - is_ambiguous_constraint = true; + } else if let Ambiguity(span) = evaluated_constraint { + ambiguity.get_or_insert(span); } else { or_constraints.push(evaluated_constraint); } } - if is_ambiguous_constraint { - RegionConstraint::Ambiguity - } else { - RegionConstraint::Or(or_constraints.into_boxed_slice()) - } + ambiguity.map_or_else( + || RegionConstraint::Or(or_constraints.into_boxed_slice()), + RegionConstraint::Ambiguity, + ) } } } @@ -652,11 +691,11 @@ fn pull_region_outlives_constraints_out_of_universe< use RegionConstraint::*; match constraint { - Ambiguity | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => { + Ambiguity(_) | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => { assert!(max_universe(infcx, constraint.clone()) < u); constraint } - RegionOutlives(region_1, region_2) => { + RegionOutlives(region_1, region_2, ()) => { let region_1_u = max_universe(infcx, region_1); let region_2_u = max_universe(infcx, region_2); @@ -666,7 +705,7 @@ fn pull_region_outlives_constraints_out_of_universe< let assumptions = match assumptions { Some(assumptions) => assumptions, - None => return RegionConstraint::Ambiguity, + None => return RegionConstraint::Ambiguity(()), }; let mut candidates = vec![]; @@ -682,7 +721,7 @@ fn pull_region_outlives_constraints_out_of_universe< // As long as any region outlived by `region_1` outlives any region region which // `region_2` outlives, we know that `region_1: region_2` holds. In other words, // there exists some set of 4 regions for which `'r1: 'i1` `'i1: 'i2` `'i2: 'r2` - candidates.push(RegionOutlives(ub, lb)); + candidates.push(RegionOutlives(ub, lb, ())); } } @@ -705,23 +744,25 @@ fn pull_region_outlives_constraints_out_of_universe< pub fn destructure_type_outlives_constraints_in_root< Infcx: InferCtxtLike, I: Interner, + S: Clone + std::fmt::Debug, >( infcx: &Infcx, - constraint: RegionConstraint, + constraint: RegionConstraint, assumptions: &Assumptions, -) -> RegionConstraint { +) -> RegionConstraint { use RegionConstraint::*; match constraint { - Ambiguity | RegionOutlives(..) => constraint, - PlaceholderTyOutlives(ty, r) => { + Ambiguity(_) | RegionOutlives(..) => constraint, + PlaceholderTyOutlives(ty, r, span) => { Or(regions_outlived_by_placeholder(ty, assumptions, infcx.cx()) - .map(move |assumption_r| RegionOutlives(assumption_r, r)) + .map(move |assumption_r| RegionOutlives(assumption_r, r, span.clone())) .collect::>() .into_boxed_slice()) } - AliasTyOutlivesViaEnv(bound_outlives) => { + AliasTyOutlivesViaEnv(bound_outlives, span) => { alias_outlives_candidates_from_assumptions(infcx, bound_outlives, assumptions) + .with_span(span) } And(constraints) => And(constraints .into_iter() @@ -767,8 +808,8 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< use RegionConstraint::*; match constraint { - Ambiguity | RegionOutlives(..) => constraint, - PlaceholderTyOutlives(ty, region) => { + Ambiguity(_) | RegionOutlives(..) => constraint, + PlaceholderTyOutlives(ty, region, ()) => { let ty_u = max_universe(infcx, ty); let region_u = max_universe(infcx, region); @@ -778,7 +819,7 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< let assumptions = match assumptions { Some(assumptions) => assumptions, - None => return Ambiguity, + None => return Ambiguity(()), }; let mut candidates = vec![]; @@ -787,7 +828,7 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< // smaller universe candidates.extend( regions_outlived_by_placeholder(ty, assumptions, infcx.cx()) - .map(move |assumption_r| RegionOutlives(assumption_r, region)), + .map(move |assumption_r| RegionOutlives(assumption_r, region, ())), ); // We can express `!T: 'region` as `!T: 'r` where `'r: 'region`. This is only necessary @@ -797,13 +838,13 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< candidates.extend( regions_outliving::(region, assumptions, infcx.cx()) .filter(|r| max_universe(infcx, *r) < u) - .map(|r| PlaceholderTyOutlives(ty, r)), + .map(|r| PlaceholderTyOutlives(ty, r, ())), ); } Or(candidates.into_boxed_slice()) } - AliasTyOutlivesViaEnv(bound_outlives) => { + AliasTyOutlivesViaEnv(bound_outlives, ()) => { let mut candidates = Vec::new(); // given there can be higher ranked assumptions, e.g. `for<'a> >::Assoc: 'c`, that @@ -839,21 +880,21 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< escaping_outlives, I::BoundVarKinds::from_vars(infcx.cx(), bound_vars), ); - let candidate = RegionConstraint::AliasTyOutlivesViaEnv(bound_outlives); + let candidate = RegionConstraint::AliasTyOutlivesViaEnv(bound_outlives, ()); if max_universe(infcx, candidate.clone()) < u { candidates.push(candidate); } else { // `PlaceholderReplacer` only folds regions. A non-lifetime binder can leave // a placeholder type in `u`, so this type-outlives constraint cannot be // handled by the region-outlives-only eager placeholder machinery. - candidates.push(Ambiguity); + candidates.push(Ambiguity(())); } } let assumptions = match assumptions { Some(assumptions) => assumptions, None => { - candidates.push(Ambiguity); + candidates.push(Ambiguity(())); return Or(candidates.into_boxed_slice()); } }; @@ -896,11 +937,11 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< .filter(|r2| max_universe(infcx, *r2) < u) { let candidate = - AliasTyOutlivesViaEnv(bound_alias.map_bound(|alias| (alias, r2))); + AliasTyOutlivesViaEnv(bound_alias.map_bound(|alias| (alias, r2)), ()); if max_universe(infcx, candidate.clone()) < u { candidates.push(candidate); } else { - candidates.push(Ambiguity); + candidates.push(Ambiguity(())); } } } @@ -909,7 +950,7 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< // let's be conservative and not let alias outlives' cause NoSolution // in coherence match infcx.typing_mode_raw() { - TypingMode::Coherence => candidates.push(RegionConstraint::Ambiguity), + TypingMode::Coherence => candidates.push(RegionConstraint::Ambiguity(())), TypingMode::Typeck { .. } | TypingMode::ErasedNotCoherence { .. } | TypingMode::PostTypeckUntilBorrowck { .. } @@ -1047,7 +1088,7 @@ fn alias_outlives_candidates_from_assumptions let mut relation = HigherRankedAliasMatcher { infcx, - region_constraints: vec![RegionConstraint::RegionOutlives(r2, r)], + region_constraints: vec![RegionConstraint::RegionOutlives(r2, r, ())], }; // FIXME(#155345): Both sides should be rigid in the future. @@ -1118,8 +1159,8 @@ impl<'a, Infcx: InferCtxtLike, I: Interner> TypeRelation fn regions(&mut self, a: Region, b: Region) -> RelateResult> { if a != b { - self.region_constraints.push(RegionConstraint::RegionOutlives(a, b)); - self.region_constraints.push(RegionConstraint::RegionOutlives(b, a)); + self.region_constraints.push(RegionConstraint::RegionOutlives(a, b, ())); + self.region_constraints.push(RegionConstraint::RegionOutlives(b, a, ())); } Ok(a) } diff --git a/tests/ui/assumptions_on_binders/alias_outlives.rs b/tests/ui/assumptions_on_binders/alias_outlives.rs index c234773fb10d8..b1be4ce6c1555 100644 --- a/tests/ui/assumptions_on_binders/alias_outlives.rs +++ b/tests/ui/assumptions_on_binders/alias_outlives.rs @@ -23,11 +23,22 @@ where } fn borrowck_env_fail<'a, T: AliasHaver>() -//~^ ERROR: unsatisfied lifetime constraint from -Zassumptions-on-binders where ::Assoc: 'a, { let _: ReqTrait; + //~^ ERROR: higher-ranked lifetime bound could not be satisfied +} + +fn borrowck_multiple_origins_fail<'a, 'b, T: AliasHaver, U: AliasHaver>() +where + ::Assoc: 'a, + ::Assoc: 'b, +{ + let _: ReqTrait; + //~^ ERROR: higher-ranked lifetime bound could not be satisfied + let _: ReqTrait; + //~^ ERROR: higher-ranked lifetime bound could not be satisfied } const REGIONCK_ENV_PASS<'a, T: AliasHaver>: ReqTrait = todo!() @@ -35,7 +46,7 @@ where ::Assoc: 'static; const REGIONCK_ENV_FAIL<'a, T: AliasHaver>: ReqTrait = todo!() -//~^ ERROR: unsatisfied lifetime constraint from -Zassumptions-on-binders +//~^ ERROR: higher-ranked lifetime bound could not be satisfied where ::Assoc: 'a; diff --git a/tests/ui/assumptions_on_binders/alias_outlives.stderr b/tests/ui/assumptions_on_binders/alias_outlives.stderr index bd3819798737d..4caa691a110c0 100644 --- a/tests/ui/assumptions_on_binders/alias_outlives.stderr +++ b/tests/ui/assumptions_on_binders/alias_outlives.stderr @@ -1,21 +1,26 @@ -error: unsatisfied lifetime constraint from -Zassumptions-on-binders :3 - --> $DIR/alias_outlives.rs:37:1 +error: higher-ranked lifetime bound could not be satisfied + --> $DIR/alias_outlives.rs:48:45 | LL | const REGIONCK_ENV_FAIL<'a, T: AliasHaver>: ReqTrait = todo!() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ + +error: higher-ranked lifetime bound could not be satisfied + --> $DIR/alias_outlives.rs:29:12 | - = note: meoow :c +LL | let _: ReqTrait; + | ^^^^^^^^^^^^^^^^^^ -error: unsatisfied lifetime constraint from -Zassumptions-on-binders :3 - --> $DIR/alias_outlives.rs:25:1 +error: higher-ranked lifetime bound could not be satisfied + --> $DIR/alias_outlives.rs:38:12 | -LL | / fn borrowck_env_fail<'a, T: AliasHaver>() -LL | | -LL | | where -LL | | ::Assoc: 'a, - | |_________________________________^ +LL | let _: ReqTrait; + | ^^^^^^^^^^^^^^^^^^ + +error: higher-ranked lifetime bound could not be satisfied + --> $DIR/alias_outlives.rs:40:12 | - = note: meoow :c +LL | let _: ReqTrait; + | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/assumptions_on_binders/higher-ranked-outlives-issue-157732.rs b/tests/ui/assumptions_on_binders/higher-ranked-outlives-issue-157732.rs new file mode 100644 index 0000000000000..5e51e8a2d416f --- /dev/null +++ b/tests/ui/assumptions_on_binders/higher-ranked-outlives-issue-157732.rs @@ -0,0 +1,12 @@ +//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally + +fn foo<'a>(_a: &'a u32) +where + for<'b> &'b (): 'a, +{ +} + +fn main() { + foo(&10); + //~^ ERROR: higher-ranked lifetime bound could not be satisfied +} diff --git a/tests/ui/assumptions_on_binders/higher-ranked-outlives-issue-157732.stderr b/tests/ui/assumptions_on_binders/higher-ranked-outlives-issue-157732.stderr new file mode 100644 index 0000000000000..cb06bdd933490 --- /dev/null +++ b/tests/ui/assumptions_on_binders/higher-ranked-outlives-issue-157732.stderr @@ -0,0 +1,8 @@ +error: higher-ranked lifetime bound could not be satisfied + --> $DIR/higher-ranked-outlives-issue-157732.rs:10:5 + | +LL | foo(&10); + | ^^^^^^^^ + +error: aborting due to 1 previous error +