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

Cannot derive(Debug) for a struct with a function with a reference parameter #45048

Closed
mathstuf opened this issue Oct 5, 2017 · 24 comments
Closed
Labels
C-feature-request Category: A feature request, i.e: not implemented / a PR. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@mathstuf
Copy link
Contributor

mathstuf commented Oct 5, 2017

This fails to compile:

#[derive(Debug)]
pub struct A {
    f: fn(&i64),
}
   Compiling no-debug v0.1.0 (file:///home/boeckb/misc/code/sb/rs-no-debug)                                                                                                                                                                                                                                                   
error[E0277]: the trait bound `fn(&i64): std::fmt::Debug` is not satisfied                                                                                                                                                                                                                                                    
 --> src/lib.rs:3:5
  |
3 |     f: fn(&i64),
  |     ^^^^^^^^^^^ `fn(&i64)` cannot be formatted using `:?`; if it is defined in your crate, add `#[derive(Debug)]` or manually implement it
  |
  = help: the trait `std::fmt::Debug` is not implemented for `fn(&i64)`
  = note: required because of the requirements on the impl of `std::fmt::Debug` for `&fn(&i64)`
  = note: required for the cast to the object type `std::fmt::Debug`

error: aborting due to previous error

It does work if the parameter is a non-reference (mutable reference also fails) including pointers. I'm using rustc 1.20.0 from Fedora's packages.

@anirudhb
Copy link
Contributor

anirudhb commented Nov 21, 2017

@mathstuf I think it has something to do with libcore/ptr.rs:

fnptr_impls_args! { }
fnptr_impls_args! { A }
fnptr_impls_args! { A, B }
fnptr_impls_args! { A, B, C }
fnptr_impls_args! { A, B, C, D }
fnptr_impls_args! { A, B, C, D, E }
fnptr_impls_args! { A, B, C, D, E, F }
fnptr_impls_args! { A, B, C, D, E, F, G }
fnptr_impls_args! { A, B, C, D, E, F, G, H }
fnptr_impls_args! { A, B, C, D, E, F, G, H, I }
fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J }
fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K }
fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K, L }

What if we did this exact thing but with &A, &B, &C, etc.? I believe that should work.
I also encountered this same problem.

edit: also with &mut A, &mut B, &mut C, etc

@anirudhb
Copy link
Contributor

anirudhb commented Nov 22, 2017

Also, you can derive(Clone) for these types of structs, so you can just use a box.
I've tested this with fn(Box<u64>) -> u64, and it works fine. Link: Boxed functions

edit: better playground

@mathstuf
Copy link
Contributor Author

@anirudhb If I need to store a function which takes a reference, how does a box solve any of that?

@anirudhb
Copy link
Contributor

I was suggesting an alternative to storing functions that take references.

@anirudhb
Copy link
Contributor

A box doesn't solve any of the problems you raised, but maybe a change to libcore/ptr.rs might work.

@mathstuf
Copy link
Contributor Author

That works if you can change the interface, but that isn't always possible (e.g., storing something like u64::add_one).

@anirudhb
Copy link
Contributor

No, I mean that fnptr_impl_args! was only called for A, B and such. Since it is a macro, passing in &A, &mut B etc. should work.

@mathstuf
Copy link
Contributor Author

Heh, I think there was a race here :) . I was replying to "suggesting an alternative", not "but maybe a change to".

@mathstuf
Copy link
Contributor Author

Also, doesn't the macro expansions become:

fnptr_impls_args! { }
fnptr_impls_args! { A }
fnptr_impls_args! { &A }
fnptr_impls_args! { &mut A }
fnptr_impls_args! { A, B }
fnptr_impls_args! { A, &B }
fnptr_impls_args! { A, &mut B }
fnptr_impls_args! { &A, B }
fnptr_impls_args! { &A, &B }
fnptr_impls_args! { &A, &mut B }
fnptr_impls_args! { &mut A, B }
fnptr_impls_args! { &mut A, &B }
fnptr_impls_args! { &mut A, &mut B }

Would raw pointer versions be needed as well? This seems…unwieldy.

@anirudhb
Copy link
Contributor

anirudhb commented Nov 22, 2017

Create another macro.

macro_rules! fnptr_impsl_argsptr {
   ($($arg: ident),+) => {
     fnptr_impls_args! { $($arg),+ };
     fnptr_impls_args! { $(&$arg),+ };
     fnptr_impls_args! { $(&mut $arg),+ };
   };
   () => {
     fnptr_impls_args! {};
   };
}

