Skip to content

Commit 57cfb40

Browse files
authored
Rollup merge of rust-lang#60449 - matthewjasper:impl-trait-outlives, r=pnkfelix
Constrain all regions in the concrete type for an opaque type `push_outlives_components` skips some regions in a type, notably the signature of a closure is ignored. Most of the time this is OK, but for opaque types the concrete type is used when checking auto-trait bounds in other functions. cc @nikomatsakis @pnkfelix Closes rust-lang#57464 Closes rust-lang#60127
2 parents 6e7bcff + d72f4de commit 57cfb40

6 files changed

+129
-72
lines changed

src/librustc/infer/opaque_types/mod.rs

+84-57
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1+
use rustc_data_structures::fx::FxHashMap;
2+
use syntax_pos::Span;
3+
14
use crate::hir::def_id::DefId;
25
use crate::hir;
36
use crate::hir::Node;
47
use crate::infer::{self, InferCtxt, InferOk, TypeVariableOrigin};
58
use crate::infer::outlives::free_region_map::FreeRegionRelations;
6-
use rustc_data_structures::fx::FxHashMap;
79
use crate::traits::{self, PredicateObligation};
810
use crate::ty::{self, Ty, TyCtxt, GenericParamDefKind};
9-
use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder};
10-
use crate::ty::outlives::Component;
11+
use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor};
1112
use crate::ty::subst::{Kind, InternalSubsts, SubstsRef, UnpackedKind};
1213
use crate::util::nodemap::DefIdMap;
1314

@@ -373,58 +374,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
373374
let least_region = least_region.unwrap_or(self.tcx.lifetimes.re_static);
374375
debug!("constrain_opaque_types: least_region={:?}", least_region);
375376

376-
// Require that the type `concrete_ty` outlives
377-
// `least_region`, modulo any type parameters that appear
378-
// in the type, which we ignore. This is because impl
379-
// trait values are assumed to capture all the in-scope
380-
// type parameters. This little loop here just invokes
381-
// `outlives` repeatedly, draining all the nested
382-
// obligations that result.
383-
let mut types = vec![concrete_ty];
384-
let bound_region = |r| self.sub_regions(infer::CallReturn(span), least_region, r);
385-
while let Some(ty) = types.pop() {
386-
let mut components = smallvec![];
387-
self.tcx.push_outlives_components(ty, &mut components);
388-
while let Some(component) = components.pop() {
389-
match component {
390-
Component::Region(r) => {
391-
bound_region(r);
392-
}
393-
394-
Component::Param(_) => {
395-
// ignore type parameters like `T`, they are captured
396-
// implicitly by the `impl Trait`
397-
}
398-
399-
Component::UnresolvedInferenceVariable(_) => {
400-
// we should get an error that more type
401-
// annotations are needed in this case
402-
self.tcx
403-
.sess
404-
.delay_span_bug(span, "unresolved inf var in opaque");
405-
}
406-
407-
Component::Projection(ty::ProjectionTy {
408-
substs,
409-
item_def_id: _,
410-
}) => {
411-
for k in substs {
412-
match k.unpack() {
413-
UnpackedKind::Lifetime(lt) => bound_region(lt),
414-
UnpackedKind::Type(ty) => types.push(ty),
415-
UnpackedKind::Const(_) => {
416-
// Const parameters don't impose constraints.
417-
}
418-
}
419-
}
420-
}
421-
422-
Component::EscapingProjection(more_components) => {
423-
components.extend(more_components);
424-
}
425-
}
426-
}
427-
}
377+
concrete_ty.visit_with(&mut OpaqueTypeOutlivesVisitor {
378+
infcx: self,
379+
least_region,
380+
span,
381+
});
428382
}
429383

430384
/// Given the fully resolved, instantiated type for an opaque
@@ -502,6 +456,80 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
502456
}
503457
}
504458

