From bc4810d907deef00ea5bd0124272cfcab3975e3c Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Sun, 15 Oct 2017 13:43:06 -0700 Subject: [PATCH] Fix impl Trait Lifetime Handling After this change, impl Trait existentials are desugared to a new `abstract type` definition paired with a set of lifetimes to apply. In-scope generics are included as parents of the `abstract type` generics. Parent regions are replaced with static, and parent regions referenced in the `impl Trait` type are duplicated at the end of the `abstract type`'s generics. --- src/librustc/diagnostics.rs | 1 + src/librustc/hir/intravisit.rs | 5 +- src/librustc/hir/lowering.rs | 131 +++++++++++- src/librustc/hir/mod.rs | 17 +- src/librustc/hir/print.rs | 6 +- src/librustc/ich/impls_hir.rs | 7 +- src/librustc/middle/free_region.rs | 26 ++- src/librustc/middle/resolve_lifetime.rs | 112 +++++++++- src/librustc/ty/subst.rs | 10 +- src/librustc/util/ppaux.rs | 4 + src/librustc_metadata/encoder.rs | 2 +- src/librustc_typeck/astconv.rs | 43 +++- src/librustc_typeck/check/mod.rs | 73 ++++++- src/librustc_typeck/check/regionck.rs | 194 +++++++++++++++++- src/librustc_typeck/check/writeback.rs | 72 +++++-- src/librustc_typeck/collect.rs | 23 ++- src/librustdoc/clean/mod.rs | 5 +- src/test/compile-fail/E0657.rs | 36 ++++ src/test/compile-fail/impl-trait/lifetimes.rs | 43 ---- .../must_outlive_least_region_or_bound.rs | 39 ++++ .../impl-trait/needs_least_region_or_bound.rs | 23 +++ .../impl-trait/type_parameters_captured.rs | 24 +++ src/test/run-pass/impl-trait/lifetimes.rs | 97 +++++++++ src/test/ui/span/loan-extend.rs | 24 --- 24 files changed, 884 insertions(+), 133 deletions(-) create mode 100644 src/test/compile-fail/E0657.rs delete mode 100644 src/test/compile-fail/impl-trait/lifetimes.rs create mode 100644 src/test/compile-fail/impl-trait/must_outlive_least_region_or_bound.rs create mode 100644 src/test/compile-fail/impl-trait/needs_least_region_or_bound.rs create mode 100644 src/test/compile-fail/impl-trait/type_parameters_captured.rs create mode 100644 src/test/run-pass/impl-trait/lifetimes.rs delete mode 100644 src/test/ui/span/loan-extend.rs diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index bab3bded77b..75ef3fc0c3d 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -2019,4 +2019,5 @@ fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 { E0628, // generators cannot have explicit arguments E0631, // type mismatch in closure arguments E0637, // "'_" is not a valid lifetime bound + E0657, // `impl Trait` can only capture lifetimes bound at the fn level } diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 84be68cb197..c9d35158ed0 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -591,8 +591,11 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) { } visitor.visit_lifetime(lifetime); } - TyImplTraitExistential(ref bounds) => { + TyImplTraitExistential(ref existty, ref lifetimes) => { + let ExistTy { ref generics, ref bounds } = *existty; + walk_generics(visitor, generics); walk_list!(visitor, visit_ty_param_bound, bounds); + walk_list!(visitor, visit_lifetime, lifetimes); } TyImplTraitUniversal(_, ref bounds) => { walk_list!(visitor, visit_ty_param_bound, bounds); diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 8c9d1a38e70..3e527f43fec 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -42,8 +42,9 @@ use dep_graph::DepGraph; use hir; -use hir::map::{Definitions, DefKey}; -use hir::def_id::{DefIndex, DefId, CRATE_DEF_INDEX}; +use hir::HirVec; +use hir::map::{Definitions, DefKey, DefPathData}; +use hir::def_id::{DefIndex, DefId, CRATE_DEF_INDEX, DefIndexAddressSpace}; use hir::def::{Def, PathResolution}; use lint::builtin::PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES; use middle::cstore::CrateStore; @@ -52,7 +53,7 @@ use util::common::FN_OUTPUT_NAME; use util::nodemap::{DefIdMap, FxHashMap, NodeMap}; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashSet}; use std::fmt::Debug; use std::iter; use std::mem; @@ -777,7 +778,24 @@ fn lower_ty(&mut self, t: &Ty, itctx: ImplTraitContext) -> P { t.span, GateIssue::Language, "`impl Trait` in return position is experimental"); } - hir::TyImplTraitExistential(self.lower_bounds(bounds, itctx)) + let def_index = self.resolver.definitions().opt_def_index(t.id).unwrap(); + let hir_bounds = self.lower_bounds(bounds, itctx); + let (lifetimes, lifetime_defs) = + self.lifetimes_from_impl_trait_bounds(def_index, &hir_bounds); + + hir::TyImplTraitExistential(hir::ExistTy { + generics: hir::Generics { + lifetimes: lifetime_defs, + // Type parameters are taken from environment: + ty_params: Vec::new().into(), + where_clause: hir::WhereClause { + id: self.next_id().node_id, + predicates: Vec::new().into(), + }, + span: t.span, + }, + bounds: hir_bounds, + }, lifetimes) }, ImplTraitContext::Universal(def_id) => { let has_feature = self.sess.features.borrow().universal_impl_trait; @@ -808,6 +826,111 @@ fn lower_ty(&mut self, t: &Ty, itctx: ImplTraitContext) -> P { }) } + fn lifetimes_from_impl_trait_bounds( + &mut self, + parent_index: DefIndex, + bounds: &hir::TyParamBounds + ) -> (HirVec, HirVec) { + + // This visitor walks over impl trait bounds and creates defs for all lifetimes which + // appear in the bounds, excluding lifetimes that are created within the bounds. + // e.g. 'a, 'b, but not 'c in `impl for<'c> SomeTrait<'a, 'b, 'c>` + struct ImplTraitLifetimeCollector<'r, 'a: 'r> { + context: &'r mut LoweringContext<'a>, + parent: DefIndex, + currently_bound_lifetimes: Vec, + already_defined_lifetimes: HashSet, + output_lifetimes: Vec, + output_lifetime_defs: Vec, + } + + impl<'r, 'a: 'r, 'v> hir::intravisit::Visitor<'v> for ImplTraitLifetimeCollector<'r, 'a> { + fn nested_visit_map<'this>(&'this mut self) + -> hir::intravisit::NestedVisitorMap<'this, 'v> { + hir::intravisit::NestedVisitorMap::None + } + + fn visit_poly_trait_ref(&mut self, + polytr: &'v hir::PolyTraitRef, + _: hir::TraitBoundModifier) { + let old_len = self.currently_bound_lifetimes.len(); + + // Record the introduction of 'a in `for<'a> ...` + for lt_def in &polytr.bound_lifetimes { + // Introduce lifetimes one at a time so that we can handle + // cases like `fn foo<'d>() -> impl for<'a, 'b: 'a, 'c: 'b + 'd> ...` + if let hir::LifetimeName::Name(name) = lt_def.lifetime.name { + self.currently_bound_lifetimes.push(name); + } + + // Visit the lifetime bounds + for lt_bound in <_def.bounds { + self.visit_lifetime(<_bound); + } + } + + hir::intravisit::walk_trait_ref(self, &polytr.trait_ref); + + self.currently_bound_lifetimes.truncate(old_len); + } + + fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) { + // Exclude '_, 'static, and elided lifetimes (there should be no elided lifetimes) + if let hir::LifetimeName::Name(lifetime_name) = lifetime.name { + if !self.currently_bound_lifetimes.contains(&lifetime_name) && + !self.already_defined_lifetimes.contains(&lifetime_name) + { + self.already_defined_lifetimes.insert(lifetime_name); + let name = hir::LifetimeName::Name(lifetime_name); + + self.output_lifetimes.push(hir::Lifetime { + id: self.context.next_id().node_id, + span: lifetime.span, + name, + }); + + let def_node_id = self.context.next_id().node_id; + self.context.resolver.definitions().create_def_with_parent( + self.parent, + def_node_id, + DefPathData::LifetimeDef(lifetime_name.as_str()), + DefIndexAddressSpace::High, + Mark::root() + ); + let def_lifetime = hir::Lifetime { + id: def_node_id, + span: lifetime.span, + name, + }; + self.output_lifetime_defs.push(hir::LifetimeDef { + lifetime: def_lifetime, + bounds: Vec::new().into(), + pure_wrt_drop: false, + }); + } + } + } + } + + let mut lifetime_collector = ImplTraitLifetimeCollector { + context: self, + parent: parent_index, + currently_bound_lifetimes: Vec::new(), + already_defined_lifetimes: HashSet::new(), + output_lifetimes: Vec::new(), + output_lifetime_defs: Vec::new(), + }; + + for bound in bounds { + hir::intravisit::walk_ty_param_bound(&mut lifetime_collector, &bound); + } + + ( + lifetime_collector.output_lifetimes.into(), + lifetime_collector.output_lifetime_defs.into() + ) + } + fn lower_foreign_mod(&mut self, fm: &ForeignMod) -> hir::ForeignMod { hir::ForeignMod { abi: fm.abi, diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index ee83000c440..563fba52aef 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1436,6 +1436,12 @@ pub struct BareFnTy { pub arg_names: HirVec>, } +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub struct ExistTy { + pub generics: Generics, + pub bounds: TyParamBounds, +} + #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] /// The different kinds of types recognized by the compiler pub enum Ty_ { @@ -1463,7 +1469,16 @@ pub enum Ty_ { TyTraitObject(HirVec, Lifetime), /// An exsitentially quantified (there exists a type satisfying) `impl /// Bound1 + Bound2 + Bound3` type where `Bound` is a trait or a lifetime. - TyImplTraitExistential(TyParamBounds), + /// + /// The `ExistTy` structure emulates an + /// `abstract type Foo<'a, 'b>: MyTrait<'a, 'b>;`. + /// + /// The `HirVec` is the list of lifetimes applied as parameters + /// to the `abstract type`, e.g. the `'c` and `'d` in `-> Foo<'c, 'd>`. + /// This list is only a list of lifetimes and not type parameters + /// because all in-scope type parameters are captured by `impl Trait`, + /// so they are resolved directly through the parent `Generics`. + TyImplTraitExistential(ExistTy, HirVec), /// An universally quantified (for all types satisfying) `impl /// Bound1 + Bound2 + Bound3` type where `Bound` is a trait or a lifetime. TyImplTraitUniversal(DefId, TyParamBounds), diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 451e870f500..d94dd24af3e 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -421,8 +421,10 @@ pub fn print_type(&mut self, ty: &hir::Ty) -> io::Result<()> { self.print_lifetime(lifetime)?; } } - hir::TyImplTraitExistential(ref bounds) | - hir::TyImplTraitUniversal(_, ref bounds) => { + hir::TyImplTraitExistential(ref existty, ref _lifetimes) => { + self.print_bounds("impl", &existty.bounds[..])?; + } + hir::TyImplTraitUniversal(_, ref bounds) => { self.print_bounds("impl", &bounds[..])?; } hir::TyArray(ref ty, v) => { diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index a04683e1b22..8d4979dc8c2 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -277,6 +277,11 @@ fn hash_stable(&self, arg_names }); +impl_stable_hash_for!(struct hir::ExistTy { + generics, + bounds +}); + impl_stable_hash_for!(enum hir::Ty_ { TySlice(t), TyArray(t, body_id), @@ -287,7 +292,7 @@ fn hash_stable(&self, TyTup(ts), TyPath(qpath), TyTraitObject(trait_refs, lifetime), - TyImplTraitExistential(bounds), + TyImplTraitExistential(existty, lifetimes), TyImplTraitUniversal(def_id, bounds), TyTypeof(body_id), TyErr, diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs index da505f16724..89c3f166847 100644 --- a/src/librustc/middle/free_region.rs +++ b/src/librustc/middle/free_region.rs @@ -84,7 +84,7 @@ pub fn is_subregion_of(&self, (&ty::ReFree(_), &ty::ReEarlyBound(_)) | (&ty::ReEarlyBound(_), &ty::ReFree(_)) | (&ty::ReFree(_), &ty::ReFree(_)) => - self.free_regions.relation.contains(&sub_region, &super_region), + self.free_regions.sub_free_regions(&sub_region, &super_region), _ => false, @@ -158,19 +158,39 @@ pub fn relate_free_regions_from_predicates(&mut self, } } - // Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`. - // (with the exception that `'static: 'x` is not notable) + /// Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`. + /// (with the exception that `'static: 'x` is not notable) pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { + debug!("relate_regions(sub={:?}, sup={:?})", sub, sup); if (is_free(sub) || *sub == ty::ReStatic) && is_free(sup) { self.relation.add(sub, sup) } } + /// True if `r_a <= r_b` is known to hold. Both `r_a` and `r_b` + /// must be free regions from the function header. + pub fn sub_free_regions<'a, 'gcx>(&self, + r_a: Region<'tcx>, + r_b: Region<'tcx>) + -> bool { + debug!("sub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b); + assert!(is_free(r_a)); + assert!(is_free(r_b)); + let result = r_a == r_b || self.relation.contains(&r_a, &r_b); + debug!("sub_free_regions: result={}", result); + result + } + + /// Compute the least-upper-bound of two free regions. In some + /// cases, this is more conservative than necessary, in order to + /// avoid making arbitrary choices. See + /// `TransitiveRelation::postdom_upper_bound` for more details. pub fn lub_free_regions<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, r_a: Region<'tcx>, r_b: Region<'tcx>) -> Region<'tcx> { + debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b); assert!(is_free(r_a)); assert!(is_free(r_b)); let result = if r_a == r_b { r_a } else { diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 8c299612064..7ed646bb9dd 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -52,6 +52,7 @@ fn early(hir_map: &Map, index: &mut u32, def: &hir::LifetimeDef) let i = *index; *index += 1; let def_id = hir_map.local_def_id(def.lifetime.id); + debug!("Region::early: index={} def_id={:?}", i, def_id); (def.lifetime.name, Region::EarlyBound(i, def_id)) } @@ -201,6 +202,11 @@ enum Scope<'a> { /// declaration `Binder` and the location it's referenced from. Binder { lifetimes: FxHashMap, + + /// if we extend this scope with another scope, what is the next index + /// we should use for an early-bound region? + next_early_index: u32, + s: ScopeRef<'a> }, @@ -343,8 +349,10 @@ fn visit_item(&mut self, item: &'tcx hir::Item) { let lifetimes = generics.lifetimes.iter().map(|def| { Region::early(self.hir_map, &mut index, def) }).collect(); + let next_early_index = index + generics.ty_params.len() as u32; let scope = Scope::Binder { lifetimes, + next_early_index, s: ROOT_SCOPE }; self.with(scope, |old_scope, this| { @@ -372,12 +380,15 @@ fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem) { } fn visit_ty(&mut self, ty: &'tcx hir::Ty) { + debug!("visit_ty: ty={:?}", ty); match ty.node { hir::TyBareFn(ref c) => { + let next_early_index = self.next_early_index(); let scope = Scope::Binder { lifetimes: c.lifetimes.iter().map(|def| { - Region::late(self.hir_map, def) - }).collect(), + Region::late(self.hir_map, def) + }).collect(), + next_early_index, s: self.scope }; self.with(scope, |old_scope, this| { @@ -405,6 +416,60 @@ fn visit_ty(&mut self, ty: &'tcx hir::Ty) { }; self.with(scope, |_, this| this.visit_ty(&mt.ty)); } + hir::TyImplTraitExistential(ref exist_ty, ref lifetimes) => { + // Resolve the lifetimes that are applied to the existential type. + // These are resolved in the current scope. + // `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to + // `fn foo<'a>() -> MyAnonTy<'a> { ... }` + // ^ ^this gets resolved in the current scope + for lifetime in lifetimes { + self.visit_lifetime(lifetime); + + // Check for predicates like `impl for<'a> SomeTrait>` + // and ban them. Type variables instantiated inside binders aren't + // well-supported at the moment, so this doesn't work. + // In the future, this should be fixed and this error should be removed. + let def = self.map.defs.get(&lifetime.id); + if let Some(&Region::LateBound(_, def_id)) = def { + if let Some(node_id) = self.hir_map.as_local_node_id(def_id) { + // Ensure that the parent of the def is an item, not HRTB + let parent_id = self.hir_map.get_parent_node(node_id); + let parent_impl_id = hir::ImplItemId { node_id: parent_id }; + let parent_trait_id = hir::TraitItemId { node_id: parent_id }; + let krate = self.hir_map.forest.krate(); + if !(krate.items.contains_key(&parent_id) || + krate.impl_items.contains_key(&parent_impl_id) || + krate.trait_items.contains_key(&parent_trait_id)) + { + span_err!(self.sess, lifetime.span, E0657, + "`impl Trait` can only capture lifetimes \ + bound at the fn or impl level"); + } + } + } + } + + // Resolve the lifetimes in the bounds to the lifetime defs in the generics. + // `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to + // `abstract type MyAnonTy<'b>: MyTrait<'b>;` + // ^ ^ this gets resolved in the scope of + // the exist_ty generics + let hir::ExistTy { ref generics, ref bounds } = *exist_ty; + let mut index = self.next_early_index(); + debug!("visit_ty: index = {}", index); + let lifetimes = generics.lifetimes.iter() + .map(|lt_def| Region::early(self.hir_map, &mut index, lt_def)) + .collect(); + + let next_early_index = index + generics.ty_params.len() as u32; + let scope = Scope::Binder { lifetimes, next_early_index, s: self.scope }; + self.with(scope, |_old_scope, this| { + this.visit_generics(generics); + for bound in bounds { + this.visit_ty_param_bound(bound); + } + }); + } _ => { intravisit::walk_ty(self, ty) } @@ -477,10 +542,12 @@ fn visit_generics(&mut self, generics: &'tcx hir::Generics) { .. }) => { if !bound_lifetimes.is_empty() { self.trait_ref_hack = true; + let next_early_index = self.next_early_index(); let scope = Scope::Binder { lifetimes: bound_lifetimes.iter().map(|def| { Region::late(self.hir_map, def) - }).collect(), + }).collect(), + next_early_index, s: self.scope }; let result = self.with(scope, |old_scope, this| { @@ -524,10 +591,12 @@ fn visit_poly_trait_ref(&mut self, span_err!(self.sess, trait_ref.span, E0316, "nested quantification of lifetimes"); } + let next_early_index = self.next_early_index(); let scope = Scope::Binder { lifetimes: trait_ref.bound_lifetimes.iter().map(|def| { Region::late(self.hir_map, def) - }).collect(), + }).collect(), + next_early_index, s: self.scope }; self.with(scope, |old_scope, this| { @@ -659,7 +728,7 @@ fn check_if_label_shadows_lifetime<'a>(sess: &'a Session, Scope::Root => { return; } - Scope::Binder { ref lifetimes, s } => { + Scope::Binder { ref lifetimes, s, next_early_index: _ } => { // FIXME (#24278): non-hygienic comparison if let Some(def) = lifetimes.get(&hir::LifetimeName::Name(label)) { let node_id = hir_map.as_local_node_id(def.id().unwrap()) @@ -860,8 +929,11 @@ fn visit_early_late(&mut self, } }).collect(); + let next_early_index = index + generics.ty_params.len() as u32; + let scope = Scope::Binder { lifetimes, + next_early_index, s: self.scope }; self.with(scope, move |old_scope, this| { @@ -870,7 +942,29 @@ fn visit_early_late(&mut self, }); } + /// Returns the next index one would use for an early-bound-region + /// if extending the current scope. + fn next_early_index(&self) -> u32 { + let mut scope = self.scope; + loop { + match *scope { + Scope::Root => + return 0, + + Scope::Binder { next_early_index, .. } => + return next_early_index, + + Scope::Body { s, .. } | + Scope::Elision { s, .. } | + Scope::ObjectLifetimeDefault { s, .. } => + scope = s, + } + } + } + fn resolve_lifetime_ref(&mut self, lifetime_ref: &hir::Lifetime) { + debug!("resolve_lifetime_ref(lifetime_ref={:?})", lifetime_ref); + // Walk up the scope chain, tracking the number of fn scopes // that we pass through, until we find a lifetime with the // given name or we run out of scopes. @@ -889,7 +983,7 @@ fn resolve_lifetime_ref(&mut self, lifetime_ref: &hir::Lifetime) { break None; } - Scope::Binder { ref lifetimes, s } => { + Scope::Binder { ref lifetimes, s, next_early_index: _ } => { if let Some(&def) = lifetimes.get(&lifetime_ref.name) { break Some(def.shifted(late_depth)); } else { @@ -1520,7 +1614,7 @@ fn check_lifetime_def_for_shadowing(&self, return; } - Scope::Binder { ref lifetimes, s } => { + Scope::Binder { ref lifetimes, s, next_early_index: _ } => { if let Some(&def) = lifetimes.get(&lifetime.name) { let node_id = self.hir_map .as_local_node_id(def.id().unwrap()) @@ -1549,7 +1643,7 @@ fn insert_lifetime(&mut self, probably a bug in syntax::fold"); } - debug!("{} resolved to {:?} span={:?}", + debug!("insert_lifetime: {} resolved to {:?} span={:?}", self.hir_map.node_to_string(lifetime_ref.id), def, self.sess.codemap().span_to_string(lifetime_ref.span)); @@ -1709,7 +1803,7 @@ fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) { } fn visit_ty(&mut self, ty: &hir::Ty) { - if let hir::TyImplTraitExistential(_) = ty.node { + if let hir::TyImplTraitExistential(..) = ty.node { self.impl_trait = true; } intravisit::walk_ty(self, ty); diff --git a/src/librustc/ty/subst.rs b/src/librustc/ty/subst.rs index 83222e79a12..80b113dfdf5 100644 --- a/src/librustc/ty/subst.rs +++ b/src/librustc/ty/subst.rs @@ -220,11 +220,11 @@ pub fn extend_to(&self, tcx.intern_substs(&result) } - fn fill_item(substs: &mut Vec>, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - defs: &ty::Generics, - mk_region: &mut FR, - mk_type: &mut FT) + pub fn fill_item(substs: &mut Vec>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + defs: &ty::Generics, + mk_region: &mut FR, + mk_type: &mut FT) where FR: FnMut(&ty::RegionParameterDef, &[Kind<'tcx>]) -> ty::Region<'tcx>, FT: FnMut(&ty::TypeParameterDef, &[Kind<'tcx>]) -> Ty<'tcx> { diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index acb929981fb..4b0cf018cb5 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -1015,6 +1015,10 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { TyForeign(def_id) => parameterized(f, subst::Substs::empty(), def_id, &[]), TyProjection(ref data) => data.print(f, cx), TyAnon(def_id, substs) => { + if cx.is_verbose { + return write!(f, "TyAnon({:?}, {:?})", def_id, substs); + } + ty::tls::with(|tcx| { // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, // by looking up the projections associated with the def_id. diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 8c40f303b93..d8bdd21a2a0 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -1487,7 +1487,7 @@ fn encode_info_for_generics(&mut self, generics: &hir::Generics) { fn encode_info_for_ty(&mut self, ty: &hir::Ty) { match ty.node { - hir::TyImplTraitExistential(_) => { + hir::TyImplTraitExistential(..) => { let def_id = self.tcx.hir.local_def_id(ty.id); self.record(def_id, IsolatedEncoder::encode_info_for_anon_ty, def_id); } diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 7aaf65e1fd0..2405461741d 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -21,7 +21,7 @@ use namespace::Namespace; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::traits; -use rustc::ty::{self, Ty, TyCtxt, ToPredicate, TypeFoldable}; +use rustc::ty::{self, RegionKind, Ty, TyCtxt, ToPredicate, TypeFoldable}; use rustc::ty::wf::object_region_bounds; use rustc_back::slice; use require_c_abi_if_variadic; @@ -1034,9 +1034,9 @@ pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty) -> Ty<'tcx> { hir::TyTraitObject(ref bounds, ref lifetime) => { self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime) } - hir::TyImplTraitExistential(_) => { + hir::TyImplTraitExistential(_, ref lifetimes) => { let def_id = tcx.hir.local_def_id(ast_ty.id); - tcx.mk_anon(def_id, Substs::identity_for_item(tcx, def_id)) + self.impl_trait_ty_to_ty(def_id, lifetimes) } hir::TyImplTraitUniversal(fn_def_id, _) => { let impl_trait_def_id = tcx.hir.local_def_id(ast_ty.id); @@ -1097,6 +1097,43 @@ pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty) -> Ty<'tcx> { result_ty } + pub fn impl_trait_ty_to_ty(&self, def_id: DefId, lifetimes: &[hir::Lifetime]) -> Ty<'tcx> { + debug!("impl_trait_ty_to_ty(def_id={:?}, lifetimes={:?})", def_id, lifetimes); + let tcx = self.tcx(); + let generics = tcx.generics_of(def_id); + + // Fill in the substs of the parent generics + debug!("impl_trait_ty_to_ty: generics={:?}", generics); + let mut substs = Vec::with_capacity(generics.count()); + if let Some(parent_id) = generics.parent { + let parent_generics = tcx.generics_of(parent_id); + Substs::fill_item( + &mut substs, tcx, parent_generics, + &mut |def, _| tcx.mk_region( + ty::ReEarlyBound(def.to_early_bound_region_data())), + &mut |def, _| tcx.mk_param_from_def(def) + ); + + // Replace all lifetimes with 'static + for subst in &mut substs { + if let Some(_) = subst.as_region() { + *subst = Kind::from(&RegionKind::ReStatic); + } + } + debug!("impl_trait_ty_to_ty: substs from parent = {:?}", substs); + } + assert_eq!(substs.len(), generics.parent_count()); + + // Fill in our own generics with the resolved lifetimes + assert_eq!(lifetimes.len(), generics.own_count()); + substs.extend(lifetimes.iter().map(|lt| + Kind::from(self.ast_region_to_region(lt, None)))); + + debug!("impl_trait_ty_to_ty: final substs = {:?}", substs); + + tcx.mk_anon(def_id, tcx.intern_substs(&substs)) + } + pub fn ty_of_arg(&self, ty: &hir::Ty, expected_ty: Option>) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index b3a07027fb0..0cb1408a450 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -213,7 +213,7 @@ pub struct Inherited<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { // associated fresh inference variable. Writeback resolves these // variables to get the concrete type, which can be used to // deanonymize TyAnon, after typeck is done with all functions. - anon_types: RefCell>>, + anon_types: RefCell>>, /// Each type parameter has an implicit region bound that /// indicates it must outlive at least the function body (the user @@ -226,6 +226,43 @@ pub struct Inherited<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { body_id: Option, } +/// Information about the anonymous, abstract types whose values we +/// are inferring in this function (these are the `impl Trait` that +/// appear in the return type). +#[derive(Debug)] +struct AnonTypeDecl<'tcx> { + /// The substitutions that we apply to the abstract that that this + /// `impl Trait` desugars to. e.g., if: + /// + /// fn foo<'a, 'b, T>() -> impl Trait<'a> + /// + /// winds up desugared to: + /// + /// abstract type Foo<'x, T>: Trait<'x> + /// fn foo<'a, 'b, T>() -> Foo<'a, T> + /// + /// then `substs` would be `['a, T]`. + substs: &'tcx Substs<'tcx>, + + /// The type variable that represents the value of the abstract type + /// that we require. In other words, after we compile this function, + /// we will be created a constraint like: + /// + /// Foo<'a, T> = ?C + /// + /// where `?C` is the value of this type variable. =) It may + /// naturally refer to the type and lifetime parameters in scope + /// in this function, though ultimately it should only reference + /// those that are arguments to `Foo` in the constraint above. (In + /// other words, `?C` should not include `'b`, even though it's a + /// lifetime parameter on `foo`.) + concrete_ty: Ty<'tcx>, + + /// A list of all required region bounds on the impl Trait type, + /// e.g. `'a` and `'b` in `fn foo<'a, 'b, 'c>() -> impl Trait<'c> + 'a + 'b`. + required_region_bounds: Vec>, +} + impl<'a, 'gcx, 'tcx> Deref for Inherited<'a, 'gcx, 'tcx> { type Target = InferCtxt<'a, 'gcx, 'tcx>; fn deref(&self) -> &Self::Target { @@ -622,7 +659,7 @@ fn new(infcx: InferCtxt<'a, 'gcx, 'tcx>, def_id: DefId) -> Self { deferred_call_resolutions: RefCell::new(DefIdMap()), deferred_cast_checks: RefCell::new(Vec::new()), deferred_generator_interiors: RefCell::new(Vec::new()), - anon_types: RefCell::new(NodeMap()), + anon_types: RefCell::new(DefIdMap()), implicit_region_bound, body_id, } @@ -870,7 +907,10 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env, &fn_sig); - check_fn(&inh, param_env, fn_sig, decl, id, body, false).0 + let fcx = check_fn(&inh, param_env, fn_sig, decl, id, body, false).0; + // Ensure anon_types have been instantiated prior to entering regionck + fcx.instantiate_anon_types(&fn_sig.output()); + fcx } else { let fcx = FnCtxt::new(&inh, param_env, body.value.id); let expected_type = tcx.type_of(def_id); @@ -1909,20 +1949,34 @@ fn instantiate_bounds(&self, span: Span, def_id: DefId, substs: &Substs<'tcx>) /// Replace all anonymized types with fresh inference variables /// and record them for writeback. fn instantiate_anon_types>(&self, value: &T) -> T { + debug!("instantiate_anon_types(value={:?})", value); value.fold_with(&mut BottomUpFolder { tcx: self.tcx, fldop: |ty| { if let ty::TyAnon(def_id, substs) = ty.sty { + debug!("instantiate_anon_types: TyAnon(def_id={:?}, substs={:?})", def_id, substs); + // Use the same type variable if the exact same TyAnon appears more // than once in the return type (e.g. if it's passed to a type alias). - let id = self.tcx.hir.as_local_node_id(def_id).unwrap(); - if let Some(ty_var) = self.anon_types.borrow().get(&id) { - return ty_var; + if let Some(anon_defn) = self.anon_types.borrow().get(&def_id) { + return anon_defn.concrete_ty; } let span = self.tcx.def_span(def_id); let ty_var = self.next_ty_var(TypeVariableOrigin::TypeInference(span)); - self.anon_types.borrow_mut().insert(id, ty_var); let predicates_of = self.tcx.predicates_of(def_id); let bounds = predicates_of.instantiate(self.tcx, substs); + debug!("instantiate_anon_types: bounds={:?}", bounds); + + let required_region_bounds = + self.tcx.required_region_bounds(ty, bounds.predicates.clone()); + debug!("instantiate_anon_types: required_region_bounds={:?}", + required_region_bounds); + + self.anon_types.borrow_mut().insert(def_id, AnonTypeDecl { + substs, + concrete_ty: ty_var, + required_region_bounds, + }); + debug!("instantiate_anon_types: ty_var={:?}", ty_var); for predicate in bounds.predicates { // Change the predicate to refer to the type variable, @@ -1931,8 +1985,11 @@ fn instantiate_anon_types>(&self, value: &T) -> T { let predicate = self.instantiate_anon_types(&predicate); // Require that the predicate holds for the concrete type. - let cause = traits::ObligationCause::new(span, self.body_id, + let cause = traits::ObligationCause::new(span, + self.body_id, traits::SizedReturnType); + + debug!("instantiate_anon_types: predicate={:?}", predicate); self.register_predicate(traits::Obligation::new(cause, self.param_env, predicate)); diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index a17133d412c..b91137aeb68 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -92,6 +92,7 @@ use rustc::ty::{self, Ty}; use rustc::infer::{self, OutlivesEnvironment}; use rustc::ty::adjustment; +use rustc::ty::outlives::Component; use std::mem; use std::ops::Deref; @@ -135,7 +136,7 @@ pub fn regionck_item(&self, item_id: ast::NodeId, span: Span, wf_tys: &[Ty<'tcx>]) { - debug!("regionck_item(item.id={:?}, wf_tys={:?}", item_id, wf_tys); + debug!("regionck_item(item.id={:?}, wf_tys={:?})", item_id, wf_tys); let subject = self.tcx.hir.local_def_id(item_id); let mut rcx = RegionCtxt::new(self, RepeatingScope(item_id), @@ -336,10 +337,13 @@ fn visit_fn_body(&mut self, debug!("visit_fn_body body.id {:?} call_site_scope: {:?}", body.id(), call_site_scope); let call_site_region = self.tcx.mk_region(ty::ReScope(call_site_scope)); + let body_hir_id = self.tcx.hir.node_to_hir_id(body_id.node_id); self.type_of_node_must_outlive(infer::CallReturn(span), body_hir_id, call_site_region); + + self.constrain_anon_types(); } fn visit_region_obligations(&mut self, node_id: ast::NodeId) @@ -358,6 +362,194 @@ fn visit_region_obligations(&mut self, node_id: ast::NodeId) self.body_id); } + /// Go through each of the existential `impl Trait` types that + /// appear in the function signature. For example, if the current + /// function is as follows: + /// + /// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>) + /// + /// we would iterate through the `impl Bar<'a>` and the + /// `impl Bar<'b>` here. Remember that each of them has + /// their own "abstract type" definition created for them. As + /// we iterate, we have a `def_id` that corresponds to this + /// definition, and a set of substitutions `substs` that are + /// being supplied to this abstract typed definition in the + /// signature: + /// + /// abstract type Foo1<'x>: Bar<'x>; + /// abstract type Foo2<'x>: Bar<'x>; + /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } + /// ^^^^ ^^ substs + /// def_id + /// + /// In addition, for each of the types we will have a type + /// variable `concrete_ty` containing the concrete type that + /// this function uses for `Foo1` and `Foo2`. That is, + /// conceptually, there is a constraint like: + /// + /// for<'a> (Foo1<'a> = C) + /// + /// where `C` is `concrete_ty`. For this equation to be satisfiable, + /// the type `C` can only refer to two regions: `'static` and `'a`. + /// + /// The problem is that this type `C` may contain arbitrary + /// region variables. In fact, it is fairly likely that it + /// does! Consider this possible definition of `foo`: + /// + /// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) { + /// (&*x, &*y) + /// } + /// + /// Here, the values for the concrete types of the two impl + /// traits will include inference variables: + /// + /// &'0 i32 + /// &'1 i32 + /// + /// Ordinarily, the subtyping rules would ensure that these are + /// sufficiently large. But since `impl Bar<'a>` isn't a specific + /// type per se, we don't get such constraints by default. This + /// is where this function comes into play. It adds extra + /// constraints to ensure that all the regions which appear in the + /// inferred type are regions that could validly appear. + /// + /// This is actually a bit of a tricky constraint in general. We + /// want to say that each variable (e.g., `'0``) can only take on + /// values that were supplied as arguments to the abstract type + /// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in + /// scope. We don't have a constraint quite of this kind in the current + /// region checker. + /// + /// What we *do* have is the `<=` relation. So what we do is to + /// find the LUB of all the arguments that appear in the substs: + /// in this case, that would be `LUB('a) = 'a`, and then we apply + /// that as a least bound to the variables (e.g., `'a <= '0`). + /// + /// In some cases this is pretty suboptimal. Consider this example: + /// + /// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... } + /// + /// Here, the regions `'a` and `'b` appear in the substitutions, + /// so we would generate `LUB('a, 'b)` as a kind of "minimal upper + /// bound", but that turns out be `'static` -- which is clearly + /// too strict! + fn constrain_anon_types(&mut self) { + debug!("constrain_anon_types()"); + + for (&def_id, anon_defn) in self.fcx.anon_types.borrow().iter() { + let concrete_ty = self.resolve_type(anon_defn.concrete_ty); + + debug!("constrain_anon_types: def_id={:?}", def_id); + debug!("constrain_anon_types: anon_defn={:#?}", anon_defn); + debug!("constrain_anon_types: concrete_ty={:?}", concrete_ty); + + let abstract_type_generics = self.tcx.generics_of(def_id); + + let span = self.tcx.def_span(def_id); + + // If there are required region bounds, we can just skip + // ahead. There will already be a registered region + // obligation related `concrete_ty` to those regions. + if anon_defn.required_region_bounds.len() != 0 { + continue; + } + + // There were no `required_region_bounds`, + // so we have to search for a `least_region`. + // Go through all the regions used as arguments to the + // abstract type. These are the parameters to the abstract + // type; so in our example above, `substs` would contain + // `['a]` for the first impl trait and `'b` for the + // second. + let mut least_region = None; + for region_def in &abstract_type_generics.regions { + // Find the index of this region in the list of substitutions. + let index = region_def.index as usize; + + // Get the value supplied for this region from the substs. + let subst_arg = anon_defn.substs[index].as_region().unwrap(); + + // Compute the least upper bound of it with the other regions. + debug!("constrain_anon_types: least_region={:?}", least_region); + debug!("constrain_anon_types: subst_arg={:?}", subst_arg); + match least_region { + None => least_region = Some(subst_arg), + Some(lr) => { + if self.outlives_environment + .free_region_map() + .sub_free_regions(lr, subst_arg) { + // keep the current least region + } else if self.outlives_environment + .free_region_map() + .sub_free_regions(subst_arg, lr) { + // switch to `subst_arg` + least_region = Some(subst_arg); + } else { + // There are two regions (`lr` and + // `subst_arg`) which are not relatable. We can't + // find a best choice. + self.tcx + .sess + .struct_span_err(span, "ambiguous lifetime bound in `impl Trait`") + .span_label(span, + format!("neither `{}` nor `{}` outlives the other", + lr, subst_arg)) + .emit(); + + least_region = Some(self.tcx.mk_region(ty::ReEmpty)); + break; + } + } + } + } + + let least_region = least_region.unwrap_or(self.tcx.types.re_static); + debug!("constrain_anon_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 = self.tcx.outlives_components(ty); + 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 anon"); + } + + Component::Projection(ty::ProjectionTy { substs, item_def_id: _ }) => { + for r in substs.regions() { + bound_region(r); + } + types.extend(substs.types()); + } + + Component::EscapingProjection(more_components) => { + components.extend(more_components); + } + } + } + } + } + } + fn resolve_regions_and_report_errors(&self) { self.fcx.resolve_regions_and_report_errors(self.subject_def_id, &self.region_scope_tree, diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index ce2ac73a27e..6bb5a839950 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -18,8 +18,9 @@ use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::infer::{InferCtxt}; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::fold::{TypeFolder,TypeFoldable}; -use rustc::util::nodemap::DefIdSet; +use rustc::ty::fold::{TypeFolder, TypeFoldable}; +use rustc::ty::subst::{Kind, Substs}; +use rustc::util::nodemap::{DefIdSet, FxHashMap}; use syntax::ast; use syntax_pos::Span; use std::mem; @@ -285,8 +286,23 @@ fn visit_free_region_map(&mut self) { fn visit_anon_types(&mut self) { let gcx = self.tcx().global_tcx(); - for (&node_id, &concrete_ty) in self.fcx.anon_types.borrow().iter() { - let inside_ty = self.resolve(&concrete_ty, &node_id); + for (&def_id, anon_defn) in self.fcx.anon_types.borrow().iter() { + let node_id = gcx.hir.as_local_node_id(def_id).unwrap(); + let inside_ty = self.resolve(&anon_defn.concrete_ty, &node_id); + + // Use substs to build up a reverse map from regions + // to their identity mappings. + // This is necessary because of `impl Trait` lifetimes + // are computed by replacing existing lifetimes with 'static + // and remapping only those used in the `impl Trait` return type, + // resulting in the parameters shifting. + let id_substs = Substs::identity_for_item(gcx, def_id); + let map: FxHashMap, Kind<'gcx>> = + anon_defn.substs + .iter() + .enumerate() + .map(|(index, subst)| (*subst, id_substs[index])) + .collect(); // Convert the type from the function into a type valid outside // the function, by replacing invalid regions with 'static, @@ -295,25 +311,39 @@ fn visit_anon_types(&mut self) { match *r { // 'static and early-bound regions are valid. ty::ReStatic | - ty::ReEarlyBound(_) | ty::ReEmpty => r, - ty::ReFree(_) | - ty::ReLateBound(..) | - ty::ReScope(_) | - ty::ReSkolemized(..) => { - let span = node_id.to_span(&self.fcx.tcx); - span_err!(self.tcx().sess, span, E0564, - "only named lifetimes are allowed in `impl Trait`, \ - but `{}` was found in the type `{}`", r, inside_ty); - gcx.types.re_static - } - - ty::ReVar(_) | - ty::ReErased => { - let span = node_id.to_span(&self.fcx.tcx); - span_bug!(span, "invalid region in impl Trait: {:?}", r); - } + // All other regions, we map them appropriately to their adjusted + // indices, erroring if we find any lifetimes that were not mapped + // into the new set. + _ => if let Some(r1) = + map.get(&Kind::from(r)).and_then(|k| k.as_region()) { r1 } else + { + // No mapping was found. This means that + // it is either a disallowed lifetime, + // which will be caught by regionck, or it + // is a region in a non-upvar closure + // generic, which is explicitly + // allowed. If that surprises you, read + // on. + // + // The case of closure 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 + // trans. + gcx.types.re_empty + }, } }); diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index b5fbbeb1692..916fe3be58c 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -935,6 +935,10 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } + NodeTy(&hir::Ty { node: hir::TyImplTraitExistential(ref exist_ty, _), .. }) => { + (&exist_ty.generics, None) + } + _ => (&no_generics, None) }; @@ -1358,6 +1362,8 @@ fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, use rustc::hir::map::*; use rustc::hir::*; + debug!("explicit_predicates_of(def_id={:?})", def_id); + let node_id = tcx.hir.as_local_node_id(def_id).unwrap(); let node = tcx.hir.get(node_id); @@ -1412,17 +1418,28 @@ fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } - NodeTy(&Ty { node: TyImplTraitExistential(ref bounds), span, .. }) => { + NodeTy(&Ty { node: TyImplTraitExistential(ref exist_ty, _), span, .. }) => { let substs = Substs::identity_for_item(tcx, def_id); let anon_ty = tcx.mk_anon(def_id, substs); + debug!("explicit_predicates_of: anon_ty={:?}", anon_ty); + // Collect the bounds, i.e. the `A+B+'c` in `impl A+B+'c`. - let bounds = compute_bounds(&icx, anon_ty, bounds, + let bounds = compute_bounds(&icx, + anon_ty, + &exist_ty.bounds, SizedByDefault::Yes, span); + + debug!("explicit_predicates_of: bounds={:?}", bounds); + + let predicates = bounds.predicates(tcx, anon_ty); + + debug!("explicit_predicates_of: predicates={:?}", predicates); + return ty::GenericPredicates { parent: None, - predicates: bounds.predicates(tcx, anon_ty) + predicates: predicates }; } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1d107c169b0..720e946d469 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1960,9 +1960,8 @@ fn clean(&self, cx: &DocContext) -> Type { } } TyBareFn(ref barefn) => BareFunction(box barefn.clean(cx)), - TyImplTraitExistential(ref bounds) | - TyImplTraitUniversal(_, ref bounds) => - ImplTrait(bounds.clean(cx)), + TyImplTraitExistential(ref exist_ty, ref _lts) => ImplTrait(exist_ty.bounds.clean(cx)), + TyImplTraitUniversal(_, ref bounds) => ImplTrait(bounds.clean(cx)), TyInfer | TyErr => Infer, TyTypeof(..) => panic!("Unimplemented type {:?}", self.node), } diff --git a/src/test/compile-fail/E0657.rs b/src/test/compile-fail/E0657.rs new file mode 100644 index 00000000000..b72a8f03089 --- /dev/null +++ b/src/test/compile-fail/E0657.rs @@ -0,0 +1,36 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![allow(warnings)] +#![feature(conservative_impl_trait)] + +trait Id {} +trait Lt<'a> {} + +impl<'a> Lt<'a> for () {} +impl Id for T {} + +fn free_fn_capture_hrtb_in_impl_trait() + -> impl for<'a> Id> + //~^ ERROR `impl Trait` can only capture lifetimes bound at the fn or impl level [E0657] +{ + () +} + +struct Foo; +impl Foo { + fn impl_fn_capture_hrtb_in_impl_trait() + -> impl for<'a> Id> + //~^ ERROR `impl Trait` can only capture lifetimes bound at the fn or impl level + { + () + } +} + +fn main() {} diff --git a/src/test/compile-fail/impl-trait/lifetimes.rs b/src/test/compile-fail/impl-trait/lifetimes.rs deleted file mode 100644 index 9d9f6bf7297..00000000000 --- a/src/test/compile-fail/impl-trait/lifetimes.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(conservative_impl_trait)] - -// Helper creating a fake borrow, captured by the impl Trait. -fn borrow<'a, T>(_: &'a mut T) -> impl Copy { () } - -fn stack() -> impl Copy { - //~^ ERROR only named lifetimes are allowed in `impl Trait` - let x = 0; - &x -} - -fn late_bound(x: &i32) -> impl Copy { - //~^ ERROR only named lifetimes are allowed in `impl Trait` - x -} - -// FIXME(#34511) Should work but doesn't at the moment, -// region-checking needs an overhault to support this. -fn early_bound<'a>(x: &'a i32) -> impl Copy { - //~^ ERROR only named lifetimes are allowed in `impl Trait` - x -} - -fn ambiguous<'a, 'b>(x: &'a [u32], y: &'b [u32]) -> impl Iterator { - //~^ ERROR only named lifetimes are allowed in `impl Trait` - if x.len() < y.len() { - x.iter().cloned() - } else { - y.iter().cloned() - } -} - -fn main() {} diff --git a/src/test/compile-fail/impl-trait/must_outlive_least_region_or_bound.rs b/src/test/compile-fail/impl-trait/must_outlive_least_region_or_bound.rs new file mode 100644 index 00000000000..837160bc2fc --- /dev/null +++ b/src/test/compile-fail/impl-trait/must_outlive_least_region_or_bound.rs @@ -0,0 +1,39 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(conservative_impl_trait)] + +use std::fmt::Debug; + +fn elided(x: &i32) -> impl Copy { x } +//~^ ERROR cannot infer an appropriate lifetime + +fn explicit<'a>(x: &'a i32) -> impl Copy { x } +//~^ ERROR cannot infer an appropriate lifetime + +trait LifetimeTrait<'a> {} +impl<'a> LifetimeTrait<'a> for &'a i32 {} + +fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } +//~^ ERROR cannot infer an appropriate lifetime + +// Tests that a closure type contianing 'b cannot be returned from a type where +// only 'a was expected. +fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32) { + //~^ ERROR lifetime mismatch + move |_| println!("{}", y) +} + +fn ty_param_wont_outlive_static(x: T) -> impl Debug + 'static { + //~^ ERROR the parameter type `T` may not live long enough + x +} + +fn main() {} diff --git a/src/test/compile-fail/impl-trait/needs_least_region_or_bound.rs b/src/test/compile-fail/impl-trait/needs_least_region_or_bound.rs new file mode 100644 index 00000000000..2a06580fe60 --- /dev/null +++ b/src/test/compile-fail/impl-trait/needs_least_region_or_bound.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(conservative_impl_trait)] + +use std::fmt::Debug; + +trait MultiRegionTrait<'a, 'b> {} +impl<'a, 'b> MultiRegionTrait<'a, 'b> for (&'a u32, &'b u32) {} + +fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { +//~^ ERROR ambiguous lifetime bound + (x, y) +} + +fn main() {} diff --git a/src/test/compile-fail/impl-trait/type_parameters_captured.rs b/src/test/compile-fail/impl-trait/type_parameters_captured.rs new file mode 100644 index 00000000000..c6ff762b905 --- /dev/null +++ b/src/test/compile-fail/impl-trait/type_parameters_captured.rs @@ -0,0 +1,24 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(conservative_impl_trait)] + +use std::fmt::Debug; + +trait Any {} +impl Any for T {} + +// Check that type parameters are captured and not considered 'static +fn foo(x: T) -> impl Any + 'static { + //~^ ERROR the parameter type `T` may not live long enough + x +} + +fn main() {} diff --git a/src/test/run-pass/impl-trait/lifetimes.rs b/src/test/run-pass/impl-trait/lifetimes.rs new file mode 100644 index 00000000000..1e19e7f6a13 --- /dev/null +++ b/src/test/run-pass/impl-trait/lifetimes.rs @@ -0,0 +1,97 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(conservative_impl_trait)] +#![allow(warnings)] + +use std::fmt::Debug; + +fn any_lifetime<'a>() -> &'a u32 { &5 } + +fn static_lifetime() -> &'static u32 { &5 } + +fn any_lifetime_as_static_impl_trait() -> impl Debug { + any_lifetime() +} + +fn lifetimes_as_static_impl_trait() -> impl Debug { + static_lifetime() +} + +fn no_params_or_lifetimes_is_static() -> impl Debug + 'static { + lifetimes_as_static_impl_trait() +} + +fn static_input_type_is_static(x: T) -> impl Debug + 'static { x } + +fn type_outlives_reference_lifetime<'a, T: Debug>(x: &'a T) -> impl Debug + 'a { x } + +trait SingleRegionTrait<'a> {} +impl<'a> SingleRegionTrait<'a> for u32 {} + +fn simple_type_hrtb<'b>() -> impl for<'a> SingleRegionTrait<'a> { 5 } +fn closure_hrtb() -> impl for<'a> Fn(&'a u32) { |_| () } + +fn mixed_lifetimes<'a>() -> impl for<'b: 'a> Fn(&'b u32) { |_| () } +fn mixed_as_static() -> impl Fn(&'static u32) { mixed_lifetimes() } + +trait MultiRegionTrait<'a, 'b>: Debug {} + +#[derive(Debug)] +struct MultiRegionStruct<'a, 'b>(&'a u32, &'b u32); +impl<'a, 'b> MultiRegionTrait<'a, 'b> for MultiRegionStruct<'a, 'b> {} + +#[derive(Debug)] +struct NoRegionStruct; +impl<'a, 'b> MultiRegionTrait<'a, 'b> for NoRegionStruct {} + +fn finds_least_region<'a: 'b, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { + MultiRegionStruct(x, y) +} + +fn finds_explicit_bound<'a: 'b, 'b> + (x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> + 'b +{ + MultiRegionStruct(x, y) +} + +fn finds_explicit_bound_even_without_least_region<'a, 'b> + (x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> + 'b +{ + NoRegionStruct +} + +/* FIXME: `impl Trait<'a> + 'b` should live as long as 'b, even if 'b outlives 'a +fn outlives_bounds_even_with_contained_regions<'a, 'b> + (x: &'a u32, y: &'b u32) -> impl Debug + 'b +{ + finds_explicit_bound_even_without_least_region(x, y) +} +*/ + +fn unnamed_lifetimes_arent_contained_in_impl_trait_and_will_unify<'a, 'b> + (x: &'a u32, y: &'b u32) -> impl Debug +{ + fn deref<'lt>(x: &'lt u32) -> impl Debug { *x } + + if true { deref(x) } else { deref(y) } +} + +fn can_add_region_bound_to_static_type<'a, 'b>(_: &'a u32) -> impl Debug + 'a { 5 } + +struct MyVec(Vec>); + +impl<'unnecessary_lifetime> MyVec { + fn iter_doesnt_capture_unnecessary_lifetime<'s>(&'s self) -> impl Iterator { + self.0.iter().flat_map(|inner_vec| inner_vec.iter()) + } +} + +fn main() {} diff --git a/src/test/ui/span/loan-extend.rs b/src/test/ui/span/loan-extend.rs deleted file mode 100644 index a4b951daab4..00000000000 --- a/src/test/ui/span/loan-extend.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(conservative_impl_trait)] - -// Helper creating a fake borrow, captured by the impl Trait. -fn borrow<'a, T>(_: &'a mut T) -> impl Copy { () } - -fn main() { - let long; - let mut short = 0; - long = borrow(&mut short); - //~^ NOTE borrow occurs here -} -//~^ ERROR `short` does not live long enough -//~| NOTE `short` dropped here while still borrowed -//~| NOTE values in a scope are dropped in the opposite order they are created -- 2.44.0