Skip to content

Commit e575773

Browse files
committed
Auto merge of #49041 - nikomatsakis:issue-46541-impl-trait-hidden-lifetimes, r=cramertj
Detect illegal hidden lifetimes in `impl Trait` This branch fixes #46541 -- however, it presently doesn't build because it also *breaks* a number of existing usages of impl Trait. I'm opening it as a WIP for now, just because we want to move on impl Trait, but I'll try to fix the problem in a bit. ~~(The problem is due to the fact that we apparently infer stricter lifetimes in closures that we need to; for example, if you capture a variable of type `&'a &'b u32`, we will put *precisely* those lifetimes into the closure, even if the closure would be happy with `&'a &'a u32`. This causes the present chance to affect things that are not invariant.)~~ fixed r? @cramertj
2 parents eb8d08d + 2e8a1ab commit e575773

17 files changed

+427
-84
lines changed

src/librustc/diagnostics.rs

+52
Original file line numberDiff line numberDiff line change
@@ -2074,6 +2074,58 @@ a (non-transparent) struct containing a single float, while `Grams` is a
20742074
transparent wrapper around a float. This can make a difference for the ABI.
20752075
"##,
20762076

2077+
E0909: r##"
2078+
The `impl Trait` return type captures lifetime parameters that do not
2079+
appear within the `impl Trait` itself.
2080+
2081+
Erroneous code example:
2082+
2083+
```compile-fail,E0909
2084+
#![feature(conservative_impl_trait)]
2085+
2086+
use std::cell::Cell;
2087+
2088+
trait Trait<'a> { }
2089+
2090+
impl<'a, 'b> Trait<'b> for Cell<&'a u32> { }
2091+
2092+
fn foo<'x, 'y>(x: Cell<&'x u32>) -> impl Trait<'y>
2093+
where 'x: 'y
2094+
{
2095+
x
2096+
}
2097+
```
2098+
2099+
Here, the function `foo` returns a value of type `Cell<&'x u32>`,
2100+
which references the lifetime `'x`. However, the return type is
2101+
declared as `impl Trait<'y>` -- this indicates that `foo` returns
2102+
"some type that implements `Trait<'y>`", but it also indicates that
2103+
the return type **only captures data referencing the lifetime `'y`**.
2104+
In this case, though, we are referencing data with lifetime `'x`, so
2105+
this function is in error.
2106+
2107+
To fix this, you must reference the lifetime `'x` from the return
2108+
type. For example, changing the return type to `impl Trait<'y> + 'x`
2109+
would work:
2110+
2111+
```
2112+
#![feature(conservative_impl_trait)]
2113+
2114+
use std::cell::Cell;
2115+
2116+
trait Trait<'a> { }
2117+
2118+
impl<'a,'b> Trait<'b> for Cell<&'a u32> { }
2119+
2120+
fn foo<'x, 'y>(x: Cell<&'x u32>) -> impl Trait<'y> + 'x
2121+
where 'x: 'y
2122+
{
2123+
x
2124+
}
2125+
```
2126+
"##,
2127+
2128+
20772129
}
20782130

20792131

src/librustc/infer/anon_types/mod.rs

+185-57
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ use infer::outlives::free_region_map::FreeRegionRelations;
1414
use rustc_data_structures::fx::FxHashMap;
1515
use syntax::ast;
1616
use traits::{self, PredicateObligation};
17-
use ty::{self, Ty};
18-
use ty::fold::{BottomUpFolder, TypeFoldable};
17+
use ty::{self, Ty, TyCtxt};
18+
use ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder};
1919
use ty::outlives::Component;
20-
use ty::subst::{Kind, UnpackedKind, Substs};
20+
use ty::subst::{Kind, Substs, UnpackedKind};
2121
use util::nodemap::DefIdMap;
2222

