-Znext-solver Ignore region constraints from the nested goals in leakcheck

This commit is contained in:
Shoyu Vanilla
2026-04-25 01:52:34 +09:00
parent f53b654a88
commit e43a1b5b44
34 changed files with 509 additions and 154 deletions
@@ -74,7 +74,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
let assumptions =
elaborate::elaborate_outlives_assumptions(self.infcx.tcx, assumptions.iter().copied());
for &(constraint, constraint_category) in constraints {
for &(constraint, constraint_category, _) in constraints {
constraint.iter_outlives().for_each(|predicate| {
self.convert(predicate, constraint_category, &assumptions);
});
@@ -296,7 +296,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
// FIXME(higher_ranked_auto): What should we do with the assumptions here?
if let Some(QueryRegionConstraints { constraints, assumptions: _ }) = constraints {
next_outlives_predicates.extend(constraints.iter().flat_map(
|(constraint, category)| {
|(constraint, category, _)| {
constraint.iter_outlives().map(|outlives| (outlives, *category))
},
));
@@ -728,6 +728,7 @@ fn region_known_to_outlive<'tcx>(
SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None),
region_b,
region_a,
ty::VisibleForLeakCheck::Unreachable,
);
})
}
@@ -496,7 +496,12 @@ pub(crate) fn coerce_unsized_info<'tcx>(
}
(&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
infcx.sub_regions(SubregionOrigin::RelateObjectBound(span), r_b, r_a);
infcx.sub_regions(
SubregionOrigin::RelateObjectBound(span),
r_b,
r_a,
ty::VisibleForLeakCheck::Yes,
);
let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b };
check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ref(tcx, r_b, ty))
@@ -188,14 +188,14 @@ impl<'tcx> InferCtxt<'tcx> {
let InferOk { value: result_args, obligations } =
self.query_response_instantiation(cause, param_env, original_values, query_response)?;
for (constraint, _category) in &query_response.value.region_constraints.constraints {
for (constraint, _category, vis) 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);
self.register_outlives_constraint(predicate, *vis, cause);
}
ty::RegionConstraint::Eq(predicate) => {
self.register_region_eq_constraint(predicate, cause);
self.register_region_eq_constraint(predicate, *vis, cause);
}
}
}
@@ -288,6 +288,7 @@ impl<'tcx> InferCtxt<'tcx> {
output_query_region_constraints.constraints.push((
ty::RegionEqPredicate(v_o.into(), v_r).into(),
constraint_category,
ty::VisibleForLeakCheck::Yes,
));
}
}
@@ -586,6 +587,7 @@ impl<'tcx> InferCtxt<'tcx> {
SubregionOrigin::RelateRegionParamBound(cause.span, None),
v1,
v2,
ty::VisibleForLeakCheck::Yes,
);
}
(GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => {
@@ -623,20 +625,24 @@ pub fn make_query_region_constraints<'tcx>(
| 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())
(constraint, origin.to_constraint_category(), c.visible_for_leak_check)
}
ConstraintKind::VarEqVar | ConstraintKind::VarEqReg | ConstraintKind::RegEqReg => {
let constraint = ty::RegionEqPredicate(c.sup, c.sub).into();
(constraint, origin.to_constraint_category())
(constraint, origin.to_constraint_category(), c.visible_for_leak_check)
}
})
.chain(outlives_obligations.into_iter().map(|obl| {
(
ty::OutlivesPredicate(obl.sup_type.into(), obl.sub_region).into(),
obl.origin.to_constraint_category(),
)
}))
.chain(outlives_obligations.into_iter().map(
|TypeOutlivesConstraint { sub_region, sup_type, origin }| {
(
ty::OutlivesPredicate(sup_type.into(), sub_region).into(),
origin.to_constraint_category(),
// We don't do leak checks for type outlives
ty::VisibleForLeakCheck::Unreachable,
)
},
))
.collect();
QueryRegionConstraints { constraints, assumptions }
+16 -2
View File
@@ -256,19 +256,33 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
self.probe(|_| probe())
}
fn sub_regions(&self, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>, span: Span) {
fn sub_regions(
&self,
sub: ty::Region<'tcx>,
sup: ty::Region<'tcx>,
vis: ty::VisibleForLeakCheck,
span: Span,
) {
self.inner.borrow_mut().unwrap_region_constraints().make_subregion(
SubregionOrigin::RelateRegionParamBound(span, None),
sub,
sup,
vis,
);
}
fn equate_regions(&self, a: ty::Region<'tcx>, b: ty::Region<'tcx>, span: Span) {
fn equate_regions(
&self,
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
vis: ty::VisibleForLeakCheck,
span: Span,
) {
self.inner.borrow_mut().unwrap_region_constraints().make_eqregion(
SubregionOrigin::RelateRegionParamBound(span, None),
a,
b,
vis,
);
}
@@ -149,7 +149,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
// Deduplicating constraints is shown to have a positive perf impact.
let mut seen = UnordSet::default();
self.data.constraints.retain(|(constraint, _)| seen.insert(*constraint));
self.data.constraints.retain_mut(|(constraint, _)| {
// We don't want to discern constraints by leak check visibility here
constraint.visible_for_leak_check = ty::VisibleForLeakCheck::Unreachable;
seen.insert(*constraint)
});
if cfg!(debug_assertions) {
self.dump_constraints();
+4 -2
View File
@@ -698,8 +698,9 @@ impl<'tcx> InferCtxt<'tcx> {
origin: SubregionOrigin<'tcx>,
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
vis: ty::VisibleForLeakCheck,
) {
self.inner.borrow_mut().unwrap_region_constraints().make_subregion(origin, a, b);
self.inner.borrow_mut().unwrap_region_constraints().make_subregion(origin, a, b, vis);
}
#[instrument(skip(self), level = "debug")]
@@ -708,8 +709,9 @@ impl<'tcx> InferCtxt<'tcx> {
origin: SubregionOrigin<'tcx>,
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
vis: ty::VisibleForLeakCheck,
) {
self.inner.borrow_mut().unwrap_region_constraints().make_eqregion(origin, a, b);
self.inner.borrow_mut().unwrap_region_constraints().make_eqregion(origin, a, b, vis);
}
/// Processes a `Coerce` predicate from the fulfillment context.
@@ -85,11 +85,12 @@ impl<'tcx> InferCtxt<'tcx> {
pub fn register_outlives_constraint(
&self,
ty::OutlivesPredicate(arg, r2): ty::ArgOutlivesPredicate<'tcx>,
vis: ty::VisibleForLeakCheck,
cause: &ObligationCause<'tcx>,
) {
match arg.kind() {
ty::GenericArgKind::Lifetime(r1) => {
self.register_region_outlives_constraint(ty::OutlivesPredicate(r1, r2), cause);
self.register_region_outlives_constraint(ty::OutlivesPredicate(r1, r2), vis, cause);
}
ty::GenericArgKind::Type(ty1) => {
self.register_type_outlives_constraint(ty1, r2, cause);
@@ -101,24 +102,26 @@ impl<'tcx> InferCtxt<'tcx> {
pub fn register_region_eq_constraint(
&self,
ty::RegionEqPredicate(r_a, r_b): ty::RegionEqPredicate<'tcx>,
vis: ty::VisibleForLeakCheck,
cause: &ObligationCause<'tcx>,
) {
let origin = SubregionOrigin::from_obligation_cause(cause, || {
SubregionOrigin::RelateRegionParamBound(cause.span, None)
});
self.equate_regions(origin, r_a, r_b);
self.equate_regions(origin, r_a, r_b, vis);
}
pub fn register_region_outlives_constraint(
&self,
ty::OutlivesPredicate(r_a, r_b): ty::RegionOutlivesPredicate<'tcx>,
vis: ty::VisibleForLeakCheck,
cause: &ObligationCause<'tcx>,
) {
let origin = SubregionOrigin::from_obligation_cause(cause, || {
SubregionOrigin::RelateRegionParamBound(cause.span, None)
});
// `'a: 'b` ==> `'b <= 'a`
self.sub_regions(origin, r_b, r_a);
self.sub_regions(origin, r_b, r_a, vis);
}
/// Registers that the given region obligation must be resolved
@@ -577,7 +580,8 @@ impl<'cx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'tcx> {
b: ty::Region<'tcx>,
_constraint_category: ConstraintCategory<'tcx>,
) {
self.sub_regions(origin, a, b)
// We don't do leak check in lexical region resolution
self.sub_regions(origin, a, b, ty::VisibleForLeakCheck::Unreachable)
}
fn push_verify(
@@ -392,10 +392,15 @@ impl<'tcx> MiniGraph<'tcx> {
{
match undo_entry {
&AddConstraint(i) => {
region_constraints.data().constraints[i]
.0
.iter_outlives()
.for_each(|c| each_edge(c.sub, c.sup));
region_constraints.data().constraints[i].0.iter_outlives().for_each(
|Constraint { kind: _, sub, sup, visible_for_leak_check }| {
match visible_for_leak_check {
ty::VisibleForLeakCheck::Yes => each_edge(sub, sup),
ty::VisibleForLeakCheck::No => {}
ty::VisibleForLeakCheck::Unreachable => unreachable!(),
}
},
);
}
&AddVerify(i) => span_bug!(
region_constraints.data().verifys[i].origin.span(),
@@ -410,7 +415,13 @@ impl<'tcx> MiniGraph<'tcx> {
.constraints
.iter()
.flat_map(|(c, _)| c.iter_outlives())
.for_each(|c| each_edge(c.sub, c.sup))
.for_each(|Constraint { kind: _, sub, sup, visible_for_leak_check }| {
match visible_for_leak_check {
ty::VisibleForLeakCheck::Yes => each_edge(sub, sup),
ty::VisibleForLeakCheck::No => {}
ty::VisibleForLeakCheck::Unreachable => unreachable!(),
}
})
}
}
@@ -115,10 +115,11 @@ pub enum ConstraintKind {
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub struct Constraint<'tcx> {
pub kind: ConstraintKind,
// If `kind` is `VarSubVar` or `VarSubReg`, this must be a `ReVar`.
// If `kind` is `VarSubVar`, `VarSubReg`, `VarEqVar` or `VarEqReg`, this must be a `ReVar`.
pub sub: Region<'tcx>,
// If `kind` is `VarSubVar` or `RegSubVar`, this must be a `ReVar`.
// If `kind` is `VarSubVar`, `RegSubVar` or `VarEqVar`, this must be a `ReVar`.
pub sup: Region<'tcx>,
pub visible_for_leak_check: ty::VisibleForLeakCheck,
}
impl Constraint<'_> {
@@ -127,7 +128,7 @@ impl Constraint<'_> {
}
pub fn iter_outlives(self) -> impl Iterator<Item = Self> {
let Constraint { kind, sub, sup } = self;
let Constraint { kind, sub, sup, visible_for_leak_check } = self;
match kind {
ConstraintKind::VarSubVar
@@ -135,18 +136,42 @@ impl Constraint<'_> {
| 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 }))
}
ConstraintKind::VarEqVar => iter::once(Constraint {
kind: ConstraintKind::VarSubVar,
sub,
sup,
visible_for_leak_check,
})
.chain(Some(Constraint {
kind: ConstraintKind::VarSubVar,
sub: sup,
sup: sub,
visible_for_leak_check,
})),
ConstraintKind::VarEqReg => iter::once(Constraint {
kind: ConstraintKind::VarSubReg,
sub,
sup,
visible_for_leak_check,
})
.chain(Some(Constraint {
kind: ConstraintKind::RegSubVar,
sub: sup,
sup: sub,
visible_for_leak_check,
})),
ConstraintKind::RegEqReg => iter::once(Constraint {
kind: ConstraintKind::RegSubReg,
sub,
sup,
visible_for_leak_check,
})
.chain(Some(Constraint {
kind: ConstraintKind::RegSubReg,
sub: sup,
sup: sub,
visible_for_leak_check,
})),
}
}
}
@@ -457,6 +482,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
origin: SubregionOrigin<'tcx>,
a: Region<'tcx>,
b: Region<'tcx>,
visible_for_leak_check: ty::VisibleForLeakCheck,
) {
if a != b {
// FIXME: We could only emit constraints if `unify_var_{var, value}` fails when
@@ -467,7 +493,12 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
}
(ReVar(a_vid), ReVar(b_vid), _, _) => {
self.add_constraint(
Constraint { kind: ConstraintKind::VarEqVar, sub: a, sup: b },
Constraint {
kind: ConstraintKind::VarEqVar,
sub: a,
sup: b,
visible_for_leak_check,
},
origin,
);
debug!("make_eqregion: unifying {:?} with {:?}", a_vid, b_vid);
@@ -479,12 +510,22 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
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 },
Constraint {
kind: ConstraintKind::RegSubVar,
sub: reg,
sup: var,
visible_for_leak_check,
},
origin,
);
} else {
self.add_constraint(
Constraint { kind: ConstraintKind::VarEqReg, sub: var, sup: reg },
Constraint {
kind: ConstraintKind::VarEqReg,
sub: var,
sup: reg,
visible_for_leak_check,
},
origin,
);
}
@@ -500,13 +541,23 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
(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 },
Constraint {
kind: ConstraintKind::RegSubReg,
sub: st,
sup: reg,
visible_for_leak_check,
},
origin,
);
}
_ => {
self.add_constraint(
Constraint { kind: ConstraintKind::RegEqReg, sub: a, sup: b },
Constraint {
kind: ConstraintKind::RegEqReg,
sub: a,
sup: b,
visible_for_leak_check,
},
origin,
);
}
@@ -520,6 +571,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
origin: SubregionOrigin<'tcx>,
sub: Region<'tcx>,
sup: Region<'tcx>,
visible_for_leak_check: ty::VisibleForLeakCheck,
) {
// cannot add constraints once regions are resolved
debug!("origin = {:#?}", origin);
@@ -534,19 +586,33 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
(ReVar(sub_id), ReVar(sup_id)) => {
if sub_id != sup_id {
self.add_constraint(
Constraint { kind: ConstraintKind::VarSubVar, sub, sup },
Constraint {
kind: ConstraintKind::VarSubVar,
sub,
sup,
visible_for_leak_check,
},
origin,
);
}
}
(_, ReVar(_)) => self
.add_constraint(Constraint { kind: ConstraintKind::RegSubVar, sub, sup }, origin),
(ReVar(_), _) => self
.add_constraint(Constraint { kind: ConstraintKind::VarSubReg, sub, sup }, origin),
(_, ReVar(_)) => self.add_constraint(
Constraint { kind: ConstraintKind::RegSubVar, sub, sup, visible_for_leak_check },
origin,
),
(ReVar(_), _) => self.add_constraint(
Constraint { kind: ConstraintKind::VarSubReg, sub, sup, visible_for_leak_check },
origin,
),
_ => {
if sub != sup {
self.add_constraint(
Constraint { kind: ConstraintKind::RegSubReg, sub, sup },
Constraint {
kind: ConstraintKind::RegSubReg,
sub,
sup,
visible_for_leak_check,
},
origin,
)
}
@@ -655,8 +721,12 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
let new_r = ty::Region::new_var(tcx, c);
for old_r in [a, b] {
match t {
Glb => self.make_subregion(origin.clone(), new_r, old_r),
Lub => self.make_subregion(origin.clone(), old_r, new_r),
Glb => {
self.make_subregion(origin.clone(), new_r, old_r, ty::VisibleForLeakCheck::Yes)
}
Lub => {
self.make_subregion(origin.clone(), old_r, new_r, ty::VisibleForLeakCheck::Yes)
}
}
}
debug!("combine_vars() c={:?}", c);
@@ -223,26 +223,29 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, 'tcx> {
match self.ambient_variance {
// Subtype(&'a u8, &'b u8) => Outlives('a: 'b) => SubRegion('b, 'a)
ty::Covariant => {
self.infcx
.inner
.borrow_mut()
.unwrap_region_constraints()
.make_subregion(origin, b, a);
self.infcx.inner.borrow_mut().unwrap_region_constraints().make_subregion(
origin,
b,
a,
ty::VisibleForLeakCheck::Yes,
);
}
// Suptype(&'a u8, &'b u8) => Outlives('b: 'a) => SubRegion('a, 'b)
ty::Contravariant => {
self.infcx
.inner
.borrow_mut()
.unwrap_region_constraints()
.make_subregion(origin, a, b);
self.infcx.inner.borrow_mut().unwrap_region_constraints().make_subregion(
origin,
a,
b,
ty::VisibleForLeakCheck::Yes,
);
}
ty::Invariant => {
self.infcx
.inner
.borrow_mut()
.unwrap_region_constraints()
.make_eqregion(origin, a, b);
self.infcx.inner.borrow_mut().unwrap_region_constraints().make_eqregion(
origin,
a,
b,
ty::VisibleForLeakCheck::Yes,
);
}
ty::Bivariant => {
unreachable!("Expected bivariance to be handled in relate_with_variance")
+3 -1
View File
@@ -135,7 +135,9 @@ impl<'tcx, R> QueryResponse<'tcx, R> {
}
}
pub type QueryRegionConstraint<'tcx> = (ty::RegionConstraint<'tcx>, ConstraintCategory<'tcx>);
// FIXME: Convert this into a struct
pub type QueryRegionConstraint<'tcx> =
(ty::RegionConstraint<'tcx>, ConstraintCategory<'tcx>, ty::VisibleForLeakCheck);
#[derive(Default)]
pub struct CanonicalParamEnvCache<'tcx> {
+1 -1
View File
@@ -64,7 +64,7 @@ pub use rustc_type_ir::fast_reject::DeepRejectCtxt;
)]
use rustc_type_ir::inherent;
pub use rustc_type_ir::relate::VarianceDiagInfo;
pub use rustc_type_ir::solve::{CandidatePreferenceMode, SizedTraitKind};
pub use rustc_type_ir::solve::{CandidatePreferenceMode, SizedTraitKind, VisibleForLeakCheck};
pub use rustc_type_ir::*;
#[allow(hidden_glob_reexports, unused_imports)]
use rustc_type_ir::{InferCtxtLike, Interner};
@@ -25,7 +25,7 @@ use crate::delegate::SolverDelegate;
use crate::resolve::eager_resolve_vars;
use crate::solve::{
CanonicalInput, CanonicalResponse, Certainty, ExternalConstraintsData, Goal,
NestedNormalizationGoals, QueryInput, Response, inspect,
NestedNormalizationGoals, QueryInput, Response, VisibleForLeakCheck, inspect,
};
pub mod canonicalizer;
@@ -99,6 +99,7 @@ pub(super) fn instantiate_and_apply_query_response<D, I>(
param_env: I::ParamEnv,
original_values: &[I::GenericArg],
response: CanonicalResponse<I>,
visible_for_leak_check: VisibleForLeakCheck,
span: I::Span,
) -> (NestedNormalizationGoals<I>, Certainty)
where
@@ -116,7 +117,11 @@ where
let ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } =
&*external_constraints;
register_region_constraints(delegate, region_constraints, span);
register_region_constraints(
delegate,
region_constraints.iter().map(|(c, vis)| (*c, vis.and(visible_for_leak_check))),
span,
);
register_new_opaque_types(delegate, opaque_types, span);
(normalization_nested_goals.clone(), certainty)
@@ -262,21 +267,21 @@ fn unify_query_var_values<D, I>(
fn register_region_constraints<D, I>(
delegate: &D,
constraints: &[ty::RegionConstraint<I>],
constraints: impl IntoIterator<Item = (ty::RegionConstraint<I>, VisibleForLeakCheck)>,
span: I::Span,
) where
D: SolverDelegate<Interner = I>,
I: Interner,
{
for &constraint in constraints {
for (constraint, vis) 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::Lifetime(lhs) => delegate.sub_regions(rhs, lhs, vis, 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)
delegate.equate_regions(lhs, rhs, vis, span)
}
}
}
@@ -1,6 +1,6 @@
use std::ops::Deref;
use rustc_type_ir::solve::{Certainty, Goal, NoSolution};
use rustc_type_ir::solve::{Certainty, Goal, NoSolution, VisibleForLeakCheck};
use rustc_type_ir::{self as ty, InferCtxtLike, Interner, TypeFoldable};
pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
@@ -45,7 +45,9 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
term: <Self::Interner as Interner>::Term,
) -> Option<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>>;
fn make_deduplicated_region_constraints(&self) -> Vec<ty::RegionConstraint<Self::Interner>>;
fn make_deduplicated_region_constraints(
&self,
) -> Vec<(ty::RegionConstraint<Self::Interner>, VisibleForLeakCheck)>;
fn instantiate_canonical<V>(
&self,
@@ -30,7 +30,8 @@ use crate::solve::ty::may_use_unstable_feature;
use crate::solve::{
CanonicalInput, CanonicalResponse, Certainty, ExternalConstraintsData, FIXPOINT_STEP_LIMIT,
Goal, GoalEvaluation, GoalSource, GoalStalledOn, HasChanged, MaybeCause,
NestedNormalizationGoals, NoSolution, QueryInput, QueryResult, Response, inspect,
NestedNormalizationGoals, NoSolution, QueryInput, QueryResult, Response, VisibleForLeakCheck,
inspect,
};
mod probe;
@@ -484,11 +485,29 @@ where
let has_changed =
if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No };
// FIXME: We should revisit and consider removing this after
// *assumptions on binders* is available, like once we had done in the
// stabilization of `-Znext-solver=coherence`(#121848).
// We ignore constraints from the nested goals in leak check. This is to match
// with the old solver's behavior, which has separated evaluation and fulfillment,
// and the former doesn't consider outlives obligations from the later.
let vis = match goal.predicate.kind().skip_binder() {
ty::PredicateKind::Clause(_)
| ty::PredicateKind::DynCompatible(_)
| ty::PredicateKind::Subtype(_)
| ty::PredicateKind::Coerce(_)
| ty::PredicateKind::ConstEquate(_, _)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::NormalizesTo(_) => VisibleForLeakCheck::No,
ty::PredicateKind::AliasRelate(_, _, _) => VisibleForLeakCheck::Yes,
};
let (normalization_nested_goals, certainty) = instantiate_and_apply_query_response(
self.delegate,
goal.param_env,
&orig_values,
response,
vis,
self.origin_span,
);
@@ -1100,9 +1119,14 @@ where
self.delegate.register_ty_outlives(ty, lt, self.origin_span);
}
pub(super) fn register_region_outlives(&self, a: I::Region, b: I::Region) {
pub(super) fn register_region_outlives(
&self,
a: I::Region,
b: I::Region,
vis: VisibleForLeakCheck,
) {
// `'a: 'b` ==> `'b <= 'a`
self.delegate.sub_regions(b, a, self.origin_span);
self.delegate.sub_regions(b, a, vis, self.origin_span);
}
/// Computes the list of goals required for `arg` to be well-formed
@@ -1303,7 +1327,7 @@ where
let mut unique = HashSet::default();
external_constraints
.region_constraints
.retain(|outlives| !outlives.is_trivial() && unique.insert(*outlives));
.retain(|(outlives, _)| !outlives.is_trivial() && unique.insert(*outlives));
let canonical = canonicalize_response(
self.delegate,
@@ -1533,6 +1557,7 @@ pub(super) fn evaluate_root_goal_for_proof_tree<D: SolverDelegate<Interner = I>,
goal.param_env,
&proof_tree.orig_values,
response,
VisibleForLeakCheck::Yes,
origin_span,
);
@@ -101,7 +101,7 @@ where
goal: Goal<I, ty::OutlivesPredicate<I, I::Region>>,
) -> QueryResult<I> {
let ty::OutlivesPredicate(a, b) = goal.predicate;
self.register_region_outlives(a, b);
self.register_region_outlives(a, b, VisibleForLeakCheck::Yes);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
@@ -1,6 +1,7 @@
use std::collections::hash_map::Entry;
use std::ops::Deref;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::LangItem;
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
@@ -112,6 +113,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
SubregionOrigin::RelateRegionParamBound(span, None),
outlives.1,
outlives.0,
ty::VisibleForLeakCheck::Yes,
);
Some(Certainty::Yes)
}
@@ -204,7 +206,9 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
.map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect())
}
fn make_deduplicated_region_constraints(&self) -> Vec<ty::RegionConstraint<'tcx>> {
fn make_deduplicated_region_constraints(
&self,
) -> Vec<(ty::RegionConstraint<'tcx>, ty::VisibleForLeakCheck)> {
// 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();
@@ -217,13 +221,23 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
)
});
let mut seen = FxHashSet::default();
region_constraints
.constraints
.into_iter()
.filter(|&(outlives, _)| seen.insert(outlives))
.map(|(outlives, _)| outlives)
.collect()
let mut seen = FxHashMap::default();
let mut constraints = vec![];
for (outlives, _, vis) in region_constraints.constraints {
match seen.entry(outlives) {
Entry::Occupied(occupied) => {
let idx = occupied.get();
let (_, prev_vis): &mut (_, ty::VisibleForLeakCheck) =
constraints.get_mut(*idx).unwrap();
*prev_vis = (*prev_vis).or(vis);
}
Entry::Vacant(vacant) => {
vacant.insert(constraints.len());
constraints.push((outlives, vis));
}
}
}
constraints
}
fn instantiate_canonical<V>(
@@ -738,7 +738,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(binder)) => {
let binder = bound_predicate.rebind(binder);
selcx.infcx.enter_forall(binder, |pred| {
selcx.infcx.register_region_outlives_constraint(pred, &dummy_cause);
selcx.infcx.register_region_outlives_constraint(pred, ty::VisibleForLeakCheck::Yes,&dummy_cause);
});
}
ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(binder)) => {
@@ -484,7 +484,11 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(data)) => {
if infcx.considering_regions {
infcx.register_region_outlives_constraint(data, &obligation.cause);
infcx.register_region_outlives_constraint(
data,
ty::VisibleForLeakCheck::Yes,
&obligation.cause,
);
}
ProcessResult::Changed(Default::default())
@@ -83,13 +83,13 @@ fn implied_outlives_bounds<'a, 'tcx>(
// outlives bound required proving some higher-ranked coroutine obl.
let QueryRegionConstraints { constraints, assumptions: _ } = constraints;
let cause = ObligationCause::misc(span, body_id);
for &(constraint, _) in &constraints {
for &(constraint, _, vis) in &constraints {
match constraint {
ty::RegionConstraint::Outlives(predicate) => {
infcx.register_outlives_constraint(predicate, &cause)
infcx.register_outlives_constraint(predicate, vis, &cause)
}
ty::RegionConstraint::Eq(predicate) => {
infcx.register_region_eq_constraint(predicate, &cause)
infcx.register_region_eq_constraint(predicate, vis, &cause)
}
}
}
@@ -80,7 +80,7 @@ fn compute_assumptions<'tcx>(
tcx.mk_outlives_from_iter(
constraints
.into_iter()
.flat_map(|(constraint, _)| constraint.iter_outlives())
.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.
+3
View File
@@ -8,6 +8,7 @@ use crate::fold::TypeFoldable;
use crate::inherent::*;
use crate::relate::RelateResult;
use crate::relate::combine::PredicateEmittingRelation;
use crate::solve::VisibleForLeakCheck;
use crate::{self as ty, Interner, TyVid};
/// The current typing mode of an inference context. We unfortunately have some
@@ -323,6 +324,7 @@ pub trait InferCtxtLike: Sized {
&self,
sub: <Self::Interner as Interner>::Region,
sup: <Self::Interner as Interner>::Region,
vis: VisibleForLeakCheck,
span: <Self::Interner as Interner>::Span,
);
@@ -330,6 +332,7 @@ pub trait InferCtxtLike: Sized {
&self,
a: <Self::Interner as Interner>::Region,
b: <Self::Interner as Interner>::Region,
vis: VisibleForLeakCheck,
span: <Self::Interner as Interner>::Span,
);
+1
View File
@@ -58,6 +58,7 @@ TrivialTypeTraversalImpls! {
crate::solve::BuiltinImplSource,
crate::solve::Certainty,
crate::solve::GoalSource,
crate::solve::VisibleForLeakCheck,
rustc_ast_ir::Mutability,
// tidy-alphabetical-end
}
@@ -4,7 +4,7 @@ use self::combine::{PredicateEmittingRelation, super_combine_consts, super_combi
use crate::data_structures::DelayedSet;
use crate::relate::combine::combine_ty_args;
pub use crate::relate::*;
use crate::solve::Goal;
use crate::solve::{Goal, VisibleForLeakCheck};
use crate::{self as ty, InferCtxtLike, Interner};
pub trait RelateExt: InferCtxtLike {
@@ -256,10 +256,10 @@ where
fn regions(&mut self, a: I::Region, b: I::Region) -> RelateResult<I, I::Region> {
match self.ambient_variance {
// Subtype(&'a u8, &'b u8) => Outlives('a: 'b) => SubRegion('b, 'a)
ty::Covariant => self.infcx.sub_regions(b, a, self.span),
ty::Covariant => self.infcx.sub_regions(b, a, VisibleForLeakCheck::Yes, self.span),
// Suptype(&'a u8, &'b u8) => Outlives('b: 'a) => SubRegion('a, 'b)
ty::Contravariant => self.infcx.sub_regions(a, b, self.span),
ty::Invariant => self.infcx.equate_regions(a, b, self.span),
ty::Contravariant => self.infcx.sub_regions(a, b, VisibleForLeakCheck::Yes, self.span),
ty::Invariant => self.infcx.equate_regions(a, b, VisibleForLeakCheck::Yes, self.span),
ty::Bivariant => {
unreachable!("Expected bivariance to be handled in relate_with_variance")
}
+42 -1
View File
@@ -250,7 +250,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::RegionConstraint<I>>,
pub region_constraints: Vec<(ty::RegionConstraint<I>, VisibleForLeakCheck)>,
pub opaque_types: Vec<(ty::OpaqueTypeKey<I>, I::Ty)>,
pub normalization_nested_goals: NestedNormalizationGoals<I>,
}
@@ -265,6 +265,47 @@ impl<I: Interner> ExternalConstraintsData<I> {
}
}
/// Whether the given region constraint should be considered/ignored for
/// leak check. In most part of the compiler, this should be `Yes`, except
/// for applying constraints from the nested goals in next-solver.
/// `Unreachable` is used in places in which leak check isn't done, e.g.
/// borrowck.
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
pub enum VisibleForLeakCheck {
Yes,
No,
Unreachable,
}
impl VisibleForLeakCheck {
pub fn and(self, other: VisibleForLeakCheck) -> VisibleForLeakCheck {
match (self, other) {
// Make sure that we never overwrite that constraints shouldn't
// be encountered by the leak checked
(VisibleForLeakCheck::Unreachable, _) | (_, VisibleForLeakCheck::Unreachable) => {
VisibleForLeakCheck::Unreachable
}
(VisibleForLeakCheck::No, _) | (_, VisibleForLeakCheck::No) => VisibleForLeakCheck::No,
(VisibleForLeakCheck::Yes, VisibleForLeakCheck::Yes) => VisibleForLeakCheck::Yes,
}
}
pub fn or(self, other: VisibleForLeakCheck) -> VisibleForLeakCheck {
match (self, other) {
// Make sure that we never overwrite that constraints shouldn't
// be encountered by the leak checked
(VisibleForLeakCheck::Unreachable, _) | (_, VisibleForLeakCheck::Unreachable) => {
VisibleForLeakCheck::Unreachable
}
(VisibleForLeakCheck::Yes, _) | (_, VisibleForLeakCheck::Yes) => {
VisibleForLeakCheck::Yes
}
(VisibleForLeakCheck::No, VisibleForLeakCheck::No) => VisibleForLeakCheck::No,
}
}
}
#[derive_where(Clone, Hash, PartialEq, Debug, Default; I: Interner)]
#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)]
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
-32
View File
@@ -1,32 +0,0 @@
//@ known-bug: #140577
//@ compile-flags: -Znext-solver=globally
//@ edition:2021
use std::future::Future;
use std::pin::Pin;
trait Acquire {
type Connection;
}
impl Acquire for &'static () {
type Connection = ();
}
fn b<T: Acquire>() -> impl Future + Send {
let x: Pin<Box<dyn Future<Output = T::Connection> + Send>> = todo!();
x
}
fn main() {
async {
b::<&()>().await;
}
.aa();
}
impl<F> Filter for F where F: Send {}
trait Filter {
fn aa(self)
where
Self: Sized,
{
}
}
@@ -2,12 +2,10 @@ error[E0308]: mismatched types
--> $DIR/must-prove-where-clauses-on-norm.rs:23:61
|
LL | let func: for<'a, 'b> fn((), &'b str) -> &'static str = foo::<()>;
| ------------------------------------------- ^^^^^^^^^ one type is more general than the other
| |
| expected due to this
| ^^^^^^^^^ one type is more general than the other
|
= note: expected fn pointer `for<'b> fn((), &'b _) -> &'static _`
found fn item `for<'b> fn(<() as Trait>::Assoc<'_, 'b>, &'b _) -> &_ {foo::<'_, ()>}`
= note: expected fn pointer `for<'b> fn((), &'b _) -> &_`
found fn pointer `for<'b> fn((), &'b _) -> &_`
error: aborting due to 1 previous error
@@ -1,14 +1,8 @@
error[E0271]: type mismatch resolving `<T as Trait<'a>>::Assoc == usize`
--> $DIR/candidate-from-env-universe-err-project.rs:38:24
error: higher-ranked subtype error
--> $DIR/candidate-from-env-universe-err-project.rs:38:5
|
LL | projection_bound::<T>();
| ^ types differ
|
note: required by a bound in `projection_bound`
--> $DIR/candidate-from-env-universe-err-project.rs:19:42
|
LL | fn projection_bound<T: for<'a> Trait<'a, Assoc = usize>>() {}
| ^^^^^^^^^^^^^ required by this bound in `projection_bound`
| ^^^^^^^^^^^^^^^^^^^^^
error: higher-ranked subtype error
--> $DIR/candidate-from-env-universe-err-project.rs:52:30
@@ -26,4 +20,3 @@ LL | let _higher_ranked_norm: for<'a> fn(<T as Trait<'a>>::Assoc) = |_| ();
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0271`.
@@ -36,7 +36,7 @@ fn function2<T: Trait<'static, Assoc = usize>>() {
// does not use the leak check when trying the where-bound, causing us
// to prefer it over the impl, resulting in a placeholder error.
projection_bound::<T>();
//[next]~^ ERROR type mismatch resolving `<T as Trait<'a>>::Assoc == usize`
//[next]~^ ERROR higher-ranked subtype error
//[current]~^^ ERROR mismatched types
}
@@ -0,0 +1,45 @@
//@ check-pass
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
//@ edition:2021
// Regression test for <https://github.com/rust-lang/rust/issues/140577>.
//
// This previously caused an ICE due to a nonwell-formed opaque type
// in a coroutine witness failing the leak check in the next-solver.
//
// In `TypingMode::Analysis`, the problematic type is hidden behind a
// stalled coroutine candidate. However, in later passes (e.g. MIR
// validation), we eagerly normalize it. The candidate that was
// previously accepted as a solution then fails the leak check, resulting
// in broken MIR and ultimately an ICE.
use std::future::Future;
use std::pin::Pin;
trait Acquire {
type Connection;
}
impl Acquire for &'static () {
type Connection = ();
}
fn b<T: Acquire>() -> impl Future + Send {
let x: Pin<Box<dyn Future<Output = T::Connection> + Send>> = todo!();
x
}
fn main() {
async {
b::<&()>().await;
}
.aa();
}
impl<F> Filter for F where F: Send {}
trait Filter {
fn aa(self)
where
Self: Sized,
{
}
}
@@ -0,0 +1,50 @@
//@ check-pass
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
//@ edition: 2021
// Regression test for <https://github.com/rust-lang/trait-system-refactor-initiative/issues/251>.
//
// This previously caused an ICE due to a nonwell-formed opaque type
// in a coroutine witness failing the leak check in the next-solver.
//
// In `TypingMode::Analysis`, the problematic type is hidden behind a
// stalled coroutine candidate. However, in later passes (e.g. MIR
// validation), we eagerly normalize it. The candidate that was
// previously accepted as a solution then fails the leak check, resulting
// in broken MIR and ultimately an ICE.
use std::future::Future;
trait Access {
// has to have an associated type, but can be anything
type Reader;
fn read(&self) -> impl Future<Output = Self::Reader> + Send {
async { loop {} }
}
}
trait AccessDyn: Sync {}
impl Access for dyn AccessDyn {
type Reader = ();
}
trait Stream {
fn poll_next(s: &'static dyn AccessDyn);
}
// has to be a function in a trait impl, can't be a normal impl block or standalone fn
impl Stream for () {
fn poll_next(s: &'static dyn AccessDyn) {
// new async block is important
is_dyn_send(&async {
s.read().await;
});
}
}
fn is_dyn_send(_: &dyn Send) {}
fn main() {}
@@ -0,0 +1,36 @@
//@ check-pass
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
//@ edition:2021
// Regression test for <https://github.com/rust-lang/trait-system-refactor-initiative/issues/251>.
//
// This previously caused an ICE due to a nonwell-formed opaque type
// in a coroutine witness failing the leak check in the next-solver.
//
// In `TypingMode::Analysis`, the problematic type is hidden behind a
// stalled coroutine candidate. However, in later passes (e.g. MIR
// validation), we eagerly normalize it. The candidate that was
// previously accepted as a solution then fails the leak check, resulting
// in broken MIR and ultimately an ICE.
trait Trait {
type Assoc;
}
impl Trait for &'static u32 {
type Assoc = ();
}
struct W<T: Trait>(T::Assoc);
fn prove_send_and_hide<T: Send>(x: T) -> impl Send { x }
fn as_dyn_send(_: &dyn Send) {}
pub fn main() {
// Checking whether the cast to the trait object is correct
// during MIR validation uses `TypingMode::PostAnalysis` and
// therefore looks into the opaque.
as_dyn_send(&async move {
let opaque_ty = prove_send_and_hide(W::<&'static u32>(()));
std::future::ready(opaque_ty).await;
});
}
@@ -0,0 +1,48 @@
//@ run-pass
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
// Regression test for <https://github.com/rust-lang/rust/issues/153596>.
#![allow(warnings)]
trait Trait {
type Assoc;
}
impl<'a, 'b: 'a> Trait for Inv<'a, 'b> {
type Assoc = ();
}
trait ReqWf {}
impl<T: Trait> ReqWf for T where T::Assoc: Sized {}
struct Inv<'a, 'b: 'a>(Option<*mut &'a &'b ()>);
fn mk_opaque<'a, 'b>(x: &'a &'b u32) -> impl ReqWf + use<'a, 'b> {
Inv::<'a, 'b>(None)
}
trait Bound<T> {}
impl<T, F, R: ReqWf> Bound<T> for F where F: FnOnce(T) -> R {}
trait ImpossiblePredicates<F> {
fn call_me(&self)
where
F: for<'a, 'b> Bound<&'a &'b u32>,
{
println!("method body");
}
}
impl<F> ImpossiblePredicates<F> for () {}
fn mk_trait_object<F>(_: F) -> Box<dyn ImpossiblePredicates<F>> {
Box::new(())
}
pub fn main() {
let obj = mk_trait_object(mk_opaque);
// This previously caused a segfault: the where-bounds of
// `ImpossiblePredicate::call_me` did not hold due to missing implied bounds
// for the fully normalized opaque type of `obj` in `fn impossible_predicates`.
// As a result, the method's vtable ended up empty.
//
// However, earlier compilation passes did not report an error because the
// opaque type had not yet been fully normalized.
obj.call_me();
}