Auto merge of #46754 - cramertj:refactor-arg-impl, r=nikomatsakis

Refactor argument-position impl Trait

Fixes https://github.com/rust-lang/rust/issues/46685, https://github.com/rust-lang/rust/issues/46470

r? @nikomatsakis
cc @chrisvittal
This commit is contained in:
bors 2017-12-21 08:04:57 +00:00
commit ab7abfcf34
10 changed files with 92 additions and 185 deletions

View file

@ -602,9 +602,6 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
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);
}
TyTypeof(expression) => {
visitor.visit_nested_body(expression)
}

View file

@ -61,6 +61,7 @@ use syntax::attr;
use syntax::ast::*;
use syntax::errors;
use syntax::ext::hygiene::{Mark, SyntaxContext};
use syntax::print::pprust;
use syntax::ptr::P;
use syntax::codemap::{self, respan, Spanned, CompilerDesugaringKind};
use syntax::std_inject;
@ -106,6 +107,13 @@ pub struct LoweringContext<'a> {
is_in_loop_condition: bool,
is_in_trait_impl: bool,
// This is a list of in-band type definitions being generated by
// Argument-position `impl Trait`.
// When traversing a signature such as `fn foo(x: impl Trait)`,
// we record `impl Trait` as a new type parameter, then later
// add it on to `foo`s generics.
in_band_ty_params: Vec<hir::TyParam>,
// Used to create lifetime definitions from in-band lifetime usages.
// e.g. `fn foo(x: &'x u8) -> &'x u8` to `fn foo<'x>(x: &'x u8) -> &'x u8`
// When a named lifetime is encountered in a function or impl header and
@ -197,6 +205,7 @@ pub fn lower_crate(sess: &Session,
node_id_to_hir_id: IndexVec::new(),
is_generator: false,
is_in_trait_impl: false,
in_band_ty_params: Vec::new(),
lifetimes_to_define: Vec::new(),
is_collecting_in_band_lifetimes: false,
in_scope_lifetimes: Vec::new(),
@ -526,20 +535,23 @@ impl<'a> LoweringContext<'a> {
// Creates a new hir::LifetimeDef for every new lifetime encountered
// while evaluating `f`. Definitions are created with the parent provided.
// If no `parent_id` is provided, no definitions will be returned.
fn collect_in_band_lifetime_defs<T, F>(
fn collect_in_band_defs<T, F>(
&mut self,
parent_id: Option<DefId>,
f: F
) -> (Vec<hir::LifetimeDef>, T) where F: FnOnce(&mut LoweringContext) -> T
) -> (Vec<hir::TyParam>, Vec<hir::LifetimeDef>, T) where F: FnOnce(&mut LoweringContext) -> T
{
assert!(!self.is_collecting_in_band_lifetimes);
assert!(self.lifetimes_to_define.is_empty());
self.is_collecting_in_band_lifetimes = self.sess.features.borrow().in_band_lifetimes;
assert!(self.in_band_ty_params.is_empty());
let res = f(self);
self.is_collecting_in_band_lifetimes = false;
let in_band_ty_params = self.in_band_ty_params.split_off(0);
let lifetimes_to_define = self.lifetimes_to_define.split_off(0);
let lifetime_defs = match parent_id {
@ -569,7 +581,7 @@ impl<'a> LoweringContext<'a> {
None => Vec::new(),
};
(lifetime_defs, res)
(in_band_ty_params, lifetime_defs, res)
}
// Evaluates `f` with the lifetimes in `lt_defs` in-scope.
@ -613,11 +625,9 @@ impl<'a> LoweringContext<'a> {
res
}
// Appends in-band lifetime defs to the existing set of out-of-band lifetime defs.
// Evaluates all within the context of the out-of-band defs.
// If provided, `impl_item_id` is used to find the parent impls of impl items so
// that their generics are not duplicated.
fn add_in_band_lifetime_defs<F, T>(
// Appends in-band lifetime defs and argument-position `impl Trait` defs
// to the existing set of generics.
fn add_in_band_defs<F, T>(
&mut self,
generics: &Generics,
parent_id: Option<DefId>,
@ -625,17 +635,23 @@ impl<'a> LoweringContext<'a> {
) -> (hir::Generics, T)
where F: FnOnce(&mut LoweringContext) -> T
{
let (in_band_defs, (mut lowered_generics, res)) =
let (in_band_ty_defs, in_band_lifetime_defs, (mut lowered_generics, res)) =
self.with_in_scope_lifetime_defs(&generics.lifetimes, |this| {
this.collect_in_band_lifetime_defs(parent_id, |this| {
this.collect_in_band_defs(parent_id, |this| {
(this.lower_generics(generics), f(this))
})
});
lowered_generics.ty_params =
lowered_generics.ty_params
.iter().cloned()
.chain(in_band_ty_defs.into_iter())
.collect();
lowered_generics.lifetimes =
lowered_generics.lifetimes
.iter().cloned()
.chain(in_band_defs.into_iter())
.chain(in_band_lifetime_defs.into_iter())
.collect();
(lowered_generics, res)
@ -922,6 +938,7 @@ impl<'a> LoweringContext<'a> {
}
TyKind::ImplTrait(ref bounds) => {
use syntax::feature_gate::{emit_feature_err, GateIssue};
let span = t.span;
match itctx {
ImplTraitContext::Existential => {
let has_feature = self.sess.features.borrow().conservative_impl_trait;
@ -944,7 +961,7 @@ impl<'a> LoweringContext<'a> {
id: self.next_id().node_id,
predicates: Vec::new().into(),
},
span: t.span,
span,
},
bounds: hir_bounds,
}, lifetimes)
@ -956,7 +973,35 @@ impl<'a> LoweringContext<'a> {
t.span, GateIssue::Language,
"`impl Trait` in argument position is experimental");
}
hir::TyImplTraitUniversal(def_id, self.lower_bounds(bounds, itctx))
let def_node_id = self.next_id().node_id;
// Add a definition for the in-band TyParam
let def_index = self.resolver.definitions().create_def_with_parent(
def_id.index,
def_node_id,
DefPathData::ImplTrait,
DefIndexAddressSpace::High,
Mark::root()
);
let hir_bounds = self.lower_bounds(bounds, itctx);
self.in_band_ty_params.push(hir::TyParam {
// Set the name to `impl Bound1 + Bound2`
name: Symbol::intern(&pprust::ty_to_string(t)),
id: def_node_id,
bounds: hir_bounds,
default: None,
span,
pure_wrt_drop: false,
synthetic: Some(hir::SyntheticTyParamKind::ImplTrait),
});
hir::TyPath(hir::QPath::Resolved(None, P(hir::Path {
span,
def: Def::TyParam(DefId::local(def_index)),
segments: vec![].into(),
})))
},
ImplTraitContext::Disallowed => {
span_err!(self.sess, t.span, E0562,
@ -1829,7 +1874,7 @@ impl<'a> LoweringContext<'a> {
this.expr_block(body, ThinVec::new())
});
let (generics, fn_decl) =
this.add_in_band_lifetime_defs(generics, fn_def_id, |this|
this.add_in_band_defs(generics, fn_def_id, |this|
this.lower_fn_decl(decl, fn_def_id, true));
hir::ItemFn(fn_decl,
@ -1883,7 +1928,7 @@ impl<'a> LoweringContext<'a> {
ref impl_items) => {
let def_id = self.resolver.definitions().opt_local_def_id(id);
let (generics, (ifce, lowered_ty)) =
self.add_in_band_lifetime_defs(ast_generics, def_id, |this| {
self.add_in_band_defs(ast_generics, def_id, |this| {
let ifce = ifce.as_ref().map(|trait_ref| {
this.lower_trait_ref(trait_ref, ImplTraitContext::Disallowed)
});
@ -2059,7 +2104,7 @@ impl<'a> LoweringContext<'a> {
}
TraitItemKind::Method(ref sig, None) => {
let names = this.lower_fn_args_to_names(&sig.decl);
this.add_in_band_lifetime_defs(&i.generics, fn_def_id, |this|
this.add_in_band_defs(&i.generics, fn_def_id, |this|
hir::TraitItemKind::Method(
this.lower_method_sig(sig, fn_def_id, false),
hir::TraitMethod::Required(names)))
@ -2070,7 +2115,7 @@ impl<'a> LoweringContext<'a> {
this.expr_block(body, ThinVec::new())
});
this.add_in_band_lifetime_defs(&i.generics, fn_def_id, |this|
this.add_in_band_defs(&i.generics, fn_def_id, |this|
hir::TraitItemKind::Method(
this.lower_method_sig(sig, fn_def_id, false),
hir::TraitMethod::Provided(body_id)))
@ -2147,7 +2192,7 @@ impl<'a> LoweringContext<'a> {
});
let impl_trait_return_allow = !this.is_in_trait_impl;
this.add_in_band_lifetime_defs(&i.generics, fn_def_id, |this|
this.add_in_band_defs(&i.generics, fn_def_id, |this|
hir::ImplItemKind::Method(
this.lower_method_sig(sig, fn_def_id, impl_trait_return_allow),
body_id))
@ -2280,7 +2325,7 @@ impl<'a> LoweringContext<'a> {
ForeignItemKind::Fn(ref fdec, ref generics) => {
// Disallow impl Trait in foreign items
let (generics, (fn_dec, fn_args)) =
this.add_in_band_lifetime_defs(
this.add_in_band_defs(
generics,
Some(def_id),
|this| (

View file

@ -1511,7 +1511,7 @@ pub enum Ty_ {
/// A trait object type `Bound1 + Bound2 + Bound3`
/// where `Bound` is a trait or a lifetime.
TyTraitObject(HirVec<PolyTraitRef>, Lifetime),
/// An exsitentially quantified (there exists a type satisfying) `impl
/// An existentially quantified (there exists a type satisfying) `impl
/// Bound1 + Bound2 + Bound3` type where `Bound` is a trait or a lifetime.
///
/// The `ExistTy` structure emulates an
@ -1523,9 +1523,6 @@ pub enum Ty_ {
/// because all in-scope type parameters are captured by `impl Trait`,
/// so they are resolved directly through the parent `Generics`.
TyImplTraitExistential(ExistTy, HirVec<Lifetime>),
/// An universally quantified (for all types satisfying) `impl
/// Bound1 + Bound2 + Bound3` type where `Bound` is a trait or a lifetime.
TyImplTraitUniversal(DefId, TyParamBounds),
/// Unused for now
TyTypeof(BodyId),
/// TyInfer means the type should be inferred instead of it having been

View file

@ -425,9 +425,6 @@ impl<'a> State<'a> {
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) => {
self.s.word("[")?;
self.print_type(&ty)?;

View file

@ -312,7 +312,6 @@ impl_stable_hash_for!(enum hir::Ty_ {
TyPath(qpath),
TyTraitObject(trait_refs, lifetime),
TyImplTraitExistential(existty, lifetimes),
TyImplTraitUniversal(def_id, bounds),
TyTypeof(body_id),
TyErr,
TyInfer

View file

@ -2212,19 +2212,6 @@ fn insert_late_bound_lifetimes(
visit_where_predicate,
&generics.where_clause.predicates
);
// We need to collect argument impl Trait lifetimes as well,
// we do so here.
walk_list!(
&mut appears_in_where_clause,
visit_ty,
decl.inputs
.iter()
.filter(|ty| if let hir::TyImplTraitUniversal(..) = ty.node {
true
} else {
false
})
);
for lifetime_def in &generics.lifetimes {
if !lifetime_def.bounds.is_empty() {
// `'a: 'b` means both `'a` and `'b` are referenced

View file

@ -30,7 +30,6 @@ use util::nodemap::FxHashSet;
use std::iter;
use syntax::{abi, ast};
use syntax::symbol::Symbol;
use syntax::feature_gate::{GateIssue, emit_feature_err};
use syntax_pos::Span;
@ -1066,13 +1065,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
let def_id = tcx.hir.local_def_id(ast_ty.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);
let generics = tcx.generics_of(fn_def_id);
let index = generics.type_param_to_index[&impl_trait_def_id.index];
tcx.mk_param(index,
Symbol::intern(&tcx.hir.node_to_pretty_string(ast_ty.id)))
}
hir::TyPath(hir::QPath::Resolved(ref maybe_qself, ref path)) => {
debug!("ast_ty_to_ty: maybe_qself={:?} path={:?}", maybe_qself, path);
let opt_self_ty = maybe_qself.as_ref().map(|qself| {

View file

@ -43,7 +43,6 @@ use rustc_const_math::ConstInt;
use std::collections::BTreeMap;
use syntax::{abi, ast};
use syntax::ptr::P;
use syntax::codemap::Spanned;
use syntax::symbol::{Symbol, keywords};
use syntax_pos::{Span, DUMMY_SP};
@ -880,32 +879,22 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let mut allow_defaults = false;
let no_generics = hir::Generics::empty();
let (ast_generics, opt_inputs) = match node {
NodeTraitItem(item) => {
match item.node {
TraitItemKind::Method(ref sig, _) => (&item.generics, Some(&sig.decl.inputs)),
_ => (&item.generics, None)
}
}
let ast_generics = match node {
NodeTraitItem(item) => &item.generics,
NodeImplItem(item) => {
match item.node {
ImplItemKind::Method(ref sig, _) => (&item.generics, Some(&sig.decl.inputs)),
_ => (&item.generics, None)
}
}
NodeImplItem(item) => &item.generics,
NodeItem(item) => {
match item.node {
ItemFn(ref decl, .., ref generics, _) => (generics, Some(&decl.inputs)),
ItemImpl(_, _, _, ref generics, ..) => (generics, None),
ItemFn(.., ref generics, _) |
ItemImpl(_, _, _, ref generics, ..) => generics,
ItemTy(_, ref generics) |
ItemEnum(_, ref generics) |
ItemStruct(_, ref generics) |
ItemUnion(_, ref generics) => {
allow_defaults = true;
(generics, None)
generics
}
ItemTrait(_, _, ref generics, ..) | ItemTraitAlias(ref generics, ..) => {
@ -926,26 +915,26 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
});
allow_defaults = true;
(generics, None)
generics
}
_ => (&no_generics, None)
_ => &no_generics,
}
}
NodeForeignItem(item) => {
match item.node {
ForeignItemStatic(..) => (&no_generics, None),
ForeignItemFn(ref decl, _, ref generics) => (generics, Some(&decl.inputs)),
ForeignItemType => (&no_generics, None)
ForeignItemStatic(..) => &no_generics,
ForeignItemFn(_, _, ref generics) => generics,
ForeignItemType => &no_generics,
}
}
NodeTy(&hir::Ty { node: hir::TyImplTraitExistential(ref exist_ty, _), .. }) => {
(&exist_ty.generics, None)
&exist_ty.generics
}
_ => (&no_generics, None)
_ => &no_generics,
};
let has_self = opt_self.is_some();
@ -1003,23 +992,7 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}
});
let fn_ins = opt_inputs.map(|tys| &tys[..]);
let univ_impl_trait_info = extract_universal_impl_trait_info(tcx, fn_ins);
let other_type_start = type_start + ast_generics.ty_params.len() as u32;
let mut types: Vec<_> = opt_self.into_iter()
.chain(types)
.chain(univ_impl_trait_info.iter().enumerate().map(|(i, info)| {
ty::TypeParameterDef {
index: other_type_start + i as u32,
name: Symbol::intern(&tcx.hir.node_to_pretty_string(info.id)),
def_id: info.def_id,
has_default: false,
object_lifetime_default: rl::Set1::Empty,
pure_wrt_drop: false,
synthetic: Some(SyntheticTyParamKind::ImplTrait),
}
}))
.collect();
let mut types: Vec<_> = opt_self.into_iter().chain(types).collect();
// provide junk type parameter defs - the only place that
// cares about anything but the length is instantiation,
@ -1419,50 +1392,36 @@ fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let icx = ItemCtxt::new(tcx, def_id);
let no_generics = hir::Generics::empty();
let (ast_generics, opt_inputs) = match node {
NodeTraitItem(item) => {
match item.node {
TraitItemKind::Method(ref sig, _) => (&item.generics, Some(&sig.decl.inputs)),
_ => (&item.generics, None)
}
}
NodeImplItem(item) => {
match item.node {
ImplItemKind::Method(ref sig, _) => (&item.generics, Some(&sig.decl.inputs)),
_ => (&item.generics, None)
}
}
let ast_generics = match node {
NodeTraitItem(item) => &item.generics,
NodeImplItem(item) => &item.generics,
NodeItem(item) => {
match item.node {
ItemFn(ref decl, .., ref generics, _) => (generics, Some(&decl.inputs)),
ItemFn(.., ref generics, _) |
ItemImpl(_, _, _, ref generics, ..) |
ItemTy(_, ref generics) |
ItemEnum(_, ref generics) |
ItemStruct(_, ref generics) |
ItemUnion(_, ref generics) => {
(generics, None)
}
ItemUnion(_, ref generics) => generics,
ItemTrait(_, _, ref generics, .., ref items) => {
is_trait = Some((ty::TraitRef {
def_id,
substs: Substs::identity_for_item(tcx, def_id)
}, items));
(generics, None)
generics
}
_ => (&no_generics, None)
_ => &no_generics,
}
}
NodeForeignItem(item) => {
match item.node {
ForeignItemStatic(..) => (&no_generics, None),
ForeignItemFn(ref decl, _, ref generics) => (generics, Some(&decl.inputs)),
ForeignItemType => (&no_generics, None),
ForeignItemStatic(..) => &no_generics,
ForeignItemFn(_, _, ref generics) => generics,
ForeignItemType => &no_generics,
}
}
@ -1491,7 +1450,7 @@ fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
};
}
_ => (&no_generics, None)
_ => &no_generics,
};
let generics = tcx.generics_of(def_id);
@ -1622,19 +1581,6 @@ fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}))
}
// Add predicates from impl Trait arguments
let fn_ins = opt_inputs.map(|tys| &tys[..]);
let univ_impl_trait_info = extract_universal_impl_trait_info(tcx, fn_ins);
for info in univ_impl_trait_info.iter() {
let name = keywords::Invalid.name();
let param_ty = ty::ParamTy::new(index, name).to_ty(tcx);
index += 1;
let bounds = compute_bounds(&icx, param_ty, info.bounds,
SizedByDefault::Yes,
info.span);
predicates.extend(bounds.predicates(tcx, param_ty));
}
// Subtle: before we store the predicates into the tcx, we
// sort them so that predicates like `T: Foo<Item=U>` come
// before uses of `U`. This avoids false ambiguity errors
@ -1795,54 +1741,3 @@ fn is_auto_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
_ => bug!("is_auto_impl applied to non-local def-id {:?}", def_id)
}
}
struct ImplTraitUniversalInfo<'hir> {
id: ast::NodeId,
def_id: DefId,
span: Span,
bounds: &'hir [hir::TyParamBound],
}
/// Take some possible list of arguments and return the DefIds of the ImplTraitUniversal
/// arguments
fn extract_universal_impl_trait_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
opt_inputs: Option<&'tcx [P<hir::Ty>]>)
-> Vec<ImplTraitUniversalInfo<'tcx>>
{
// A visitor for simply collecting Universally quantified impl Trait arguments
struct ImplTraitUniversalVisitor<'tcx> {
items: Vec<&'tcx hir::Ty>
}
impl<'tcx> Visitor<'tcx> for ImplTraitUniversalVisitor<'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::None
}
fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
if let hir::TyImplTraitUniversal(..) = ty.node {
self.items.push(ty);
}
intravisit::walk_ty(self, ty);
}
}
let mut visitor = ImplTraitUniversalVisitor { items: Vec::new() };
if let Some(inputs) = opt_inputs {
for t in inputs.iter() {
visitor.visit_ty(t);
}
}
visitor.items.into_iter().map(|ty| if let hir::TyImplTraitUniversal(_, ref bounds) = ty.node {
ImplTraitUniversalInfo {
id: ty.id,
def_id: tcx.hir.local_def_id(ty.id),
span: ty.span,
bounds: bounds
}
} else {
span_bug!(ty.span, "this type should be a universally quantified impl trait. this is a bug")
}).collect()
}

View file

@ -2154,7 +2154,6 @@ impl Clean<Type> for hir::Ty {
}
TyBareFn(ref barefn) => BareFunction(box barefn.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),
}

View file

@ -57,9 +57,8 @@ fn pass_through_elision_with_fn_path<T: Fn(&u32) -> &u32>(
x: &T
) -> impl Into<&impl Fn(&u32) -> &u32> { x }
// FIXME(cramertj) Currently ICEing, part of issue #46685:
// fn foo(x: &impl Debug) -> impl Into<&impl Debug> { x }
// Works:
fn foo(x: &impl Debug) -> impl Into<&impl Debug> { x }
fn foo_explicit_lifetime<'a>(x: &'a impl Debug) -> impl Into<&'a impl Debug> { x }
fn foo_no_outer_impl(x: &impl Debug) -> &impl Debug { x }
fn foo_explicit_arg<T: Debug>(x: &T) -> impl Into<&impl Debug> { x }