1
Fork 0

Auto merge of #120544 - BoxyUwU:enter_forall, r=lcnr

Introduce `enter_forall` to supercede `instantiate_binder_with_placeholders`

r? `@lcnr`

Long term we'd like to experiment with decrementing the universe count after "exiting" binders so that we do not end up creating infer vars in non-root universes even when they logically reside in the root universe. The fact that we dont do this currently results in a number of issues in the new trait solver where we consider goals to be ambiguous because otherwise it would require lowering the universe of an infer var. i.e. the goal  `?x.0 eq <T as Trait<?y.1>>::Assoc` where the alias is rigid would not be able to instantiate `?x` with the alias as there would be a universe error.

This PR is the first-ish sort of step towards being able to implement this as eventually we would want to decrement the universe in `enter_forall`. Unfortunately its Difficult to actually implement decrementing universes nicely so this is a separate step which moves us closer to the long term goal 
This commit is contained in:
bors 2024-02-08 16:42:56 +00:00
commit c29082fe7d
21 changed files with 629 additions and 549 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,48 @@ 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::CoroutineClosure(_, args) => {
Ok(vec![ty::Binder::dummy(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 +118,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 +152,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 +165,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 +196,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 +207,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 +221,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,11 @@ 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);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
}
@ -801,13 +802,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,108 @@ 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),
);
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

@ -140,7 +140,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
)
}
ty::PredicateKind::Subtype(pred) => {
let (a, b) = infcx.instantiate_binder_with_placeholders(
let (a, b) = infcx.enter_forall_and_leak_universe(
goal.predicate.kind().rebind((pred.a, pred.b)),
);
let expected_found = ExpectedFound::new(true, a, b);
@ -150,7 +150,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
)
}
ty::PredicateKind::Coerce(pred) => {
let (a, b) = infcx.instantiate_binder_with_placeholders(
let (a, b) = infcx.enter_forall_and_leak_universe(
goal.predicate.kind().rebind((pred.a, pred.b)),
);
let expected_found = ExpectedFound::new(false, a, b);

View file

@ -1049,14 +1049,21 @@ 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| {
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)

View file

@ -971,7 +971,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> {
let Goal { param_env, predicate } = goal.goal();
// For bound predicates we simply call `infcx.instantiate_binder_with_placeholders`
// For bound predicates we simply call `infcx.enter_forall`
// and then prove the resulting predicate as a nested goal.
let trait_ref = match predicate.kind().no_bound_vars() {
Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr))) => tr.trait_ref,

View file

@ -21,51 +21,62 @@ pub fn recompute_applicable_impls<'tcx>(
let impl_may_apply = |impl_def_id| {
let ocx = ObligationCtxt::new(infcx);
let placeholder_obligation =
infcx.instantiate_binder_with_placeholders(obligation.predicate);
let obligation_trait_ref =
ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref);
infcx.enter_forall(obligation.predicate, |placeholder_obligation| {
let obligation_trait_ref = ocx.normalize(
&ObligationCause::dummy(),
param_env,
placeholder_obligation.trait_ref,
);
let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args);
let impl_trait_ref = ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref);
let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
let impl_trait_ref =
tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args);
let impl_trait_ref =
ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref);
if let Err(_) =
ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref)
{
return false;
}
if let Err(_) =
ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref)
{
return false;
}
let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_args);
ocx.register_obligations(impl_predicates.predicates.iter().map(|&predicate| {
Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate)
}));
let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_args);
ocx.register_obligations(impl_predicates.predicates.iter().map(|&predicate| {
Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate)
}));
ocx.select_where_possible().is_empty()
ocx.select_where_possible().is_empty()
})
};
let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| {
let ocx = ObligationCtxt::new(infcx);
let placeholder_obligation =
infcx.instantiate_binder_with_placeholders(obligation.predicate);
let obligation_trait_ref =
ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref);
infcx.enter_forall(obligation.predicate, |placeholder_obligation| {
let obligation_trait_ref = ocx.normalize(
&ObligationCause::dummy(),
param_env,
placeholder_obligation.trait_ref,
);
let param_env_predicate = infcx.instantiate_binder_with_fresh_vars(
DUMMY_SP,
BoundRegionConversionTime::HigherRankedType,
poly_trait_predicate,
);
let param_env_trait_ref =
ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref);
let param_env_predicate = infcx.instantiate_binder_with_fresh_vars(
DUMMY_SP,
BoundRegionConversionTime::HigherRankedType,
poly_trait_predicate,
);
let param_env_trait_ref =
ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref);
if let Err(_) =
ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, param_env_trait_ref)
{
return false;
}
if let Err(_) = ocx.eq(
&ObligationCause::dummy(),
param_env,
obligation_trait_ref,
param_env_trait_ref,
) {
return false;
}
ocx.select_where_possible().is_empty()
ocx.select_where_possible().is_empty()
})
};
let mut ambiguities = Vec::new();

