From b002b5cc82b8138308c1aad791ae1f80ca6f5c44 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 14 Feb 2025 01:28:15 +0000 Subject: [PATCH] Deeply normalize associated type bounds before proving them --- .../src/check/compare_impl_item.rs | 64 +++++++------------ ...1883.stderr => issue-91883.current.stderr} | 6 +- .../issue-91883.next.stderr | 20 ++++++ .../generic-associated-types/issue-91883.rs | 4 ++ .../in-trait/default-body-with-rpit.rs | 3 + .../in-trait/nested-rpitit-bounds.rs | 3 + .../predicate-entailment-passes.rs | 11 ---- ...trait_ref_is_knowable-norm-overflow.stderr | 12 +--- tests/ui/traits/next-solver/gat-wf.rs | 16 +++++ tests/ui/traits/next-solver/gat-wf.stderr | 15 +++++ 10 files changed, 87 insertions(+), 67 deletions(-) rename tests/ui/generic-associated-types/{issue-91883.stderr => issue-91883.current.stderr} (90%) create mode 100644 tests/ui/generic-associated-types/issue-91883.next.stderr create mode 100644 tests/ui/traits/next-solver/gat-wf.rs create mode 100644 tests/ui/traits/next-solver/gat-wf.stderr diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 84d5ec4a1e5b..79cf86191e0d 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -2105,18 +2105,21 @@ pub(super) fn check_type_bounds<'tcx>( ObligationCause::new(impl_ty_span, impl_ty_def_id, code) }; - let mut obligations: Vec<_> = tcx - .explicit_item_bounds(trait_ty.def_id) - .iter_instantiated_copied(tcx, rebased_args) - .map(|(concrete_ty_bound, span)| { - debug!(?concrete_ty_bound); - traits::Obligation::new(tcx, mk_cause(span), param_env, concrete_ty_bound) - }) - .collect(); + let mut obligations: Vec<_> = util::elaborate( + tcx, + tcx.explicit_item_bounds(trait_ty.def_id).iter_instantiated_copied(tcx, rebased_args).map( + |(concrete_ty_bound, span)| { + debug!(?concrete_ty_bound); + traits::Obligation::new(tcx, mk_cause(span), param_env, concrete_ty_bound) + }, + ), + ) + .collect(); // 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) { - obligations.extend( + obligations.extend(util::elaborate( + tcx, tcx.explicit_implied_const_bounds(trait_ty.def_id) .iter_instantiated_copied(tcx, rebased_args) .map(|(c, span)| { @@ -2127,7 +2130,7 @@ pub(super) fn check_type_bounds<'tcx>( c.to_host_effect_clause(tcx, ty::BoundConstness::Maybe), ) }), - ); + )); } 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 // predicate too, but we don't do that because of performance issues. // See . - 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); - for mut obligation in util::elaborate(tcx, obligations) { - let normalized_predicate = if infcx.next_trait_solver() { - obligation.predicate.fold_with(&mut ReplaceTy { - tcx, - from: trait_projection_ty, - 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); + for obligation in &mut obligations { + match ocx.deeply_normalize(&normalize_cause, normalize_param_env, obligation.predicate) { + Ok(pred) => obligation.predicate = pred, + Err(e) => { + return Err(infcx.err_ctxt().report_fulfillment_errors(e)); + } + } } + // Check that all obligations are satisfied by the implementation's // version. + ocx.register_obligations(obligations); let errors = ocx.select_all_or_error(); if !errors.is_empty() { 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) } -struct ReplaceTy<'tcx> { - tcx: TyCtxt<'tcx>, - from: Ty<'tcx>, - to: Ty<'tcx>, -} - -impl<'tcx> TypeFolder> 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 /// definition types. This is not allowed in general in cases of default /// associated types in trait definitions, or when specialization is involved, diff --git a/tests/ui/generic-associated-types/issue-91883.stderr b/tests/ui/generic-associated-types/issue-91883.current.stderr similarity index 90% rename from tests/ui/generic-associated-types/issue-91883.stderr rename to tests/ui/generic-associated-types/issue-91883.current.stderr index ac636ebb648c..0741cf9581d5 100644 --- a/tests/ui/generic-associated-types/issue-91883.stderr +++ b/tests/ui/generic-associated-types/issue-91883.current.stderr @@ -1,5 +1,5 @@ error[E0478]: lifetime bound not satisfied - --> $DIR/issue-91883.rs:30:24 + --> $DIR/issue-91883.rs:34:24 | LL | type Cursor<'tx>: Cursor<'tx> | ----------------------------- 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 - --> $DIR/issue-91883.rs:29:6 + --> $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:30:17 + --> $DIR/issue-91883.rs:34:17 | LL | type Cursor<'tx> = CursorImpl<'tx>; | ^^^ diff --git a/tests/ui/generic-associated-types/issue-91883.next.stderr b/tests/ui/generic-associated-types/issue-91883.next.stderr new file mode 100644 index 000000000000..b3ed2d81b63e --- /dev/null +++ b/tests/ui/generic-associated-types/issue-91883.next.stderr @@ -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`. diff --git a/tests/ui/generic-associated-types/issue-91883.rs b/tests/ui/generic-associated-types/issue-91883.rs index e870e08a3a2b..d31e1736cf28 100644 --- a/tests/ui/generic-associated-types/issue-91883.rs +++ b/tests/ui/generic-associated-types/issue-91883.rs @@ -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::marker::PhantomData; diff --git a/tests/ui/impl-trait/in-trait/default-body-with-rpit.rs b/tests/ui/impl-trait/in-trait/default-body-with-rpit.rs index c1a78bc23885..1bbff839ffa5 100644 --- a/tests/ui/impl-trait/in-trait/default-body-with-rpit.rs +++ b/tests/ui/impl-trait/in-trait/default-body-with-rpit.rs @@ -1,5 +1,8 @@ //@ edition:2021 //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver use std::fmt::Debug; diff --git a/tests/ui/impl-trait/in-trait/nested-rpitit-bounds.rs b/tests/ui/impl-trait/in-trait/nested-rpitit-bounds.rs index 10c2a8112434..6954a7d80672 100644 --- a/tests/ui/impl-trait/in-trait/nested-rpitit-bounds.rs +++ b/tests/ui/impl-trait/in-trait/nested-rpitit-bounds.rs @@ -1,4 +1,7 @@ //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver use std::ops::Deref; diff --git a/tests/ui/traits/const-traits/predicate-entailment-passes.rs b/tests/ui/traits/const-traits/predicate-entailment-passes.rs index 9c8d5a5e3f6a..28ae21891f38 100644 --- a/tests/ui/traits/const-traits/predicate-entailment-passes.rs +++ b/tests/ui/traits/const-traits/predicate-entailment-passes.rs @@ -6,32 +6,21 @@ #[const_trait] trait Bar {} impl const Bar for () {} - #[const_trait] trait TildeConst { - type Bar where T: ~const Bar; - fn foo() where T: ~const Bar; } impl TildeConst for () { - type Bar = () where T: Bar; - fn foo() where T: Bar {} } #[const_trait] trait AlwaysConst { - type Bar where T: const Bar; - fn foo() where T: const Bar; } impl AlwaysConst for i32 { - type Bar = () where T: Bar; - fn foo() where T: Bar {} } impl const AlwaysConst for u32 { - type Bar = () where T: ~const Bar; - fn foo() where T: ~const Bar {} } diff --git a/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr b/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr index 1d42dbdfe00e..294fa0d7613c 100644 --- a/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr +++ b/tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr @@ -1,18 +1,8 @@ -error[E0275]: overflow evaluating the requirement `::Assoc: Sized` +error[E0275]: overflow evaluating the requirement `::Assoc == _` --> $DIR/trait_ref_is_knowable-norm-overflow.rs:10:18 | LL | type Assoc = ::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` --> $DIR/trait_ref_is_knowable-norm-overflow.rs:18:1 diff --git a/tests/ui/traits/next-solver/gat-wf.rs b/tests/ui/traits/next-solver/gat-wf.rs new file mode 100644 index 000000000000..ff6e2665ef3e --- /dev/null +++ b/tests/ui/traits/next-solver/gat-wf.rs @@ -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() {} diff --git a/tests/ui/traits/next-solver/gat-wf.stderr b/tests/ui/traits/next-solver/gat-wf.stderr new file mode 100644 index 000000000000..620bca77e4b9 --- /dev/null +++ b/tests/ui/traits/next-solver/gat-wf.stderr @@ -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`.