2323
pub type AnonTypeMap<'tcx> = DefIdMap<AnonTypeDecl<'tcx>>;
@@ -113,10 +113,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
113113
) -> InferOk<'tcx, (T, AnonTypeMap<'tcx>)> {
114114
debug!(
115115
"instantiate_anon_types(value={:?}, parent_def_id={:?}, body_id={:?}, param_env={:?})",
116-
value,
117-
parent_def_id,
118-
body_id,
119-
param_env,
116+
value, parent_def_id, body_id, param_env,
120117
);
121118
let mut instantiator = Instantiator {
122119
infcx: self,
@@ -458,55 +455,184 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
458455
// Convert the type from the function into a type valid outside
459456
// the function, by replacing invalid regions with 'static,
460457
// after producing an error for each of them.
461-
let definition_ty = gcx.fold_regions(&instantiated_ty, &mut false, |r, _| {
462-
match *r {
463-
// 'static and early-bound regions are valid.
464-
ty::ReStatic | ty::ReEmpty => r,
465-
466-
// All other regions, we map them appropriately to their adjusted
467-
// indices, erroring if we find any lifetimes that were not mapped
468-
// into the new set.
469-
_ => if let Some(UnpackedKind::Lifetime(r1)) = map.get(&r.into())
470-
.map(|k| k.unpack()) {
471-
r1
472-
} else {
473-
// No mapping was found. This means that
474-
// it is either a disallowed lifetime,
475-
// which will be caught by regionck, or it
476-
// is a region in a non-upvar closure
477-
// generic, which is explicitly
478-
// allowed. If that surprises you, read
479-
// on.
480-
//
481-
// The case of closure is a somewhat
482-
// subtle (read: hacky) consideration. The
483-
// problem is that our closure types
484-
// currently include all the lifetime
485-
// parameters declared on the enclosing
486-
// function, even if they are unused by
487-
// the closure itself. We can't readily
488-
// filter them out, so here we replace
489-
// those values with `'empty`. This can't
490-
// really make a difference to the rest of
491-
// the compiler; those regions are ignored
492-
// for the outlives relation, and hence
493-
// don't affect trait selection or auto
494-
// traits, and they are erased during
495-
// trans.
496-
gcx.types.re_empty
497-
},
498-
}
499-
});
500-
458+
let definition_ty =
459+
instantiated_ty.fold_with(&mut ReverseMapper::new(
460+
self.tcx,
461+
self.is_tainted_by_errors(),
462+
def_id,
463+
map,
464+
instantiated_ty,
465+
));
501466
debug!(
502467
"infer_anon_definition_from_instantiation: definition_ty={:?}",
503468
definition_ty
504469
);
505470

471+
// We can unwrap here because our reverse mapper always
472+
// produces things with 'gcx lifetime, though the type folder
473+
// obscures that.
474+
let definition_ty = gcx.lift(&definition_ty).unwrap();
475+
506476
definition_ty
507477
}
508478
}
509479

