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
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {

#[instrument(skip(self), level = "debug")]
pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) {
let QueryRegionConstraints { constraints, assumptions } = query_constraints;
let QueryRegionConstraints { constraints, assumptions, solver_constraints } =
query_constraints;
let assumptions =
elaborate::elaborate_outlives_assumptions(self.infcx.tcx, assumptions.iter().copied());

Expand All @@ -77,6 +78,10 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
self.convert(predicate, category, &assumptions);
});
}

if !solver_constraints.is_true() {
self.infcx.add_solver_region_constraint(solver_constraints.clone(), self.span);
}
}

/// Given an instance of the closure type, this method instantiates the "extra" requirements
Expand Down
23 changes: 21 additions & 2 deletions compiler/rustc_infer/src/infer/canonical/query_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,14 @@ impl<'tcx> InferCtxt<'tcx> {
let region_obligations = self.take_registered_region_obligations();
let region_assumptions = self.take_registered_region_assumptions();
debug!(?region_obligations);
let region_constraints = self.with_region_constraints(|region_constraints| {
let mut region_constraints = self.with_region_constraints(|region_constraints| {
make_query_region_constraints(
region_obligations,
region_constraints,
region_assumptions,
)
});
region_constraints.solver_constraints = self.clone_solver_region_constraints();
debug!(?region_constraints);

let opaque_types = self
Expand Down Expand Up @@ -214,6 +215,15 @@ impl<'tcx> InferCtxt<'tcx> {
self.register_region_assumption(assumption);
}

let solver_constraints = instantiate_value(
self.tcx,
&result_args,
query_response.value.region_constraints.solver_constraints.clone(),
);
if !solver_constraints.is_true() {
self.add_solver_region_constraint(solver_constraints, cause.span);
}

let user_result: R =
query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone());

Expand Down Expand Up @@ -347,6 +357,15 @@ impl<'tcx> InferCtxt<'tcx> {
.map(|&r_c| instantiate_value(self.tcx, &result_args, r_c)),
);

let solver_constraints = instantiate_value(
self.tcx,
&result_args,
query_response.value.region_constraints.solver_constraints.clone(),
);
output_query_region_constraints.solver_constraints =
std::mem::take(&mut output_query_region_constraints.solver_constraints)
.and(solver_constraints);

let user_result: R =
query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone());

Expand Down Expand Up @@ -663,5 +682,5 @@ pub fn make_query_region_constraints<'tcx>(
))
.collect();

QueryRegionConstraints { constraints, assumptions }
QueryRegionConstraints { constraints, assumptions, solver_constraints: Default::default() }
}
8 changes: 1 addition & 7 deletions compiler/rustc_infer/src/infer/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,13 +334,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
c: rustc_type_ir::region_constraint::RegionConstraint<TyCtxt<'tcx>>,
span: Span,
) {
let mut inner = self.inner.borrow_mut();
use rustc_data_structures::undo_log::UndoLogs;

use crate::infer::UndoLog;
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, span);
self.add_solver_region_constraint(c, span);
}

fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>, span: Span) {
Expand Down
45 changes: 45 additions & 0 deletions compiler/rustc_infer/src/infer/solver_region_constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use rustc_type_ir::region_constraint::{
};
use tracing::instrument;

use super::InferCtxt;

pub(crate) type SolverRegionConstraint<'tcx> = SpannedRegionConstraint<TyCtxt<'tcx>>;

#[derive(Clone, Debug)]
Expand All @@ -23,6 +25,10 @@ impl<'tcx> SolverRegionConstraintStorage<'tcx> {
self.0.clone().without_spans()
}

fn take(&mut self) -> SolverRegionConstraint<'tcx> {
core::mem::take(&mut self.0)
}

pub(crate) fn is_and(&self) -> bool {
self.0.is_and()
}
Expand Down Expand Up @@ -73,5 +79,44 @@ impl<'tcx> SolverRegionConstraintStorage<'tcx> {
}
}

impl<'tcx> InferCtxt<'tcx> {
pub fn add_solver_region_constraint(
&self,
constraint: UnspannedRegionConstraint<TyCtxt<'tcx>>,
span: Span,
) {
use rustc_data_structures::undo_log::UndoLogs;

use super::UndoLog;

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(constraint, span);
}

pub(crate) fn clone_solver_region_constraints(
&self,
) -> UnspannedRegionConstraint<TyCtxt<'tcx>> {
self.inner.borrow().solver_region_constraint_storage.get_unspanned_constraint()
}

