Do not generate bounds from recursive types

This commit is contained in:
David Tolnay 2016-06-04 16:12:01 -07:00
parent 4e6cd2d63f
commit bd40830905
2 changed files with 66 additions and 6 deletions

View File

@ -70,6 +70,7 @@ pub fn with_bound<F>(
.map(|&(ref field, _)| &field.ty)
// TODO this filter can be removed later, see comment on function
.filter(|ty| contains_generic(ty, generics))
.filter(|ty| !contains_recursion(ty, item.ident))
.map(|ty| strip_reference(ty))
.map(|ty| builder.where_predicate()
// the type that is being bounded e.g. T
@ -159,6 +160,50 @@ fn contains_generic(ty: &ast::Ty, generics: &ast::Generics) -> bool {
visitor.found_generic
}
// We do not attempt to generate any bounds based on field types that are
// directly recursive, as in:
//
// struct Test<D> {
// next: Box<Test<D>>,
// }
//
// This does not catch field types that are mutually recursive with some other
// type. For those, we require bounds to be specified by a `where` attribute if
// the inferred ones are not correct.
//
// struct Test<D> {
// #[serde(where="D: Serialize + Deserialize")]
// next: Box<Other<D>>,
// }
// struct Other<D> {
// #[serde(where="D: Serialize + Deserialize")]
// next: Box<Test<D>>,
// }
fn contains_recursion(ty: &ast::Ty, ident: ast::Ident) -> bool {
struct FindRecursion {
ident: ast::Ident,
found_recursion: bool,
}
impl<'v> visit::Visitor<'v> for FindRecursion {
fn visit_path(&mut self, path: &'v ast::Path, _id: ast::NodeId) {
if !path.global
&& path.segments.len() == 1
&& path.segments[0].identifier == self.ident {
self.found_recursion = true;
} else {
visit::walk_path(self, path);
}
}
}
let mut visitor = FindRecursion {
ident: ident,
found_recursion: false,
};
visit::walk_ty(&mut visitor, ty);
visitor.found_recursion
}
// This is required to handle types that use both a reference and a value of
// the same type, as in:
//

View File

@ -75,7 +75,6 @@ struct Tuple<T>(
);
#[derive(Serialize, Deserialize)]
#[serde(where(serialize="D: Serialize", deserialize="D: Deserialize"))]
enum TreeNode<D> {
Split {
left: Box<TreeNode<D>>,
@ -89,17 +88,33 @@ enum TreeNode<D> {
#[derive(Serialize, Deserialize)]
struct ListNode<D> {
data: D,
#[serde(where="")]
next: Box<ListNode<D>>,
}
#[derive(Serialize, Deserialize)]
struct SerializeWithTrait<D> {
#[serde(where="D: SerializeWith + DeserializeWith")]
struct WithTraits1<D, E> {
#[serde(serialize_with="SerializeWith::serialize_with",
deserialize_with="DeserializeWith::deserialize_with")]
d: D,
#[serde(serialize_with="SerializeWith::serialize_with",
deserialize_with="DeserializeWith::deserialize_with",
where(serialize="D: SerializeWith",
deserialize="D: DeserializeWith"))]
data: D,
where="E: SerializeWith + DeserializeWith")]
e: E,
}
#[derive(Serialize, Deserialize)]
#[serde(where(serialize="D: SerializeWith",
deserialize="D: DeserializeWith"))]
struct WithTraits2<D, E> {
#[serde(serialize_with="SerializeWith::serialize_with",
deserialize_with="DeserializeWith::deserialize_with")]
d: D,
#[serde(serialize_with="SerializeWith::serialize_with",
deserialize_with="DeserializeWith::deserialize_with",
where(serialize="E: SerializeWith",
deserialize="E: DeserializeWith"))]
e: E,
}
//////////////////////////////////////////////////////////////////////////