1
Fork 0

Rollup merge of #133517 - compiler-errors:deep-norm, r=lcnr

Deeply normalize when computing implied outlives bounds

r? lcnr

Unfortunately resolving regions is still slightly scuffed (though in an unrelated way). Specifically, we should be normalizing our param-env outlives when constructing the `OutlivesEnv`; otherwise, these assumptions (dd2837ec5d/compiler/rustc_infer/src/infer/outlives/env.rs (L78)) are not constructed correctly.

Let me know if you want us to track that somewhere.
This commit is contained in:
Matthias Krüger 2024-12-03 07:48:33 +01:00 committed by GitHub
commit 68279097d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 88 additions and 29 deletions

View file

@ -415,6 +415,10 @@ impl<O: ForestObligation> ObligationForest<O> {
.collect() .collect()
} }
pub fn has_pending_obligations(&self) -> bool {
self.nodes.iter().any(|node| node.state.get() == NodeState::Pending)
}
fn insert_into_error_cache(&mut self, index: usize) { fn insert_into_error_cache(&mut self, index: usize) {
let node = &self.nodes[index]; let node = &self.nodes[index];
self.error_cache self.error_cache

View file

@ -117,13 +117,12 @@ where
} }
f(&mut wfcx)?; f(&mut wfcx)?;
let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?;
let errors = wfcx.select_all_or_error(); let errors = wfcx.select_all_or_error();
if !errors.is_empty() { if !errors.is_empty() {
return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
} }
let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?;
debug!(?assumed_wf_types); debug!(?assumed_wf_types);
let infcx_compat = infcx.fork(); let infcx_compat = infcx.fork();

View file

@ -84,6 +84,8 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx {
self.collect_remaining_errors(infcx) self.collect_remaining_errors(infcx)
} }
fn has_pending_obligations(&self) -> bool;
fn pending_obligations(&self) -> PredicateObligations<'tcx>; fn pending_obligations(&self) -> PredicateObligations<'tcx>;
/// Among all pending obligations, collect those are stalled on a inference variable which has /// Among all pending obligations, collect those are stalled on a inference variable which has

View file

@ -199,6 +199,10 @@ where
errors errors
} }
fn has_pending_obligations(&self) -> bool {
!self.obligations.pending.is_empty() || !self.obligations.overflowed.is_empty()
}
fn pending_obligations(&self) -> PredicateObligations<'tcx> { fn pending_obligations(&self) -> PredicateObligations<'tcx> {
self.obligations.clone_pending() self.obligations.clone_pending()
} }

View file

@ -213,6 +213,10 @@ where
} }
} }
fn has_pending_obligations(&self) -> bool {
self.predicates.has_pending_obligations()
}
fn pending_obligations(&self) -> PredicateObligations<'tcx> { fn pending_obligations(&self) -> PredicateObligations<'tcx> {
self.predicates.map_pending_obligations(|o| o.obligation.clone()) self.predicates.map_pending_obligations(|o| o.obligation.clone())
} }

View file

