Skip to content

Commit 83c1788

Browse files
committed
Make outlives::{components,verify} agree
1 parent 5b9775f commit 83c1788

File tree

4 files changed

+123
-53
lines changed

4 files changed

+123
-53
lines changed

Diff for: compiler/rustc_infer/src/infer/outlives/components.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,11 @@ fn compute_components<'tcx>(
190190
}
191191
}
192192

193-
fn compute_components_recursive<'tcx>(
193+
/// Collect [Component]s for *all* the substs of `parent`.
194+
///
195+
/// This should not be used to get the components of `parent` itself.
196+
/// Use [push_outlives_components] instead.
197+
pub(super) fn compute_components_recursive<'tcx>(
194198
tcx: TyCtxt<'tcx>,
195199
parent: GenericArg<'tcx>,
196200
out: &mut SmallVec<[Component<'tcx>; 4]>,

Diff for: compiler/rustc_infer/src/infer/outlives/verify.rs

+41-52
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1+
use crate::infer::outlives::components::{compute_components_recursive, Component};
12
use crate::infer::outlives::env::RegionBoundPairs;
23
use crate::infer::region_constraints::VerifyIfEq;
34
use crate::infer::{GenericKind, VerifyBound};
45
use rustc_data_structures::captures::Captures;
56
use rustc_data_structures::sso::SsoHashSet;
67
use rustc_hir::def_id::DefId;
7-
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
8+
use rustc_middle::ty::subst::{GenericArg, Subst};
89
use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt};
910

11+
use smallvec::smallvec;
12+
1013
/// The `TypeOutlives` struct has the job of "lowering" a `T: 'a`
1114
/// obligation into a series of `'a: 'b` constraints and "verifys", as
1215
/// described on the module comment. The final constraints are emitted
@@ -47,43 +50,6 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
4750
}
4851
}
4952

50-
fn type_bound(
51-
&self,
52-
ty: Ty<'tcx>,
53-
visited: &mut SsoHashSet<GenericArg<'tcx>>,
54-
) -> VerifyBound<'tcx> {
55-
match *ty.kind() {
56-
ty::Param(p) => self.param_bound(p),
57-
ty::Projection(data) => self.projection_bound(data, visited),
58-
ty::FnDef(_, substs) => {
59-
// HACK(eddyb) ignore lifetimes found shallowly in `substs`.
60-
// This is inconsistent with `ty::Adt` (including all substs),
61-
// but consistent with previous (accidental) behavior.
62-
// See https://github.com/rust-lang/rust/issues/70917
63-
// for further background and discussion.
64-
let mut bounds = substs
65-
.iter()
66-
.filter_map(|child| match child.unpack() {
67-
GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)),
68-
GenericArgKind::Lifetime(_) => None,
69-
GenericArgKind::Const(_) => Some(self.recursive_bound(child, visited)),
70-
})
71-
.filter(|bound| {
72-
// Remove bounds that must hold, since they are not interesting.
73-
!bound.must_hold()
74-
});
75-
76-
match (bounds.next(), bounds.next()) {
77-
(Some(first), None) => first,
78-
(first, second) => VerifyBound::AllBounds(
79-
first.into_iter().chain(second).chain(bounds).collect(),
80-
),
81-
}
82-
}
83-
_ => self.recursive_bound(ty.into(), visited),
84-
}
85-
}
86-
8753
fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> {
8854
debug!("param_bound(param_ty={:?})", param_ty);
8955

@@ -188,27 +154,24 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
188154
.map(|r| VerifyBound::OutlivedBy(r));
189155

190156
// see the extensive comment in projection_must_outlive
191-
let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs);
192-
let recursive_bound = self.recursive_bound(ty.into(), visited);
157+
let recursive_bound = {
158+
let mut components = smallvec![];
159+
let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs);
160+
compute_components_recursive(self.tcx, ty.into(), &mut components, visited);
161+
self.bound_from_components(&components, visited)
162+
};
193163

194164
VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound)
195165
}
196166

197-
fn recursive_bound(
167+
fn bound_from_components(
198168
&self,
199-
parent: GenericArg<'tcx>,
169+
components: &[Component<'tcx>],
200170
visited: &mut SsoHashSet<GenericArg<'tcx>>,
201171
) -> VerifyBound<'tcx> {
202-
let mut bounds = parent
203-
.walk_shallow(visited)
204-
.filter_map(|child| match child.unpack() {
205-
GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)),
206-
GenericArgKind::Lifetime(lt) => {
207-
// Ignore late-bound regions.
208-
if !lt.is_late_bound() { Some(VerifyBound::OutlivedBy(lt)) } else { None }
209-
}
210-
GenericArgKind::Const(_) => Some(self.recursive_bound(child, visited)),
211-
})
172+
let mut bounds = components
173+
.iter()
174+
.map(|component| self.bound_from_single_component(component, visited))
212175
.filter(|bound| {
213176
// Remove bounds that must hold, since they are not interesting.
214177
!bound.must_hold()
@@ -222,6 +185,32 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
222185
}
223186
}
224187

