normalizes-to change from '1' to '0 to inf' steps
This commit is contained in:
parent
d99c775feb
commit
92b280ce81
12 changed files with 148 additions and 296 deletions
|
@ -121,8 +121,6 @@ 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
|
||||
|
@ -132,6 +130,8 @@ pub enum ProbeStep<'tcx> {
|
|||
pub enum ProbeKind<'tcx> {
|
||||
/// The root inference context while proving a goal.
|
||||
Root { result: QueryResult<'tcx> },
|
||||
/// Trying to normalize an alias by at least one stpe in `NormalizesTo`.
|
||||
TryNormalizeNonRigid { result: QueryResult<'tcx> },
|
||||
/// Probe entered when normalizing the self ty during candidate assembly
|
||||
NormalizedSelfTyAssembly,
|
||||
/// Some candidate to prove the current goal.
|
||||
|
@ -143,9 +143,6 @@ 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.
|
||||
|
|
|
@ -100,6 +100,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
|
|||
ProbeKind::Root { result } => {
|
||||
write!(self.f, "ROOT RESULT: {result:?}")
|
||||
}
|
||||
ProbeKind::TryNormalizeNonRigid { result } => {
|
||||
write!(self.f, "TRY NORMALIZE NON-RIGID: {result:?}")
|
||||
}
|
||||
ProbeKind::NormalizedSelfTyAssembly => {
|
||||
write!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:")
|
||||
}
|
||||
|
@ -109,9 +112,6 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
|
|||
ProbeKind::UpcastProjectionCompatibility => {
|
||||
write!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:")
|
||||
}
|
||||
ProbeKind::CommitIfOk => {
|
||||
write!(self.f, "COMMIT_IF_OK:")
|
||||
}
|
||||
ProbeKind::MiscCandidate { name, result } => {
|
||||
write!(self.f, "CANDIDATE {name}: {result:?}")
|
||||
}
|
||||
|
@ -132,8 +132,6 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
|
|||
}
|
||||
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(())
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
//! Doing this via a separate goal is called "deferred alias relation" and part
|
||||
//! of our more general approach to "lazy normalization".
|
||||
//!
|
||||
//! This is done by first normalizing both sides of the goal, ending up in
|
||||
//! either a concrete type, rigid alias, or an infer variable.
|
||||
//! This is done by first structurally normalizing both sides of the goal, ending
|
||||
//! up in either a concrete type, rigid alias, or an infer variable.
|
||||
//! These are related further according to the rules below:
|
||||
//!
|
||||
//! (1.) If we end up with two rigid aliases, then we relate them structurally.
|
||||
|
@ -14,17 +14,10 @@
|
|||
//!
|
||||
//! (3.) Otherwise, if we end with two rigid (non-projection) or infer types,
|
||||
//! relate them structurally.
|
||||
//!
|
||||
//! Subtle: when relating an opaque to another type, we emit a
|
||||
//! `NormalizesTo(opaque, ?fresh_var)` goal when trying to normalize the opaque.
|
||||
//! This nested goal starts out as ambiguous and does not actually define the opaque.
|
||||
//! However, if `?fresh_var` ends up geteting equated to another type, we retry the
|
||||
//! `NormalizesTo` goal, at which point the opaque is actually defined.
|
||||
|
||||
use super::EvalCtxt;
|
||||
use rustc_infer::traits::query::NoSolution;
|
||||
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty;
|
||||
|
||||
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
|
@ -35,21 +28,34 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
let tcx = self.tcx();
|
||||
let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
|
||||
|
||||
let Some(lhs) = self.try_normalize_term(param_env, lhs)? else {
|
||||
return self
|
||||
.evaluate_added_goals_and_make_canonical_response(Certainty::overflow(true));
|
||||
// Structurally normalize the lhs.
|
||||
let lhs = if let Some(alias) = lhs.to_alias_ty(self.tcx()) {
|
||||
let term = self.next_term_infer_of_kind(lhs);
|
||||
self.add_normalizes_to_goal(goal.with(tcx, ty::NormalizesTo { alias, term }));
|
||||
term
|
||||
} else {
|
||||
lhs
|
||||
};
|
||||
|
||||
let Some(rhs) = self.try_normalize_term(param_env, rhs)? else {
|
||||
return self
|
||||
.evaluate_added_goals_and_make_canonical_response(Certainty::overflow(true));
|
||||
// Structurally normalize the rhs.
|
||||
let rhs = if let Some(alias) = rhs.to_alias_ty(self.tcx()) {
|
||||
let term = self.next_term_infer_of_kind(rhs);
|
||||
self.add_normalizes_to_goal(goal.with(tcx, ty::NormalizesTo { alias, term }));
|
||||
term
|
||||
} else {
|
||||
rhs
|
||||
};
|
||||
|
||||
// Apply the constraints.
|
||||
self.try_evaluate_added_goals()?;
|
||||
let lhs = self.resolve_vars_if_possible(lhs);
|
||||
let rhs = self.resolve_vars_if_possible(rhs);
|
||||
debug!(?lhs, ?rhs);
|
||||
|
||||
let variance = match direction {
|
||||
ty::AliasRelationDirection::Equate => ty::Variance::Invariant,
|
||||
ty::AliasRelationDirection::Subtype => ty::Variance::Covariant,
|
||||
};
|
||||
|
||||
match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) {
|
||||
(None, None) => {
|
||||
self.relate(param_env, lhs, variance, rhs)?;
|
||||
|
@ -57,14 +63,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
}
|
||||
|
||||
(Some(alias), None) => {
|
||||
self.relate_rigid_alias_non_alias(param_env, alias, variance, rhs)
|
||||
self.relate_rigid_alias_non_alias(param_env, alias, variance, rhs)?;
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
(None, Some(alias)) => {
|
||||
self.relate_rigid_alias_non_alias(
|
||||
param_env,
|
||||
alias,
|
||||
variance.xform(ty::Variance::Contravariant),
|
||||
lhs,
|
||||
)?;
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
(None, Some(alias)) => self.relate_rigid_alias_non_alias(
|
||||
param_env,
|
||||
alias,
|
||||
variance.xform(ty::Variance::Contravariant),
|
||||
lhs,
|
||||
),
|
||||
|
||||
(Some(alias_lhs), Some(alias_rhs)) => {
|
||||
self.relate(param_env, alias_lhs, variance, alias_rhs)?;
|
||||
|
@ -72,97 +82,4 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Relate a rigid alias with another type. This is the same as
|
||||
/// an ordinary relate except that we treat the outer most alias
|
||||
/// constructor as rigid.
|
||||
#[instrument(level = "debug", skip(self, param_env), ret)]
|
||||
fn relate_rigid_alias_non_alias(
|
||||
&mut self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
alias: ty::AliasTy<'tcx>,
|
||||
variance: ty::Variance,
|
||||
term: ty::Term<'tcx>,
|
||||
) -> QueryResult<'tcx> {
|
||||
// NOTE: this check is purely an optimization, the structural eq would
|
||||
// always fail if the term is not an inference variable.
|
||||
if term.is_infer() {
|
||||
let tcx = self.tcx();
|
||||
// We need to relate `alias` to `term` treating only the outermost
|
||||
// constructor as rigid, relating any contained generic arguments as
|
||||
// normal. We do this by first structurally equating the `term`
|
||||
// with the alias constructor instantiated with unconstrained infer vars,
|
||||
// and then relate this with the whole `alias`.
|
||||
//
|
||||
// Alternatively we could modify `Equate` for this case by adding another
|
||||
// variant to `StructurallyRelateAliases`.
|
||||
let identity_args = self.fresh_args_for_item(alias.def_id);
|
||||
let rigid_ctor = ty::AliasTy::new(tcx, alias.def_id, identity_args);
|
||||
self.eq_structurally_relating_aliases(param_env, term, rigid_ctor.to_ty(tcx).into())?;
|
||||
self.eq(param_env, alias, rigid_ctor)?;
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This needs a name that reflects that it's okay to bottom-out with an inference var.
|
||||
/// Normalize the `term` to equate it later.
|
||||
#[instrument(level = "debug", skip(self, param_env), ret)]
|
||||
fn try_normalize_term(
|
||||
&mut self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
term: ty::Term<'tcx>,
|
||||
) -> Result<Option<ty::Term<'tcx>>, NoSolution> {
|
||||
match term.unpack() {
|
||||
ty::TermKind::Ty(ty) => {
|
||||
Ok(self.try_normalize_ty_recur(param_env, 0, ty).map(Into::into))
|
||||
}
|
||||
ty::TermKind::Const(_) => {
|
||||
if let Some(alias) = term.to_alias_ty(self.tcx()) {
|
||||
let term = self.next_term_infer_of_kind(term);
|
||||
self.add_normalizes_to_goal(Goal::new(
|
||||
self.tcx(),
|
||||
param_env,
|
||||
ty::NormalizesTo { alias, term },
|
||||
));
|
||||
self.try_evaluate_added_goals()?;
|
||||
Ok(Some(self.resolve_vars_if_possible(term)))
|
||||
} else {
|
||||
Ok(Some(term))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, param_env), ret)]
|
||||
fn try_normalize_ty_recur(
|
||||
&mut self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
depth: usize,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Option<Ty<'tcx>> {
|
||||
if !self.tcx().recursion_limit().value_within_limit(depth) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ty::Alias(_, alias) = *ty.kind() else {
|
||||
return Some(ty);
|
||||
};
|
||||
|
||||
match self.commit_if_ok(|this| {
|
||||
let tcx = this.tcx();
|
||||
let normalized_ty = this.next_ty_infer();
|
||||
this.add_normalizes_to_goal(Goal::new(
|
||||
tcx,
|
||||
param_env,
|
||||
ty::NormalizesTo { alias, term: normalized_ty.into() },
|
||||
));
|
||||
this.try_evaluate_added_goals()?;
|
||||
Ok(this.resolve_vars_if_possible(normalized_ty))
|
||||
}) {
|
||||
Ok(ty) => self.try_normalize_ty_recur(param_env, depth + 1, ty),
|
||||
Err(NoSolution) => Some(ty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -332,7 +332,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
/// whether an alias is rigid by using the trait solver. When instantiating a response
|
||||
/// from the solver we assume that the solver correctly handled aliases and therefore
|
||||
/// always relate them structurally here.
|
||||
#[instrument(level = "debug", skip(infcx), ret)]
|
||||
#[instrument(level = "debug", skip(infcx))]
|
||||
fn unify_query_var_values(
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
use super::{EvalCtxt, NestedGoals};
|
||||
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,
|
||||
is_normalizes_to_goal: self.is_normalizes_to_goal,
|
||||
predefined_opaques_in_body: self.predefined_opaques_in_body,
|
||||
max_input_universe: self.max_input_universe,
|
||||
search_graph: self.search_graph,
|
||||
nested_goals: NestedGoals::new(),
|
||||
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: _,
|
||||
is_normalizes_to_goal: _,
|
||||
predefined_opaques_in_body: _,
|
||||
max_input_universe: _,
|
||||
search_graph: _,
|
||||
nested_goals,
|
||||
tainted,
|
||||
inspect,
|
||||
} = nested_ecx;
|
||||
self.nested_goals.extend(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
|
||||
}
|
||||
}
|
|
@ -24,7 +24,6 @@ use rustc_middle::ty::{
|
|||
use rustc_session::config::DumpSolverProofTree;
|
||||
use rustc_span::DUMMY_SP;
|
||||
use std::io::Write;
|
||||
use std::iter;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
|
||||
|
@ -36,7 +35,6 @@ use super::{GoalSource, SolverMode};
|
|||
pub use select::InferCtxtSelectExt;
|
||||
|
||||
mod canonical;
|
||||
mod commit_if_ok;
|
||||
mod probe;
|
||||
mod select;
|
||||
|
||||
|
@ -124,11 +122,6 @@ impl<'tcx> NestedGoals<'tcx> {
|
|||
pub(super) fn is_empty(&self) -> bool {
|
||||
self.normalizes_to_goals.is_empty() && self.goals.is_empty()
|
||||
}
|
||||
|
||||
pub(super) fn extend(&mut self, other: NestedGoals<'tcx>) {
|
||||
self.normalizes_to_goals.extend(other.normalizes_to_goals);
|
||||
self.goals.extend(other.goals)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
|
||||
|
@ -511,12 +504,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
|||
|
||||
self.inspect.evaluate_added_goals_loop_start();
|
||||
|
||||
fn with_misc_source<'tcx>(
|
||||
it: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
) -> impl Iterator<Item = (GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)> {
|
||||
iter::zip(iter::repeat(GoalSource::Misc), it)
|
||||
}
|
||||
|
||||
// If this loop did not result in any progress, what's our final certainty.
|
||||
let mut unchanged_certainty = Some(Certainty::Yes);
|
||||
for goal in goals.normalizes_to_goals {
|
||||
|
@ -534,16 +521,28 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
|||
unconstrained_goal,
|
||||
)?;
|
||||
// Add the nested goals from normalization to our own nested goals.
|
||||
debug!(?nested_goals);
|
||||
goals.goals.extend(nested_goals);
|
||||
|
||||
// Finally, equate the goal's RHS with the unconstrained var.
|
||||
// We put the nested goals from this into goals instead of
|
||||
// next_goals to avoid needing to process the loop one extra
|
||||
// time if this goal returns something -- I don't think this
|
||||
// matters in practice, though.
|
||||
let eq_goals =
|
||||
self.eq_and_get_goals(goal.param_env, goal.predicate.term, unconstrained_rhs)?;
|
||||
goals.goals.extend(with_misc_source(eq_goals));
|
||||
//
|
||||
// SUBTLE:
|
||||
// We structurally relate aliases here. This is necessary
|
||||
// as we otherwise emit a nested `AliasRelate` goal in case the
|
||||
// returned term is a rigid alias, resulting in overflow.
|
||||
//
|
||||
// It is correct as both `goal.predicate.term` and `unconstrained_rhs`
|
||||
// start out as an unconstrained inference variable so any aliases get
|
||||
// fully normalized when instantiating it.
|
||||
//
|
||||
// FIXME: Strictly speaking this may be incomplete if the normalized-to
|
||||
// type contains an ambiguous alias referencing bound regions. We should
|
||||
// consider changing this to only use "shallow structural equality".
|
||||
self.eq_structurally_relating_aliases(
|
||||
goal.param_env,
|
||||
goal.predicate.term,
|
||||
unconstrained_rhs,
|
||||
)?;
|
||||
|
||||
// We only look at the `projection_ty` part here rather than
|
||||
// looking at the "has changed" return from evaluate_goal,
|
||||
|
@ -730,6 +729,46 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
})
|
||||
}
|
||||
|
||||
/// This should be used when relating a rigid alias with another type.
|
||||
///
|
||||
/// Normally we emit a nested `AliasRelate` when equating an inference
|
||||
/// variable and an alias. This causes us to instead constrain the inference
|
||||
/// variable to the alias without emitting a nested alias relate goals.
|
||||
#[instrument(level = "debug", skip(self, param_env), ret)]
|
||||
pub(super) fn relate_rigid_alias_non_alias(
|
||||
&mut self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
alias: ty::AliasTy<'tcx>,
|
||||
variance: ty::Variance,
|
||||
term: ty::Term<'tcx>,
|
||||
) -> Result<(), NoSolution> {
|
||||
// NOTE: this check is purely an optimization, the structural eq would
|
||||
// always fail if the term is not an inference variable.
|
||||
if term.is_infer() {
|
||||
let tcx = self.tcx();
|
||||
// We need to relate `alias` to `term` treating only the outermost
|
||||
// constructor as rigid, relating any contained generic arguments as
|
||||
// normal. We do this by first structurally equating the `term`
|
||||
// with the alias constructor instantiated with unconstrained infer vars,
|
||||
// and then relate this with the whole `alias`.
|
||||
//
|
||||
// Alternatively we could modify `Equate` for this case by adding another
|
||||
// variant to `StructurallyRelateAliases`.
|
||||
let identity_args = self.fresh_args_for_item(alias.def_id);
|
||||
let rigid_ctor = ty::AliasTy::new(tcx, alias.def_id, identity_args);
|
||||
let ctor_ty = rigid_ctor.to_ty(tcx);
|
||||
let InferOk { value: (), obligations } = self
|
||||
.infcx
|
||||
.at(&ObligationCause::dummy(), param_env)
|
||||
.trace(term, ctor_ty.into())
|
||||
.eq_structurally_relating_aliases(term, ctor_ty.into())?;
|
||||
debug_assert!(obligations.is_empty());
|
||||
self.relate(param_env, alias, variance, rigid_ctor)
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
}
|
||||
|
||||
/// This sohuld only be used when we're either instantiating a previously
|
||||
/// unconstrained "return value" or when we're sure that all aliases in
|
||||
/// the types are rigid.
|
||||
|
|
|
@ -130,17 +130,14 @@ 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 => (),
|
||||
inspect::ProbeStep::EvaluateGoals(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
match probe.kind {
|
||||
inspect::ProbeKind::NormalizedSelfTyAssembly
|
||||
| inspect::ProbeKind::UnsizeAssembly
|
||||
| inspect::ProbeKind::UpcastProjectionCompatibility
|
||||
| inspect::ProbeKind::CommitIfOk => (),
|
||||
| inspect::ProbeKind::UpcastProjectionCompatibility => (),
|
||||
// We add a candidate for the root evaluation if there
|
||||
// is only one way to prove a given goal, e.g. for `WellFormed`.
|
||||
//
|
||||
|
@ -157,7 +154,8 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
|
|||
});
|
||||
}
|
||||
}
|
||||
inspect::ProbeKind::MiscCandidate { name: _, result }
|
||||
inspect::ProbeKind::TryNormalizeNonRigid { result }
|
||||
| inspect::ProbeKind::MiscCandidate { name: _, result }
|
||||
| inspect::ProbeKind::TraitCandidate { source: _, result } => {
|
||||
candidates.push(InspectCandidate {
|
||||
goal: self,
|
||||
|
|
|
@ -220,8 +220,6 @@ enum WipProbeStep<'tcx> {
|
|||
AddGoal(GoalSource, inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>),
|
||||
EvaluateGoals(WipAddedGoalsEvaluation<'tcx>),
|
||||
NestedProbe(WipProbe<'tcx>),
|
||||
CommitIfOkStart,
|
||||
CommitIfOkSuccess,
|
||||
}
|
||||
|
||||
impl<'tcx> WipProbeStep<'tcx> {
|
||||
|
@ -230,8 +228,6 @@ impl<'tcx> WipProbeStep<'tcx> {
|
|||
WipProbeStep::AddGoal(source, goal) => inspect::ProbeStep::AddGoal(source, 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -467,29 +463,6 @@ 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()) {
|
||||
(
|
||||
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 })
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use rustc_hir::def::DefKind;
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_infer::traits::query::NoSolution;
|
||||
use rustc_infer::traits::solve::inspect::ProbeKind;
|
||||
use rustc_infer::traits::specialization_graph::LeafDef;
|
||||
use rustc_infer::traits::Reveal;
|
||||
use rustc_middle::traits::solve::{
|
||||
|
@ -32,6 +33,38 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
) -> QueryResult<'tcx> {
|
||||
self.set_is_normalizes_to_goal();
|
||||
debug_assert!(self.term_is_fully_unconstrained(goal));
|
||||
let normalize_result = self
|
||||
.probe(|&result| ProbeKind::TryNormalizeNonRigid { result })
|
||||
.enter(|this| this.normalize_at_least_one_step(goal));
|
||||
|
||||
match normalize_result {
|
||||
Ok(res) => Ok(res),
|
||||
Err(NoSolution) => {
|
||||
let Goal { param_env, predicate: NormalizesTo { alias, term } } = goal;
|
||||
if alias.opt_kind(self.tcx()).is_some() {
|
||||
self.relate_rigid_alias_non_alias(
|
||||
param_env,
|
||||
alias,
|
||||
ty::Variance::Invariant,
|
||||
term,
|
||||
)?;
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
} else {
|
||||
// FIXME(generic_const_exprs): we currently do not support rigid
|
||||
// unevaluated constants.
|
||||
Err(NoSolution)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Normalize the given alias by at least one step. If the alias is rigid, this
|
||||
/// returns `NoSolution`.
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
fn normalize_at_least_one_step(
|
||||
&mut self,
|
||||
goal: Goal<'tcx, NormalizesTo<'tcx>>,
|
||||
) -> QueryResult<'tcx> {
|
||||
let def_id = goal.predicate.def_id();
|
||||
match self.tcx().def_kind(def_id) {
|
||||
DefKind::AssocTy | DefKind::AssocConst => {
|
||||
|
@ -52,31 +85,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// When normalizing an associated item, constrain the result to `term`.
|
||||
/// When normalizing an associated item, constrain the expected term to `term`.
|
||||
///
|
||||
/// While `NormalizesTo` goals have the normalized-to term as an argument,
|
||||
/// this argument is always fully unconstrained for associated items.
|
||||
/// It is therefore appropriate to instead think of these `NormalizesTo` goals
|
||||
/// as function returning a term after normalizing.
|
||||
///
|
||||
/// When equating an inference variable and an alias, we tend to emit `alias-relate`
|
||||
/// goals and only actually instantiate the inference variable with an alias if the
|
||||
/// alias is rigid. However, this means that constraining the expected term of
|
||||
/// such goals ends up fully structurally normalizing the resulting type instead of
|
||||
/// only by one step. To avoid this we instead use structural equality here, resulting
|
||||
/// in each `NormalizesTo` only projects by a single step.
|
||||
///
|
||||
/// Not doing so, currently causes issues because trying to normalize an opaque type
|
||||
/// during alias-relate doesn't actually constrain the opaque if the concrete type
|
||||
/// is an inference variable. This means that `NormalizesTo` for associated types
|
||||
/// normalizing to an opaque type always resulted in ambiguity, breaking tests e.g.
|
||||
/// tests/ui/type-alias-impl-trait/issue-78450.rs.
|
||||
/// We know `term` to always be a fully unconstrained inference variable, so
|
||||
/// `eq` should never fail here. However, in case `term` contains aliases, we
|
||||
/// emit nested `AliasRelate` goals to structurally normalize the alias.
|
||||
pub fn instantiate_normalizes_to_term(
|
||||
&mut self,
|
||||
goal: Goal<'tcx, NormalizesTo<'tcx>>,
|
||||
term: ty::Term<'tcx>,
|
||||
) {
|
||||
self.eq_structurally_relating_aliases(goal.param_env, goal.predicate.term, term)
|
||||
self.eq(goal.param_env, goal.predicate.term, term)
|
||||
.expect("expected goal term to be fully unconstrained");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
error[E0283]: type annotations needed
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:17:5
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:16:5
|
||||
|
|
||||
LL | cmp_eq
|
||||
| ^^^^^^ cannot infer type of the type parameter `A` declared on the function `cmp_eq`
|
||||
|
|
||||
= note: cannot satisfy `_: Scalar`
|
||||
note: required by a bound in `cmp_eq`
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:9:22
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:10:22
|
||||
|
|
||||
LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O {
|
||||
| ^^^^^^ required by this bound in `cmp_eq`
|
||||
|
@ -15,45 +15,6 @@ help: consider specifying the generic arguments
|
|||
LL | cmp_eq::<A, B, O>
|
||||
| +++++++++++
|
||||
|
||||
error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(<A as Scalar>::RefType<'a>, <B as Scalar>::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}`
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:17:5
|
||||
|
|
||||
LL | cmp_eq
|
||||
| ^^^^^^
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(<A as Scalar>::RefType<'a>, <B as Scalar>::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}`
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:17:5
|
||||
|
|
||||
LL | cmp_eq
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0275]: overflow evaluating the requirement `for<'a, 'b> fn(<O as Scalar>::RefType<'a>, <_ as Scalar>::RefType<'b>) -> _ {cmp_eq::<O, ..., ...>} <: ...`
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:14:51
|
||||
|
|
||||
LL | ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O {
|
||||
| ___________________________________________________^
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | cmp_eq
|
||||
... |
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(<A as Scalar>::RefType<'a>, <B as Scalar>::RefType<'b>) -> O: Sized`
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:14:6
|
||||
|
|
||||
LL | ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | cmp_eq
|
||||
| ------ this returned value is of type `for<'a, 'b> fn(<O as Scalar>::RefType<'a>, <_ as Scalar>::RefType<'b>) -> _ {cmp_eq::<O, _, _>}`
|
||||
|
|
||||
= note: the return type of a function must have a statically known size
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0275, E0283.
|
||||
For more information about an error, try `rustc --explain E0275`.
|
||||
For more information about this error, try `rustc --explain E0283`.
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
error[E0283]: type annotations needed
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:17:5
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:16:5
|
||||
|
|
||||
LL | cmp_eq
|
||||
| ^^^^^^ cannot infer type of the type parameter `A` declared on the function `cmp_eq`
|
||||
|
|
||||
= note: cannot satisfy `_: Scalar`
|
||||
note: required by a bound in `cmp_eq`
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:9:22
|
||||
--> $DIR/ambig-hr-projection-issue-93340.rs:10:22
|
||||
|
|
||||
LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O {
|
||||
| ^^^^^^ required by this bound in `cmp_eq`
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
//@ revisions: old next
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
pub trait Scalar: 'static {
|
||||
type RefType<'a>: ScalarRef<'a>;
|
||||
|
@ -12,12 +13,8 @@ fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefT
|
|||
|
||||
fn build_expression<A: Scalar, B: Scalar, O: Scalar>(
|
||||
) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O {
|
||||
//[next]~^ ERROR overflow evaluating the requirement
|
||||
//[next]~| ERROR overflow evaluating the requirement
|
||||
cmp_eq
|
||||
//~^ ERROR type annotations needed
|
||||
//[next]~| ERROR overflow evaluating the requirement
|
||||
//[next]~| ERROR overflow evaluating the requirement
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
Loading…
Add table
Reference in a new issue