initial info dump
This commit is contained in:
parent
8d1fa473dd
commit
3009b2c647
11 changed files with 929 additions and 402 deletions
|
@ -981,7 +981,7 @@ pub enum CodegenObligationError {
|
||||||
FulfillmentError,
|
FulfillmentError,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||||
pub enum DefiningAnchor {
|
pub enum DefiningAnchor {
|
||||||
/// `DefId` of the item.
|
/// `DefId` of the item.
|
||||||
Bind(LocalDefId),
|
Bind(LocalDefId),
|
||||||
|
|
|
@ -92,7 +92,7 @@ pub type CanonicalTypeOpProvePredicateGoal<'tcx> =
|
||||||
pub type CanonicalTypeOpNormalizeGoal<'tcx, T> =
|
pub type CanonicalTypeOpNormalizeGoal<'tcx, T> =
|
||||||
Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>;
|
Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, HashStable, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Hash, HashStable, PartialEq, Eq)]
|
||||||
pub struct NoSolution;
|
pub struct NoSolution;
|
||||||
|
|
||||||
impl<'tcx> From<TypeError<'tcx>> for NoSolution {
|
impl<'tcx> From<TypeError<'tcx>> for NoSolution {
|
||||||
|
|
|
@ -11,6 +11,8 @@ use crate::ty::{
|
||||||
TypeVisitor,
|
TypeVisitor,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod inspect;
|
||||||
|
|
||||||
pub type EvaluationCache<'tcx> = Cache<CanonicalInput<'tcx>, QueryResult<'tcx>>;
|
pub type EvaluationCache<'tcx> = Cache<CanonicalInput<'tcx>, QueryResult<'tcx>>;
|
||||||
|
|
||||||
/// A goal is a statement, i.e. `predicate`, we want to prove
|
/// A goal is a statement, i.e. `predicate`, we want to prove
|
||||||
|
@ -18,7 +20,7 @@ pub type EvaluationCache<'tcx> = Cache<CanonicalInput<'tcx>, QueryResult<'tcx>>;
|
||||||
///
|
///
|
||||||
/// Most of the time the `param_env` contains the `where`-bounds of the function
|
/// Most of the time the `param_env` contains the `where`-bounds of the function
|
||||||
/// we're currently typechecking while the `predicate` is some trait bound.
|
/// we're currently typechecking while the `predicate` is some trait bound.
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||||
pub struct Goal<'tcx, P> {
|
pub struct Goal<'tcx, P> {
|
||||||
pub predicate: P,
|
pub predicate: P,
|
||||||
pub param_env: ty::ParamEnv<'tcx>,
|
pub param_env: ty::ParamEnv<'tcx>,
|
||||||
|
@ -39,7 +41,7 @@ impl<'tcx, P> Goal<'tcx, P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||||
pub struct Response<'tcx> {
|
pub struct Response<'tcx> {
|
||||||
pub certainty: Certainty,
|
pub certainty: Certainty,
|
||||||
pub var_values: CanonicalVarValues<'tcx>,
|
pub var_values: CanonicalVarValues<'tcx>,
|
||||||
|
@ -47,7 +49,7 @@ pub struct Response<'tcx> {
|
||||||
pub external_constraints: ExternalConstraints<'tcx>,
|
pub external_constraints: ExternalConstraints<'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||||
pub enum Certainty {
|
pub enum Certainty {
|
||||||
Yes,
|
Yes,
|
||||||
Maybe(MaybeCause),
|
Maybe(MaybeCause),
|
||||||
|
@ -86,7 +88,7 @@ impl Certainty {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Why we failed to evaluate a goal.
|
/// Why we failed to evaluate a goal.
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||||
pub enum MaybeCause {
|
pub enum MaybeCause {
|
||||||
/// We failed due to ambiguity. This ambiguity can either
|
/// We failed due to ambiguity. This ambiguity can either
|
||||||
/// be a true ambiguity, i.e. there are multiple different answers,
|
/// be a true ambiguity, i.e. there are multiple different answers,
|
||||||
|
@ -96,7 +98,7 @@ pub enum MaybeCause {
|
||||||
Overflow,
|
Overflow,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||||
pub struct QueryInput<'tcx, T> {
|
pub struct QueryInput<'tcx, T> {
|
||||||
pub goal: Goal<'tcx, T>,
|
pub goal: Goal<'tcx, T>,
|
||||||
pub anchor: DefiningAnchor,
|
pub anchor: DefiningAnchor,
|
||||||
|
@ -104,12 +106,12 @@ pub struct QueryInput<'tcx, T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Additional constraints returned on success.
|
/// Additional constraints returned on success.
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
|
#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default)]
|
||||||
pub struct PredefinedOpaquesData<'tcx> {
|
pub struct PredefinedOpaquesData<'tcx> {
|
||||||
pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>,
|
pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)]
|
||||||
pub struct PredefinedOpaques<'tcx>(pub(crate) Interned<'tcx, PredefinedOpaquesData<'tcx>>);
|
pub struct PredefinedOpaques<'tcx>(pub(crate) Interned<'tcx, PredefinedOpaquesData<'tcx>>);
|
||||||
|
|
||||||
impl<'tcx> std::ops::Deref for PredefinedOpaques<'tcx> {
|
impl<'tcx> std::ops::Deref for PredefinedOpaques<'tcx> {
|
||||||
|
@ -132,7 +134,7 @@ pub type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>;
|
||||||
/// solver, merge the two responses again.
|
/// solver, merge the two responses again.
|
||||||
pub type QueryResult<'tcx> = Result<CanonicalResponse<'tcx>, NoSolution>;
|
pub type QueryResult<'tcx> = Result<CanonicalResponse<'tcx>, NoSolution>;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)]
|
||||||
pub struct ExternalConstraints<'tcx>(pub(crate) Interned<'tcx, ExternalConstraintsData<'tcx>>);
|
pub struct ExternalConstraints<'tcx>(pub(crate) Interned<'tcx, ExternalConstraintsData<'tcx>>);
|
||||||
|
|
||||||
impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> {
|
impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> {
|
||||||
|
@ -144,7 +146,7 @@ impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Additional constraints returned on success.
|
/// Additional constraints returned on success.
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
|
#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default)]
|
||||||
pub struct ExternalConstraintsData<'tcx> {
|
pub struct ExternalConstraintsData<'tcx> {
|
||||||
// FIXME: implement this.
|
// FIXME: implement this.
|
||||||
pub region_constraints: QueryRegionConstraints<'tcx>,
|
pub region_constraints: QueryRegionConstraints<'tcx>,
|
||||||
|
|
168
compiler/rustc_middle/src/traits/solve/inspect.rs
Normal file
168
compiler/rustc_middle/src/traits/solve/inspect.rs
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
use super::{CanonicalInput, Certainty, Goal, NoSolution, QueryInput, QueryResult};
|
||||||
|
use crate::ty;
|
||||||
|
use std::fmt::{Debug, Write};
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Hash, HashStable)]
|
||||||
|
pub struct GoalEvaluation<'tcx> {
|
||||||
|
pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
|
||||||
|
pub canonicalized_goal: Option<CanonicalInput<'tcx>>,
|
||||||
|
|
||||||
|
/// To handle coinductive cycles we can end up re-evaluating a goal
|
||||||
|
/// multiple times with different results for a nested goal. Each rerun
|
||||||
|
/// is represented as an entry in this vec.
|
||||||
|
pub evaluation_steps: Vec<GoalEvaluationStep<'tcx>>,
|
||||||
|
|
||||||
|
pub cache_hit: bool,
|
||||||
|
|
||||||
|
pub result: Option<QueryResult<'tcx>>,
|
||||||
|
}
|
||||||
|
impl Debug for GoalEvaluation<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
ProofTreeFormatter { f, on_newline: true }.format_goal_evaluation(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Hash, HashStable)]
|
||||||
|
pub struct AddedGoalsEvaluation<'tcx> {
|
||||||
|
pub evaluations: Vec<Vec<GoalEvaluation<'tcx>>>,
|
||||||
|
pub result: Option<Result<Certainty, NoSolution>>,
|
||||||
|
}
|
||||||
|
impl Debug for AddedGoalsEvaluation<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
ProofTreeFormatter { f, on_newline: true }.format_nested_goal_evaluation(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Hash, HashStable)]
|
||||||
|
pub struct GoalEvaluationStep<'tcx> {
|
||||||
|
pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
|
||||||
|
|
||||||
|
pub nested_goal_evaluations: Vec<AddedGoalsEvaluation<'tcx>>,
|
||||||
|
pub candidates: Vec<GoalCandidate<'tcx>>,
|
||||||
|
|
||||||
|
pub result: Option<QueryResult<'tcx>>,
|
||||||
|
}
|
||||||
|
impl Debug for GoalEvaluationStep<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
ProofTreeFormatter { f, on_newline: true }.format_evaluation_step(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Hash, HashStable)]
|
||||||
|
pub struct GoalCandidate<'tcx> {
|
||||||
|
pub nested_goal_evaluations: Vec<AddedGoalsEvaluation<'tcx>>,
|
||||||
|
pub candidates: Vec<GoalCandidate<'tcx>>,
|
||||||
|
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub result: Option<QueryResult<'tcx>>,
|
||||||
|
}
|
||||||
|
impl Debug for GoalCandidate<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
ProofTreeFormatter { f, on_newline: true }.format_candidate(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ProofTreeFormatter<'a, 'b> {
|
||||||
|
f: &'a mut (dyn Write + 'b),
|
||||||
|
on_newline: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for ProofTreeFormatter<'_, '_> {
|
||||||
|
fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
||||||
|
for line in s.split_inclusive("\n") {
|
||||||
|
if self.on_newline {
|
||||||
|
self.f.write_str(" ")?;
|
||||||
|
}
|
||||||
|
self.on_newline = line.ends_with("\n");
|
||||||
|
self.f.write_str(line)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProofTreeFormatter<'_, '_> {
|
||||||
|
fn nested(&mut self) -> ProofTreeFormatter<'_, '_> {
|
||||||
|
ProofTreeFormatter { f: self, on_newline: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_goal_evaluation(&mut self, goal: &GoalEvaluation<'_>) -> std::fmt::Result {
|
||||||
|
let f = &mut *self.f;
|
||||||
|
writeln!(f, "GOAL: {:?}", goal.uncanonicalized_goal)?;
|
||||||
|
writeln!(f, "CANONICALIZED: {:?}", goal.canonicalized_goal)?;
|
||||||
|
|
||||||
|
match goal.cache_hit {
|
||||||
|
true => writeln!(f, "CACHE HIT: {:?}", goal.result),
|
||||||
|
false => {
|
||||||
|
for (n, step) in goal.evaluation_steps.iter().enumerate() {
|
||||||
|
let f = &mut *self.f;
|
||||||
|
writeln!(f, "REVISION {n}: {:?}", step.result.unwrap())?;
|
||||||
|
let mut f = self.nested();
|
||||||
|
f.format_evaluation_step(step)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let f = &mut *self.f;
|
||||||
|
writeln!(f, "RESULT: {:?}", goal.result.unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_evaluation_step(
|
||||||
|
&mut self,
|
||||||
|
evaluation_step: &GoalEvaluationStep<'_>,
|
||||||
|
) -> std::fmt::Result {
|
||||||
|
let f = &mut *self.f;
|
||||||
|
writeln!(f, "INSTANTIATED: {:?}", evaluation_step.instantiated_goal)?;
|
||||||
|
|
||||||
|
for candidate in &evaluation_step.candidates {
|
||||||
|
let mut f = self.nested();
|
||||||
|
f.format_candidate(candidate)?;
|
||||||
|
}
|
||||||
|
for nested_goal_evaluation in &evaluation_step.nested_goal_evaluations {
|
||||||
|
let mut f = self.nested();
|
||||||
|
f.format_nested_goal_evaluation(nested_goal_evaluation)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_candidate(&mut self, candidate: &GoalCandidate<'_>) -> std::fmt::Result {
|
||||||
|
let f = &mut *self.f;
|
||||||
|
|
||||||
|
match (candidate.name.as_ref(), candidate.result) {
|
||||||
|
(Some(name), Some(result)) => writeln!(f, "CANDIDATE {}: {:?}", name, result,)?,
|
||||||
|
(None, None) => writeln!(f, "MISC PROBE")?,
|
||||||
|
(None, Some(_)) => unreachable!("unexpected probe with no name but a result"),
|
||||||
|
(Some(_), None) => unreachable!("unexpected probe with a name but no candidate"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut f = self.nested();
|
||||||
|
for candidate in &candidate.candidates {
|
||||||
|
f.format_candidate(candidate)?;
|
||||||
|
}
|
||||||
|
for nested_evaluations in &candidate.nested_goal_evaluations {
|
||||||
|
f.format_nested_goal_evaluation(nested_evaluations)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_nested_goal_evaluation(
|
||||||
|
&mut self,
|
||||||
|
nested_goal_evaluation: &AddedGoalsEvaluation<'_>,
|
||||||
|
) -> std::fmt::Result {
|
||||||
|
let f = &mut *self.f;
|
||||||
|
writeln!(f, "TRY_EVALUATE_ADDED_GOALS: {:?}", nested_goal_evaluation.result.unwrap())?;
|
||||||
|
|
||||||
|
for (n, revision) in nested_goal_evaluation.evaluations.iter().enumerate() {
|
||||||
|
let f = &mut *self.f;
|
||||||
|
writeln!(f, "REVISION {n}")?;
|
||||||
|
let mut f = self.nested();
|
||||||
|
for goal_evaluation in revision {
|
||||||
|
f.format_goal_evaluation(goal_evaluation)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -109,10 +109,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
direction: ty::AliasRelationDirection,
|
direction: ty::AliasRelationDirection,
|
||||||
invert: Invert,
|
invert: Invert,
|
||||||
) -> QueryResult<'tcx> {
|
) -> QueryResult<'tcx> {
|
||||||
self.probe(|ecx| {
|
self.probe_candidate(
|
||||||
|
|ecx| {
|
||||||
ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?;
|
ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?;
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
})
|
},
|
||||||
|
|| "normalizes-to".into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normalizes_to_inner(
|
fn normalizes_to_inner(
|
||||||
|
@ -153,7 +156,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
alias_rhs: ty::AliasTy<'tcx>,
|
alias_rhs: ty::AliasTy<'tcx>,
|
||||||
direction: ty::AliasRelationDirection,
|
direction: ty::AliasRelationDirection,
|
||||||
) -> QueryResult<'tcx> {
|
) -> QueryResult<'tcx> {
|
||||||
self.probe(|ecx| {
|
self.probe_candidate(
|
||||||
|
|ecx| {
|
||||||
match direction {
|
match direction {
|
||||||
ty::AliasRelationDirection::Equate => {
|
ty::AliasRelationDirection::Equate => {
|
||||||
ecx.eq(param_env, alias_lhs, alias_rhs)?;
|
ecx.eq(param_env, alias_lhs, alias_rhs)?;
|
||||||
|
@ -164,7 +168,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
})
|
},
|
||||||
|
|| "substs relate".into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assemble_bidirectional_normalizes_to_candidate(
|
fn assemble_bidirectional_normalizes_to_candidate(
|
||||||
|
@ -174,7 +180,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
rhs: ty::Term<'tcx>,
|
rhs: ty::Term<'tcx>,
|
||||||
direction: ty::AliasRelationDirection,
|
direction: ty::AliasRelationDirection,
|
||||||
) -> QueryResult<'tcx> {
|
) -> QueryResult<'tcx> {
|
||||||
self.probe(|ecx| {
|
self.probe_candidate(
|
||||||
|
|ecx| {
|
||||||
ecx.normalizes_to_inner(
|
ecx.normalizes_to_inner(
|
||||||
param_env,
|
param_env,
|
||||||
lhs.to_alias_ty(ecx.tcx()).unwrap(),
|
lhs.to_alias_ty(ecx.tcx()).unwrap(),
|
||||||
|
@ -190,6 +197,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
Invert::Yes,
|
Invert::Yes,
|
||||||
)?;
|
)?;
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
})
|
},
|
||||||
|
|| "bidir normalizes-to".into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,10 @@ use rustc_middle::ty::{
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
|
use crate::solve::inspect::DebugSolver;
|
||||||
use crate::traits::specialization_graph;
|
use crate::traits::specialization_graph;
|
||||||
|
|
||||||
|
use super::inspect::InspectSolve;
|
||||||
use super::search_graph::{self, OverflowHandler};
|
use super::search_graph::{self, OverflowHandler};
|
||||||
use super::SolverMode;
|
use super::SolverMode;
|
||||||
use super::{search_graph::SearchGraph, Goal};
|
use super::{search_graph::SearchGraph, Goal};
|
||||||
|
@ -73,6 +75,8 @@ pub struct EvalCtxt<'a, 'tcx> {
|
||||||
// ambiguous goals. Instead, a probe needs to be introduced somewhere in the
|
// ambiguous goals. Instead, a probe needs to be introduced somewhere in the
|
||||||
// evaluation code.
|
// evaluation code.
|
||||||
tainted: Result<(), NoSolution>,
|
tainted: Result<(), NoSolution>,
|
||||||
|
|
||||||
|
inspect: Box<dyn InspectSolve<'tcx> + 'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
@ -143,9 +147,18 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
|
||||||
var_values: CanonicalVarValues::dummy(),
|
var_values: CanonicalVarValues::dummy(),
|
||||||
nested_goals: NestedGoals::new(),
|
nested_goals: NestedGoals::new(),
|
||||||
tainted: Ok(()),
|
tainted: Ok(()),
|
||||||
|
inspect: Box::new(DebugSolver::new()),
|
||||||
};
|
};
|
||||||
let result = ecx.evaluate_goal(IsNormalizesToHack::No, goal);
|
let result = ecx.evaluate_goal(IsNormalizesToHack::No, goal);
|
||||||
|
|
||||||
|
let tree = match ecx.inspect.into_debug_solver() {
|
||||||
|
Some(tree) => match Box::leak(tree) {
|
||||||
|
DebugSolver::GoalEvaluation(tree) => tree,
|
||||||
|
_ => unreachable!("unable to convert to `DebugSolver::GoalEvaluation`"),
|
||||||
|
},
|
||||||
|
_ => unreachable!("unable to convert to `DebugSolver::GoalEvaluation`"),
|
||||||
|
};
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
ecx.nested_goals.is_empty(),
|
ecx.nested_goals.is_empty(),
|
||||||
"root `EvalCtxt` should not have any goals added to it"
|
"root `EvalCtxt` should not have any goals added to it"
|
||||||
|
@ -170,16 +183,23 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
/// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal]
|
/// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal]
|
||||||
/// if you're inside of the solver or [InferCtxtEvalExt::evaluate_root_goal] if you're
|
/// if you're inside of the solver or [InferCtxtEvalExt::evaluate_root_goal] if you're
|
||||||
/// outside of it.
|
/// outside of it.
|
||||||
#[instrument(level = "debug", skip(tcx, search_graph), ret)]
|
#[instrument(level = "debug", skip(tcx, search_graph, goal_evaluation), ret)]
|
||||||
fn evaluate_canonical_goal(
|
fn evaluate_canonical_goal(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
search_graph: &'a mut search_graph::SearchGraph<'tcx>,
|
search_graph: &'a mut search_graph::SearchGraph<'tcx>,
|
||||||
canonical_input: CanonicalInput<'tcx>,
|
canonical_input: CanonicalInput<'tcx>,
|
||||||
|
mut goal_evaluation: &mut dyn InspectSolve<'tcx>,
|
||||||
) -> QueryResult<'tcx> {
|
) -> QueryResult<'tcx> {
|
||||||
|
goal_evaluation.canonicalized_goal(canonical_input);
|
||||||
|
|
||||||
// Deal with overflow, caching, and coinduction.
|
// Deal with overflow, caching, and coinduction.
|
||||||
//
|
//
|
||||||
// The actual solver logic happens in `ecx.compute_goal`.
|
// The actual solver logic happens in `ecx.compute_goal`.
|
||||||
search_graph.with_new_goal(tcx, canonical_input, |search_graph| {
|
search_graph.with_new_goal(
|
||||||
|
tcx,
|
||||||
|
canonical_input,
|
||||||
|
goal_evaluation,
|
||||||
|
|search_graph, goal_evaluation| {
|
||||||
let intercrate = match search_graph.solver_mode() {
|
let intercrate = match search_graph.solver_mode() {
|
||||||
SolverMode::Normal => false,
|
SolverMode::Normal => false,
|
||||||
SolverMode::Coherence => true,
|
SolverMode::Coherence => true,
|
||||||
|
@ -199,6 +219,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
search_graph,
|
search_graph,
|
||||||
nested_goals: NestedGoals::new(),
|
nested_goals: NestedGoals::new(),
|
||||||
tainted: Ok(()),
|
tainted: Ok(()),
|
||||||
|
inspect: goal_evaluation.new_goal_evaluation_step(input),
|
||||||
};
|
};
|
||||||
|
|
||||||
for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
|
for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
|
||||||
|
@ -207,10 +228,15 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ecx.nested_goals.is_empty() {
|
if !ecx.nested_goals.is_empty() {
|
||||||
panic!("prepopulating opaque types shouldn't add goals: {:?}", ecx.nested_goals);
|
panic!(
|
||||||
|
"prepopulating opaque types shouldn't add goals: {:?}",
|
||||||
|
ecx.nested_goals
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = ecx.compute_goal(input.goal);
|
let result = ecx.compute_goal(input.goal);
|
||||||
|
ecx.inspect.query_result(result);
|
||||||
|
goal_evaluation.goal_evaluation_step(ecx.inspect);
|
||||||
|
|
||||||
// When creating a query response we clone the opaque type constraints
|
// When creating a query response we clone the opaque type constraints
|
||||||
// instead of taking them. This would cause an ICE here, since we have
|
// instead of taking them. This would cause an ICE here, since we have
|
||||||
|
@ -221,7 +247,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively evaluates `goal`, returning whether any inference vars have
|
/// Recursively evaluates `goal`, returning whether any inference vars have
|
||||||
|
@ -232,8 +259,16 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
goal: Goal<'tcx, ty::Predicate<'tcx>>,
|
goal: Goal<'tcx, ty::Predicate<'tcx>>,
|
||||||
) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
|
) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
|
||||||
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
|
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
|
||||||
let canonical_response =
|
let mut goal_evaluation = self.inspect.new_goal_evaluation(goal);
|
||||||
EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
|
let canonical_response = EvalCtxt::evaluate_canonical_goal(
|
||||||
|
self.tcx(),
|
||||||
|
self.search_graph,
|
||||||
|
canonical_goal,
|
||||||
|
&mut *goal_evaluation,
|
||||||
|
);
|
||||||
|
goal_evaluation.query_result(canonical_response);
|
||||||
|
self.inspect.goal_evaluation(goal_evaluation);
|
||||||
|
let canonical_response = canonical_response?;
|
||||||
|
|
||||||
let has_changed = !canonical_response.value.var_values.is_identity()
|
let has_changed = !canonical_response.value.var_values.is_identity()
|
||||||
|| !canonical_response.value.external_constraints.opaque_types.is_empty();
|
|| !canonical_response.value.external_constraints.opaque_types.is_empty();
|
||||||
|
@ -261,8 +296,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
{
|
{
|
||||||
debug!("rerunning goal to check result is stable");
|
debug!("rerunning goal to check result is stable");
|
||||||
let (_orig_values, canonical_goal) = self.canonicalize_goal(goal);
|
let (_orig_values, canonical_goal) = self.canonicalize_goal(goal);
|
||||||
let new_canonical_response =
|
let new_canonical_response = EvalCtxt::evaluate_canonical_goal(
|
||||||
EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
|
self.tcx(),
|
||||||
|
self.search_graph,
|
||||||
|
canonical_goal,
|
||||||
|
// FIXME(-Ztrait-solver=next): we do not track what happens in `evaluate_canonical_goal`
|
||||||
|
&mut (),
|
||||||
|
)?;
|
||||||
// We only check for modulo regions as we convert all regions in
|
// We only check for modulo regions as we convert all regions in
|
||||||
// the input to new existentials, even if they're expected to be
|
// the input to new existentials, even if they're expected to be
|
||||||
// `'static` or a placeholder region.
|
// `'static` or a placeholder region.
|
||||||
|
@ -353,12 +393,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
// the certainty of all the goals.
|
// the certainty of all the goals.
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
pub(super) fn try_evaluate_added_goals(&mut self) -> Result<Certainty, NoSolution> {
|
pub(super) fn try_evaluate_added_goals(&mut self) -> Result<Certainty, NoSolution> {
|
||||||
|
let inspect = self.inspect.new_evaluate_added_goals();
|
||||||
|
let inspect = core::mem::replace(&mut self.inspect, inspect);
|
||||||
|
|
||||||
let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new());
|
let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new());
|
||||||
let mut new_goals = NestedGoals::new();
|
let mut new_goals = NestedGoals::new();
|
||||||
|
|
||||||
let response = self.repeat_while_none(
|
let response = self.repeat_while_none(
|
||||||
|_| Ok(Certainty::Maybe(MaybeCause::Overflow)),
|
|_| Ok(Certainty::Maybe(MaybeCause::Overflow)),
|
||||||
|this| {
|
|this| {
|
||||||
|
this.inspect.evaluate_added_goals_loop_start();
|
||||||
|
|
||||||
let mut has_changed = Err(Certainty::Yes);
|
let mut has_changed = Err(Certainty::Yes);
|
||||||
|
|
||||||
if let Some(goal) = goals.normalizes_to_hack_goal.take() {
|
if let Some(goal) = goals.normalizes_to_hack_goal.take() {
|
||||||
|
@ -447,10 +492,15 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
self.inspect.eval_added_goals_result(response);
|
||||||
|
|
||||||
if response.is_err() {
|
if response.is_err() {
|
||||||
self.tainted = Err(NoSolution);
|
self.tainted = Err(NoSolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let goal_evaluations = std::mem::replace(&mut self.inspect, inspect);
|
||||||
|
self.inspect.added_goals_evaluation(goal_evaluations);
|
||||||
|
|
||||||
self.nested_goals = goals;
|
self.nested_goals = goals;
|
||||||
response
|
response
|
||||||
}
|
}
|
||||||
|
@ -466,8 +516,24 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
search_graph: self.search_graph,
|
search_graph: self.search_graph,
|
||||||
nested_goals: self.nested_goals.clone(),
|
nested_goals: self.nested_goals.clone(),
|
||||||
tainted: self.tainted,
|
tainted: self.tainted,
|
||||||
|
inspect: self.inspect.new_goal_candidate(),
|
||||||
};
|
};
|
||||||
self.infcx.probe(|_| f(&mut ecx))
|
let r = self.infcx.probe(|_| f(&mut ecx));
|
||||||
|
self.inspect.goal_candidate(ecx.inspect);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn probe_candidate(
|
||||||
|
&mut self,
|
||||||
|
f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
|
||||||
|
mut name: impl FnMut() -> String,
|
||||||
|
) -> QueryResult<'tcx> {
|
||||||
|
self.probe(|ecx| {
|
||||||
|
let result = f(ecx);
|
||||||
|
ecx.inspect.candidate_name(&mut name);
|
||||||
|
ecx.inspect.query_result(result);
|
||||||
|
result
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn tcx(&self) -> TyCtxt<'tcx> {
|
pub(super) fn tcx(&self) -> TyCtxt<'tcx> {
|
||||||
|
@ -781,14 +847,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
if candidate_key.def_id != key.def_id {
|
if candidate_key.def_id != key.def_id {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
values.extend(self.probe(|ecx| {
|
values.extend(self.probe(
|
||||||
|
|ecx| {
|
||||||
for (a, b) in std::iter::zip(candidate_key.substs, key.substs) {
|
for (a, b) in std::iter::zip(candidate_key.substs, key.substs) {
|
||||||
ecx.eq(param_env, a, b)?;
|
ecx.eq(param_env, a, b)?;
|
||||||
}
|
}
|
||||||
ecx.eq(param_env, candidate_ty, ty)?;
|
ecx.eq(param_env, candidate_ty, ty)?;
|
||||||
ecx.add_item_bounds_for_hidden_type(candidate_key, param_env, candidate_ty);
|
ecx.add_item_bounds_for_hidden_type(candidate_key, param_env, candidate_ty);
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
}));
|
},
|
||||||
|
|| "opaque type storage".into(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
values
|
values
|
||||||
}
|
}
|
||||||
|
|
244
compiler/rustc_trait_selection/src/solve/inspect/mod.rs
Normal file
244
compiler/rustc_trait_selection/src/solve/inspect/mod.rs
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
use rustc_middle::{
|
||||||
|
traits::{
|
||||||
|
query::NoSolution,
|
||||||
|
solve::{inspect::*, CanonicalInput, Certainty, Goal, QueryInput, QueryResult},
|
||||||
|
},
|
||||||
|
ty,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DebugSolver<'tcx> {
|
||||||
|
Root,
|
||||||
|
GoalEvaluation(GoalEvaluation<'tcx>),
|
||||||
|
AddedGoalsEvaluation(AddedGoalsEvaluation<'tcx>),
|
||||||
|
GoalEvaluationStep(GoalEvaluationStep<'tcx>),
|
||||||
|
GoalCandidate(GoalCandidate<'tcx>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait InspectSolve<'tcx> {
|
||||||
|
fn into_debug_solver(self: Box<Self>) -> Option<Box<DebugSolver<'tcx>>>;
|
||||||
|
|
||||||
|
fn new_goal_evaluation(
|
||||||
|
&mut self,
|
||||||
|
goal: Goal<'tcx, ty::Predicate<'tcx>>,
|
||||||
|
) -> Box<dyn InspectSolve<'tcx> + 'tcx>;
|
||||||
|
fn canonicalized_goal(&mut self, canonical_goal: CanonicalInput<'tcx>);
|
||||||
|
fn cache_hit(&mut self);
|
||||||
|
fn goal_evaluation(&mut self, goal_evaluation: Box<dyn InspectSolve<'tcx> + 'tcx>);
|
||||||
|
|
||||||
|
fn new_goal_evaluation_step(
|
||||||
|
&mut self,
|
||||||
|
instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
|
||||||
|
) -> Box<dyn InspectSolve<'tcx> + 'tcx>;
|
||||||
|
fn goal_evaluation_step(&mut self, goal_eval_step: Box<dyn InspectSolve<'tcx> + 'tcx>);
|
||||||
|
|
||||||
|
fn new_goal_candidate(&mut self) -> Box<dyn InspectSolve<'tcx> + 'tcx>;
|
||||||
|
fn candidate_name(&mut self, f: &mut dyn FnMut() -> String);
|
||||||
|
fn goal_candidate(&mut self, candidate: Box<dyn InspectSolve<'tcx> + 'tcx>);
|
||||||
|
|
||||||
|
fn new_evaluate_added_goals(&mut self) -> Box<dyn InspectSolve<'tcx> + 'tcx>;
|
||||||
|
fn evaluate_added_goals_loop_start(&mut self);
|
||||||
|
fn eval_added_goals_result(&mut self, result: Result<Certainty, NoSolution>);
|
||||||
|
fn added_goals_evaluation(&mut self, goals_evaluation: Box<dyn InspectSolve<'tcx> + 'tcx>);
|
||||||
|
|
||||||
|
fn query_result(&mut self, result: QueryResult<'tcx>);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// No-op `InspectSolve` impl to use for normal trait solving when we do not want
|
||||||
|
/// to take a performance hit from recording information about how things are being
|
||||||
|
/// proven.
|
||||||
|
impl<'tcx> InspectSolve<'tcx> for () {
|
||||||
|
fn into_debug_solver(self: Box<Self>) -> Option<Box<DebugSolver<'tcx>>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_goal_evaluation(
|
||||||
|
&mut self,
|
||||||
|
_goal: Goal<'tcx, ty::Predicate<'tcx>>,
|
||||||
|
) -> Box<dyn InspectSolve<'tcx> + 'tcx> {
|
||||||
|
Box::new(())
|
||||||
|
}
|
||||||
|
fn canonicalized_goal(&mut self, _canonical_goal: CanonicalInput<'tcx>) {}
|
||||||
|
fn cache_hit(&mut self) {}
|
||||||
|
fn goal_evaluation(&mut self, _goal_evaluation: Box<dyn InspectSolve<'tcx> + 'tcx>) {}
|
||||||
|
|
||||||
|
fn new_goal_evaluation_step(
|
||||||
|
&mut self,
|
||||||
|
_instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
|
||||||
|
) -> Box<dyn InspectSolve<'tcx> + 'tcx> {
|
||||||
|
Box::new(())
|
||||||
|
}
|
||||||
|
fn goal_evaluation_step(&mut self, _goal_eval_step: Box<dyn InspectSolve<'tcx> + 'tcx>) {}
|
||||||
|
|
||||||
|
fn new_goal_candidate(&mut self) -> Box<dyn InspectSolve<'tcx> + 'tcx> {
|
||||||
|
Box::new(())
|
||||||
|
}
|
||||||
|
fn candidate_name(&mut self, _f: &mut dyn FnMut() -> String) {}
|
||||||
|
fn goal_candidate(&mut self, _candidate: Box<dyn InspectSolve<'tcx> + 'tcx>) {}
|
||||||
|
|
||||||
|
fn new_evaluate_added_goals(&mut self) -> Box<dyn InspectSolve<'tcx> + 'tcx> {
|
||||||
|
Box::new(())
|
||||||
|
}
|
||||||
|
fn evaluate_added_goals_loop_start(&mut self) {}
|
||||||
|
fn eval_added_goals_result(&mut self, _result: Result<Certainty, NoSolution>) {}
|
||||||
|
fn added_goals_evaluation(&mut self, _goals_evaluation: Box<dyn InspectSolve<'tcx> + 'tcx>) {}
|
||||||
|
|
||||||
|
fn query_result(&mut self, _result: QueryResult<'tcx>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> DebugSolver<'tcx> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::Root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'tcx> InspectSolve<'tcx> for DebugSolver<'tcx> {
|
||||||
|
fn into_debug_solver(self: Box<Self>) -> Option<Box<DebugSolver<'tcx>>> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_goal_evaluation(
|
||||||
|
&mut self,
|
||||||
|
goal: Goal<'tcx, ty::Predicate<'tcx>>,
|
||||||
|
) -> Box<dyn InspectSolve<'tcx> + 'tcx> {
|
||||||
|
Box::new(DebugSolver::GoalEvaluation(GoalEvaluation {
|
||||||
|
uncanonicalized_goal: goal,
|
||||||
|
canonicalized_goal: None,
|
||||||
|
evaluation_steps: vec![],
|
||||||
|
cache_hit: false,
|
||||||
|
result: None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
fn canonicalized_goal(&mut self, canonical_goal: CanonicalInput<'tcx>) {
|
||||||
|
match self {
|
||||||
|
DebugSolver::GoalEvaluation(goal_evaluation) => {
|
||||||
|
assert!(goal_evaluation.canonicalized_goal.is_none());
|
||||||
|
goal_evaluation.canonicalized_goal = Some(canonical_goal)
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn cache_hit(&mut self) {
|
||||||
|
match self {
|
||||||
|
DebugSolver::GoalEvaluation(goal_evaluation) => goal_evaluation.cache_hit = true,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fn goal_evaluation(&mut self, goal_evaluation: Box<dyn InspectSolve<'tcx> + 'tcx>) {
|
||||||
|
let goal_evaluation = goal_evaluation.into_debug_solver().unwrap();
|
||||||
|
match (self, *goal_evaluation) {
|
||||||
|
(
|
||||||
|
DebugSolver::AddedGoalsEvaluation(AddedGoalsEvaluation { evaluations, .. }),
|
||||||
|
DebugSolver::GoalEvaluation(goal_evaluation),
|
||||||
|
) => evaluations.last_mut().unwrap().push(goal_evaluation),
|
||||||
|
(this @ DebugSolver::Root, goal_evaluation) => *this = goal_evaluation,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_goal_evaluation_step(
|
||||||
|
&mut self,
|
||||||
|
instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
|
||||||
|
) -> Box<dyn InspectSolve<'tcx> + 'tcx> {
|
||||||
|
Box::new(DebugSolver::GoalEvaluationStep(GoalEvaluationStep {
|
||||||
|
instantiated_goal,
|
||||||
|
nested_goal_evaluations: vec![],
|
||||||
|
candidates: vec![],
|
||||||
|
result: None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
fn goal_evaluation_step(&mut self, goal_eval_step: Box<dyn InspectSolve<'tcx> + 'tcx>) {
|
||||||
|
let goal_eval_step = goal_eval_step.into_debug_solver().unwrap();
|
||||||
|
match (self, *goal_eval_step) {
|
||||||
|
(DebugSolver::GoalEvaluation(goal_eval), DebugSolver::GoalEvaluationStep(step)) => {
|
||||||
|
goal_eval.evaluation_steps.push(step);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_goal_candidate(&mut self) -> Box<dyn InspectSolve<'tcx> + 'tcx> {
|
||||||
|
Box::new(DebugSolver::GoalCandidate(GoalCandidate {
|
||||||
|
nested_goal_evaluations: vec![],
|
||||||
|
candidates: vec![],
|
||||||
|
name: None,
|
||||||
|
result: None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
fn candidate_name(&mut self, f: &mut dyn FnMut() -> String) {
|
||||||
|
let name = f();
|
||||||
|
|
||||||
|
match self {
|
||||||
|
DebugSolver::GoalCandidate(goal_candidate) => {
|
||||||
|
assert!(goal_candidate.name.is_none());
|
||||||
|
goal_candidate.name = Some(name);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn goal_candidate(&mut self, candidate: Box<dyn InspectSolve<'tcx> + 'tcx>) {
|
||||||
|
let candidate = candidate.into_debug_solver().unwrap();
|
||||||
|
match (self, *candidate) {
|
||||||
|
(
|
||||||
|
DebugSolver::GoalCandidate(GoalCandidate { candidates, .. })
|
||||||
|
| DebugSolver::GoalEvaluationStep(GoalEvaluationStep { candidates, .. }),
|
||||||
|
DebugSolver::GoalCandidate(candidate),
|
||||||
|
) => candidates.push(candidate),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_evaluate_added_goals(&mut self) -> Box<dyn InspectSolve<'tcx> + 'tcx> {
|
||||||
|
Box::new(DebugSolver::AddedGoalsEvaluation(AddedGoalsEvaluation {
|
||||||
|
evaluations: vec![],
|
||||||
|
result: None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
fn evaluate_added_goals_loop_start(&mut self) {
|
||||||
|
match self {
|
||||||
|
DebugSolver::AddedGoalsEvaluation(this) => {
|
||||||
|
this.evaluations.push(vec![]);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn eval_added_goals_result(&mut self, result: Result<Certainty, NoSolution>) {
|
||||||
|
match self {
|
||||||
|
DebugSolver::AddedGoalsEvaluation(this) => {
|
||||||
|
assert!(this.result.is_none());
|
||||||
|
this.result = Some(result);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn added_goals_evaluation(&mut self, goals_evaluation: Box<dyn InspectSolve<'tcx> + 'tcx>) {
|
||||||
|
let goals_evaluation = goals_evaluation.into_debug_solver().unwrap();
|
||||||
|
match (self, *goals_evaluation) {
|
||||||
|
(
|
||||||
|
DebugSolver::GoalEvaluationStep(GoalEvaluationStep {
|
||||||
|
nested_goal_evaluations, ..
|
||||||
|
})
|
||||||
|
| DebugSolver::GoalCandidate(GoalCandidate { nested_goal_evaluations, .. }),
|
||||||
|
DebugSolver::AddedGoalsEvaluation(added_goals_evaluation),
|
||||||
|
) => nested_goal_evaluations.push(added_goals_evaluation),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_result(&mut self, result: QueryResult<'tcx>) {
|
||||||
|
match self {
|
||||||
|
DebugSolver::GoalEvaluation(goal_evaluation) => {
|
||||||
|
assert!(goal_evaluation.result.is_none());
|
||||||
|
goal_evaluation.result = Some(result);
|
||||||
|
}
|
||||||
|
DebugSolver::Root | DebugSolver::AddedGoalsEvaluation(_) => unreachable!(),
|
||||||
|
DebugSolver::GoalEvaluationStep(evaluation_step) => {
|
||||||
|
assert!(evaluation_step.result.is_none());
|
||||||
|
evaluation_step.result = Some(result);
|
||||||
|
}
|
||||||
|
DebugSolver::GoalCandidate(candidate) => {
|
||||||
|
assert!(candidate.result.is_none());
|
||||||
|
candidate.result = Some(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ mod assembly;
|
||||||
mod canonicalize;
|
mod canonicalize;
|
||||||
mod eval_ctxt;
|
mod eval_ctxt;
|
||||||
mod fulfill;
|
mod fulfill;
|
||||||
|
pub mod inspect;
|
||||||
mod opaques;
|
mod opaques;
|
||||||
mod project_goals;
|
mod project_goals;
|
||||||
mod search_graph;
|
mod search_graph;
|
||||||
|
|
|
@ -112,7 +112,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||||
if let Some(projection_pred) = assumption.as_projection_clause()
|
if let Some(projection_pred) = assumption.as_projection_clause()
|
||||||
&& projection_pred.projection_def_id() == goal.predicate.def_id()
|
&& projection_pred.projection_def_id() == goal.predicate.def_id()
|
||||||
{
|
{
|
||||||
ecx.probe(|ecx| {
|
ecx.probe_candidate(|ecx| {
|
||||||
let assumption_projection_pred =
|
let assumption_projection_pred =
|
||||||
ecx.instantiate_binder_with_infer(projection_pred);
|
ecx.instantiate_binder_with_infer(projection_pred);
|
||||||
ecx.eq(
|
ecx.eq(
|
||||||
|
@ -123,7 +123,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||||
ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)
|
ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)
|
||||||
.expect("expected goal term to be fully unconstrained");
|
.expect("expected goal term to be fully unconstrained");
|
||||||
then(ecx)
|
then(ecx)
|
||||||
})
|
}, || "assumption".into())
|
||||||
} else {
|
} else {
|
||||||
Err(NoSolution)
|
Err(NoSolution)
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||||
return Err(NoSolution);
|
return Err(NoSolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
ecx.probe(|ecx| {
|
ecx.probe_candidate(
|
||||||
|
|ecx| {
|
||||||
let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
|
let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
|
||||||
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
|
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
|
||||||
|
|
||||||
|
@ -223,7 +224,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||||
ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs))
|
ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs))
|
||||||
.expect("expected goal term to be fully unconstrained");
|
.expect("expected goal term to be fully unconstrained");
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
})
|
},
|
||||||
|
|| "impl".into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consider_auto_trait_candidate(
|
fn consider_auto_trait_candidate(
|
||||||
|
@ -318,7 +321,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||||
goal: Goal<'tcx, Self>,
|
goal: Goal<'tcx, Self>,
|
||||||
) -> QueryResult<'tcx> {
|
) -> QueryResult<'tcx> {
|
||||||
let tcx = ecx.tcx();
|
let tcx = ecx.tcx();
|
||||||
ecx.probe(|ecx| {
|
ecx.probe_candidate(
|
||||||
|
|ecx| {
|
||||||
let metadata_ty = match goal.predicate.self_ty().kind() {
|
let metadata_ty = match goal.predicate.self_ty().kind() {
|
||||||
ty::Bool
|
ty::Bool
|
||||||
| ty::Char
|
| ty::Char
|
||||||
|
@ -369,8 +373,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||||
tcx,
|
tcx,
|
||||||
ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)),
|
ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)),
|
||||||
));
|
));
|
||||||
return ecx
|
return ecx.evaluate_added_goals_and_make_canonical_response(
|
||||||
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
|
Certainty::Yes,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -400,7 +405,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||||
ecx.eq(goal.param_env, goal.predicate.term, metadata_ty.into())
|
ecx.eq(goal.param_env, goal.predicate.term, metadata_ty.into())
|
||||||
.expect("expected goal term to be fully unconstrained");
|
.expect("expected goal term to be fully unconstrained");
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
})
|
},
|
||||||
|
|| "builtin pointee".into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consider_builtin_future_candidate(
|
fn consider_builtin_future_candidate(
|
||||||
|
@ -535,11 +542,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
ecx.probe(|ecx| {
|
ecx.probe_candidate(
|
||||||
|
|ecx| {
|
||||||
ecx.eq(goal.param_env, goal.predicate.term, discriminant_ty.into())
|
ecx.eq(goal.param_env, goal.predicate.term, discriminant_ty.into())
|
||||||
.expect("expected goal term to be fully unconstrained");
|
.expect("expected goal term to be fully unconstrained");
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
})
|
},
|
||||||
|
|| "builtin discriminant kind".into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consider_builtin_destruct_candidate(
|
fn consider_builtin_destruct_candidate(
|
||||||
|
|
|
@ -12,6 +12,7 @@ use rustc_middle::traits::solve::{CanonicalInput, Certainty, MaybeCause, QueryRe
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
use std::{collections::hash_map::Entry, mem};
|
use std::{collections::hash_map::Entry, mem};
|
||||||
|
|
||||||
|
use super::inspect::InspectSolve;
|
||||||
use super::SolverMode;
|
use super::SolverMode;
|
||||||
|
|
||||||
rustc_index::newtype_index! {
|
rustc_index::newtype_index! {
|
||||||
|
@ -205,11 +206,13 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||||
&mut self,
|
&mut self,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
canonical_input: CanonicalInput<'tcx>,
|
canonical_input: CanonicalInput<'tcx>,
|
||||||
mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>,
|
inspect: &mut dyn InspectSolve<'tcx>,
|
||||||
|
mut loop_body: impl FnMut(&mut Self, &mut dyn InspectSolve<'tcx>) -> QueryResult<'tcx>,
|
||||||
) -> QueryResult<'tcx> {
|
) -> QueryResult<'tcx> {
|
||||||
if self.should_use_global_cache() {
|
if self.should_use_global_cache() {
|
||||||
if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) {
|
if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) {
|
||||||
debug!(?canonical_input, ?result, "cache hit");
|
debug!(?canonical_input, ?result, "cache hit");
|
||||||
|
inspect.cache_hit();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -231,7 +234,7 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||||
result
|
result
|
||||||
},
|
},
|
||||||
|this| {
|
|this| {
|
||||||
let result = loop_body(this);
|
let result = loop_body(this, inspect);
|
||||||
this.try_finalize_goal(canonical_input, result).then(|| result)
|
this.try_finalize_goal(canonical_input, result).then(|| result)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -61,7 +61,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
ecx.probe(|ecx| {
|
ecx.probe_candidate(
|
||||||
|
|ecx| {
|
||||||
let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
|
let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
|
||||||
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
|
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
|
||||||
|
|
||||||
|
@ -75,7 +76,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
ecx.add_goals(where_clause_bounds);
|
ecx.add_goals(where_clause_bounds);
|
||||||
|
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty)
|
ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty)
|
||||||
})
|
},
|
||||||
|
|| "impl".into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn probe_and_match_goal_against_assumption(
|
fn probe_and_match_goal_against_assumption(
|
||||||
|
@ -89,7 +92,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
&& trait_clause.polarity() == goal.predicate.polarity
|
&& trait_clause.polarity() == goal.predicate.polarity
|
||||||
{
|
{
|
||||||
// FIXME: Constness
|
// FIXME: Constness
|
||||||
ecx.probe(|ecx| {
|
ecx.probe_candidate(|ecx| {
|
||||||
let assumption_trait_pred =
|
let assumption_trait_pred =
|
||||||
ecx.instantiate_binder_with_infer(trait_clause);
|
ecx.instantiate_binder_with_infer(trait_clause);
|
||||||
ecx.eq(
|
ecx.eq(
|
||||||
|
@ -98,7 +101,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
assumption_trait_pred.trait_ref,
|
assumption_trait_pred.trait_ref,
|
||||||
)?;
|
)?;
|
||||||
then(ecx)
|
then(ecx)
|
||||||
})
|
}, || "assumption".into())
|
||||||
} else {
|
} else {
|
||||||
Err(NoSolution)
|
Err(NoSolution)
|
||||||
}
|
}
|
||||||
|
@ -132,13 +135,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
|
|
||||||
let tcx = ecx.tcx();
|
let tcx = ecx.tcx();
|
||||||
|
|
||||||
ecx.probe(|ecx| {
|
ecx.probe_candidate(
|
||||||
|
|ecx| {
|
||||||
let nested_obligations = tcx
|
let nested_obligations = tcx
|
||||||
.predicates_of(goal.predicate.def_id())
|
.predicates_of(goal.predicate.def_id())
|
||||||
.instantiate(tcx, goal.predicate.trait_ref.substs);
|
.instantiate(tcx, goal.predicate.trait_ref.substs);
|
||||||
ecx.add_goals(nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)));
|
ecx.add_goals(nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)));
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
})
|
},
|
||||||
|
|| "trait alias".into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consider_builtin_sized_candidate(
|
fn consider_builtin_sized_candidate(
|
||||||
|
@ -344,7 +350,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
if b_ty.is_ty_var() {
|
if b_ty.is_ty_var() {
|
||||||
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
|
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
|
||||||
}
|
}
|
||||||
ecx.probe(|ecx| {
|
ecx.probe_candidate(
|
||||||
|
|ecx| {
|
||||||
match (a_ty.kind(), b_ty.kind()) {
|
match (a_ty.kind(), b_ty.kind()) {
|
||||||
// Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
|
// Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
|
||||||
(&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
|
(&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
|
||||||
|
@ -440,13 +447,19 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
// Similar to ADTs, require that the rest of the fields are equal.
|
// Similar to ADTs, require that the rest of the fields are equal.
|
||||||
ecx.add_goal(goal.with(
|
ecx.add_goal(goal.with(
|
||||||
tcx,
|
tcx,
|
||||||
ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
|
ty::TraitRef::new(
|
||||||
|
tcx,
|
||||||
|
goal.predicate.def_id(),
|
||||||
|
[*a_last_ty, *b_last_ty],
|
||||||
|
),
|
||||||
));
|
));
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
}
|
}
|
||||||
_ => Err(NoSolution),
|
_ => Err(NoSolution),
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
|| "builtin unsize".into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consider_builtin_dyn_upcast_candidates(
|
fn consider_builtin_dyn_upcast_candidates(
|
||||||
|
@ -475,8 +488,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
|
let mut unsize_dyn_to_principal =
|
||||||
ecx.probe(|ecx| -> Result<_, NoSolution> {
|
|principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
|
||||||
|
ecx.probe_candidate(
|
||||||
|
|ecx| -> Result<_, NoSolution> {
|
||||||
// Require that all of the trait predicates from A match B, except for
|
// Require that all of the trait predicates from A match B, except for
|
||||||
// the auto traits. We do this by constructing a new A type with B's
|
// the auto traits. We do this by constructing a new A type with B's
|
||||||
// auto traits, and equating these types.
|
// auto traits, and equating these types.
|
||||||
|
@ -497,11 +512,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
|
|
||||||
// We also require that A's lifetime outlives B's lifetime.
|
// We also require that A's lifetime outlives B's lifetime.
|
||||||
ecx.eq(goal.param_env, new_a_ty, b_ty)?;
|
ecx.eq(goal.param_env, new_a_ty, b_ty)?;
|
||||||
ecx.add_goal(
|
ecx.add_goal(goal.with(
|
||||||
goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))),
|
tcx,
|
||||||
);
|
ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
|
||||||
|
));
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
})
|
},
|
||||||
|
|| "upcast dyn to principle".into(),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut responses = vec![];
|
let mut responses = vec![];
|
||||||
|
@ -698,7 +716,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
goal: Goal<'tcx, TraitPredicate<'tcx>>,
|
goal: Goal<'tcx, TraitPredicate<'tcx>>,
|
||||||
constituent_tys: impl Fn(&EvalCtxt<'_, 'tcx>, Ty<'tcx>) -> Result<Vec<Ty<'tcx>>, NoSolution>,
|
constituent_tys: impl Fn(&EvalCtxt<'_, 'tcx>, Ty<'tcx>) -> Result<Vec<Ty<'tcx>>, NoSolution>,
|
||||||
) -> QueryResult<'tcx> {
|
) -> QueryResult<'tcx> {
|
||||||
self.probe(|ecx| {
|
self.probe_candidate(
|
||||||
|
|ecx| {
|
||||||
ecx.add_goals(
|
ecx.add_goals(
|
||||||
constituent_tys(ecx, goal.predicate.self_ty())?
|
constituent_tys(ecx, goal.predicate.self_ty())?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -711,7 +730,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
);
|
);
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
})
|
},
|
||||||
|
|| "constituent tys".into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue