diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index bb28622edf931..7c10047e9dc83 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -1,5 +1,5 @@ use rustc_data_structures::frozen::Frozen; -use rustc_data_structures::transitive_relation::TransitiveRelation; +use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder}; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives; use rustc_infer::infer::outlives::env::RegionBoundPairs; @@ -61,25 +61,13 @@ pub(crate) fn create<'tcx>( constraints, universal_regions: universal_regions.clone(), region_bound_pairs: Default::default(), - relations: UniversalRegionRelations { - universal_regions: universal_regions.clone(), - outlives: Default::default(), - inverse_outlives: Default::default(), - }, + outlives: Default::default(), + inverse_outlives: Default::default(), } .create() } impl UniversalRegionRelations<'_> { - /// Records in the `outlives_relation` (and - /// `inverse_outlives_relation`) that `fr_a: fr_b`. Invoked by the - /// builder below. - fn relate_universal_regions(&mut self, fr_a: RegionVid, fr_b: RegionVid) { - debug!("relate_universal_regions: fr_a={:?} outlives fr_b={:?}", fr_a, fr_b); - self.outlives.add(fr_a, fr_b); - self.inverse_outlives.add(fr_b, fr_a); - } - /// Given two universal regions, returns the postdominating /// upper-bound (effectively the least upper bound). /// @@ -216,11 +204,20 @@ struct UniversalRegionRelationsBuilder<'this, 'tcx> { constraints: &'this mut MirTypeckRegionConstraints<'tcx>, // outputs: - relations: UniversalRegionRelations<'tcx>, + outlives: TransitiveRelationBuilder, + inverse_outlives: TransitiveRelationBuilder, region_bound_pairs: RegionBoundPairs<'tcx>, } impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { + /// Records in the `outlives_relation` (and + /// `inverse_outlives_relation`) that `fr_a: fr_b`. + fn relate_universal_regions(&mut self, fr_a: RegionVid, fr_b: RegionVid) { + debug!("relate_universal_regions: fr_a={:?} outlives fr_b={:?}", fr_a, fr_b); + self.outlives.add(fr_a, fr_b); + self.inverse_outlives.add(fr_b, fr_a); + } + pub(crate) fn create(mut self) -> CreateResult<'tcx> { let unnormalized_input_output_tys = self .universal_regions @@ -292,9 +289,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { let fr_fn_body = self.universal_regions.fr_fn_body; for fr in self.universal_regions.universal_regions() { debug!("build: relating free region {:?} to itself and to 'static", fr); - self.relations.relate_universal_regions(fr, fr); - self.relations.relate_universal_regions(fr_static, fr); - self.relations.relate_universal_regions(fr, fr_fn_body); + self.relate_universal_regions(fr, fr); + self.relate_universal_regions(fr_static, fr); + self.relate_universal_regions(fr, fr_fn_body); } for data in &constraint_sets { @@ -313,7 +310,11 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } CreateResult { - universal_region_relations: Frozen::freeze(self.relations), + universal_region_relations: Frozen::freeze(UniversalRegionRelations { + universal_regions: self.universal_regions, + outlives: self.outlives.freeze(), + inverse_outlives: self.inverse_outlives.freeze(), + }), region_bound_pairs: self.region_bound_pairs, normalized_inputs_and_output, } @@ -356,7 +357,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // The bound says that `r1 <= r2`; we store `r2: r1`. let r1 = self.universal_regions.to_region_vid(r1); let r2 = self.universal_regions.to_region_vid(r2); - self.relations.relate_universal_regions(r2, r1); + self.relate_universal_regions(r2, r1); } OutlivesBound::RegionSubParam(r_a, param_b) => { diff --git a/compiler/rustc_data_structures/src/transitive_relation.rs b/compiler/rustc_data_structures/src/transitive_relation.rs index 0ff64969b071c..f016c391fe777 100644 --- a/compiler/rustc_data_structures/src/transitive_relation.rs +++ b/compiler/rustc_data_structures/src/transitive_relation.rs @@ -1,45 +1,57 @@ +use crate::frozen::Frozen; use crate::fx::FxIndexSet; -use crate::sync::Lock; use rustc_index::bit_set::BitMatrix; use std::fmt::Debug; use std::hash::Hash; use std::mem; +use std::ops::Deref; #[cfg(test)] mod tests; #[derive(Clone, Debug)] -pub struct TransitiveRelation { +pub struct TransitiveRelationBuilder { // List of elements. This is used to map from a T to a usize. elements: FxIndexSet, // List of base edges in the graph. Require to compute transitive // closure. edges: Vec, +} + +#[derive(Debug)] +pub struct TransitiveRelation { + // Frozen transitive relation elements and edges. + builder: Frozen>, - // This is a cached transitive closure derived from the edges. - // Currently, we build it lazily and just throw out any existing - // copy whenever a new edge is added. (The Lock is to permit - // the lazy computation.) This is kind of silly, except for the - // fact its size is tied to `self.elements.len()`, so I wanted to - // wait before building it up to avoid reallocating as new edges - // are added with new elements. Perhaps better would be to ask the - // user for a batch of edges to minimize this effect, but I - // already wrote the code this way. :P -nmatsakis - closure: Lock>>, + // Cached transitive closure derived from the edges. + closure: Frozen>, } -// HACK(eddyb) manual impl avoids `Default` bound on `T`. -impl Default for TransitiveRelation { - fn default() -> Self { +impl Deref for TransitiveRelation { + type Target = Frozen>; + + fn deref(&self) -> &Self::Target { + &self.builder + } +} + +impl Clone for TransitiveRelation { + fn clone(&self) -> Self { TransitiveRelation { - elements: Default::default(), - edges: Default::default(), - closure: Default::default(), + builder: Frozen::freeze(self.builder.deref().clone()), + closure: Frozen::freeze(self.closure.deref().clone()), } } } +// HACK(eddyb) manual impl avoids `Default` bound on `T`. +impl Default for TransitiveRelationBuilder { + fn default() -> Self { + TransitiveRelationBuilder { elements: Default::default(), edges: Default::default() } + } +} + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] struct Index(usize); @@ -49,7 +61,7 @@ struct Edge { target: Index, } -impl TransitiveRelation { +impl TransitiveRelationBuilder { pub fn is_empty(&self) -> bool { self.edges.is_empty() } @@ -63,23 +75,19 @@ impl TransitiveRelation { } fn add_index(&mut self, a: T) -> Index { - let (index, added) = self.elements.insert_full(a); - if added { - // if we changed the dimensions, clear the cache - *self.closure.get_mut() = None; - } + let (index, _added) = self.elements.insert_full(a); Index(index) } /// Applies the (partial) function to each edge and returns a new - /// relation. If `f` returns `None` for any end-point, returns - /// `None`. - pub fn maybe_map(&self, mut f: F) -> Option> + /// relation builder. If `f` returns `None` for any end-point, + /// returns `None`. + pub fn maybe_map(&self, mut f: F) -> Option> where F: FnMut(T) -> Option, U: Clone + Debug + Eq + Hash + Copy, { - let mut result = TransitiveRelation::default(); + let mut result = TransitiveRelationBuilder::default(); for edge in &self.edges { result.add(f(self.elements[edge.source.0])?, f(self.elements[edge.target.0])?); } @@ -93,10 +101,38 @@ impl TransitiveRelation { let edge = Edge { source: a, target: b }; if !self.edges.contains(&edge) { self.edges.push(edge); + } + } + + /// Compute the transitive closure derived from the edges, and converted to + /// the final result. After this, all elements will be immutable to maintain + /// the correctness of the result. + pub fn freeze(self) -> TransitiveRelation { + let mut matrix = BitMatrix::new(self.elements.len(), self.elements.len()); + let mut changed = true; + while changed { + changed = false; + for edge in &self.edges { + // add an edge from S -> T + changed |= matrix.insert(edge.source.0, edge.target.0); - // added an edge, clear the cache - *self.closure.get_mut() = None; + // add all outgoing edges from T into S + changed |= matrix.union_rows(edge.target.0, edge.source.0); + } } + TransitiveRelation { builder: Frozen::freeze(self), closure: Frozen::freeze(matrix) } + } +} + +impl TransitiveRelation { + /// Applies the (partial) function to each edge and returns a new + /// relation including transitive closures. + pub fn maybe_map(&self, f: F) -> Option> + where + F: FnMut(T) -> Option, + U: Clone + Debug + Eq + Hash + Copy, + { + Some(self.builder.maybe_map(f)?.freeze()) } /// Checks whether `a < target` (transitively) @@ -322,30 +358,7 @@ impl TransitiveRelation { where OP: FnOnce(&BitMatrix) -> R, { - let mut closure_cell = self.closure.borrow_mut(); - let mut closure = closure_cell.take(); - if closure.is_none() { - closure = Some(self.compute_closure()); - } - let result = op(closure.as_ref().unwrap()); - *closure_cell = closure; - result - } - - fn compute_closure(&self) -> BitMatrix { - let mut matrix = BitMatrix::new(self.elements.len(), self.elements.len()); - let mut changed = true; - while changed { - changed = false; - for edge in &self.edges { - // add an edge from S -> T - changed |= matrix.insert(edge.source.0, edge.target.0); - - // add all outgoing edges from T into S - changed |= matrix.union_rows(edge.target.0, edge.source.0); - } - } - matrix + op(&self.closure) } /// Lists all the base edges in the graph: the initial _non-transitive_ set of element diff --git a/compiler/rustc_data_structures/src/transitive_relation/tests.rs b/compiler/rustc_data_structures/src/transitive_relation/tests.rs index e1f4c7ee07315..e756c546e41ba 100644 --- a/compiler/rustc_data_structures/src/transitive_relation/tests.rs +++ b/compiler/rustc_data_structures/src/transitive_relation/tests.rs @@ -10,9 +10,10 @@ impl TransitiveRelation { #[test] fn test_one_step() { - let mut relation = TransitiveRelation::default(); + let mut relation = TransitiveRelationBuilder::default(); relation.add("a", "b"); relation.add("a", "c"); + let relation = relation.freeze(); assert!(relation.contains("a", "c")); assert!(relation.contains("a", "b")); assert!(!relation.contains("b", "a")); @@ -21,7 +22,7 @@ fn test_one_step() { #[test] fn test_many_steps() { - let mut relation = TransitiveRelation::default(); + let mut relation = TransitiveRelationBuilder::default(); relation.add("a", "b"); relation.add("a", "c"); relation.add("a", "f"); @@ -31,6 +32,7 @@ fn test_many_steps() { relation.add("b", "e"); relation.add("e", "g"); + let relation = relation.freeze(); assert!(relation.contains("a", "b")); assert!(relation.contains("a", "c")); @@ -51,9 +53,10 @@ fn mubs_triangle() { // ^ // | // b - let mut relation = TransitiveRelation::default(); + let mut relation = TransitiveRelationBuilder::default(); relation.add("a", "tcx"); relation.add("b", "tcx"); + let relation = relation.freeze(); assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["tcx"]); assert_eq!(relation.parents("a"), vec!["tcx"]); assert_eq!(relation.parents("b"), vec!["tcx"]); @@ -72,7 +75,7 @@ fn mubs_best_choice1() { // need the second pare down call to get the right result (after // intersection, we have [1, 2], but 2 -> 1). - let mut relation = TransitiveRelation::default(); + let mut relation = TransitiveRelationBuilder::default(); relation.add("0", "1"); relation.add("0", "2"); @@ -80,6 +83,7 @@ fn mubs_best_choice1() { relation.add("3", "1"); relation.add("3", "2"); + let relation = relation.freeze(); assert_eq!(relation.minimal_upper_bounds("0", "3"), vec!["2"]); assert_eq!(relation.parents("0"), vec!["2"]); @@ -99,7 +103,7 @@ fn mubs_best_choice2() { // Like the preceding test, but in this case intersection is [2, // 1], and hence we rely on the first pare down call. - let mut relation = TransitiveRelation::default(); + let mut relation = TransitiveRelationBuilder::default(); relation.add("0", "1"); relation.add("0", "2"); @@ -107,6 +111,7 @@ fn mubs_best_choice2() { relation.add("3", "1"); relation.add("3", "2"); + let relation = relation.freeze(); assert_eq!(relation.minimal_upper_bounds("0", "3"), vec!["1"]); assert_eq!(relation.parents("0"), vec!["1"]); @@ -118,12 +123,13 @@ fn mubs_best_choice2() { fn mubs_no_best_choice() { // in this case, the intersection yields [1, 2], and the "pare // down" calls find nothing to remove. - let mut relation = TransitiveRelation::default(); + let mut relation = TransitiveRelationBuilder::default(); relation.add("0", "1"); relation.add("0", "2"); relation.add("3", "1"); relation.add("3", "2"); + let relation = relation.freeze(); assert_eq!(relation.minimal_upper_bounds("0", "3"), vec!["1", "2"]); assert_eq!(relation.parents("0"), vec!["1", "2"]); @@ -135,7 +141,7 @@ fn mubs_best_choice_scc() { // in this case, 1 and 2 form a cycle; we pick arbitrarily (but // consistently). - let mut relation = TransitiveRelation::default(); + let mut relation = TransitiveRelationBuilder::default(); relation.add("0", "1"); relation.add("0", "2"); @@ -144,6 +150,7 @@ fn mubs_best_choice_scc() { relation.add("3", "1"); relation.add("3", "2"); + let relation = relation.freeze(); assert_eq!(relation.minimal_upper_bounds("0", "3"), vec!["1"]); assert_eq!(relation.parents("0"), vec!["1"]); @@ -157,13 +164,14 @@ fn pdub_crisscross() { // /\ | // b -> b1 ---+ - let mut relation = TransitiveRelation::default(); + let mut relation = TransitiveRelationBuilder::default(); relation.add("a", "a1"); relation.add("a", "b1"); relation.add("b", "a1"); relation.add("b", "b1"); relation.add("a1", "x"); relation.add("b1", "x"); + let relation = relation.freeze(); assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["a1", "b1"]); assert_eq!(relation.postdom_upper_bound("a", "b"), Some("x")); @@ -179,7 +187,7 @@ fn pdub_crisscross_more() { // /\ /\ | // b -> b1 -> b2 ---------+ - let mut relation = TransitiveRelation::default(); + let mut relation = TransitiveRelationBuilder::default(); relation.add("a", "a1"); relation.add("a", "b1"); relation.add("b", "a1"); @@ -194,6 +202,7 @@ fn pdub_crisscross_more() { relation.add("a3", "x"); relation.add("b2", "x"); + let relation = relation.freeze(); assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["a1", "b1"]); assert_eq!(relation.minimal_upper_bounds("a1", "b1"), vec!["a2", "b2"]); @@ -210,11 +219,12 @@ fn pdub_lub() { // | // b -> b1 ---+ - let mut relation = TransitiveRelation::default(); + let mut relation = TransitiveRelationBuilder::default(); relation.add("a", "a1"); relation.add("b", "b1"); relation.add("a1", "x"); relation.add("b1", "x"); + let relation = relation.freeze(); assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["x"]); assert_eq!(relation.postdom_upper_bound("a", "b"), Some("x")); @@ -233,10 +243,11 @@ fn mubs_intermediate_node_on_one_side_only() { // b // "digraph { a -> c -> d; b -> d; }", - let mut relation = TransitiveRelation::default(); + let mut relation = TransitiveRelationBuilder::default(); relation.add("a", "c"); relation.add("c", "d"); relation.add("b", "d"); + let relation = relation.freeze(); assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["d"]); } @@ -252,12 +263,13 @@ fn mubs_scc_1() { // b // "digraph { a -> c -> d; d -> c; a -> d; b -> d; }", - let mut relation = TransitiveRelation::default(); + let mut relation = TransitiveRelationBuilder::default(); relation.add("a", "c"); relation.add("c", "d"); relation.add("d", "c"); relation.add("a", "d"); relation.add("b", "d"); + let relation = relation.freeze(); assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["c"]); } @@ -272,12 +284,13 @@ fn mubs_scc_2() { // +--- b // "digraph { a -> c -> d; d -> c; b -> d; b -> c; }", - let mut relation = TransitiveRelation::default(); + let mut relation = TransitiveRelationBuilder::default(); relation.add("a", "c"); relation.add("c", "d"); relation.add("d", "c"); relation.add("b", "d"); relation.add("b", "c"); + let relation = relation.freeze(); assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["c"]); } @@ -292,13 +305,14 @@ fn mubs_scc_3() { // b ---+ // "digraph { a -> c -> d -> e -> c; b -> d; b -> e; }", - let mut relation = TransitiveRelation::default(); + let mut relation = TransitiveRelationBuilder::default(); relation.add("a", "c"); relation.add("c", "d"); relation.add("d", "e"); relation.add("e", "c"); relation.add("b", "d"); relation.add("b", "e"); + let relation = relation.freeze(); assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["c"]); } @@ -314,13 +328,14 @@ fn mubs_scc_4() { // b ---+ // "digraph { a -> c -> d -> e -> c; a -> d; b -> e; }" - let mut relation = TransitiveRelation::default(); + let mut relation = TransitiveRelationBuilder::default(); relation.add("a", "c"); relation.add("c", "d"); relation.add("d", "e"); relation.add("e", "c"); relation.add("a", "d"); relation.add("b", "e"); + let relation = relation.freeze(); assert_eq!(relation.minimal_upper_bounds("a", "b"), vec!["c"]); } @@ -352,10 +367,11 @@ fn parent() { (1, /*->*/ 3), ]; - let mut relation = TransitiveRelation::default(); + let mut relation = TransitiveRelationBuilder::default(); for (a, b) in pairs { relation.add(a, b); } + let relation = relation.freeze(); let p = relation.postdom_parent(3); assert_eq!(p, Some(0)); diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs index d566634a49203..728d691a2be7d 100644 --- a/compiler/rustc_infer/src/infer/free_regions.rs +++ b/compiler/rustc_infer/src/infer/free_regions.rs @@ -27,13 +27,13 @@ impl<'a, 'tcx> RegionRelations<'a, 'tcx> { } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct FreeRegionMap<'tcx> { // Stores the relation `a < b`, where `a` and `b` are regions. // // Invariant: only free regions like `'x` or `'static` are stored // in this relation, not scopes. - relation: TransitiveRelation>, + pub(crate) relation: TransitiveRelation>, } impl<'tcx> FreeRegionMap<'tcx> { @@ -45,15 +45,6 @@ impl<'tcx> FreeRegionMap<'tcx> { self.relation.is_empty() } - // Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`. - // (with the exception that `'static: 'x` is not notable) - pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { - debug!("relate_regions(sub={:?}, sup={:?})", sub, sup); - if sub.is_free_or_static() && sup.is_free() { - self.relation.add(sub, sup) - } - } - /// Tests whether `r_a <= r_b`. /// /// Both regions must meet `is_free_or_static`. diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index b2decd64f0fd9..872886da36261 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -2,6 +2,7 @@ use crate::infer::free_regions::FreeRegionMap; use crate::infer::{GenericKind, InferCtxt}; use crate::traits::query::OutlivesBound; use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::transitive_relation::TransitiveRelationBuilder; use rustc_middle::ty::{self, ReEarlyBound, ReFree, ReVar, Region}; use super::explicit_outlives_bounds; @@ -51,23 +52,48 @@ pub struct OutlivesEnvironment<'tcx> { region_bound_pairs: RegionBoundPairs<'tcx>, } +/// Builder of OutlivesEnvironment. +struct OutlivesEnvironmentBuilder<'tcx> { + param_env: ty::ParamEnv<'tcx>, + region_relation: TransitiveRelationBuilder>, + region_bound_pairs: RegionBoundPairs<'tcx>, +} + /// "Region-bound pairs" tracks outlives relations that are known to /// be true, either because of explicit where-clauses like `T: 'a` or /// because of implied bounds. pub type RegionBoundPairs<'tcx> = FxIndexSet, Region<'tcx>>>; -impl<'a, 'tcx> OutlivesEnvironment<'tcx> { - pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { - let mut env = OutlivesEnvironment { +impl<'tcx> OutlivesEnvironment<'tcx> { + /// Create a builder using `ParamEnv` and add explicit outlives bounds into it. + fn builder(param_env: ty::ParamEnv<'tcx>) -> OutlivesEnvironmentBuilder<'tcx> { + let mut builder = OutlivesEnvironmentBuilder { param_env, - free_region_map: Default::default(), + region_relation: Default::default(), region_bound_pairs: Default::default(), }; - env.add_outlives_bounds(None, explicit_outlives_bounds(param_env)); + builder.add_outlives_bounds(None, explicit_outlives_bounds(param_env)); - env + builder + } + + #[inline] + /// Create a new `OutlivesEnvironment` without extra outlives bounds. + pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { + Self::builder(param_env).build() + } + + /// Create a new `OutlivesEnvironment` with extra outlives bounds. + pub fn with_bounds<'a>( + param_env: ty::ParamEnv<'tcx>, + infcx: Option<&InferCtxt<'a, 'tcx>>, + extra_bounds: impl IntoIterator>, + ) -> Self { + let mut builder = Self::builder(param_env); + builder.add_outlives_bounds(infcx, extra_bounds); + builder.build() } /// Borrows current value of the `free_region_map`. @@ -79,6 +105,17 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { pub fn region_bound_pairs(&self) -> &RegionBoundPairs<'tcx> { &self.region_bound_pairs } +} + +impl<'a, 'tcx> OutlivesEnvironmentBuilder<'tcx> { + #[inline] + fn build(self) -> OutlivesEnvironment<'tcx> { + OutlivesEnvironment { + param_env: self.param_env, + free_region_map: FreeRegionMap { relation: self.region_relation.freeze() }, + region_bound_pairs: self.region_bound_pairs, + } + } /// Processes outlives bounds that are known to hold, whether from implied or other sources. /// @@ -86,11 +123,8 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { /// contain inference variables, it must be supplied, in which /// case we will register "givens" on the inference context. (See /// `RegionConstraintData`.) - pub fn add_outlives_bounds( - &mut self, - infcx: Option<&InferCtxt<'a, 'tcx>>, - outlives_bounds: I, - ) where + fn add_outlives_bounds(&mut self, infcx: Option<&InferCtxt<'a, 'tcx>>, outlives_bounds: I) + where I: IntoIterator>, { // Record relationships such as `T:'x` that don't go into the @@ -122,7 +156,9 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { // system to be more general and to make use // of *every* relationship that arises here, // but presently we do not.) - self.free_region_map.relate_regions(r_a, r_b); + if r_a.is_free_or_static() && r_b.is_free() { + self.region_relation.add(r_a, r_b) + } } } } diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index f9fd6c9c56b42..d9aff94fef2f9 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -256,6 +256,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// .iterate_to_fixpoint() /// .into_results_cursor(body); /// ``` + #[inline] fn into_engine<'mir>( self, tcx: TyCtxt<'tcx>, @@ -413,7 +414,7 @@ where } /* Extension methods */ - + #[inline] fn into_engine<'mir>( self, tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index db82be7a98fc6..dfef924f6993a 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -1,6 +1,6 @@ use super::potentially_plural_count; -use crate::check::regionck::OutlivesEnvironmentExt; use crate::errors::LifetimesOrBoundsMismatchOnTrait; +use crate::outlives::outlives_bounds::InferCtxtExt as _; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed}; use rustc_hir as hir; @@ -401,8 +401,11 @@ fn compare_predicate_entailment<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(infcx, wf_tys, impl_m_hir_id); + let outlives_environment = OutlivesEnvironment::with_bounds( + param_env, + Some(infcx), + infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys), + ); infcx.check_region_obligations_and_report_errors( impl_m.def_id.expect_local(), &outlives_environment, @@ -1513,8 +1516,10 @@ pub fn check_type_bounds<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(&infcx, assumed_wf_types, impl_ty_hir_id); + let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_hir_id, assumed_wf_types); + let outlives_environment = + OutlivesEnvironment::with_bounds(param_env, Some(&infcx), implied_bounds); + infcx.check_region_obligations_and_report_errors( impl_ty.def_id.expect_local(), &outlives_environment, diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index dfbef544b1d28..fb675212e3ffb 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -87,7 +87,6 @@ mod op; mod pat; mod place_op; mod region; -pub mod regionck; pub mod rvalue_scopes; mod upvar; pub mod wfcheck; diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs deleted file mode 100644 index d49a6138f7a02..0000000000000 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::outlives::outlives_bounds::InferCtxtExt as _; -use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::InferCtxt; -use rustc_middle::ty::Ty; - -pub(crate) trait OutlivesEnvironmentExt<'tcx> { - fn add_implied_bounds( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - fn_sig_tys: FxHashSet>, - body_id: hir::HirId, - ); -} - -impl<'tcx> OutlivesEnvironmentExt<'tcx> for OutlivesEnvironment<'tcx> { - /// This method adds "implied bounds" into the outlives environment. - /// Implied bounds are outlives relationships that we can deduce - /// on the basis that certain types must be well-formed -- these are - /// either the types that appear in the function signature or else - /// the input types to an impl. For example, if you have a function - /// like - /// - /// ``` - /// fn foo<'a, 'b, T>(x: &'a &'b [T]) { } - /// ``` - /// - /// we can assume in the caller's body that `'b: 'a` and that `T: - /// 'b` (and hence, transitively, that `T: 'a`). This method would - /// add those assumptions into the outlives-environment. - /// - /// Tests: `src/test/ui/regions/regions-free-region-ordering-*.rs` - #[instrument(level = "debug", skip(self, infcx))] - fn add_implied_bounds<'a>( - &mut self, - infcx: &InferCtxt<'a, 'tcx>, - fn_sig_tys: FxHashSet>, - body_id: hir::HirId, - ) { - for ty in fn_sig_tys { - let ty = infcx.resolve_vars_if_possible(ty); - let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty); - self.add_outlives_bounds(Some(infcx), implied_bounds) - } - } -} diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 4814aea7afb9e..b4b6fe8eeada3 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -1,5 +1,5 @@ -use crate::check::regionck::OutlivesEnvironmentExt; use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter}; +use crate::outlives::outlives_bounds::InferCtxtExt as _; use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; @@ -104,8 +104,10 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>( return; } - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(infcx, assumed_wf_types, body_id); + let implied_bounds = infcx.implied_bounds_tys(param_env, body_id, assumed_wf_types); + let outlives_environment = + OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds); + infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment); }) } @@ -694,8 +696,11 @@ fn resolve_regions_with_wf_tys<'tcx>( // region constraints get added and solved there and we need to test each // call individually. tcx.infer_ctxt().enter(|infcx| { - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id); + let outlives_environment = OutlivesEnvironment::with_bounds( + param_env, + Some(&infcx), + infcx.implied_bounds_tys(param_env, id, wf_tys.clone()), + ); let region_bound_pairs = outlives_environment.region_bound_pairs(); add_constraints(&infcx, region_bound_pairs); diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs index 97346f0f834c8..6240024d49c74 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs @@ -65,9 +65,9 @@ //! cause use after frees with purely safe code in the same way as specializing //! on traits with methods can. -use crate::check::regionck::OutlivesEnvironmentExt; use crate::constrained_generic_params as cgp; use crate::errors::SubstsOnOverriddenImpl; +use crate::outlives::outlives_bounds::InferCtxtExt as _; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -157,8 +157,8 @@ fn get_impl_substs<'tcx>( return None; } - let mut outlives_env = OutlivesEnvironment::new(param_env); - outlives_env.add_implied_bounds(infcx, assumed_wf_types, impl1_hir_id); + let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_hir_id, assumed_wf_types); + let outlives_env = OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds); infcx.check_region_obligations_and_report_errors(impl1_def_id, &outlives_env); let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else { let span = tcx.def_span(impl1_def_id); diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 8c6fb6a77181d..1ff9c6271316a 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -72,6 +72,7 @@ This API is completely unstable and subject to change. #![feature(slice_partition_dedup)] #![feature(try_blocks)] #![feature(is_some_with)] +#![feature(type_alias_impl_trait)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_typeck/src/outlives/outlives_bounds.rs b/compiler/rustc_typeck/src/outlives/outlives_bounds.rs index 229a64650848c..024e20d92234b 100644 --- a/compiler/rustc_typeck/src/outlives/outlives_bounds.rs +++ b/compiler/rustc_typeck/src/outlives/outlives_bounds.rs @@ -1,5 +1,7 @@ +use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_middle::ty::{self, Ty}; +use rustc_hir::HirId; +use rustc_middle::ty::{self, ParamEnv, Ty}; use rustc_trait_selection::infer::InferCtxt; use rustc_trait_selection::traits::query::type_op::{self, TypeOp, TypeOpOutput}; use rustc_trait_selection::traits::query::NoSolution; @@ -7,16 +9,24 @@ use rustc_trait_selection::traits::{ObligationCause, TraitEngine, TraitEngineExt pub use rustc_middle::traits::query::OutlivesBound; -pub trait InferCtxtExt<'tcx> { +type Bounds<'a, 'tcx: 'a> = impl Iterator> + 'a; +pub trait InferCtxtExt<'a, 'tcx> { fn implied_outlives_bounds( &self, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, ty: Ty<'tcx>, ) -> Vec>; + + fn implied_bounds_tys( + &'a self, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + tys: FxHashSet>, + ) -> Bounds<'a, 'tcx>; } -impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { +impl<'a, 'cx, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'cx, 'tcx> { /// Implied bounds are region relationships that we deduce /// automatically. The idea is that (e.g.) a caller must check that a /// function's argument types are well-formed immediately before @@ -87,4 +97,18 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { output } + + fn implied_bounds_tys( + &'a self, + param_env: ParamEnv<'tcx>, + body_id: HirId, + tys: FxHashSet>, + ) -> Bounds<'a, 'tcx> { + tys.into_iter() + .map(move |ty| { + let ty = self.resolve_vars_if_possible(ty); + self.implied_outlives_bounds(param_env, body_id, ty) + }) + .flatten() + } }