fnptr_impls_argsptr! { }
fnptr_impls_argsptr! { A }
fnptr_impls_argsptr! { A, B }
fnptr_impls_argsptr! { A, B, C }
fnptr_impls_argsptr! { A, B, C, D }
fnptr_impls_argsptr! { A, B, C, D, E }
fnptr_impls_argsptr! { A, B, C, D, E, F }
fnptr_impls_argsptr! { A, B, C, D, E, F, G }
fnptr_impls_argsptr! { A, B, C, D, E, F, G, H }
fnptr_impls_argsptr! { A, B, C, D, E, F, G, H, I }
fnptr_impls_argsptr! { A, B, C, D, E, F, G, H, I, J }
fnptr_impls_argsptr! { A, B, C, D, E, F, G, H, I, J, K }
fnptr_impls_argsptr! { A, B, C, D, E, F, G, H, I, J, K, L }

@anirudhb
Copy link
Contributor

anirudhb commented Nov 22, 2017

Macros solve repetition :)
edit: how would we enumerate all of the possibilities? we should probably start looking into compiler plugins.

@mathstuf
Copy link
Contributor Author

No, that only does A, B, &A, &B, and &mut A, &mut B; A, &B is not generated with that (it's exponential in the number of variants of a type, your macro is still linear).

@anirudhb
Copy link
Contributor

That's what my edit is for. We should look into a compiler plugin to do the enumeration.

@mathstuf
Copy link
Contributor Author

Sorry, github doesn't autoload edits here, so I didn't see it.

@anirudhb
Copy link
Contributor

anirudhb commented Nov 22, 2017

I'm trying to think of a good way to enumerate A, &A, &mut A. Any ideas? @mathstuf

@anirudhb
Copy link
Contributor

That is, for a certain length of list.

@XAMPPRocky XAMPPRocky added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. C-feature-request Category: A feature request, i.e: not implemented / a PR. labels Jan 22, 2018
@Phlosioneer
Copy link
Contributor

Fun fact: that's just a base-3 number with a number of digits equal to the number of parameters. Suuuuper easy to enumerate. For n params, that's 3^n macro invocations.

To be clear: 0 is A, 1 is &A, 2 is &mut A. Start with a 0 for each parameter. Then treat it as a number in base 3, and increment until you get back to all 0's. Every time you increment, do a fnptr_impls_argsptr! call.

I have no idea how to make a compiler plugin, or else I'd try doing this myself.

@RalfJung
Copy link
Member

RalfJung commented Nov 19, 2018

This doesn't just affect reference types though, it affects all types with lifetimes. The issue is that fn(&T) is short for for<'a> fn(&'a T), and the instances in libcore do not cover the case where there is a lifetime quantifier.

So, for example, once you covered fn(&T) and fn(&mut T), you still did not cover
fn( (&T, &mut T) ).

@mathstuf
Copy link
Contributor Author

Blah. That increases the complexity considerably. I wonder if there shouldn't just be some compiler magic for fn X to just output the function's address?

@jethrogb
Copy link
Contributor

jethrogb commented Feb 4, 2019

Triage: This now works as expected on beta, but I don't know why.

@mathstuf
Copy link
Contributor Author

mathstuf commented Feb 4, 2019

Thanks for the update. Seems like #55517 from discussion on the referencing issue.

@jdahlstrom
Copy link

jdahlstrom commented Feb 13, 2019

A bit late to the party now, but I'm not sure it would have been reasonable to generate over 3^12=531441 trait impls, compiler plugin or not...

@mathstuf
Copy link
Contributor Author

Triage: This now works as expected on beta, but I don't know why.

Doesn't seem to work on stable today.

cannadayr added a commit to cannadayr/rsbqn that referenced this issue Jan 15, 2022
Change Fn,R1, & R2 to struct tuple types.
impl Debug and PartialEq for fn types manually.
    see rust-lang/rust#45048
Adjust types to work with now-borrowed values.
Reduce clones when available.

In general, by borrowing the arguments for all builtin
functions, this lets us only clone when creating a new
value. This ends up being significant because previously
during "table" for example, each "call" applied to the
Array values cloned the value when calling the builtin
fn. Now it will only clone the value when returning a
Vs to the stack.
@Dylan-DPC
Copy link
Member

This now works on latest stable and nightly so closing it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-feature-request Category: A feature request, i.e: not implemented / a PR. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

8 participants