188+
fn bound_from_single_component(
189+
&self,
190+
component: &Component<'tcx>,
191+
visited: &mut SsoHashSet<GenericArg<'tcx>>,
192+
) -> VerifyBound<'tcx> {
193+
match *component {
194+
Component::Region(lt) => VerifyBound::OutlivedBy(lt),
195+
Component::Param(param_ty) => self.param_bound(param_ty),
196+
Component::Projection(projection_ty) => self.projection_bound(projection_ty, visited),
197+
Component::EscapingProjection(ref components) => {
198+
self.bound_from_components(components, visited)
199+
}
200+
Component::UnresolvedInferenceVariable(v) => {
201+
// ignore this, we presume it will yield an error
202+
// later, since if a type variable is not resolved by
203+
// this point it never will be
204+
self.tcx.sess.delay_span_bug(
205+
rustc_span::DUMMY_SP,
206+
&format!("unresolved inference variable in outlives: {:?}", v),
207+
);
208+
// add a bound that never holds
209+
VerifyBound::AnyBound(vec![])
210+
}
211+
}
212+
}
213+
225214
/// Searches the environment for where-clauses like `G: 'a` where
226215
/// `G` is either some type parameter `T` or a projection like
227216
/// `T::Item`. Returns a vector of the `'a` bounds it can find.
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Regression test for #97405.
2+
// In `good_generic_fn` the param `T` ends up in the substs of closures/generators,
3+
// but we should be able to prove `<Gen<T> as Iterator>::Item: 'static` without
4+
// requiring `T: 'static`
5+
6+
// edition:2018
7+
// check-fail
8+
9+
fn opaque<F>(_: F) -> impl Iterator { b"".iter() }
10+
11+
fn assert_static<T: 'static>(_: T) {}
12+
13+
fn good_generic_fn<T>() {
14+
// Previously, proving `<OpaqueTy<type_of(async {})> as Iterator>::Item: 'static`
15+
// used to require `T: 'static`.
16+
assert_static(opaque(async {}).next());
17+
assert_static(opaque(|| {}).next());
18+
assert_static(opaque(opaque(async {}).next()).next());
19+
}
20+
21+
22+
// This should fail because `T` ends up in the upvars of the closure.
23+
fn bad_generic_fn<T: Copy>(t: T) {
24+
assert_static(opaque(async move { t; }).next());
25+
//~^ ERROR the associated type `<impl Iterator as Iterator>::Item` may not live long enough
26+
assert_static(opaque(move || { t; }).next());
27+
//~^ ERROR the associated type `<impl Iterator as Iterator>::Item` may not live long enough
28+
assert_static(opaque(opaque(async move { t; }).next()).next());
29+
//~^ ERROR the associated type `<impl Iterator as Iterator>::Item` may not live long enough
30+
}
31+
32+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
error[E0310]: the associated type `<impl Iterator as Iterator>::Item` may not live long enough
2+
--> $DIR/closure-in-projection-issue-97405.rs:24:5
3+
|
4+
LL | assert_static(opaque(async move { t; }).next());
5+
| ^^^^^^^^^^^^^
6+
|
7+
= help: consider adding an explicit lifetime bound `<impl Iterator as Iterator>::Item: 'static`...
8+
= note: ...so that the type `Option<<impl Iterator as Iterator>::Item>` will meet its required lifetime bounds...
9+
note: ...that is required by this bound
10+
--> $DIR/closure-in-projection-issue-97405.rs:11:21
11+
|
12+
LL | fn assert_static<T: 'static>(_: T) {}
13+
| ^^^^^^^
14+
15+
error[E0310]: the associated type `<impl Iterator as Iterator>::Item` may not live long enough
16+
--> $DIR/closure-in-projection-issue-97405.rs:26:5
17+
|
18+
LL | assert_static(opaque(move || { t; }).next());
19+
| ^^^^^^^^^^^^^
20+
|
21+
= help: consider adding an explicit lifetime bound `<impl Iterator as Iterator>::Item: 'static`...
22+
= note: ...so that the type `Option<<impl Iterator as Iterator>::Item>` will meet its required lifetime bounds...
23+
note: ...that is required by this bound
24+
--> $DIR/closure-in-projection-issue-97405.rs:11:21
25+
|
26+
LL | fn assert_static<T: 'static>(_: T) {}
27+
| ^^^^^^^
28+
29+
error[E0310]: the associated type `<impl Iterator as Iterator>::Item` may not live long enough
30+
--> $DIR/closure-in-projection-issue-97405.rs:28:5
31+
|
32+
LL | assert_static(opaque(opaque(async move { t; }).next()).next());
33+
| ^^^^^^^^^^^^^
34+
|
35+
= help: consider adding an explicit lifetime bound `<impl Iterator as Iterator>::Item: 'static`...
36+
= note: ...so that the type `Option<<impl Iterator as Iterator>::Item>` will meet its required lifetime bounds...
37+
note: ...that is required by this bound
38+
--> $DIR/closure-in-projection-issue-97405.rs:11:21
39+
|
40+
LL | fn assert_static<T: 'static>(_: T) {}
41+
| ^^^^^^^
42+
43+
error: aborting due to 3 previous errors
44+
45+
For more information about this error, try `rustc --explain E0310`.

0 commit comments

Comments
 (0)