Auto merge of #135057 - compiler-errors:project-unconstrained, r=oli-obk
Project to `TyKind::Error` when there are unconstrained non-lifetime (ty/const) impl params It splits the `enforce_impl_params_are_constrained` function into lifetime/non-lifetime, and queryfies the latter. We can then use the result of the latter query (`Result<(), ErrorGuaranteed>`) to intercept projection and constrain the projected type to `TyKind::Error`, which ensures that we leak no ty or const vars to places that don't expect them, like `normalize_erasing_regions`. The reason we split `enforce_impl_params_are_constrained` into two parts is because we only error for *lifetimes* if the lifetime ends up showing up in any of the associated types of the impl (e.g. we allow `impl<'a> Foo { type Assoc = (); }`). However, in order to compute the `type_of` query for the anonymous associated type of an RPITIT, we need to do trait solving (in `query collect_return_position_impl_trait_in_trait_tys`). That would induce cycles. Luckily, it turns out for lifetimes we don't even care about if they're unconstrained, since they're erased in all contexts that we are trying to fix ICEs. So it's sufficient to keep this check separated out of the query. I think this is a bit less invasive of an approach compared to #127973. The major difference between this PR and that PR is that we queryify the check instead of merging it into the `explicit_predicates_of` query, and we use the result to taint just projection goals, rather than trait goals too. This doesn't require a lot of new tracking in `ItemCtxt` and `GenericPredicates`, and it also seems to not require any other changes to typeck like that PR did. Fixes #123141 Fixes #125874 Fixes #126942 Fixes #127804 Fixes #130967 r? oli-obk
This commit is contained in:
commit
7349f6b503
32 changed files with 269 additions and 304 deletions
|
@ -190,9 +190,8 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
|
|||
goal_trait_ref: ty::TraitRef<'tcx>,
|
||||
trait_assoc_def_id: DefId,
|
||||
impl_def_id: DefId,
|
||||
) -> Result<Option<DefId>, NoSolution> {
|
||||
let node_item = specialization_graph::assoc_def(self.tcx, impl_def_id, trait_assoc_def_id)
|
||||
.map_err(|ErrorGuaranteed { .. }| NoSolution)?;
|
||||
) -> Result<Option<DefId>, ErrorGuaranteed> {
|
||||
let node_item = specialization_graph::assoc_def(self.tcx, impl_def_id, trait_assoc_def_id)?;
|
||||
|
||||
let eligible = if node_item.is_final() {
|
||||
// Non-specializable items are always projectable.
|
||||
|
|
|
@ -950,39 +950,45 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
|
|||
//
|
||||
// NOTE: This should be kept in sync with the similar code in
|
||||
// `rustc_ty_utils::instance::resolve_associated_item()`.
|
||||
let node_item = specialization_graph::assoc_def(
|
||||
match specialization_graph::assoc_def(
|
||||
selcx.tcx(),
|
||||
impl_data.impl_def_id,
|
||||
obligation.predicate.def_id,
|
||||
)
|
||||
.map_err(|ErrorGuaranteed { .. }| ())?;
|
||||
|
||||
if node_item.is_final() {
|
||||
// Non-specializable items are always projectable.
|
||||
true
|
||||
} else {
|
||||
// Only reveal a specializable default if we're past type-checking
|
||||
// and the obligation is monomorphic, otherwise passes such as
|
||||
// transmute checking and polymorphic MIR optimizations could
|
||||
// get a result which isn't correct for all monomorphizations.
|
||||
match selcx.infcx.typing_mode() {
|
||||
TypingMode::Coherence
|
||||
| TypingMode::Analysis { .. }
|
||||
| TypingMode::PostBorrowckAnalysis { .. } => {
|
||||
debug!(
|
||||
assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id),
|
||||
?obligation.predicate,
|
||||
"assemble_candidates_from_impls: not eligible due to default",
|
||||
);
|
||||
false
|
||||
}
|
||||
TypingMode::PostAnalysis => {
|
||||
// NOTE(eddyb) inference variables can resolve to parameters, so
|
||||
// assume `poly_trait_ref` isn't monomorphic, if it contains any.
|
||||
let poly_trait_ref = selcx.infcx.resolve_vars_if_possible(trait_ref);
|
||||
!poly_trait_ref.still_further_specializable()
|
||||
) {
|
||||
Ok(node_item) => {
|
||||
if node_item.is_final() {
|
||||
// Non-specializable items are always projectable.
|
||||
true
|
||||
} else {
|
||||
// Only reveal a specializable default if we're past type-checking
|
||||
// and the obligation is monomorphic, otherwise passes such as
|
||||
// transmute checking and polymorphic MIR optimizations could
|
||||
// get a result which isn't correct for all monomorphizations.
|
||||
match selcx.infcx.typing_mode() {
|
||||
TypingMode::Coherence
|
||||
| TypingMode::Analysis { .. }
|
||||
| TypingMode::PostBorrowckAnalysis { .. } => {
|
||||
debug!(
|
||||
assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id),
|
||||
?obligation.predicate,
|
||||
"not eligible due to default",
|
||||
);
|
||||
false
|
||||
}
|
||||
TypingMode::PostAnalysis => {
|
||||
// NOTE(eddyb) inference variables can resolve to parameters, so
|
||||
// assume `poly_trait_ref` isn't monomorphic, if it contains any.
|
||||
let poly_trait_ref =
|
||||
selcx.infcx.resolve_vars_if_possible(trait_ref);
|
||||
!poly_trait_ref.still_further_specializable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Always project `ErrorGuaranteed`, since this will just help
|
||||
// us propagate `TyKind::Error` around which suppresses ICEs
|
||||
// and spurious, unrelated inference errors.
|
||||
Err(ErrorGuaranteed { .. }) => true,
|
||||
}
|
||||
}
|
||||
ImplSource::Builtin(BuiltinImplSource::Misc, _) => {
|
||||
|
@ -2014,7 +2020,6 @@ fn confirm_impl_candidate<'cx, 'tcx>(
|
|||
Ok(assoc_ty) => assoc_ty,
|
||||
Err(guar) => return Progress::error(tcx, guar),
|
||||
};
|
||||
|
||||
if !assoc_ty.item.defaultness(tcx).has_value() {
|
||||
// This means that the impl is missing a definition for the
|
||||
// associated type. This error will be reported by the type
|
||||
|
|
|
@ -376,6 +376,12 @@ pub(crate) fn assoc_def(
|
|||
// If there is no such item in that impl, this function will fail with a
|
||||
// cycle error if the specialization graph is currently being built.
|
||||
if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_def_id) {
|
||||
// Ensure that the impl is constrained, otherwise projection may give us
|
||||
// bad unconstrained infer vars.
|
||||
if let Some(impl_def_id) = impl_def_id.as_local() {
|
||||
tcx.ensure().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?;
|
||||
}
|
||||
|
||||
let item = tcx.associated_item(impl_item_id);
|
||||
let impl_node = Node::Impl(impl_def_id);
|
||||
return Ok(LeafDef {
|
||||
|
@ -391,6 +397,14 @@ pub(crate) fn assoc_def(
|
|||
|
||||
let ancestors = trait_def.ancestors(tcx, impl_def_id)?;
|
||||
if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) {
|
||||
// Ensure that the impl is constrained, otherwise projection may give us
|
||||
// bad unconstrained infer vars.
|
||||
if assoc_item.item.container == ty::AssocItemContainer::Impl
|
||||
&& let Some(impl_def_id) = assoc_item.item.container_id(tcx).as_local()
|
||||
{
|
||||
tcx.ensure().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?;
|
||||
}
|
||||
|
||||
Ok(assoc_item)
|
||||
} else {
|
||||
// This is saying that neither the trait nor
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue