Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

format_with misses bounds on the Dummy struct and its instantiation. #55

Closed
glandium opened this issue Oct 30, 2019 · 2 comments
Closed
Labels
bug The crate does not work as expected

Comments

@glandium
Copy link
Contributor

glandium commented Oct 30, 2019

Consider the following rust code:

use derivative::Derivative;

trait Foo {}

fn fmt<T>(t: &T, f: &mut std::fmt::Formatter) -> std::fmt::Result {
    write!(f, "foo")
}

#[derive(Debug)]
struct Qux<'a, T: Foo>(&'a T);

#[derive(Derivative)]
#[derivative(Debug)]
struct Bar<'a, T: Foo>(#[derivative(Debug(format_with="fmt"))] Qux<'a, T>);

fn main() {
}

This fails to build with

error[E0277]: the trait bound `T: Foo` is not satisfied
  --> src/main.rs:14:24
   |
14 | struct Bar<'a, T: Foo>(#[derivative(Debug(format_with="fmt"))] Qux<'a, T>);
   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `T`
   |
   = help: consider adding a `where T: Foo` bound
note: required by `Qux`
  --> src/main.rs:10:1
   |
10 | struct Qux<'a, T: Foo>(&'a T);
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expanded, the code looks like:

impl <'a, T: Foo> ::std::fmt::Debug for Bar<'a, T> where T: ::std::fmt::Debug
 {
    fn fmt(&self, __f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            Bar(ref __arg_0) => {
                let mut __debug_trait_builder = __f.debug_tuple("Bar");
                let __arg_0 =
                    {
                        struct Dummy<'a, '_derivative,
                                     T>(&'_derivative Qux<'a, T>,
                                        ::std::marker::PhantomData<(T)>) where
                               T: '_derivative;
                        impl <'a, '_derivative, T: Foo> ::std::fmt::Debug for
                         Dummy<'a, '_derivative, T> where T: '_derivative {
                            fn fmt(&self, __f: &mut ::std::fmt::Formatter)
                             -> ::std::fmt::Result {
                                fmt(&self.0, __f)
                            }
                        }
                        Dummy::<'a, T>(__arg_0, ::std::marker::PhantomData)
                    };
                let _ = __debug_trait_builder.field(&__arg_0);
                __debug_trait_builder.finish()
            }
        }
    }
}

With the code expanded, the error becomes:

error[E0277]: the trait bound `T: Foo` is not satisfied
  --> src/main.rs:23:41
   |
23 |                                      T>(&'_derivative Qux<'a, T>,
   |                                         ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `T`
   |
   = help: consider adding a `where T: Foo` bound
note: required by `Qux`
  --> src/main.rs:10:1
   |
10 | struct Qux<'a, T: Foo>(&'a T);
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The code around line 23 is:

struct Dummy<'a, '_derivative,
             T: Foo>(&'_derivative Qux<'a, T>,
                ::std::marker::PhantomData<(T)>) where
       T: '_derivative;

So what is missing here is the original bound for T, because Qux needs it (as per the error message).

But adding that is not enough in this case, because fixing up the expanded code still fails, with a different error:

error[E0107]: wrong number of lifetime arguments: expected 2, found 1
  --> src/main.rs:33:25
   |
33 |                         Dummy::<'a, T>(__arg_0, ::std::marker::PhantomData)
   |                         ^^^^^^^^^^^^^^ expected 2 lifetime arguments

What works, here, is Dummy::<'a, '_, T>(...).

@glandium glandium added the bug The crate does not work as expected label Oct 30, 2019
@glandium
Copy link
Contributor Author

Not sure this is completely correct, but the following patch works for this testcase:

diff --git a/src/debug.rs b/src/debug.rs
index a4ffa10..4f14a9b 100644
--- a/src/debug.rs
+++ b/src/debug.rs
@@ -131,9 +131,6 @@ fn format_with(
     let fmt_path = fmt_path();
     let phantom_path = phantom_path();
 
-    let ctor_generics = generics.clone();
-    let (_, ctor_ty_generics, _) = ctor_generics.split_for_impl();
-
     generics
         .make_where_clause()
         .predicates
@@ -178,11 +175,14 @@ fn format_with(
     // Leave off the type parameter bounds, defaults, and attributes
     let phantom = generics.type_params().map(|tp| &tp.ident);
 
+    let mut ctor_generics = generics.clone();
+    *ctor_generics.lifetimes_mut().last().unwrap() = syn::LifetimeDef::new(parse_quote!('_));
+    let (_, ctor_ty_generics, _) = ctor_generics.split_for_impl();
     let ctor_ty_generics = ctor_ty_generics.as_turbofish();
 
     quote_spanned!(f.span=>
         let #arg_n = {
-            struct Dummy #ty_generics (&'_derivative #ty, #phantom_path <(#(#phantom),*)>) #where_clause;
+            struct Dummy #impl_generics (&'_derivative #ty, #phantom_path <(#(#phantom),*)>) #where_clause;
 
             impl #impl_generics #debug_trait_path for Dummy #ty_generics #where_clause {
                 fn fmt(&self, __f: &mut #fmt_path::Formatter) -> #fmt_path::Result {

mcarton added a commit that referenced this issue Mar 10, 2020
mcarton added a commit that referenced this issue Mar 10, 2020
@mcarton
Copy link
Owner

mcarton commented Mar 11, 2020

Fixed in #61 and released as v2.0.2.

Thanks for the report and patch!

@mcarton mcarton closed this as completed Mar 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug The crate does not work as expected
Projects
None yet
Development

No branches or pull requests

2 participants