@ -7,6 +7,7 @@ use rustc_infer::traits::{
FromSolverError, Normalized, Obligation, PredicateObligations, TraitEngine, FromSolverError, Normalized, Obligation, PredicateObligations, TraitEngine,
}; };
use rustc_macros::extension; use rustc_macros::extension;
use rustc_middle::span_bug;
use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
use rustc_middle::ty::{ use rustc_middle::ty::{
self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, TypeVisitableExt, self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, TypeVisitableExt,
@ -63,10 +64,18 @@ impl<'tcx> At<'_, 'tcx> {
if self.infcx.next_trait_solver() { if self.infcx.next_trait_solver() {
crate::solve::deeply_normalize(self, value) crate::solve::deeply_normalize(self, value)
} else { } else {
if fulfill_cx.has_pending_obligations() {
let pending_obligations = fulfill_cx.pending_obligations();
span_bug!(
pending_obligations[0].cause.span,
"deeply_normalize should not be called with pending obligations: \
{pending_obligations:#?}"
);
}
let value = self let value = self
.normalize(value) .normalize(value)
.into_value_registering_obligations(self.infcx, &mut *fulfill_cx); .into_value_registering_obligations(self.infcx, &mut *fulfill_cx);
let errors = fulfill_cx.select_where_possible(self.infcx); let errors = fulfill_cx.select_all_or_error(self.infcx);
let value = self.infcx.resolve_vars_if_possible(value); let value = self.infcx.resolve_vars_if_possible(value);
if errors.is_empty() { Ok(value) } else { Err(errors) } if errors.is_empty() { Ok(value) } else { Err(errors) }
} }

View file

@ -59,12 +59,16 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>, ty: Ty<'tcx>,
) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> { ) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
let normalize_op = |ty| { let normalize_op = |ty| -> Result<_, NoSolution> {
let ty = ocx.normalize(&ObligationCause::dummy(), param_env, ty); // We must normalize the type so we can compute the right outlives components.
// for example, if we have some constrained param type like `T: Trait<Out = U>`,
// and we know that `&'a T::Out` is WF, then we want to imply `U: 'a`.
let ty = ocx
.deeply_normalize(&ObligationCause::dummy(), param_env, ty)
.map_err(|_| NoSolution)?;
if !ocx.select_all_or_error().is_empty() { if !ocx.select_all_or_error().is_empty() {
return Err(NoSolution); return Err(NoSolution);
} }
let ty = ocx.infcx.resolve_vars_if_possible(ty);
let ty = OpportunisticRegionResolver::new(&ocx.infcx).fold_ty(ty); let ty = OpportunisticRegionResolver::new(&ocx.infcx).fold_ty(ty);
Ok(ty) Ok(ty)
}; };

View file

@ -12,6 +12,5 @@ impl<T> Overlap<T> for T {}
impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {} impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {}
//~^ ERROR the trait bound `for<'a> T: ToUnit<'a>` is not satisfied //~^ ERROR the trait bound `for<'a> T: ToUnit<'a>` is not satisfied
//~| ERROR the trait bound `for<'a> T: ToUnit<'a>` is not satisfied
fn main() {} fn main() {}

View file

@ -10,17 +10,6 @@ help: consider restricting type parameter `T`
LL | impl<T: for<'a> ToUnit<'a>> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {} LL | impl<T: for<'a> ToUnit<'a>> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {}
| ++++++++++++++++++++ | ++++++++++++++++++++
error[E0277]: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied error: aborting due to 1 previous error
--> $DIR/structually-relate-aliases.rs:13:17
|
LL | impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> ToUnit<'a>` is not implemented for `T`
|
help: consider restricting type parameter `T`
|
LL | impl<T: for<'a> ToUnit<'a>> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {}
| ++++++++++++++++++++
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`. For more information about this error, try `rustc --explain E0277`.

View file

@ -8,10 +8,10 @@ LL | #![feature(lazy_type_alias)]
= note: `#[warn(incomplete_features)]` on by default = note: `#[warn(incomplete_features)]` on by default
error[E0277]: the trait bound `usize: Foo` is not satisfied error[E0277]: the trait bound `usize: Foo` is not satisfied
--> $DIR/alias-bounds-when-not-wf.rs:16:13 --> $DIR/alias-bounds-when-not-wf.rs:16:15
| |
LL | fn hello(_: W<A<usize>>) {} LL | fn hello(_: W<A<usize>>) {}
| ^^^^^^^^^^^ the trait `Foo` is not implemented for `usize` | ^^^^^^^^ the trait `Foo` is not implemented for `usize`
| |
help: this trait has no implementations, consider adding one help: this trait has no implementations, consider adding one
--> $DIR/alias-bounds-when-not-wf.rs:6:1 --> $DIR/alias-bounds-when-not-wf.rs:6:1

View file

@ -18,6 +18,6 @@ impl<T> Overlap<T> for T {}
impl<T> Overlap<for<'a> fn(Assoc<'a, T>)> for T where Missing: Overlap<T> {} impl<T> Overlap<for<'a> fn(Assoc<'a, T>)> for T where Missing: Overlap<T> {}
//~^ ERROR cannot find type `Missing` in this scope //~^ ERROR cannot find type `Missing` in this scope
//~| ERROR the trait bound `for<'a> *const T: ToUnit<'a>` is not satisfied //~| ERROR the trait bound `T: Overlap<for<'a> fn(Assoc<'a, T>)>` is not satisfied
fn main() {} fn main() {}

View file

@ -26,17 +26,16 @@ LL | trait ToUnit<'a> {
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), .. } WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), .. }
error[E0277]: the trait bound `for<'a> *const T: ToUnit<'a>` is not satisfied error[E0277]: the trait bound `T: Overlap<for<'a> fn(Assoc<'a, T>)>` is not satisfied
--> $DIR/issue-118950-root-region.rs:19:17 --> $DIR/issue-118950-root-region.rs:19:47
| |
LL | impl<T> Overlap<for<'a> fn(Assoc<'a, T>)> for T where Missing: Overlap<T> {} LL | impl<T> Overlap<for<'a> fn(Assoc<'a, T>)> for T where Missing: Overlap<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> ToUnit<'a>` is not implemented for `*const T` | ^ the trait `Overlap<for<'a> fn(Assoc<'a, T>)>` is not implemented for `T`
| |
help: this trait has no implementations, consider adding one help: consider further restricting type parameter `T`
--> $DIR/issue-118950-root-region.rs:8:1
| |
LL | trait ToUnit<'a> { LL | impl<T> Overlap<for<'a> fn(Assoc<'a, T>)> for T where Missing: Overlap<T>, T: Overlap<for<'a> fn(Assoc<'a, T>)> {}
| ^^^^^^^^^^^^^^^^ | ++++++++++++++++++++++++++++++++++++++
error: aborting due to 3 previous errors; 1 warning emitted error: aborting due to 3 previous errors; 1 warning emitted

View file

@ -0,0 +1,46 @@
//@ check-pass
//@ compile-flags: -Znext-solver
// Minimized example from `rustc_type_ir` that demonstrates a missing deep normalization
// in the new solver when computing the implies outlives bounds of an impl.
use std::marker::PhantomData;
use std::ops::Deref;
pub struct SearchGraph<D: Delegate, X = <D as Delegate>::Cx> {
d: PhantomData<D>,
x: PhantomData<X>,
}
pub trait Delegate {
type Cx;
}
struct SearchGraphDelegate<D: SolverDelegate> {
_marker: PhantomData<D>,
}
impl<D> Delegate for SearchGraphDelegate<D>
where
D: SolverDelegate,
{
type Cx = D::Interner;
}
pub trait SolverDelegate {
type Interner;
}
struct EvalCtxt<'a, D, I>
where
D: SolverDelegate<Interner = I>,
{
search_graph: &'a SearchGraph<SearchGraphDelegate<D>>,
}
impl<'a, D, I> EvalCtxt<'a, D, <D as SolverDelegate>::Interner>
where
D: SolverDelegate<Interner = I>
{}
fn main() {}