Use both existential-type desugaring and where-clause (predicate) desugaring depending on context.

This commit is contained in:
Alexander Regueiro 2019-03-21 17:55:09 +00:00
parent aaa53ec853
commit 01f49f0bb2
4 changed files with 173 additions and 87 deletions

View File

@ -106,6 +106,7 @@ pub struct LoweringContext<'a> {
loop_scopes: Vec<NodeId>,
is_in_loop_condition: bool,
is_in_trait_impl: bool,
is_in_dyn_type: bool,
/// What to do when we encounter either an "anonymous lifetime
/// reference". The term "anonymous" is meant to encompass both
@ -195,12 +196,6 @@ enum ImplTraitContext<'a> {
/// (e.g., for consts and statics).
Existential(Option<DefId> /* fn def-ID */),
/// Treat `impl Trait` as a bound on the associated type applied to the trait.
/// Example: `trait Foo { type Bar: Iterator<Item = impl Debug>; }` is conceptually
/// equivalent to `trait Foo where <Self::Bar as Iterator>::Item: Debug
/// { type Bar: Iterator; }`.
AssociatedTy,
/// `impl Trait` is not accepted in this position.
Disallowed(ImplTraitPosition),
}
@ -208,7 +203,10 @@ enum ImplTraitContext<'a> {
/// Position in which `impl Trait` is disallowed. Used for error reporting.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum ImplTraitPosition {
/// Disallowed in `let` / `const` / `static` bindings.
Binding,
/// All other posiitons.
Other,
}
@ -223,7 +221,6 @@ impl<'a> ImplTraitContext<'a> {
match self {
Universal(params) => Universal(params),
Existential(fn_def_id) => Existential(*fn_def_id),
AssociatedTy => AssociatedTy,
Disallowed(pos) => Disallowed(*pos),
}
}
@ -256,6 +253,8 @@ pub fn lower_crate(
catch_scopes: Vec::new(),
loop_scopes: Vec::new(),
is_in_loop_condition: false,
is_in_trait_impl: false,
is_in_dyn_type: false,
anonymous_lifetime_mode: AnonymousLifetimeMode::PassThrough,
type_def_lifetime_params: Default::default(),
current_module: CRATE_NODE_ID,
@ -265,7 +264,6 @@ pub fn lower_crate(
is_generator: false,
is_async_body: false,
current_item: None,
is_in_trait_impl: false,
lifetimes_to_define: Vec::new(),
is_collecting_in_band_lifetimes: false,
in_scope_lifetimes: Vec::new(),
@ -1230,6 +1228,20 @@ impl<'a> LoweringContext<'a> {
result
}
fn with_dyn_type_scope<T, F>(&mut self, in_scope: bool, f: F) -> T
where
F: FnOnce(&mut LoweringContext<'_>) -> T,
{
let was_in_dyn_type = self.is_in_dyn_type;
self.is_in_dyn_type = in_scope;
let result = f(self);
self.is_in_dyn_type = was_in_dyn_type;
result
}
fn with_new_scopes<T, F>(&mut self, f: F) -> T
where
F: FnOnce(&mut LoweringContext<'_>) -> T,
@ -1353,24 +1365,58 @@ impl<'a> LoweringContext<'a> {
c: &AssocTyConstraint,
itctx: ImplTraitContext<'_>)
-> hir::TypeBinding {
debug!("lower_assoc_ty_constraint(constraint={:?}, itctx={:?})", c, itctx);
let ty = match c.kind {
AssocTyConstraintKind::Equality { ref ty } => self.lower_ty(ty, itctx),
AssocTyConstraintKind::Bound { ref bounds } => {
// Desugar `AssocTy: Bounds` into `AssocTy = impl Bounds`.
let impl_ty_node_id = self.sess.next_node_id();
let parent_def_index = self.current_hir_id_owner.last().unwrap().0;
self.resolver.definitions().create_def_with_parent(
parent_def_index,
impl_ty_node_id,
DefPathData::Misc,
DefIndexAddressSpace::High,
Mark::root(),
DUMMY_SP);
self.lower_ty(&Ty {
id: self.sess.next_node_id(),
node: TyKind::ImplTrait(impl_ty_node_id, bounds.clone()),
span: DUMMY_SP,
}, itctx)
let (existential_desugaring, itctx) = match itctx {
ImplTraitContext::Existential(_) => (true, itctx),
ImplTraitContext::Universal(_) if self.is_in_dyn_type => (true, itctx),
// FIXME: this is only needed until `impl Trait` is allowed in type aliases.
ImplTraitContext::Disallowed(_) if self.is_in_dyn_type =>
(true, ImplTraitContext::Existential(None)),
_ => (false, itctx),
};
if existential_desugaring {
// Desugar `AssocTy: Bounds` into `AssocTy = impl Bounds`.
let impl_ty_node_id = self.sess.next_node_id();
let parent_def_index = self.current_hir_id_owner.last().unwrap().0;
self.resolver.definitions().create_def_with_parent(
parent_def_index,
impl_ty_node_id,
DefPathData::Misc,
DefIndexAddressSpace::High,
Mark::root(),
DUMMY_SP
);
self.with_dyn_type_scope(false, |this| {
this.lower_ty(
&Ty {
id: this.sess.next_node_id(),
node: TyKind::ImplTrait(impl_ty_node_id, bounds.clone()),
span: DUMMY_SP,
},
itctx,
)
})
} else {
// Desugar `AssocTy: Bounds` into `AssocTy = ∃ T (T: Bounds)`, where the
// "false existential" later desugars into a trait predicate.
let bounds = self.lower_param_bounds(bounds, itctx);
let id = self.sess.next_node_id();
let LoweredNodeId { node_id: _, hir_id } = self.lower_node_id(id);
P(hir::Ty {
hir_id,
node: hir::TyKind::AssocTyExistential(bounds),
span: DUMMY_SP,
})
}
}
};
@ -1477,23 +1523,26 @@ impl<'a> LoweringContext<'a> {
}
TyKind::TraitObject(ref bounds, kind) => {
let mut lifetime_bound = None;
let bounds = bounds
.iter()
.filter_map(|bound| match *bound {
GenericBound::Trait(ref ty, TraitBoundModifier::None) => {
Some(self.lower_poly_trait_ref(ty, itctx.reborrow()))
}
GenericBound::Trait(_, TraitBoundModifier::Maybe) => None,
GenericBound::Outlives(ref lifetime) => {
if lifetime_bound.is_none() {
lifetime_bound = Some(self.lower_lifetime(lifetime));
let (bounds, lifetime_bound) = self.with_dyn_type_scope(true, |this| {
let bounds = bounds
.iter()
.filter_map(|bound| match *bound {
GenericBound::Trait(ref ty, TraitBoundModifier::None) => {
Some(this.lower_poly_trait_ref(ty, itctx.reborrow()))
}
None
}
})
.collect();
let lifetime_bound =
lifetime_bound.unwrap_or_else(|| self.elided_dyn_bound(t.span));
GenericBound::Trait(_, TraitBoundModifier::Maybe) => None,
GenericBound::Outlives(ref lifetime) => {
if lifetime_bound.is_none() {
lifetime_bound = Some(this.lower_lifetime(lifetime));
}
None
}
})
.collect();
let lifetime_bound =
lifetime_bound.unwrap_or_else(|| this.elided_dyn_bound(t.span));
(bounds, lifetime_bound)
});
if kind != TraitObjectSyntax::Dyn {
self.maybe_lint_bare_trait(t.span, t.id, false);
}
@ -1544,16 +1593,6 @@ impl<'a> LoweringContext<'a> {
}),
))
}
ImplTraitContext::AssociatedTy => {
let hir_bounds = self.lower_param_bounds(
bounds,
ImplTraitContext::AssociatedTy,
);
hir::TyKind::AssocTyExistential(
hir_bounds,
)
}
ImplTraitContext::Disallowed(pos) => {
let allowed_in = if self.sess.features_untracked()
.impl_trait_in_bindings {
@ -2407,7 +2446,8 @@ impl<'a> LoweringContext<'a> {
FunctionRetTy::Ty(ref ty) => match in_band_ty_params {
Some((def_id, _)) if impl_trait_return_allow => {
hir::Return(self.lower_ty(ty,
ImplTraitContext::Existential(Some(def_id))))
ImplTraitContext::Existential(Some(def_id))
))
}
_ => {
hir::Return(self.lower_ty(ty, ImplTraitContext::disallowed()))
@ -2770,7 +2810,7 @@ impl<'a> LoweringContext<'a> {
let kind = hir::GenericParamKind::Type {
default: default.as_ref().map(|x| {
self.lower_ty(x, ImplTraitContext::disallowed())
self.lower_ty(x, ImplTraitContext::Existential(None))
}),
synthetic: param.attrs.iter()
.filter(|attr| attr.check_name(sym::rustc_synthetic))
@ -3275,39 +3315,43 @@ impl<'a> LoweringContext<'a> {
ItemKind::ForeignMod(ref nm) => hir::ItemKind::ForeignMod(self.lower_foreign_mod(nm)),
ItemKind::GlobalAsm(ref ga) => hir::ItemKind::GlobalAsm(self.lower_global_asm(ga)),
ItemKind::Ty(ref t, ref generics) => hir::ItemKind::Ty(
self.lower_ty(t, ImplTraitContext::AssociatedTy),
self.lower_generics(generics, ImplTraitContext::AssociatedTy),
self.lower_ty(t, ImplTraitContext::disallowed()),
self.lower_generics(generics, ImplTraitContext::disallowed()),
),
ItemKind::Existential(ref b, ref generics) => hir::ItemKind::Existential(
hir::ExistTy {
generics: self.lower_generics(generics, ImplTraitContext::AssociatedTy),
bounds: self.lower_param_bounds(b, ImplTraitContext::AssociatedTy),
generics: self.lower_generics(generics,
ImplTraitContext::Existential(None)),
bounds: self.lower_param_bounds(b,
ImplTraitContext::Existential(None)),
impl_trait_fn: None,
origin: hir::ExistTyOrigin::ExistentialType,
},
),
ItemKind::Enum(ref enum_definition, ref generics) => hir::ItemKind::Enum(
hir::EnumDef {
variants: enum_definition
.variants
.iter()
.map(|x| self.lower_variant(x))
.collect(),
},
self.lower_generics(generics, ImplTraitContext::AssociatedTy),
),
ItemKind::Enum(ref enum_definition, ref generics) => {
hir::ItemKind::Enum(
hir::EnumDef {
variants: enum_definition
.variants
.iter()
.map(|x| self.lower_variant(x))
.collect(),
},
self.lower_generics(generics, ImplTraitContext::disallowed()),
)
},
ItemKind::Struct(ref struct_def, ref generics) => {
let struct_def = self.lower_variant_data(struct_def);
hir::ItemKind::Struct(
struct_def,
self.lower_generics(generics, ImplTraitContext::AssociatedTy),
self.lower_generics(generics, ImplTraitContext::disallowed()),
)
}
ItemKind::Union(ref vdata, ref generics) => {
let vdata = self.lower_variant_data(vdata);
hir::ItemKind::Union(
vdata,
self.lower_generics(generics, ImplTraitContext::AssociatedTy),
self.lower_generics(generics, ImplTraitContext::disallowed()),
)
}
ItemKind::Impl(
@ -3675,9 +3719,9 @@ impl<'a> LoweringContext<'a> {
(generics, hir::TraitItemKind::Method(sig, hir::TraitMethod::Provided(body_id)))
}
TraitItemKind::Type(ref bounds, ref default) => {
let generics = self.lower_generics(&i.generics, ImplTraitContext::AssociatedTy);
let generics = self.lower_generics(&i.generics, ImplTraitContext::disallowed());
let node = hir::TraitItemKind::Type(
self.lower_param_bounds(bounds, ImplTraitContext::AssociatedTy),
self.lower_param_bounds(bounds, ImplTraitContext::disallowed()),
default
.as_ref()
.map(|x| self.lower_ty(x, ImplTraitContext::disallowed())),

View File

@ -63,6 +63,10 @@ struct AstValidator<'a> {
/// or `Foo::Bar<impl Trait>`
is_impl_trait_banned: bool,
/// Used to ban associated type bounds (i.e., `Type<AssocType: Bounds>`) in
/// certain positions.
is_assoc_ty_bound_banned: bool,
/// rust-lang/rust#57979: the ban of nested `impl Trait` was buggy
/// until PRs #57730 and #57981 landed: it would jump directly to
/// walk_ty rather than visit_ty (or skip recurring entirely for
@ -87,6 +91,12 @@ impl<'a> AstValidator<'a> {
self.is_impl_trait_banned = old;
}
fn with_banned_assoc_ty_bound(&mut self, f: impl FnOnce(&mut Self)) {
let old = mem::replace(&mut self.is_assoc_ty_bound_banned, true);
f(self);
self.is_assoc_ty_bound_banned = old;
}
fn with_impl_trait(&mut self, outer: Option<OuterImplTrait>, f: impl FnOnce(&mut Self)) {
let old = mem::replace(&mut self.outer_impl_trait, outer);
f(self);
@ -94,12 +104,21 @@ impl<'a> AstValidator<'a> {
}
fn visit_assoc_ty_constraint_from_generic_args(&mut self, constraint: &'a AssocTyConstraint) {
if let AssocTyConstraintKind::Equality { ref ty } = constraint.kind {
// rust-lang/rust#57979: bug in old `visit_generic_args` called
// `walk_ty` rather than `visit_ty`, skipping outer `impl Trait`
// if it happened to occur at `ty`.
if let TyKind::ImplTrait(..) = ty.node {
self.warning_period_57979_didnt_record_next_impl_trait = true;
match constraint.kind {
AssocTyConstraintKind::Equality { ref ty } => {
// rust-lang/rust#57979: bug in old `visit_generic_args` called
// `walk_ty` rather than `visit_ty`, skipping outer `impl Trait`
// if it happened to occur at `ty`.
if let TyKind::ImplTrait(..) = ty.node {
self.warning_period_57979_didnt_record_next_impl_trait = true;
}
}
AssocTyConstraintKind::Bound { .. } => {
if self.is_assoc_ty_bound_banned {
self.err_handler().span_err(constraint.span,
"associated type bounds are not allowed within structs, enums, or unions"
);
}
}
}
self.visit_assoc_ty_constraint(constraint);
@ -726,7 +745,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
// Type bindings such as `Item = impl Debug` in `Iterator<Item = Debug>`
// are allowed to contain nested `impl Trait`.
self.with_impl_trait(None, |this| {
walk_list!(this, visit_assoc_ty_constraint_from_generic_args, &data.constraints);
walk_list!(this, visit_assoc_ty_constraint_from_generic_args,
&data.constraints);
});
}
GenericArgs::Parenthesized(ref data) => {
@ -819,6 +839,17 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
visit::walk_poly_trait_ref(self, t, m);
}
fn visit_variant_data(&mut self, s: &'a VariantData, _: Ident,
_: &'a Generics, _: NodeId, _: Span) {
self.with_banned_assoc_ty_bound(|this| visit::walk_struct_def(this, s))
}
fn visit_enum_def(&mut self, enum_definition: &'a EnumDef,
generics: &'a Generics, item_id: NodeId, _: Span) {
self.with_banned_assoc_ty_bound(
|this| visit::walk_enum_def(this, enum_definition, generics, item_id))
}
fn visit_mac(&mut self, mac: &Spanned<Mac_>) {
// when a new macro kind is added but the author forgets to set it up for expansion
// because that's the only part that won't cause a compiler error
@ -842,6 +873,7 @@ pub fn check_crate(session: &Session, krate: &Crate) -> (bool, bool) {
has_global_allocator: false,
outer_impl_trait: None,
is_impl_trait_banned: false,
is_assoc_ty_bound_banned: false,
warning_period_57979_didnt_record_next_impl_trait: false,
warning_period_57979_impl_trait_in_proj: false,
};

View File

@ -930,13 +930,11 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
.into_iter()
.map(|r| (self.ast_region_to_region(r, None), r.span))
);
bounds.trait_bounds.sort_by_key(|(t, _)| t.def_id());
}
/// Translates the AST's notion of ty param bounds (which are an enum consisting of a newtyped `Ty`
/// or a region) to ty's notion of ty param bounds, which can either be user-defined traits or the
/// built-in trait `Send`.
/// Translates the AST's notion of ty param bounds (which are an enum consisting of a newtyped
/// `Ty` or a region) to ty's notion of ty param bounds, which can either be user-defined traits
/// or the built-in trait `Send`.
pub fn compute_bounds(&self,
param_ty: Ty<'tcx>,
ast_bounds: &[hir::GenericBound],
@ -944,7 +942,10 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
span: Span,
) -> Bounds<'tcx> {
let mut bounds = Bounds::default();
self.add_bounds(param_ty, ast_bounds, &mut bounds);
bounds.trait_bounds.sort_by_key(|(t, _)| t.def_id());
bounds.implicitly_sized = if let SizedByDefault::Yes = sized_by_default {
if !self.is_unsized(ast_bounds, span) {
Some(span)
@ -954,6 +955,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
} else {
None
};
bounds
}
@ -993,7 +995,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
if let ConvertedBindingKind::Equality(ty) = binding.kind {
let late_bound_in_trait_ref = tcx.collect_constrained_late_bound_regions(&trait_ref);
let late_bound_in_trait_ref =
tcx.collect_constrained_late_bound_regions(&trait_ref);
let late_bound_in_ty =
tcx.collect_referenced_late_bound_regions(&ty::Binder::bind(ty));
debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref);
@ -1074,8 +1077,11 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
}), binding.span));
}
ConvertedBindingKind::Constraint(ref ast_bounds) => {
// Calling `skip_binder` is okay, because the predicates are re-bound later by
// `instantiate_poly_trait_ref`.
let param_ty = tcx.mk_projection(assoc_ty.def_id, candidate.skip_binder().substs);
self.add_bounds(
trait_ref.self_ty(),
param_ty,
ast_bounds,
bounds,
);
@ -2050,6 +2056,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
}
};
debug!("ast_ty_to_ty: result_ty={:?}", result_ty);
self.record_ty(ast_ty.hir_id, result_ty, ast_ty.span);
result_ty
}
@ -2135,7 +2143,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
}
}
});
debug!("impl_trait_ty_to_ty: final substs = {:?}", substs);
debug!("impl_trait_ty_to_ty: substs={:?}", substs);
let ty = tcx.mk_opaque(def_id, substs);
debug!("impl_trait_ty_to_ty: {}", ty);

View File

@ -703,7 +703,8 @@ fn super_predicates_of<'a, 'tcx>(
// Convert the bounds that follow the colon, e.g., `Bar + Zed` in `trait Foo: Bar + Zed`.
let self_param_ty = tcx.mk_self_type();
let superbounds1 = AstConv::compute_bounds(&icx, self_param_ty, bounds, SizedByDefault::No, item.span);
let superbounds1 = AstConv::compute_bounds(&icx, self_param_ty, bounds, SizedByDefault::No,
item.span);
let superbounds1 = superbounds1.predicates(tcx, self_param_ty);
@ -1988,15 +1989,16 @@ fn explicit_predicates_of<'a, 'tcx>(
tcx.def_span(def_id),
);
let bounds_predicates = bounds.predicates(tcx, opaque_ty);
if impl_trait_fn.is_some() {
// opaque types
return tcx.arena.alloc(ty::GenericPredicates {
parent: None,
predicates: bounds.predicates(tcx, opaque_ty),
predicates: bounds_predicates,
});
} else {
// named existential types
predicates.extend(bounds.predicates(tcx, opaque_ty));
predicates.extend(bounds_predicates);
generics
}
}