Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 4 additions & 17 deletions compiler/rustc_infer/src/infer/outlives/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
//! Various code related to computing outlives relations.

use rustc_data_structures::undo_log::UndoLogs;
use rustc_middle::traits::query::{NoSolution, OutlivesBound};
use rustc_middle::traits::query::OutlivesBound;
use rustc_middle::ty;
use tracing::instrument;

use self::env::OutlivesEnvironment;
use super::region_constraints::{RegionConstraintData, UndoLog};
use super::{InferCtxt, RegionResolutionError, SubregionOrigin};
use super::{InferCtxt, RegionResolutionError};
use crate::infer::free_regions::RegionRelations;
use crate::infer::lexical_region_resolve;
use crate::infer::region_constraints::ConstraintKind;
Expand Down Expand Up @@ -35,25 +35,12 @@ impl<'tcx> InferCtxt<'tcx> {
/// result. After this, no more unification operations should be
/// done -- or the compiler will panic -- but it is legal to use
/// `resolve_vars_if_possible` as well as `fully_resolve`.
///
/// If you are in a crate that has access to `rustc_trait_selection`,
/// then it's probably better to use `resolve_regions`,
/// which knows how to normalize registered region obligations.
#[must_use]
pub fn resolve_regions_with_normalize(
pub fn resolve_regions_with_outlives_env(
&self,
outlives_env: &OutlivesEnvironment<'tcx>,
deeply_normalize_ty: impl Fn(
ty::PolyTypeOutlivesPredicate<'tcx>,
SubregionOrigin<'tcx>,
) -> Result<ty::PolyTypeOutlivesPredicate<'tcx>, NoSolution>,
) -> Vec<RegionResolutionError<'tcx>> {
match self.process_registered_region_obligations(outlives_env, deeply_normalize_ty) {
Ok(()) => {}
Err((clause, origin)) => {
return vec![RegionResolutionError::CannotNormalize(clause, origin)];
}
};
self.process_registered_region_obligations(outlives_env);

let mut storage = {
let mut inner = self.inner.borrow_mut();
Expand Down
93 changes: 31 additions & 62 deletions compiler/rustc_infer/src/infer/outlives/obligations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,10 @@
//! imply that `'b: 'a`.

use rustc_data_structures::undo_log::UndoLogs;
use rustc_middle::bug;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::traits::query::NoSolution;
use rustc_middle::ty::outlives::{Component, push_outlives_components};
use rustc_middle::ty::{
self, GenericArgKind, GenericArgsRef, PolyTypeOutlivesPredicate, Region, Ty, TyCtxt,
TypeFoldable as _, TypeVisitableExt,
self, GenericArgKind, GenericArgsRef, Region, Ty, TyCtxt, TypeFoldable as _, TypeVisitableExt,
};
use smallvec::smallvec;
use tracing::{debug, instrument};
Expand Down Expand Up @@ -194,70 +191,42 @@ impl<'tcx> InferCtxt<'tcx> {
/// flow of the inferencer. The key point is that it is
/// invoked after all type-inference variables have been bound --
/// right before lexical region resolution.
#[instrument(level = "debug", skip(self, outlives_env, deeply_normalize_ty))]
pub fn process_registered_region_obligations(
&self,
outlives_env: &OutlivesEnvironment<'tcx>,
mut deeply_normalize_ty: impl FnMut(
PolyTypeOutlivesPredicate<'tcx>,
SubregionOrigin<'tcx>,
)
-> Result<PolyTypeOutlivesPredicate<'tcx>, NoSolution>,
) -> Result<(), (PolyTypeOutlivesPredicate<'tcx>, SubregionOrigin<'tcx>)> {
#[instrument(level = "debug", skip(self, outlives_env))]
pub fn process_registered_region_obligations(&self, outlives_env: &OutlivesEnvironment<'tcx>) {
assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot");

// Must loop since the process of normalizing may itself register region obligations.
for iteration in 0.. {
let my_region_obligations = self.take_registered_region_obligations();
if my_region_obligations.is_empty() {
break;
}

if !self.tcx.recursion_limit().value_within_limit(iteration) {
// This may actually be reachable. If so, we should convert
// this to a proper error/consider whether we should detect
// this somewhere else.
bug!(
"unexpected overflowed when processing region obligations: {my_region_obligations:#?}"
);
for TypeOutlivesConstraint { sup_type, sub_region, origin } in
self.take_registered_region_obligations()
{
let ty::OutlivesPredicate(sup_type, sub_region) =
self.resolve_vars_if_possible(ty::OutlivesPredicate(sup_type, sub_region));

// `TypeOutlives` is structural, so we should try to opportunistically resolve all
// region vids before processing regions, so we have a better chance to match clauses
// in our param-env.
let (sup_type, sub_region) =
(sup_type, sub_region).fold_with(&mut OpportunisticRegionResolver::new(self));

if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions
&& outlives_env
.higher_ranked_assumptions()
.contains(&ty::OutlivesPredicate(sup_type.into(), sub_region))
{
continue;
}

for TypeOutlivesConstraint { sup_type, sub_region, origin } in my_region_obligations {
let outlives = ty::Binder::dummy(ty::OutlivesPredicate(sup_type, sub_region));
let ty::OutlivesPredicate(sup_type, sub_region) =
deeply_normalize_ty(outlives, origin.clone())
.map_err(|NoSolution| (outlives, origin.clone()))?
.no_bound_vars()
.expect("started with no bound vars, should end with no bound vars");
// `TypeOutlives` is structural, so we should try to opportunistically resolve all
// region vids before processing regions, so we have a better chance to match clauses
// in our param-env.
let (sup_type, sub_region) =
(sup_type, sub_region).fold_with(&mut OpportunisticRegionResolver::new(self));

if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions
&& outlives_env
.higher_ranked_assumptions()
.contains(&ty::OutlivesPredicate(sup_type.into(), sub_region))
{
continue;
}
debug!(?sup_type, ?sub_region, ?origin);

debug!(?sup_type, ?sub_region, ?origin);

let outlives = &mut TypeOutlives::new(
self,
self.tcx,
outlives_env.region_bound_pairs(),
None,
outlives_env.known_type_outlives(),
);
let category = origin.to_constraint_category();
outlives.type_must_outlive(origin, sup_type, sub_region, category);
}
let outlives = &mut TypeOutlives::new(
self,
self.tcx,
outlives_env.region_bound_pairs(),
None,
outlives_env.known_type_outlives(),
);
let category = origin.to_constraint_category();
outlives.type_must_outlive(origin, sup_type, sub_region, category);
}

Ok(())
}
}

Expand Down
104 changes: 104 additions & 0 deletions compiler/rustc_next_trait_solver/src/placeholder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,107 @@ where
if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
}
}

/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which
/// they came.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the version outside of rustc_next_trait-solver and use this one in rustc_trait_selection

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah sorry it exists, addressed in ce67440.

pub struct PlaceholderReplacer<'a, I: Interner> {
cx: I,
mapped_regions: IndexMap<ty::PlaceholderRegion<I>, ty::BoundRegion<I>>,
mapped_types: IndexMap<ty::PlaceholderType<I>, ty::BoundTy<I>>,
mapped_consts: IndexMap<ty::PlaceholderConst<I>, ty::BoundConst<I>>,
universe_indices: &'a [Option<ty::UniverseIndex>],
current_index: ty::DebruijnIndex,
}

impl<'a, I: Interner> PlaceholderReplacer<'a, I> {
pub fn replace_placeholders<T: TypeFoldable<I>>(
cx: I,
mapped_regions: IndexMap<ty::PlaceholderRegion<I>, ty::BoundRegion<I>>,
mapped_types: IndexMap<ty::PlaceholderType<I>, ty::BoundTy<I>>,
mapped_consts: IndexMap<ty::PlaceholderConst<I>, ty::BoundConst<I>>,
universe_indices: &'a [Option<ty::UniverseIndex>],
value: T,
) -> T {
let mut replacer = PlaceholderReplacer {
cx,
mapped_regions,
mapped_types,
mapped_consts,
universe_indices,
current_index: ty::INNERMOST,
};
value.fold_with(&mut replacer)
}

fn debruijn_for_universe(&self, universe: ty::UniverseIndex) -> ty::DebruijnIndex {
let index = self
.universe_indices
.iter()
.position(|u| matches!(u, Some(u_idx) if *u_idx == universe))
.unwrap_or_else(|| panic!("unexpected placeholder universe {universe:?}"));

ty::DebruijnIndex::from_usize(
self.universe_indices.len() - index + self.current_index.as_usize() - 1,
)
}
}

impl<I: Interner> TypeFolder<I> for PlaceholderReplacer<'_, I> {
fn cx(&self) -> I {
self.cx
}

fn fold_binder<T: TypeFoldable<I>>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T> {
if !t.has_placeholders() && !t.has_infer() {
return t;
}

self.current_index.shift_in(1);
let t = t.super_fold_with(self);
self.current_index.shift_out(1);
t
}

fn fold_region(&mut self, r: I::Region) -> I::Region {
if let ty::RePlaceholder(p) = r.kind() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

u don't try to resolve things here anymore? Either add that back or add an assert that we do. I think this should take an InferCtxtLike

if let Some(replace_var) = self.mapped_regions.get(&p) {
let db = self.debruijn_for_universe(p.universe());
return Region::new_bound(self.cx(), db, *replace_var);
}
}

r
}

fn fold_ty(&mut self, ty: I::Ty) -> I::Ty {
if let ty::Placeholder(p) = ty.kind() {
match self.mapped_types.get(&p) {
Some(replace_var) => {
let db = self.debruijn_for_universe(p.universe());
Ty::new_bound(self.cx(), db, *replace_var)
}
None => ty,
}
} else if ty.has_placeholders() || ty.has_infer() {
ty.super_fold_with(self)
} else {
ty
}
}

fn fold_const(&mut self, ct: I::Const) -> I::Const {
if let ty::ConstKind::Placeholder(p) = ct.kind() {
match self.mapped_consts.get(&p) {
Some(replace_var) => {
let db = self.debruijn_for_universe(p.universe());
Const::new_bound(self.cx(), db, *replace_var)
}
None => ct,
}
} else if ct.has_placeholders() || ct.has_infer() {
ct.super_fold_with(self)
} else {
ct
}
}
}
19 changes: 18 additions & 1 deletion compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::ops::ControlFlow;

#[cfg(feature = "nightly")]
use rustc_macros::HashStable_NoContext;
use rustc_type_ir::data_structures::{HashMap, HashSet};
use rustc_type_ir::data_structures::{HashMap, HashSet, IndexMap};
use rustc_type_ir::inherent::*;
use rustc_type_ir::relate::Relate;
use rustc_type_ir::relate::solver_relating::RelateExt;
Expand Down Expand Up @@ -1076,6 +1076,10 @@ where
self.delegate.shallow_resolve(ty)
}

pub(super) fn shallow_resolve_const(&self, ct: I::Const) -> I::Const {
self.delegate.shallow_resolve_const(ct)
}

pub(super) fn eager_resolve_region(&self, r: I::Region) -> I::Region {
if let ty::ReVar(vid) = r.kind() {
self.delegate.opportunistic_resolve_lt_var(vid)
Expand Down Expand Up @@ -1185,6 +1189,19 @@ where
BoundVarReplacer::replace_bound_vars(&**self.delegate, universes, t).0
}

pub(super) fn replace_escaping_bound_vars<T: TypeFoldable<I>>(
&self,
value: T,
universes: &mut Vec<Option<ty::UniverseIndex>>,
) -> (
T,
IndexMap<ty::PlaceholderRegion<I>, ty::BoundRegion<I>>,
IndexMap<ty::PlaceholderType<I>, ty::BoundTy<I>>,
IndexMap<ty::PlaceholderConst<I>, ty::BoundConst<I>>,
) {
BoundVarReplacer::replace_bound_vars(&**self.delegate, universes, value)
}

pub(super) fn may_use_unstable_feature(
&self,
param_env: I::ParamEnv,
Expand Down
Loading
Loading