Skip to content

Commit a5ef291

Browse files
Rollup merge of rust-lang#137000 - compiler-errors:deeply-normalize-item-bounds, r=lcnr
Deeply normalize item bounds in new solver Built on rust-lang#136863. Fixes rust-lang/trait-system-refactor-initiative#142. Fixes rust-lang/trait-system-refactor-initiative#151. cc rust-lang/trait-system-refactor-initiative#116 First commit reworks candidate preference for projection bounds to prefer param-env projection clauses even if the corresponding trait ref doesn't come from the param-env. Second commit adjusts the associated type item bounds check to deeply normalize in the new solver. This causes some test fallout which I will point out. r? lcnr
2 parents 20ea0c8 + b002b5c commit a5ef291

File tree

11 files changed

+116
-75
lines changed

11 files changed

+116
-75
lines changed

Diff for: compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+22-42
Original file line numberDiff line numberDiff line change
@@ -2104,18 +2104,21 @@ pub(super) fn check_type_bounds<'tcx>(
21042104
ObligationCause::new(impl_ty_span, impl_ty_def_id, code)
21052105
};
21062106

2107-
let mut obligations: Vec<_> = tcx
2108-
.explicit_item_bounds(trait_ty.def_id)
2109-
.iter_instantiated_copied(tcx, rebased_args)
2110-
.map(|(concrete_ty_bound, span)| {
2111-
debug!(?concrete_ty_bound);
2112-
traits::Obligation::new(tcx, mk_cause(span), param_env, concrete_ty_bound)
2113-
})
2114-
.collect();
2107+
let mut obligations: Vec<_> = util::elaborate(
2108+
tcx,
2109+
tcx.explicit_item_bounds(trait_ty.def_id).iter_instantiated_copied(tcx, rebased_args).map(
2110+
|(concrete_ty_bound, span)| {
2111+
debug!(?concrete_ty_bound);
2112+
traits::Obligation::new(tcx, mk_cause(span), param_env, concrete_ty_bound)
2113+
},
2114+
),
2115+
)
2116+
.collect();
21152117

21162118
// Only in a const implementation do we need to check that the `~const` item bounds hold.
21172119
if tcx.is_conditionally_const(impl_ty_def_id) {
2118-
obligations.extend(
2120+
obligations.extend(util::elaborate(
2121+
tcx,
21192122
tcx.explicit_implied_const_bounds(trait_ty.def_id)
21202123
.iter_instantiated_copied(tcx, rebased_args)
21212124
.map(|(c, span)| {
@@ -2126,34 +2129,27 @@ pub(super) fn check_type_bounds<'tcx>(
21262129
c.to_host_effect_clause(tcx, ty::BoundConstness::Maybe),
21272130
)
21282131
}),
2129-
);
2132+
));
21302133
}
21312134
debug!(item_bounds=?obligations);
21322135

21332136
// Normalize predicates with the assumption that the GAT may always normalize
21342137
// to its definition type. This should be the param-env we use to *prove* the
21352138
// predicate too, but we don't do that because of performance issues.
21362139
// See <https://github.com/rust-lang/rust/pull/117542#issue-1976337685>.
2137-
let trait_projection_ty = Ty::new_projection_from_args(tcx, trait_ty.def_id, rebased_args);
2138-
let impl_identity_ty = tcx.type_of(impl_ty.def_id).instantiate_identity();
21392140
let normalize_param_env = param_env_with_gat_bounds(tcx, impl_ty, impl_trait_ref);
2140-
for mut obligation in util::elaborate(tcx, obligations) {
2141-
let normalized_predicate = if infcx.next_trait_solver() {
2142-
obligation.predicate.fold_with(&mut ReplaceTy {
2143-
tcx,
2144-
from: trait_projection_ty,
2145-
to: impl_identity_ty,
2146-
})
2147-
} else {
2148-
ocx.normalize(&normalize_cause, normalize_param_env, obligation.predicate)
2149-
};
2150-
debug!(?normalized_predicate);
2151-
obligation.predicate = normalized_predicate;
2152-
2153-
ocx.register_obligation(obligation);
2141+
for obligation in &mut obligations {
2142+
match ocx.deeply_normalize(&normalize_cause, normalize_param_env, obligation.predicate) {
2143+
Ok(pred) => obligation.predicate = pred,
2144+
Err(e) => {
2145+
return Err(infcx.err_ctxt().report_fulfillment_errors(e));
2146+
}
2147+
}
21542148
}
2149+
21552150
// Check that all obligations are satisfied by the implementation's
21562151
// version.
2152+
ocx.register_obligations(obligations);
21572153
let errors = ocx.select_all_or_error();
21582154
if !errors.is_empty() {
21592155
let reported = infcx.err_ctxt().report_fulfillment_errors(errors);
@@ -2165,22 +2161,6 @@ pub(super) fn check_type_bounds<'tcx>(
21652161
ocx.resolve_regions_and_report_errors(impl_ty_def_id, param_env, assumed_wf_types)
21662162
}
21672163

2168-
struct ReplaceTy<'tcx> {
2169-
tcx: TyCtxt<'tcx>,
2170-
from: Ty<'tcx>,
2171-
to: Ty<'tcx>,
2172-
}
2173-
2174-
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceTy<'tcx> {
2175-
fn cx(&self) -> TyCtxt<'tcx> {
2176-
self.tcx
2177-
}
2178-
2179-
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
2180-
if self.from == ty { self.to } else { ty.super_fold_with(self) }
2181-
}
2182-
}
2183-
21842164
/// Install projection predicates that allow GATs to project to their own
21852165
/// definition types. This is not allowed in general in cases of default
21862166
/// associated types in trait definitions, or when specialization is involved,