480+
struct ReverseMapper<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
481+
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
482+
483+
/// If errors have already been reported in this fn, we suppress
484+
/// our own errors because they are sometimes derivative.
485+
tainted_by_errors: bool,
486+
487+
anon_type_def_id: DefId,
488+
map: FxHashMap<Kind<'tcx>, Kind<'gcx>>,
489+
map_missing_regions_to_empty: bool,
490+
491+
/// initially `Some`, set to `None` once error has been reported
492+
hidden_ty: Option<Ty<'tcx>>,
493+
}
494+
495+
impl<'cx, 'gcx, 'tcx> ReverseMapper<'cx, 'gcx, 'tcx> {
496+
fn new(
497+
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
498+
tainted_by_errors: bool,
499+
anon_type_def_id: DefId,
500+
map: FxHashMap<Kind<'tcx>, Kind<'gcx>>,
501+
hidden_ty: Ty<'tcx>,
502+
) -> Self {
503+
Self {
504+
tcx,
505+
tainted_by_errors,
506+
anon_type_def_id,
507+
map,
508+
map_missing_regions_to_empty: false,
509+
hidden_ty: Some(hidden_ty),
510+
}
511+
}
512+
513+
fn fold_kind_mapping_missing_regions_to_empty(&mut self, kind: Kind<'tcx>) -> Kind<'tcx> {
514+
assert!(!self.map_missing_regions_to_empty);
515+
self.map_missing_regions_to_empty = true;
516+
let kind = kind.fold_with(self);
517+
self.map_missing_regions_to_empty = false;
518+
kind
519+
}
520+
521+
fn fold_kind_normally(&mut self, kind: Kind<'tcx>) -> Kind<'tcx> {
522+
assert!(!self.map_missing_regions_to_empty);
523+
kind.fold_with(self)
524+
}
525+
}
526+
527+
impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for ReverseMapper<'cx, 'gcx, 'tcx> {
528+
fn tcx(&self) -> TyCtxt<'_, 'gcx, 'tcx> {
529+
self.tcx
530+
}
531+
532+
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
533+
match r {
534+
// ignore bound regions that appear in the type (e.g., this
535+
// would ignore `'r` in a type like `for<'r> fn(&'r u32)`.
536+
ty::ReLateBound(..) => return r,
537+
538+
// ignore `'static`, as that can appear anywhere
539+
ty::ReStatic => return r,
540+
541+
_ => { }
542+
}
543+
544+
match self.map.get(&r.into()).map(|k| k.unpack()) {
545+
Some(UnpackedKind::Lifetime(r1)) => r1,
546+
Some(u) => panic!("region mapped to unexpected kind: {:?}", u),
547+
None => {
548+
if !self.map_missing_regions_to_empty && !self.tainted_by_errors {
549+
if let Some(hidden_ty) = self.hidden_ty.take() {
550+
let span = self.tcx.def_span(self.anon_type_def_id);
551+
let mut err = struct_span_err!(
552+
self.tcx.sess,
553+
span,
554+
E0909,
555+
"hidden type for `impl Trait` captures lifetime that \
556+
does not appear in bounds",
557+
);
558+
559+
// Assuming regionck succeeded, then we must
560+
// be capturing *some* region from the fn
561+
// header, and hence it must be free, so it's
562+
// ok to invoke this fn (which doesn't accept
563+
// all regions, and would ICE if an
564+
// inappropriate region is given). We check
565+
// `is_tainted_by_errors` by errors above, so
566+
// we don't get in here unless regionck
567+
// succeeded. (Note also that if regionck
568+
// failed, then the regions we are attempting
569+
// to map here may well be giving errors
570+
// *because* the constraints were not
571+
// satisfiable.)
572+
self.tcx.note_and_explain_free_region(
573+
&mut err,
574+
&format!("hidden type `{}` captures ", hidden_ty),
575+
r,
576+
""
577+
);
578+
579+
err.emit();
580+
}
581+
}
582+
self.tcx.types.re_empty
583+
},
584+
}
585+
}
586+
587+
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
588+
match ty.sty {
589+
ty::TyClosure(def_id, substs) => {
590+
// I am a horrible monster and I pray for death. When
591+
// we encounter a closure here, it is always a closure
592+
// from within the function that we are currently
593+
// type-checking -- one that is now being encapsulated
594+
// in an existential abstract type. Ideally, we would
595+
// go through the types/lifetimes that it references
596+
// and treat them just like we would any other type,
597+
// which means we would error out if we find any
598+
// reference to a type/region that is not in the
599+
// "reverse map".
600+
//
601+
// **However,** in the case of closures, there is a
602+
// somewhat subtle (read: hacky) consideration. The
603+
// problem is that our closure types currently include
604+
// all the lifetime parameters declared on the
605+
// enclosing function, even if they are unused by the
606+
// closure itself. We can't readily filter them out,
607+
// so here we replace those values with `'empty`. This
608+
// can't really make a difference to the rest of the
609+
// compiler; those regions are ignored for the
610+
// outlives relation, and hence don't affect trait
611+
// selection or auto traits, and they are erased
612+
// during trans.
613+
614+
let generics = self.tcx.generics_of(def_id);
615+
let parent_len = generics.parent_count();
616+
let substs = self.tcx.mk_substs(substs.substs.iter().enumerate().map(
617+
|(index, &kind)| {
618+
if index < parent_len {
619+
// Accommodate missing regions in the parent kinds...
620+
self.fold_kind_mapping_missing_regions_to_empty(kind)
621+
} else {
622+
// ...but not elsewhere.
623+
self.fold_kind_normally(kind)
624+
}
625+
},
626+
));
627+
628+
self.tcx.mk_closure(def_id, ty::ClosureSubsts { substs })
629+
}
630+
631+
_ => ty.super_fold_with(self),
632+
}
633+
}
634+
}
635+
510636
struct Instantiator<'a, 'gcx: 'tcx, 'tcx: 'a> {
511637
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
512638
parent_def_id: DefId,
@@ -565,12 +691,13 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
565691
return self.fold_anon_ty(ty, def_id, substs);
566692
}
567693

