1
Fork 0

Deeply normalize associated type bounds before proving them

This commit is contained in:
Michael Goulet 2025-02-14 01:28:15 +00:00
parent 2cdb7fac95
commit b002b5cc82
10 changed files with 87 additions and 67 deletions

View file

@ -2105,18 +2105,21 @@ pub(super) fn check_type_bounds<'tcx>(
ObligationCause::new(impl_ty_span, impl_ty_def_id, code) ObligationCause::new(impl_ty_span, impl_ty_def_id, code)
}; };
let mut obligations: Vec<_> = tcx let mut obligations: Vec<_> = util::elaborate(
.explicit_item_bounds(trait_ty.def_id) tcx,
.iter_instantiated_copied(tcx, rebased_args) tcx.explicit_item_bounds(trait_ty.def_id).iter_instantiated_copied(tcx, rebased_args).map(
.map(|(concrete_ty_bound, span)| { |(concrete_ty_bound, span)| {
debug!(?concrete_ty_bound); debug!(?concrete_ty_bound);
traits::Obligation::new(tcx, mk_cause(span), param_env, concrete_ty_bound) traits::Obligation::new(tcx, mk_cause(span), param_env, concrete_ty_bound)
}) },
.collect(); ),
)
.collect();
// Only in a const implementation do we need to check that the `~const` item bounds hold. // Only in a const implementation do we need to check that the `~const` item bounds hold.
if tcx.is_conditionally_const(impl_ty_def_id) { if tcx.is_conditionally_const(impl_ty_def_id) {
obligations.extend( obligations.extend(util::elaborate(
tcx,
tcx.explicit_implied_const_bounds(trait_ty.def_id) tcx.explicit_implied_const_bounds(trait_ty.def_id)
.iter_instantiated_copied(tcx, rebased_args) .iter_instantiated_copied(tcx, rebased_args)
.map(|(c, span)| { .map(|(c, span)| {
@ -2127,7 +2130,7 @@ pub(super) fn check_type_bounds<'tcx>(
c.to_host_effect_clause(tcx, ty::BoundConstness::Maybe), c.to_host_effect_clause(tcx, ty::BoundConstness::Maybe),
) )
}), }),
); ));
} }
debug!(item_bounds=?obligations); debug!(item_bounds=?obligations);
@ -2135,26 +2138,19 @@ pub(super) fn check_type_bounds<'tcx>(
// to its definition type. This should be the param-env we use to *prove* the // to its definition type. This should be the param-env we use to *prove* the
// predicate too, but we don't do that because of performance issues. // predicate too, but we don't do that because of performance issues.
// See <https://github.com/rust-lang/rust/pull/117542#issue-1976337685>. // See <https://github.com/rust-lang/rust/pull/117542#issue-1976337685>.
let trait_projection_ty = Ty::new_projection_from_args(tcx, trait_ty.def_id, rebased_args);
let impl_identity_ty = tcx.type_of(impl_ty.def_id).instantiate_identity();
let normalize_param_env = param_env_with_gat_bounds(tcx, impl_ty, impl_trait_ref); let normalize_param_env = param_env_with_gat_bounds(tcx, impl_ty, impl_trait_ref);
for mut obligation in util::elaborate(tcx, obligations) { for obligation in &mut obligations {
let normalized_predicate = if infcx.next_trait_solver() { match ocx.deeply_normalize(&normalize_cause, normalize_param_env, obligation.predicate) {
obligation.predicate.fold_with(&mut ReplaceTy { Ok(pred) => obligation.predicate = pred,
tcx, Err(e) => {
from: trait_projection_ty, return Err(infcx.err_ctxt().report_fulfillment_errors(e));
to: impl_identity_ty, }
}) }
} else {
ocx.normalize(&normalize_cause, normalize_param_env, obligation.predicate)
};
debug!(?normalized_predicate);
obligation.predicate = normalized_predicate;
ocx.register_obligation(obligation);
} }
// Check that all obligations are satisfied by the implementation's // Check that all obligations are satisfied by the implementation's
// version. // version.
ocx.register_obligations(obligations);
let errors = ocx.select_all_or_error(); let errors = ocx.select_all_or_error();
if !errors.is_empty() { if !errors.is_empty() {
let reported = infcx.err_ctxt().report_fulfillment_errors(errors); let reported = infcx.err_ctxt().report_fulfillment_errors(errors);
@ -2166,22 +2162,6 @@ pub(super) fn check_type_bounds<'tcx>(
ocx.resolve_regions_and_report_errors(impl_ty_def_id, param_env, assumed_wf_types) ocx.resolve_regions_and_report_errors(impl_ty_def_id, param_env, assumed_wf_types)
} }
struct ReplaceTy<'tcx> {
tcx: TyCtxt<'tcx>,
from: Ty<'tcx>,
to: Ty<'tcx>,
}
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceTy<'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
if self.from == ty { self.to } else { ty.super_fold_with(self) }
}
}
/// Install projection predicates that allow GATs to project to their own /// Install projection predicates that allow GATs to project to their own
/// definition types. This is not allowed in general in cases of default /// definition types. This is not allowed in general in cases of default
/// associated types in trait definitions, or when specialization is involved, /// associated types in trait definitions, or when specialization is involved,

View file

