Skip to content
Merged
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
167 changes: 88 additions & 79 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use rustc_lint_defs::builtin::{REDUNDANT_LIFETIMES, SHADOWING_SUPERTRAIT_ITEMS};
use rustc_macros::{Diagnostic, TypeFoldable, TypeVisitable};
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::traits::solve::NoSolution;
use rustc_middle::ty::region_constraint::{And, LeafRegionConstraint, Or};
use rustc_middle::ty::trait_def::TraitSpecializationKind;
use rustc_middle::ty::{
self, GenericArgKind, GenericArgs, GenericParamDefKind, RegionExt, Ty, TyCtxt, TypeFlags,
Expand Down Expand Up @@ -2334,7 +2335,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
pub(super) fn check_test_binder_body(&self, body: TestBinderBody<'tcx>) {
let constraints = match validate(self.tcx(), &body.constraints) {
Ok(()) => body.constraints,
Err(_guar) => ty::region_constraint::RegionConstraint::And(Box::new([])),
Err(_guar) => ty::region_constraint::RegionConstraint::new_true(),
};

self.infcx.register_solver_region_constraint(constraints);
Expand All @@ -2350,40 +2351,39 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
tcx: TyCtxt<'tcx>,
constraint: &SolverRegionConstraint<'tcx>,
) -> Result<(), ErrorGuaranteed> {
match constraint {
ty::region_constraint::RegionConstraint::Ambiguity(_) => Ok(()),
ty::region_constraint::RegionConstraint::RegionOutlives(..) => Ok(()),
ty::region_constraint::RegionConstraint::AliasTyOutlivesViaEnv(..) => Ok(()),
ty::region_constraint::RegionConstraint::PlaceholderTyOutlives(ty, _, span) => {
// we can't check this during lowering, because the ty is a ty::Bound that gets
// instantiated with a placeholder when entering the containing forall.
if let ty::Placeholder(_) | ty::Param(_) = ty.kind() {
Ok(())
} else {
let mut err = tcx.dcx().struct_span_err(
*span,
"the lhs of a ty outlives must be a placeholder",
);
err.note(format!("it is a {ty}"));
err.note(format!("and here it is `Debug`ged :3 {ty:?}"));
Err(err.emit())
}
}
ty::region_constraint::RegionConstraint::And(constraints) => {
let mut res = Ok(());
for constraint in constraints {
res = res.and(validate(tcx, constraint));
}
res
}
ty::region_constraint::RegionConstraint::Or(constraints) => {
let mut res = Ok(());
for constraint in constraints {
res = res.and(validate(tcx, constraint));
let mut r = Ok(());

let mut validate_and = |and: &And<TyCtxt<'_>, _>| {
for c in and.0.iter() {
match c {
LeafRegionConstraint::Ambiguity(_)
| LeafRegionConstraint::RegionOutlives(..)
| LeafRegionConstraint::AliasTyOutlivesViaEnv(..) => (), // OK
LeafRegionConstraint::PlaceholderTyOutlives(ty, _, span) => {
// we can't check this during lowering, because the ty is a ty::Bound that gets
// instantiated with a placeholder when entering the containing forall.
if let ty::Placeholder(_) | ty::Param(_) = ty.kind() {
// all OK
} else {
let mut err = tcx.dcx().struct_span_err(
*span,
"the lhs of a ty outlives must be a placeholder",
);
err.note(format!("it is a {ty}"));
err.note(format!("and here it is `Debug`ged :3 {ty:?}"));
r = Err(err.emit());
}
}
}
res
}
};

validate_and(&constraint.and_constraint);
for and in constraint.or_constraint.0.iter() {
validate_and(and);
}

r
}
}

Expand All @@ -2405,13 +2405,9 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
solver_region_constraint.without_spans(),
u,
)
.with_span(forall.span);
if let Some(assert_on_exit) = forall.assert_on_exit {
self.check_test_binder_region_constraints(
forall.span,
&assert_on_exit.clone().canonical_form(),
&constraint.clone().canonical_form(),
);
.with_spans(forall.span);
if let Some(assert_on_exit) = &forall.assert_on_exit {
self.check_test_binder_region_constraints(forall.span, assert_on_exit, &constraint);
}
self.infcx.overwrite_solver_region_constraint(constraint);
});
Expand All @@ -2424,58 +2420,71 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
expected: &SolverRegionConstraint<'tcx>,
actual: &SolverRegionConstraint<'tcx>,
) {
fn span_of<'tcx>(constraint: &SolverRegionConstraint<'tcx>) -> Option<Span> {
match constraint {
SolverRegionConstraint::Ambiguity(sp)
| SolverRegionConstraint::RegionOutlives(_, _, sp)
| SolverRegionConstraint::AliasTyOutlivesViaEnv(_, sp)
| ty::region_constraint::RegionConstraint::PlaceholderTyOutlives(_, _, sp) => {
Some(*sp)
}
SolverRegionConstraint::And(constraints)
| SolverRegionConstraint::Or(constraints) => constraints
.iter()
.map(span_of)
.flatten()
.fold(None, |l, r| Some(l.map_or(r, |l| l.to(r)))),
}
}
fn err<'tcx>(
tcx: TyCtxt<'tcx>,
fallback_span: Span,
expected: &SolverRegionConstraint<'tcx>,
actual: &SolverRegionConstraint<'tcx>,
expected_span: Span,
expected: impl std::fmt::Debug,
actual_span: Option<Span>,
actual: impl std::fmt::Debug,
) {
let mut err = tcx.dcx().struct_span_err(
span_of(expected).unwrap_or(fallback_span),
"forall expect clause failed",
);
if let Some(actual_span) = span_of(actual) {
let mut err = tcx.dcx().struct_span_err(expected_span, "forall expect clause failed");
if let Some(actual_span) = actual_span {
err.span_note(actual_span, "constraint from here");
}
err.note(format!("expected: {expected:?}"));
err.note(format!("actual: {actual:?}"));
err.emit();
}
match (expected, actual) {
(
SolverRegionConstraint::And(expected_arr),
SolverRegionConstraint::And(actual_arr),
)
| (SolverRegionConstraint::Or(expected_arr), SolverRegionConstraint::Or(actual_arr)) => {
if expected_arr.len() != actual_arr.len() {
err(self.tcx(), fallback_span, expected, actual);
} else {
for (expected, actual) in expected_arr.iter().zip(actual_arr) {
self.check_test_binder_region_constraints(fallback_span, expected, actual);
}

let span_of_and = |c: &And<_, _>| {
c.0.iter().map(|leaf| leaf.span()).reduce(|span: Span, acc| acc.to(span))
};

let span_of_or = |c: &Or<_, _>| {
c.0.iter().flat_map(|and| span_of_and(and)).reduce(|span, acc| acc.to(span))
};

let check_leaf_constraint =
|expected: LeafRegionConstraint<_, _>, actual: LeafRegionConstraint<_, _>| {
if expected.clone().without_span() != actual.clone().without_span() {
err(self.tcx(), expected.span(), expected, Some(actual.span()), actual);
}
};

let check_and_constraint = |expected: And<_, _>, actual: And<_, _>| {
if expected.0.len() != actual.0.len() {
err(
self.tcx(),
span_of_and(&expected).unwrap_or(fallback_span),
expected,
span_of_and(&actual),
actual,
)
} else {
for (expected, actual) in expected.0.into_iter().zip(actual.0.into_iter()) {
check_leaf_constraint(expected, actual);
}
}
_ if expected.clone().without_spans() != actual.clone().without_spans() => {
err(self.tcx(), fallback_span, expected, actual);
};

let check_or_constraint = |expected: Or<_, _>, actual: Or<_, _>| {
if expected.0.len() != actual.0.len() {
err(
self.tcx(),
span_of_or(&expected).unwrap_or(fallback_span),
expected,
span_of_or(&actual),
actual,
)
} else {
for (expected, actual) in expected.0.into_iter().zip(actual.0.into_iter()) {
check_and_constraint(expected, actual);
}
}
_ => (),
}
};

check_or_constraint(expected.or_constraint.clone(), actual.or_constraint.clone());
check_and_constraint(expected.and_constraint.clone(), actual.and_constraint.clone());
}

#[instrument(level = "debug", skip(self))]
Expand Down
27 changes: 17 additions & 10 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ use rustc_trait_selection::traits::{
FulfillmentError, ObligationCtxt, hir_ty_lowering_dyn_compatibility_violations,
};
use tracing::{debug, instrument};
use ty::region_constraint::LeafRegionConstraint;

use crate::check::wfcheck::{TestBinderBody, TestBinderExists, TestBinderForall};
use crate::diagnostics::{self, ElidedLifetimesAreNotAllowedInDelegations};
Expand Down Expand Up @@ -432,19 +433,23 @@ impl<'tcx> ItemCtxt<'tcx> {
constraint: &hir::TestBinderConstraint<'tcx>,
) -> SolverRegionConstraint<'tcx> {
match constraint {
hir::TestBinderConstraint::And { items } => {
ty::region_constraint::RegionConstraint::And(
items.iter().map(|i| self.lower_test_binder_constraint(i)).collect(),
)
}
hir::TestBinderConstraint::Or { items } => ty::region_constraint::RegionConstraint::Or(
items.iter().map(|i| self.lower_test_binder_constraint(i)).collect(),
),
hir::TestBinderConstraint::And { items } => items
.into_iter()
.map(|item| self.lower_test_binder_constraint(item))
.reduce(SolverRegionConstraint::build_and)
.unwrap_or(SolverRegionConstraint::new_true()),
hir::TestBinderConstraint::Or { items } => items
.into_iter()
.map(|item| self.lower_test_binder_constraint(item))
.reduce(SolverRegionConstraint::build_or)
.unwrap_or(SolverRegionConstraint::new_false()),
hir::TestBinderConstraint::Lifetime { lhs, rhs } => {
let span = lhs.ident.span.to(rhs.ident.span);
let lhs = self.lowerer().lower_lifetime(lhs, RegionInferReason::RegionPredicate);
let rhs = self.lowerer().lower_lifetime(rhs, RegionInferReason::RegionPredicate);
ty::region_constraint::RegionConstraint::RegionOutlives(lhs, rhs, span)
SolverRegionConstraint::new_leaf(LeafRegionConstraint::RegionOutlives(
lhs, rhs, span,
))
}
hir::TestBinderConstraint::Type { lhs, rhs } => {
let span = lhs.span.to(rhs.ident.span);
Expand All @@ -453,7 +458,9 @@ impl<'tcx> ItemCtxt<'tcx> {
// note that we cannot check that lhs is a placeholder at this moment, as at this
// point it is a bound variable that is not yet instantiated with a placeholder.
// instead, we check it when we emit the region constraint.
ty::region_constraint::RegionConstraint::PlaceholderTyOutlives(lhs, rhs, span)
SolverRegionConstraint::new_leaf(LeafRegionConstraint::PlaceholderTyOutlives(
lhs, rhs, span,
))
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_infer/src/infer/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
constraint: rustc_type_ir::region_constraint::RegionConstraint<TyCtxt<'tcx>>,
span: Span,
) {
self.overwrite_solver_region_constraint(constraint.with_span(span));
self.overwrite_solver_region_constraint(constraint.with_spans(span));
}

fn universe_of_ty(&self, vid: ty::TyVid) -> Option<ty::UniverseIndex> {
Expand Down Expand Up @@ -328,7 +328,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
c: rustc_type_ir::region_constraint::RegionConstraint<TyCtxt<'tcx>>,
span: Span,
) {
self.register_solver_region_constraint(c.with_span(span));
self.register_solver_region_constraint(c.with_spans(span));
}

fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>, span: Span) {
Expand Down
49 changes: 32 additions & 17 deletions compiler/rustc_infer/src/infer/outlives/obligations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ use rustc_middle::ty::{
TyCtxt, TypeVisitableExt, eager_resolve_vars,
};
use rustc_span::Span;
use rustc_type_ir::region_constraint::{self, LeafRegionConstraint};
use smallvec::smallvec;
use tracing::{debug, instrument};

Expand Down Expand Up @@ -142,9 +143,18 @@ impl<'tcx> InferCtxt<'tcx> {

pub fn register_solver_region_constraint(&self, c: SolverRegionConstraint<'tcx>) {
let mut inner = self.inner.borrow_mut();
let previous_was_and = inner.solver_region_constraint_storage.is_and();
inner.undo_log.push(UndoLog::PushSolverRegionConstraint { previous_was_and });
inner.solver_region_constraint_storage.push(c);

let old_constraint = inner.solver_region_constraint_storage.get_constraint();
let new_constraint = rustc_type_ir::region_constraint::RegionConstraint::build_and(
c,
old_constraint.clone(),
);

// FIXME(-Zassumptions-on-binders): This is pretty bad for perf, we don't make incremental
// changes to the region constraints, instead we just rewrite the entire thing every time
// and store the old version.
inner.undo_log.push(UndoLog::OverwriteSolverRegionConstraint { old_constraint });
Comment thread
BoxyUwU marked this conversation as resolved.
inner.solver_region_constraint_storage.overwrite(new_constraint);
}

pub fn register_type_outlives_constraint(
Expand Down Expand Up @@ -238,7 +248,7 @@ impl<'tcx> InferCtxt<'tcx> {
known_type_outlives: &[PolyTypeOutlivesClause<'tcx>],
region_outlives: TransitiveRelation<RegionVid>,
) {
let assumptions = rustc_type_ir::region_constraint::Assumptions::new(
let assumptions = region_constraint::Assumptions::new(
known_type_outlives.into_iter().cloned().collect(),
region_outlives.maybe_map(|r| Some(Region::new_var(self.tcx, r))).unwrap(),
);
Expand All @@ -256,19 +266,24 @@ impl<'tcx> InferCtxt<'tcx> {

let constraint = self.inner.borrow().solver_region_constraint_storage.get_constraint();
debug!(?constraint);
let constraint =
rustc_type_ir::region_constraint::destructure_type_outlives_constraints_in_root(
self,
constraint,
&assumptions,
);
let constraint = region_constraint::destructure_type_outlives_constraints_in_root(
self,
constraint,
&assumptions,
);
debug!(?constraint);
let constraint = rustc_type_ir::region_constraint::evaluate_solver_constraint(&constraint);
let constraint = region_constraint::propagate_ambiguity(constraint);
debug!(?constraint);

let mut constraints = vec![constraint];
while let Some(c) = constraints.pop() {
use rustc_type_ir::region_constraint::RegionConstraint::*;
// FIXME(-Zassumptions-on-binders): actually implement OR as an OR
for c in constraint.and_constraint.0.into_iter().chain(
constraint
.or_constraint
.0
.into_iter()
.flat_map(|and_constraint| and_constraint.0.into_iter()),
) {
use LeafRegionConstraint::*;

match c {
Ambiguity(span) => {
Expand All @@ -287,9 +302,9 @@ impl<'tcx> InferCtxt<'tcx> {
b, a, category,
);
}
// FIXME(-Zassumptions-on-binders): actually implement OR as an OR
And(nested) | Or(nested) => constraints.extend(nested),
AliasTyOutlivesViaEnv(..) | PlaceholderTyOutlives(..) => unreachable!(),
AliasTyOutlivesViaEnv(..) | PlaceholderTyOutlives(..) => {
unreachable!()
}
}
}
}
Expand Down
9 changes: 0 additions & 9 deletions compiler/rustc_infer/src/infer/snapshot/undo_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ pub(crate) enum UndoLog<'tcx> {
RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
ProjectionCache(traits::UndoLog<'tcx>),
PushTypeOutlivesConstraint,
PushSolverRegionConstraint { previous_was_and: bool },
OverwriteSolverRegionConstraint { old_constraint: SolverRegionConstraint<'tcx> },
PushRegionAssumption,
PushHirTypeckPotentiallyRegionDependentGoal,
Expand Down Expand Up @@ -79,14 +78,6 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo)
}
UndoLog::ProjectionCache(undo) => self.projection_cache.reverse(undo),
UndoLog::PushSolverRegionConstraint { previous_was_and } => {
let popped = self.solver_region_constraint_storage.pop(previous_was_and);
assert_matches!(
popped,
Some(_),
"pushed solver region constraint but could not pop it"
);
}
UndoLog::OverwriteSolverRegionConstraint { old_constraint } => {
self.solver_region_constraint_storage.overwrite(old_constraint);
}
Expand Down
Loading
Loading