diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 0a1787a23c893..4e1bb89b18e6a 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2433,7 +2433,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { body.type_outlives, builder.freeze(), ); - self.infcx.insert_placeholder_assumptions(u, Some(assumptions)); + self.infcx.insert_placeholder_assumptions(u, assumptions); self.check_test_binder_body(body.value); let solver_region_constraint = self.infcx.get_solver_region_constraint(); let constraint = ty::region_constraint::eagerly_handle_placeholders_in_universe( diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 4d90e60736995..38dab6ec4875d 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -1,4 +1,6 @@ //! Definition of `InferCtxtLike` from the librarified type layer. +use std::range::RangeInclusive; + use rustc_data_structures::sso::SsoHashMap; use rustc_hir::def_id::DefId; use rustc_middle::traits::ObligationCause; @@ -49,7 +51,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { fn insert_placeholder_assumptions( &self, u: ty::UniverseIndex, - assumptions: Option>>, + assumptions: rustc_type_ir::region_constraint::Assumptions>, ) { self.insert_placeholder_assumptions(u, assumptions); } @@ -57,7 +59,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { fn get_placeholder_assumptions( &self, u: ty::UniverseIndex, - ) -> Option>> { + ) -> rustc_type_ir::region_constraint::Assumptions> { self.get_placeholder_assumptions(u) } @@ -67,6 +69,10 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.get_solver_region_constraint().without_spans() } + fn has_placeholder_assumptions(&self, range: RangeInclusive) -> bool { + self.has_placeholder_assumptions(range) + } + fn overwrite_solver_region_constraint( &self, constraint: rustc_type_ir::region_constraint::RegionConstraint>, @@ -189,7 +195,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { let u = self.universe(); self.placeholder_assumptions_for_next_solver .borrow_mut() - .insert(u, Some(rustc_type_ir::region_constraint::Assumptions::empty())); + .insert(u, rustc_type_ir::region_constraint::Assumptions::empty()); f(value) }) } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 267c36652656d..e02cc9a7acd1f 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1,5 +1,6 @@ use std::cell::{Cell, RefCell}; use std::fmt; +use std::range::RangeInclusive; pub use at::DefineOpaqueTypes; use free_regions::RegionRelations; @@ -335,10 +336,7 @@ pub struct InferCtxt<'tcx> { // FIXME(-Zassumptions-on-binders): This and `universe` should probably be // in `InferCtxtInner` so they can participate in rollbacks and whatnot placeholder_assumptions_for_next_solver: RefCell< - FxIndexMap< - ty::UniverseIndex, - Option>>, - >, + FxIndexMap>>, >, next_trait_solver: bool, @@ -1579,28 +1577,34 @@ impl<'tcx> InferCtxt<'tcx> { pub fn insert_placeholder_assumptions( &self, u: ty::UniverseIndex, - assumptions: Option>>, + assumptions: rustc_type_ir::region_constraint::Assumptions>, ) { - if let Some(assumptions) = &assumptions { - assert!( - !assumptions.type_outlives.has_escaping_bound_vars(), - "assumptions has escaping bound vars, which is indicative of a bug in how assumptions are handled: {:?}", - assumptions.type_outlives - ); - assert!( - assumptions.region_outlives.base_edges().all(|r| !r.has_escaping_bound_vars()), - "assumptions has escaping bound vars, which is indicative of a bug in how assumptions are handled: {:?}", - assumptions.region_outlives - ); - } + assert!( + !assumptions.type_outlives.has_escaping_bound_vars(), + "assumptions has escaping bound vars, which is indicative of a bug in how assumptions are handled: {:?}", + assumptions.type_outlives + ); + assert!( + assumptions.region_outlives.base_edges().all(|r| !r.has_escaping_bound_vars()), + "assumptions has escaping bound vars, which is indicative of a bug in how assumptions are handled: {:?}", + assumptions.region_outlives + ); self.placeholder_assumptions_for_next_solver.borrow_mut().insert(u, assumptions); } pub fn get_placeholder_assumptions( &self, u: ty::UniverseIndex, - ) -> Option>> { - self.placeholder_assumptions_for_next_solver.borrow().get(&u).unwrap().as_ref().cloned() + ) -> rustc_type_ir::region_constraint::Assumptions> { + self.placeholder_assumptions_for_next_solver.borrow().get(&u).unwrap().clone() + } + + pub fn has_placeholder_assumptions(&self, range: RangeInclusive) -> bool { + let assumptions = self.placeholder_assumptions_for_next_solver.borrow(); + // Walk around that `Step` trait is nightly only. + (range.start.index()..=range.last.index()) + .map(ty::UniverseIndex::from_usize) + .all(|u| assumptions.get(&u).is_some()) } pub fn get_solver_region_constraint(&self) -> SolverRegionConstraint<'tcx> { diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 14f534a3e7eb0..4b833da8eeb59 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -287,8 +287,6 @@ impl<'tcx> InferCtxt<'tcx> { &assumptions, ); debug!(?constraint); - let constraint = region_constraint::propagate_ambiguity(constraint); - debug!(?constraint); // FIXME(-Zassumptions-on-binders): actually implement OR as an OR for c in constraint.and_constraint.0.into_iter().chain( diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 45ae4cadf519c..655ab64145db5 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -173,7 +173,7 @@ where // recreated universes here. delegate.insert_placeholder_assumptions( new_universe, - Some(rustc_type_ir::region_constraint::Assumptions::empty()), + rustc_type_ir::region_constraint::Assumptions::empty(), ); } } diff --git a/compiler/rustc_next_trait_solver/src/placeholder.rs b/compiler/rustc_next_trait_solver/src/placeholder.rs index c96aa90a10792..fdf702248196a 100644 --- a/compiler/rustc_next_trait_solver/src/placeholder.rs +++ b/compiler/rustc_next_trait_solver/src/placeholder.rs @@ -75,7 +75,7 @@ where // context to compute placeholder assumptions for the binders it enters. infcx.insert_placeholder_assumptions( *new, - Some(rustc_type_ir::region_constraint::Assumptions::empty()), + rustc_type_ir::region_constraint::Assumptions::empty(), ); } } 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 7397d43a48a00..4d74cbc339be3 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 @@ -5,7 +5,7 @@ use std::ops::ControlFlow; use rustc_macros::StableHash; use rustc_type_ir::data_structures::HashSet; use rustc_type_ir::inherent::*; -use rustc_type_ir::region_constraint::{self, RegionConstraint}; +use rustc_type_ir::region_constraint::RegionConstraint; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; use rustc_type_ir::search_graph::{ @@ -1292,13 +1292,19 @@ where f: impl FnOnce(&mut Self, T) -> U, ) -> U { self.delegate.enter_forall_without_assumptions(value, |value| { + // Invariant: we shouldn't insert empty assumptions if assumptions computation fails. + // When handling placeholder constraints, we rely on vacancies to force ambiguity. let u = self.delegate.universe(); - let assumptions = if self.cx().assumptions_on_binders() { - self.region_assumptions_for_placeholders_in_universe(value.clone(), u, param_env) - } else { - None - }; - self.delegate.insert_placeholder_assumptions(u, assumptions); + if self.cx().assumptions_on_binders() + && let Some(assumptions) = self.region_assumptions_for_placeholders_in_universe( + value.clone(), + u, + param_env, + ) + { + self.delegate.insert_placeholder_assumptions(u, assumptions); + } + f(self, value) }) } @@ -1672,10 +1678,6 @@ where let region_constraints = if self.cx().assumptions_on_binders() { ExternalRegionConstraints::NextGen(if let Certainty::Yes = certainty { let constraint = self.delegate.get_solver_region_constraint(); - debug_assert_eq!( - constraint, - region_constraint::propagate_ambiguity(constraint.clone()) - ); constraint } else { RegionConstraint::new_true() 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 10dd8585b09c4..8a4d3fba99a39 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 @@ -8,7 +8,6 @@ use rustc_type_ir::outlives::{Component, push_outlives_components}; use rustc_type_ir::region_constraint::TransitiveRelationBuilder; use rustc_type_ir::region_constraint::{ And, Assumptions, LeafRegionConstraint, Or, eagerly_handle_placeholders_in_universe, - propagate_ambiguity, }; use rustc_type_ir::{ AliasTy, Binder, ClauseKind, Const, InferCtxtLike, Interner, Region, TypeVisitable, @@ -115,17 +114,27 @@ where pub(super) fn eagerly_handle_placeholders(&mut self) -> Result { let constraint = self.delegate.get_solver_region_constraint(); - let smallest_universe = self.max_input_universe.index(); - let largest_universe = self.delegate.universe().index(); - debug!(?smallest_universe, largest_universe); + let smallest_universe = self.max_input_universe; + let largest_universe = self.delegate.universe(); + debug!("smallest_universe={smallest_universe:#?}, largest_universe={largest_universe:#?}"); - let constraint = ((smallest_universe + 1)..=largest_universe) - .map(|u| UniverseIndex::from_usize(u)) + // We rely on placeholder assumptions to evaluate region constraints with placeholders. + // But we can fail to compute assumptions due to unresolved type variables. In that case, + // we conservatively bail with ambiguity. + if !self + .delegate + .has_placeholder_assumptions(((smallest_universe + 1)..=largest_universe).into()) + { + return Ok(Certainty::AMBIGUOUS); + } + + // Walk around that `Step` trait is nightly only. + let constraint = ((smallest_universe.index() + 1)..=largest_universe.index()) + .map(UniverseIndex::from_usize) .rev() .fold(constraint, |constraint, u| { eagerly_handle_placeholders_in_universe(&**self.delegate, constraint, u) }); - let constraint = propagate_ambiguity(constraint); debug!("final constraint={:?}", constraint); self.delegate.overwrite_solver_region_constraint(constraint.clone(), self.origin_span); @@ -164,7 +173,9 @@ where Or::new_leaf(PlaceholderTyOutlives(Ty::new_placeholder(self.cx(), *p), r, ())) } Alias(_, alias) => self.destructure_alias_outlives(*alias, r), - UnresolvedInferenceVariable(_) => Or::new_ambig(()), + UnresolvedInferenceVariable(_) => { + panic!("Shouldn't destructure type outlives when ty vars exist") + } Param(_) => panic!("Params should have been canonicalized to placeholders"), EscapingAlias(components) => self.destructure_components(components, r), } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 1d7d2dd75c316..dddc78373b1b5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -92,15 +92,6 @@ where let ty::OutlivesClause(ty, lt) = goal.predicate; let ty = self.normalize(GoalSource::Misc, goal.param_env, ty::Unnormalized::new_wip(ty))?; - if self.cx().assumptions_on_binders() { - use rustc_type_ir::region_constraint::RegionConstraint; - - let constraint = self.destructure_type_outlives(ty, lt); - self.register_solver_region_constraint(RegionConstraint::new_from_or(constraint)); - } else { - self.register_ty_outlives(ty, lt); - } - // The normalized type can still contain non-rigid higher ranked aliases if their // normalization ends up with ambiguity. Or we have non-rigid aliases inside rigid ones. // Infer vars may be resolved to types/consts containing non-rigid aliases later. @@ -109,6 +100,18 @@ where if ty.has_non_region_infer() || ty.has_non_rigid_aliases() { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } else { + // We drop region constraints in ambiguous response so there's no need to add them in + // the ambiguous branch. Also this guarantees we don't have ty vars when destructuring + // type outlives. + if self.cx().assumptions_on_binders() { + use rustc_type_ir::region_constraint::RegionConstraint; + + let constraint = self.destructure_type_outlives(ty, lt); + self.register_solver_region_constraint(RegionConstraint::new_from_or(constraint)); + } else { + self.register_ty_outlives(ty, lt); + } + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 22ebedd9f7e3c..d50f6521441d5 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -1,5 +1,6 @@ use core::fmt; use std::hash::{Hash, Hasher}; +use std::range::RangeInclusive; use derive_where::derive_where; #[cfg(feature = "nightly")] @@ -407,12 +408,13 @@ pub trait InferCtxtLike: Sized { fn insert_placeholder_assumptions( &self, u: ty::UniverseIndex, - assumptions: Option>, + assumptions: crate::region_constraint::Assumptions, ); fn get_placeholder_assumptions( &self, u: ty::UniverseIndex, - ) -> Option>; + ) -> crate::region_constraint::Assumptions; + fn has_placeholder_assumptions(&self, range: RangeInclusive) -> bool; fn get_solver_region_constraint( &self, ) -> crate::region_constraint::RegionConstraint; diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index e13ecb5e9575e..e1b1d157ff85f 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -558,8 +558,7 @@ pub fn eagerly_handle_placeholders_in_universe, I: Interne ) } -/// Force the whole constraint to be ambiguous if it contains ambiguities which could -/// have caused the constraint to be `false` if they had been `false` themselves. -/// -/// For example if we have `'a: 'b AND ambig` it's possible that if we had more inference -/// information we could have produced a better region constraint than `ambig`, and that -/// constraint may then have gone on to be false, at which point we would have `'a: 'b AND false` -/// causing the whole constraint to be `false`. -/// -/// If we're not careful we can wind up returning `'a: 'b AND ambig` from passing trait solver -/// goals and then upon rerunning wind up returning `NoSolution` which would be dubious :3 -/// -/// This is inherently conservative and this method should be called as little as possible as it -/// can cause us to get ambiguities instead of `NoSolution` (for example if `'a: 'b` is `false`), -/// which can affect coherence, candidate selection, etc. -/// -/// FIXME(-Zassumptions-on-binders): this method should probably be trait-solver internal as it only -/// matters at trait solver query boundaries. We currently call it in more than just that location -#[instrument(level = "debug", ret)] -pub fn propagate_ambiguity( - constraint: RegionConstraint, -) -> RegionConstraint { - if let Some(ambig) = constraint.and_constraint.0.iter().find(|c| c.is_ambig()) { - return RegionConstraint::new_leaf(ambig.clone()); - } - - for and in constraint.or_constraint.0.iter() { - // FIXME(-Zassumptions-on-binders): This is overly conservative. If we have: - // `'a: 'b OR ambig` we don't necessarily want to propagate ambiguity here - // as we might end up with `'a: 'b` being satisfied in which case we unnecessarily - // errored here. - // - // It's fine if the `ambig` wound up being `false` as that wouldn't cause a goal to - // become `NoSolution`, it would instead result in us returning the `'a: 'b` constraint - // by itself. - // - // `rust-lang/project-assumptions-on-binders#21` - if let Some(ambig) = and.0.iter().find(|c| c.is_ambig()) { - return RegionConstraint::new_leaf(ambig.clone()); - } - } - - constraint -} - /// Handles converting region outlives constraints involving placeholders from `u` into OR constraints /// involving regions from smaller universes with known relationships to the placeholder. For example: /// ```ignore (not rust) @@ -715,7 +670,7 @@ fn pull_region_outlives_constraints_out_of_universe< infcx: &Infcx, constraint: RegionConstraint, u: UniverseIndex, - assumptions: &Option>, + assumptions: &Assumptions, ) -> RegionConstraint { assert!(max_universe(infcx, constraint.clone()) <= u); @@ -745,14 +700,6 @@ fn pull_region_outlives_constraints_out_of_universe< continue; } - let assumptions = match assumptions { - Some(assumptions) => assumptions, - None => { - pulled_constraints.push(Or::new_ambig(())); - continue; - } - }; - // The constraint may already be entailed by the assumptions of the binder we are // leaving, e.g. `for<'a, 'b> where 'b: 'a { 'b: 'a }`. There is nothing to lift into // a smaller universe in that case, and looking for lower universe candidates would @@ -875,7 +822,7 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< infcx: &Infcx, constraint: RegionConstraint, u: UniverseIndex, - assumptions: &Option>, + assumptions: &Assumptions, ) -> RegionConstraint { use LeafRegionConstraint::*; @@ -920,7 +867,7 @@ fn rewrite_placeholder_ty_outlives_constraints_in_universe_for_eager_placeholder ty: I::Ty, region: Region, u: UniverseIndex, - assumptions: &Option>, + assumptions: &Assumptions, ) -> Or { use LeafRegionConstraint::*; @@ -931,11 +878,6 @@ fn rewrite_placeholder_ty_outlives_constraints_in_universe_for_eager_placeholder return Or::new_leaf(PlaceholderTyOutlives(ty, region, ())); } - let assumptions = match assumptions { - Some(assumptions) => assumptions, - None => return Or::new_ambig(()), - }; - let mut candidates = vec![]; // There could be `!T: 'region` assumptions in the env even if `!T` is in a @@ -966,7 +908,7 @@ fn rewrite_alias_ty_outlives_constraints_in_universe_for_eager_placeholder_handl infcx: &Infcx, bound_outlives: Binder, Region)>, u: UniverseIndex, - assumptions: &Option>, + assumptions: &Assumptions, ) -> Or { use LeafRegionConstraint::*; @@ -1016,14 +958,6 @@ fn rewrite_alias_ty_outlives_constraints_in_universe_for_eager_placeholder_handl } } - let assumptions = match assumptions { - Some(assumptions) => assumptions, - None => { - candidates.push(Or::new_ambig(())); - return candidates.into_iter().fold(Or::new_false(), |acc, c| Or::build_or(acc, c)); - } - }; - // Actually look at the assumptions and matching our higher ranked alias outlives goal // against potentially higher ranked type outlives assumptions. candidates.push(alias_outlives_candidates_from_assumptions(infcx, bound_outlives, assumptions)); @@ -1274,14 +1208,14 @@ impl<'a, Infcx: InferCtxtLike, I: Interner> TypeRelation { self.infcx.enter_forall_with_empty_assumptions(a, |a| { let u = self.infcx.universe(); - self.infcx.insert_placeholder_assumptions(u, Some(Assumptions::empty())); + self.infcx.insert_placeholder_assumptions(u, Assumptions::empty()); let b = self.infcx.instantiate_binder_with_infer(b); self.relate(a, b) })?; self.infcx.enter_forall_with_empty_assumptions(b, |b| { let u = self.infcx.universe(); - self.infcx.insert_placeholder_assumptions(u, Some(Assumptions::empty())); + self.infcx.insert_placeholder_assumptions(u, Assumptions::empty()); let a = self.infcx.instantiate_binder_with_infer(a); self.relate(a, b) })?;