introduce enter_forall
This commit is contained in:
@@ -20,7 +20,7 @@ use crate::solve::EvalCtxt;
|
||||
pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
|
||||
ecx: &EvalCtxt<'_, 'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Result<Vec<Ty<'tcx>>, NoSolution> {
|
||||
) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution> {
|
||||
let tcx = ecx.tcx();
|
||||
match *ty.kind() {
|
||||
ty::Uint(_)
|
||||
@@ -34,7 +34,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
|
||||
| ty::Char => Ok(vec![]),
|
||||
|
||||
// Treat `str` like it's defined as `struct str([u8]);`
|
||||
ty::Str => Ok(vec![Ty::new_slice(tcx, tcx.types.u8)]),
|
||||
ty::Str => Ok(vec![ty::Binder::dummy(Ty::new_slice(tcx, tcx.types.u8))]),
|
||||
|
||||
ty::Dynamic(..)
|
||||
| ty::Param(..)
|
||||
@@ -47,46 +47,46 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
|
||||
}
|
||||
|
||||
ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => {
|
||||
Ok(vec![element_ty])
|
||||
Ok(vec![ty::Binder::dummy(element_ty)])
|
||||
}
|
||||
|
||||
ty::Array(element_ty, _) | ty::Slice(element_ty) => Ok(vec![element_ty]),
|
||||
ty::Array(element_ty, _) | ty::Slice(element_ty) => Ok(vec![ty::Binder::dummy(element_ty)]),
|
||||
|
||||
ty::Tuple(tys) => {
|
||||
// (T1, ..., Tn) -- meets any bound that all of T1...Tn meet
|
||||
Ok(tys.iter().collect())
|
||||
Ok(tys.iter().map(ty::Binder::dummy).collect())
|
||||
}
|
||||
|
||||
ty::Closure(_, args) => Ok(vec![args.as_closure().tupled_upvars_ty()]),
|
||||
ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]),
|
||||
|
||||
ty::CoroutineClosure(_, args) => Ok(vec![args.as_coroutine_closure().tupled_upvars_ty()]),
|
||||
|
||||
ty::Coroutine(_, args) => {
|
||||
let coroutine_args = args.as_coroutine();
|
||||
Ok(vec![coroutine_args.tupled_upvars_ty(), coroutine_args.witness()])
|
||||
Ok(vec![
|
||||
ty::Binder::dummy(coroutine_args.tupled_upvars_ty()),
|
||||
ty::Binder::dummy(coroutine_args.witness()),
|
||||
])
|
||||
}
|
||||
|
||||
ty::CoroutineWitness(def_id, args) => Ok(ecx
|
||||
.tcx()
|
||||
.coroutine_hidden_types(def_id)
|
||||
.map(|bty| {
|
||||
ecx.instantiate_binder_with_placeholders(replace_erased_lifetimes_with_bound_vars(
|
||||
tcx,
|
||||
bty.instantiate(tcx, args),
|
||||
))
|
||||
})
|
||||
.map(|bty| replace_erased_lifetimes_with_bound_vars(tcx, bty.instantiate(tcx, args)))
|
||||
.collect()),
|
||||
|
||||
// For `PhantomData<T>`, we pass `T`.
|
||||
ty::Adt(def, args) if def.is_phantom_data() => Ok(vec![args.type_at(0)]),
|
||||
ty::Adt(def, args) if def.is_phantom_data() => Ok(vec![ty::Binder::dummy(args.type_at(0))]),
|
||||
|
||||
ty::Adt(def, args) => Ok(def.all_fields().map(|f| f.ty(tcx, args)).collect()),
|
||||
ty::Adt(def, args) => {
|
||||
Ok(def.all_fields().map(|f| ty::Binder::dummy(f.ty(tcx, args))).collect())
|
||||
}
|
||||
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
|
||||
// We can resolve the `impl Trait` to its concrete type,
|
||||
// which enforces a DAG between the functions requiring
|
||||
// the auto trait bounds in question.
|
||||
Ok(vec![tcx.type_of(def_id).instantiate(tcx, args)])
|
||||
Ok(vec![ty::Binder::dummy(tcx.type_of(def_id).instantiate(tcx, args))])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,7 @@ pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>(
|
||||
pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
|
||||
ecx: &EvalCtxt<'_, 'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Result<Vec<Ty<'tcx>>, NoSolution> {
|
||||
) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution> {
|
||||
match *ty.kind() {
|
||||
ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
|
||||
| ty::Uint(_)
|
||||
@@ -150,11 +150,11 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
|
||||
bug!("unexpected type `{ty}`")
|
||||
}
|
||||
|
||||
ty::Tuple(tys) => Ok(tys.to_vec()),
|
||||
ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()),
|
||||
|
||||
ty::Adt(def, args) => {
|
||||
let sized_crit = def.sized_constraint(ecx.tcx());
|
||||
Ok(sized_crit.iter_instantiated(ecx.tcx(), args).collect())
|
||||
Ok(sized_crit.iter_instantiated(ecx.tcx(), args).map(ty::Binder::dummy).collect())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,7 +163,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
|
||||
pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
|
||||
ecx: &EvalCtxt<'_, 'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Result<Vec<Ty<'tcx>>, NoSolution> {
|
||||
) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution> {
|
||||
match *ty.kind() {
|
||||
ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Ok(vec![]),
|
||||
|
||||
@@ -194,9 +194,9 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
|
||||
bug!("unexpected type `{ty}`")
|
||||
}
|
||||
|
||||
ty::Tuple(tys) => Ok(tys.to_vec()),
|
||||
ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()),
|
||||
|
||||
ty::Closure(_, args) => Ok(vec![args.as_closure().tupled_upvars_ty()]),
|
||||
ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]),
|
||||
|
||||
ty::CoroutineClosure(..) => Err(NoSolution),
|
||||
|
||||
@@ -205,7 +205,10 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
|
||||
Movability::Movable => {
|
||||
if ecx.tcx().features().coroutine_clone {
|
||||
let coroutine = args.as_coroutine();
|
||||
Ok(vec![coroutine.tupled_upvars_ty(), coroutine.witness()])
|
||||
Ok(vec![
|
||||
ty::Binder::dummy(coroutine.tupled_upvars_ty()),
|
||||
ty::Binder::dummy(coroutine.witness()),
|
||||
])
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
@@ -216,10 +219,10 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
|
||||
.tcx()
|
||||
.coroutine_hidden_types(def_id)
|
||||
.map(|bty| {
|
||||
ecx.instantiate_binder_with_placeholders(replace_erased_lifetimes_with_bound_vars(
|
||||
replace_erased_lifetimes_with_bound_vars(
|
||||
ecx.tcx(),
|
||||
bty.instantiate(ecx.tcx(), args),
|
||||
))
|
||||
)
|
||||
})
|
||||
.collect()),
|
||||
}
|
||||
|
||||
@@ -477,10 +477,12 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let kind = self.infcx.instantiate_binder_with_placeholders(kind);
|
||||
let goal = goal.with(self.tcx(), ty::Binder::dummy(kind));
|
||||
self.add_goal(GoalSource::Misc, goal);
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
self.infcx.enter_forall(kind, |kind| {
|
||||
let goal = goal.with(self.tcx(), ty::Binder::dummy(kind));
|
||||
self.add_goal(GoalSource::Misc, goal);
|
||||
// FIXME(tree_universes): leaking universes
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -801,13 +803,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn instantiate_binder_with_placeholders<T: TypeFoldable<TyCtxt<'tcx>> + Copy>(
|
||||
pub(super) fn enter_forall<T: TypeFoldable<TyCtxt<'tcx>> + Copy, U>(
|
||||
&self,
|
||||
value: ty::Binder<'tcx, T>,
|
||||
) -> T {
|
||||
self.infcx.instantiate_binder_with_placeholders(value)
|
||||
f: impl FnOnce(T) -> U,
|
||||
) -> U {
|
||||
self.infcx.enter_forall(value, f)
|
||||
}
|
||||
|
||||
pub(super) fn resolve_vars_if_possible<T>(&self, value: T) -> T
|
||||
where
|
||||
T: TypeFoldable<TyCtxt<'tcx>>,
|
||||
|
||||
@@ -31,103 +31,109 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
|
||||
) -> SelectionResult<'tcx, Selection<'tcx>> {
|
||||
assert!(self.next_trait_solver());
|
||||
|
||||
let trait_goal = Goal::new(
|
||||
self.tcx,
|
||||
obligation.param_env,
|
||||
self.instantiate_binder_with_placeholders(obligation.predicate),
|
||||
);
|
||||
// FIXME(tree_universes): leaking universes?
|
||||
self.enter_forall(obligation.predicate, |pred| {
|
||||
let trait_goal = Goal::new(self.tcx, obligation.param_env, pred);
|
||||
|
||||
let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::Never, |ecx| {
|
||||
let goal = Goal::new(ecx.tcx(), trait_goal.param_env, trait_goal.predicate);
|
||||
let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal);
|
||||
let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal);
|
||||
let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::Never, |ecx| {
|
||||
let goal = Goal::new(ecx.tcx(), trait_goal.param_env, trait_goal.predicate);
|
||||
let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal);
|
||||
let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal);
|
||||
|
||||
// pseudo-winnow
|
||||
if candidates.len() == 0 {
|
||||
return Err(SelectionError::Unimplemented);
|
||||
} else if candidates.len() > 1 {
|
||||
let mut i = 0;
|
||||
while i < candidates.len() {
|
||||
let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| {
|
||||
candidate_should_be_dropped_in_favor_of(
|
||||
ecx.tcx(),
|
||||
&candidates[i],
|
||||
&candidates[j],
|
||||
)
|
||||
});
|
||||
if should_drop_i {
|
||||
candidates.swap_remove(i);
|
||||
} else {
|
||||
i += 1;
|
||||
if i > 1 {
|
||||
return Ok(None);
|
||||
// pseudo-winnow
|
||||
if candidates.len() == 0 {
|
||||
return Err(SelectionError::Unimplemented);
|
||||
} else if candidates.len() > 1 {
|
||||
let mut i = 0;
|
||||
while i < candidates.len() {
|
||||
let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| {
|
||||
candidate_should_be_dropped_in_favor_of(
|
||||
ecx.tcx(),
|
||||
&candidates[i],
|
||||
&candidates[j],
|
||||
)
|
||||
});
|
||||
if should_drop_i {
|
||||
candidates.swap_remove(i);
|
||||
} else {
|
||||
i += 1;
|
||||
if i > 1 {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let candidate = candidates.pop().unwrap();
|
||||
let (certainty, nested_goals) = ecx
|
||||
.instantiate_and_apply_query_response(
|
||||
trait_goal.param_env,
|
||||
orig_values,
|
||||
candidate.result,
|
||||
)
|
||||
.map_err(|_| SelectionError::Unimplemented)?;
|
||||
|
||||
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
|
||||
.into_iter()
|
||||
.map(|goal| {
|
||||
Obligation::new(
|
||||
self.tcx,
|
||||
ObligationCause::dummy(),
|
||||
goal.param_env,
|
||||
goal.predicate,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let goal = self.resolve_vars_if_possible(trait_goal);
|
||||
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)) => {
|
||||
rematch_impl(self, goal, def_id, nested_obligations)
|
||||
}
|
||||
|
||||
// If an unsize goal is ambiguous, then we can manually rematch it to make
|
||||
// selection progress for coercion during HIR typeck. If it is *not* ambiguous,
|
||||
// but is `BuiltinImplSource::Misc`, it may have nested `Unsize` goals,
|
||||
// and we need to rematch those to detect tuple unsizing and trait upcasting.
|
||||
// FIXME: This will be wrong if we have param-env or where-clause bounds
|
||||
// with the unsize goal -- we may need to mark those with different impl
|
||||
// sources.
|
||||
(Certainty::Maybe(_), CandidateSource::BuiltinImpl(src))
|
||||
| (Certainty::Yes, CandidateSource::BuiltinImpl(src @ BuiltinImplSource::Misc))
|
||||
if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) =>
|
||||
{
|
||||
rematch_unsize(self, goal, nested_obligations, src, certainty)
|
||||
}
|
||||
|
||||
// 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(src)) => {
|
||||
Ok(Some(ImplSource::Builtin(src, nested_obligations)))
|
||||
}
|
||||
|
||||
// It's fine not to do anything to rematch these, since there are no
|
||||
// nested obligations.
|
||||
(Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => {
|
||||
Ok(Some(ImplSource::Param(nested_obligations)))
|
||||
}
|
||||
|
||||
(Certainty::Maybe(_), _) => Ok(None),
|
||||
}
|
||||
|
||||
let candidate = candidates.pop().unwrap();
|
||||
let (certainty, nested_goals) = ecx
|
||||
.instantiate_and_apply_query_response(
|
||||
trait_goal.param_env,
|
||||
orig_values,
|
||||
candidate.result,
|
||||
)
|
||||
.map_err(|_| SelectionError::Unimplemented)?;
|
||||
|
||||
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
|
||||
.into_iter()
|
||||
.map(|goal| {
|
||||
Obligation::new(self.tcx, ObligationCause::dummy(), goal.param_env, goal.predicate)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let goal = self.resolve_vars_if_possible(trait_goal);
|
||||
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)) => {
|
||||
rematch_impl(self, goal, def_id, nested_obligations)
|
||||
}
|
||||
|
||||
// If an unsize goal is ambiguous, then we can manually rematch it to make
|
||||
// selection progress for coercion during HIR typeck. If it is *not* ambiguous,
|
||||
// but is `BuiltinImplSource::Misc`, it may have nested `Unsize` goals,
|
||||
// and we need to rematch those to detect tuple unsizing and trait upcasting.
|
||||
// FIXME: This will be wrong if we have param-env or where-clause bounds
|
||||
// with the unsize goal -- we may need to mark those with different impl
|
||||
// sources.
|
||||
(Certainty::Maybe(_), CandidateSource::BuiltinImpl(src))
|
||||
| (Certainty::Yes, CandidateSource::BuiltinImpl(src @ BuiltinImplSource::Misc))
|
||||
if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) =>
|
||||
{
|
||||
rematch_unsize(self, goal, nested_obligations, src, certainty)
|
||||
}
|
||||
|
||||
// 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(src)) => {
|
||||
Ok(Some(ImplSource::Builtin(src, nested_obligations)))
|
||||
}
|
||||
|
||||
// It's fine not to do anything to rematch these, since there are no
|
||||
// nested obligations.
|
||||
(Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => {
|
||||
Ok(Some(ImplSource::Param(nested_obligations)))
|
||||
}
|
||||
|
||||
(Certainty::Maybe(_), _) => Ok(None),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1049,14 +1049,22 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
fn probe_and_evaluate_goal_for_constituent_tys(
|
||||
&mut self,
|
||||
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::Binder<'tcx, Ty<'tcx>>>, NoSolution>,
|
||||
) -> QueryResult<'tcx> {
|
||||
self.probe_misc_candidate("constituent tys").enter(|ecx| {
|
||||
ecx.add_goals(
|
||||
GoalSource::ImplWhereBound,
|
||||
constituent_tys(ecx, goal.predicate.self_ty())?
|
||||
.into_iter()
|
||||
.map(|ty| goal.with(ecx.tcx(), goal.predicate.with_self_ty(ecx.tcx(), ty)))
|
||||
.map(|ty| {
|
||||
// FIXME(tree_universes): leaking universes
|
||||
ecx.enter_forall(ty, |ty| {
|
||||
goal.with(ecx.tcx(), goal.predicate.with_self_ty(ecx.tcx(), ty))
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
|
||||
Reference in New Issue
Block a user