Skip to content

Commit 76cedeb

Browse files
committed
Auto merge of rust-lang#118404 - aliemjay:implied-bounds-normalize, r=<try>
correctly handle normalization in implied bounds query v2 Revives rust-lang#109482. I've opened a new PR because Github doesn't allow me to reuse the old one. Updated to refresh the toolchain build in order to test bevy.
2 parents c2ec908 + b3e3b53 commit 76cedeb

File tree

8 files changed

+104
-107
lines changed

8 files changed

+104
-107
lines changed

compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs

+35-83
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ use rustc_infer::traits::query::OutlivesBound;
99
use rustc_middle::infer::canonical::CanonicalQueryResponse;
1010
use rustc_middle::traits::ObligationCause;
1111
use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
12-
use rustc_span::def_id::CRATE_DEF_ID;
13-
use rustc_span::DUMMY_SP;
1412
use smallvec::{smallvec, SmallVec};
1513

1614
#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
@@ -58,69 +56,52 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> {
5856
}
5957
}
6058

59+
/// For the sake of completeness, we should be careful when dealing with inference artifacts:
60+
/// - `ty` must be fully resolved.
61+
/// - `normalize_op` must return a fully resolved type.
6162
pub fn compute_implied_outlives_bounds_inner<'tcx>(
6263
ocx: &ObligationCtxt<'_, 'tcx>,
6364
param_env: ty::ParamEnv<'tcx>,
6465
ty: Ty<'tcx>,
6566
) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
66-
let tcx = ocx.infcx.tcx;
67+
let normalize_op = |ty: Ty<'tcx>| {
68+
let ty = if ocx.infcx.next_trait_solver() {
69+
solve::deeply_normalize(ocx.infcx.at(&ObligationCause::dummy(), param_env), ty)
70+
// FIXME(-Ztrait-solver=next)
71+
.unwrap_or_else(|_errs| ty)
72+
} else {
73+
ocx.normalize(&ObligationCause::dummy(), param_env, ty)
74+
};
75+
if !ocx.select_all_or_error().is_empty() {
76+
return Err(NoSolution);
77+
}
78+
let ty = ocx.infcx.resolve_vars_if_possible(ty);
79+
assert!(!ty.has_non_region_infer());
80+
Ok(ty)
81+
};
6782

6883
// Sometimes when we ask what it takes for T: WF, we get back that
6984
// U: WF is required; in that case, we push U onto this stack and
7085
// process it next. Because the resulting predicates aren't always
7186
// guaranteed to be a subset of the original type, so we need to store the
7287
// WF args we've computed in a set.
7388
let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
74-
let mut wf_args = vec![ty.into()];
89+
let mut wf_args = vec![ty.into(), normalize_op(ty)?.into()];
7590

76-
let mut outlives_bounds: Vec<ty::OutlivesPredicate<ty::GenericArg<'tcx>, ty::Region<'tcx>>> =
77-
vec![];
91+
let mut outlives_bounds: Vec<OutlivesBound<'tcx>> = vec![];
7892

7993
while let Some(arg) = wf_args.pop() {
8094
if !checked_wf_args.insert(arg) {
8195
continue;
8296
}
8397

84-
// Compute the obligations for `arg` to be well-formed. If `arg` is
85-
// an unresolved inference variable, just substituted an empty set
86-
// -- because the return type here is going to be things we *add*
87-
// to the environment, it's always ok for this set to be smaller
88-
// than the ultimate set. (Note: normally there won't be
89-
// unresolved inference variables here anyway, but there might be
90-
// during typeck under some circumstances.)
91-
//
92-
// FIXME(@lcnr): It's not really "always fine", having fewer implied
93-
// bounds can be backward incompatible, e.g. #101951 was caused by
94-
// us not dealing with inference vars in `TypeOutlives` predicates.
95-
let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP)
96-
.unwrap_or_default();
97-
98-
for obligation in obligations {
99-
debug!(?obligation);
98+
// From the full set of obligations, just filter down to the region relationships.
99+
for obligation in
100+
wf::unnormalized_obligations(ocx.infcx, param_env, arg).into_iter().flatten()
101+
{
100102
assert!(!obligation.has_escaping_bound_vars());
101-
102-
// While these predicates should all be implied by other parts of
103-
// the program, they are still relevant as they may constrain
104-
// inference variables, which is necessary to add the correct
105-
// implied bounds in some cases, mostly when dealing with projections.
106-
//
107-
// Another important point here: we only register `Projection`
108-
// predicates, since otherwise we might register outlives
109-
// predicates containing inference variables, and we don't
110-
// learn anything new from those.
111-
if obligation.predicate.has_non_region_infer() {
112-
match obligation.predicate.kind().skip_binder() {
113-
ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
114-
| ty::PredicateKind::AliasRelate(..) => {
115-
ocx.register_obligation(obligation.clone());
116-
}
117-
_ => {}
118-
}
119-
}
120-
121-
let pred = match obligation.predicate.kind().no_bound_vars() {
122-
None => continue,
123-
Some(pred) => pred,
103+
let Some(pred) = obligation.predicate.kind().no_bound_vars() else {
104+
continue;
124105
};
125106
match pred {
126107
ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
@@ -143,53 +124,24 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
143124
}
144125

145126
// We need to register region relationships
146-
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(
147-
r_a,
148-
r_b,
149-
))) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)),
127+
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(
128+
ty::OutlivesPredicate(r_a, r_b),
129+
)) => outlives_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a)),
150130

