diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 7f181ef2e7e04..76abad679d3f9 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -36,7 +36,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::{ self, AdtKind, Const, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized, - fold_regions, + VisibleForLeakCheck, fold_regions, }; use rustc_span::def_id::LocalModId; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, bug, kw, span_bug, sym}; @@ -444,7 +444,10 @@ impl<'tcx> ItemCtxt<'tcx> { let lhs = self.lowerer().lower_lifetime(lhs, RegionInferReason::RegionPredicate); let rhs = self.lowerer().lower_lifetime(rhs, RegionInferReason::RegionPredicate); SolverRegionConstraint::new_leaf(LeafRegionConstraint::RegionOutlives( - lhs, rhs, span, + lhs, + rhs, + VisibleForLeakCheck::Unreachable, + span, )) } hir::TestBinderConstraint::PlaceholderOutlives { lhs, rhs } => { diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 53226265cc9d7..c2ca30272254b 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -295,7 +295,7 @@ impl<'tcx> InferCtxt<'tcx> { ) .emit(); } - RegionOutlives(a, b, span) => { + RegionOutlives(a, b, _, span) => { let origin = SubregionOrigin::SolverRegionConstraint(span); let category = origin.to_constraint_category(); conversion.push_sub_region_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 8ddd4d6342e4d..a59b4d8c4fe1a 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -410,21 +410,58 @@ impl<'tcx> MiniGraph<'tcx> { &AddCombination(..) | &AddVar(..) => {} // FIXME(-Zassumptions-on-binders): we probably need to handle this in some manner &OverwriteSolverRegionConstraint { .. } => {} + &AddSolverLeafRegionConstraint(i) => { + if let SolverLeafRegionConstraint::RegionOutlives( + sup, + sub, + visible_for_leak_check, + _, + ) = region_constraints.solver_data().leafs()[i] + { + match visible_for_leak_check { + ty::VisibleForLeakCheck::Yes => each_edge(sub, sup), + ty::VisibleForLeakCheck::No => {} + ty::VisibleForLeakCheck::Unreachable => unreachable!(), + } + } + } } } } else { - region_constraints - .legacy_data() - .constraints - .iter() - .flat_map(|(c, _)| c.iter_outlives()) - .for_each(|Constraint { kind: _, sub, sup, visible_for_leak_check }| { - match visible_for_leak_check { - ty::VisibleForLeakCheck::Yes => each_edge(sub, sup), - ty::VisibleForLeakCheck::No => {} - ty::VisibleForLeakCheck::Unreachable => unreachable!(), + let assumptions_on_binders_enabled = + rustc_middle::ty::tls::with(|tcx| tcx.assumptions_on_binders()); + if assumptions_on_binders_enabled { + region_constraints.solver_data().leafs().iter().for_each(|leaf| { + if let SolverLeafRegionConstraint::RegionOutlives( + sup, + sub, + visible_for_leak_check, + _, + ) = leaf + { + match visible_for_leak_check { + ty::VisibleForLeakCheck::Yes => each_edge(*sub, *sup), + ty::VisibleForLeakCheck::No => {} + ty::VisibleForLeakCheck::Unreachable => unreachable!(), + } + } else { + unreachable!() } }) + } else { + region_constraints + .legacy_data() + .constraints + .iter() + .flat_map(|(c, _)| c.iter_outlives()) + .for_each(|Constraint { kind: _, sub, sup, visible_for_leak_check }| { + match visible_for_leak_check { + ty::VisibleForLeakCheck::Yes => each_edge(sub, sup), + ty::VisibleForLeakCheck::No => {} + ty::VisibleForLeakCheck::Unreachable => unreachable!(), + } + }) + } } } diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 936c78ab166d3..1d288a8921503 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -86,6 +86,9 @@ pub struct RegionConstraintData<'tcx> { pub type SolverRegionConstraint<'tcx> = rustc_type_ir::region_constraint::RegionConstraint, Span>; +pub type SolverLeafRegionConstraint<'tcx> = + rustc_type_ir::region_constraint::LeafRegionConstraint, Span>; + #[derive(Clone, Debug, Default)] pub struct SolverRegionConstraintData<'tcx>(Option>); @@ -101,6 +104,17 @@ impl<'tcx> SolverRegionConstraintData<'tcx> { pub(crate) fn overwrite(&mut self, constraint: SolverRegionConstraint<'tcx>) { self.0 = Some(constraint); } + + pub(crate) fn leafs_len(&self) -> usize { + match &self.0 { + Some(v) => v.and_constraint.0.len(), + None => 0, + } + } + + pub(crate) fn leafs(&self) -> &[SolverLeafRegionConstraint<'tcx>] { + &self.0.as_ref().unwrap().and_constraint.0 + } } /// Represents a constraint that influences the inference process. @@ -350,6 +364,9 @@ pub(crate) enum UndoLog<'tcx> { /// Overwrite the `-Zassumptions-on-binders` storage OverwriteSolverRegionConstraint { old_constraint: Box> }, + + /// We added the given leaf solver constraint. + AddSolverLeafRegionConstraint(usize), } #[derive(Copy, Clone, PartialEq)] @@ -477,10 +494,22 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { "solver region constraint has escaping bound vars, which is indicative of a bug in how constraints are handled: {constraint:?}", ); + // FIXME(-Zassumptions-on-binders): We don't actually register leaf constraints in + // `or_constraint`. This is a problem for complete incremental undo log. + // + // Currently it's only used for snapshot based leak check. Therefore, we can get away with + // ignoring `Or` constraint since region constraints from type outlives aren't visible for + // leak check. + for leaf in constraint.and_constraint.0.clone().into_iter() { + debug!("RegionConstraintCollector: add_solver_leaf_region_constraint({:?})", leaf); + let index = self.storage.solver_data.leafs_len(); + self.undo_log.push(AddSolverLeafRegionConstraint(index)); + } + let old_constraint = self.storage.solver_data.get_constraint(); let new_constraint = rustc_type_ir::region_constraint::RegionConstraint::build_and( - constraint, old_constraint.clone(), + constraint, ); // FIXME(-Zassumptions-on-binders): This is pretty bad for perf, we don't make incremental @@ -943,6 +972,10 @@ impl<'tcx> Rollback> for RegionConstraintStorage<'tcx> { OverwriteSolverRegionConstraint { old_constraint } => { self.solver_data.overwrite(*old_constraint); } + AddSolverLeafRegionConstraint(_) => { + // Nothing to do. + // The storage is restored via `OverwriteSolverRegionConstraint` + } } } } 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 8a4d3fba99a39..18dca8f97caca 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 @@ -16,7 +16,7 @@ use rustc_type_ir::{ use tracing::{debug, instrument}; use crate::delegate::SolverDelegate; -use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution}; +use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution, VisibleForLeakCheck}; /// Logic for `-Zassumptions-on-binders` stuff impl<'a, D, I> EvalCtxt<'a, D> @@ -168,7 +168,7 @@ where use Component::*; use LeafRegionConstraint::*; match c { - Region(c_r) => Or::new_leaf(RegionOutlives(*c_r, r, ())), + Region(c_r) => Or::new_leaf(RegionOutlives(*c_r, r, VisibleForLeakCheck::No, ())), Placeholder(p) => { Or::new_leaf(PlaceholderTyOutlives(Ty::new_placeholder(self.cx(), *p), r, ())) } @@ -194,7 +194,7 @@ where let item_bounds = rustc_type_ir::outlives::declared_bounds_from_definition(self.cx(), alias) - .map(|bound| And::new([RegionOutlives(bound, r, ())])); + .map(|bound| And::new([RegionOutlives(bound, r, VisibleForLeakCheck::No, ())])); let item_bound_outlives = Or::new(item_bounds); let where_clause_outlives = diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index c9da77eced6d3..2904fd2bb78c0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -157,8 +157,12 @@ where if self.cx().assumptions_on_binders() { use rustc_type_ir::region_constraint::{LeafRegionConstraint, RegionConstraint}; - let constraint = - RegionConstraint::new_leaf(LeafRegionConstraint::RegionOutlives(a, b, ())); + let constraint = RegionConstraint::new_leaf(LeafRegionConstraint::RegionOutlives( + a, + b, + VisibleForLeakCheck::Yes, + (), + )); self.register_solver_region_constraint(constraint); } else { self.register_region_outlives(a, b, VisibleForLeakCheck::Yes); diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 6f7a1ede0315d..f11aa16de6b67 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -52,6 +52,7 @@ use crate::data_structures::IndexMap; use crate::fold::TypeSuperFoldable; use crate::inherent::*; use crate::relate::{Relate, RelateResult, TypeRelation, VarianceDiagInfo}; +use crate::solve::VisibleForLeakCheck; use crate::visit::TypeVisitableExt; use crate::{ AliasTy, Binder, BoundRegion, BoundRegionKind, BoundVar, BoundVariableKind, ClauseKind, @@ -171,7 +172,7 @@ impl Assumptions { #[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] pub enum LeafRegionConstraint { Ambiguity(S), - RegionOutlives(Region, Region, S), + RegionOutlives(Region, Region, VisibleForLeakCheck, S), /// Requirement that a (potentially higher ranked) alias outlives some (potentially higher ranked) /// region due to an assumption in the environment. This cannot be satisfied via component outlives /// or item bounds. @@ -198,8 +199,8 @@ impl std::fmt::Debug for LeafRegionConstraint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { match self { Self::Ambiguity(_span) => write!(f, "Ambiguity"), - Self::RegionOutlives(r1, r2, _span) => { - f.debug_tuple("RegionOutlives").field(r1).field(r2).finish() + Self::RegionOutlives(r1, r2, vis, _span) => { + f.debug_tuple("RegionOutlives").field(r1).field(r2).field(vis).finish() } Self::AliasTyOutlivesViaEnv(bound_outlives, _span) => { f.debug_tuple("AliasTyOutlivesViaEnv").field(bound_outlives).finish() @@ -220,7 +221,7 @@ impl LeafRegionConstraint { match self { Ambiguity(()) => Ambiguity(span), - RegionOutlives(r1, r2, ()) => RegionOutlives(r1, r2, span), + RegionOutlives(r1, r2, vis, ()) => RegionOutlives(r1, r2, vis, span), AliasTyOutlivesViaEnv(bound_outlives, ()) => { AliasTyOutlivesViaEnv(bound_outlives, span) } @@ -235,7 +236,7 @@ impl LeafRegionC match self { Ambiguity(_) => Ambiguity(()), - RegionOutlives(r1, r2, _) => RegionOutlives(r1, r2, ()), + RegionOutlives(r1, r2, vis, _) => RegionOutlives(r1, r2, vis, ()), AliasTyOutlivesViaEnv(bound_outlives, _) => AliasTyOutlivesViaEnv(bound_outlives, ()), PlaceholderTyOutlives(ty, r, _) => PlaceholderTyOutlives(ty, r, ()), } @@ -245,7 +246,7 @@ impl LeafRegionC use LeafRegionConstraint::*; let (Ambiguity(s) - | RegionOutlives(_, _, s) + | RegionOutlives(_, _, _, s) | AliasTyOutlivesViaEnv(_, s) | PlaceholderTyOutlives(_, _, s)) = self; s.clone() @@ -383,7 +384,7 @@ impl And { // Outlives is reflexive so a `'a: 'a` leaf is always true and carries no // information. Dropping it here keeps the rest of the code from having to special // case it, and is what lets an OR with a reflexive candidate be recognized as true. - if let LeafRegionConstraint::RegionOutlives(r1, r2, _) = leaf + if let LeafRegionConstraint::RegionOutlives(r1, r2, _, _) = leaf && r1 == r2 { return false; @@ -607,6 +608,7 @@ fn compute_new_region_constraints, I: Interne use LeafRegionConstraint::*; let extend_from_and = |builder: &mut TransitiveRelationBuilder<_>, + visible_builder: &mut TransitiveRelationBuilder<_>, regions: &mut IndexSet<_>, constraints: &mut Vec<_>, and: &And| { @@ -615,20 +617,25 @@ fn compute_new_region_constraints, I: Interne Ambiguity(()) | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => { constraints.push(c.clone()) } - RegionOutlives(r1, r2, ()) => { + RegionOutlives(r1, r2, visible_for_leak_check, ()) => { regions.insert(*r1); regions.insert(*r2); builder.add(*r2, *r1); + if *visible_for_leak_check == VisibleForLeakCheck::Yes { + visible_builder.add(*r2, *r1); + } } } } }; let mut base_region_flows_builder = TransitiveRelationBuilder::default(); + let mut base_visible_region_flows_builder = TransitiveRelationBuilder::default(); let mut base_regions = IndexSet::new(); let mut base_constraints = Vec::new(); extend_from_and( &mut base_region_flows_builder, + &mut base_visible_region_flows_builder, &mut base_regions, &mut base_constraints, &constraint.and_constraint, @@ -637,11 +644,19 @@ fn compute_new_region_constraints, I: Interne let mut new_ands = Vec::new(); for and in &constraint.or_constraint.0 { let mut region_flows_builder = base_region_flows_builder.clone(); + let mut visible_region_flows_builder = base_visible_region_flows_builder.clone(); let mut regions = base_regions.clone(); let mut constraints = base_constraints.clone(); - extend_from_and(&mut region_flows_builder, &mut regions, &mut constraints, and); + extend_from_and( + &mut region_flows_builder, + &mut visible_region_flows_builder, + &mut regions, + &mut constraints, + and, + ); let region_flow = region_flows_builder.freeze(); + let visible_region_flow = visible_region_flows_builder.freeze(); for r in regions.into_iter() { for ub in region_flow.reachable_from(r) { // we want to retain any region constraints between two "placeholder-likes" where for our @@ -657,7 +672,12 @@ fn compute_new_region_constraints, I: Interne }; if is_placeholder_like(r) && is_placeholder_like(ub) { - constraints.push(RegionOutlives(ub, r, ())); + let visible_for_leak_check = if visible_region_flow.contains(r, ub) { + VisibleForLeakCheck::Yes + } else { + VisibleForLeakCheck::No + }; + constraints.push(RegionOutlives(ub, r, visible_for_leak_check, ())); } } } @@ -720,7 +740,7 @@ fn pull_region_outlives_constraints_out_of_universe< assert!(max_universe(infcx, c.clone()) < u); pulled_constraints.push(Or::new_leaf(c.clone())); } - RegionOutlives(region_1, region_2, ()) => { + RegionOutlives(region_1, region_2, visible_for_leak_check, ()) => { let region_1_u = max_universe(infcx, region_1); let region_2_u = max_universe(infcx, region_2); @@ -751,7 +771,9 @@ fn pull_region_outlives_constraints_out_of_universe< // As long as any region outlived by `region_1` outlives any region region which // `region_2` outlives, we know that `region_1: region_2` holds. In other words, // there exists some set of 4 regions for which `'r1: 'i1` `'i1: 'i2` `'i2: 'r2` - candidates.push(RegionOutlives(ub, lb, ())); + // + // FIXME: unsure about whether the inherited `VisibleForLeakCheck` is correct. + candidates.push(RegionOutlives(ub, lb, visible_for_leak_check, ())); } } @@ -799,7 +821,12 @@ pub fn destructure_type_outlives_constraints_in_root< PlaceholderTyOutlives(ty, r, span) => destructured_constraints.push(Or::new( regions_outlived_by_placeholder(*ty, assumptions, infcx.cx()).map( move |assumption_r| { - And::new([RegionOutlives(assumption_r, *r, span.clone())]) + And::new([RegionOutlives( + assumption_r, + *r, + VisibleForLeakCheck::Unreachable, + span.clone(), + )]) }, ), )), @@ -912,10 +939,9 @@ fn rewrite_placeholder_ty_outlives_constraints_in_universe_for_eager_placeholder // There could be `!T: 'region` assumptions in the env even if `!T` is in a // smaller universe - candidates.extend( - regions_outlived_by_placeholder(ty, assumptions, infcx.cx()) - .map(move |assumption_r| RegionOutlives(assumption_r, region, ())), - ); + candidates.extend(regions_outlived_by_placeholder(ty, assumptions, infcx.cx()).map( + move |assumption_r| RegionOutlives(assumption_r, region, VisibleForLeakCheck::No, ()), + )); // We can express `!T: 'region` as `!T: 'r` where `'r: 'region`. This is only necessary // if the placeholder type is in a smaller universe as otherwise we know all regions which @@ -1157,7 +1183,12 @@ fn alias_outlives_candidates_from_assumptions let mut relation = HigherRankedAliasMatcher { infcx, - region_constraints: vec![LeafRegionConstraint::RegionOutlives(r2, r, ())], + region_constraints: vec![LeafRegionConstraint::RegionOutlives( + r2, + r, + VisibleForLeakCheck::No, + (), + )], }; // FIXME(#155345): Both sides should be rigid in the future. @@ -1229,8 +1260,18 @@ impl<'a, Infcx: InferCtxtLike, I: Interner> TypeRelation fn regions(&mut self, a: Region, b: Region) -> RelateResult> { if a != b { - self.region_constraints.push(LeafRegionConstraint::RegionOutlives(a, b, ())); - self.region_constraints.push(LeafRegionConstraint::RegionOutlives(b, a, ())); + self.region_constraints.push(LeafRegionConstraint::RegionOutlives( + a, + b, + VisibleForLeakCheck::No, + (), + )); + self.region_constraints.push(LeafRegionConstraint::RegionOutlives( + b, + a, + VisibleForLeakCheck::No, + (), + )); } Ok(a) }