/// Runs `op` with an empty solver-region-constraint store, restores the
/// caller's constraints, and returns the constraints produced by `op`.
pub fn with_fresh_solver_region_constraints<R>(
&self,
op: impl FnOnce() -> R,
) -> (R, UnspannedRegionConstraint<TyCtxt<'tcx>>) {
assert!(!self.in_snapshot(), "cannot isolate solver region constraints in a snapshot");

let previous = self.inner.borrow_mut().solver_region_constraint_storage.take();
let result = op();
let current = self.inner.borrow_mut().solver_region_constraint_storage.take();
self.inner.borrow_mut().solver_region_constraint_storage.overwrite_spanned(previous);

(result, current.without_spans())
}
}

#[cfg(test)]
mod tests;
22 changes: 19 additions & 3 deletions compiler/rustc_middle/src/infer/canonical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,21 @@ pub struct QueryResponse<'tcx, R> {
pub value: R,
}

#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, Default, PartialEq, Hash)]
#[derive(StableHash, TypeFoldable, TypeVisitable)]
pub struct QueryRegionConstraints<'tcx> {
pub constraints: Vec<QueryRegionConstraint<'tcx>>,
pub assumptions: Vec<ty::ArgOutlivesClause<'tcx>>,
/// Region constraints emitted by the next solver under
/// `-Zassumptions-on-binders`.
///
/// These stay unspanned while passing through a canonical query. The type-op
/// caller attaches its origin span when consuming the response.
pub solver_constraints: ir::region_constraint::RegionConstraint<TyCtxt<'tcx>>,
}

impl Eq for QueryRegionConstraints<'_> {}

impl QueryRegionConstraints<'_> {
/// Represents an empty (trivially true) set of region constraints.
///
Expand All @@ -91,8 +99,16 @@ 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 {
let QueryRegionConstraints { constraints, assumptions } = self;
constraints.is_empty() && assumptions.is_empty()
self.constraints.is_empty()
&& self.assumptions.is_empty()
&& self.solver_constraints.is_true()
}

pub fn extend(&mut self, other: &Self) {
self.constraints.extend(other.constraints.iter().cloned());
self.assumptions.extend(other.assumptions.iter().cloned());
self.solver_constraints =
std::mem::take(&mut self.solver_constraints).and(other.solver_constraints.clone());
}
}

Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ 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 { constraints, assumptions: _ } = constraints;
let QueryRegionConstraints { constraints, solver_constraints, .. } = constraints;
if !solver_constraints.is_true() {
infcx.add_solver_region_constraint(solver_constraints, span);
}

let cause = ObligationCause::misc(span, body_def_id);
for &QueryRegionConstraint { constraint, visible_for_leak_check: vis, .. } in &constraints {
match constraint {
Expand Down
46 changes: 26 additions & 20 deletions compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ impl<F> fmt::Debug for CustomTypeOp<F> {
}
}

/// Executes `op` and then scrapes out all the "old style" region
/// constraints that result, creating query-region-constraints.
/// Executes `op` and then scrapes out all resulting region constraints,
/// creating query-region-constraints.
pub fn scrape_region_constraints<'tcx, Op, R>(
infcx: &InferCtxt<'tcx>,
root_def_id: LocalDefId,
Expand Down Expand Up @@ -89,10 +89,11 @@ where
"scrape_region_constraints: incoming region assumptions = {pre_assumptions:#?}",
);