@ -1,5 +1,5 @@
error[E0478]: lifetime bound not satisfied error[E0478]: lifetime bound not satisfied
--> $DIR/issue-91883.rs:30:24 --> $DIR/issue-91883.rs:34:24
| |
LL | type Cursor<'tx>: Cursor<'tx> LL | type Cursor<'tx>: Cursor<'tx>
| ----------------------------- definition of `Cursor` from trait | ----------------------------- definition of `Cursor` from trait
@ -8,12 +8,12 @@ LL | type Cursor<'tx> = CursorImpl<'tx>;
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
| |
note: lifetime parameter instantiated with the lifetime `'db` as defined here note: lifetime parameter instantiated with the lifetime `'db` as defined here
--> $DIR/issue-91883.rs:29:6 --> $DIR/issue-91883.rs:33:6
| |
LL | impl<'db> Transaction<'db> for TransactionImpl<'db> { LL | impl<'db> Transaction<'db> for TransactionImpl<'db> {
| ^^^ | ^^^
note: but lifetime parameter must outlive the lifetime `'tx` as defined here note: but lifetime parameter must outlive the lifetime `'tx` as defined here
--> $DIR/issue-91883.rs:30:17 --> $DIR/issue-91883.rs:34:17
| |
LL | type Cursor<'tx> = CursorImpl<'tx>; LL | type Cursor<'tx> = CursorImpl<'tx>;
| ^^^ | ^^^

View file

@ -0,0 +1,20 @@
error[E0478]: lifetime bound not satisfied
--> $DIR/issue-91883.rs:34:24
|
LL | type Cursor<'tx> = CursorImpl<'tx>;
| ^^^^^^^^^^^^^^^
|
note: lifetime parameter instantiated with the lifetime `'db` as defined here
--> $DIR/issue-91883.rs:33:6
|
LL | impl<'db> Transaction<'db> for TransactionImpl<'db> {
| ^^^
note: but lifetime parameter must outlive the lifetime `'tx` as defined here
--> $DIR/issue-91883.rs:34:17
|
LL | type Cursor<'tx> = CursorImpl<'tx>;
| ^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0478`.

View file

@ -1,3 +1,7 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
use std::fmt::Debug; use std::fmt::Debug;
use std::marker::PhantomData; use std::marker::PhantomData;

View file

@ -1,5 +1,8 @@
//@ edition:2021 //@ edition:2021
//@ check-pass //@ check-pass
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
use std::fmt::Debug; use std::fmt::Debug;

View file

@ -1,4 +1,7 @@
//@ check-pass //@ check-pass
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
use std::ops::Deref; use std::ops::Deref;

View file

@ -6,32 +6,21 @@
#[const_trait] trait Bar {} #[const_trait] trait Bar {}
impl const Bar for () {} impl const Bar for () {}
#[const_trait] trait TildeConst { #[const_trait] trait TildeConst {
type Bar<T> where T: ~const Bar;
fn foo<T>() where T: ~const Bar; fn foo<T>() where T: ~const Bar;
} }
impl TildeConst for () { impl TildeConst for () {
type Bar<T> = () where T: Bar;
fn foo<T>() where T: Bar {} fn foo<T>() where T: Bar {}
} }
#[const_trait] trait AlwaysConst { #[const_trait] trait AlwaysConst {
type Bar<T> where T: const Bar;
fn foo<T>() where T: const Bar; fn foo<T>() where T: const Bar;
} }
impl AlwaysConst for i32 { impl AlwaysConst for i32 {
type Bar<T> = () where T: Bar;
fn foo<T>() where T: Bar {} fn foo<T>() where T: Bar {}
} }
impl const AlwaysConst for u32 { impl const AlwaysConst for u32 {
type Bar<T> = () where T: ~const Bar;
fn foo<T>() where T: ~const Bar {} fn foo<T>() where T: ~const Bar {}
} }

View file

@ -1,18 +1,8 @@
error[E0275]: overflow evaluating the requirement `<T as Overflow>::Assoc: Sized` error[E0275]: overflow evaluating the requirement `<T as Overflow>::Assoc == _`
--> $DIR/trait_ref_is_knowable-norm-overflow.rs:10:18 --> $DIR/trait_ref_is_knowable-norm-overflow.rs:10:18
| |
LL | type Assoc = <T as Overflow>::Assoc; LL | type Assoc = <T as Overflow>::Assoc;
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
|
note: required by a bound in `Overflow::Assoc`
--> $DIR/trait_ref_is_knowable-norm-overflow.rs:7:5
|
LL | type Assoc;
| ^^^^^^^^^^^ required by this bound in `Overflow::Assoc`
help: consider relaxing the implicit `Sized` restriction
|
LL | type Assoc: ?Sized;
| ++++++++
error[E0119]: conflicting implementations of trait `Trait` error[E0119]: conflicting implementations of trait `Trait`
--> $DIR/trait_ref_is_knowable-norm-overflow.rs:18:1 --> $DIR/trait_ref_is_knowable-norm-overflow.rs:18:1

View file

@ -0,0 +1,16 @@
//@ compile-flags: -Znext-solver
// Make sure that, like the old trait solver, we end up requiring that the WC of
// impl GAT matches that of the trait. This is not a restriction that we *need*,
// but is a side-effect of registering the where clauses when normalizing the GAT
// when proving it satisfies its item bounds.
trait Foo {
type T<'a>: Sized where Self: 'a;
}
impl Foo for &() {
type T<'a> = (); //~ the type `&()` does not fulfill the required lifetime
}
fn main() {}

View file

@ -0,0 +1,15 @@
error[E0477]: the type `&()` does not fulfill the required lifetime
--> $DIR/gat-wf.rs:13:18
|
LL | type T<'a> = ();
| ^^
|
note: type must outlive the lifetime `'a` as defined here
--> $DIR/gat-wf.rs:13:12
|
LL | type T<'a> = ();
| ^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0477`.