Constrain all regions in the concrete type for an opaque type

This commit is contained in:
Matthew Jasper 2019-05-01 21:15:01 +01:00
parent 6cc24f2603
commit d72f4de659
6 changed files with 129 additions and 72 deletions

View File

@ -1,13 +1,14 @@
use rustc_data_structures::fx::FxHashMap;
use syntax_pos::Span;
use crate::hir::def_id::DefId;
use crate::hir;
use crate::hir::Node;
use crate::infer::{self, InferCtxt, InferOk, TypeVariableOrigin};
use crate::infer::outlives::free_region_map::FreeRegionRelations;
use rustc_data_structures::fx::FxHashMap;
use crate::traits::{self, PredicateObligation};
use crate::ty::{self, Ty, TyCtxt, GenericParamDefKind};
use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder};
use crate::ty::outlives::Component;
use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor};
use crate::ty::subst::{Kind, InternalSubsts, SubstsRef, UnpackedKind};
use crate::util::nodemap::DefIdMap;
@ -373,58 +374,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
let least_region = least_region.unwrap_or(self.tcx.lifetimes.re_static);
debug!("constrain_opaque_types: least_region={:?}", least_region);
// Require that the type `concrete_ty` outlives
// `least_region`, modulo any type parameters that appear
// in the type, which we ignore. This is because impl
// trait values are assumed to capture all the in-scope
// type parameters. This little loop here just invokes
// `outlives` repeatedly, draining all the nested
// obligations that result.
let mut types = vec![concrete_ty];
let bound_region = |r| self.sub_regions(infer::CallReturn(span), least_region, r);
while let Some(ty) = types.pop() {
let mut components = smallvec![];
self.tcx.push_outlives_components(ty, &mut components);
while let Some(component) = components.pop() {
match component {
Component::Region(r) => {
bound_region(r);
}
Component::Param(_) => {
// ignore type parameters like `T`, they are captured
// implicitly by the `impl Trait`
}
Component::UnresolvedInferenceVariable(_) => {
// we should get an error that more type
// annotations are needed in this case
self.tcx
.sess
.delay_span_bug(span, "unresolved inf var in opaque");
}
Component::Projection(ty::ProjectionTy {
substs,
item_def_id: _,
}) => {
for k in substs {
match k.unpack() {
UnpackedKind::Lifetime(lt) => bound_region(lt),
UnpackedKind::Type(ty) => types.push(ty),
UnpackedKind::Const(_) => {
// Const parameters don't impose constraints.
}
}
}
}
Component::EscapingProjection(more_components) => {
components.extend(more_components);
}
}
}
}
concrete_ty.visit_with(&mut OpaqueTypeOutlivesVisitor {
infcx: self,
least_region,
span,
});
}
/// Given the fully resolved, instantiated type for an opaque
@ -502,6 +456,80 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
}
// Visitor that requires that (almost) all regions in the type visited outlive
// `least_region`. We cannot use `push_outlives_components` because regions in
// closure signatures are not included in their outlives components. We need to
// ensure all regions outlive the given bound so that we don't end up with,
// say, `ReScope` appearing in a return type and causing ICEs when other
// functions end up with region constraints involving regions from other
// functions.
//
// We also cannot use `for_each_free_region` because for closures it includes
// the regions parameters from the enclosing item.
//
// We ignore any type parameters because impl trait values are assumed to
// capture all the in-scope type parameters.
struct OpaqueTypeOutlivesVisitor<'a, 'gcx, 'tcx> {
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
least_region: ty::Region<'tcx>,
span: Span,
}
impl<'tcx> TypeVisitor<'tcx> for OpaqueTypeOutlivesVisitor<'_, '_, 'tcx>
{
fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &ty::Binder<T>) -> bool {
t.skip_binder().visit_with(self);
false // keep visiting
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
match *r {
// ignore bound regions, keep visiting
ty::ReLateBound(_, _) => false,
_ => {
self.infcx.sub_regions(infer::CallReturn(self.span), self.least_region, r);
false
}
}
}
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
// We're only interested in types involving regions
if !ty.flags.intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
return false; // keep visiting
}
match ty.sty {
ty::Closure(def_id, ref substs) => {
// Skip lifetime parameters of the enclosing item(s)
for upvar_ty in substs.upvar_tys(def_id, self.infcx.tcx) {
upvar_ty.visit_with(self);
}
substs.closure_sig_ty(def_id, self.infcx.tcx).visit_with(self);
}
ty::Generator(def_id, ref substs, _) => {
// Skip lifetime parameters of the enclosing item(s)
// Also skip the witness type, because that has no free regions.
for upvar_ty in substs.upvar_tys(def_id, self.infcx.tcx) {
upvar_ty.visit_with(self);
}
substs.return_ty(def_id, self.infcx.tcx).visit_with(self);
substs.yield_ty(def_id, self.infcx.tcx).visit_with(self);
}
_ => {
ty.super_visit_with(self);
}
}
false
}
}
struct ReverseMapper<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
@ -563,8 +591,7 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for ReverseMapper<'cx, 'gcx, 'tcx>
// ignore `'static`, as that can appear anywhere
ty::ReStatic |
// ignore `ReScope`, as that can appear anywhere
// See `src/test/run-pass/issue-49556.rs` for example.
// ignore `ReScope`, which may appear in impl Trait in bindings.
ty::ReScope(..) => return r,
_ => { }