Diff for: compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs

+29-8
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ where
791791
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Ambiguity));
792792
};
793793

794-
let responses: Vec<_> = match proven_via {
794+
match proven_via {
795795
// Even when a trait bound has been proven using a where-bound, we
796796
// still need to consider alias-bounds for normalization, see
797797
// tests/ui/next-solver/alias-bound-shadowed-by-env.rs.
@@ -800,7 +800,7 @@ where
800800
// constness checking. Doing so is *at least theoretically* breaking,
801801
// see github.com/rust-lang/rust/issues/133044#issuecomment-2500709754
802802
TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => {
803-
let mut candidates_from_env: Vec<_> = candidates
803+
let mut candidates_from_env_and_bounds: Vec<_> = candidates
804804
.iter()
805805
.filter(|c| {
806806
matches!(
@@ -813,16 +813,37 @@ where
813813

814814
// If the trait goal has been proven by using the environment, we want to treat
815815
// aliases as rigid if there are no applicable projection bounds in the environment.
816-
if candidates_from_env.is_empty() {
816+
if candidates_from_env_and_bounds.is_empty() {
817817
if let Ok(response) = inject_normalize_to_rigid_candidate(self) {
818-
candidates_from_env.push(response);
818+
candidates_from_env_and_bounds.push(response);
819819
}
820820
}
821-
candidates_from_env
821+
822+
if let Some(response) = self.try_merge_responses(&candidates_from_env_and_bounds) {
823+
Ok(response)
824+
} else {
825+
self.flounder(&candidates_from_env_and_bounds)
826+
}
822827
}
823-
TraitGoalProvenVia::Misc => candidates.iter().map(|c| c.result).collect(),
824-
};
828+
TraitGoalProvenVia::Misc => {
829+
// Prefer "orphaned" param-env normalization predicates, which are used
830+
// (for example, and ideally only) when proving item bounds for an impl.
831+
let candidates_from_env: Vec<_> = candidates
832+
.iter()
833+
.filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
834+
.map(|c| c.result)
835+
.collect();
836+
if let Some(response) = self.try_merge_responses(&candidates_from_env) {
837+
return Ok(response);
838+
}
825839

826-
self.try_merge_responses(&responses).map_or_else(|| self.flounder(&responses), Ok)
840+
let responses: Vec<_> = candidates.iter().map(|c| c.result).collect();
841+
if let Some(response) = self.try_merge_responses(&responses) {
842+
Ok(response)
843+
} else {
844+
self.flounder(&responses)
845+
}
846+
}
847+
}
827848
}
828849
}

Diff for: tests/ui/generic-associated-types/issue-91883.stderr renamed to tests/ui/generic-associated-types/issue-91883.current.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0478]: lifetime bound not satisfied
2-
--> $DIR/issue-91883.rs:30:24
2+
--> $DIR/issue-91883.rs:34:24
33
|
44
LL | type Cursor<'tx>: Cursor<'tx>
55
| ----------------------------- definition of `Cursor` from trait
@@ -8,12 +8,12 @@ LL | type Cursor<'tx> = CursorImpl<'tx>;
88
| ^^^^^^^^^^^^^^^
99
|
1010
note: lifetime parameter instantiated with the lifetime `'db` as defined here
11-
--> $DIR/issue-91883.rs:29:6
11+
--> $DIR/issue-91883.rs:33:6
1212
|
1313
LL | impl<'db> Transaction<'db> for TransactionImpl<'db> {
1414
| ^^^
1515
note: but lifetime parameter must outlive the lifetime `'tx` as defined here
16-
--> $DIR/issue-91883.rs:30:17
16+
--> $DIR/issue-91883.rs:34:17
1717
|
1818
LL | type Cursor<'tx> = CursorImpl<'tx>;
1919
| ^^^
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0478]: lifetime bound not satisfied
2+
--> $DIR/issue-91883.rs:34:24
3+
|
4+
LL | type Cursor<'tx> = CursorImpl<'tx>;
5+
| ^^^^^^^^^^^^^^^
6+
|
7+
note: lifetime parameter instantiated with the lifetime `'db` as defined here
8+
--> $DIR/issue-91883.rs:33:6
9+
|
10+
LL | impl<'db> Transaction<'db> for TransactionImpl<'db> {
11+
| ^^^
12+
note: but lifetime parameter must outlive the lifetime `'tx` as defined here
13+
--> $DIR/issue-91883.rs:34:17
14+
|
15+
LL | type Cursor<'tx> = CursorImpl<'tx>;
16+
| ^^^
17+
18+
error: aborting due to 1 previous error
19+
20+
For more information about this error, try `rustc --explain E0478`.

