diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 8c9c444bd1793..ebf84ab9d6470 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -372,7 +372,8 @@ fn try_extract_error_from_fulfill_cx<'a, 'tcx>( // already run, but there's no point using `span_delayed_bug` // when we're going to emit an error here anyway. let _errors = ocx.evaluate_obligations_error_on_ambiguity(); - let region_constraints = ocx.infcx.with_region_constraints(|r| r.clone()); + // FIXME(-Zassumptions-on-binders): do something here for abby_constraints? + let region_constraints = ocx.infcx.with_region_constraints(|r, _| r.clone()); try_extract_error_from_region_constraints( ocx.infcx, generic_param_scope, diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 11b8890346dfb..837d11fe21baa 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -246,7 +246,9 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( mut outlives_constraints, universe_causes, type_tests, + solver_constraints, } = constraints; + assert!(solver_constraints.is_true(), "solver constraints were not lowered to NLL"); let fr_static = universal_regions.fr_static; let compute_sccs = diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index b295a66ee74ee..25efb24d60979 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -38,9 +38,12 @@ where let TypeOpOutput { output, constraints: query_constraints, error_info } = op.fully_perform(infcx, infcx.root_def_id, locations.span(body))?; if cfg!(debug_assertions) { - let data = infcx.take_and_reset_region_constraints(); - if !data.is_empty() { - panic!("leftover region constraints: {data:#?}"); + let (legacy_constraints, abby_constraints) = infcx.take_and_reset_region_constraints(); + if !legacy_constraints.is_empty() { + panic!("leftover region constraints: {legacy_constraints:#?}"); + } + if !abby_constraints.get_constraint().is_true() { + panic!("leftover region constraints: {abby_constraints:#?}"); } } diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 9790cece59196..c36cda49ab768 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -67,7 +67,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()); @@ -76,6 +77,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 2a103ff70eb6c..bf987c1ec955f 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::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; @@ -111,6 +112,7 @@ pub(crate) fn type_check<'tcx>( outlives_constraints: OutlivesConstraintSet::default(), type_tests: Vec::default(), universe_causes: FxIndexMap::default(), + solver_constraints: Default::default(), }; let CreateResult { @@ -171,6 +173,8 @@ pub(crate) fn type_check<'tcx>( let polonius_context = typeck.polonius_context; if infcx.tcx.assumptions_on_binders() { + let constraint = mem::take(&mut typeck.constraints.solver_constraints); + assert!(infcx.tcx.assumptions_on_binders()); let mut converter = constraint_conversion::ConstraintConversion::new( typeck.infcx, typeck.universal_regions, @@ -182,6 +186,7 @@ pub(crate) fn type_check<'tcx>( typeck.constraints, ); typeck.infcx.destructure_solver_region_constraints_for_borrowck( + constraint, &mut converter, typeck.known_type_outlives_obligations, typeck.region_bound_pairs, @@ -290,9 +295,17 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> { pub(crate) universe_causes: FxIndexMap>, pub(crate) type_tests: Vec>, + + /// Constraints from completed AoB type operations, lowered once all bounds are available. + pub(crate) solver_constraints: SolverRegionConstraint<'tcx>, } impl<'tcx> MirTypeckRegionConstraints<'tcx> { + pub(crate) fn register_solver_constraint(&mut self, constraint: SolverRegionConstraint<'tcx>) { + let prev = mem::take(&mut self.solver_constraints); + self.solver_constraints = SolverRegionConstraint::build_and(prev, constraint); + } + /// 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 a0c8eb22a8273..7bc4a4c0b7f0b 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -146,13 +146,15 @@ impl<'tcx> InferCtxt<'tcx> { let region_obligations = self.take_registered_region_obligations(); let region_assumptions = self.take_registered_region_assumptions(); debug!(?region_obligations); - let region_constraints = self.with_region_constraints(|region_constraints| { - make_query_region_constraints( - region_obligations, - region_constraints, - region_assumptions, - ) - }); + let region_constraints = + self.with_region_constraints(|legacy_constraints, abby_constraints| { + make_query_region_constraints( + region_obligations, + legacy_constraints, + region_assumptions, + abby_constraints.get_constraint().without_spans(), + ) + }); debug!(?region_constraints); let opaque_types = self @@ -195,9 +197,9 @@ 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 +211,14 @@ 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 constraint = instantiate_value(self.tcx, &result_args, solver_constraints.clone()); + self.register_solver_region_constraint(constraint.with_spans(cause.span)); + let user_result: R = query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone()); @@ -324,27 +329,26 @@ 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 constraint = instantiate_value(self.tcx, &result_args, solver_constraints.clone()); + output_query_region_constraints.add_solver_constraint(constraint); let user_result: R = query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone()); @@ -618,6 +622,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; @@ -662,5 +667,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/lexical_region_resolve/indexed_edges.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/indexed_edges.rs index 382998e55d2e2..179a788b1002a 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/indexed_edges.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/indexed_edges.rs @@ -1,8 +1,9 @@ use rustc_index::IndexVec; use rustc_type_ir::RegionVid; +use super::Data; use crate::infer::SubregionOrigin; -use crate::infer::region_constraints::{Constraint, ConstraintKind, RegionConstraintData}; +use crate::infer::region_constraints::{Constraint, ConstraintKind}; /// Selects either out-edges or in-edges for [`IndexedConstraintEdges::adjacent_edges`]. #[derive(Clone, Copy, Debug)] @@ -13,24 +14,23 @@ pub(super) enum EdgeDirection { /// Type alias for the pairs stored in [`RegionConstraintData::constraints`], /// which we are indexing. -type ConstraintPair<'data, 'tcx> = (Constraint<'tcx>, &'data SubregionOrigin<'tcx>); +type ConstraintPair<'tcx> = (Constraint<'tcx>, SubregionOrigin<'tcx>); /// An index from region variables to their corresponding constraint edges, /// used on some error paths. -pub(super) struct IndexedConstraintEdges<'data, 'tcx> { - out_edges: IndexVec>>, - in_edges: IndexVec>>, +pub(super) struct IndexedConstraintEdges<'tcx> { + out_edges: IndexVec>>, + in_edges: IndexVec>>, } -impl<'data, 'tcx> IndexedConstraintEdges<'data, 'tcx> { - pub(super) fn build_index(num_vars: usize, data: &'data RegionConstraintData<'tcx>) -> Self { +impl<'tcx> IndexedConstraintEdges<'tcx> { + pub(super) fn build_index>(num_vars: usize, data: &D) -> Self { let mut out_edges = IndexVec::from_fn_n(|_| vec![], num_vars); let mut in_edges = IndexVec::from_fn_n(|_| vec![], num_vars); for pair @ (c, _) in data - .constraints - .iter() - .flat_map(|(c, origin)| c.iter_outlives().map(move |c| (c, origin))) + .constraints() + .flat_map(|(c, origin)| c.iter_outlives().map(move |c| (c, origin.clone()))) { // Only push a var out-edge for `VarSub...` constraints. match c.kind { @@ -51,10 +51,9 @@ impl<'data, 'tcx> IndexedConstraintEdges<'data, 'tcx> { // (It's unclear whether this is important or not.) for pair @ (c, _) in data - .constraints - .iter() + .constraints() .rev() - .flat_map(|(c, origin)| c.iter_outlives().map(move |c| (c, origin))) + .flat_map(|(c, origin)| c.iter_outlives().map(move |c| (c, origin.clone()))) { // Only push a var in-edge for `...SubVar` constraints. match c.kind { @@ -79,7 +78,7 @@ impl<'data, 'tcx> IndexedConstraintEdges<'data, 'tcx> { &self, region_vid: RegionVid, dir: EdgeDirection, - ) -> &[ConstraintPair<'data, 'tcx>] { + ) -> &[ConstraintPair<'tcx>] { let edges = match dir { EdgeDirection::Out => &self.out_edges, EdgeDirection::In => &self.in_edges, 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 84255fd9ed3e7..7ced5680a10ae 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -16,7 +16,8 @@ use tracing::{debug, instrument}; use super::outlives::test_type_match; use crate::infer::lexical_region_resolve::indexed_edges::{EdgeDirection, IndexedConstraintEdges}; use crate::infer::region_constraints::{ - ConstraintKind, GenericKind, RegionConstraintData, VarInfos, VerifyBound, + AbbyRegionConstraintData, Constraint, ConstraintKind, GenericKind, RegionConstraintData, + VarInfos, Verify, VerifyBound, }; use crate::infer::{RegionRelations, RegionVariableOrigin, SubregionOrigin}; @@ -28,13 +29,13 @@ mod indexed_edges; /// 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(crate) fn resolve<'tcx>( +pub(crate) fn resolve<'tcx, D: Data<'tcx>>( region_rels: &RegionRelations<'_, 'tcx>, var_infos: VarInfos<'tcx>, - data: RegionConstraintData<'tcx>, + data: D, ) -> (LexicalRegionResolutions<'tcx>, Vec>) { assert!( - data.constraints.iter().all(|(c, _)| match c.kind { + data.constraints().all(|(c, _)| match c.kind { ConstraintKind::VarSubVar | ConstraintKind::RegSubVar | ConstraintKind::VarSubReg @@ -129,13 +130,94 @@ struct RegionAndOrigin<'tcx> { origin: SubregionOrigin<'tcx>, } -struct LexicalResolver<'cx, 'tcx> { +pub(crate) trait Data<'tcx> { + fn dedupe_constraints(&mut self); + fn constraints( + &self, + ) -> impl DoubleEndedIterator, SubregionOrigin<'tcx>)>; + fn verifys<'a>(&'a self) -> impl Iterator> + where + 'tcx: 'a; +} + +impl<'tcx> Data<'tcx> for RegionConstraintData<'tcx> { + fn dedupe_constraints(&mut self) { + let mut seen = UnordSet::default(); + self.constraints.retain_mut(|(constraint, _)| { + // We don't want to discern constraints by leak check visibility here + constraint.visible_for_leak_check = ty::VisibleForLeakCheck::Unreachable; + seen.insert(*constraint) + }); + } + + fn constraints( + &self, + ) -> impl DoubleEndedIterator, SubregionOrigin<'tcx>)> { + self.constraints.iter().cloned() + } + + fn verifys<'a>(&'a self) -> impl Iterator> + where + 'tcx: 'a, + { + self.verifys.iter() + } +} + +impl<'tcx> Data<'tcx> for AbbyRegionConstraintData<'tcx> { + fn dedupe_constraints(&mut self) { + // abby constraints are always deduplicated! + } + + fn constraints( + &self, + ) -> impl DoubleEndedIterator, SubregionOrigin<'tcx>)> { + use rustc_type_ir::region_constraint::LeafRegionConstraint::*; + let convert_leaf = |c| match c { + RegionOutlives(r1, r2, sp) => { + let kind = match (r1.is_var(), r2.is_var()) { + (true, true) => ConstraintKind::VarSubVar, + (true, false) => ConstraintKind::VarSubReg, + (false, true) => ConstraintKind::RegSubVar, + (false, false) => ConstraintKind::RegSubReg, + }; + let constraint = Constraint { + kind, + sub: r1, + sup: r2, + visible_for_leak_check: ty::VisibleForLeakCheck::Unreachable, + }; + let origin = SubregionOrigin::SolverRegionConstraint(sp); + Some((constraint, origin)) + } + Ambiguity(_) => None, + AliasTyOutlivesViaEnv(_, _) | PlaceholderTyOutlives(_, _, _) => unreachable!(), + }; + + let c = self.get_constraint(); + // FIXME(-Zassumptions-on-binders): actually implement OR as an OR + let leafs = c.and_constraint.0.into_iter().chain( + c.or_constraint.0.into_iter().flat_map(|and_constraint| and_constraint.0.into_iter()), + ); + leafs.flat_map(convert_leaf) + } + + fn verifys<'a>(&'a self) -> impl Iterator> + where + 'tcx: 'a, + { + // we don't have verifys! + [].into_iter() + } +} + +struct LexicalResolver<'cx, 'tcx, D: Data<'tcx>> { region_rels: &'cx RegionRelations<'cx, 'tcx>, var_infos: VarInfos<'tcx>, - data: RegionConstraintData<'tcx>, + data: D, } -impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { +impl<'cx, 'tcx, D: Data<'tcx>> LexicalResolver<'cx, 'tcx, D> { fn tcx(&self) -> TyCtxt<'tcx> { self.region_rels.tcx } @@ -147,12 +229,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let mut var_data = self.construct_var_data(); // Deduplicating constraints is shown to have a positive perf impact. - let mut seen = UnordSet::default(); - self.data.constraints.retain_mut(|(constraint, _)| { - // We don't want to discern constraints by leak check visibility here - constraint.visible_for_leak_check = ty::VisibleForLeakCheck::Unreachable; - seen.insert(*constraint) - }); + self.data.dedupe_constraints(); if cfg!(debug_assertions) { self.dump_constraints(); @@ -184,7 +261,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { #[instrument(level = "debug", skip(self))] fn dump_constraints(&self) { - for (idx, (constraint, _)) in self.data.constraints.iter().enumerate() { + for (idx, (constraint, _)) in self.data.constraints().enumerate() { debug!("Constraint {} => {:?}", idx, constraint); } } @@ -200,7 +277,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let mut constraints = IndexVec::from_elem(Vec::new(), &var_values.values); // Tracks the changed region vids. let mut changes = Vec::new(); - for (c, _) in &self.data.constraints { + for (c, _) in self.data.constraints() { match c.kind { ConstraintKind::RegSubVar => { let sup_vid = c.sup.as_var(); @@ -550,7 +627,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { var_data: &mut LexicalRegionResolutions<'tcx>, errors: &mut Vec>, ) { - for (c, origin) in &self.data.constraints { + for (c, origin) in self.data.constraints() { debug!(?c, ?origin); match c.kind { ConstraintKind::RegSubVar | ConstraintKind::VarSubVar => { @@ -568,7 +645,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { ); errors.push(RegionResolutionError::ConcreteFailure( - (*origin).clone(), + origin.clone(), c.sub, c.sup, )); @@ -601,7 +678,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } - for verify in &self.data.verifys { + for verify in self.data.verifys() { debug!("collect_errors: verify={:?}", verify); let sub = var_data.normalize(self.tcx(), verify.region); @@ -648,7 +725,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let mut dup_vec = IndexVec::from_elem_n(None, self.num_vars()); // Only construct the edge index when necessary, because it's moderately expensive. - let mut edges: Option> = None; + let mut edges: Option> = None; for (node_vid, value) in var_data.values.iter_enumerated() { match *value { @@ -693,7 +770,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { fn collect_error_for_expanding_node( &self, - edges: &IndexedConstraintEdges<'_, 'tcx>, + edges: &IndexedConstraintEdges<'tcx>, dup_vec: &mut IndexSlice>, node_idx: RegionVid, errors: &mut Vec>, @@ -811,7 +888,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { /// those returned by a previous call for another region. fn collect_bounding_regions( &self, - edges: &IndexedConstraintEdges<'_, 'tcx>, + edges: &IndexedConstraintEdges<'tcx>, orig_node_idx: RegionVid, dir: EdgeDirection, mut dup_vec: Option<&mut IndexSlice>>, @@ -857,7 +934,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { fn process_edges<'tcx>( state: &mut WalkState<'tcx>, - edges: &IndexedConstraintEdges<'_, 'tcx>, + edges: &IndexedConstraintEdges<'tcx>, source_vid: RegionVid, dir: EdgeDirection, ) { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index c45c5e44aaa31..0847e89bdfb83 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -61,12 +61,10 @@ 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 use solver_region_constraints::SolverRegionConstraint; -use solver_region_constraints::SolverRegionConstraintStorage; +pub use region_constraints::SolverRegionConstraint; /// `InferOk<'tcx, ()>` is used a lot. It may seem like a useless wrapper /// around `PredicateObligations<'tcx>`, but it has one important property: @@ -133,9 +131,6 @@ pub struct InferCtxtInner<'tcx> { /// region constraints would've been added. region_constraint_storage: Option>, - /// Used by the next solver when `-Zassumptions-on-binders` is set. - solver_region_constraint_storage: SolverRegionConstraintStorage<'tcx>, - /// A set of constraints that regionck must validate. /// /// Each constraint has the form `T:'a`, meaning "some type `T` must @@ -183,7 +178,6 @@ impl<'tcx> InferCtxtInner<'tcx> { float_unification_storage: Default::default(), float_origin_origin_storage: Default::default(), region_constraint_storage: Some(Default::default()), - solver_region_constraint_storage: SolverRegionConstraintStorage::new(), region_obligations: Default::default(), region_assumptions: Default::default(), hir_typeck_potentially_region_dependent_goals: Default::default(), @@ -1114,7 +1108,8 @@ impl<'tcx> InferCtxt<'tcx> { let inner = self.inner.borrow(); assert!(!UndoLogs::>::in_snapshot(&inner.undo_log)); let storage = inner.region_constraint_storage.as_ref().expect("regions already resolved"); - assert!(storage.data.is_empty(), "{:#?}", storage.data); + // FIXME(-Zassumptions-on-binders): Handle abby_data + assert!(storage.legacy_data.is_empty(), "{:#?}", storage.legacy_data); // We clone instead of taking because borrowck still wants to use the // inference context after calling this for diagnostics and the new // trait solver. @@ -1616,18 +1611,11 @@ impl<'tcx> InferCtxt<'tcx> { } pub fn get_solver_region_constraint(&self) -> SolverRegionConstraint<'tcx> { - self.inner.borrow().solver_region_constraint_storage.get_constraint() + self.inner.borrow().region_constraint_storage.as_ref().unwrap().abby_data.get_constraint() } pub fn overwrite_solver_region_constraint(&self, constraint: SolverRegionConstraint<'tcx>) { - assert!( - !constraint.has_escaping_bound_vars(), - "solver region constraint has escaping bound vars, which is indicative of a bug in how constraints are handled: {constraint:?}", - ); - let mut inner = self.inner.borrow_mut(); - let old_constraint = inner.solver_region_constraint_storage.get_constraint(); - inner.undo_log.push(UndoLog::OverwriteSolverRegionConstraint { old_constraint }); - inner.solver_region_constraint_storage.overwrite(constraint); + self.inner.borrow_mut().unwrap_region_constraints().overwrite_abby_constraint(constraint); } /// See the [`region_constraints::RegionConstraintCollector::verify_generic_bound`] method. diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index 79c1a4fb7662f..1d1362a7910f4 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -5,6 +5,7 @@ use std::iter; use rustc_data_structures::undo_log::UndoLogs; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty; +use rustc_type_ir::region_constraint::LeafRegionConstraint; use tracing::instrument; use self::env::OutlivesEnvironment; @@ -12,7 +13,7 @@ use super::region_constraints::{RegionConstraintData, UndoLog}; use super::{InferCtxt, RegionResolutionError}; use crate::infer::free_regions::RegionRelations; use crate::infer::lexical_region_resolve; -use crate::infer::region_constraints::ConstraintKind; +use crate::infer::region_constraints::{AbbyRegionConstraintData, ConstraintKind}; pub mod env; pub mod obligations; @@ -58,8 +59,8 @@ impl<'tcx> InferCtxt<'tcx> { inner.region_constraint_storage.take().expect("regions already resolved") }; - storage.data.constraints = storage - .data + storage.legacy_data.constraints = storage + .legacy_data .constraints .iter() .flat_map(|(constraint, origin)| { @@ -70,7 +71,7 @@ impl<'tcx> InferCtxt<'tcx> { // Filter out any region-region outlives assumptions that are implied by // coroutine well-formedness. if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions { - storage.data.constraints.retain(|(c, _)| match c.kind { + storage.legacy_data.constraints.retain(|(c, _)| match c.kind { ConstraintKind::RegSubReg => !outlives_env .higher_ranked_assumptions() .contains(&ty::OutlivesClause(c.sup.into(), c.sub)), @@ -87,8 +88,32 @@ impl<'tcx> InferCtxt<'tcx> { let region_rels = &RegionRelations::new(self.tcx, outlives_env.free_region_map()); - let (lexical_region_resolutions, errors) = - lexical_region_resolve::resolve(region_rels, storage.var_infos, storage.data); + let abby = self.tcx.sess.opts.unstable_opts.assumptions_on_binders; + let (lexical_region_resolutions, errors) = if abby { + let c = storage.abby_data.get_constraint(); + let r = + lexical_region_resolve::resolve(region_rels, storage.var_infos, storage.abby_data); + + // Emit an error if the region constraint was ambiguous + for leaf in c.and_constraint.0.into_iter().chain( + c.or_constraint + .0 + .into_iter() + .flat_map(|and_constraint| and_constraint.0.into_iter()), + ) { + if let LeafRegionConstraint::Ambiguity(span) = leaf { + self.dcx() + .span_err( + span, + "unable to satisfy constraints involving placeholders due to unknown implied bounds", + ); + } + } + + r + } else { + lexical_region_resolve::resolve(region_rels, storage.var_infos, storage.legacy_data) + }; let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); assert!(old_value.is_none()); @@ -106,7 +131,11 @@ impl<'tcx> InferCtxt<'tcx> { /// region constraints as normal, but then we take them and /// translate them into the form that the NLL solver /// understands. See the NLL module for mode details. - pub fn take_and_reset_region_constraints(&self) -> RegionConstraintData<'tcx> { + pub fn take_and_reset_region_constraints( + &self, + ) -> (RegionConstraintData<'tcx>, AbbyRegionConstraintData<'tcx>) { + // FIXME(-Zassumptions-on-binders): should we be asserting no type + // outlives constraints in the abby constraints storage? assert!( self.inner.borrow().region_obligations.is_empty(), "region_obligations not empty: {:#?}", @@ -124,9 +153,10 @@ impl<'tcx> InferCtxt<'tcx> { /// Gives temporary access to the region constraint data. pub fn with_region_constraints( &self, - op: impl FnOnce(&RegionConstraintData<'tcx>) -> R, + op: impl FnOnce(&RegionConstraintData<'tcx>, &AbbyRegionConstraintData<'tcx>) -> R, ) -> R { let mut inner = self.inner.borrow_mut(); - op(inner.unwrap_region_constraints().data()) + let constraints = inner.unwrap_region_constraints(); + op(constraints.legacy_data(), constraints.abby_data()) } } diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 4b833da8eeb59..fa3e618cb0740 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -141,19 +141,7 @@ impl<'tcx> InferCtxt<'tcx> { } pub fn register_solver_region_constraint(&self, c: SolverRegionConstraint<'tcx>) { - let mut inner = self.inner.borrow_mut(); - - let old_constraint = inner.solver_region_constraint_storage.get_constraint(); - let new_constraint = rustc_type_ir::region_constraint::RegionConstraint::build_and( - c, - old_constraint.clone(), - ); - - // FIXME(-Zassumptions-on-binders): This is pretty bad for perf, we don't make incremental - // changes to the region constraints, instead we just rewrite the entire thing every time - // and store the old version. - inner.undo_log.push(UndoLog::OverwriteSolverRegionConstraint { old_constraint }); - inner.solver_region_constraint_storage.overwrite(new_constraint); + self.inner.borrow_mut().unwrap_region_constraints().register_abby_constraint(c); } pub fn register_type_outlives_constraint( @@ -233,6 +221,9 @@ impl<'tcx> InferCtxt<'tcx> { &self, outlives_env: &OutlivesEnvironment<'tcx>, ) { + assert!(self.tcx.assumptions_on_binders()); + assert!(self.next_trait_solver()); + // `FreeRegionMap::relation` stores `'sub <= 'sup` edges while // `Assumptions::region_outlives` expects `'longer: 'shorter` ones, so the // edges have to be inverted here. @@ -250,37 +241,34 @@ impl<'tcx> InferCtxt<'tcx> { region_outlives.freeze(), ty::UniverseIndex::ROOT, ); - self.destructure_solver_region_constraints(assumptions, self); + + let constraint = region_constraint::destructure_type_outlives_constraints_in_root( + self, + self.get_solver_region_constraint(), + &assumptions, + ); + self.overwrite_solver_region_constraint(constraint); } pub fn destructure_solver_region_constraints_for_borrowck( &self, + constraint: SolverRegionConstraint<'tcx>, // this is always ConstraintConversion but lol - conversion: impl TypeOutlivesDelegate<'tcx>, + mut conversion: impl TypeOutlivesDelegate<'tcx>, known_type_outlives: &[PolyTypeOutlivesClause<'tcx>], region_bound_pairs: &RegionBoundPairs<'tcx>, region_outlives: TransitiveRelation, ) { + assert!(self.tcx.assumptions_on_binders()); + assert!(self.next_trait_solver()); + let assumptions = region_constraint::Assumptions::new( self, assumed_type_outlives(self.tcx, known_type_outlives, region_bound_pairs), region_outlives.maybe_map(|r| Some(Region::new_var(self.tcx, r))).unwrap(), ty::UniverseIndex::ROOT, ); - self.destructure_solver_region_constraints(assumptions, conversion); - } - - #[instrument(level = "debug", skip(self, conversion))] - pub fn destructure_solver_region_constraints( - &self, - 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, constraint, diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs index b761f73645e20..8912d22b9fe96 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -392,7 +392,8 @@ impl<'tcx> MiniGraph<'tcx> { { match undo_entry { &AddConstraint(i) => { - region_constraints.data().constraints[i].0.iter_outlives().for_each( + // FIXME(-Zassumptions-on-binders): Handle abby_data here and elsewhere in this fn + region_constraints.legacy_data().constraints[i].0.iter_outlives().for_each( |Constraint { kind: _, sub, sup, visible_for_leak_check }| { match visible_for_leak_check { ty::VisibleForLeakCheck::Yes => each_edge(sub, sup), @@ -403,15 +404,17 @@ impl<'tcx> MiniGraph<'tcx> { ); } &AddVerify(i) => span_bug!( - region_constraints.data().verifys[i].origin.span(), + region_constraints.legacy_data().verifys[i].origin.span(), "we never add verifications while doing higher-ranked things", ), &AddCombination(..) | &AddVar(..) => {} + // FIXME(-Zassumptions-on-binders): we probably need to handle this in some manner + &OverwriteSolverRegionConstraint { .. } => {} } } } else { region_constraints - .data() + .legacy_data() .constraints .iter() .flat_map(|(c, _)| c.iter_outlives()) diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index befa7537e7a4f..15d164afb67bd 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -9,7 +9,8 @@ use rustc_data_structures::unify as ut; use rustc_index::IndexVec; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::ty::{self, ReBound, ReStatic, ReVar, Region, RegionVid, Ty, TyCtxt}; -use rustc_span::{bug, span_bug}; +use rustc_span::{Span, bug, span_bug}; +use rustc_type_ir::TypeVisitableExt; use tracing::{debug, instrument}; use self::CombineMapType::*; @@ -25,7 +26,11 @@ pub struct RegionConstraintStorage<'tcx> { /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. pub(super) var_infos: IndexVec>, - pub(super) data: RegionConstraintData<'tcx>, + pub(super) legacy_data: RegionConstraintData<'tcx>, + // FIXME(-Zassumptions-on-binders): We should merge `abby_data` and `data` + // into just one field and have the representation of region constraints be + // the same regardless of whether abby is enabled or not + pub(super) abby_data: AbbyRegionConstraintData<'tcx>, /// For a given pair of regions (R1, R2), maps to a region R3 that /// is designated as their LUB (edges R1 <= R3 and R2 <= R3 @@ -78,6 +83,33 @@ pub struct RegionConstraintData<'tcx> { pub verifys: Vec>, } +pub type SolverRegionConstraint<'tcx> = + rustc_type_ir::region_constraint::RegionConstraint, Span>; + +#[derive(Clone, Debug)] +pub struct AbbyRegionConstraintData<'tcx>(SolverRegionConstraint<'tcx>); + +impl<'tcx> Default for AbbyRegionConstraintData<'tcx> { + fn default() -> Self { + Self::new() + } +} + +impl<'tcx> AbbyRegionConstraintData<'tcx> { + pub(crate) fn new() -> Self { + Self(SolverRegionConstraint::new_true()) + } + + pub fn get_constraint(&self) -> SolverRegionConstraint<'tcx> { + self.0.clone() + } + + #[instrument(level = "debug", skip(self))] + pub(crate) fn overwrite(&mut self, constraint: SolverRegionConstraint<'tcx>) { + self.0 = constraint; + } +} + /// Represents a constraint that influences the inference process. #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub enum ConstraintKind { @@ -309,7 +341,7 @@ pub(crate) struct TwoRegions<'tcx> { b: Region<'tcx>, } -#[derive(Copy, Clone, PartialEq)] +#[derive(Clone, PartialEq)] pub(crate) enum UndoLog<'tcx> { /// We added `RegionVid`. AddVar(RegionVid), @@ -322,6 +354,9 @@ pub(crate) enum UndoLog<'tcx> { /// We added a GLB/LUB "combination variable". AddCombination(CombineMapType, TwoRegions<'tcx>), + + /// Overwrite the `-Zassumptions-on-binders` storage + OverwriteSolverRegionConstraint { old_constraint: SolverRegionConstraint<'tcx> }, } #[derive(Copy, Clone, PartialEq)] @@ -381,7 +416,9 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { /// some other point Q, in NLL. /// /// Not legal during a snapshot. - pub fn take_and_reset_data(&mut self) -> RegionConstraintData<'tcx> { + pub fn take_and_reset_data( + &mut self, + ) -> (RegionConstraintData<'tcx>, AbbyRegionConstraintData<'tcx>) { assert!(!UndoLogs::>::in_snapshot(&self.undo_log)); // If you add a new field to `RegionConstraintCollector`, you @@ -389,7 +426,8 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { // or updated in some way. let RegionConstraintStorage { var_infos: _, - data, + legacy_data, + abby_data, lubs, glbs, unification_table: _, @@ -403,7 +441,8 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { lubs.clear(); glbs.clear(); - let data = mem::take(data); + let legacy_data = mem::take(legacy_data); + let abby_data = mem::take(abby_data); // Clear all unifications and recreate the variables a "now // un-unified" state. Note that when we unify `a` and `b`, we @@ -418,11 +457,45 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { }); } - data + (legacy_data, abby_data) + } + + pub fn legacy_data(&self) -> &RegionConstraintData<'tcx> { + &self.storage.legacy_data + } + + pub fn abby_data(&self) -> &AbbyRegionConstraintData<'tcx> { + &self.storage.abby_data } - pub fn data(&self) -> &RegionConstraintData<'tcx> { - &self.storage.data + pub fn overwrite_abby_constraint(&mut self, constraint: SolverRegionConstraint<'tcx>) { + assert!( + !constraint.has_escaping_bound_vars(), + "solver region constraint has escaping bound vars, which is indicative of a bug in how constraints are handled: {constraint:?}", + ); + + let old_constraint = self.storage.abby_data.get_constraint(); + self.storage.abby_data.overwrite(constraint); + self.undo_log.push(OverwriteSolverRegionConstraint { old_constraint }); + } + + pub fn register_abby_constraint(&mut self, constraint: SolverRegionConstraint<'tcx>) { + assert!( + !constraint.has_escaping_bound_vars(), + "solver region constraint has escaping bound vars, which is indicative of a bug in how constraints are handled: {constraint:?}", + ); + + let old_constraint = self.storage.abby_data.get_constraint(); + let new_constraint = rustc_type_ir::region_constraint::RegionConstraint::build_and( + constraint, + old_constraint.clone(), + ); + + // FIXME(-Zassumptions-on-binders): This is pretty bad for perf, we don't make incremental + // changes to the region constraints, instead we just rewrite the entire thing every time + // and store the old version. + self.undo_log.push(OverwriteSolverRegionConstraint { old_constraint }); + self.storage.abby_data.overwrite(new_constraint); } pub(super) fn start_snapshot(&self) -> RegionSnapshot { @@ -458,8 +531,8 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { // cannot add constraints once regions are resolved debug!("RegionConstraintCollector: add_constraint({:?})", constraint); - let index = self.storage.data.constraints.len(); - self.storage.data.constraints.push((constraint, origin)); + let index = self.storage.legacy_data.constraints.len(); + self.storage.legacy_data.constraints.push((constraint, origin)); self.undo_log.push(AddConstraint(index)); } @@ -474,8 +547,8 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { return; } - let index = self.storage.data.verifys.len(); - self.storage.data.verifys.push(verify); + let index = self.storage.legacy_data.verifys.len(); + self.storage.legacy_data.verifys.push(verify); self.undo_log.push(AddVerify(index)); } @@ -767,7 +840,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { pub fn region_constraints_added_in_snapshot(&self, mark: &Snapshot<'tcx>) -> bool { self.undo_log .region_constraints_in_snapshot(mark) - .any(|&elt| matches!(elt, AddConstraint(_))) + .any(|elt| matches!(elt, &AddConstraint(_))) } #[inline] @@ -861,12 +934,12 @@ impl<'tcx> Rollback> for RegionConstraintStorage<'tcx> { assert_eq!(self.var_infos.len(), vid.index()); } AddConstraint(index) => { - self.data.constraints.pop().unwrap(); - assert_eq!(self.data.constraints.len(), index); + self.legacy_data.constraints.pop().unwrap(); + assert_eq!(self.legacy_data.constraints.len(), index); } AddVerify(index) => { - self.data.verifys.pop(); - assert_eq!(self.data.verifys.len(), index); + self.legacy_data.verifys.pop(); + assert_eq!(self.legacy_data.verifys.len(), index); } AddCombination(Glb, ref regions) => { self.glbs.remove(regions); @@ -874,6 +947,9 @@ impl<'tcx> Rollback> for RegionConstraintStorage<'tcx> { AddCombination(Lub, ref regions) => { self.lubs.remove(regions); } + OverwriteSolverRegionConstraint { old_constraint } => { + self.abby_data.overwrite(old_constraint); + } } } } diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index 2b1ac29173483..a10026f2f77c7 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -7,7 +7,7 @@ use rustc_middle::ty::{self, OpaqueTypeKey, ProvisionalHiddenType}; use tracing::debug; use crate::infer::unify_key::{ConstVidKey, RegionVidKey}; -use crate::infer::{InferCtxtInner, SolverRegionConstraint, region_constraints, type_variable}; +use crate::infer::{InferCtxtInner, region_constraints, type_variable}; use crate::traits; pub struct Snapshot<'tcx> { @@ -28,7 +28,6 @@ pub(crate) enum UndoLog<'tcx> { RegionUnificationTable(sv::UndoLog>>), ProjectionCache(traits::UndoLog<'tcx>), PushTypeOutlivesConstraint, - OverwriteSolverRegionConstraint { old_constraint: SolverRegionConstraint<'tcx> }, PushRegionAssumption, PushHirTypeckPotentiallyRegionDependentGoal, } @@ -78,9 +77,6 @@ impl<'tcx> Rollback> for InferCtxtInner<'tcx> { self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo) } UndoLog::ProjectionCache(undo) => self.projection_cache.reverse(undo), - UndoLog::OverwriteSolverRegionConstraint { old_constraint } => { - self.solver_region_constraint_storage.overwrite(old_constraint); - } UndoLog::PushTypeOutlivesConstraint => { let popped = self.region_obligations.pop(); assert_matches!(popped, Some(_), "pushed region constraint but could not pop it"); diff --git a/compiler/rustc_infer/src/infer/solver_region_constraints.rs b/compiler/rustc_infer/src/infer/solver_region_constraints.rs deleted file mode 100644 index 03363c9f57a94..0000000000000 --- a/compiler/rustc_infer/src/infer/solver_region_constraints.rs +++ /dev/null @@ -1,30 +0,0 @@ -use rustc_middle::ty::TyCtxt; -use rustc_span::Span; -use tracing::instrument; - -pub type SolverRegionConstraint<'tcx> = - rustc_type_ir::region_constraint::RegionConstraint, Span>; - -#[derive(Clone, Debug)] -pub(crate) struct SolverRegionConstraintStorage<'tcx>(Option>); - -impl<'tcx> SolverRegionConstraintStorage<'tcx> { - pub(crate) fn new() -> Self { - Self(None) - } - - pub(crate) fn get_constraint(&self) -> SolverRegionConstraint<'tcx> { - match &self.0 { - Some(v) => v.clone(), - None => SolverRegionConstraint::new_true(), - } - } - - #[instrument(level = "debug", skip(self))] - pub(crate) fn overwrite(&mut self, constraint: SolverRegionConstraint<'tcx>) { - self.0 = Some(constraint); - } -} - -#[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 deleted file mode 100644 index 529ecf43f5ee1..0000000000000 --- a/compiler/rustc_infer/src/infer/solver_region_constraints/tests.rs +++ /dev/null @@ -1,18 +0,0 @@ -use rustc_middle::ty::TyCtxt; -use rustc_span::{BytePos, Span}; -use rustc_type_ir::region_constraint::{And, LeafRegionConstraint, Or}; - -#[test] -fn canonicalization_preserves_only_one_ambiguity() { - let first = Span::with_root_ctxt(BytePos(1), BytePos(2)); - let second = Span::with_root_ctxt(BytePos(3), BytePos(4)); - - let first = LeafRegionConstraint::Ambiguity::, _>(first); - let second = LeafRegionConstraint::Ambiguity::, _>(second); - - let c = And::new([first.clone(), second.clone()]); - assert_eq!(c.0.len(), 1); - - let c = Or::new([And::new([first]), And::new([second])]); - assert_eq!(c.0.len(), 1); -} diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 291a7b8e59f4f..fd7c3e5f63ade 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -76,14 +76,19 @@ 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>, + /// Unspanned AoB constraints, canonicalized with the rest of the response. + /// The caller attaches its own span when consuming them. + pub solver_constraints: ir::region_constraint::RegionConstraint>, } -impl QueryRegionConstraints<'_> { +impl Eq for QueryRegionConstraints<'_> {} + +impl<'tcx> QueryRegionConstraints<'tcx> { /// Represents an empty (trivially true) set of region constraints. /// /// FIXME(higher_ranked_auto): This could still just be true if there are only assumptions? @@ -91,8 +96,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 } = other; + self.constraints.extend(constraints.iter().cloned()); + self.assumptions.extend(assumptions.iter().cloned()); + self.add_solver_constraint(solver_constraints.clone()); + } + + pub fn add_solver_constraint( + &mut self, + c: ir::region_constraint::RegionConstraint>, + ) { + let prev = core::mem::take(&mut self.solver_constraints); + self.solver_constraints = ir::region_constraint::RegionConstraint::build_and(prev, c); } } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 2335868a17b40..c0c4fa2b327e4 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -377,13 +377,20 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< // inside of a `probe` whenever we have multiple choices inside of the solver. let region_obligations = self.0.inner.borrow().region_obligations().to_owned(); let region_assumptions = self.0.inner.borrow().region_assumptions().to_owned(); - let region_constraints = self.0.with_region_constraints(|region_constraints| { - make_query_region_constraints( - region_obligations, - region_constraints, - region_assumptions, - ) - }); + let region_constraints = + self.0.with_region_constraints(|legacy_constraints, abby_constraints| { + let abby_constraints = abby_constraints.get_constraint().without_spans(); + assert!( + abby_constraints.is_true(), + "unexpected abby constraint without abby enabled" + ); + make_query_region_constraints( + region_obligations, + legacy_constraints, + region_assumptions, + abby_constraints, + ) + }); let mut seen = FxHashMap::default(); let mut constraints = vec![]; diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 71dee3de72cb8..52b509bdf5213 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -175,7 +175,9 @@ impl<'tcx> AutoTraitFinder<'tcx> { let outlives_env = OutlivesEnvironment::new(&infcx, CRATE_DEF_ID, full_env, []); let _ = infcx.process_registered_region_obligations(&outlives_env); - let region_data = infcx.inner.borrow_mut().unwrap_region_constraints().data().clone(); + // FIXME(-Zassumptions-on-binders): Handle abby_data + let region_data = + infcx.inner.borrow_mut().unwrap_region_constraints().legacy_data().clone(); let vid_to_region = self.map_vid_to_region(®ion_data); diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index df21f682d7380..f1f2dc1ec37e5 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -245,7 +245,8 @@ fn overlap<'tcx>( .inner .borrow_mut() .unwrap_region_constraints() - .data() + // FIXME(-Zassumptions-on-binders): Handle abby_data + .legacy_data() .constraints .iter() .any(|c| c.0.involves_placeholders()); diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index fad89d17cbf44..bf28e643c8632 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -80,7 +80,9 @@ 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 651c872cd9072..4c4a0183cac64 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,7 @@ 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 scrapes all resulting region constraints into query-region constraints. pub fn scrape_region_constraints<'tcx, Op, R>( infcx: &InferCtxt<'tcx>, root_def_id: LocalDefId, @@ -144,17 +143,20 @@ where let region_obligations = infcx.take_registered_region_obligations(); let region_assumptions = infcx.take_registered_region_assumptions(); - let region_constraint_data = infcx.take_and_reset_region_constraints(); + let (legacy_constraints, abby_constraints) = infcx.take_and_reset_region_constraints(); let region_constraints = query_response::make_query_region_constraints( region_obligations, - ®ion_constraint_data, + &legacy_constraints, region_assumptions, + abby_constraints.get_constraint().without_spans(), ); + // `legacy_constraints` is only used by the old solver for the `InstantiateOpaqueTypes` + // type op and only for diagnostics. so we don't need to do anything here for abby. if region_constraints.is_empty() { Ok(( TypeOpOutput { output: value, constraints: None, error_info: None }, - region_constraint_data, + legacy_constraints, )) } else { Ok(( @@ -163,7 +165,7 @@ where constraints: Some(infcx.tcx.arena.alloc(region_constraints)), error_info: None, }, - region_constraint_data, + legacy_constraints, )) } } 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 2a6087003cb75..4b9f22c6b503b 100644 --- a/compiler/rustc_traits/src/coroutine_witnesses.rs +++ b/compiler/rustc_traits/src/coroutine_witnesses.rs @@ -77,13 +77,16 @@ fn compute_assumptions<'tcx>( let region_obligations = infcx.take_registered_region_obligations(); let region_assumptions = infcx.take_registered_region_assumptions(); - let region_constraints = infcx.take_and_reset_region_constraints(); + let (legacy_constraints, abby_constraints) = infcx.take_and_reset_region_constraints(); + let abby_constraints = abby_constraints.get_constraint().without_spans(); + assert!(abby_constraints.is_true(), "unexpected abby constraint without abby enabled"); let constraints = infcx.deeply_resolve_via_unification_table( make_query_region_constraints( region_obligations, - ®ion_constraints, + &legacy_constraints, region_assumptions, + abby_constraints, ) .constraints, ); diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 388796732c2cb..a7d197ee00fb8 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -50,6 +50,7 @@ use std::mem; use std::sync::Arc; use rustc_index::{Idx, IndexVec}; +use smallvec::SmallVec; use thin_vec::ThinVec; use tracing::{debug, instrument}; @@ -369,6 +370,16 @@ impl> TypeFoldable for ThinVec { } } +impl, const N: usize> TypeFoldable for SmallVec<[T; N]> { + fn try_fold_with>(self, folder: &mut F) -> Result { + self.into_iter().map(|t| t.try_fold_with(folder)).collect() + } + + fn fold_with>(self, folder: &mut F) -> Self { + self.into_iter().map(|t| t.fold_with(folder)).collect() + } +} + impl> TypeFoldable for Box<[T]> { fn try_fold_with>(self, folder: &mut F) -> Result { Vec::from(self).try_fold_with(folder).map(Vec::into_boxed_slice) diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 6f7a1ede0315d..9957dbe3451f3 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -48,6 +48,8 @@ impl Default for TransitiveRelationBuilder { } } +use smallvec::SmallVec; + use crate::data_structures::IndexMap; use crate::fold::TypeSuperFoldable; use crate::inherent::*; @@ -265,7 +267,7 @@ impl LeafRegionC /// than proving the subset, so it can never be the candidate which makes the OR hold. E.g. /// `OR(AND('a: 'b), AND('a: 'b, 'b: 'c))` really ought to just be `OR(AND('a: 'b))`. Only keeping /// an empty AND is the degenerate case of that rule. -pub struct Or(pub Box<[And]>); +pub struct Or(pub SmallVec<[And; 1]>); impl Or { pub fn with_spans( self, @@ -276,7 +278,7 @@ impl Or { } impl Or { pub fn new_true() -> Self { - Self(Box::new([And::new_true()])) + Self(SmallVec::from_buf([And::new_true()])) } pub fn is_true(&self) -> bool { @@ -291,7 +293,7 @@ impl Or { } pub fn new_false() -> Self { - Self(Box::new([])) + Self(SmallVec::new()) } pub fn is_false(&self) -> bool { @@ -300,10 +302,9 @@ impl Or { } pub fn new(i: impl IntoIterator>) -> Self { - let ands = i.into_iter().collect::>().into_boxed_slice(); - let mut new_ands: Vec> = Vec::new(); + let mut new_ands: SmallVec<[And; _]> = SmallVec::new(); - for and in ands { + for and in i { // An empty AND is trivially true, which makes the whole OR true no matter what the // other candidates are. `And::new` discards leaf constraints which are trivially // true, so this is how e.g. a reflexive `'a: 'a` candidate discharges an OR. @@ -320,7 +321,7 @@ impl Or { } } - Self(new_ands.into_boxed_slice()) + Self(new_ands) } pub fn new_ambig(s: S) -> Self { @@ -361,7 +362,9 @@ impl Or { /// An AND of leaf constraints. Always in "canonical form", meaning: /// - No leaf constraints are present twice in this AND /// - No leaf constraint is trivially true, i.e. a reflexive `'a: 'a` -pub struct And(pub Box<[LeafRegionConstraint]>); +pub struct And( + pub SmallVec<[LeafRegionConstraint; 1]>, +); impl And { pub fn with_spans( self, @@ -372,7 +375,7 @@ impl And { } impl And { pub fn new_true() -> Self { - Self(Box::new([])) + Self(SmallVec::new()) } pub fn new(i: impl IntoIterator>) -> Self { @@ -432,6 +435,14 @@ pub struct RegionConstraint { pub or_constraint: Or, } +impl Default + for RegionConstraint +{ + fn default() -> Self { + Self::new_true() + } +} + impl RegionConstraint { pub fn with_spans( self, diff --git a/tests/ui/assumptions_on_binders/alias_outlives.rs b/tests/ui/assumptions_on_binders/alias_outlives.rs index 7dd14da4fd773..6f1599a6292fa 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,29 @@ const REGIONCK_ENV_FAIL<'a, T: AliasHaver>: ReqTrait = todo!() where ::Assoc: 'a; +// Constraints produced while normalizing implied bounds must reach 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..db90dc95dd6e8 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:60: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 diff --git a/tests/ui/assumptions_on_binders/canonical-type-ops-outlives-pass.rs b/tests/ui/assumptions_on_binders/canonical-type-ops-outlives-pass.rs new file mode 100644 index 0000000000000..1fcbf6394f0b4 --- /dev/null +++ b/tests/ui/assumptions_on_binders/canonical-type-ops-outlives-pass.rs @@ -0,0 +1,65 @@ +//@ check-pass +//@ revisions: default full next +//@[full] compile-flags: -Zassumptions-on-binders -Znext-solver +//@[next] compile-flags: -Znext-solver + +// Exercise bound discharge and successive type operations in the same body. +#![allow(dead_code)] +trait Trait { + type Assoc; +} +trait Bounded { + type Assoc: 'static; +} +fn outlives<'a, T: 'a>() {} +fn environment<'a, T: Trait>() +where + T::Assoc: 'a, +{ + outlives::<'a, T::Assoc>(); + outlives::<'a, T::Assoc>(); +} +fn item_bound() { + outlives::<'static, T::Assoc>(); +} +fn component<'a, T: Trait + 'a>() { + outlives::<'a, T::Assoc>(); +} +fn implied<'a, T: Trait>(_: &'a T::Assoc) { + outlives::<'a, T::Assoc>(); +} +trait Object { + fn method(&self) {} +} +impl Object for T {} +fn body_bound(value: T::Assoc) { + let object: &dyn Object = &value; + object.method(); +} +trait Higher<'a> { + type Assoc: 'static; +} +trait Check<'a, 'b> {} +impl<'a, 'b, T: Higher<'b>> Check<'a, 'b> for T where T::Assoc: 'a {} +trait Outer<'a> {} +impl<'a, T> Outer<'a> for T where T: for<'b> Check<'a, 'b> {} +fn require Outer<'a>>() {} +fn nested Higher<'a>>() { + require::(); +} +trait Project { + type Out; +} +impl Project for (T,) +where + T::Assoc: 'static, +{ + type Out = (); +} +fn normalize() +where + T::Assoc: 'static, +{ + let _: <(T,) as Project>::Out = (); +} +fn main() {}