568-
debug!("instantiate_anon_types_in_map: \
569-
encountered anon with wrong parent \
570-
def_id={:?} \
571-
anon_parent_def_id={:?}",
572-
def_id,
573-
anon_parent_def_id);
694+
debug!(
695+
"instantiate_anon_types_in_map: \
696+
encountered anon with wrong parent \
697+
def_id={:?} \
698+
anon_parent_def_id={:?}",
699+
def_id, anon_parent_def_id
700+
);
574701
}
575702
}
576703

@@ -590,8 +717,7 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
590717

591718
debug!(
592719
"instantiate_anon_types: TyAnon(def_id={:?}, substs={:?})",
593-
def_id,
594-
substs
720+
def_id, substs
595721
);
596722

597723
// Use the same type variable if the exact same TyAnon appears more
@@ -600,8 +726,10 @@ impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
600726
return anon_defn.concrete_ty;
601727
}
602728
let span = tcx.def_span(def_id);
603-
let ty_var = infcx.next_ty_var(ty::UniverseIndex::ROOT,
604-
TypeVariableOrigin::TypeInference(span));
729+
let ty_var = infcx.next_ty_var(
730+
ty::UniverseIndex::ROOT,
731+
TypeVariableOrigin::TypeInference(span),
732+
);
605733

606734
let predicates_of = tcx.predicates_of(def_id);
607735
let bounds = predicates_of.instantiate(tcx, substs);

src/librustc/infer/canonical.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use traits::{Obligation, ObligationCause, PredicateObligation};
4040
use ty::{self, CanonicalVar, Lift, Region, Slice, Ty, TyCtxt, TypeFlags};
4141
use ty::subst::{Kind, UnpackedKind};
4242
use ty::fold::{TypeFoldable, TypeFolder};
43+
use util::captures::Captures;
4344
use util::common::CellUsizeExt;
4445

4546
use rustc_data_structures::indexed_vec::IndexVec;
@@ -382,7 +383,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
382383
param_env: ty::ParamEnv<'tcx>,
383384
unsubstituted_region_constraints: &'a QueryRegionConstraints<'tcx>,
384385
result_subst: &'a CanonicalVarValues<'tcx>,
385-
) -> impl Iterator<Item = PredicateObligation<'tcx>> + 'a {
386+
) -> impl Iterator<Item = PredicateObligation<'tcx>> + Captures<'gcx> + 'a {
386387
let QueryRegionConstraints {
387388
region_outlives,
388389
ty_outlives,

src/librustc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ pub mod traits;
157157
pub mod ty;
158158

159159
pub mod util {
160+
pub mod captures;
160161
pub mod common;
161162
pub mod ppaux;
162163
pub mod nodemap;

0 commit comments

Comments
 (0)