151131
ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
152132
ty_a,
153133
r_b,
154-
))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)),
155-
}
156-
}
157-
}
158-
159-
// This call to `select_all_or_error` is necessary to constrain inference variables, which we
160-
// use further down when computing the implied bounds.
161-
match ocx.select_all_or_error().as_slice() {
162-
[] => (),
163-
_ => return Err(NoSolution),
164-
}
165-
166-
// We lazily compute the outlives components as
167-
// `select_all_or_error` constrains inference variables.
168-
let mut implied_bounds = Vec::new();
169-
for ty::OutlivesPredicate(a, r_b) in outlives_bounds {
170-
match a.unpack() {
171-
ty::GenericArgKind::Lifetime(r_a) => {
172-
implied_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a))
173-
}
174-
ty::GenericArgKind::Type(ty_a) => {
175-
let mut ty_a = ocx.infcx.resolve_vars_if_possible(ty_a);
176-
// Need to manually normalize in the new solver as `wf::obligations` does not.
177-
if ocx.infcx.next_trait_solver() {
178-
ty_a = solve::deeply_normalize(
179-
ocx.infcx.at(&ObligationCause::dummy(), param_env),
180-
ty_a,
181-
)
182-
.map_err(|_errs| NoSolution)?;
134+
))) => {
135+
let ty_a = normalize_op(ty_a)?;
136+
let mut components = smallvec![];
137+
push_outlives_components(ocx.infcx.tcx, ty_a, &mut components);
138+
outlives_bounds.extend(implied_bounds_from_components(r_b, components))
183139
}
184-
let mut components = smallvec![];
185-
push_outlives_components(tcx, ty_a, &mut components);
186-
implied_bounds.extend(implied_bounds_from_components(r_b, components))
187140
}
188-
ty::GenericArgKind::Const(_) => unreachable!(),
189141
}
190142
}
191143

192-
Ok(implied_bounds)
144+
Ok(outlives_bounds)
193145
}
194146

195147
/// When we have an implied bound that `T: 'a`, we can further break

tests/ui/associated-inherent-types/issue-111404-1.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ impl<'a> Foo<fn(&'a ())> {
1010
fn bar(_: fn(Foo<for<'b> fn(Foo<fn(&'b ())>::Assoc)>::Assoc)) {}
1111
//~^ ERROR higher-ranked subtype error
1212
//~| ERROR higher-ranked subtype error
13+
//~| ERROR higher-ranked subtype error
1314

1415
fn main() {}

tests/ui/associated-inherent-types/issue-111404-1.stderr

+9-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,13 @@ LL | fn bar(_: fn(Foo<for<'b> fn(Foo<fn(&'b ())>::Assoc)>::Assoc)) {}
1212
|
1313
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
1414

15-
error: aborting due to 2 previous errors
15+
error: higher-ranked subtype error
16+
--> $DIR/issue-111404-1.rs:10:1
17+
|
18+
LL | fn bar(_: fn(Foo<for<'b> fn(Foo<fn(&'b ())>::Assoc)>::Assoc)) {}
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20+
|
21+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
22+
23+
error: aborting due to 3 previous errors
1624

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Foo<Vec<X>> shouldn't imply X: 'static.
2+
// We don't use region constraints from trait impls in implied bounds.
3+
4+
trait Trait {
5+
type Assoc;
6+
}
7+
8+
impl<X: 'static> Trait for Vec<X> {
9+
type Assoc = ();
10+
}
11+
12+
struct Foo<T: Trait>(T)
13+
where
14+
T::Assoc: 'static, // any predicate naming T::Assoc
15+
;
16+
17+
fn foo<X>(_: Foo<Vec<X>>) {}
18+
//~^ ERROR `X` may not live long enough
19+
//~| ERROR `X` may not live long enough
20+
21+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
error[E0310]: the parameter type `X` may not live long enough
2+
--> $DIR/from-trait-impl.rs:17:1
3+
|
4+
LL | fn foo<X>(_: Foo<Vec<X>>) {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
6+
| |
7+
| the parameter type `X` must be valid for the static lifetime...
8+
| ...so that the type `X` will meet its required lifetime bounds
9+
|
10+
help: consider adding an explicit lifetime bound
11+
|
12+
LL | fn foo<X: 'static>(_: Foo<Vec<X>>) {}
13+
| +++++++++
14+
15+
error[E0310]: the parameter type `X` may not live long enough
16+
--> $DIR/from-trait-impl.rs:17:14
17+
|
18+
LL | fn foo<X>(_: Foo<Vec<X>>) {}
19+
| ^^^^^^^^^^^
20+
| |
21+
| the parameter type `X` must be valid for the static lifetime...
22+
| ...so that the type `X` will meet its required lifetime bounds
23+
|
24+
help: consider adding an explicit lifetime bound
25+
|
26+
LL | fn foo<X: 'static>(_: Foo<Vec<X>>) {}
27+
| +++++++++
28+
29+
error: aborting due to 2 previous errors
30+
31+
For more information about this error, try `rustc --explain E0310`.

tests/ui/implied-bounds/normalization-nested.lifetime.stderr

-18
This file was deleted.

tests/ui/implied-bounds/normalization-nested.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
// Test for normalization of projections that appear in the item bounds
22
// (versus those that appear directly in the input types).
3-
// Both revisions should pass. `lifetime` revision is a bug.
3+
// Both revisions should pass.
4+
// `lifetime` revision was incorrectly rejected. See #109799.
45
//
56
// revisions: param_ty lifetime
6-
// [param_ty] check-pass
7-
// [lifetime] check-fail
8-
// [lifetime] known-bug: #109799
7+
// check-pass
98

109
pub trait Iter {
1110
type Item;

tests/ui/inference/issue-80409.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
// check-pass
1+
// This is an ICE because of #104478.
2+
// known-bug: unknown
3+
// failure-status: 101
4+
// dont-check-compiler-stderr
25

36
#![allow(unreachable_code, unused)]
47

0 commit comments

Comments
 (0)