Skip to content

Commit 3be9417

Browse files
author
Alexander Regueiro
committed
Allow opaque types to hide lifetimes in concrete type so long as they outlive concrete type.
1 parent 697739d commit 3be9417

File tree

5 files changed

+115
-190
lines changed

5 files changed

+115
-190
lines changed

src/librustc/infer/opaque_types/mod.rs

+93-188
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::hir;
33
use crate::hir::Node;
44
use crate::infer::{self, InferCtxt, InferOk, TypeVariableOrigin};
55
use crate::infer::outlives::free_region_map::FreeRegionRelations;
6-
use rustc_data_structures::fx::FxHashMap;
6+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
77
use crate::traits::{self, PredicateObligation};
88
use crate::ty::{self, Ty, TyCtxt, GenericParamDefKind};
99
use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder};
@@ -276,84 +276,87 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
276276
opaque_defn: &OpaqueTypeDecl<'tcx>,
277277
free_region_relations: &FRR,
278278
) {
279-
debug!("constrain_opaque_type()");
280-
debug!("constrain_opaque_type: def_id={:?}", def_id);
281-
debug!("constrain_opaque_type: opaque_defn={:#?}", opaque_defn);
279+
debug!("constrain_opaque_type(def_id={:?}, opaque_defn={:#?})", def_id, opaque_defn);
282280

283281
let concrete_ty = self.resolve_type_vars_if_possible(&opaque_defn.concrete_ty);
284282

285283
debug!("constrain_opaque_type: concrete_ty={:?}", concrete_ty);
286284

287-
let abstract_type_generics = self.tcx.generics_of(def_id);
285+
let opaque_type_generics = self.tcx.generics_of(def_id);
288286

289287
let span = self.tcx.def_span(def_id);
290288

291289
// If there are required region bounds, we can just skip
292-
// ahead. There will already be a registered region
293-
// obligation related `concrete_ty` to those regions.
294-
if opaque_defn.has_required_region_bounds {
295-
return;
296-
}
297-
298-
// There were no `required_region_bounds`,
299-
// so we have to search for a `least_region`.
300-
// Go through all the regions used as arguments to the
301-
// abstract type. These are the parameters to the abstract
302-
// type; so in our example above, `substs` would contain
303-
// `['a]` for the first impl trait and `'b` for the
304-
// second.
305-
let mut least_region = None;
306-
for param in &abstract_type_generics.params {
307-
match param.kind {
308-
GenericParamDefKind::Lifetime => {}
309-
_ => continue
310-
}
311-
// Get the value supplied for this region from the substs.
312-
let subst_arg = opaque_defn.substs.region_at(param.index as usize);
313-
314-
// Compute the least upper bound of it with the other regions.
315-
debug!("constrain_opaque_types: least_region={:?}", least_region);
316-
debug!("constrain_opaque_types: subst_arg={:?}", subst_arg);
317-
match least_region {
318-
None => least_region = Some(subst_arg),
319-
Some(lr) => {
320-
if free_region_relations.sub_free_regions(lr, subst_arg) {
321-
// keep the current least region
322-
} else if free_region_relations.sub_free_regions(subst_arg, lr) {
323-
// switch to `subst_arg`
324-
least_region = Some(subst_arg);
325-
} else {
326-
// There are two regions (`lr` and
327-
// `subst_arg`) which are not relatable. We can't
328-
// find a best choice.
329-
self.tcx
330-
.sess
331-
.struct_span_err(span, "ambiguous lifetime bound in `impl Trait`")
332-
.span_label(
333-
span,
334-
format!("neither `{}` nor `{}` outlives the other", lr, subst_arg),
335-
)
336-
.emit();
337-
338-
least_region = Some(self.tcx.mk_region(ty::ReEmpty));
339-
break;
290+
// ahead. There will already be a registered region
291+
// obligation relating `concrete_ty` to those regions.
292+
let least_region = if opaque_defn.has_required_region_bounds {
293+
None
294+
} else {
295+
// There were no `required_region_bounds`,
296+
// so we have to search for a `least_region`.
297+
// Go through all the regions used as arguments to the
298+
// existential type. These are the parameters to the existential
299+
// type; so in our example above, `substs` would contain
300+
// `['a]` for the first impl trait and `'b` for the
301+
// second.
302+
let mut least_region = None;
303+
for param in &opaque_type_generics.params {
304+
match param.kind {
305+
GenericParamDefKind::Lifetime => {}
306+
_ => continue
307+
}
308+
// Get the value supplied for this region from the substs.
309+
let subst_arg = opaque_defn.substs.region_at(param.index as usize);
310+
311+
// Compute the least upper bound of it with the other regions.
312+
debug!("constrain_opaque_type: least_region={:?}", least_region);
313+
debug!("constrain_opaque_type: subst_arg={:?}", subst_arg);
314+
match least_region {
315+
None => least_region = Some(subst_arg),
316+
Some(lr) => {
317+
if free_region_relations.sub_free_regions(lr, subst_arg) {
318+
// Keep the current least region.
319+
} else if free_region_relations.sub_free_regions(subst_arg, lr) {
320+
// Switch to `subst_arg`.
321+
least_region = Some(subst_arg);
322+
} else {
323+
// There are two regions (`lr` and `subst_arg`) that are not relatable.
324+
// We can't find a best choice.
325+
self.tcx
326+
.sess
327+
.struct_span_err(span, "ambiguous lifetime bound in `impl Trait`")
328+
.span_label(
329+
span,
330+
format!("neither `{}` nor `{}` outlives the other",
331+
lr, subst_arg),
332+
)
333+
.emit();
334+
335+
least_region = Some(self.tcx.mk_region(ty::ReEmpty));
336+
break;
337+
}
340338
}
341339
}
342340
}
343-
}
344-
345-
let least_region = least_region.unwrap_or(self.tcx.types.re_static);
346-
debug!("constrain_opaque_types: least_region={:?}", least_region);
341+
least_region
342+
}.unwrap_or(self.tcx.types.re_static);
343+
debug!("constrain_opaque_type: least_region={:?}", least_region);
347344

348345
// Require that the type `concrete_ty` outlives
349346
// `least_region`, modulo any type parameters that appear
350-
// in the type, which we ignore. This is because impl
351-
// trait values are assumed to capture all the in-scope
347+
// in the type, which we ignore. This is because `impl
348+
// Trait` values are assumed to capture all the in-scope
352349
// type parameters. This little loop here just invokes
353350
// `outlives` repeatedly, draining all the nested
354351
// obligations that result.
352+
let mut concrete_ty_regions = FxHashSet::default();
355353
let mut types = vec![concrete_ty];
356-
let bound_region = |r| self.sub_regions(infer::CallReturn(span), least_region, r);
354+
let mut bound_region = |r| {
355+
concrete_ty_regions.insert(r);
356+
if !opaque_defn.has_required_region_bounds {
357+
self.sub_regions(infer::CallReturn(span), least_region, r)
358+
}
359+
};
357360
while let Some(ty) = types.pop() {
358361
let mut components = smallvec![];
359362
self.tcx.push_outlives_components(ty, &mut components);
@@ -485,49 +488,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
485488

486489
struct ReverseMapper<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
487490
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
488-
489-
/// If errors have already been reported in this fn, we suppress
490-
/// our own errors because they are sometimes derivative.
491-
tainted_by_errors: bool,
492-
493-
opaque_type_def_id: DefId,
494491
map: FxHashMap<Kind<'tcx>, Kind<'gcx>>,
495-
map_missing_regions_to_empty: bool,
496-
497-
/// initially `Some`, set to `None` once error has been reported
498-
hidden_ty: Option<Ty<'tcx>>,
499-
}
500-
501-
impl<'cx, 'gcx, 'tcx> ReverseMapper<'cx, 'gcx, 'tcx> {
502-
fn new(
503-
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
504-
tainted_by_errors: bool,
505-
opaque_type_def_id: DefId,
506-
map: FxHashMap<Kind<'tcx>, Kind<'gcx>>,
507-
hidden_ty: Ty<'tcx>,
508-
) -> Self {
509-
Self {
510-
tcx,
511-
tainted_by_errors,
512-
opaque_type_def_id,
513-
map,
514-
map_missing_regions_to_empty: false,
515-
hidden_ty: Some(hidden_ty),
516-
}
517-
}
518-
519-
fn fold_kind_mapping_missing_regions_to_empty(&mut self, kind: Kind<'tcx>) -> Kind<'tcx> {
520-
assert!(!self.map_missing_regions_to_empty);
521-
self.map_missing_regions_to_empty = true;
522-
let kind = kind.fold_with(self);
523-
self.map_missing_regions_to_empty = false;
524-
kind
525-
}
526-
527-
fn fold_kind_normally(&mut self, kind: Kind<'tcx>) -> Kind<'tcx> {
528-
assert!(!self.map_missing_regions_to_empty);
529-
kind.fold_with(self)
530-
}
531492
}
532493

533494
impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for ReverseMapper<'cx, 'gcx, 'tcx> {
@@ -537,109 +498,53 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for ReverseMapper<'cx, 'gcx, 'tcx>
537498

538499
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
539500
match r {
540-
// ignore bound regions that appear in the type (e.g., this
501+
// Ignore bound regions that appear in the type (e.g., this
541502
// would ignore `'r` in a type like `for<'r> fn(&'r u32)`.
542503
ty::ReLateBound(..) |
543504

544-
// ignore `'static`, as that can appear anywhere
505+
// Ignore `'static`, as that can appear anywhere.
545506
ty::ReStatic |
546507

547-
// ignore `ReScope`, as that can appear anywhere
548-
// See `src/test/run-pass/issue-49556.rs` for example.
508+
// Ignore `ReScope`, as that can appear anywhere.
509+
// See `src/test/run-pass/issue-49556.rs`, for example.
549510
ty::ReScope(..) => return r,
550511

551-
_ => { }
512+
_ => {}
552513
}
553514

554515
match self.map.get(&r.into()).map(|k| k.unpack()) {
555516
Some(UnpackedKind::Lifetime(r1)) => r1,
556517
Some(u) => panic!("region mapped to unexpected kind: {:?}", u),
557518
None => {
558-
if !self.map_missing_regions_to_empty && !self.tainted_by_errors {
559-
if let Some(hidden_ty) = self.hidden_ty.take() {
560-
let span = self.tcx.def_span(self.opaque_type_def_id);
561-
let mut err = struct_span_err!(
562-
self.tcx.sess,
563-
span,
564-
E0700,
565-
"hidden type for `impl Trait` captures lifetime that \
566-
does not appear in bounds",
567-
);
568-
569-
// Assuming regionck succeeded, then we must
570-
// be capturing *some* region from the fn
571-
// header, and hence it must be free, so it's
572-
// ok to invoke this fn (which doesn't accept
573-
// all regions, and would ICE if an
574-
// inappropriate region is given). We check
575-
// `is_tainted_by_errors` by errors above, so
576-
// we don't get in here unless regionck
577-
// succeeded. (Note also that if regionck
578-
// failed, then the regions we are attempting
579-
// to map here may well be giving errors
580-
// *because* the constraints were not
581-
// satisfiable.)
582-
self.tcx.note_and_explain_free_region(
583-
&mut err,
584-
&format!("hidden type `{}` captures ", hidden_ty),
585-
r,
586-
""
587-
);
588-
589-
err.emit();
590-
}
591-
}
519+
// No mapping was found. This means that it is either a
520+
// disallowed lifetime, which will be caught by regionck,
521+
// a region in a non-upvar closure generic, which is
522+
// explicitly allowed (see below for more on this),
523+
// or a lifetime that does not explicitly appear in
524+
// `impl Trait` bounds but which outlives the lifetimes
525+
// or types which *do* appear in the `impl Trait` bounds.
526+
//
527+
// These lifetimes are explicitly allowed to prevent
528+
// the user from having to add dummy references to the
529+
// unnamed lifetimes in their `impl Trait` bounds
530+
// (e.g., `+ DummyTraitWithALifetimeArg<'a>`). Adding
531+
// the lifetimes via `+` doesn't work because the type
532+
// doesn't outlive those lifetimes, it just contains them.
533+
//
534+
// On closures: there is a somewhat subtle (read: hacky)
535+
// consideration. The problem is that our closure types
536+
// currently include all the lifetime parameters declared
537+
// on the enclosing function, even if they are unused by
538+
// the closure itself. We can't readily filter them out,
539+
// so we replace them with `empty`. This can't really make
540+
// a diference to the rest of hte compiler; those regions
541+
// are ignored for the outlives relation, and hence don't
542+
// affect trait selection or auto traits, and they are
543+
// erased during trans.
592544
self.tcx.types.re_empty
593545
},
594546
}
595547
}
596-
597-
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
598-
match ty.sty {
599-
ty::Closure(def_id, substs) => {
600-
// I am a horrible monster and I pray for death. When
601-
// we encounter a closure here, it is always a closure
602-
// from within the function that we are currently
603-
// type-checking -- one that is now being encapsulated
604-
// in an existential abstract type. Ideally, we would
605-
// go through the types/lifetimes that it references
606-
// and treat them just like we would any other type,
607-
// which means we would error out if we find any
608-
// reference to a type/region that is not in the
609-
// "reverse map".
610-
//
611-
// **However,** in the case of closures, there is a
612-
// somewhat subtle (read: hacky) consideration. The
613-
// problem is that our closure types currently include
614-
// all the lifetime parameters declared on the
615-
// enclosing function, even if they are unused by the
616-
// closure itself. We can't readily filter them out,
617-
// so here we replace those values with `'empty`. This
618-
// can't really make a difference to the rest of the
619-
// compiler; those regions are ignored for the
620-
// outlives relation, and hence don't affect trait
621-
// selection or auto traits, and they are erased
622-
// during codegen.
623-
624-
let generics = self.tcx.generics_of(def_id);
625-
let substs = self.tcx.mk_substs(substs.substs.iter().enumerate().map(
626-
|(index, &kind)| {
627-
if index < generics.parent_count {
628-
// Accommodate missing regions in the parent kinds...
629-
self.fold_kind_mapping_missing_regions_to_empty(kind)
630-
} else {
631-
// ...but not elsewhere.
632-
self.fold_kind_normally(kind)
633-
}
634-
},
635-
));
636-
637-
self.tcx.mk_closure(def_id, ty::ClosureSubsts { substs })
638-
}
639-
640-
_ => ty.super_fold_with(self),
641-
}
642-
}
643548
}
644549

645550
struct Instantiator<'a, 'gcx: 'tcx, 'tcx: 'a> {

src/librustc/infer/outlives/free_region_map.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::ty::{self, Lift, TyCtxt, Region};
2+
use rustc_data_structures::fx::FxHashSet;
23
use rustc_data_structures::transitive_relation::TransitiveRelation;
34

45
#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Default)]
@@ -51,12 +52,18 @@ impl<'tcx> FreeRegionMap<'tcx> {
5152
/// slightly different way; this trait allows functions to be abstract
5253
/// over which version is in use.
5354
pub trait FreeRegionRelations<'tcx> {
54-
/// Tests whether `r_a <= r_b`. Both must be free regions or
55-
/// `'static`.
55+
/// Gets all the free regions in the set of relations.
56+
fn all_regions(&self) -> FxHashSet<ty::Region<'tcx>>;
57+
58+
/// Tests whether `r_a <= r_b`. Both must be free regions or `'static`.
5659
fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool;
5760
}
5861

5962
impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> {
63+
fn all_regions(&self) -> FxHashSet<ty::Region<'tcx>> {
64+
self.relation.elements().map(|r| *r).collect()
65+
}
66+
6067
fn sub_free_regions(&self,
6168
r_a: Region<'tcx>,
6269
r_b: Region<'tcx>)

src/librustc_data_structures/transitive_relation.rs

+4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ struct Edge {
5353
}
5454

5555
impl<T: Clone + Debug + Eq + Hash> TransitiveRelation<T> {
56+
pub fn elements(&self) -> impl Iterator<Item = &T> {
57+
self.elements.iter()
58+
}
59+
5660
pub fn is_empty(&self) -> bool {
5761
self.edges.is_empty()
5862
}

0 commit comments

Comments
 (0)