Skip to content

Commit

Permalink
Correctly lower bounds on GATs
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewjasper committed Dec 20, 2019
1 parent 4b164f6 commit 6394032
Showing 1 changed file with 100 additions and 19 deletions.
119 changes: 100 additions & 19 deletions src/librustc_typeck/collect.rs
Expand Up @@ -2300,25 +2300,7 @@ fn explicit_predicates_of(
// Add predicates from associated type bounds.
if let Some((self_trait_ref, trait_items)) = is_trait {
predicates.extend(trait_items.iter().flat_map(|trait_item_ref| {
let trait_item = tcx.hir().trait_item(trait_item_ref.id);
let bounds = match trait_item.kind {
hir::TraitItemKind::Type(ref bounds, _) => bounds,
_ => return Vec::new().into_iter()
};

let assoc_ty =
tcx.mk_projection(tcx.hir().local_def_id(trait_item.hir_id),
self_trait_ref.substs);

let bounds = AstConv::compute_bounds(
&ItemCtxt::new(tcx, def_id),
assoc_ty,
bounds,
SizedByDefault::Yes,
trait_item.span,
);

bounds.predicates(tcx, assoc_ty).into_iter()
associated_item_predicates(tcx, def_id, self_trait_ref, trait_item_ref)
}))
}

Expand Down Expand Up @@ -2352,6 +2334,105 @@ fn explicit_predicates_of(
result
}

fn associated_item_predicates(
tcx: TyCtxt<'tcx>,
def_id: DefId,
self_trait_ref: ty::TraitRef<'tcx>,
trait_item_ref: &hir::TraitItemRef,
) -> Vec<(ty::Predicate<'tcx>, Span)> {
let trait_item = tcx.hir().trait_item(trait_item_ref.id);
let item_def_id = tcx.hir().local_def_id(trait_item_ref.id.hir_id);
let bounds = match trait_item.kind {
hir::TraitItemKind::Type(ref bounds, _) => bounds,
_ => return Vec::new()
};

let is_gat = !tcx.generics_of(item_def_id).params.is_empty();

let mut had_error = false;

let mut unimplemented_error = |arg_kind: &str| {
if !had_error {
tcx.sess.struct_span_err(
trait_item.span,
&format!("{}-generic associated types are not yet implemented", arg_kind),
)
.note("for more information, see https://github.com/rust-lang/rust/issues/44265")
.emit();
had_error = true;
}
};

let mk_bound_param = |param: &ty::GenericParamDef, _: &_| {
match param.kind {
ty::GenericParamDefKind::Lifetime => {
tcx.mk_region(ty::RegionKind::ReLateBound(
ty::INNERMOST,
ty::BoundRegion::BrNamed(param.def_id, param.name)
)).into()
}
// FIXME(generic_associated_types): Use bound types and constants
// once they are handled by the trait system.
ty::GenericParamDefKind::Type { .. } => {
unimplemented_error("type");
tcx.types.err.into()
}
ty::GenericParamDefKind::Const => {
unimplemented_error("const");
tcx.consts.err.into()
}
}
};

let bound_substs = if is_gat {
// Given:
//
// trait X<'a, B, const C: usize> {
// type T<'d, E, const F: usize>: Default;
// }
//
// We need to create predicates on the trait:
//
// for<'d, E, const F: usize>
// <Self as X<'a, B, const C: usize>>::T<'d, E, const F: usize>: Sized + Default
//
// We substitute escaping bound parameters for the generic
// arguments to the associated type which are then bound by
// the `Binder` around the the predicate.
//
// FIXME(generic_associated_types): Currently only lifetimes are handled.
self_trait_ref.substs.extend_to(tcx, item_def_id, mk_bound_param)
} else {
self_trait_ref.substs
};

let assoc_ty = tcx.mk_projection(
tcx.hir().local_def_id(trait_item.hir_id),
bound_substs,
);

let bounds = AstConv::compute_bounds(
&ItemCtxt::new(tcx, def_id),
assoc_ty,
bounds,
SizedByDefault::Yes,
trait_item.span,
);

let predicates = bounds.predicates(tcx, assoc_ty);

if is_gat {
// We use shifts to get the regions that we're substituting to
// be bound by the binders in the `Predicate`s rather that
// escaping.
let shifted_in = ty::fold::shift_vars(tcx, &predicates, 1);
let substituted = shifted_in.subst(tcx, bound_substs);
ty::fold::shift_out_vars(tcx, &substituted, 1)
} else {
predicates
}
}

/// Converts a specific `GenericBound` from the AST into a set of
/// predicates that apply to the self type. A vector is returned
/// because this can be anywhere from zero predicates (`T: ?Sized` adds no
Expand Down

0 comments on commit 6394032

Please sign in to comment.