Helpers for creating EvalCtxts, some comments
This commit is contained in:
parent
298c0d1a62
commit
f3f8793268
3 changed files with 173 additions and 156 deletions
|
@ -142,15 +142,34 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
|
||||||
Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>,
|
Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>,
|
||||||
Option<inspect::GoalEvaluation<'tcx>>,
|
Option<inspect::GoalEvaluation<'tcx>>,
|
||||||
) {
|
) {
|
||||||
let mode = if self.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
|
EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
|
||||||
let mut search_graph = search_graph::SearchGraph::new(self.tcx, mode);
|
ecx.evaluate_goal(IsNormalizesToHack::No, goal)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
|
pub(super) fn solver_mode(&self) -> SolverMode {
|
||||||
|
self.search_graph.solver_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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`]).
|
||||||
|
fn enter_root<R>(
|
||||||
|
infcx: &InferCtxt<'tcx>,
|
||||||
|
generate_proof_tree: GenerateProofTree,
|
||||||
|
f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> R,
|
||||||
|
) -> (R, Option<inspect::GoalEvaluation<'tcx>>) {
|
||||||
|
let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
|
||||||
|
let mut search_graph = search_graph::SearchGraph::new(infcx.tcx, mode);
|
||||||
|
|
||||||
let mut ecx = EvalCtxt {
|
let mut ecx = EvalCtxt {
|
||||||
search_graph: &mut search_graph,
|
search_graph: &mut search_graph,
|
||||||
infcx: self,
|
infcx: infcx,
|
||||||
// Only relevant when canonicalizing the response,
|
// Only relevant when canonicalizing the response,
|
||||||
// which we don't do within this evaluation context.
|
// which we don't do within this evaluation context.
|
||||||
predefined_opaques_in_body: self
|
predefined_opaques_in_body: infcx
|
||||||
.tcx
|
.tcx
|
||||||
.mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
|
.mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
|
||||||
// Only relevant when canonicalizing the response.
|
// Only relevant when canonicalizing the response.
|
||||||
|
@ -158,12 +177,12 @@ 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: (self.tcx.sess.opts.unstable_opts.dump_solver_proof_tree
|
inspect: (infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree
|
||||||
|| matches!(generate_proof_tree, GenerateProofTree::Yes))
|
|| matches!(generate_proof_tree, GenerateProofTree::Yes))
|
||||||
.then(ProofTreeBuilder::new_root)
|
.then(ProofTreeBuilder::new_root)
|
||||||
.unwrap_or_else(ProofTreeBuilder::new_noop),
|
.unwrap_or_else(ProofTreeBuilder::new_noop),
|
||||||
};
|
};
|
||||||
let result = ecx.evaluate_goal(IsNormalizesToHack::No, goal);
|
let result = f(&mut ecx);
|
||||||
|
|
||||||
let tree = ecx.inspect.finalize();
|
let tree = ecx.inspect.finalize();
|
||||||
if let Some(tree) = &tree {
|
if let Some(tree) = &tree {
|
||||||
|
@ -179,39 +198,22 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
|
||||||
assert!(search_graph.is_empty());
|
assert!(search_graph.is_empty());
|
||||||
(result, tree)
|
(result, tree)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
/// Creates a nested evaluation context that shares the same search graph as the
|
||||||
pub(super) fn solver_mode(&self) -> SolverMode {
|
/// one passed in. This is suitable for evaluation, granted that the search graph
|
||||||
self.search_graph.solver_mode()
|
/// has had the nested goal recorded on its stack ([`SearchGraph::with_new_goal`]),
|
||||||
}
|
/// but it's preferable to use other methods that call this one rather than this
|
||||||
|
/// method directly.
|
||||||
/// The entry point of the solver.
|
|
||||||
///
|
///
|
||||||
/// This function deals with (coinductive) cycles, overflow, and caching
|
/// This function takes care of setting up the inference context, setting the anchor,
|
||||||
/// and then calls [`EvalCtxt::compute_goal`] which contains the actual
|
/// and registering opaques from the canonicalized input.
|
||||||
/// logic of the solver.
|
fn enter_canonical<R>(
|
||||||
///
|
|
||||||
/// 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
|
|
||||||
/// outside of it.
|
|
||||||
#[instrument(level = "debug", skip(tcx, search_graph, goal_evaluation), ret)]
|
|
||||||
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 ProofTreeBuilder<'tcx>,
|
goal_evaluation: &mut ProofTreeBuilder<'tcx>,
|
||||||
) -> QueryResult<'tcx> {
|
f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>, Goal<'tcx, ty::Predicate<'tcx>>) -> R,
|
||||||
goal_evaluation.canonicalized_goal(canonical_input);
|
) -> R {
|
||||||
|
|
||||||
// Deal with overflow, caching, and coinduction.
|
|
||||||
//
|
|
||||||
// The actual solver logic happens in `ecx.compute_goal`.
|
|
||||||
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,
|
||||||
|
@ -240,14 +242,11 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ecx.nested_goals.is_empty() {
|
if !ecx.nested_goals.is_empty() {
|
||||||
panic!(
|
panic!("prepopulating opaque types shouldn't add goals: {:?}", ecx.nested_goals);
|
||||||
"prepopulating opaque types shouldn't add goals: {:?}",
|
|
||||||
ecx.nested_goals
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = ecx.compute_goal(input.goal);
|
let result = f(&mut ecx, input.goal);
|
||||||
ecx.inspect.query_result(result);
|
|
||||||
goal_evaluation.goal_evaluation_step(ecx.inspect);
|
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
|
||||||
|
@ -259,6 +258,45 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The entry point of the solver.
|
||||||
|
///
|
||||||
|
/// This function deals with (coinductive) cycles, overflow, and caching
|
||||||
|
/// and then calls [`EvalCtxt::compute_goal`] which contains the actual
|
||||||
|
/// logic of the solver.
|
||||||
|
///
|
||||||
|
/// 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
|
||||||
|
/// outside of it.
|
||||||
|
#[instrument(level = "debug", skip(tcx, search_graph, goal_evaluation), ret)]
|
||||||
|
fn evaluate_canonical_goal(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
search_graph: &'a mut search_graph::SearchGraph<'tcx>,
|
||||||
|
canonical_input: CanonicalInput<'tcx>,
|
||||||
|
mut goal_evaluation: &mut ProofTreeBuilder<'tcx>,
|
||||||
|
) -> QueryResult<'tcx> {
|
||||||
|
goal_evaluation.canonicalized_goal(canonical_input);
|
||||||
|
|
||||||
|
// Deal with overflow, caching, and coinduction.
|
||||||
|
//
|
||||||
|
// The actual solver logic happens in `ecx.compute_goal`.
|
||||||
|
search_graph.with_new_goal(
|
||||||
|
tcx,
|
||||||
|
canonical_input,
|
||||||
|
goal_evaluation,
|
||||||
|
|search_graph, goal_evaluation| {
|
||||||
|
EvalCtxt::enter_canonical(
|
||||||
|
tcx,
|
||||||
|
search_graph,
|
||||||
|
canonical_input,
|
||||||
|
goal_evaluation,
|
||||||
|
|ecx, goal| {
|
||||||
|
let result = ecx.compute_goal(goal);
|
||||||
|
ecx.inspect.query_result(result);
|
||||||
|
result
|
||||||
|
},
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,23 @@
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt};
|
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
|
||||||
use rustc_infer::traits::util::supertraits;
|
use rustc_infer::traits::util::supertraits;
|
||||||
use rustc_infer::traits::{
|
use rustc_infer::traits::{
|
||||||
Obligation, PredicateObligation, Selection, SelectionResult, TraitObligation,
|
Obligation, PredicateObligation, Selection, SelectionResult, TraitObligation,
|
||||||
};
|
};
|
||||||
use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
|
use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal};
|
||||||
use rustc_middle::traits::solve::{Certainty, Goal, PredefinedOpaquesData, QueryInput};
|
|
||||||
use rustc_middle::traits::{
|
use rustc_middle::traits::{
|
||||||
DefiningAnchor, ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData,
|
ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData,
|
||||||
ImplSourceUserDefinedData, ObligationCause, SelectionError,
|
ObligationCause, SelectionError,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::{self, TyCtxt};
|
use rustc_middle::ty::{self, TyCtxt};
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
|
|
||||||
use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource};
|
use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource};
|
||||||
use crate::solve::eval_ctxt::{EvalCtxt, NestedGoals};
|
use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree};
|
||||||
use crate::solve::inspect::ProofTreeBuilder;
|
use crate::solve::inspect::ProofTreeBuilder;
|
||||||
use crate::solve::search_graph::SearchGraph;
|
use crate::solve::search_graph::OverflowHandler;
|
||||||
use crate::solve::SolverMode;
|
|
||||||
use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
|
use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
|
||||||
|
|
||||||
pub trait InferCtxtSelectExt<'tcx> {
|
pub trait InferCtxtSelectExt<'tcx> {
|
||||||
|
@ -36,30 +34,14 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
|
||||||
) -> SelectionResult<'tcx, Selection<'tcx>> {
|
) -> SelectionResult<'tcx, Selection<'tcx>> {
|
||||||
assert!(self.next_trait_solver());
|
assert!(self.next_trait_solver());
|
||||||
|
|
||||||
let goal = Goal::new(
|
let trait_goal = Goal::new(
|
||||||
self.tcx,
|
self.tcx,
|
||||||
obligation.param_env,
|
obligation.param_env,
|
||||||
self.instantiate_binder_with_placeholders(obligation.predicate),
|
self.instantiate_binder_with_placeholders(obligation.predicate),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mode = if self.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
|
let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::No, |ecx| {
|
||||||
let mut search_graph = SearchGraph::new(self.tcx, mode);
|
let goal = Goal::new(ecx.tcx(), trait_goal.param_env, trait_goal.predicate);
|
||||||
let mut ecx = EvalCtxt {
|
|
||||||
search_graph: &mut search_graph,
|
|
||||||
infcx: self,
|
|
||||||
// Only relevant when canonicalizing the response,
|
|
||||||
// which we don't do within this evaluation context.
|
|
||||||
predefined_opaques_in_body: self
|
|
||||||
.tcx
|
|
||||||
.mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
|
|
||||||
// Only relevant when canonicalizing the response.
|
|
||||||
max_input_universe: ty::UniverseIndex::ROOT,
|
|
||||||
var_values: CanonicalVarValues::dummy(),
|
|
||||||
nested_goals: NestedGoals::new(),
|
|
||||||
tainted: Ok(()),
|
|
||||||
inspect: ProofTreeBuilder::new_noop(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal);
|
let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal);
|
||||||
let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal);
|
let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal);
|
||||||
|
|
||||||
|
@ -85,10 +67,21 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
|
||||||
|
|
||||||
let candidate = candidates.pop().unwrap();
|
let candidate = candidates.pop().unwrap();
|
||||||
let (certainty, nested_goals) = ecx
|
let (certainty, nested_goals) = ecx
|
||||||
.instantiate_and_apply_query_response(goal.param_env, orig_values, candidate.result)
|
.instantiate_and_apply_query_response(
|
||||||
|
trait_goal.param_env,
|
||||||
|
orig_values,
|
||||||
|
candidate.result,
|
||||||
|
)
|
||||||
.map_err(|_| SelectionError::Unimplemented)?;
|
.map_err(|_| SelectionError::Unimplemented)?;
|
||||||
|
|
||||||
let goal = self.resolve_vars_if_possible(goal);
|
Ok(Some((candidate, certainty, nested_goals)))
|
||||||
|
});
|
||||||
|
|
||||||
|
let (candidate, certainty, nested_goals) = match result {
|
||||||
|
Ok(Some((candidate, certainty, nested_goals))) => (candidate, certainty, nested_goals),
|
||||||
|
Ok(None) => return Ok(None),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
let nested_obligations: Vec<_> = nested_goals
|
let nested_obligations: Vec<_> = nested_goals
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -97,15 +90,18 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if let Certainty::Maybe(_) = certainty {
|
let goal = self.resolve_vars_if_possible(trait_goal);
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
match (certainty, candidate.source) {
|
match (certainty, candidate.source) {
|
||||||
|
// Rematching the implementation will instantiate the same nested goals that
|
||||||
|
// would have caused the ambiguity, so we can still make progress here regardless.
|
||||||
(_, CandidateSource::Impl(def_id)) => {
|
(_, CandidateSource::Impl(def_id)) => {
|
||||||
rematch_impl(self, goal, def_id, nested_obligations)
|
rematch_impl(self, goal, def_id, nested_obligations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rematching the dyn upcast or object goal will instantiate the same nested
|
||||||
|
// goals that would have caused the ambiguity, so we can still make progress here
|
||||||
|
// regardless.
|
||||||
|
// FIXME: This doesn't actually check the object bounds hold here.
|
||||||
(
|
(
|
||||||
_,
|
_,
|
||||||
CandidateSource::BuiltinImpl(
|
CandidateSource::BuiltinImpl(
|
||||||
|
@ -113,16 +109,16 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
|
||||||
),
|
),
|
||||||
) => rematch_object(self, goal, nested_obligations),
|
) => rematch_object(self, goal, nested_obligations),
|
||||||
|
|
||||||
|
// Technically some builtin impls have nested obligations, but if
|
||||||
|
// `Certainty::Yes`, then they should've all been verified and don't
|
||||||
|
// need re-checking.
|
||||||
(Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => {
|
(Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => {
|
||||||
// technically some builtin impls have nested obligations, but if
|
|
||||||
// `Certainty::Yes`, then they should've all been verified by the
|
|
||||||
// evaluation above.
|
|
||||||
Ok(Some(ImplSource::Builtin(nested_obligations)))
|
Ok(Some(ImplSource::Builtin(nested_obligations)))
|
||||||
}
|
}
|
||||||
|
|
||||||
(Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => {
|
|
||||||
// It's fine not to do anything to rematch these, since there are no
|
// It's fine not to do anything to rematch these, since there are no
|
||||||
// nested obligations.
|
// nested obligations.
|
||||||
|
(Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => {
|
||||||
Ok(Some(ImplSource::Param(nested_obligations, ty::BoundConstness::NotConst)))
|
Ok(Some(ImplSource::Param(nested_obligations, ty::BoundConstness::NotConst)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,44 +131,31 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
|
||||||
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
fn compute_canonical_trait_candidates(
|
fn compute_canonical_trait_candidates(
|
||||||
&mut self,
|
&mut self,
|
||||||
canonical_input: Canonical<'tcx, QueryInput<'tcx, ty::TraitPredicate<'tcx>>>,
|
canonical_input: CanonicalInput<'tcx>,
|
||||||
) -> Vec<Candidate<'tcx>> {
|
) -> Vec<Candidate<'tcx>> {
|
||||||
let intercrate = match self.search_graph.solver_mode() {
|
// This doesn't record the canonical goal on the stack during the
|
||||||
SolverMode::Normal => false,
|
// candidate assembly step, but that's fine. Selection is conceptually
|
||||||
SolverMode::Coherence => true,
|
// outside of the solver, and if there were any cycles, we'd encounter
|
||||||
|
// the cycle anyways one step later.
|
||||||
|
EvalCtxt::enter_canonical(
|
||||||
|
self.tcx(),
|
||||||
|
self.search_graph(),
|
||||||
|
canonical_input,
|
||||||
|
// FIXME: This is wrong, idk if we even want to track stuff here.
|
||||||
|
&mut ProofTreeBuilder::new_noop(),
|
||||||
|
|ecx, goal| {
|
||||||
|
let trait_goal = Goal {
|
||||||
|
param_env: goal.param_env,
|
||||||
|
predicate: goal
|
||||||
|
.predicate
|
||||||
|
.to_opt_poly_trait_pred()
|
||||||
|
.expect("we canonicalized a trait goal")
|
||||||
|
.no_bound_vars()
|
||||||
|
.expect("we instantiated all bound vars"),
|
||||||
};
|
};
|
||||||
let (canonical_infcx, input, var_values) = self
|
ecx.assemble_and_evaluate_candidates(trait_goal)
|
||||||
.tcx()
|
},
|
||||||
.infer_ctxt()
|
)
|
||||||
.intercrate(intercrate)
|
|
||||||
.with_next_trait_solver(true)
|
|
||||||
.with_opaque_type_inference(canonical_input.value.anchor)
|
|
||||||
.build_with_canonical(DUMMY_SP, &canonical_input);
|
|
||||||
|
|
||||||
let mut ecx = EvalCtxt {
|
|
||||||
infcx: &canonical_infcx,
|
|
||||||
var_values,
|
|
||||||
predefined_opaques_in_body: input.predefined_opaques_in_body,
|
|
||||||
max_input_universe: canonical_input.max_universe,
|
|
||||||
search_graph: &mut self.search_graph,
|
|
||||||
nested_goals: NestedGoals::new(),
|
|
||||||
tainted: Ok(()),
|
|
||||||
inspect: ProofTreeBuilder::new_noop(),
|
|
||||||
};
|
|
||||||
|
|
||||||
for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
|
|
||||||
ecx.insert_hidden_type(key, input.goal.param_env, ty)
|
|
||||||
.expect("failed to prepopulate opaque types");
|
|
||||||
}
|
|
||||||
|
|
||||||
let candidates = ecx.assemble_and_evaluate_candidates(input.goal);
|
|
||||||
|
|
||||||
// We don't need the canonicalized context anymore
|
|
||||||
if input.anchor != DefiningAnchor::Error {
|
|
||||||
let _ = canonical_infcx.take_opaque_types();
|
|
||||||
}
|
|
||||||
|
|
||||||
candidates
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,6 @@ LL | for item in *things { *item = 0 }
|
||||||
= help: the trait `Sized` is not implemented for `<dyn Iterator<Item = &'a mut u8> as IntoIterator>::IntoIter`
|
= help: the trait `Sized` is not implemented for `<dyn Iterator<Item = &'a mut u8> as IntoIterator>::IntoIter`
|
||||||
= note: all local variables must have a statically known size
|
= note: all local variables must have a statically known size
|
||||||
= help: unsized locals are gated as an unstable feature
|
= help: unsized locals are gated as an unstable feature
|
||||||
help: consider further restricting the associated type
|
|
||||||
|
|
|
||||||
LL | fn changer<'a>(mut things: Box<dyn Iterator<Item=&'a mut u8>>) where <dyn Iterator<Item = &'a mut u8> as IntoIterator>::IntoIter: Sized {
|
|
||||||
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
|
|
||||||
error: the type `<dyn Iterator<Item = &'a mut u8> as IntoIterator>::IntoIter` is not well-formed
|
error: the type `<dyn Iterator<Item = &'a mut u8> as IntoIterator>::IntoIter` is not well-formed
|
||||||
--> $DIR/issue-20605.rs:5:17
|
--> $DIR/issue-20605.rs:5:17
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue