mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Auto merge of #155258 - ShoyuVanilla:eq-constraint, r=lcnr
Make region equality emits Eq constraints For context, see.. - The box named *coroutine witness Send lifetime requirements now considered by leakcheck* from [this roadmap](https://raw.githubusercontent.com/hexcatnl/roadmap/6f23e638f65249ef02af86a5454275103a71552d/next-solver.svg) - [#t-types/trait-system-refactor > A question on #251 @ 💬](https://rust-lang.zulipchat.com/#narrow/channel/364551-t-types.2Ftrait-system-refactor/topic/A.20question.20on.20.23251/near/584166935) - Comments on `rustc_type_ir::predicate::RegionEqPredicate`
This commit is contained in:
@@ -451,7 +451,7 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>(
|
||||
(RePlaceholder(a_p), RePlaceholder(b_p)) => a_p.bound == b_p.bound,
|
||||
_ => a_region == b_region,
|
||||
};
|
||||
let mut check = |c: &Constraint<'tcx>, cause: &SubregionOrigin<'tcx>, exact| match c.kind {
|
||||
let mut check = |c: Constraint<'tcx>, cause: &SubregionOrigin<'tcx>, exact| match c.kind {
|
||||
ConstraintKind::RegSubReg
|
||||
if ((exact && c.sup == placeholder_region)
|
||||
|| (!exact && regions_the_same(c.sup, placeholder_region)))
|
||||
@@ -467,13 +467,23 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>(
|
||||
{
|
||||
Some((c.sub, cause.clone()))
|
||||
}
|
||||
_ => None,
|
||||
ConstraintKind::VarSubVar
|
||||
| ConstraintKind::RegSubVar
|
||||
| ConstraintKind::VarSubReg
|
||||
| ConstraintKind::RegSubReg => None,
|
||||
|
||||
ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
|
||||
let mut find_culprit = |exact_match: bool| {
|
||||
region_constraints
|
||||
.constraints
|
||||
.iter()
|
||||
.flat_map(|(constraint, cause)| {
|
||||
constraint.iter_outlives().map(move |constraint| (constraint, cause))
|
||||
})
|
||||
.find_map(|(constraint, cause)| check(constraint, cause, exact_match))
|
||||
};
|
||||
|
||||
|
||||
@@ -70,12 +70,14 @@ pub(crate) fn new(
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) {
|
||||
let QueryRegionConstraints { outlives, assumptions } = query_constraints;
|
||||
let QueryRegionConstraints { constraints, assumptions } = query_constraints;
|
||||
let assumptions =
|
||||
elaborate::elaborate_outlives_assumptions(self.infcx.tcx, assumptions.iter().copied());
|
||||
|
||||
for &(predicate, constraint_category) in outlives {
|
||||
self.convert(predicate, constraint_category, &assumptions);
|
||||
for &(constraint, constraint_category) in constraints {
|
||||
constraint.iter_outlives().for_each(|predicate| {
|
||||
self.convert(predicate, constraint_category, &assumptions);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,8 +294,12 @@ fn normalize_and_add_type_outlives_constraints(
|
||||
) {
|
||||
Ok(TypeOpOutput { output: ty, constraints, .. }) => {
|
||||
// FIXME(higher_ranked_auto): What should we do with the assumptions here?
|
||||
if let Some(QueryRegionConstraints { outlives, assumptions: _ }) = constraints {
|
||||
next_outlives_predicates.extend(outlives.iter().copied());
|
||||
if let Some(QueryRegionConstraints { constraints, assumptions: _ }) = constraints {
|
||||
next_outlives_predicates.extend(constraints.iter().flat_map(
|
||||
|(constraint, category)| {
|
||||
constraint.iter_outlives().map(|outlives| (outlives, *category))
|
||||
},
|
||||
));
|
||||
}
|
||||
ty
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
Canonical, CanonicalQueryResponse, CanonicalVarValues, Certainty, OriginalQueryValues,
|
||||
QueryRegionConstraints, QueryResponse,
|
||||
};
|
||||
use crate::infer::region_constraints::RegionConstraintData;
|
||||
use crate::infer::region_constraints::{ConstraintKind, RegionConstraintData};
|
||||
use crate::infer::{
|
||||
DefineOpaqueTypes, InferCtxt, InferOk, InferResult, OpaqueTypeStorageEntries, SubregionOrigin,
|
||||
TypeOutlivesConstraint,
|
||||
@@ -188,9 +188,16 @@ pub fn instantiate_query_response_and_region_obligations<R>(
|
||||
let InferOk { value: result_args, obligations } =
|
||||
self.query_response_instantiation(cause, param_env, original_values, query_response)?;
|
||||
|
||||
for (predicate, _category) in &query_response.value.region_constraints.outlives {
|
||||
let predicate = instantiate_value(self.tcx, &result_args, *predicate);
|
||||
self.register_outlives_constraint(predicate, cause);
|
||||
for (constraint, _category) in &query_response.value.region_constraints.constraints {
|
||||
let constraint = instantiate_value(self.tcx, &result_args, *constraint);
|
||||
match constraint {
|
||||
ty::RegionConstraint::Outlives(predicate) => {
|
||||
self.register_outlives_constraint(predicate, cause);
|
||||
}
|
||||
ty::RegionConstraint::Eq(predicate) => {
|
||||
self.register_region_eq_constraint(predicate, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for assumption in &query_response.value.region_constraints.assumptions {
|
||||
@@ -277,14 +284,11 @@ pub fn instantiate_nll_query_response_and_region_obligations<R>(
|
||||
}
|
||||
|
||||
(GenericArgKind::Lifetime(v_o), GenericArgKind::Lifetime(v_r)) => {
|
||||
// To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`.
|
||||
if v_o != v_r {
|
||||
output_query_region_constraints
|
||||
.outlives
|
||||
.push((ty::OutlivesPredicate(v_o.into(), v_r), constraint_category));
|
||||
output_query_region_constraints
|
||||
.outlives
|
||||
.push((ty::OutlivesPredicate(v_r.into(), v_o), constraint_category));
|
||||
output_query_region_constraints.constraints.push((
|
||||
ty::RegionEqPredicate(v_o.into(), v_r).into(),
|
||||
constraint_category,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,13 +315,12 @@ pub fn instantiate_nll_query_response_and_region_obligations<R>(
|
||||
}
|
||||
|
||||
// ...also include the other query region constraints from the query.
|
||||
output_query_region_constraints.outlives.extend(
|
||||
query_response.value.region_constraints.outlives.iter().filter_map(|&r_c| {
|
||||
output_query_region_constraints.constraints.extend(
|
||||
query_response.value.region_constraints.constraints.iter().filter_map(|&r_c| {
|
||||
let r_c = instantiate_value(self.tcx, &result_args, r_c);
|
||||
|
||||
// Screen out `'a: 'a` cases.
|
||||
let ty::OutlivesPredicate(k1, r2) = r_c.0;
|
||||
if k1 != r2.into() { Some(r_c) } else { None }
|
||||
// Screen out `'a: 'a` or `'a == 'a` cases.
|
||||
if r_c.0.is_trivial() { None } else { Some(r_c) }
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -611,20 +614,30 @@ pub fn make_query_region_constraints<'tcx>(
|
||||
|
||||
debug!(?constraints);
|
||||
|
||||
let outlives: Vec<_> = constraints
|
||||
let constraints: Vec<_> = constraints
|
||||
.iter()
|
||||
.map(|(c, origin)| {
|
||||
// Swap regions because we are going from sub (<=) to outlives (>=).
|
||||
let constraint = ty::OutlivesPredicate(c.sup.into(), c.sub);
|
||||
(constraint, origin.to_constraint_category())
|
||||
.map(|(c, origin)| match c.kind {
|
||||
ConstraintKind::VarSubVar
|
||||
| ConstraintKind::RegSubVar
|
||||
| ConstraintKind::VarSubReg
|
||||
| ConstraintKind::RegSubReg => {
|
||||
// Swap regions because we are going from sub (<=) to outlives (>=).
|
||||
let constraint = ty::OutlivesPredicate(c.sup.into(), c.sub).into();
|
||||
(constraint, origin.to_constraint_category())
|
||||
}
|
||||
|
||||
ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => {
|
||||
let constraint = ty::RegionEqPredicate(c.sup, c.sub).into();
|
||||
(constraint, origin.to_constraint_category())
|
||||
}
|
||||
})
|
||||
.chain(outlives_obligations.into_iter().map(|obl| {
|
||||
(
|
||||
ty::OutlivesPredicate(obl.sup_type.into(), obl.sub_region),
|
||||
ty::OutlivesPredicate(obl.sup_type.into(), obl.sub_region).into(),
|
||||
obl.origin.to_constraint_category(),
|
||||
)
|
||||
}))
|
||||
.collect();
|
||||
|
||||
QueryRegionConstraints { outlives, assumptions }
|
||||
QueryRegionConstraints { constraints, assumptions }
|
||||
}
|
||||
|
||||
@@ -13,13 +13,13 @@ pub(super) enum EdgeDirection {
|
||||
|
||||
/// Type alias for the pairs stored in [`RegionConstraintData::constraints`],
|
||||
/// which we are indexing.
|
||||
type ConstraintPair<'tcx> = (Constraint<'tcx>, SubregionOrigin<'tcx>);
|
||||
type ConstraintPair<'data, 'tcx> = (Constraint<'tcx>, &'data SubregionOrigin<'tcx>);
|
||||
|
||||
/// An index from region variables to their corresponding constraint edges,
|
||||
/// used on some error paths.
|
||||
pub(super) struct IndexedConstraintEdges<'data, 'tcx> {
|
||||
out_edges: IndexVec<RegionVid, Vec<&'data ConstraintPair<'tcx>>>,
|
||||
in_edges: IndexVec<RegionVid, Vec<&'data ConstraintPair<'tcx>>>,
|
||||
out_edges: IndexVec<RegionVid, Vec<ConstraintPair<'data, 'tcx>>>,
|
||||
in_edges: IndexVec<RegionVid, Vec<ConstraintPair<'data, 'tcx>>>,
|
||||
}
|
||||
|
||||
impl<'data, 'tcx> IndexedConstraintEdges<'data, 'tcx> {
|
||||
@@ -27,25 +27,46 @@ pub(super) fn build_index(num_vars: usize, data: &'data RegionConstraintData<'tc
|
||||
let mut out_edges = IndexVec::from_fn_n(|_| vec![], num_vars);
|
||||
let mut in_edges = IndexVec::from_fn_n(|_| vec![], num_vars);
|
||||
|
||||
for pair @ (c, _) in &data.constraints {
|
||||
for pair @ (c, _) in data
|
||||
.constraints
|
||||
.iter()
|
||||
.flat_map(|(c, origin)| c.iter_outlives().map(move |c| (c, origin)))
|
||||
{
|
||||
// Only push a var out-edge for `VarSub...` constraints.
|
||||
match c.kind {
|
||||
ConstraintKind::VarSubVar | ConstraintKind::VarSubReg => {
|
||||
out_edges[c.sub.as_var()].push(pair)
|
||||
out_edges[c.sub.as_var()].push(pair);
|
||||
}
|
||||
|
||||
ConstraintKind::RegSubVar | ConstraintKind::RegSubReg => {}
|
||||
|
||||
ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: We should merge this loop with the above one eventually.
|
||||
// Index in-edges in reverse order, to match what current tests expect.
|
||||
// (It's unclear whether this is important or not.)
|
||||
for pair @ (c, _) in data.constraints.iter().rev() {
|
||||
|
||||
for pair @ (c, _) in data
|
||||
.constraints
|
||||
.iter()
|
||||
.rev()
|
||||
.flat_map(|(c, origin)| c.iter_outlives().map(move |c| (c, origin)))
|
||||
{
|
||||
// Only push a var in-edge for `...SubVar` constraints.
|
||||
match c.kind {
|
||||
ConstraintKind::VarSubVar | ConstraintKind::RegSubVar => {
|
||||
in_edges[c.sup.as_var()].push(pair)
|
||||
in_edges[c.sup.as_var()].push(pair);
|
||||
}
|
||||
|
||||
ConstraintKind::VarSubReg | ConstraintKind::RegSubReg => {}
|
||||
|
||||
ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +79,7 @@ pub(super) fn adjacent_edges(
|
||||
&self,
|
||||
region_vid: RegionVid,
|
||||
dir: EdgeDirection,
|
||||
) -> &[&'data ConstraintPair<'tcx>] {
|
||||
) -> &[ConstraintPair<'data, 'tcx>] {
|
||||
let edges = match dir {
|
||||
EdgeDirection::Out => &self.out_edges,
|
||||
EdgeDirection::In => &self.in_edges,
|
||||
|
||||
@@ -34,6 +34,18 @@ pub(crate) fn resolve<'tcx>(
|
||||
var_infos: VarInfos<'tcx>,
|
||||
data: RegionConstraintData<'tcx>,
|
||||
) -> (LexicalRegionResolutions<'tcx>, Vec<RegionResolutionError<'tcx>>) {
|
||||
assert!(
|
||||
data.constraints.iter().all(|(c, _)| match c.kind {
|
||||
ConstraintKind::VarSubVar
|
||||
| ConstraintKind::RegSubVar
|
||||
| ConstraintKind::VarSubReg
|
||||
| ConstraintKind::RegSubReg => true,
|
||||
|
||||
ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => false,
|
||||
}),
|
||||
"Every constraint should be decomposed into outlives here"
|
||||
);
|
||||
|
||||
let mut errors = vec![];
|
||||
let mut resolver = LexicalResolver { region_rels, var_infos, data };
|
||||
let values = resolver.infer_variable_values(&mut errors);
|
||||
@@ -279,6 +291,10 @@ fn expansion(&self, var_values: &mut LexicalRegionResolutions<'tcx>) {
|
||||
// is done, in `collect_errors`.
|
||||
continue;
|
||||
}
|
||||
|
||||
ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,6 +591,10 @@ fn collect_errors(
|
||||
*sub_data = VarValue::ErrorValue;
|
||||
}
|
||||
}
|
||||
|
||||
ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -852,12 +872,12 @@ fn process_edges<'tcx>(
|
||||
}
|
||||
|
||||
ConstraintKind::RegSubVar => {
|
||||
let origin = origin.clone();
|
||||
let origin = (*origin).clone();
|
||||
state.result.push(RegionAndOrigin { region: c.sub, origin });
|
||||
}
|
||||
|
||||
ConstraintKind::VarSubReg => {
|
||||
let origin = origin.clone();
|
||||
let origin = (*origin).clone();
|
||||
state.result.push(RegionAndOrigin { region: c.sup, origin });
|
||||
}
|
||||
|
||||
@@ -865,6 +885,10 @@ fn process_edges<'tcx>(
|
||||
"cannot reach reg-sub-reg edge in region inference \
|
||||
post-processing"
|
||||
),
|
||||
|
||||
ConstraintKind::VarEqVar
|
||||
| ConstraintKind::VarEqReg
|
||||
| ConstraintKind::RegEqReg => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -702,6 +702,16 @@ pub fn sub_regions(
|
||||
self.inner.borrow_mut().unwrap_region_constraints().make_subregion(origin, a, b);
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub fn equate_regions(
|
||||
&self,
|
||||
origin: SubregionOrigin<'tcx>,
|
||||
a: ty::Region<'tcx>,
|
||||
b: ty::Region<'tcx>,
|
||||
) {
|
||||
self.inner.borrow_mut().unwrap_region_constraints().make_eqregion(origin, a, b);
|
||||
}
|
||||
|
||||
/// Processes a `Coerce` predicate from the fulfillment context.
|
||||
/// This is NOT the preferred way to handle coercion, which is to
|
||||
/// invoke `FnCtxt::coerce` or a similar method (see `coercion.rs`).
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//! Various code related to computing outlives relations.
|
||||
|
||||
use std::iter;
|
||||
|
||||
use rustc_data_structures::undo_log::UndoLogs;
|
||||
use rustc_middle::traits::query::{NoSolution, OutlivesBound};
|
||||
use rustc_middle::ty;
|
||||
@@ -67,6 +69,15 @@ pub fn resolve_regions_with_normalize(
|
||||
inner.region_constraint_storage.take().expect("regions already resolved")
|
||||
};
|
||||
|
||||
storage.data.constraints = storage
|
||||
.data
|
||||
.constraints
|
||||
.iter()
|
||||
.flat_map(|(constraint, origin)| {
|
||||
constraint.iter_outlives().zip(iter::repeat_with(|| origin.clone()))
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Filter out any region-region outlives assumptions that are implied by
|
||||
// coroutine well-formedness.
|
||||
if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions {
|
||||
@@ -74,7 +85,14 @@ pub fn resolve_regions_with_normalize(
|
||||
ConstraintKind::RegSubReg => !outlives_env
|
||||
.higher_ranked_assumptions()
|
||||
.contains(&ty::OutlivesPredicate(c.sup.into(), c.sub)),
|
||||
_ => true,
|
||||
|
||||
ConstraintKind::VarSubVar
|
||||
| ConstraintKind::RegSubVar
|
||||
| ConstraintKind::VarSubReg => true,
|
||||
|
||||
ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => {
|
||||
unreachable!();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,17 @@ pub fn register_outlives_constraint(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_region_eq_constraint(
|
||||
&self,
|
||||
ty::RegionEqPredicate(r_a, r_b): ty::RegionEqPredicate<'tcx>,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
) {
|
||||
let origin = SubregionOrigin::from_obligation_cause(cause, || {
|
||||
SubregionOrigin::RelateRegionParamBound(cause.span, None)
|
||||
});
|
||||
self.equate_regions(origin, r_a, r_b);
|
||||
}
|
||||
|
||||
pub fn register_region_outlives_constraint(
|
||||
&self,
|
||||
ty::OutlivesPredicate(r_a, r_b): ty::RegionOutlivesPredicate<'tcx>,
|
||||
|
||||
@@ -392,8 +392,10 @@ fn iterate_region_constraints(
|
||||
{
|
||||
match undo_entry {
|
||||
&AddConstraint(i) => {
|
||||
let c = region_constraints.data().constraints[i].0;
|
||||
each_edge(c.sub, c.sup);
|
||||
region_constraints.data().constraints[i]
|
||||
.0
|
||||
.iter_outlives()
|
||||
.for_each(|c| each_edge(c.sub, c.sup));
|
||||
}
|
||||
&AddVerify(i) => span_bug!(
|
||||
region_constraints.data().verifys[i].origin.span(),
|
||||
@@ -403,7 +405,12 @@ fn iterate_region_constraints(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
region_constraints.data().constraints.iter().for_each(|(c, _)| each_edge(c.sub, c.sup))
|
||||
region_constraints
|
||||
.data()
|
||||
.constraints
|
||||
.iter()
|
||||
.flat_map(|(c, _)| c.iter_outlives())
|
||||
.for_each(|c| each_edge(c.sub, c.sup))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! See `README.md`.
|
||||
|
||||
use std::ops::Range;
|
||||
use std::{cmp, fmt, mem};
|
||||
use std::{cmp, fmt, iter, mem};
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::undo_log::UndoLogs;
|
||||
@@ -96,6 +96,19 @@ pub enum ConstraintKind {
|
||||
/// directly affect inference, but instead is checked after
|
||||
/// inference is complete.
|
||||
RegSubReg,
|
||||
|
||||
/// A region variable is equal to another.
|
||||
VarEqVar,
|
||||
|
||||
/// A region variable is equal to a concrete region. This does not
|
||||
/// directly affect inference, but instead is checked after
|
||||
/// inference is complete.
|
||||
VarEqReg,
|
||||
|
||||
/// An equality constraint where neither side is a variable. This does not
|
||||
/// directly affect inference, but instead is checked after
|
||||
/// inference is complete.
|
||||
RegEqReg,
|
||||
}
|
||||
|
||||
/// Represents a constraint that influences the inference process.
|
||||
@@ -112,6 +125,30 @@ impl Constraint<'_> {
|
||||
pub fn involves_placeholders(&self) -> bool {
|
||||
self.sub.is_placeholder() || self.sup.is_placeholder()
|
||||
}
|
||||
|
||||
pub fn iter_outlives(self) -> impl Iterator<Item = Self> {
|
||||
let Constraint { kind, sub, sup } = self;
|
||||
|
||||
match kind {
|
||||
ConstraintKind::VarSubVar
|
||||
| ConstraintKind::RegSubVar
|
||||
| ConstraintKind::VarSubReg
|
||||
| ConstraintKind::RegSubReg => iter::once(self).chain(None),
|
||||
|
||||
ConstraintKind::VarEqVar => {
|
||||
iter::once(Constraint { kind: ConstraintKind::VarSubVar, sub, sup })
|
||||
.chain(Some(Constraint { kind: ConstraintKind::VarSubVar, sub: sup, sup: sub }))
|
||||
}
|
||||
ConstraintKind::VarEqReg => {
|
||||
iter::once(Constraint { kind: ConstraintKind::VarSubReg, sub, sup })
|
||||
.chain(Some(Constraint { kind: ConstraintKind::RegSubVar, sub: sup, sup: sub }))
|
||||
}
|
||||
ConstraintKind::RegEqReg => {
|
||||
iter::once(Constraint { kind: ConstraintKind::RegSubReg, sub, sup })
|
||||
.chain(Some(Constraint { kind: ConstraintKind::RegSubReg, sub: sup, sup: sub }))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -422,39 +459,57 @@ pub(super) fn make_eqregion(
|
||||
b: Region<'tcx>,
|
||||
) {
|
||||
if a != b {
|
||||
// Eventually, it would be nice to add direct support for
|
||||
// equating regions.
|
||||
self.make_subregion(origin.clone(), a, b);
|
||||
self.make_subregion(origin, b, a);
|
||||
|
||||
match (a.kind(), b.kind()) {
|
||||
(ty::ReVar(a), ty::ReVar(b)) => {
|
||||
debug!("make_eqregion: unifying {:?} with {:?}", a, b);
|
||||
if self.unification_table_mut().unify_var_var(a, b).is_ok() {
|
||||
// FIXME: We could only emit constraints if `unify_var_{var, value}` fails when
|
||||
// equating region vars.
|
||||
match (a.kind(), b.kind(), a, b) {
|
||||
(ReBound(..), _, _, _) | (_, ReBound(..), _, _) => {
|
||||
span_bug!(origin.span(), "cannot relate bound region: {:?} == {:?}", a, b);
|
||||
}
|
||||
(ReVar(a_vid), ReVar(b_vid), _, _) => {
|
||||
self.add_constraint(
|
||||
Constraint { kind: ConstraintKind::VarEqVar, sub: a, sup: b },
|
||||
origin,
|
||||
);
|
||||
debug!("make_eqregion: unifying {:?} with {:?}", a_vid, b_vid);
|
||||
if self.unification_table_mut().unify_var_var(a_vid, b_vid).is_ok() {
|
||||
self.storage.any_unifications = true;
|
||||
}
|
||||
}
|
||||
(ty::ReVar(vid), _) => {
|
||||
debug!("make_eqregion: unifying {:?} with {:?}", vid, b);
|
||||
(ReVar(vid), _, var, reg) | (_, ReVar(vid), reg, var) => {
|
||||
if reg.is_static() {
|
||||
// all regions are subregions of static, so don't go bidirectional here
|
||||
self.add_constraint(
|
||||
Constraint { kind: ConstraintKind::RegSubVar, sub: reg, sup: var },
|
||||
origin,
|
||||
);
|
||||
} else {
|
||||
self.add_constraint(
|
||||
Constraint { kind: ConstraintKind::VarEqReg, sub: var, sup: reg },
|
||||
origin,
|
||||
);
|
||||
}
|
||||
debug!("make_eqregion: unifying {:?} with {:?}", vid, reg);
|
||||
if self
|
||||
.unification_table_mut()
|
||||
.unify_var_value(vid, RegionVariableValue::Known { value: b })
|
||||
.unify_var_value(vid, RegionVariableValue::Known { value: reg })
|
||||
.is_ok()
|
||||
{
|
||||
self.storage.any_unifications = true;
|
||||
};
|
||||
}
|
||||
(_, ty::ReVar(vid)) => {
|
||||
debug!("make_eqregion: unifying {:?} with {:?}", a, vid);
|
||||
if self
|
||||
.unification_table_mut()
|
||||
.unify_var_value(vid, RegionVariableValue::Known { value: a })
|
||||
.is_ok()
|
||||
{
|
||||
self.storage.any_unifications = true;
|
||||
};
|
||||
(ReStatic, _, st, reg) | (_, ReStatic, reg, st) => {
|
||||
// all regions are subregions of static, so don't go bidirectional here
|
||||
self.add_constraint(
|
||||
Constraint { kind: ConstraintKind::RegSubReg, sub: st, sup: reg },
|
||||
origin,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
self.add_constraint(
|
||||
Constraint { kind: ConstraintKind::RegEqReg, sub: a, sup: b },
|
||||
origin,
|
||||
);
|
||||
}
|
||||
(_, _) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ pub struct QueryResponse<'tcx, R> {
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
|
||||
#[derive(HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct QueryRegionConstraints<'tcx> {
|
||||
pub outlives: Vec<QueryOutlivesConstraint<'tcx>>,
|
||||
pub constraints: Vec<QueryRegionConstraint<'tcx>>,
|
||||
pub assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
|
||||
}
|
||||
|
||||
@@ -91,7 +91,8 @@ impl QueryRegionConstraints<'_> {
|
||||
/// discharge a requirement from another query, which is a potential problem if we did throw
|
||||
/// away these assumptions because there were no constraints.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.outlives.is_empty() && self.assumptions.is_empty()
|
||||
let QueryRegionConstraints { constraints, assumptions } = self;
|
||||
constraints.is_empty() && assumptions.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +135,7 @@ pub fn is_proven(&self) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub type QueryOutlivesConstraint<'tcx> = (ty::ArgOutlivesPredicate<'tcx>, ConstraintCategory<'tcx>);
|
||||
pub type QueryRegionConstraint<'tcx> = (ty::RegionConstraint<'tcx>, ConstraintCategory<'tcx>);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CanonicalParamEnvCache<'tcx> {
|
||||
|
||||
@@ -97,7 +97,8 @@
|
||||
PolyExistentialPredicate, PolyExistentialProjection, PolyExistentialTraitRef,
|
||||
PolyProjectionPredicate, PolyRegionOutlivesPredicate, PolySubtypePredicate, PolyTraitPredicate,
|
||||
PolyTraitRef, PolyTypeOutlivesPredicate, Predicate, PredicateKind, ProjectionPredicate,
|
||||
RegionOutlivesPredicate, SubtypePredicate, TraitPredicate, TraitRef, TypeOutlivesPredicate,
|
||||
RegionConstraint, RegionEqPredicate, RegionOutlivesPredicate, SubtypePredicate, TraitPredicate,
|
||||
TraitRef, TypeOutlivesPredicate,
|
||||
};
|
||||
pub use self::region::{
|
||||
EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region, RegionKind, RegionVid,
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, ty::Region<'tcx>>;
|
||||
pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, Ty<'tcx>>;
|
||||
pub type ArgOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>;
|
||||
pub type RegionEqPredicate<'tcx> = ir::RegionEqPredicate<TyCtxt<'tcx>>;
|
||||
pub type RegionConstraint<'tcx> = ir::RegionConstraint<TyCtxt<'tcx>>;
|
||||
pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>;
|
||||
pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>;
|
||||
pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>;
|
||||
|
||||
@@ -250,17 +250,22 @@ fn unify_query_var_values<D, I>(
|
||||
|
||||
fn register_region_constraints<D, I>(
|
||||
delegate: &D,
|
||||
outlives: &[ty::OutlivesPredicate<I, I::GenericArg>],
|
||||
constraints: &[ty::RegionConstraint<I>],
|
||||
span: I::Span,
|
||||
) where
|
||||
D: SolverDelegate<Interner = I>,
|
||||
I: Interner,
|
||||
{
|
||||
for &ty::OutlivesPredicate(lhs, rhs) in outlives {
|
||||
match lhs.kind() {
|
||||
ty::GenericArgKind::Lifetime(lhs) => delegate.sub_regions(rhs, lhs, span),
|
||||
ty::GenericArgKind::Type(lhs) => delegate.register_ty_outlives(lhs, rhs, span),
|
||||
ty::GenericArgKind::Const(_) => panic!("const outlives: {lhs:?}: {rhs:?}"),
|
||||
for &constraint in constraints {
|
||||
match constraint {
|
||||
ty::RegionConstraint::Outlives(ty::OutlivesPredicate(lhs, rhs)) => match lhs.kind() {
|
||||
ty::GenericArgKind::Lifetime(lhs) => delegate.sub_regions(rhs, lhs, span),
|
||||
ty::GenericArgKind::Type(lhs) => delegate.register_ty_outlives(lhs, rhs, span),
|
||||
ty::GenericArgKind::Const(_) => panic!("const outlives: {lhs:?}: {rhs:?}"),
|
||||
},
|
||||
ty::RegionConstraint::Eq(ty::RegionEqPredicate(lhs, rhs)) => {
|
||||
delegate.equate_regions(lhs, rhs, span)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,9 +45,7 @@ fn well_formed_goals(
|
||||
term: <Self::Interner as Interner>::Term,
|
||||
) -> Option<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>>;
|
||||
|
||||
fn make_deduplicated_outlives_constraints(
|
||||
&self,
|
||||
) -> Vec<ty::OutlivesPredicate<Self::Interner, <Self::Interner as Interner>::GenericArg>>;
|
||||
fn make_deduplicated_region_constraints(&self) -> Vec<ty::RegionConstraint<Self::Interner>>;
|
||||
|
||||
fn instantiate_canonical<V>(
|
||||
&self,
|
||||
|
||||
@@ -1294,9 +1294,9 @@ pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response(
|
||||
|
||||
// Remove any trivial or duplicated region constraints once we've resolved regions
|
||||
let mut unique = HashSet::default();
|
||||
external_constraints.region_constraints.retain(|outlives| {
|
||||
outlives.0.as_region().is_none_or(|re| re != outlives.1) && unique.insert(*outlives)
|
||||
});
|
||||
external_constraints
|
||||
.region_constraints
|
||||
.retain(|outlives| !outlives.is_trivial() && unique.insert(*outlives));
|
||||
|
||||
let canonical = canonicalize_response(
|
||||
self.delegate,
|
||||
@@ -1350,7 +1350,7 @@ fn compute_external_query_constraints(
|
||||
// `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and
|
||||
// `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`.
|
||||
let region_constraints = if certainty == Certainty::Yes {
|
||||
self.delegate.make_deduplicated_outlives_constraints()
|
||||
self.delegate.make_deduplicated_region_constraints()
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
@@ -204,7 +204,7 @@ fn well_formed_goals(
|
||||
.map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect())
|
||||
}
|
||||
|
||||
fn make_deduplicated_outlives_constraints(&self) -> Vec<ty::ArgOutlivesPredicate<'tcx>> {
|
||||
fn make_deduplicated_region_constraints(&self) -> Vec<ty::RegionConstraint<'tcx>> {
|
||||
// Cannot use `take_registered_region_obligations` as we may compute the response
|
||||
// inside of a `probe` whenever we have multiple choices inside of the solver.
|
||||
let region_obligations = self.0.inner.borrow().region_obligations().to_owned();
|
||||
@@ -219,7 +219,7 @@ fn make_deduplicated_outlives_constraints(&self) -> Vec<ty::ArgOutlivesPredicate
|
||||
|
||||
let mut seen = FxHashSet::default();
|
||||
region_constraints
|
||||
.outlives
|
||||
.constraints
|
||||
.into_iter()
|
||||
.filter(|&(outlives, _)| seen.insert(outlives))
|
||||
.map(|(outlives, _)| outlives)
|
||||
|
||||
@@ -453,7 +453,7 @@ fn map_vid_to_region<'cx>(
|
||||
let mut vid_map = FxIndexMap::<RegionTarget<'cx>, RegionDeps<'cx>>::default();
|
||||
let mut finished_map = FxIndexMap::default();
|
||||
|
||||
for (c, _) in ®ions.constraints {
|
||||
for c in regions.constraints.iter().flat_map(|(c, _)| c.iter_outlives()) {
|
||||
match c.kind {
|
||||
ConstraintKind::VarSubVar => {
|
||||
let sub_vid = c.sub.as_var();
|
||||
@@ -489,6 +489,10 @@ fn map_vid_to_region<'cx>(
|
||||
let deps2 = vid_map.entry(RegionTarget::Region(c.sup)).or_default();
|
||||
deps2.smaller.insert(RegionTarget::Region(c.sub));
|
||||
}
|
||||
|
||||
ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,10 +81,17 @@ fn implied_outlives_bounds<'a, 'tcx>(
|
||||
// FIXME(higher_ranked_auto): Should we register assumptions here?
|
||||
// We otherwise would get spurious errors if normalizing an implied
|
||||
// outlives bound required proving some higher-ranked coroutine obl.
|
||||
let QueryRegionConstraints { outlives, assumptions: _ } = constraints;
|
||||
let QueryRegionConstraints { constraints, assumptions: _ } = constraints;
|
||||
let cause = ObligationCause::misc(span, body_id);
|
||||
for &(predicate, _) in &outlives {
|
||||
infcx.register_outlives_constraint(predicate, &cause);
|
||||
for &(constraint, _) in &constraints {
|
||||
match constraint {
|
||||
ty::RegionConstraint::Outlives(predicate) => {
|
||||
infcx.register_outlives_constraint(predicate, &cause)
|
||||
}
|
||||
ty::RegionConstraint::Eq(predicate) => {
|
||||
infcx.register_region_eq_constraint(predicate, &cause)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -180,8 +180,8 @@ fn fully_perform(
|
||||
Ok(output)
|
||||
})?;
|
||||
output.error_info = error_info;
|
||||
if let Some(QueryRegionConstraints { outlives, assumptions }) = output.constraints {
|
||||
region_constraints.outlives.extend(outlives.iter().cloned());
|
||||
if let Some(QueryRegionConstraints { constraints, assumptions }) = output.constraints {
|
||||
region_constraints.constraints.extend(constraints.iter().cloned());
|
||||
region_constraints.assumptions.extend(assumptions.iter().cloned());
|
||||
}
|
||||
output.constraints = if region_constraints.is_empty() {
|
||||
|
||||
@@ -69,18 +69,18 @@ fn compute_assumptions<'tcx>(
|
||||
let region_assumptions = infcx.take_registered_region_assumptions();
|
||||
let region_constraints = infcx.take_and_reset_region_constraints();
|
||||
|
||||
let outlives = make_query_region_constraints(
|
||||
let constraints = make_query_region_constraints(
|
||||
region_obligations,
|
||||
®ion_constraints,
|
||||
region_assumptions,
|
||||
)
|
||||
.outlives
|
||||
.constraints
|
||||
.fold_with(&mut OpportunisticRegionResolver::new(&infcx));
|
||||
|
||||
tcx.mk_outlives_from_iter(
|
||||
outlives
|
||||
constraints
|
||||
.into_iter()
|
||||
.map(|(o, _)| o)
|
||||
.flat_map(|(constraint, _)| constraint.iter_outlives())
|
||||
// FIXME(higher_ranked_auto): We probably should deeply resolve these before
|
||||
// filtering out infers which only correspond to unconstrained infer regions
|
||||
// which we can sometimes get.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use std::{fmt, iter};
|
||||
|
||||
use derive_where::derive_where;
|
||||
#[cfg(feature = "nightly")]
|
||||
@@ -42,6 +42,72 @@ fn lift_to_interner(self, cx: U) -> Option<Self::Lifted> {
|
||||
}
|
||||
}
|
||||
|
||||
/// `'a == 'b`.
|
||||
/// For the rationale behind having this instead of a pair of bidirectional
|
||||
/// `'a: 'b` and `'b: 'a`, see
|
||||
/// [this discusstion on Zulip](https://rust-lang.zulipchat.com/#narrow/channel/364551-t-types.2Ftrait-system-refactor/topic/A.20question.20on.20.23251/near/584167074).
|
||||
#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)]
|
||||
#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)]
|
||||
#[cfg_attr(
|
||||
feature = "nightly",
|
||||
derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext)
|
||||
)]
|
||||
pub struct RegionEqPredicate<I: Interner>(pub I::Region, pub I::Region);
|
||||
|
||||
impl<I: Interner> RegionEqPredicate<I> {
|
||||
/// Decompose `'a == 'b` into `['a: 'b, 'b: 'a]`
|
||||
pub fn into_bidirectional_outlives(self) -> [OutlivesPredicate<I, I::GenericArg>; 2] {
|
||||
[OutlivesPredicate(self.0.into(), self.1), OutlivesPredicate(self.1.into(), self.0)]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)]
|
||||
#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)]
|
||||
#[cfg_attr(
|
||||
feature = "nightly",
|
||||
derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext)
|
||||
)]
|
||||
pub enum RegionConstraint<I: Interner> {
|
||||
Outlives(OutlivesPredicate<I, I::GenericArg>),
|
||||
Eq(RegionEqPredicate<I>),
|
||||
}
|
||||
|
||||
impl<I: Interner> From<OutlivesPredicate<I, I::GenericArg>> for RegionConstraint<I> {
|
||||
fn from(value: OutlivesPredicate<I, I::GenericArg>) -> Self {
|
||||
RegionConstraint::Outlives(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner> From<RegionEqPredicate<I>> for RegionConstraint<I> {
|
||||
fn from(value: RegionEqPredicate<I>) -> Self {
|
||||
RegionConstraint::Eq(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner> RegionConstraint<I> {
|
||||
/// Whether the given constraint is either `'a: 'a` or `'a == 'a`.
|
||||
pub fn is_trivial(self) -> bool {
|
||||
match self {
|
||||
RegionConstraint::Outlives(outlives) => {
|
||||
outlives.0.as_region().is_some_and(|re| re == outlives.1)
|
||||
}
|
||||
RegionConstraint::Eq(eq) => eq.0 == eq.1,
|
||||
}
|
||||
}
|
||||
|
||||
/// If `self` is an eq constraint, iterate through its decomposed bidirectional outlives
|
||||
/// bounds and if not, just iterate once for the outlives bound itself.
|
||||
pub fn iter_outlives(self) -> impl Iterator<Item = OutlivesPredicate<I, I::GenericArg>> {
|
||||
match self {
|
||||
RegionConstraint::Outlives(outlives) => iter::once(outlives).chain(None),
|
||||
RegionConstraint::Eq(eq) => {
|
||||
let [outlives1, outlives2] = eq.into_bidirectional_outlives();
|
||||
iter::once(outlives1).chain(Some(outlives2))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A complete reference to a trait.
|
||||
///
|
||||
/// These take numerous guises in syntax,
|
||||
|
||||
@@ -253,7 +253,7 @@ impl<I: Interner> Eq for Response<I> {}
|
||||
#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)]
|
||||
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
|
||||
pub struct ExternalConstraintsData<I: Interner> {
|
||||
pub region_constraints: Vec<ty::OutlivesPredicate<I, I::GenericArg>>,
|
||||
pub region_constraints: Vec<ty::RegionConstraint<I>>,
|
||||
pub opaque_types: Vec<(ty::OpaqueTypeKey<I>, I::Ty)>,
|
||||
pub normalization_nested_goals: NestedNormalizationGoals<I>,
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ fn clean_region_outlives_constraints<'tcx>(
|
||||
// Each `RegionTarget` (a `RegionVid` or a `Region`) maps to its smaller and larger regions.
|
||||
// Note that "larger" regions correspond to sub regions in the surface language.
|
||||
// E.g., in `'a: 'b`, `'a` is the larger region.
|
||||
for (c, _) in ®ions.constraints {
|
||||
for c in regions.constraints.iter().flat_map(|(c, _)| c.iter_outlives()) {
|
||||
match c.kind {
|
||||
ConstraintKind::VarSubVar => {
|
||||
let sub_vid = c.sub.as_var();
|
||||
@@ -265,6 +265,9 @@ fn clean_region_outlives_constraints<'tcx>(
|
||||
.push(c.sub);
|
||||
}
|
||||
}
|
||||
ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user