mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-20 10:55:14 +00:00
dont discard overflow from normalizes-to goals
This commit is contained in:
parent
ca718ffd2d
commit
4a38442c90
@ -248,6 +248,9 @@ pub enum GoalSource {
|
||||
/// This also impacts whether we erase constraints on overflow.
|
||||
/// Erasing constraints is generally very useful for perf and also
|
||||
/// results in better error messages by avoiding spurious errors.
|
||||
/// We do not erase overflow constraints in `normalizes-to` goals unless
|
||||
/// they are from an impl where-clause. This is necessary due to
|
||||
/// backwards compatability, cc trait-system-refactor-initiatitive#70.
|
||||
ImplWhereBound,
|
||||
}
|
||||
|
||||
|
@ -94,20 +94,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
);
|
||||
|
||||
let certainty = certainty.unify_with(goals_certainty);
|
||||
if let Certainty::OVERFLOW = certainty {
|
||||
// If we have overflow, it's probable that we're substituting a type
|
||||
// into itself infinitely and any partial substitutions in the query
|
||||
// response are probably not useful anyways, so just return an empty
|
||||
// query response.
|
||||
//
|
||||
// This may prevent us from potentially useful inference, e.g.
|
||||
// 2 candidates, one ambiguous and one overflow, which both
|
||||
// have the same inference constraints.
|
||||
//
|
||||
// Changing this to retain some constraints in the future
|
||||
// won't be a breaking change, so this is good enough for now.
|
||||
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow));
|
||||
}
|
||||
|
||||
let var_values = self.var_values;
|
||||
let external_constraints = self.compute_external_query_constraints()?;
|
||||
|
@ -157,7 +157,7 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
|
||||
Option<inspect::GoalEvaluation<'tcx>>,
|
||||
) {
|
||||
EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
|
||||
ecx.evaluate_goal(GoalEvaluationKind::Root, goal)
|
||||
ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -335,6 +335,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
fn evaluate_goal(
|
||||
&mut self,
|
||||
goal_evaluation_kind: GoalEvaluationKind,
|
||||
source: GoalSource,
|
||||
goal: Goal<'tcx, ty::Predicate<'tcx>>,
|
||||
) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
|
||||
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
|
||||
@ -354,13 +355,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
Ok(response) => response,
|
||||
};
|
||||
|
||||
let has_changed = !canonical_response.value.var_values.is_identity_modulo_regions()
|
||||
|| !canonical_response.value.external_constraints.opaque_types.is_empty();
|
||||
let (certainty, nested_goals) = match self.instantiate_and_apply_query_response(
|
||||
goal.param_env,
|
||||
orig_values,
|
||||
canonical_response,
|
||||
) {
|
||||
let (certainty, has_changed, nested_goals) = match self
|
||||
.instantiate_response_discarding_overflow(
|
||||
goal.param_env,
|
||||
source,
|
||||
orig_values,
|
||||
canonical_response,
|
||||
) {
|
||||
Err(e) => {
|
||||
self.inspect.goal_evaluation(goal_evaluation);
|
||||
return Err(e);
|
||||
@ -387,6 +388,30 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
Ok((has_changed, certainty, nested_goals))
|
||||
}
|
||||
|
||||
fn instantiate_response_discarding_overflow(
|
||||
&mut self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
source: GoalSource,
|
||||
original_values: Vec<ty::GenericArg<'tcx>>,
|
||||
response: CanonicalResponse<'tcx>,
|
||||
) -> Result<(Certainty, bool, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
|
||||
let keep_overflow_constraints = || {
|
||||
self.search_graph.current_goal_is_normalizes_to()
|
||||
&& source != GoalSource::ImplWhereBound
|
||||
};
|
||||
|
||||
if response.value.certainty == Certainty::OVERFLOW && !keep_overflow_constraints() {
|
||||
Ok((Certainty::OVERFLOW, false, Vec::new()))
|
||||
} else {
|
||||
let has_changed = !response.value.var_values.is_identity_modulo_regions()
|
||||
|| !response.value.external_constraints.opaque_types.is_empty();
|
||||
|
||||
let (certainty, nested_goals) =
|
||||
self.instantiate_and_apply_query_response(param_env, original_values, response)?;
|
||||
Ok((certainty, has_changed, nested_goals))
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
|
||||
let Goal { param_env, predicate } = goal;
|
||||
let kind = predicate.kind();
|
||||
@ -509,6 +534,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
|
||||
let (_, certainty, instantiate_goals) = self.evaluate_goal(
|
||||
GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes },
|
||||
GoalSource::Misc,
|
||||
unconstrained_goal,
|
||||
)?;
|
||||
self.nested_goals.goals.extend(with_misc_source(instantiate_goals));
|
||||
@ -544,6 +570,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
for (source, goal) in goals.goals.drain(..) {
|
||||
let (has_changed, certainty, instantiate_goals) = self.evaluate_goal(
|
||||
GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::No },
|
||||
source,
|
||||
goal,
|
||||
)?;
|
||||
self.nested_goals.goals.extend(with_misc_source(instantiate_goals));
|
||||
|
@ -8,6 +8,7 @@ use rustc_index::IndexVec;
|
||||
use rustc_middle::dep_graph::dep_kinds;
|
||||
use rustc_middle::traits::solve::CacheData;
|
||||
use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::Limit;
|
||||
use std::collections::hash_map::Entry;
|
||||
@ -111,6 +112,15 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||
self.stack.is_empty()
|
||||
}
|
||||
|
||||
pub(super) fn current_goal_is_normalizes_to(&self) -> bool {
|
||||
self.stack.raw.last().map_or(false, |e| {
|
||||
matches!(
|
||||
e.input.value.goal.predicate.kind().skip_binder(),
|
||||
ty::PredicateKind::NormalizesTo(..)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the remaining depth allowed for nested goals.
|
||||
///
|
||||
/// This is generally simply one less than the current depth.
|
||||
|
@ -1,7 +1,15 @@
|
||||
// revisions: old next
|
||||
//[next] compile-flags: -Znext-solver=coherence
|
||||
// check-pass
|
||||
|
||||
// Regression test for issue #90662
|
||||
// Tests that projection caching does not cause a spurious error
|
||||
// Tests that projection caching does not cause a spurious error.
|
||||
// Coherence relies on the following overflowing goal to still constrain
|
||||
// `?0` to `dyn Service`.
|
||||
//
|
||||
// Projection(<ServiceImpl as Provider<TestModule>>::Interface. ?0)
|
||||
//
|
||||
// cc https://github.com/rust-lang/trait-system-refactor-initiative/issues/70.
|
||||
|
||||
trait HasProvider<T: ?Sized> {}
|
||||
trait Provider<M> {
|
||||
|
@ -0,0 +1,25 @@
|
||||
// compile-flags: -Znext-solver=coherence
|
||||
// check-pass
|
||||
|
||||
// A regression test for trait-system-refactor-initiative#70.
|
||||
|
||||
trait Trait {
|
||||
type Assoc;
|
||||
}
|
||||
|
||||
struct W<T: ?Sized>(*mut T);
|
||||
impl<T: ?Sized> Trait for W<W<T>>
|
||||
where
|
||||
W<T>: Trait,
|
||||
{
|
||||
type Assoc = ();
|
||||
}
|
||||
|
||||
trait NoOverlap {}
|
||||
impl<T: Trait<Assoc = u32>> NoOverlap for T {}
|
||||
// `Projection(<W<_> as Trait>::Assoc, u32)` should result in error even
|
||||
// though applying the impl results in overflow. This is necessary to match
|
||||
// the behavior of the old solver.
|
||||
impl<T: ?Sized> NoOverlap for W<T> {}
|
||||
|
||||
fn main() {}
|
Loading…
Reference in New Issue
Block a user