Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow hidden lifetimes in impl Trait #57870

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 1 addition & 47 deletions src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2011,53 +2011,6 @@ a (non-transparent) struct containing a single float, while `Grams` is a
transparent wrapper around a float. This can make a difference for the ABI.
"##,

E0700: r##"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we outright remove error code descriptions we don't emit anymore (at least not if they have hit stable, I'm sure).

The `impl Trait` return type captures lifetime parameters that do not
appear within the `impl Trait` itself.

Erroneous code example:

```compile-fail,E0700
use std::cell::Cell;

trait Trait<'a> { }

impl<'a, 'b> Trait<'b> for Cell<&'a u32> { }

fn foo<'x, 'y>(x: Cell<&'x u32>) -> impl Trait<'y>
where 'x: 'y
{
x
}
```

Here, the function `foo` returns a value of type `Cell<&'x u32>`,
which references the lifetime `'x`. However, the return type is
declared as `impl Trait<'y>` -- this indicates that `foo` returns
"some type that implements `Trait<'y>`", but it also indicates that
the return type **only captures data referencing the lifetime `'y`**.
In this case, though, we are referencing data with lifetime `'x`, so
this function is in error.

To fix this, you must reference the lifetime `'x` from the return
type. For example, changing the return type to `impl Trait<'y> + 'x`
would work:

```
use std::cell::Cell;

trait Trait<'a> { }

impl<'a,'b> Trait<'b> for Cell<&'a u32> { }

fn foo<'x, 'y>(x: Cell<&'x u32>) -> impl Trait<'y> + 'x
where 'x: 'y
{
x
}
```
"##,

