When pretty-printing object types, include the output associated type

This commit is contained in:
Niko Matsakis 2015-01-11 15:21:01 -05:00
parent 006f3eacae
commit 7d68250eb4
1 changed files with 97 additions and 45 deletions

View File

@ -298,17 +298,9 @@ pub fn ty_to_string<'tcx>(cx: &ctxt<'tcx>, typ: &ty::TyS<'tcx>) -> String {
fn closure_to_string<'tcx>(cx: &ctxt<'tcx>, cty: &ty::ClosureTy<'tcx>) -> String {
let mut s = String::new();
match cty.unsafety {
ast::Unsafety::Normal => {}
ast::Unsafety::Unsafe => {
s.push_str(cty.unsafety.to_string().as_slice());
s.push(' ');
}
};
push_sig_to_string(cx, &mut s, '|', '|', &cty.sig);
s.push_str("[closure");
push_sig_to_string(cx, &mut s, '(', ')', &cty.sig);
s.push(']');
s
}
@ -399,18 +391,10 @@ pub fn ty_to_string<'tcx>(cx: &ctxt<'tcx>, typ: &ty::TyS<'tcx>) -> String {
ty_enum(did, substs) | ty_struct(did, substs) => {
let base = ty::item_path_str(cx, did);
let generics = ty::lookup_item_type(cx, did).generics;
parameterized(cx, base.as_slice(), substs, &generics, did)
parameterized(cx, base.as_slice(), substs, &generics, did, &[])
}
ty_trait(box ty::TyTrait {
ref principal, ref bounds
}) => {
let principal = principal.user_string(cx);
let bound_str = bounds.user_string(cx);
let bound_sep = if bound_str.is_empty() { "" } else { " + " };
format!("{}{}{}",
principal,
bound_sep,
bound_str)
ty_trait(ref data) => {
data.user_string(cx)
}
ty::ty_projection(ref data) => {
format!("<{} as {}>::{}",
@ -420,14 +404,15 @@ pub fn ty_to_string<'tcx>(cx: &ctxt<'tcx>, typ: &ty::TyS<'tcx>) -> String {
}
ty_str => "str".to_string(),
ty_closure(ref did, _, substs) => {
cx.closures.borrow().get(did).map(|cl| {
let closures = cx.closures.borrow();
closures.get(did).map(|cl| {
closure_to_string(cx, &cl.closure_type.subst(cx, substs))
}).unwrap_or_else(|| {
if did.krate == ast::LOCAL_CRATE {
let span = cx.map.span(did.node);
format!("closure[{}]", span.repr(cx))
format!("[closure {}]", span.repr(cx))
} else {
format!("closure")
format!("[closure]")
}
})
}
@ -458,7 +443,8 @@ pub fn parameterized<'tcx>(cx: &ctxt<'tcx>,
base: &str,
substs: &subst::Substs<'tcx>,
generics: &ty::Generics<'tcx>,
did: ast::DefId)
did: ast::DefId,
projections: &[ty::ProjectionPredicate<'tcx>])
-> String
{
if cx.sess.verbose() {
@ -511,8 +497,21 @@ pub fn parameterized<'tcx>(cx: &ctxt<'tcx>,
strs.push(ty_to_string(cx, *t))
}
if cx.lang_items.fn_trait_kind(did).is_some() {
format!("{}({})", // TODO
for projection in projections.iter() {
strs.push(format!("{}={}",
projection.projection_ty.item_name.user_string(cx),
projection.ty.user_string(cx)));
}
if cx.lang_items.fn_trait_kind(did).is_some() && projections.len() == 1 {
let projection_ty = projections[0].ty;
let tail =
if ty::type_is_nil(projection_ty) {
format!("")
} else {
format!(" -> {}", projection_ty.user_string(cx))
};
format!("{}({}){}",
base,
if strs[0].starts_with("(") && strs[0].ends_with(",)") {
&strs[0][1 .. strs[0].len() - 2] // Remove '(' and ',)'
@ -520,7 +519,8 @@ pub fn parameterized<'tcx>(cx: &ctxt<'tcx>,
&strs[0][1 .. strs[0].len() - 1] // Remove '(' and ')'
} else {
&strs[0][]
})
},
tail)
} else if strs.len() > 0 {
format!("{}<{}>", base, strs.connect(", "))
} else {
@ -622,6 +622,65 @@ impl<'tcx> Repr<'tcx> for def::Def {
}
}
/// This curious type is here to help pretty-print trait objects. In
/// a trait object, the projections are stored separately from the
/// main trait bound, but in fact we want to package them together
/// when printing out; they also have separate binders, but we want
/// them to share a binder when we print them out. (And the binder
/// pretty-printing logic is kind of clever and we don't want to
/// reproduce it.) So we just repackage up the structure somewhat.
///
/// Right now there is only one trait in an object that can have
/// projection bounds, so we just stuff them altogether. But in
/// reality we should eventually sort things out better.
type TraitAndProjections<'tcx> =
(Rc<ty::TraitRef<'tcx>>, Vec<ty::ProjectionPredicate<'tcx>>);
impl<'tcx> UserString<'tcx> for TraitAndProjections<'tcx> {
fn user_string(&self, tcx: &ctxt<'tcx>) -> String {
let &(ref trait_ref, ref projection_bounds) = self;
let base = ty::item_path_str(tcx, trait_ref.def_id);
let trait_def = ty::lookup_trait_def(tcx, trait_ref.def_id);
parameterized(tcx,
base.as_slice(),
trait_ref.substs,
&trait_def.generics,
trait_ref.def_id,
&projection_bounds[])
}
}
impl<'tcx> UserString<'tcx> for ty::TyTrait<'tcx> {
fn user_string(&self, tcx: &ctxt<'tcx>) -> String {
let &ty::TyTrait { ref principal, ref bounds } = self;
let mut components = vec![];
let tap: ty::Binder<TraitAndProjections<'tcx>> =
ty::Binder((principal.0.clone(),
bounds.projection_bounds.iter().map(|x| x.0.clone()).collect()));
// Generate the main trait ref, including associated types.
components.push(tap.user_string(tcx));
// Builtin bounds.
for bound in bounds.builtin_bounds.iter() {
components.push(bound.user_string(tcx));
}
// Region, if not obviously implied by builtin bounds.
if bounds.region_bound != ty::ReStatic ||
!bounds.builtin_bounds.contains(&ty::BoundSend)
{ // Region bound is implied by builtin bounds:
components.push(bounds.region_bound.user_string(tcx));
}
components.retain(|s| !s.is_empty());
components.connect(" + ")
}
}
impl<'tcx> Repr<'tcx> for ty::TypeParameterDef<'tcx> {
fn repr(&self, tcx: &ctxt<'tcx>) -> String {
format!("TypeParameterDef({:?}, {}, {:?}/{})",
@ -700,12 +759,6 @@ impl<'tcx> Repr<'tcx> for ty::BuiltinBounds {
}
}
impl<'tcx> Repr<'tcx> for ty::ExistentialBounds<'tcx> {
fn repr(&self, tcx: &ctxt<'tcx>) -> String {
self.user_string(tcx)
}
}
impl<'tcx> Repr<'tcx> for ty::ParamBounds<'tcx> {
fn repr(&self, tcx: &ctxt<'tcx>) -> String {
let mut res = Vec::new();
@ -726,7 +779,8 @@ impl<'tcx> Repr<'tcx> for ty::TraitRef<'tcx> {
let trait_def = ty::lookup_trait_def(tcx, self.def_id);
format!("TraitRef({}, {})",
self.substs.self_ty().repr(tcx),
parameterized(tcx, base.as_slice(), self.substs, &trait_def.generics, self.def_id))
parameterized(tcx, base.as_slice(), self.substs,
&trait_def.generics, self.def_id, &[]))
}
}
@ -1109,14 +1163,8 @@ impl<'tcx> UserString<'tcx> for ty::ParamBounds<'tcx> {
}
}
impl<'tcx> UserString<'tcx> for ty::ExistentialBounds<'tcx> {
fn user_string(&self, tcx: &ctxt<'tcx>) -> String {
if self.builtin_bounds.contains(&ty::BoundSend) &&
self.region_bound == ty::ReStatic
{ // Region bound is implied by builtin bounds:
return self.builtin_bounds.repr(tcx);
}
impl<'tcx> Repr<'tcx> for ty::ExistentialBounds<'tcx> {
fn repr(&self, tcx: &ctxt<'tcx>) -> String {
let mut res = Vec::new();
let region_str = self.region_bound.user_string(tcx);
@ -1128,6 +1176,10 @@ impl<'tcx> UserString<'tcx> for ty::ExistentialBounds<'tcx> {
res.push(bound.user_string(tcx));
}
for projection_bound in self.projection_bounds.iter() {
res.push(projection_bound.user_string(tcx));
}
res.connect("+")
}
}
@ -1183,7 +1235,7 @@ impl<'tcx> UserString<'tcx> for ty::TraitRef<'tcx> {
let path_str = ty::item_path_str(tcx, self.def_id);
let trait_def = ty::lookup_trait_def(tcx, self.def_id);
parameterized(tcx, path_str.as_slice(), self.substs,
&trait_def.generics, self.def_id)
&trait_def.generics, self.def_id, &[])
}
}