View File

@ -0,0 +1,19 @@
// Test that we are special casing "outlives" for opaque types.
//
// The return type of a closure is not required to outlive the closure. As such
// the following code would not compile if we used a standard outlives check
// when checking the return type, because the return type of the closure would
// be `&ReEmpty i32`, and we don't allow `ReEmpty` to occur in the concrete
// type used for an opaque type.
//
// However, opaque types are special cased to include check all regions in the
// concrete type against the bound, which forces the return type to be
// `&'static i32` here.
// compile-pass
fn make_identity() -> impl Sized {
|x: &'static i32| x
}
fn main() {}

View File

@ -1,9 +1,9 @@
// This used to ICE because it creates an `impl Trait` that captures a
// hidden empty region.
#![feature(conservative_impl_trait)]
// compile-pass
fn server() -> impl FilterBase2 { //~ ERROR [E0700]
fn server() -> impl FilterBase2 {
segment2(|| { loop { } }).map2(|| "")
}

View File

@ -1,11 +0,0 @@
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
--> $DIR/issue-55608-captures-empty-region.rs:6:16
|
LL | fn server() -> impl FilterBase2 {
| ^^^^^^^^^^^^^^^^
|
= note: hidden type `Map2<[closure@$DIR/issue-55608-captures-empty-region.rs:7:36: 7:41]>` captures an empty lifetime
error: aborting due to previous error
For more information about this error, try `rustc --explain E0700`.

View File

@ -0,0 +1,22 @@
// Regression test for issue 57464.
//
// Closure are (surprisingly) allowed to outlive their signature. As such it
// was possible to end up with `ReScope`s appearing in the concrete type of an
// opaque type. As all regions are now required to outlive the bound in an
// opaque type we avoid the issue here.
// compile-pass
struct A<F>(F);
unsafe impl <'a, 'b, F: Fn(&'a i32) -> &'b i32> Send for A<F> {}
fn wrapped_closure() -> impl Sized {
let f = |x| x;
f(&0);
A(f)
}
fn main() {
let x: Box<dyn Send> = Box::new(wrapped_closure());
}

View File

@ -2,10 +2,10 @@
fn iter<'a>(data: &'a [usize]) -> impl Iterator<Item = usize> + 'a {
data.iter()
.map(
|x| x // fn(&'a usize) -> &'(ReScope) usize
|x| x // fn(&'a usize) -> &'a usize
)
.map(
|x| *x // fn(&'(ReScope) usize) -> usize
|x| *x // fn(&'a usize) -> usize
)
}