diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 5269ec90def5c..0505c470c2c6b 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -345,6 +345,7 @@ impl<'a, 'gcx, 'tcx> HashStable> for ty::Ge // Reverse map to each `TypeParameterDef`'s `index` field, from // `def_id.index` (`def_id.krate` is the same as the item's). type_param_to_index: _, // Don't hash this + region_param_to_index: _, // Don't hash this either has_self, has_late_bound_regions, } = *self; diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 7f4aedb5f14bb..a1e22273afe10 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -716,6 +716,10 @@ pub struct Generics { pub regions: Vec, pub types: Vec, + /// Reverse map to each `RegionParameterDef`'s `index` field. + /// Currently only appears for an `impl Trait` type's `ty::Generics` + pub region_param_to_index: Option>, + /// Reverse map to each `TypeParameterDef`'s `index` field, from /// `def_id.index` (`def_id.krate` is the same as the item's). pub type_param_to_index: BTreeMap, diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 1ec850ad7f349..c7952193688bb 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -1078,6 +1078,54 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } } + fn impl_trait_to_ty(&self, impl_trait_node: ast::NodeId) -> Ty<'tcx> { + let tcx = self.tcx(); + let def_id = tcx.hir.local_def_id(impl_trait_node); + let generics = tcx.generics_of(def_id); + let type_param_to_index = &generics.type_param_to_index; + let region_param_to_index = if let Some(ref x) = generics.region_param_to_index { + x + } else { + bug!("missing region_param_to_index for `impl Trait` generics") + }; + let param_count = type_param_to_index.len() + region_param_to_index.len(); + + // Filter the parent substs into just the ones that are relevant to us + let parent = tcx.hir.local_def_id(tcx.hir.get_parent(impl_trait_node)); + let parent_substs = Substs::identity_for_item(tcx, parent); + let mut opt_substs: Vec> = + ::std::iter::repeat(None).take(param_count).collect(); + + let mut parent_id = Some(parent); + while let Some(cur_gen_id) = parent_id { + let cur_generics = tcx.generics_of(cur_gen_id); + + for region in &cur_generics.regions { + if let Some(&new_index) = region_param_to_index.get(®ion.def_id.index) { + opt_substs[new_index as usize] = Some(parent_substs[region.index as usize]); + } + } + for type_ in &cur_generics.types { + if let Some(&new_index) = type_param_to_index.get(&type_.def_id.index) { + opt_substs[new_index as usize] = Some(parent_substs[type_.index as usize]); + } + } + + parent_id = cur_generics.parent; + } + + let mut substs = Vec::with_capacity(param_count); + for opt_subst in opt_substs { + if let Some(subst) = opt_subst { + substs.push(subst); + } else { + bug!("missing `impl Trait` subst"); + } + } + + tcx.mk_anon(def_id, tcx.intern_substs(&substs)) + } + /// Parses the programmer's textual representation of a type into our /// internal notion of a type. pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty) -> Ty<'tcx> { @@ -1154,8 +1202,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { // Create the anonymized type. if allow { - 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_to_ty(ast_ty.id) } else { span_err!(tcx.sess, ast_ty.span, E0562, "`impl Trait` not allowed outside of function \ diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index f25a6cf58a79e..718dbc3615f02 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -65,7 +65,7 @@ use rustc::ty::{ToPredicate, ReprOptions}; use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt}; use rustc::ty::maps::Providers; use rustc::ty::util::IntTypeExt; -use util::nodemap::FxHashMap; +use util::nodemap::{DefIdSet, FxHashMap}; use rustc_const_math::ConstInt; @@ -76,7 +76,7 @@ use syntax::codemap::Spanned; use syntax::symbol::{Symbol, keywords}; use syntax_pos::{Span, DUMMY_SP}; -use rustc::hir::{self, map as hir_map}; +use rustc::hir::{self, map as hir_map, Lifetime}; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::DefId; @@ -863,6 +863,35 @@ fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } +struct ImplTraitRegionBoundsCollector<'a, 'tcx: 'a, 'b> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + region_bounds: &'b mut DefIdSet, +} + +impl<'a, 'tcx: 'a, 'b> Visitor<'tcx> for ImplTraitRegionBoundsCollector<'a, 'tcx, 'b> { + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { + NestedVisitorMap::None + } + + fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { + if !lifetime.is_static() { // no need to look up the lifetime if we already know it's static + match self.tcx.named_region_map.defs.get(&lifetime.id) { + Some(&rl::Region::EarlyBound(_, id)) => { + self.region_bounds.insert(self.tcx.hir.local_def_id(id)); + } + Some(&rl::Region::LateBound(_, _)) | + Some(&rl::Region::LateBoundAnon(_, _)) => { + span_bug!(lifetime.span, + "Late-bound lifetimes not yet handed properly in `impl Trait`"); + } + Some(&rl::Region::Static) | + Some(&rl::Region::Free(_, _)) | + None => {} + } + } + } +} + fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx ty::Generics { @@ -884,18 +913,6 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, NodeExpr(&hir::Expr { node: hir::ExprClosure(..), .. }) => { Some(tcx.closure_base_def_id(def_id)) } - NodeTy(&hir::Ty { node: hir::TyImplTrait(..), .. }) => { - let mut parent_id = node_id; - loop { - match tcx.hir.get(parent_id) { - NodeItem(_) | NodeImplItem(_) | NodeTraitItem(_) => break, - _ => { - parent_id = tcx.hir.get_parent_node(parent_id); - } - } - } - Some(tcx.hir.local_def_id(parent_id)) - } _ => None }; @@ -962,6 +979,67 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } + NodeTy(&hir::Ty{ node: hir::TyImplTrait(ref impl_trait_bounds), .. }) => { + // Traverse impl_trait_bounds to identify all of the region bounds that appear + // so that we can copy them from the environment + let mut region_bounds = DefIdSet(); + { + let mut bounds_collector = ImplTraitRegionBoundsCollector { + tcx: tcx, + region_bounds: &mut region_bounds, + }; + for bound in impl_trait_bounds { + bounds_collector.visit_ty_param_bound(bound); + } + } + + let mut regions = vec![]; + let mut types = vec![]; + let mut region_param_to_index = BTreeMap::new(); + let mut type_param_to_index = BTreeMap::new(); + let mut has_self = false; + + // Copy generics from parents, filtering out unused region parameters + let mut parent_id = Some(tcx.hir.local_def_id(tcx.hir.get_parent(node_id))); + while let Some(cur_gen_id) = parent_id { + let cur_generics = tcx.generics_of(cur_gen_id); + + has_self |= cur_generics.has_self; + + for region in &cur_generics.regions { + if region_bounds.contains(®ion.def_id) { + regions.push(*region); + } + } + + types.extend_from_slice(&cur_generics.types); + + parent_id = cur_generics.parent; + } + + // Fixup indices + for (i, ref mut region) in regions.iter_mut().enumerate() { + region.index = i as u32; + region_param_to_index.insert(region.def_id.index, region.index); + } + for (i, ref mut type_) in types.iter_mut().enumerate() { + type_.index = (regions.len() + i) as u32; + type_param_to_index.insert(type_.def_id.index, type_.index); + } + + return tcx.alloc_generics(ty::Generics { + parent: None, + parent_regions: 0, + parent_types: 0, + regions: regions, + types: types, + region_param_to_index: Some(region_param_to_index), + type_param_to_index: type_param_to_index, + has_self: has_self, + has_late_bound_regions: None, + }); + } + _ => &no_generics }; @@ -1048,6 +1126,7 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, regions: regions, types: types, type_param_to_index: type_param_to_index, + region_param_to_index: None, has_self: has_self || parent_has_self, has_late_bound_regions: has_late_bound_regions(tcx, node), }) diff --git a/src/test/run-pass/impl-trait/example-st.rs b/src/test/run-pass/impl-trait/example-st.rs index e9326ed286aff..2d49e13797a02 100644 --- a/src/test/run-pass/impl-trait/example-st.rs +++ b/src/test/run-pass/impl-trait/example-st.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-test + #![feature(conservative_impl_trait)] struct State; diff --git a/src/test/run-pass/impl-trait/lifetime-generic-scoping.rs b/src/test/run-pass/impl-trait/lifetime-generic-scoping.rs new file mode 100644 index 0000000000000..1b032c3165a09 --- /dev/null +++ b/src/test/run-pass/impl-trait/lifetime-generic-scoping.rs @@ -0,0 +1,28 @@ +// 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 foo<'a>(x: &'a u32) -> impl Debug { *x } +fn foo_elided(x: &u32) -> impl Debug { *x } + +fn main() { + // Make sure that the lifetime parameter of `foo` isn't included in `foo`'s return type: + let _ = { + let x = 5; + foo(&x) + }; + let _ = { + let x = 5; + foo_elided(&x) + }; +}