introduce enter_forall

This commit is contained in:
Boxy
2024-01-30 01:27:06 +00:00
parent af88f7db51
commit ac559af98f
15 changed files with 524 additions and 446 deletions

View File

@@ -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()),
}

View File

@@ -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>>,

View File

@@ -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),
}
})
}
}

View File

@@ -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)