let value = infcx.commit_if_ok(|_| {
let ocx = ObligationCtxt::new(infcx);
let value = op(&ocx).map_err(|_| {
infcx.tcx.check_potentially_region_dependent_goals(root_def_id).err().unwrap_or_else(
let (value, solver_constraints) = infcx.with_fresh_solver_region_constraints(|| {
infcx.commit_if_ok(|_| {
let ocx = ObligationCtxt::new(infcx);
let value = op(&ocx).map_err(|_| {
infcx.tcx.check_potentially_region_dependent_goals(root_def_id).err().unwrap_or_else(
// FIXME: In this region-dependent context, `type_op` should only fail due to
// region-dependent goals. Any other kind of failure indicates a bug and we
// should ICE.
Expand Down Expand Up @@ -125,31 +126,36 @@ where
.dcx()
.span_delayed_bug(span, format!("error performing operation: {name}"))
},
)
})?;
let errors = ocx.evaluate_obligations_error_on_ambiguity();
if errors.no_errors() {
Ok(value)
} else if let Err(guar) = infcx.tcx.check_potentially_region_dependent_goals(root_def_id) {
Err(guar)
} else {
Err(infcx.dcx().delayed_bug(format!(
"errors selecting obligation during MIR typeck: {name} {root_def_id:?} {errors:?}"
)))
}
})?;
)
})?;
let errors = ocx.evaluate_obligations_error_on_ambiguity();
if errors.no_errors() {
Ok(value)
} else if let Err(guar) =
infcx.tcx.check_potentially_region_dependent_goals(root_def_id)
{
Err(guar)
} else {
Err(infcx.dcx().delayed_bug(format!(
"errors selecting obligation during MIR typeck: {name} {root_def_id:?} {errors:?}"
)))
}
})
});
let value = value?;

// Next trait solver performs operations locally, and normalize goals should resolve vars.
let value = infcx.resolve_vars_if_possible(value);

let region_obligations = infcx.take_registered_region_obligations();
let region_assumptions = infcx.take_registered_region_assumptions();
let region_constraint_data = infcx.take_and_reset_region_constraints();
let region_constraints = query_response::make_query_region_constraints(
let mut region_constraints = query_response::make_query_region_constraints(
region_obligations,
&region_constraint_data,
region_assumptions,
);
region_constraints.solver_constraints = solver_constraints;

if region_constraints.is_empty() {
Ok((
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,8 @@ where
Ok(output)
})?;
output.error_info = error_info;
if let Some(QueryRegionConstraints { constraints, assumptions }) = output.constraints {
region_constraints.constraints.extend(constraints.iter().cloned());
region_constraints.assumptions.extend(assumptions.iter().cloned());
if let Some(constraints) = output.constraints {
region_constraints.extend(constraints);
}
output.constraints = if region_constraints.is_empty() {
None
Expand Down
28 changes: 27 additions & 1 deletion tests/ui/assumptions_on_binders/alias_outlives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ where
}

fn borrowck_env_fail<'a, T: AliasHaver>()
// FIXME: ^ this should raise an ERROR: unsatisfied lifetime constraint from -Zassumptions-on-binders
where
<T as AliasHaver>::Assoc: 'a,
{
let _: ReqTrait<T::Assoc>;
//~^ ERROR: higher-ranked lifetime bound could not be satisfied
}

const REGIONCK_ENV_PASS<'a, T: AliasHaver>: ReqTrait<T::Assoc> = todo!()
Expand All @@ -39,4 +39,30 @@ const REGIONCK_ENV_FAIL<'a, T: AliasHaver>: ReqTrait<T::Assoc> = todo!()
where
<T as AliasHaver>::Assoc: 'a;

// Solver constraints produced while normalizing implied bounds must be returned
// to lexical regionck.
trait Project {
type Assoc;
}

impl<T: AliasHaver> Project for (T,)
where
T::Assoc: for<'a> Trait<'a>,
{
type Assoc = ();
}

struct Normalizes<T: Project>(T)
where
T::Assoc: Clone;

trait TestTrait {}

impl<'a, T: AliasHaver> TestTrait for [Normalizes<(T,)>; 1]
//~^ ERROR: higher-ranked lifetime bound could not be satisfied
where
T::Assoc: 'a,
{
}

fn main() {}
17 changes: 16 additions & 1 deletion tests/ui/assumptions_on_binders/alias_outlives.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,20 @@ error: higher-ranked lifetime bound could not be satisfied
LL | const REGIONCK_ENV_FAIL<'a, T: AliasHaver>: ReqTrait<T::Assoc> = todo!()
| ^^^^^^^^^^^^^^^^^^

error: aborting due to 1 previous error
error: higher-ranked lifetime bound could not be satisfied
--> $DIR/alias_outlives.rs:61:1
|
LL | / impl<'a, T: AliasHaver> TestTrait for [Normalizes<(T,)>; 1]
LL | |
LL | | where
LL | | T::Assoc: 'a,
| |_________________^

error: higher-ranked lifetime bound could not be satisfied
--> $DIR/alias_outlives.rs:29:12
|
LL | let _: ReqTrait<T::Assoc>;
| ^^^^^^^^^^^^^^^^^^

error: aborting due to 3 previous errors

Loading