View file

@ -64,39 +64,44 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
) -> Option<(DefId, GenericArgsRef<'tcx>)> {
let tcx = self.tcx;
let param_env = obligation.param_env;
let trait_ref = self.instantiate_binder_with_placeholders(trait_ref);
let trait_self_ty = trait_ref.self_ty();
self.enter_forall(trait_ref, |trait_ref| {
let trait_self_ty = trait_ref.self_ty();
let mut self_match_impls = vec![];
let mut fuzzy_match_impls = vec![];
let mut self_match_impls = vec![];
let mut fuzzy_match_impls = vec![];
self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
let impl_args = self.fresh_args_for_item(obligation.cause.span, def_id);
let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().instantiate(tcx, impl_args);
self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
let impl_args = self.fresh_args_for_item(obligation.cause.span, def_id);
let impl_trait_ref =
tcx.impl_trait_ref(def_id).unwrap().instantiate(tcx, impl_args);
let impl_self_ty = impl_trait_ref.self_ty();
let impl_self_ty = impl_trait_ref.self_ty();
if self.can_eq(param_env, trait_self_ty, impl_self_ty) {
self_match_impls.push((def_id, impl_args));
if self.can_eq(param_env, trait_self_ty, impl_self_ty) {
self_match_impls.push((def_id, impl_args));
if iter::zip(trait_ref.args.types().skip(1), impl_trait_ref.args.types().skip(1))
if iter::zip(
trait_ref.args.types().skip(1),
impl_trait_ref.args.types().skip(1),
)
.all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some())
{
fuzzy_match_impls.push((def_id, impl_args));
{
fuzzy_match_impls.push((def_id, impl_args));
}
}
}
});
});
let impl_def_id_and_args = if self_match_impls.len() == 1 {
self_match_impls[0]
} else if fuzzy_match_impls.len() == 1 {
fuzzy_match_impls[0]
} else {
return None;
};
let impl_def_id_and_args = if self_match_impls.len() == 1 {
self_match_impls[0]
} else if fuzzy_match_impls.len() == 1 {
fuzzy_match_impls[0]
} else {
return None;
};
tcx.has_attr(impl_def_id_and_args.0, sym::rustc_on_unimplemented)
.then_some(impl_def_id_and_args)
tcx.has_attr(impl_def_id_and_args.0, sym::rustc_on_unimplemented)
.then_some(impl_def_id_and_args)
})
}
/// Used to set on_unimplemented's `ItemContext`

View file

@ -1248,52 +1248,55 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool {
let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
let ty = self.instantiate_binder_with_placeholders(self_ty);
let Some(generics) = self.tcx.hir().get_generics(obligation.cause.body_id) else {
return false;
};
let ty::Ref(_, inner_ty, hir::Mutability::Not) = ty.kind() else { return false };
let ty::Param(param) = inner_ty.kind() else { return false };
let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } =
obligation.cause.code()
else {
return false;
};
let arg_node = self.tcx.hir_node(*arg_hir_id);
let Node::Expr(Expr { kind: hir::ExprKind::Path(_), .. }) = arg_node else { return false };
self.enter_forall(self_ty, |ty: Ty<'_>| {
let Some(generics) = self.tcx.hir().get_generics(obligation.cause.body_id) else {
return false;
};
let ty::Ref(_, inner_ty, hir::Mutability::Not) = ty.kind() else { return false };
let ty::Param(param) = inner_ty.kind() else { return false };
let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } =
obligation.cause.code()
else {
return false;
};
let arg_node = self.tcx.hir_node(*arg_hir_id);
let Node::Expr(Expr { kind: hir::ExprKind::Path(_), .. }) = arg_node else {
return false;
};
let clone_trait = self.tcx.require_lang_item(LangItem::Clone, None);
let has_clone = |ty| {
self.type_implements_trait(clone_trait, [ty], obligation.param_env)
.must_apply_modulo_regions()
};
let clone_trait = self.tcx.require_lang_item(LangItem::Clone, None);
let has_clone = |ty| {
self.type_implements_trait(clone_trait, [ty], obligation.param_env)
.must_apply_modulo_regions()
};
let new_obligation = self.mk_trait_obligation_with_new_self_ty(
obligation.param_env,
trait_pred.map_bound(|trait_pred| (trait_pred, *inner_ty)),
);
if self.predicate_may_hold(&new_obligation) && has_clone(ty) {
if !has_clone(param.to_ty(self.tcx)) {
suggest_constraining_type_param(
self.tcx,
generics,
err,
param.name.as_str(),
"Clone",
Some(clone_trait),
None,
);
}
err.span_suggestion_verbose(
obligation.cause.span.shrink_to_hi(),
"consider using clone here",
".clone()",
Applicability::MaybeIncorrect,
let new_obligation = self.mk_trait_obligation_with_new_self_ty(
obligation.param_env,
trait_pred.map_bound(|trait_pred| (trait_pred, *inner_ty)),
);
return true;
}
false
if self.predicate_may_hold(&new_obligation) && has_clone(ty) {
if !has_clone(param.to_ty(self.tcx)) {
suggest_constraining_type_param(
self.tcx,
generics,
err,
param.name.as_str(),
"Clone",
Some(clone_trait),
None,
);
}
err.span_suggestion_verbose(
obligation.cause.span.shrink_to_hi(),
"consider using clone here",
".clone()".to_string(),
Applicability::MaybeIncorrect,
);
return true;
}
false
})
}
/// Extracts information about a callable type for diagnostics. This is a
@ -4038,26 +4041,27 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let Some(where_pred) = where_pred.as_trait_clause()
&& let Some(failed_pred) = failed_pred.to_opt_poly_trait_pred()
{
let where_pred = self.instantiate_binder_with_placeholders(where_pred);
let failed_pred = self.instantiate_binder_with_fresh_vars(
expr.span,
BoundRegionConversionTime::FnCall,
failed_pred,
);
self.enter_forall(where_pred, |where_pred| {
let failed_pred = self.instantiate_binder_with_fresh_vars(
expr.span,
BoundRegionConversionTime::FnCall,
failed_pred,
);
let zipped = iter::zip(where_pred.trait_ref.args, failed_pred.trait_ref.args);
for (expected, actual) in zipped {
self.probe(|_| {
match self.at(&ObligationCause::misc(expr.span, body_id), param_env).eq(
DefineOpaqueTypes::No,
expected,
actual,
) {
Ok(_) => (), // We ignore nested obligations here for now.
Err(err) => type_diffs.push(err),
}
})
}
let zipped =
iter::zip(where_pred.trait_ref.args, failed_pred.trait_ref.args);
for (expected, actual) in zipped {
self.probe(|_| {
match self
.at(&ObligationCause::misc(expr.span, body_id), param_env)
.eq(DefineOpaqueTypes::No, expected, actual)
{
Ok(_) => (), // We ignore nested obligations here for now.
Err(err) => type_diffs.push(err),
}
})
}
})
} else if let Some(where_pred) = where_pred.as_projection_clause()
&& let Some(failed_pred) = failed_pred.to_opt_poly_projection_pred()
&& let Some(found) = failed_pred.skip_binder().term.ty()
@ -4614,14 +4618,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
{
self.probe(|_| {
let ocx = ObligationCtxt::new(self);
let pred = self.instantiate_binder_with_placeholders(pred);
let pred = ocx.normalize(&ObligationCause::dummy(), param_env, pred);
ocx.register_obligation(Obligation::new(
self.tcx,
ObligationCause::dummy(),
param_env,
pred,
));
self.enter_forall(pred, |pred| {
let pred = ocx.normalize(&ObligationCause::dummy(), param_env, pred);
ocx.register_obligation(Obligation::new(
self.tcx,
ObligationCause::dummy(),
param_env,
pred,
));
});
if !ocx.select_where_possible().is_empty() {
// encountered errors.
return;
@ -4768,13 +4773,13 @@ fn hint_missing_borrow<'tcx>(
}
let found_args = match found.kind() {
ty::FnPtr(f) => infcx.instantiate_binder_with_placeholders(*f).inputs().iter(),
ty::FnPtr(f) => infcx.enter_forall(*f, |f| f.inputs().iter()),
kind => {
span_bug!(span, "found was converted to a FnPtr above but is now {:?}", kind)
}
};
let expected_args = match expected.kind() {
ty::FnPtr(f) => infcx.instantiate_binder_with_placeholders(*f).inputs().iter(),
ty::FnPtr(f) => infcx.enter_forall(*f, |f| f.inputs().iter()),
kind => {
span_bug!(span, "expected was converted to a FnPtr above but is now {:?}", kind)
}

View file

@ -1305,12 +1305,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut pred = obligation.predicate.to_opt_poly_trait_pred();
while let Some((next_code, next_pred)) = code.parent() {
if let Some(pred) = pred {
let pred = self.instantiate_binder_with_placeholders(pred);
diag.note(format!(
"`{}` must implement `{}`, but it does not",
pred.self_ty(),
pred.print_modifiers_and_trait_path()
));
self.enter_forall(pred, |pred| {
diag.note(format!(
"`{}` must implement `{}`, but it does not",
pred.self_ty(),
pred.print_modifiers_and_trait_path()
));
})
}
code = next_code;
pred = next_pred;
@ -2015,70 +2016,78 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let [single] = &impl_candidates {
if self.probe(|_| {
let ocx = ObligationCtxt::new(self);
let obligation_trait_ref = self.instantiate_binder_with_placeholders(trait_ref);
let impl_args = self.fresh_args_for_item(DUMMY_SP, single.impl_def_id);
let impl_trait_ref = ocx.normalize(
&ObligationCause::dummy(),
param_env,
ty::EarlyBinder::bind(single.trait_ref).instantiate(self.tcx, impl_args),
);
ocx.register_obligations(
self.tcx
.predicates_of(single.impl_def_id)
.instantiate(self.tcx, impl_args)
.into_iter()
.map(|(clause, _)| {
Obligation::new(self.tcx, ObligationCause::dummy(), param_env, clause)
}),
);
if !ocx.select_where_possible().is_empty() {
return false;
}
self.enter_forall(trait_ref, |obligation_trait_ref| {
let impl_args = self.fresh_args_for_item(DUMMY_SP, single.impl_def_id);
let impl_trait_ref = ocx.normalize(
&ObligationCause::dummy(),
param_env,
ty::EarlyBinder::bind(single.trait_ref).instantiate(self.tcx, impl_args),
);
let mut terrs = vec![];
for (obligation_arg, impl_arg) in
std::iter::zip(obligation_trait_ref.args, impl_trait_ref.args)
{
if let Err(terr) =
ocx.eq(&ObligationCause::dummy(), param_env, impl_arg, obligation_arg)
{
terrs.push(terr);
}
ocx.register_obligations(
self.tcx
.predicates_of(single.impl_def_id)
.instantiate(self.tcx, impl_args)
.into_iter()
.map(|(clause, _)| {
Obligation::new(
self.tcx,
ObligationCause::dummy(),
param_env,
clause,
)
}),
);
if !ocx.select_where_possible().is_empty() {
return false;
}
}
// Literally nothing unified, just give up.
if terrs.len() == impl_trait_ref.args.len() {
return false;
}
let mut terrs = vec![];
for (obligation_arg, impl_arg) in
std::iter::zip(obligation_trait_ref.args, impl_trait_ref.args)
{
if let Err(terr) =
ocx.eq(&ObligationCause::dummy(), param_env, impl_arg, obligation_arg)
{
terrs.push(terr);
}
if !ocx.select_where_possible().is_empty() {
return false;
}
}
let cand =
self.resolve_vars_if_possible(impl_trait_ref).fold_with(&mut BottomUpFolder {
tcx: self.tcx,
ty_op: |ty| ty,
lt_op: |lt| lt,
ct_op: |ct| ct.normalize(self.tcx, ty::ParamEnv::empty()),
});
err.highlighted_help(vec![
StringPart::normal(format!("the trait `{}` ", cand.print_trait_sugared())),
StringPart::highlighted("is"),
StringPart::normal(" implemented for `"),
StringPart::highlighted(cand.self_ty().to_string()),
StringPart::normal("`"),
]);
// Literally nothing unified, just give up.
if terrs.len() == impl_trait_ref.args.len() {
return false;
}
if let [TypeError::Sorts(exp_found)] = &terrs[..] {
let exp_found = self.resolve_vars_if_possible(*exp_found);
err.help(format!(
"for that trait implementation, expected `{}`, found `{}`",
exp_found.expected, exp_found.found
));
}
let cand = self.resolve_vars_if_possible(impl_trait_ref).fold_with(
&mut BottomUpFolder {
tcx: self.tcx,
ty_op: |ty| ty,
lt_op: |lt| lt,
ct_op: |ct| ct.normalize(self.tcx, ty::ParamEnv::empty()),
},
);
err.highlighted_help(vec![
StringPart::normal(format!("the trait `{}` ", cand.print_trait_sugared())),
StringPart::highlighted("is"),
StringPart::normal(" implemented for `"),
StringPart::highlighted(cand.self_ty().to_string()),
StringPart::normal("`"),
]);
true
if let [TypeError::Sorts(exp_found)] = &terrs[..] {
let exp_found = self.resolve_vars_if_possible(*exp_found);
err.help(format!(
"for that trait implementation, expected `{}`, found `{}`",
exp_found.expected, exp_found.found
));
}
true
})
}) {
return true;
}

View file

@ -358,8 +358,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
| ty::PredicateKind::Coerce(_)
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..) => {
let pred =
ty::Binder::dummy(infcx.instantiate_binder_with_placeholders(binder));
let pred = ty::Binder::dummy(infcx.enter_forall_and_leak_universe(binder));
ProcessResult::Changed(mk_pending(vec![obligation.with(infcx.tcx, pred)]))
}
ty::PredicateKind::Ambiguous => ProcessResult::Unchanged,

View file

@ -250,8 +250,7 @@ pub(super) fn poly_project_and_unify_type<'cx, 'tcx>(
let infcx = selcx.infcx;
let r = infcx.commit_if_ok(|_snapshot| {
let old_universe = infcx.universe();
let placeholder_predicate =
infcx.instantiate_binder_with_placeholders(obligation.predicate);
let placeholder_predicate = infcx.enter_forall_and_leak_universe(obligation.predicate);
let new_universe = infcx.universe();
let placeholder_obligation = obligation.with(infcx.tcx, placeholder_predicate);

View file

@ -728,64 +728,63 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.infcx.probe(|_snapshot| {
let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate);
let placeholder_trait_predicate =
self.infcx.instantiate_binder_with_placeholders(poly_trait_predicate);
let self_ty = placeholder_trait_predicate.self_ty();
let principal_trait_ref = match self_ty.kind() {
ty::Dynamic(data, ..) => {
if data.auto_traits().any(|did| did == obligation.predicate.def_id()) {
debug!(
"assemble_candidates_from_object_ty: matched builtin bound, \
self.infcx.enter_forall(poly_trait_predicate, |placeholder_trait_predicate| {
let self_ty = placeholder_trait_predicate.self_ty();
let principal_trait_ref = match self_ty.kind() {
ty::Dynamic(data, ..) => {
if data.auto_traits().any(|did| did == obligation.predicate.def_id()) {
debug!(
"assemble_candidates_from_object_ty: matched builtin bound, \
pushing candidate"
);
candidates.vec.push(BuiltinObjectCandidate);
return;
}
if let Some(principal) = data.principal() {
if !self.infcx.tcx.features().object_safe_for_dispatch {
principal.with_self_ty(self.tcx(), self_ty)
} else if self.tcx().check_is_object_safe(principal.def_id()) {
principal.with_self_ty(self.tcx(), self_ty)
} else {
);
candidates.vec.push(BuiltinObjectCandidate);
return;
}
} else {
// Only auto trait bounds exist.
if let Some(principal) = data.principal() {
if !self.infcx.tcx.features().object_safe_for_dispatch {
principal.with_self_ty(self.tcx(), self_ty)
} else if self.tcx().check_is_object_safe(principal.def_id()) {
principal.with_self_ty(self.tcx(), self_ty)
} else {
return;
}
} else {
// Only auto trait bounds exist.
return;
}
}
ty::Infer(ty::TyVar(_)) => {
debug!("assemble_candidates_from_object_ty: ambiguous");
candidates.ambiguous = true; // could wind up being an object type
return;
}
}
ty::Infer(ty::TyVar(_)) => {
debug!("assemble_candidates_from_object_ty: ambiguous");
candidates.ambiguous = true; // could wind up being an object type
return;
}
_ => return,
};
_ => return,
};
debug!(?principal_trait_ref, "assemble_candidates_from_object_ty");
debug!(?principal_trait_ref, "assemble_candidates_from_object_ty");
// Count only those upcast versions that match the trait-ref
// we are looking for. Specifically, do not only check for the
// correct trait, but also the correct type parameters.
// For example, we may be trying to upcast `Foo` to `Bar<i32>`,
// but `Foo` is declared as `trait Foo: Bar<u32>`.
let candidate_supertraits = util::supertraits(self.tcx(), principal_trait_ref)
.enumerate()
.filter(|&(_, upcast_trait_ref)| {
self.infcx.probe(|_| {
self.match_normalize_trait_ref(
obligation,
upcast_trait_ref,
placeholder_trait_predicate.trait_ref,
)
.is_ok()
// Count only those upcast versions that match the trait-ref
// we are looking for. Specifically, do not only check for the
// correct trait, but also the correct type parameters.
// For example, we may be trying to upcast `Foo` to `Bar<i32>`,
// but `Foo` is declared as `trait Foo: Bar<u32>`.
let candidate_supertraits = util::supertraits(self.tcx(), principal_trait_ref)
.enumerate()
.filter(|&(_, upcast_trait_ref)| {
self.infcx.probe(|_| {
self.match_normalize_trait_ref(
obligation,
upcast_trait_ref,
placeholder_trait_predicate.trait_ref,
)
.is_ok()
})
})
})
.map(|(idx, _)| ObjectCandidate(idx));
.map(|(idx, _)| ObjectCandidate(idx));
candidates.vec.extend(candidate_supertraits);
candidates.vec.extend(candidate_supertraits);
})
})
}

View file

@ -159,7 +159,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let trait_predicate = self.infcx.shallow_resolve(obligation.predicate);
let placeholder_trait_predicate =
self.infcx.instantiate_binder_with_placeholders(trait_predicate).trait_ref;
self.infcx.enter_forall_and_leak_universe(trait_predicate).trait_ref;
let placeholder_self_ty = placeholder_trait_predicate.self_ty();
let placeholder_trait_predicate = ty::Binder::dummy(placeholder_trait_predicate);
let (def_id, args) = match *placeholder_self_ty.kind() {
@ -402,7 +402,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let cause = obligation.derived_cause(BuiltinDerivedObligation);
let poly_trait_ref = obligation.predicate.to_poly_trait_ref();
let trait_ref = self.infcx.instantiate_binder_with_placeholders(poly_trait_ref);
let trait_ref = self.infcx.enter_forall_and_leak_universe(poly_trait_ref);
let trait_obligations: Vec<PredicateObligation<'_>> = self.impl_or_trait_obligations(
&cause,
obligation.recursion_depth + 1,
@ -493,7 +493,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let tcx = self.tcx();
debug!(?obligation, ?index, "confirm_object_candidate");
let trait_predicate = self.infcx.instantiate_binder_with_placeholders(obligation.predicate);
let trait_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
let self_ty = self.infcx.shallow_resolve(trait_predicate.self_ty());
let obligation_trait_ref = ty::Binder::dummy(trait_predicate.trait_ref);
let ty::Dynamic(data, ..) = *self_ty.kind() else {
@ -691,7 +691,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let cause = obligation.derived_cause(BuiltinDerivedObligation);
// Confirm the `type Output: Sized;` bound that is present on `FnOnce`
let output_ty = self.infcx.instantiate_binder_with_placeholders(sig.output());
let output_ty = self.infcx.enter_forall_and_leak_universe(sig.output());
let output_ty = normalize_with_depth_to(
self,
obligation.param_env,
@ -712,7 +712,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
) -> Vec<PredicateObligation<'tcx>> {
debug!(?obligation, "confirm_trait_alias_candidate");
let predicate = self.infcx.instantiate_binder_with_placeholders(obligation.predicate);
let predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
let trait_ref = predicate.trait_ref;
let trait_def_id = trait_ref.def_id;
let args = trait_ref.args;

View file

@ -1606,7 +1606,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
) -> smallvec::SmallVec<[usize; 2]> {
let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate);
let placeholder_trait_predicate =
self.infcx.instantiate_binder_with_placeholders(poly_trait_predicate);
self.infcx.enter_forall_and_leak_universe(poly_trait_predicate);
debug!(?placeholder_trait_predicate);
let tcx = self.infcx.tcx;
@ -2386,7 +2386,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
.flat_map(|ty| {
let ty: ty::Binder<'tcx, Ty<'tcx>> = types.rebind(*ty); // <----/
let placeholder_ty = self.infcx.instantiate_binder_with_placeholders(ty);
let placeholder_ty = self.infcx.enter_forall_and_leak_universe(ty);
let Normalized { value: normalized_ty, mut obligations } =
ensure_sufficient_stack(|| {
project::normalize_with_depth(
@ -2472,7 +2472,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
obligation: &PolyTraitObligation<'tcx>,
) -> Result<Normalized<'tcx, GenericArgsRef<'tcx>>, ()> {
let placeholder_obligation =
self.infcx.instantiate_binder_with_placeholders(obligation.predicate);
self.infcx.enter_forall_and_leak_universe(obligation.predicate);
let placeholder_obligation_trait_ref = placeholder_obligation.trait_ref;
let impl_args = self.infcx.fresh_args_for_item(obligation.cause.span, impl_def_id);