E0701: r##"
This error indicates that a `#[non_exhaustive]` attribute was incorrectly placed
on something other than a struct or enum.
Expand Down Expand Up @@ -2141,6 +2094,7 @@ register_diagnostics! {
E0687, // in-band lifetimes cannot be used in `fn`/`Fn` syntax
E0688, // in-band lifetimes cannot be mixed with explicit lifetime binders
E0697, // closures cannot be static
// E0700, // `impl Trait` can capture unnamed lifetime parameters now
E0707, // multiple elided lifetimes used in arguments of `async fn`
E0708, // `async` non-`move` closures with arguments are not currently supported
E0709, // multiple different lifetimes used in arguments of `async fn`
Expand Down
157 changes: 27 additions & 130 deletions src/librustc/infer/opaque_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,13 +449,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// the function, by replacing invalid regions with 'static,
// after producing an error for each of them.
let definition_ty =
instantiated_ty.fold_with(&mut ReverseMapper::new(
self.tcx,
self.is_tainted_by_errors(),
def_id,
map,
instantiated_ty,
));
instantiated_ty.fold_with(&mut ReverseMapper { tcx: self.tcx, map });

debug!(
"infer_opaque_definition_from_instantiation: definition_ty={:?}",
definition_ty
Expand All @@ -472,49 +467,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {

struct ReverseMapper<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
tcx: TyCtxt<'cx, 'gcx, 'tcx>,

/// If errors have already been reported in this fn, we suppress
/// our own errors because they are sometimes derivative.
tainted_by_errors: bool,

opaque_type_def_id: DefId,
map: FxHashMap<Kind<'tcx>, Kind<'gcx>>,
map_missing_regions_to_empty: bool,

/// initially `Some`, set to `None` once error has been reported
hidden_ty: Option<Ty<'tcx>>,
}

impl<'cx, 'gcx, 'tcx> ReverseMapper<'cx, 'gcx, 'tcx> {
fn new(
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
tainted_by_errors: bool,
opaque_type_def_id: DefId,
map: FxHashMap<Kind<'tcx>, Kind<'gcx>>,
hidden_ty: Ty<'tcx>,
) -> Self {
Self {
tcx,
tainted_by_errors,
opaque_type_def_id,
map,
map_missing_regions_to_empty: false,
hidden_ty: Some(hidden_ty),
}
}

fn fold_kind_mapping_missing_regions_to_empty(&mut self, kind: Kind<'tcx>) -> Kind<'tcx> {
assert!(!self.map_missing_regions_to_empty);
self.map_missing_regions_to_empty = true;
let kind = kind.fold_with(self);
self.map_missing_regions_to_empty = false;
kind
}

fn fold_kind_normally(&mut self, kind: Kind<'tcx>) -> Kind<'tcx> {
assert!(!self.map_missing_regions_to_empty);
kind.fold_with(self)
}
}

impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for ReverseMapper<'cx, 'gcx, 'tcx> {
Expand Down Expand Up @@ -542,91 +495,35 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for ReverseMapper<'cx, 'gcx, 'tcx>
Some(UnpackedKind::Lifetime(r1)) => r1,
Some(u) => panic!("region mapped to unexpected kind: {:?}", u),
None => {
if !self.map_missing_regions_to_empty && !self.tainted_by_errors {
if let Some(hidden_ty) = self.hidden_ty.take() {
let span = self.tcx.def_span(self.opaque_type_def_id);
let mut err = struct_span_err!(
self.tcx.sess,
span,
E0700,
"hidden type for `impl Trait` captures lifetime that \
does not appear in bounds",
);

// Assuming regionck succeeded, then we must
// be capturing *some* region from the fn
// header, and hence it must be free, so it's
// ok to invoke this fn (which doesn't accept
// all regions, and would ICE if an
// inappropriate region is given). We check
// `is_tainted_by_errors` by errors above, so
// we don't get in here unless regionck
// succeeded. (Note also that if regionck
// failed, then the regions we are attempting
// to map here may well be giving errors
// *because* the constraints were not
// satisfiable.)
self.tcx.note_and_explain_free_region(
&mut err,
&format!("hidden type `{}` captures ", hidden_ty),
r,
""
);

err.emit();
}
}
// No mapping was found. This means that it is either a
// disallowed lifetime, which will be caught by regionck,
// a region in a non-upvar closure generic, which is
// explicitly allowed (see below for more on this),
// or a lifetime that does not explicitly appear in
// `impl Trait` bounds but which outlives the lifetimes
// or types which *do* appear in the `impl Trait` bounds.
//
// These lifetimes are explicitly allowed to prevent
// the user from having to add dummy references to the
// unnamed lifetimes in their `impl Trait` bounds
// (e.g. `+ DummyTraitWithALifetimeArg<'a>`). Adding
// the lifetimes via `+` doesn't work because the type
// doesn't outlive those lifetimes, it just contains them.
//
// On closures: there is a somewhat subtle (read: hacky)
// consideration. The problem is that our closure types
// currently include all hte lifetime parameters declared
// on the enclosing function, even if they are unused by
// the closure itself. We can't readily filter them out,
// so we replace them with `empty`. This can't really make
// a diference to the rest of hte compiler; those regions
// are ignored for the outlives relation, and hence don't
// affect trait selection or auto traits, and they are
// erased during trans.
self.tcx.types.re_empty
},
}
}

fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
match ty.sty {
ty::Closure(def_id, substs) => {
// I am a horrible monster and I pray for death. When
// we encounter a closure here, it is always a closure
// from within the function that we are currently
// type-checking -- one that is now being encapsulated
// in an existential abstract type. Ideally, we would
// go through the types/lifetimes that it references
// and treat them just like we would any other type,
// which means we would error out if we find any
// reference to a type/region that is not in the
// "reverse map".
//
// **However,** in the case of closures, there is a
// somewhat subtle (read: hacky) consideration. The
// problem is that our closure types currently include
// all the lifetime parameters declared on the
// enclosing function, even if they are unused by the
// closure itself. We can't readily filter them out,
// so here we replace those values with `'empty`. This
// can't really make a difference to the rest of the
// compiler; those regions are ignored for the
// outlives relation, and hence don't affect trait
// selection or auto traits, and they are erased
// during codegen.

let generics = self.tcx.generics_of(def_id);
let substs = self.tcx.mk_substs(substs.substs.iter().enumerate().map(
|(index, &kind)| {
if index < generics.parent_count {
// Accommodate missing regions in the parent kinds...
self.fold_kind_mapping_missing_regions_to_empty(kind)
} else {
// ...but not elsewhere.
self.fold_kind_normally(kind)
}
},
));

self.tcx.mk_closure(def_id, ty::ClosureSubsts { substs })
}

_ => ty.super_fold_with(self),
}
}
}

struct Instantiator<'a, 'gcx: 'tcx, 'tcx: 'a> {
Expand Down
6 changes: 3 additions & 3 deletions src/test/ui/impl-trait/issue-55608-captures-empty-region.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// compile-pass
//
// This used to ICE because it creates an `impl Trait` that captures a
// hidden empty region.

#![feature(conservative_impl_trait)]

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

Expand Down
11 changes: 0 additions & 11 deletions src/test/ui/impl-trait/issue-55608-captures-empty-region.stderr

This file was deleted.

9 changes: 6 additions & 3 deletions src/test/ui/impl-trait/region-escape-via-bound.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Test that we do not allow the region `'x` to escape in the impl
// trait **even though** `'y` escapes, which outlives `'x`.
// run-pass
//
// Test that we allow the region `'x` to escape in the impl
// because 'y` escapes, which outlives `'x`.
//
// See https://github.com/rust-lang/rust/issues/46541 for more details.

Expand All @@ -14,7 +16,8 @@ trait Trait<'a> { }
impl Trait<'b> for Cell<&'a u32> { }

fn foo(x: Cell<&'x u32>) -> impl Trait<'y>
//~^ ERROR hidden type for `impl Trait` captures lifetime that does not appear in bounds [E0700]
// ^ hidden type for `impl Trait` captures lifetime that does not appear in bounds
// because it outlives the lifetime that *does* appear in the bounds, `'y`
where 'x: 'y
{
x
Expand Down
15 changes: 0 additions & 15 deletions src/test/ui/impl-trait/region-escape-via-bound.stderr

This file was deleted.