Skip to content

Commit

Permalink
Check for Self bounds on methods and add those bounds to impl header
Browse files Browse the repository at this point in the history
See #11
  • Loading branch information
LukasKalbertodt committed Jul 16, 2019
1 parent 82bf029 commit cf6c9cc
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 2 deletions.
32 changes: 30 additions & 2 deletions src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,42 @@ fn gen_header(
quote! { + ?::std::marker::Sized }
};

// Check if there are some `Self: Foo` bounds on methods. If so, we
// need to add those bounds to `T` as well. See issue #11 for more
// information, but in short: there is no better solution. Method where
// clauses with `Self: Foo` force us to add `T: Foo` to the impl
// header, as we otherwise cannot generate a valid impl block.
let additional_bounds = trait_def.items.iter()
// Only interested in methods
.filter_map(|item| if let TraitItem::Method(m) = item { Some(m) } else { None })
// We also ignore methods that we will not override. In the case of
// invalid attributes it is save to assume default behavior.
.filter(|m| !should_keep_default_for(m, proxy_type).unwrap_or(false))
// Exact all relevant bounds
.flat_map(|m| {
m.sig.decl.generics.where_clause.iter()
.flat_map(|wc| &wc.predicates)
.filter_map(|pred| {
if let WherePredicate::Type(p) = pred { Some(p) } else { None }
})
.filter(|p| {
// Only `Self:` bounds
match &p.bounded_ty {
Type::Path(p) => p.path.is_ident("Self"),
_ => false,
}
})
.flat_map(|p| &p.bounds)
});

// Determine if our proxy type needs a lifetime parameter
let (mut params, ty_bounds) = match proxy_type {
ProxyType::Ref | ProxyType::RefMut => (
quote! { #proxy_lt_param, },
quote! { : #proxy_lt_param + #trait_path #relaxation }
quote! { : #proxy_lt_param + #trait_path #relaxation #(+ #additional_bounds)* }
),
ProxyType::Box | ProxyType::Rc | ProxyType::Arc => {
(quote!{}, quote! { : #trait_path #relaxation })
(quote!{}, quote! { : #trait_path #relaxation #(+ #additional_bounds)* })
}
ProxyType::Fn | ProxyType::FnMut | ProxyType::FnOnce => {
let fn_bound = gen_fn_type_for_trait(proxy_type, trait_def)?;
Expand Down
23 changes: 23 additions & 0 deletions tests/compile-pass/self_bound.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use auto_impl::auto_impl;


#[auto_impl(&)]
trait Trait {
fn foo(&self)
where Self: Clone;
}

#[derive(Clone)]
struct Foo {}
impl Trait for Foo {
fn foo(&self)
where Self: Clone,
{}
}

fn assert_impl<T: Trait>() {}

fn main() {
assert_impl::<Foo>();
assert_impl::<&Foo>();
}
24 changes: 24 additions & 0 deletions tests/compile-pass/self_bound_default_method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use auto_impl::auto_impl;


#[auto_impl(Box)]
trait Trait {
fn bar(&self);

#[auto_impl(keep_default_for(Box))]
fn foo(&self)
where Self: Clone
{}
}

fn assert_impl<T: Trait>() {}

struct Foo {}
impl Trait for Foo {
fn bar(&self) {}
}

fn main() {
assert_impl::<Foo>();
assert_impl::<Box<Foo>>();
}
35 changes: 35 additions & 0 deletions tests/compile-pass/self_bound_multiple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use std::fmt;
use auto_impl::auto_impl;


#[auto_impl(&)]
trait Trait {
fn foo(&self)
where Self: Clone;
fn bar(&self)
where Self: Default + fmt::Display;
}

#[derive(Clone, Default)]
struct Foo {}
impl Trait for Foo {
fn foo(&self)
where Self: Clone,
{}
fn bar(&self)
where Self: Default + fmt::Display,
{}
}

impl fmt::Display for Foo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
unimplemented!()
}
}

fn assert_impl<T: Trait>() {}

fn main() {
assert_impl::<Foo>();
assert_impl::<&Foo>();
}

0 comments on commit cf6c9cc

Please sign in to comment.