Rollup merge of #130273 - lcnr:overflow-no-constraints, r=compiler-errors

more eagerly discard constraints on overflow

We always discard the results of overflowing goals inside of the trait solver. We previously did so when instantiating the response in `evaluate_goal`. Canonicalizing results only to later discard them is also  inefficient 🤷

It's simpler and nicer to debug to eagerly discard constraints inside of the query itself.

r? ``@compiler-errors``
This commit is contained in:
Matthias Krüger 2024-09-12 19:03:43 +02:00 committed by GitHub
commit cb1d80d1e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 27 additions and 35 deletions

View File

@ -122,6 +122,21 @@ pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response(
(certainty, NestedNormalizationGoals::empty()) (certainty, NestedNormalizationGoals::empty())
}; };
if let Certainty::Maybe(cause @ MaybeCause::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(cause));
}
let external_constraints = let external_constraints =
self.compute_external_query_constraints(certainty, normalization_nested_goals); self.compute_external_query_constraints(certainty, normalization_nested_goals);
let (var_values, mut external_constraints) = (self.var_values, external_constraints) let (var_values, mut external_constraints) = (self.var_values, external_constraints)

View File

@ -17,7 +17,7 @@
use crate::solve::inspect::{self, ProofTreeBuilder}; use crate::solve::inspect::{self, ProofTreeBuilder};
use crate::solve::search_graph::SearchGraph; use crate::solve::search_graph::SearchGraph;
use crate::solve::{ use crate::solve::{
CanonicalInput, CanonicalResponse, Certainty, Goal, GoalEvaluationKind, GoalSource, MaybeCause, CanonicalInput, CanonicalResponse, Certainty, Goal, GoalEvaluationKind, GoalSource,
NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryResult, SolverMode, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryResult, SolverMode,
FIXPOINT_STEP_LIMIT, FIXPOINT_STEP_LIMIT,
}; };
@ -370,7 +370,7 @@ pub(super) fn evaluate_goal_raw(
canonical_goal, canonical_goal,
&mut goal_evaluation, &mut goal_evaluation,
); );
let canonical_response = match canonical_response { let response = match canonical_response {
Err(e) => { Err(e) => {
self.inspect.goal_evaluation(goal_evaluation); self.inspect.goal_evaluation(goal_evaluation);
return Err(e); return Err(e);
@ -378,12 +378,11 @@ pub(super) fn evaluate_goal_raw(
Ok(response) => response, Ok(response) => response,
}; };
let (normalization_nested_goals, certainty, has_changed) = self let has_changed = !response.value.var_values.is_identity_modulo_regions()
.instantiate_response_discarding_overflow( || !response.value.external_constraints.opaque_types.is_empty();
goal.param_env,
orig_values, let (normalization_nested_goals, certainty) =
canonical_response, self.instantiate_and_apply_query_response(goal.param_env, orig_values, response);
);
self.inspect.goal_evaluation(goal_evaluation); self.inspect.goal_evaluation(goal_evaluation);
// FIXME: We previously had an assert here that checked that recomputing // FIXME: We previously had an assert here that checked that recomputing
// a goal after applying its constraints did not change its response. // a goal after applying its constraints did not change its response.
@ -398,24 +397,6 @@ pub(super) fn evaluate_goal_raw(
Ok((normalization_nested_goals, has_changed, certainty)) Ok((normalization_nested_goals, has_changed, certainty))
} }
fn instantiate_response_discarding_overflow(
&mut self,
param_env: I::ParamEnv,
original_values: Vec<I::GenericArg>,
response: CanonicalResponse<I>,
) -> (NestedNormalizationGoals<I>, Certainty, bool) {
if let Certainty::Maybe(MaybeCause::Overflow { .. }) = response.value.certainty {
return (NestedNormalizationGoals::empty(), response.value.certainty, false);
}
let has_changed = !response.value.var_values.is_identity_modulo_regions()
|| !response.value.external_constraints.opaque_types.is_empty();
let (normalization_nested_goals, certainty) =
self.instantiate_and_apply_query_response(param_env, original_values, response);
(normalization_nested_goals, certainty, has_changed)
}
fn compute_goal(&mut self, goal: Goal<I, I::Predicate>) -> QueryResult<I> { fn compute_goal(&mut self, goal: Goal<I, I::Predicate>) -> QueryResult<I> {
let Goal { param_env, predicate } = goal; let Goal { param_env, predicate } = goal;
let kind = predicate.kind(); let kind = predicate.kind();

View File

@ -302,7 +302,7 @@ fn fulfillment_error_for_stalled<'tcx>(
Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => ( Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => (
FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
// Don't look into overflows because we treat overflows weirdly anyways. // Don't look into overflows because we treat overflows weirdly anyways.
// In `instantiate_response_discarding_overflow` we set `has_changed = false`, // We discard the inference constraints from overflowing goals, so
// recomputing the goal again during `find_best_leaf_obligation` may apply // recomputing the goal again during `find_best_leaf_obligation` may apply
// inference guidance that makes other goals go from ambig -> pass, for example. // inference guidance that makes other goals go from ambig -> pass, for example.
// //

View File

@ -117,7 +117,8 @@ pub fn with<Q>(self, cx: I, predicate: impl Upcast<I, Q>) -> Goal<I, Q> {
/// Why a specific goal has to be proven. /// Why a specific goal has to be proven.
/// ///
/// This is necessary as we treat nested goals different depending on /// This is necessary as we treat nested goals different depending on
/// their source. /// their source. This is currently mostly used by proof tree visitors
/// but will be used by cycle handling in the future.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
pub enum GoalSource { pub enum GoalSource {
@ -126,13 +127,6 @@ pub enum GoalSource {
/// ///
/// FIXME(-Znext-solver=coinductive): Explain how and why this /// FIXME(-Znext-solver=coinductive): Explain how and why this
/// changes whether cycles are coinductive. /// changes whether cycles are coinductive.
///
/// 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 compatibility, cc trait-system-refactor-initiatitive#70.
ImplWhereBound, ImplWhereBound,
/// Instantiating a higher-ranked goal and re-proving it. /// Instantiating a higher-ranked goal and re-proving it.
InstantiateHigherRanked, InstantiateHigherRanked,

View File

@ -37,6 +37,8 @@ LL | impl<T> Overlap<T> for T {}
LL | LL |
LL | impl<T> Overlap<for<'a> fn(Assoc<'a, T>)> for T where Missing: Overlap<T> {} LL | impl<T> Overlap<for<'a> fn(Assoc<'a, T>)> for T where Missing: Overlap<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `fn(_)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `fn(_)`
|
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
error: aborting due to 3 previous errors; 1 warning emitted error: aborting due to 3 previous errors; 1 warning emitted