NormalizesTo
return nested goals
This commit is contained in:
parent
33c274f658
commit
f26e1e8b63
@ -164,6 +164,19 @@ pub struct ExternalConstraintsData<'tcx> {
|
||||
// FIXME: implement this.
|
||||
pub region_constraints: QueryRegionConstraints<'tcx>,
|
||||
pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>,
|
||||
pub normalization_nested_goals: NestedNormalizationGoals<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default, TypeVisitable, TypeFoldable)]
|
||||
pub struct NestedNormalizationGoals<'tcx>(pub Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>);
|
||||
impl<'tcx> NestedNormalizationGoals<'tcx> {
|
||||
pub fn empty() -> Self {
|
||||
NestedNormalizationGoals(vec![])
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Having to clone `region_constraints` for folding feels bad and
|
||||
@ -183,6 +196,10 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> {
|
||||
.iter()
|
||||
.map(|opaque| opaque.try_fold_with(folder))
|
||||
.collect::<Result<_, F::Error>>()?,
|
||||
normalization_nested_goals: self
|
||||
.normalization_nested_goals
|
||||
.clone()
|
||||
.try_fold_with(folder)?,
|
||||
}))
|
||||
}
|
||||
|
||||
@ -190,6 +207,7 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> {
|
||||
TypeFolder::interner(folder).mk_external_constraints(ExternalConstraintsData {
|
||||
region_constraints: self.region_constraints.clone().fold_with(folder),
|
||||
opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(),
|
||||
normalization_nested_goals: self.normalization_nested_goals.clone().fold_with(folder),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -197,7 +215,8 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> {
|
||||
impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> {
|
||||
fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
|
||||
try_visit!(self.region_constraints.visit_with(visitor));
|
||||
self.opaque_types.visit_with(visitor)
|
||||
try_visit!(self.opaque_types.visit_with(visitor));
|
||||
self.normalization_nested_goals.visit_with(visitor)
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,7 +258,7 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for PredefinedOpaques<'tcx> {
|
||||
///
|
||||
/// This is necessary as we treat nested goals different depending on
|
||||
/// their source.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TypeVisitable, TypeFoldable)]
|
||||
pub enum GoalSource {
|
||||
Misc,
|
||||
/// We're proving a where-bound of an impl.
|
||||
|
@ -9,6 +9,7 @@
|
||||
//!
|
||||
//! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
|
||||
use super::{CanonicalInput, Certainty, EvalCtxt, Goal};
|
||||
use crate::solve::eval_ctxt::NestedGoals;
|
||||
use crate::solve::{
|
||||
inspect, response_no_constraints_raw, CanonicalResponse, QueryResult, Response,
|
||||
};
|
||||
@ -19,6 +20,7 @@ use rustc_infer::infer::canonical::CanonicalVarValues;
|
||||
use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
|
||||
use rustc_infer::infer::resolve::EagerResolver;
|
||||
use rustc_infer::infer::{InferCtxt, InferOk};
|
||||
use rustc_infer::traits::solve::NestedNormalizationGoals;
|
||||
use rustc_middle::infer::canonical::Canonical;
|
||||
use rustc_middle::traits::query::NoSolution;
|
||||
use rustc_middle::traits::solve::{
|
||||
@ -93,13 +95,26 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
previous call to `try_evaluate_added_goals!`"
|
||||
);
|
||||
|
||||
let certainty = certainty.unify_with(goals_certainty);
|
||||
|
||||
let var_values = self.var_values;
|
||||
let external_constraints = self.compute_external_query_constraints()?;
|
||||
// When normalizing, we've replaced the expected term with an unconstrained
|
||||
// inference variable. This means that we dropped information which could
|
||||
// have been important. We handle this by instead returning the nested goals
|
||||
// to the caller, where they are then handled.
|
||||
//
|
||||
// As we return all ambiguous nested goals, we can ignore the certainty returned
|
||||
// by `try_evaluate_added_goals()`.
|
||||
let (certainty, normalization_nested_goals) = if self.is_normalizes_to_goal {
|
||||
let NestedGoals { normalizes_to_goals, goals } = std::mem::take(&mut self.nested_goals);
|
||||
assert!(normalizes_to_goals.is_empty());
|
||||
(certainty, NestedNormalizationGoals(goals))
|
||||
} else {
|
||||
let certainty = certainty.unify_with(goals_certainty);
|
||||
(certainty, NestedNormalizationGoals::empty())
|
||||
};
|
||||
|
||||
let external_constraints =
|
||||
self.compute_external_query_constraints(normalization_nested_goals)?;
|
||||
let (var_values, mut external_constraints) =
|
||||
(var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx));
|
||||
(self.var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx));
|
||||
// Remove any trivial region constraints once we've resolved regions
|
||||
external_constraints
|
||||
.region_constraints
|
||||
@ -146,6 +161,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
fn compute_external_query_constraints(
|
||||
&self,
|
||||
normalization_nested_goals: NestedNormalizationGoals<'tcx>,
|
||||
) -> Result<ExternalConstraintsData<'tcx>, NoSolution> {
|
||||
// We only check for leaks from universes which were entered inside
|
||||
// of the query.
|
||||
@ -176,7 +192,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a)
|
||||
});
|
||||
|
||||
Ok(ExternalConstraintsData { region_constraints, opaque_types })
|
||||
Ok(ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals })
|
||||
}
|
||||
|
||||
/// After calling a canonical query, we apply the constraints returned
|
||||
@ -185,13 +201,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
/// This happens in three steps:
|
||||
/// - we instantiate the bound variables of the query response
|
||||
/// - we unify the `var_values` of the response with the `original_values`
|
||||
/// - we apply the `external_constraints` returned by the query
|
||||
/// - we apply the `external_constraints` returned by the query, returning
|
||||
/// the `normalization_nested_goals`
|
||||
pub(super) fn instantiate_and_apply_query_response(
|
||||
&mut self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
original_values: Vec<ty::GenericArg<'tcx>>,
|
||||
response: CanonicalResponse<'tcx>,
|
||||
) -> Certainty {
|
||||
) -> (NestedNormalizationGoals<'tcx>, Certainty) {
|
||||
let instantiation = Self::compute_query_response_instantiation_values(
|
||||
self.infcx,
|
||||
&original_values,
|
||||
@ -203,11 +220,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
|
||||
Self::unify_query_var_values(self.infcx, param_env, &original_values, var_values);
|
||||
|
||||
let ExternalConstraintsData { region_constraints, opaque_types } =
|
||||
external_constraints.deref();
|
||||
let ExternalConstraintsData {
|
||||
region_constraints,
|
||||
opaque_types,
|
||||
normalization_nested_goals,
|
||||
} = external_constraints.deref();
|
||||
self.register_region_constraints(region_constraints);
|
||||
self.register_new_opaque_types(param_env, opaque_types);
|
||||
certainty
|
||||
(normalization_nested_goals.clone(), certainty)
|
||||
}
|
||||
|
||||
/// This returns the canoncial variable values to instantiate the bound variables of
|
||||
|
@ -11,6 +11,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
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,
|
||||
@ -25,6 +26,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
infcx: _,
|
||||
variables: _,
|
||||
var_values: _,
|
||||
is_normalizes_to_goal: _,
|
||||
predefined_opaques_in_body: _,
|
||||
max_input_universe: _,
|
||||
search_graph: _,
|
||||
|
@ -7,7 +7,7 @@ use rustc_infer::infer::{
|
||||
BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt,
|
||||
};
|
||||
use rustc_infer::traits::query::NoSolution;
|
||||
use rustc_infer::traits::solve::MaybeCause;
|
||||
use rustc_infer::traits::solve::{MaybeCause, NestedNormalizationGoals};
|
||||
use rustc_infer::traits::ObligationCause;
|
||||
use rustc_middle::infer::canonical::CanonicalVarInfos;
|
||||
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
|
||||
@ -61,6 +61,14 @@ pub struct EvalCtxt<'a, 'tcx> {
|
||||
/// The variable info for the `var_values`, only used to make an ambiguous response
|
||||
/// with no constraints.
|
||||
variables: CanonicalVarInfos<'tcx>,
|
||||
/// Whether we're currently computing a `NormalizesTo` goal. Unlike other goals,
|
||||
/// `NormalizesTo` goals act like functions with the expected term always being
|
||||
/// fully unconstrained. This would weaken inference however, as the nested goals
|
||||
/// never get the inference constraints from the actual normalized-to type. Because
|
||||
/// of this we return any ambiguous nested goals from `NormalizesTo` to the caller
|
||||
/// when then adds these to its own context. The caller is always an `AliasRelate`
|
||||
/// goal so this never leaks out of the solver.
|
||||
is_normalizes_to_goal: bool,
|
||||
pub(super) var_values: CanonicalVarValues<'tcx>,
|
||||
|
||||
predefined_opaques_in_body: PredefinedOpaques<'tcx>,
|
||||
@ -91,7 +99,7 @@ pub struct EvalCtxt<'a, 'tcx> {
|
||||
pub(super) inspect: ProofTreeBuilder<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub(super) struct NestedGoals<'tcx> {
|
||||
/// These normalizes-to goals are treated specially during the evaluation
|
||||
/// loop. In each iteration we take the RHS of the projection, replace it with
|
||||
@ -153,6 +161,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
self.search_graph.solver_mode()
|
||||
}
|
||||
|
||||
pub(super) fn set_is_normalizes_to_goal(&mut self) {
|
||||
self.is_normalizes_to_goal = true;
|
||||
}
|
||||
|
||||
/// Creates a root evaluation context and search graph. This should only be
|
||||
/// used from outside of any evaluation, and other methods should be preferred
|
||||
/// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
|
||||
@ -165,8 +177,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
let mut search_graph = search_graph::SearchGraph::new(mode);
|
||||
|
||||
let mut ecx = EvalCtxt {
|
||||
search_graph: &mut search_graph,
|
||||
infcx,
|
||||
search_graph: &mut search_graph,
|
||||
nested_goals: NestedGoals::new(),
|
||||
inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree),
|
||||
|
||||
@ -178,6 +190,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
max_input_universe: ty::UniverseIndex::ROOT,
|
||||
variables: ty::List::empty(),
|
||||
var_values: CanonicalVarValues::dummy(),
|
||||
is_normalizes_to_goal: false,
|
||||
tainted: Ok(()),
|
||||
};
|
||||
let result = f(&mut ecx);
|
||||
@ -231,6 +244,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
infcx,
|
||||
variables: canonical_input.variables,
|
||||
var_values,
|
||||
is_normalizes_to_goal: false,
|
||||
predefined_opaques_in_body: input.predefined_opaques_in_body,
|
||||
max_input_universe: canonical_input.max_universe,
|
||||
search_graph,
|
||||
@ -317,6 +331,20 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
source: GoalSource,
|
||||
goal: Goal<'tcx, ty::Predicate<'tcx>>,
|
||||
) -> Result<(bool, Certainty), NoSolution> {
|
||||
let (normalization_nested_goals, has_changed, certainty) =
|
||||
self.evaluate_goal_raw(goal_evaluation_kind, source, goal)?;
|
||||
assert!(normalization_nested_goals.is_empty());
|
||||
Ok((has_changed, certainty))
|
||||
}
|
||||
|
||||
/// FIXME(-Znext-solver=coinduction): `_source` is currently unused but will
|
||||
/// be necessary once we implement the new coinduction approach.
|
||||
fn evaluate_goal_raw(
|
||||
&mut self,
|
||||
goal_evaluation_kind: GoalEvaluationKind,
|
||||
_source: GoalSource,
|
||||
goal: Goal<'tcx, ty::Predicate<'tcx>>,
|
||||
) -> Result<(NestedNormalizationGoals<'tcx>, bool, Certainty), NoSolution> {
|
||||
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
|
||||
let mut goal_evaluation =
|
||||
self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind);
|
||||
@ -334,12 +362,12 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
Ok(response) => response,
|
||||
};
|
||||
|
||||
let (certainty, has_changed) = self.instantiate_response_discarding_overflow(
|
||||
goal.param_env,
|
||||
source,
|
||||
orig_values,
|
||||
canonical_response,
|
||||
);
|
||||
let (normalization_nested_goals, certainty, has_changed) = self
|
||||
.instantiate_response_discarding_overflow(
|
||||
goal.param_env,
|
||||
orig_values,
|
||||
canonical_response,
|
||||
);
|
||||
self.inspect.goal_evaluation(goal_evaluation);
|
||||
// FIXME: We previously had an assert here that checked that recomputing
|
||||
// a goal after applying its constraints did not change its response.
|
||||
@ -351,47 +379,25 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
// Once we have decided on how to handle trait-system-refactor-initiative#75,
|
||||
// we should re-add an assert here.
|
||||
|
||||
Ok((has_changed, certainty))
|
||||
Ok((normalization_nested_goals, has_changed, certainty))
|
||||
}
|
||||
|
||||
fn instantiate_response_discarding_overflow(
|
||||
&mut self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
source: GoalSource,
|
||||
original_values: Vec<ty::GenericArg<'tcx>>,
|
||||
response: CanonicalResponse<'tcx>,
|
||||
) -> (Certainty, bool) {
|
||||
// The old solver did not evaluate nested goals when normalizing.
|
||||
// It returned the selection constraints allowing a `Projection`
|
||||
// obligation to not hold in coherence while avoiding the fatal error
|
||||
// from overflow.
|
||||
//
|
||||
// We match this behavior here by considering all constraints
|
||||
// from nested goals which are not from where-bounds. We will already
|
||||
// need to track which nested goals are required by impl where-bounds
|
||||
// for coinductive cycles, so we simply reuse that here.
|
||||
//
|
||||
// While we could consider overflow constraints in more cases, this should
|
||||
// not be necessary for backcompat and results in better perf. It also
|
||||
// avoids a potential inconsistency which would otherwise require some
|
||||
// tracking for root goals as well. See #119071 for an example.
|
||||
let keep_overflow_constraints = || {
|
||||
self.search_graph.current_goal_is_normalizes_to()
|
||||
&& source != GoalSource::ImplWhereBound
|
||||
};
|
||||
|
||||
if let Certainty::Maybe(MaybeCause::Overflow { .. }) = response.value.certainty
|
||||
&& !keep_overflow_constraints()
|
||||
{
|
||||
return (response.value.certainty, false);
|
||||
) -> (NestedNormalizationGoals<'tcx>, 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 certainty =
|
||||
let (normalization_nested_goals, certainty) =
|
||||
self.instantiate_and_apply_query_response(param_env, original_values, response);
|
||||
(certainty, has_changed)
|
||||
(normalization_nested_goals, certainty, has_changed)
|
||||
}
|
||||
|
||||
fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
|
||||
@ -494,7 +500,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
/// Goals for the next step get directly added to the nested goals of the `EvalCtxt`.
|
||||
fn evaluate_added_goals_step(&mut self) -> Result<Option<Certainty>, NoSolution> {
|
||||
let tcx = self.tcx();
|
||||
let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new());
|
||||
let mut goals = core::mem::take(&mut self.nested_goals);
|
||||
|
||||
self.inspect.evaluate_added_goals_loop_start();
|
||||
|
||||
@ -515,11 +521,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs },
|
||||
);
|
||||
|
||||
let (_, certainty) = self.evaluate_goal(
|
||||
let (NestedNormalizationGoals(nested_goals), _, certainty) = self.evaluate_goal_raw(
|
||||
GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes },
|
||||
GoalSource::Misc,
|
||||
unconstrained_goal,
|
||||
)?;
|
||||
// Add the nested goals from normalization to our own 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
|
||||
|
@ -24,6 +24,7 @@ where
|
||||
infcx: outer_ecx.infcx,
|
||||
variables: outer_ecx.variables,
|
||||
var_values: outer_ecx.var_values,
|
||||
is_normalizes_to_goal: outer_ecx.is_normalizes_to_goal,
|
||||
predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body,
|
||||
max_input_universe: outer_ecx.max_input_universe,
|
||||
search_graph: outer_ecx.search_graph,
|
||||
|
@ -58,12 +58,13 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
}
|
||||
|
||||
let candidate = candidates.pop().unwrap();
|
||||
let certainty = ecx.instantiate_and_apply_query_response(
|
||||
trait_goal.param_env,
|
||||
orig_values,
|
||||
candidate.result,
|
||||
);
|
||||
|
||||
let (normalization_nested_goals, certainty) = ecx
|
||||
.instantiate_and_apply_query_response(
|
||||
trait_goal.param_env,
|
||||
orig_values,
|
||||
candidate.result,
|
||||
);
|
||||
assert!(normalization_nested_goals.is_empty());
|
||||
Ok(Some((candidate, certainty)))
|
||||
});
|
||||
|
||||
|
@ -70,7 +70,19 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
|
||||
instantiated_goals.push(goal);
|
||||
}
|
||||
|
||||
for &goal in &instantiated_goals {
|
||||
for goal in instantiated_goals.iter().copied() {
|
||||
// We need to be careful with `NormalizesTo` goals as the
|
||||
// expected term has to be replaced with an unconstrained
|
||||
// inference variable.
|
||||
if let Some(kind) = goal.predicate.kind().no_bound_vars()
|
||||
&& let ty::PredicateKind::NormalizesTo(predicate) = kind
|
||||
&& !predicate.alias.is_opaque(infcx.tcx)
|
||||
{
|
||||
// FIXME: We currently skip these goals as
|
||||
// `fn evaluate_root_goal` ICEs if there are any
|
||||
// `NestedNormalizationGoals`.
|
||||
continue;
|
||||
};
|
||||
let (_, proof_tree) = infcx.evaluate_root_goal(goal, GenerateProofTree::Yes);
|
||||
let proof_tree = proof_tree.unwrap();
|
||||
try_visit!(visitor.visit_goal(&InspectGoal::new(
|
||||
|
@ -32,10 +32,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
) -> QueryResult<'tcx> {
|
||||
let def_id = goal.predicate.def_id();
|
||||
let def_kind = self.tcx().def_kind(def_id);
|
||||
if cfg!(debug_assertions) && !matches!(def_kind, DefKind::OpaqueTy) {
|
||||
assert!(self.term_is_fully_unconstrained(goal));
|
||||
match def_kind {
|
||||
DefKind::OpaqueTy => return self.normalize_opaque_type(goal),
|
||||
_ => self.set_is_normalizes_to_goal(),
|
||||
}
|
||||
|
||||
debug_assert!(self.term_is_fully_unconstrained(goal));
|
||||
match self.tcx().def_kind(def_id) {
|
||||
DefKind::AssocTy | DefKind::AssocConst => {
|
||||
match self.tcx().associated_item(def_id).container {
|
||||
@ -49,9 +51,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
DefKind::AnonConst => self.normalize_anon_const(goal),
|
||||
DefKind::OpaqueTy => self.normalize_opaque_type(goal),
|
||||
DefKind::TyAlias => self.normalize_weak_type(goal),
|
||||
kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)),
|
||||
kind => bug!("unknown DefKind {} in normalizes-to goal: {goal:#?}", kind.descr(def_id)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,6 @@ 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::mem;
|
||||
@ -175,15 +174,6 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `<T as Overflow>::Assoc: Sized
|
||||
LL | type Assoc = <T as Overflow>::Assoc;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`trait_ref_is_knowable_norm_overflow`)
|
||||
note: required by a bound in `Overflow::Assoc`
|
||||
--> $DIR/trait_ref_is_knowable-norm-overflow.rs:7:5
|
||||
|
|
||||
@ -23,9 +22,6 @@ LL | impl<T: Copy> Trait for T {}
|
||||
LL | struct LocalTy;
|
||||
LL | impl Trait for <LocalTy as Overflow>::Assoc {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
|
||||
|
|
||||
= note: overflow evaluating the requirement `_ == <LocalTy as Overflow>::Assoc`
|
||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`trait_ref_is_knowable_norm_overflow`)
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -0,0 +1,45 @@
|
||||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver=coherence
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@ check-pass
|
||||
|
||||
// A regression test for `paperclip-core`. This previously failed to compile
|
||||
// in the new solver.
|
||||
//
|
||||
// Behavior in old solver:
|
||||
// We prove `Projection(<W<?0> as Unconstrained>::Assoc, ())`. This
|
||||
// normalizes `<W<?0> as Unconstrained>::Assoc` to `?1` with nested goals
|
||||
// `[Projection(<?0 as Unconstrained>::Assoc, ?1), Trait(?1: NoImpl)]`.
|
||||
// We then unify `?1` with `()`. At this point `?1: NoImpl` does not hold,
|
||||
// and we get an error.
|
||||
//
|
||||
// Previous behavior of the new solver:
|
||||
// We prove `Projection(<W<?0> as Unconstrained>::Assoc, ())`. This normalizes
|
||||
// `<W<?0> as Unconstrained>::Assoc` to `?1` and eagerly computes the nested
|
||||
// goals `[Projection(<?0 as Unconstrained>::Assoc, ?1), Trait(?1: NoImpl)]`.
|
||||
// These goals are both ambiguous. `NormalizesTo`` then returns `?1` as the
|
||||
// normalized-to type. It discards the nested goals, forcing the certainty of
|
||||
// the normalization to `Maybe`. Unifying `?1` with `()` succeeds¹. However,
|
||||
// this is never propagated to the `?1: NoImpl` goal, as it only exists inside
|
||||
// of the `NormalizesTo` goal. The normalized-to term always starts out as
|
||||
// unconstrained.
|
||||
//
|
||||
// We fix this regression by returning the nested goals of `NormalizesTo` goals
|
||||
// to the `AliasRelate`. This results in us checking `(): NoImpl`, same as the
|
||||
// old solver.
|
||||
|
||||
struct W<T: ?Sized>(T);
|
||||
trait NoImpl {}
|
||||
trait Unconstrained {
|
||||
type Assoc;
|
||||
}
|
||||
impl<T: Unconstrained<Assoc = U>, U: NoImpl> Unconstrained for W<T> {
|
||||
type Assoc = U;
|
||||
}
|
||||
|
||||
|
||||
trait Overlap {}
|
||||
impl<T: Unconstrained<Assoc = ()>> Overlap for T {}
|
||||
impl<U> Overlap for W<U> {}
|
||||
|
||||
fn main() {}
|
@ -3,8 +3,6 @@ error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1 == _`
|
||||
|
|
||||
LL | needs_bar::<T::Assoc1>();
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`)
|
||||
|
||||
error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1: Bar`
|
||||
--> $DIR/recursive-self-normalization-2.rs:15:17
|
||||
@ -12,7 +10,6 @@ error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1: Bar`
|
||||
LL | needs_bar::<T::Assoc1>();
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`)
|
||||
note: required by a bound in `needs_bar`
|
||||
--> $DIR/recursive-self-normalization-2.rs:12:17
|
||||
|
|
||||
@ -25,7 +22,6 @@ error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1: Sized`
|
||||
LL | needs_bar::<T::Assoc1>();
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`)
|
||||
note: required by an implicit `Sized` bound in `needs_bar`
|
||||
--> $DIR/recursive-self-normalization-2.rs:12:14
|
||||
|
|
||||
@ -41,8 +37,6 @@ error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1 == _`
|
||||
|
|
||||
LL | needs_bar::<T::Assoc1>();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`)
|
||||
|
||||
error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1 == _`
|
||||
--> $DIR/recursive-self-normalization-2.rs:15:5
|
||||
@ -50,7 +44,6 @@ error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1 == _`
|
||||
LL | needs_bar::<T::Assoc1>();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`)
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1 == _`
|
||||
@ -59,7 +52,6 @@ error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1 == _`
|
||||
LL | needs_bar::<T::Assoc1>();
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`)
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
@ -3,8 +3,6 @@ error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc == _`
|
||||
|
|
||||
LL | needs_bar::<T::Assoc>();
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`)
|
||||
|
||||
error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc: Bar`
|
||||
--> $DIR/recursive-self-normalization.rs:11:17
|
||||
@ -12,7 +10,6 @@ error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc: Bar`
|
||||
LL | needs_bar::<T::Assoc>();
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`)
|
||||
note: required by a bound in `needs_bar`
|
||||
--> $DIR/recursive-self-normalization.rs:8:17
|
||||
|
|
||||
@ -25,7 +22,6 @@ error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc: Sized`
|
||||
LL | needs_bar::<T::Assoc>();
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`)
|
||||
note: required by an implicit `Sized` bound in `needs_bar`
|
||||
--> $DIR/recursive-self-normalization.rs:8:14
|
||||
|
|
||||
@ -41,8 +37,6 @@ error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc == _`
|
||||
|
|
||||
LL | needs_bar::<T::Assoc>();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`)
|
||||
|
||||
error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc == _`
|
||||
--> $DIR/recursive-self-normalization.rs:11:5
|
||||
@ -50,7 +44,6 @@ error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc == _`
|
||||
LL | needs_bar::<T::Assoc>();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`)
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc == _`
|
||||
@ -59,7 +52,6 @@ error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc == _`
|
||||
LL | needs_bar::<T::Assoc>();
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`)
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
Loading…
x
Reference in New Issue
Block a user