diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 11b8890346dfb..bdb30090925d0 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -246,8 +246,16 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( mut outlives_constraints, universe_causes, type_tests, + solver_constraints, } = constraints; + // These have already been destructured into `outlives_constraints` at the + // end of MIR type checking. + assert!( + solver_constraints.is_true(), + "solver region constraints not lowered to NLL = {solver_constraints:#?}", + ); + let fr_static = universal_regions.fr_static; let compute_sccs = |constraints: &OutlivesConstraintSet<'tcx>, diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index f845d9137f759..f11e3b8fd6d80 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -68,7 +68,8 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { #[instrument(skip(self), level = "debug")] pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) { - let QueryRegionConstraints { constraints, assumptions } = query_constraints; + let QueryRegionConstraints { constraints, assumptions, solver_constraints } = + query_constraints; let assumptions = elaborate::elaborate_outlives_assumptions(self.infcx.tcx, assumptions.iter().copied()); @@ -77,6 +78,9 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { self.convert(predicate, category, &assumptions); }); } + + self.constraints + .register_solver_constraint(solver_constraints.clone().with_spans(self.span)); } /// Given an instance of the closure type, this method instantiates the "extra" requirements diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 9f23a0d5ab631..ed080c26d60f3 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -17,6 +17,7 @@ use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::region_constraints::RegionConstraintData; use rustc_infer::infer::{ BoundRegionConversionTime, InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, + SolverRegionConstraint, }; use rustc_infer::traits::{Obligation, ObligationCause, PredicateObligations}; use rustc_middle::bug; @@ -113,6 +114,7 @@ pub(crate) fn type_check<'tcx>( outlives_constraints: OutlivesConstraintSet::default(), type_tests: Vec::default(), universe_causes: FxIndexMap::default(), + solver_constraints: SolverRegionConstraint::new_true(), }; let CreateResult { @@ -134,6 +136,13 @@ pub(crate) fn type_check<'tcx>( pre_assumptions.is_empty(), "there should be no incoming region assumptions = {pre_assumptions:#?}", ); + // Solver region constraints from computing the implied bounds went through + // `ConstraintConversion` and are already stored in `constraints`. + let pre_solver_constraints = infcx.take_solver_region_constraints(); + assert!( + pre_solver_constraints.is_true(), + "there should be no incoming solver region constraints = {pre_solver_constraints:#?}", + ); } debug!(?normalized_inputs_and_output); @@ -174,6 +183,10 @@ pub(crate) fn type_check<'tcx>( let polonius_context = typeck.polonius_context; if infcx.tcx.assumptions_on_binders() { + let solver_constraints = mem::replace( + &mut typeck.constraints.solver_constraints, + SolverRegionConstraint::new_true(), + ); let mut converter = constraint_conversion::ConstraintConversion::new( typeck.infcx, typeck.universal_regions, @@ -185,6 +198,7 @@ pub(crate) fn type_check<'tcx>( typeck.constraints, ); typeck.infcx.destructure_solver_region_constraints_for_borrowck( + solver_constraints, &mut converter, typeck.known_type_outlives_obligations, universal_region_relations.outlives.clone(), @@ -293,9 +307,25 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> { pub(crate) universe_causes: FxIndexMap>, pub(crate) type_tests: Vec>, + + /// The region constraints emitted by the next solver under + /// `-Zassumptions-on-binders`. Unlike the constraints above these are not yet + /// lowered to NLL, we destructure them into `outlives_constraints` at the end + /// of MIR type checking. + pub(crate) solver_constraints: SolverRegionConstraint<'tcx>, } impl<'tcx> MirTypeckRegionConstraints<'tcx> { + /// Adds `constraint` to the constraints we've accumulated so far. + pub(crate) fn register_solver_constraint(&mut self, constraint: SolverRegionConstraint<'tcx>) { + // FIXME(-Zassumptions-on-binders): This is pretty bad for perf, we rebuild the + // entire constraint every time instead of updating it incrementally. + self.solver_constraints = SolverRegionConstraint::build_and( + constraint, + mem::replace(&mut self.solver_constraints, SolverRegionConstraint::new_true()), + ); + } + /// Creates a `Region` for a given `PlaceholderRegion`, or returns the /// region that corresponds to a previously created one. pub(crate) fn placeholder_region( diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index cd35af8be73cf..d1c4480b59347 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -146,11 +146,13 @@ impl<'tcx> InferCtxt<'tcx> { let region_obligations = self.take_registered_region_obligations(); let region_assumptions = self.take_registered_region_assumptions(); debug!(?region_obligations); + let solver_constraints = self.take_solver_region_constraints(); let region_constraints = self.with_region_constraints(|region_constraints| { make_query_region_constraints( region_obligations, region_constraints, region_assumptions, + solver_constraints, ) }); debug!(?region_constraints); @@ -195,9 +197,10 @@ impl<'tcx> InferCtxt<'tcx> { let InferOk { value: result_args, obligations } = self.query_response_instantiation(cause, param_env, original_values, query_response)?; - for QueryRegionConstraint { constraint, visible_for_leak_check: vis, .. } in - &query_response.value.region_constraints.constraints - { + let QueryRegionConstraints { constraints, assumptions, solver_constraints } = + &query_response.value.region_constraints; + + for QueryRegionConstraint { constraint, visible_for_leak_check: vis, .. } in constraints { let constraint = instantiate_value(self.tcx, &result_args, *constraint); match constraint { ty::RegionConstraint::Outlives(clause) => { @@ -209,11 +212,15 @@ impl<'tcx> InferCtxt<'tcx> { } } - for assumption in &query_response.value.region_constraints.assumptions { + for assumption in assumptions { let assumption = instantiate_value(self.tcx, &result_args, *assumption); self.register_region_assumption(assumption); } + let solver_constraints = + instantiate_value(self.tcx, &result_args, solver_constraints.clone()); + self.register_solver_region_constraint(solver_constraints.with_spans(cause.span)); + let user_result: R = query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone()); @@ -325,27 +332,31 @@ impl<'tcx> InferCtxt<'tcx> { } } + let QueryRegionConstraints { constraints, assumptions, solver_constraints } = + &query_response.value.region_constraints; + // ...also include the other query region constraints from the query. - output_query_region_constraints.constraints.extend( - query_response.value.region_constraints.constraints.iter().filter_map(|&r_c| { - let r_c = instantiate_value(self.tcx, &result_args, r_c); + output_query_region_constraints.constraints.extend(constraints.iter().filter_map(|&r_c| { + let r_c = instantiate_value(self.tcx, &result_args, r_c); - // Screen out `'a: 'a` or `'a == 'a` cases. - if r_c.constraint.is_trivial() { None } else { Some(r_c) } - }), - ); + // Screen out `'a: 'a` or `'a == 'a` cases. + if r_c.constraint.is_trivial() { None } else { Some(r_c) } + })); // FIXME(higher_ranked_auto): Optimize this to instantiate all assumptions // at once, rather than calling `instantiate_value` repeatedly which may // create more universes. - output_query_region_constraints.assumptions.extend( - query_response - .value - .region_constraints - .assumptions - .iter() - .map(|&r_c| instantiate_value(self.tcx, &result_args, r_c)), - ); + output_query_region_constraints + .assumptions + .extend(assumptions.iter().map(|&r_c| instantiate_value(self.tcx, &result_args, r_c))); + + let solver_constraints = + instantiate_value(self.tcx, &result_args, solver_constraints.clone()); + output_query_region_constraints.solver_constraints = + ty::region_constraint::RegionConstraint::build_and( + std::mem::take(&mut output_query_region_constraints.solver_constraints), + solver_constraints, + ); let user_result: R = query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone()); @@ -619,6 +630,7 @@ pub fn make_query_region_constraints<'tcx>( outlives_obligations: Vec>, region_constraints: &RegionConstraintData<'tcx>, assumptions: Vec>, + solver_constraints: ty::region_constraint::RegionConstraint>, ) -> QueryRegionConstraints<'tcx> { let RegionConstraintData { constraints, verifys } = region_constraints; @@ -663,5 +675,5 @@ pub fn make_query_region_constraints<'tcx>( )) .collect(); - QueryRegionConstraints { constraints, assumptions } + QueryRegionConstraints { constraints, assumptions, solver_constraints } } diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 42f686b39136b..c7fafd00ce740 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -238,11 +238,16 @@ impl<'tcx> InferCtxt<'tcx> { outlives_env.known_type_outlives().into_iter().cloned().collect(), outlives_env.free_region_map().relation.clone(), ); - self.destructure_solver_region_constraints(assumptions, self); + let constraint = self.inner.borrow().solver_region_constraint_storage.get_constraint(); + self.destructure_solver_region_constraints(constraint, assumptions, self); } + /// Unlike regionck, borrowck doesn't keep these constraints in the `InferCtxt`. + /// It stores them in `MirTypeckRegionConstraints` alongside its other region + /// constraints, so it hands us the constraint to destructure. pub fn destructure_solver_region_constraints_for_borrowck( &self, + constraint: SolverRegionConstraint<'tcx>, // this is always ConstraintConversion but lol conversion: impl TypeOutlivesDelegate<'tcx>, known_type_outlives: &[PolyTypeOutlivesClause<'tcx>], @@ -252,19 +257,19 @@ impl<'tcx> InferCtxt<'tcx> { 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); + self.destructure_solver_region_constraints(constraint, assumptions, conversion); } #[instrument(level = "debug", skip(self, conversion))] pub fn destructure_solver_region_constraints( &self, + constraint: SolverRegionConstraint<'tcx>, assumptions: rustc_type_ir::region_constraint::Assumptions>, mut conversion: impl TypeOutlivesDelegate<'tcx>, ) { assert!(self.tcx.assumptions_on_binders()); assert!(self.next_trait_solver()); - let constraint = self.inner.borrow().solver_region_constraint_storage.get_constraint(); debug!(?constraint); let constraint = region_constraint::destructure_type_outlives_constraints_in_root( self, diff --git a/compiler/rustc_infer/src/infer/solver_region_constraints.rs b/compiler/rustc_infer/src/infer/solver_region_constraints.rs index 09baae2ca8a42..965ccf70442b8 100644 --- a/compiler/rustc_infer/src/infer/solver_region_constraints.rs +++ b/compiler/rustc_infer/src/infer/solver_region_constraints.rs @@ -1,9 +1,11 @@ use rustc_middle::ty::TyCtxt; use rustc_span::Span; +use rustc_type_ir::region_constraint::RegionConstraint; use tracing::instrument; -pub type SolverRegionConstraint<'tcx> = - rustc_type_ir::region_constraint::RegionConstraint, Span>; +use super::InferCtxt; + +pub type SolverRegionConstraint<'tcx> = RegionConstraint, Span>; #[derive(Clone, Debug)] pub(crate) struct SolverRegionConstraintStorage<'tcx>(SolverRegionConstraint<'tcx>); @@ -17,11 +19,24 @@ impl<'tcx> SolverRegionConstraintStorage<'tcx> { self.0.clone() } + pub(crate) fn take(&mut self) -> SolverRegionConstraint<'tcx> { + core::mem::replace(&mut self.0, SolverRegionConstraint::new_true()) + } + #[instrument(level = "debug", skip(self))] pub(crate) fn overwrite(&mut self, constraint: SolverRegionConstraint<'tcx>) { self.0 = constraint; } } +impl<'tcx> InferCtxt<'tcx> { + /// Trait queries just want to pass back the solver region constraints "as is", + /// mirroring `take_registered_region_obligations`. + pub fn take_solver_region_constraints(&self) -> RegionConstraint> { + assert!(!self.in_snapshot(), "cannot take solver region constraints in a snapshot"); + self.inner.borrow_mut().solver_region_constraint_storage.take().without_spans() + } +} + #[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 index 529ecf43f5ee1..540aa5e4f8cb2 100644 --- a/compiler/rustc_infer/src/infer/solver_region_constraints/tests.rs +++ b/compiler/rustc_infer/src/infer/solver_region_constraints/tests.rs @@ -1,7 +1,29 @@ +use rustc_middle::infer::canonical::QueryRegionConstraints; use rustc_middle::ty::TyCtxt; use rustc_span::{BytePos, Span}; use rustc_type_ir::region_constraint::{And, LeafRegionConstraint, Or}; +use super::{SolverRegionConstraint, SolverRegionConstraintStorage}; + +#[test] +fn true_constraint_keeps_query_response_empty() { + // Mirrors `register_solver_region_constraint`, which registers unconditionally: + // anding a trivially true constraint into an empty store has to leave the store + // trivially true, as the resulting query response would otherwise no longer be + // empty. This relies on `And`/`Or` being kept in canonical form. + let mut storage = SolverRegionConstraintStorage::<'static>::new(); + storage.overwrite(SolverRegionConstraint::build_and( + SolverRegionConstraint::new_true(), + storage.get_constraint(), + )); + + let constraints = QueryRegionConstraints { + solver_constraints: storage.get_constraint().without_spans(), + ..Default::default() + }; + assert!(constraints.is_empty()); +} + #[test] fn canonicalization_preserves_only_one_ambiguity() { let first = Span::with_root_ctxt(BytePos(1), BytePos(2)); diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 46429f7adfb12..f99280bb744a1 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -76,13 +76,21 @@ pub struct QueryResponse<'tcx, R> { pub value: R, } -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, Default, PartialEq, Hash)] #[derive(StableHash, TypeFoldable, TypeVisitable)] pub struct QueryRegionConstraints<'tcx> { pub constraints: Vec>, pub assumptions: Vec>, + /// Region constraints emitted by the next solver under + /// `-Zassumptions-on-binders`. + /// + /// These stay unspanned while passing through a canonical query. The type-op + /// caller attaches its origin span when consuming the response. + pub solver_constraints: ir::region_constraint::RegionConstraint>, } +impl Eq for QueryRegionConstraints<'_> {} + impl QueryRegionConstraints<'_> { /// Represents an empty (trivially true) set of region constraints. /// @@ -91,8 +99,23 @@ impl QueryRegionConstraints<'_> { /// discharge a requirement from another query, which is a potential problem if we did throw /// away these assumptions because there were no constraints. pub fn is_empty(&self) -> bool { - let QueryRegionConstraints { constraints, assumptions } = self; - constraints.is_empty() && assumptions.is_empty() + let QueryRegionConstraints { constraints, assumptions, solver_constraints } = self; + constraints.is_empty() && assumptions.is_empty() && solver_constraints.is_true() + } + + pub fn extend(&mut self, other: &Self) { + let QueryRegionConstraints { constraints, assumptions, solver_constraints } = self; + let QueryRegionConstraints { + constraints: other_constraints, + assumptions: other_assumptions, + solver_constraints: other_solver_constraints, + } = other; + constraints.extend(other_constraints.iter().cloned()); + assumptions.extend(other_assumptions.iter().cloned()); + *solver_constraints = ir::region_constraint::RegionConstraint::build_and( + std::mem::take(solver_constraints), + other_solver_constraints.clone(), + ); } } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 818d8e1a4e0c3..60339b1982233 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -374,6 +374,11 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< region_obligations, region_constraints, region_assumptions, + // We're only called with `-Zassumptions-on-binders` disabled, in which + // case the solver never emits new-style region constraints. With it + // enabled the solver instead returns `ExternalRegionConstraints::NextGen`, + // reading the constraint straight out of the `InferCtxt`. + Default::default(), ) }); diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 84cae1e7bfa0a..bcb932f74b1f8 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -82,7 +82,10 @@ fn implied_outlives_bounds<'a, 'tcx>( // FIXME(higher_ranked_auto): Should we register assumptions here? // We otherwise would get spurious errors if normalizing an implied // outlives bound required proving some higher-ranked coroutine obl. - let QueryRegionConstraints { constraints, assumptions: _ } = constraints; + let QueryRegionConstraints { constraints, assumptions: _, solver_constraints } = + constraints; + infcx.register_solver_region_constraint(solver_constraints.with_spans(span)); + let cause = ObligationCause::misc(span, body_def_id); for &QueryRegionConstraint { constraint, visible_for_leak_check: vis, .. } in &constraints { match constraint { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 25385d15e36f4..39f439cad64d3 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -60,8 +60,8 @@ impl fmt::Debug for CustomTypeOp { } } -/// Executes `op` and then scrapes out all the "old style" region -/// constraints that result, creating query-region-constraints. +/// Executes `op` and then scrapes out all resulting region constraints +/// in the `infcx`, creating query-region-constraints. pub fn scrape_region_constraints<'tcx, Op, R>( infcx: &InferCtxt<'tcx>, root_def_id: LocalDefId, @@ -88,6 +88,11 @@ where pre_assumptions.is_empty(), "scrape_region_constraints: incoming region assumptions = {pre_assumptions:#?}", ); + let pre_solver_constraints = infcx.take_solver_region_constraints(); + assert!( + pre_solver_constraints.is_true(), + "scrape_region_constraints: incoming solver constraints = {pre_solver_constraints:#?}", + ); let value = infcx.commit_if_ok(|_| { let ocx = ObligationCtxt::new(infcx); @@ -144,11 +149,13 @@ where let region_obligations = infcx.take_registered_region_obligations(); let region_assumptions = infcx.take_registered_region_assumptions(); + let solver_constraints = infcx.take_solver_region_constraints(); let region_constraint_data = infcx.take_and_reset_region_constraints(); let region_constraints = query_response::make_query_region_constraints( region_obligations, ®ion_constraint_data, region_assumptions, + solver_constraints, ); if region_constraints.is_empty() { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 250579a2b064a..ac516afbc783c 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -151,9 +151,8 @@ where Ok(output) })?; output.error_info = error_info; - if let Some(QueryRegionConstraints { constraints, assumptions }) = output.constraints { - region_constraints.constraints.extend(constraints.iter().cloned()); - region_constraints.assumptions.extend(assumptions.iter().cloned()); + if let Some(constraints) = output.constraints { + region_constraints.extend(constraints); } output.constraints = if region_constraints.is_empty() { None diff --git a/compiler/rustc_traits/src/coroutine_witnesses.rs b/compiler/rustc_traits/src/coroutine_witnesses.rs index 762471eefe4dd..b7ff4cca0bb1f 100644 --- a/compiler/rustc_traits/src/coroutine_witnesses.rs +++ b/compiler/rustc_traits/src/coroutine_witnesses.rs @@ -84,6 +84,10 @@ fn compute_assumptions<'tcx>( region_obligations, ®ion_constraints, region_assumptions, + // We return early above unless the old solver is used globally, while + // `-Zassumptions-on-binders` enables the next solver globally. So there + // are never any new-style region constraints to pass along here. + Default::default(), ) .constraints .fold_with(&mut OpportunisticRegionResolver::new(&infcx)); diff --git a/tests/ui/assumptions_on_binders/alias_outlives.rs b/tests/ui/assumptions_on_binders/alias_outlives.rs index 0c2ed6585cf45..9e1516132dd9d 100644 --- a/tests/ui/assumptions_on_binders/alias_outlives.rs +++ b/tests/ui/assumptions_on_binders/alias_outlives.rs @@ -23,11 +23,11 @@ where } fn borrowck_env_fail<'a, T: AliasHaver>() -// FIXME: ^ this should raise an ERROR: unsatisfied lifetime constraint from -Zassumptions-on-binders where ::Assoc: 'a, { let _: ReqTrait; + //~^ ERROR: higher-ranked lifetime bound could not be satisfied } const REGIONCK_ENV_PASS<'a, T: AliasHaver>: ReqTrait = todo!() @@ -39,4 +39,30 @@ const REGIONCK_ENV_FAIL<'a, T: AliasHaver>: ReqTrait = todo!() where ::Assoc: 'a; +// Solver constraints produced while normalizing implied bounds must be returned +// to lexical regionck. +trait Project { + type Assoc; +} + +impl Project for (T,) +where + T::Assoc: for<'a> Trait<'a>, +{ + type Assoc = (); +} + +struct Normalizes(T) +where + T::Assoc: Clone; + +trait TestTrait {} + +impl<'a, T: AliasHaver> TestTrait for [Normalizes<(T,)>; 1] +//~^ ERROR: higher-ranked lifetime bound could not be satisfied +where + T::Assoc: 'a, +{ +} + fn main() {} diff --git a/tests/ui/assumptions_on_binders/alias_outlives.stderr b/tests/ui/assumptions_on_binders/alias_outlives.stderr index 1787c1912ae4f..a6eaf3e11bad7 100644 --- a/tests/ui/assumptions_on_binders/alias_outlives.stderr +++ b/tests/ui/assumptions_on_binders/alias_outlives.stderr @@ -4,5 +4,20 @@ error: higher-ranked lifetime bound could not be satisfied LL | const REGIONCK_ENV_FAIL<'a, T: AliasHaver>: ReqTrait = todo!() | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error: higher-ranked lifetime bound could not be satisfied + --> $DIR/alias_outlives.rs:61:1 + | +LL | / impl<'a, T: AliasHaver> TestTrait for [Normalizes<(T,)>; 1] +LL | | +LL | | where +LL | | T::Assoc: 'a, + | |_________________^ + +error: higher-ranked lifetime bound could not be satisfied + --> $DIR/alias_outlives.rs:29:12 + | +LL | let _: ReqTrait; + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors