Skip to content

Commit

Permalink
try_normalize_ty end with rigid alias on failure
Browse files Browse the repository at this point in the history
  • Loading branch information
lcnr committed Oct 27, 2023
1 parent ccb160d commit b5a0d26
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 30 deletions.
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/traits/solve/inspect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ pub enum ProbeStep<'tcx> {
/// used whenever there are multiple candidates to prove the
/// current goalby .
NestedProbe(Probe<'tcx>),
CommitIfOkStart,
CommitIfOkSuccess,
}

/// What kind of probe we're in. In case the probe represents a candidate, or
Expand All @@ -148,6 +150,9 @@ pub enum ProbeKind<'tcx> {
/// Used in the probe that wraps normalizing the non-self type for the unsize
/// trait, which is also structurally matched on.
UnsizeAssembly,
/// A call to `EvalCtxt::commit_if_ok` which failed, causing the work
/// to be discarded.
CommitIfOk,
/// During upcasting from some source object to target object type, used to
/// do a probe to find out what projection type(s) may be used to prove that
/// the source type upholds all of the target type's object bounds.
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/traits/solve/inspect/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
ProbeKind::UpcastProjectionCompatibility => {
writeln!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:")
}
ProbeKind::CommitIfOk => {
writeln!(self.f, "COMMIT_IF_OK:")
}
ProbeKind::MiscCandidate { name, result } => {
writeln!(self.f, "CANDIDATE {name}: {result:?}")
}
Expand All @@ -126,6 +129,8 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
ProbeStep::AddGoal(goal) => writeln!(this.f, "ADDED GOAL: {goal:?}")?,
ProbeStep::EvaluateGoals(eval) => this.format_added_goals_evaluation(eval)?,
ProbeStep::NestedProbe(probe) => this.format_probe(probe)?,
ProbeStep::CommitIfOkStart => writeln!(this.f, "COMMIT_IF_OK START")?,
ProbeStep::CommitIfOkSuccess => writeln!(this.f, "COMMIT_IF_OK SUCCESS")?,
}
}
Ok(())
Expand Down
15 changes: 5 additions & 10 deletions compiler/rustc_trait_selection/src/solve/assembly/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -852,23 +852,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {

let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| {
let trait_ref = goal.predicate.trait_ref(tcx);

#[derive(Debug)]
enum FailureKind {
Overflow,
NoSolution(NoSolution),
}
struct Overflow;
let lazily_normalize_ty = |ty| match ecx.try_normalize_ty(goal.param_env, ty) {
Ok(Some(ty)) => Ok(ty),
Ok(None) => Err(FailureKind::Overflow),
Err(e) => Err(FailureKind::NoSolution(e)),
Some(ty) => Ok(ty),
None => Err(Overflow),
};

match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty) {
Err(FailureKind::Overflow) => {
Err(Overflow) => {
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW)
}
Err(FailureKind::NoSolution(NoSolution)) | Ok(Ok(())) => Err(NoSolution),
Ok(Ok(())) => Err(NoSolution),
Ok(Err(_)) => {
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
Expand Down
45 changes: 45 additions & 0 deletions compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use super::EvalCtxt;
use crate::solve::inspect;
use rustc_middle::traits::query::NoSolution;

impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
pub(in crate::solve) fn commit_if_ok<T>(
&mut self,
f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> Result<T, NoSolution>,
) -> Result<T, NoSolution> {
let mut nested_ecx = EvalCtxt {
infcx: self.infcx,
variables: self.variables,
var_values: self.var_values,
predefined_opaques_in_body: self.predefined_opaques_in_body,
max_input_universe: self.max_input_universe,
search_graph: self.search_graph,
nested_goals: self.nested_goals.clone(),
tainted: self.tainted,
inspect: self.inspect.new_probe(),
};

let result = nested_ecx.infcx.commit_if_ok(|_| f(&mut nested_ecx));
if result.is_ok() {
let EvalCtxt {
infcx: _,
variables: _,
var_values: _,
predefined_opaques_in_body: _,
max_input_universe: _,
search_graph: _,
nested_goals,
tainted,
inspect,
} = nested_ecx;
self.nested_goals = nested_goals;
self.tainted = tainted;
self.inspect.integrate_snapshot(inspect);
} else {
nested_ecx.inspect.probe_kind(inspect::ProbeKind::CommitIfOk);
self.inspect.finish_probe(nested_ecx.inspect);
}

result
}
}
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use super::{search_graph::SearchGraph, Goal};
pub use select::InferCtxtSelectExt;

mod canonical;
mod commit_if_ok;
mod probe;
mod select;

Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
for step in &probe.steps {
match step {
&inspect::ProbeStep::AddGoal(goal) => nested_goals.push(goal),
inspect::ProbeStep::EvaluateGoals(_) => (),
inspect::ProbeStep::NestedProbe(ref probe) => {
// Nested probes have to prove goals added in their parent
// but do not leak them, so we truncate the added goals
Expand All @@ -130,13 +129,17 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
self.candidates_recur(candidates, nested_goals, probe);
nested_goals.truncate(num_goals);
}
inspect::ProbeStep::EvaluateGoals(_)
| inspect::ProbeStep::CommitIfOkStart
| inspect::ProbeStep::CommitIfOkSuccess => (),
}
}

match probe.kind {
inspect::ProbeKind::NormalizedSelfTyAssembly
| inspect::ProbeKind::UnsizeAssembly
| inspect::ProbeKind::UpcastProjectionCompatibility => (),
| inspect::ProbeKind::UpcastProjectionCompatibility
| inspect::ProbeKind::CommitIfOk => (),
// We add a candidate for the root evaluation if there
// is only one way to prove a given goal, e.g. for `WellFormed`.
//
Expand Down
27 changes: 27 additions & 0 deletions compiler/rustc_trait_selection/src/solve/inspect/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ enum WipProbeStep<'tcx> {
AddGoal(inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>),
EvaluateGoals(WipAddedGoalsEvaluation<'tcx>),
NestedProbe(WipProbe<'tcx>),
CommitIfOkStart,
CommitIfOkSuccess,
}

impl<'tcx> WipProbeStep<'tcx> {
Expand All @@ -221,6 +223,8 @@ impl<'tcx> WipProbeStep<'tcx> {
WipProbeStep::AddGoal(goal) => inspect::ProbeStep::AddGoal(goal),
WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()),
WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()),
WipProbeStep::CommitIfOkStart => inspect::ProbeStep::CommitIfOkStart,
WipProbeStep::CommitIfOkSuccess => inspect::ProbeStep::CommitIfOkSuccess,
}
}
}
Expand Down Expand Up @@ -458,6 +462,29 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
}
}

/// Used by `EvalCtxt::commit_if_ok` to flatten the work done inside
/// of the probe into the parent.
pub fn integrate_snapshot(&mut self, probe: ProofTreeBuilder<'tcx>) {
if let Some(this) = self.as_mut() {
match (this, probe.state.unwrap().tree) {
(
DebugSolver::Probe(WipProbe { steps, .. })
| DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
evaluation: WipProbe { steps, .. },
..
}),
DebugSolver::Probe(probe),
) => {
steps.push(WipProbeStep::CommitIfOkStart);
assert_eq!(probe.kind, None);
steps.extend(probe.steps);
steps.push(WipProbeStep::CommitIfOkSuccess);
}
_ => unreachable!(),
}
}
}

pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> {
self.nested(|| WipAddedGoalsEvaluation { evaluations: vec![], result: None })
}
Expand Down
43 changes: 29 additions & 14 deletions compiler/rustc_trait_selection/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,25 +299,40 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
fn try_normalize_ty(
&mut self,
param_env: ty::ParamEnv<'tcx>,
mut ty: Ty<'tcx>,
) -> Result<Option<Ty<'tcx>>, NoSolution> {
for _ in 0..self.local_overflow_limit() {
let ty::Alias(_, projection_ty) = *ty.kind() else {
return Ok(Some(ty));
};

let normalized_ty = self.next_ty_infer();
ty: Ty<'tcx>,
) -> Option<Ty<'tcx>> {
self.try_normalize_ty_recur(param_env, 0, ty)
}

fn try_normalize_ty_recur(
&mut self,
param_env: ty::ParamEnv<'tcx>,
depth: usize,
ty: Ty<'tcx>,
) -> Option<Ty<'tcx>> {
if depth >= self.local_overflow_limit() {
return None;
}

let ty::Alias(_, projection_ty) = *ty.kind() else {
return Some(ty);
};

match self.commit_if_ok(|this| {
let normalized_ty = this.next_ty_infer();
let normalizes_to_goal = Goal::new(
self.tcx(),
this.tcx(),
param_env,
ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() },
);
self.add_goal(normalizes_to_goal);
self.try_evaluate_added_goals()?;
ty = self.resolve_vars_if_possible(normalized_ty);
this.add_goal(normalizes_to_goal);
this.try_evaluate_added_goals()?;
let ty = this.resolve_vars_if_possible(normalized_ty);
Ok(this.try_normalize_ty_recur(param_env, depth + 1, ty))
}) {
Ok(ty) => ty,
Err(NoSolution) => Some(ty),
}

Ok(None)
}
}

Expand Down
7 changes: 3 additions & 4 deletions compiler/rustc_trait_selection/src/solve/trait_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
let a_ty = goal.predicate.self_ty();
// We need to normalize the b_ty since it's destructured as a `dyn Trait`.
let Some(b_ty) =
ecx.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))?
ecx.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))
else {
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW);
};
Expand Down Expand Up @@ -499,9 +499,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
let b_ty = match ecx
.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))
{
Ok(Some(b_ty)) => b_ty,
Ok(None) => return vec![misc_candidate(ecx, Certainty::OVERFLOW)],
Err(_) => return vec![],
Some(b_ty) => b_ty,
None => return vec![misc_candidate(ecx, Certainty::OVERFLOW)],
};

let goal = goal.with(ecx.tcx(), (a_ty, b_ty));
Expand Down

0 comments on commit b5a0d26

Please sign in to comment.