459+
// Visitor that requires that (almost) all regions in the type visited outlive
460+
// `least_region`. We cannot use `push_outlives_components` because regions in
461+
// closure signatures are not included in their outlives components. We need to
462+
// ensure all regions outlive the given bound so that we don't end up with,
463+
// say, `ReScope` appearing in a return type and causing ICEs when other
464+
// functions end up with region constraints involving regions from other
465+
// functions.
466+
//
467+
// We also cannot use `for_each_free_region` because for closures it includes
468+
// the regions parameters from the enclosing item.
469+
//
470+
// We ignore any type parameters because impl trait values are assumed to
471+
// capture all the in-scope type parameters.
472+
struct OpaqueTypeOutlivesVisitor<'a, 'gcx, 'tcx> {
473+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
474+
least_region: ty::Region<'tcx>,
475+
span: Span,
476+
}
477+
478+
impl<'tcx> TypeVisitor<'tcx> for OpaqueTypeOutlivesVisitor<'_, '_, 'tcx>
479+
{
480+
fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &ty::Binder<T>) -> bool {
481+
t.skip_binder().visit_with(self);
482+
false // keep visiting
483+
}
484+
485+
fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
486+
match *r {
487+
// ignore bound regions, keep visiting
488+
ty::ReLateBound(_, _) => false,
489+
_ => {
490+
self.infcx.sub_regions(infer::CallReturn(self.span), self.least_region, r);
491+
false
492+
}
493+
}
494+
}
495+
496+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
497+
// We're only interested in types involving regions
498+
if !ty.flags.intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
499+
return false; // keep visiting
500+
}
501+
502+
match ty.sty {
503+
ty::Closure(def_id, ref substs) => {
504+
// Skip lifetime parameters of the enclosing item(s)
505+
506+
for upvar_ty in substs.upvar_tys(def_id, self.infcx.tcx) {
507+
upvar_ty.visit_with(self);
508+
}
509+
510+
substs.closure_sig_ty(def_id, self.infcx.tcx).visit_with(self);
511+
}
512+
513+
ty::Generator(def_id, ref substs, _) => {
514+
// Skip lifetime parameters of the enclosing item(s)
515+
// Also skip the witness type, because that has no free regions.
516+
517+
for upvar_ty in substs.upvar_tys(def_id, self.infcx.tcx) {
518+
upvar_ty.visit_with(self);
519+
}
520+
521+
substs.return_ty(def_id, self.infcx.tcx).visit_with(self);
522+
substs.yield_ty(def_id, self.infcx.tcx).visit_with(self);
523+
}
524+
_ => {
525+
ty.super_visit_with(self);
526+
}
527+
}
528+
529+
false
530+
}
531+
}
532+
505533
struct ReverseMapper<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
506534
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
507535

@@ -563,8 +591,7 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for ReverseMapper<'cx, 'gcx, 'tcx>
563591
// ignore `'static`, as that can appear anywhere
564592
ty::ReStatic |
565593

566-
// ignore `ReScope`, as that can appear anywhere
567-
// See `src/test/run-pass/issue-49556.rs` for example.
594+
// ignore `ReScope`, which may appear in impl Trait in bindings.
568595
ty::ReScope(..) => return r,
569596

570597
_ => { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Test that we are special casing "outlives" for opaque types.
2+
//
3+
// The return type of a closure is not required to outlive the closure. As such
4+
// the following code would not compile if we used a standard outlives check
5+
// when checking the return type, because the return type of the closure would
6+
// be `&ReEmpty i32`, and we don't allow `ReEmpty` to occur in the concrete
7+
// type used for an opaque type.
8+
//
9+
// However, opaque types are special cased to include check all regions in the
10+
// concrete type against the bound, which forces the return type to be
11+
// `&'static i32` here.
12+
13+
// compile-pass
14+
15+
fn make_identity() -> impl Sized {
16+
|x: &'static i32| x
17+
}
18+
19+
fn main() {}

src/test/ui/impl-trait/issue-55608-captures-empty-region.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// This used to ICE because it creates an `impl Trait` that captures a
22
// hidden empty region.
33

4-
#![feature(conservative_impl_trait)]
4+
// compile-pass
55

6-
fn server() -> impl FilterBase2 { //~ ERROR [E0700]
6+
fn server() -> impl FilterBase2 {
77
segment2(|| { loop { } }).map2(|| "")
88
}
99

src/test/ui/impl-trait/issue-55608-captures-empty-region.stderr

-11
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Regression test for issue 57464.
2+
//
3+
// Closure are (surprisingly) allowed to outlive their signature. As such it
4+
// was possible to end up with `ReScope`s appearing in the concrete type of an
5+
// opaque type. As all regions are now required to outlive the bound in an
6+
// opaque type we avoid the issue here.
7+
8+
// compile-pass
9+
10+
struct A<F>(F);
11+
12+
unsafe impl <'a, 'b, F: Fn(&'a i32) -> &'b i32> Send for A<F> {}
13+
14+
fn wrapped_closure() -> impl Sized {
15+
let f = |x| x;
16+
f(&0);
17+
A(f)
18+
}
19+
20+
fn main() {
21+
let x: Box<dyn Send> = Box::new(wrapped_closure());
22+
}

src/test/ui/issues/issue-49556.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
fn iter<'a>(data: &'a [usize]) -> impl Iterator<Item = usize> + 'a {
33
data.iter()
44
.map(
5-
|x| x // fn(&'a usize) -> &'(ReScope) usize
5+
|x| x // fn(&'a usize) -> &'a usize
66
)
77
.map(
8-
|x| *x // fn(&'(ReScope) usize) -> usize
8+
|x| *x // fn(&'a usize) -> usize
99
)
1010
}
1111

0 commit comments

Comments
 (0)