diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 9f23a0d5ab631..dde44e7e57713 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -187,6 +187,7 @@ pub(crate) fn type_check<'tcx>( typeck.infcx.destructure_solver_region_constraints_for_borrowck( &mut converter, typeck.known_type_outlives_obligations, + typeck.region_bound_pairs, universal_region_relations.outlives.clone(), ); } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 0dee9690737df..34aafd72526a8 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2406,8 +2406,12 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { for &(r1, r2) in &body.region_outlives { builder.add(r1, r2); } - let assumptions = - ty::region_constraint::Assumptions::new(body.type_outlives, builder.freeze()); + // Deliberately unelaborated: the assumptions of a `forall` are exactly the ones + // written down in the test, no extra ones hidden behind the scenes. + let assumptions = ty::region_constraint::Assumptions::new_unelaborated( + body.type_outlives, + builder.freeze(), + ); self.infcx.insert_placeholder_assumptions(u, Some(assumptions)); self.check_test_binder_body(body.value); let solver_region_constraint = self.infcx.get_solver_region_constraint(); diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index cbbf5e3c91c42..dbe85e5315500 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -59,14 +59,14 @@ //! might later infer `?U` to something like `&'b u32`, which would //! imply that `'b: 'a`. -use rustc_data_structures::transitive_relation::TransitiveRelation; +use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder}; use rustc_data_structures::undo_log::UndoLogs; use rustc_middle::bug; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::outlives::{Component, push_outlives_components}; use rustc_middle::ty::{ self, GenericArgKind, GenericArgsRef, PolyTypeOutlivesClause, Region, RegionVid, Ty, TyCtxt, - TypeVisitableExt, eager_resolve_vars, + TypeVisitableExt, Upcast, eager_resolve_vars, }; use rustc_span::Span; use rustc_type_ir::region_constraint::{self, LeafRegionConstraint}; @@ -234,9 +234,22 @@ impl<'tcx> InferCtxt<'tcx> { &self, outlives_env: &OutlivesEnvironment<'tcx>, ) { + // `FreeRegionMap::relation` stores `'sub <= 'sup` edges while + // `Assumptions::region_outlives` expects `'longer: 'shorter` ones, so the + // edges have to be inverted here. + let mut region_outlives = TransitiveRelationBuilder::default(); + for (r1, r2) in outlives_env.free_region_map().relation.base_edges() { + region_outlives.add(r2, r1); + } let assumptions = rustc_type_ir::region_constraint::Assumptions::new( - outlives_env.known_type_outlives().into_iter().cloned().collect(), - outlives_env.free_region_map().relation.clone(), + self, + assumed_type_outlives( + self.tcx, + outlives_env.known_type_outlives(), + outlives_env.region_bound_pairs(), + ), + region_outlives.freeze(), + ty::UniverseIndex::ROOT, ); self.destructure_solver_region_constraints(assumptions, self); } @@ -246,11 +259,14 @@ impl<'tcx> InferCtxt<'tcx> { // this is always ConstraintConversion but lol conversion: impl TypeOutlivesDelegate<'tcx>, known_type_outlives: &[PolyTypeOutlivesClause<'tcx>], + region_bound_pairs: &RegionBoundPairs<'tcx>, region_outlives: TransitiveRelation, ) { let assumptions = region_constraint::Assumptions::new( - known_type_outlives.into_iter().cloned().collect(), + 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); } @@ -376,6 +392,28 @@ impl<'tcx> InferCtxt<'tcx> { } } +/// The type outlives assumptions available in the root context, as clauses for +/// [`region_constraint::Assumptions::new`] to elaborate. +/// +/// `known_type_outlives` only contains the explicit `Ty: 'a` where clauses. The implied bounds, +/// e.g. `T: 'a` from a `&'a T` argument, are only tracked in `region_bound_pairs` so we have to +/// pull them in separately. Without them we'd fail to prove `T: 'a` for a `&'a T` argument +/// whenever the only explicit bound on `T` mentions a different region. +fn assumed_type_outlives<'tcx>( + tcx: TyCtxt<'tcx>, + known_type_outlives: &[PolyTypeOutlivesClause<'tcx>], + region_bound_pairs: &RegionBoundPairs<'tcx>, +) -> Vec> { + known_type_outlives + .iter() + .copied() + .chain(region_bound_pairs.iter().map(|&ty::OutlivesClause(kind, r)| { + ty::Binder::dummy(ty::OutlivesClause(kind.to_ty(tcx), r)) + })) + .map(|c| c.map_bound(ty::ClauseKind::TypeOutlives).upcast(tcx)) + .collect() +} + /// The `TypeOutlives` struct has the job of "lowering" a `T: 'a` /// obligation into a series of `'a: 'b` constraints and "verify"s, as /// described on the module comment. The final constraints are emitted 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 5a4daa5e44fc5..5ecc06b30f33b 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 @@ -2,7 +2,6 @@ #[cfg(feature = "nightly")] use rustc_data_structures::transitive_relation::TransitiveRelationBuilder; -use rustc_type_ir::ClauseKind::*; use rustc_type_ir::inherent::*; use rustc_type_ir::outlives::{Component, push_outlives_components}; #[cfg(not(feature = "nightly"))] @@ -12,8 +11,8 @@ use rustc_type_ir::region_constraint::{ propagate_ambiguity, }; use rustc_type_ir::{ - AliasTy, Binder, ClauseKind, InferCtxtLike, Interner, OutlivesClause, Region, TypeVisitable, - TypeVisitableExt, TypeVisitor, UniverseIndex, max_universe, + AliasTy, Binder, ClauseKind, InferCtxtLike, Interner, Region, TypeVisitable, TypeVisitableExt, + TypeVisitor, UniverseIndex, }; use tracing::{debug, instrument}; @@ -82,9 +81,6 @@ where t.visit_with(&mut reqs_builder); let reqs = reqs_builder.out; - let mut region_outlives_builder = TransitiveRelationBuilder::default(); - let mut type_outlives = vec![]; - // If there are inference variables in type outlives then we may not be able // to elaborate to the full set of implied bounds right now. To avoid incorrectly // NoSolution'ing when lifting constraints to a lower universe due to no usable @@ -102,25 +98,17 @@ where // FIXME(-Zassumptions-on-binders): we need to normalize here/somewhere // as we assume the type outlives assumptions only have rigid types :> - let clauses = rustc_type_ir::elaborate::elaborate( - self.cx(), - reqs.into_iter().filter_map(|goal| goal.predicate.as_clause()), - ); - - clauses.filter(move |clause| max_universe(&**self.delegate, *clause) == u).for_each( - |clause| match clause.kind().skip_binder() { - RegionOutlives(OutlivesClause(r1, r2)) => { - assert!(clause.kind().no_bound_vars().is_some()); - region_outlives_builder.add(r1, r2); - } - TypeOutlives(p) => { - type_outlives.push(clause.kind().map_bound(|_| p)); - } - _ => (), - }, - ); - - Some(Assumptions::new(type_outlives, region_outlives_builder.freeze())) + // + // `Assumptions::new` elaborates, restricts the clauses to `u` and picks out the + // outlives ones for us, so we just hand over everything the requirements gave us. + let clauses = reqs.into_iter().filter_map(|goal| goal.predicate.as_clause()); + + Some(Assumptions::new( + &**self.delegate, + clauses, + TransitiveRelationBuilder::default().freeze(), + u, + )) } #[instrument(level = "debug", skip(self), ret)] diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 9a643b538d93f..815acb11b9955 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -51,14 +51,19 @@ use crate::fold::TypeSuperFoldable; use crate::inherent::*; use crate::relate::{Relate, RelateResult, TypeRelation, VarianceDiagInfo}; use crate::{ - AliasTy, Binder, BoundRegion, BoundVar, BoundVariableKind, DebruijnIndex, InferCtxtLike, - Interner, IsRigid, OutlivesClause, Region, RegionKind, TyKind, TypeFoldable, TypeFolder, - TypingMode, UniverseIndex, Variance, max_universe, set_aliases_to_non_rigid, + AliasTy, Binder, BoundRegion, BoundVar, BoundVariableKind, ClauseKind, DebruijnIndex, + InferCtxtLike, Interner, IsRigid, OutlivesClause, Region, RegionKind, TyKind, TypeFoldable, + TypeFolder, TypingMode, UniverseIndex, Variance, elaborate, max_universe, + set_aliases_to_non_rigid, }; #[derive_where(Clone, Debug; I: Interner)] pub struct Assumptions { pub type_outlives: Vec>>, + /// Known `'a: 'b` assumptions, stored as an edge from the outliving region to the + /// outlived one, i.e. an edge `('a, 'b)` means `'a: 'b`. Constructors expect a relation + /// with this direction, see [`regions_outlived_by`] and [`regions_outliving`] for how it + /// is consumed. pub region_outlives: TransitiveRelation>, pub inverse_region_outlives: TransitiveRelation>, } @@ -72,7 +77,66 @@ impl Assumptions { } } + /// Builds assumptions from `clauses`, elaborating them and keeping the outlives ones. + /// + /// Callers hand us their clauses straight from the environment, so we have to elaborate + /// here to get at the implied outlives bounds: + /// - a `Ty: 'a` clause tells us that every region component of `Ty` outlives `'a`, e.g. + /// `&'b u8: 'a` implies `'b: 'a`. Without it we'd fail to prove `'b: 'a` when leaving + /// the binder these assumptions belong to. + /// - it also gives us the components as type outlives, e.g. `Vec: 'a` implies `T: 'a`, + /// which we need for placeholder and alias outlives. + /// - trait clauses imply their supertraits, so `T: Bound<'a>` where `trait Bound<'c>: 'c` + /// gives us `T: 'a`. This is why we take clauses rather than just the outlives ones: + /// filtering down to outlives before elaborating would throw those away. + /// + /// Only the clauses whose max universe is exactly `universe` are kept, which is what the + /// solver wants when computing the assumptions of a single binder. This happens after + /// elaboration on purpose, so a clause whose regions live in more than one universe still + /// contributes its implied bounds to each of them: `(&'b u8, &'c u8): 'a` gives us + /// `'c: 'a` in `'c`s universe even though the clause itself is in `'b`s. + /// + /// Use [`Assumptions::new_unelaborated`] when the caller needs the assumptions to be + /// exactly the clauses it passed in. pub fn new( + infcx: &impl InferCtxtLike, + clauses: impl IntoIterator, + region_outlives: TransitiveRelation>, + universe: UniverseIndex, + ) -> Self { + let mut type_outlives = vec![]; + let mut region_outlives_builder = TransitiveRelationBuilder::default(); + for (r1, r2) in region_outlives.base_edges() { + region_outlives_builder.add(r1, r2); + } + + let clauses = elaborate::elaborate(infcx.cx(), clauses) + .filter(|clause| max_universe(infcx, *clause) == universe); + for clause in clauses { + match clause.kind().skip_binder() { + // The type outlives assumptions are kept around as they are required for + // proving placeholder and alias outlives. + ClauseKind::TypeOutlives(_) => { + type_outlives.push(clause.as_type_outlives_clause().unwrap()); + } + ClauseKind::RegionOutlives(OutlivesClause(r1, r2)) => { + // `elaborate` drops the components which are bound inside of the type and + // bails on `for<'a> Ty: 'a`, so both regions here are free even though the + // clause itself may still be under a binder. + debug_assert!(!r1.is_bound() && !r2.is_bound()); + region_outlives_builder.add(r1, r2); + } + // Anything else can't be used as an outlives assumption. + _ => (), + } + } + + Self::new_unelaborated(type_outlives, region_outlives_builder.freeze()) + } + + /// Builds assumptions from exactly the given clauses, see [`Assumptions::new`] for when + /// the clauses should get elaborated instead. + pub fn new_unelaborated( type_outlives: Vec>>, region_outlives: TransitiveRelation>, ) -> Self { diff --git a/tests/ui/assumptions_on_binders/supertrait-implied-outlives-assumptions.rs b/tests/ui/assumptions_on_binders/supertrait-implied-outlives-assumptions.rs new file mode 100644 index 0000000000000..8c30faecc11b0 --- /dev/null +++ b/tests/ui/assumptions_on_binders/supertrait-implied-outlives-assumptions.rs @@ -0,0 +1,33 @@ +//@ check-pass +//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally + +// A trait clause implies its supertraits, so a `&'x (): Bound<'x>` requirement is also evidence +// for `&'x (): 'static`, which elaborates to the region assumption `'x: 'static`. +// `Assumptions::new` therefore takes clauses and elaborates them itself; handing it only the +// outlives clauses would drop the trait clause before it could imply anything. +// +// The clause has to mention the binder's own `'x` to survive the `max_universe == u` filter, +// while the supertrait outlives is on `'static` so that the assumption can discharge `'x: 'a`. +// +// Keeping the requirement binder-local matters: the `for<'x> Wrap<'x>: 'a` bound is proven at the +// call site below, so failing to discharge `'x: 'a` is a `NoSolution` inside the solver rather +// than a constraint escaping to the root. Constraints reaching the root are still dropped, so a +// shape which lets `'x` escape (e.g. requiring `T: 'x` for an outer `T`) would pass either way. +// Removing the `&'x (): Bound<'x>` clause below makes this fail, as does dropping trait clauses +// before elaborating. + +trait Bound<'c>: 'static {} + +struct Wrap<'x>(&'x ()) +where + &'x (): Bound<'x>; + +fn foo<'a>(_a: &'a u32) +where + for<'x> Wrap<'x>: 'a, +{ +} + +fn main() { + foo(&10); +} diff --git a/tests/ui/assumptions_on_binders/type-outlives-assumptions.rs b/tests/ui/assumptions_on_binders/type-outlives-assumptions.rs new file mode 100644 index 0000000000000..bc3b3a7f2c250 --- /dev/null +++ b/tests/ui/assumptions_on_binders/type-outlives-assumptions.rs @@ -0,0 +1,26 @@ +//@ check-pass +//@ compile-flags: -Zassumptions-on-binders + +// Regression test for rust-lang/project-assumptions-on-binders#19, based on the `syn` failure. +// The receiver gives us an implied `I: 'b` bound and `'b: 'a` lets that satisfy the object +// lifetime. The implied type bound has to be included in the root assumptions for that to work. +trait IterTrait<'a, T: 'a>: Iterator { + fn clone_box<'b>(&'b self) -> Box + 'a> + where + 'b: 'a; +} + +impl<'a, T, I> IterTrait<'a, T> for I +where + T: 'a, + I: Iterator + Clone, +{ + fn clone_box<'b>(&'b self) -> Box + 'a> + where + 'b: 'a, + { + Box::new(self.clone()) + } +} + +fn main() {}