Diff for: tests/ui/generic-associated-types/issue-91883.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
//@ revisions: current next
2+
//@ ignore-compare-mode-next-solver (explicit revisions)
3+
//@[next] compile-flags: -Znext-solver
4+
15
use std::fmt::Debug;
26
use std::marker::PhantomData;
37

Diff for: tests/ui/impl-trait/in-trait/default-body-with-rpit.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
//@ edition:2021
22
//@ check-pass
3+
//@ revisions: current next
4+
//@ ignore-compare-mode-next-solver (explicit revisions)
5+
//@[next] compile-flags: -Znext-solver
36

47
use std::fmt::Debug;
58

Diff for: tests/ui/impl-trait/in-trait/nested-rpitit-bounds.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
//@ check-pass
2+
//@ revisions: current next
3+
//@ ignore-compare-mode-next-solver (explicit revisions)
4+
//@[next] compile-flags: -Znext-solver
25

36
use std::ops::Deref;
47

Diff for: tests/ui/traits/const-traits/predicate-entailment-passes.rs

-11
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,21 @@
66
#[const_trait] trait Bar {}
77
impl const Bar for () {}
88

9-
109
#[const_trait] trait TildeConst {
11-
type Bar<T> where T: ~const Bar;
12-
1310
fn foo<T>() where T: ~const Bar;
1411
}
1512
impl TildeConst for () {
16-
type Bar<T> = () where T: Bar;
17-
1813
fn foo<T>() where T: Bar {}
1914
}
2015

2116

2217
#[const_trait] trait AlwaysConst {
23-
type Bar<T> where T: const Bar;
24-
2518
fn foo<T>() where T: const Bar;
2619
}
2720
impl AlwaysConst for i32 {
28-
type Bar<T> = () where T: Bar;
29-
3021
fn foo<T>() where T: Bar {}
3122
}
3223
impl const AlwaysConst for u32 {
33-
type Bar<T> = () where T: ~const Bar;
34-
3524
fn foo<T>() where T: ~const Bar {}
3625
}
3726

Diff for: tests/ui/traits/next-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr

+1-11
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
1-
error[E0275]: overflow evaluating the requirement `<T as Overflow>::Assoc: Sized`
1+
error[E0275]: overflow evaluating the requirement `<T as Overflow>::Assoc == _`
22
--> $DIR/trait_ref_is_knowable-norm-overflow.rs:10:18
33
|
44
LL | type Assoc = <T as Overflow>::Assoc;
55
| ^^^^^^^^^^^^^^^^^^^^^^
6-
|
7-
note: required by a bound in `Overflow::Assoc`
8-
--> $DIR/trait_ref_is_knowable-norm-overflow.rs:7:5
9-
|
10-
LL | type Assoc;
11-
| ^^^^^^^^^^^ required by this bound in `Overflow::Assoc`
12-
help: consider relaxing the implicit `Sized` restriction
13-
|
14-
LL | type Assoc: ?Sized;
15-
| ++++++++
166

177
error[E0119]: conflicting implementations of trait `Trait`
188
--> $DIR/trait_ref_is_knowable-norm-overflow.rs:18:1

Diff for: tests/ui/traits/next-solver/gat-wf.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//@ compile-flags: -Znext-solver
2+
3+
// Make sure that, like the old trait solver, we end up requiring that the WC of
4+
// impl GAT matches that of the trait. This is not a restriction that we *need*,
5+
// but is a side-effect of registering the where clauses when normalizing the GAT
6+
// when proving it satisfies its item bounds.
7+
8+
trait Foo {
9+
type T<'a>: Sized where Self: 'a;
10+
}
11+
12+
impl Foo for &() {
13+
type T<'a> = (); //~ the type `&()` does not fulfill the required lifetime
14+
}
15+
16+
fn main() {}

Diff for: tests/ui/traits/next-solver/gat-wf.stderr

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0477]: the type `&()` does not fulfill the required lifetime
2+
--> $DIR/gat-wf.rs:13:18
3+
|
4+
LL | type T<'a> = ();
5+
| ^^
6+
|
7+
note: type must outlive the lifetime `'a` as defined here
8+
--> $DIR/gat-wf.rs:13:12
9+
|
10+
LL | type T<'a> = ();
11+
| ^^
12+
13+
error: aborting due to 1 previous error
14+
15+
For more information about this error, try `rustc --explain E0477`.

0 commit comments

Comments
 (0)