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

Can't use #[derive] and macro on a generic type at the same time #50676

Open
tuxzz opened this issue May 12, 2018 · 6 comments
Open

Can't use #[derive] and macro on a generic type at the same time #50676

tuxzz opened this issue May 12, 2018 · 6 comments
Labels
A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@tuxzz
Copy link

tuxzz commented May 12, 2018

I have this code:

macro_rules! typ {
    () => { i32 };
}

#[derive(Debug)]
struct MyStruct<T> {
    field: typ!(),
}

The compiler gives this error:

error: `derive` cannot be used on items with type macros
 --> src/main.rs:7:12
  |
7 |     field: typ!(),
  |            ^^^^^^

But if I change MyStruct into non-generic type

macro_rules! typ {
    () => { i32 };
}

#[derive(Debug)]
struct MyStruct {
    field: typ!(),
}

It works without any error.

Why can't use #[derive] and macro on a generic type at the same time?
Is it a compiler bug?

-> Mirror issue on StackOverflow

@durka
Copy link
Contributor

durka commented May 14, 2018

It is intentional (here is the historical record), but there is a possibility the situation could be improved in the future, and at least the error message should be rewritten to explain why it refuses to compile.

The underlying issue is that #[derive] macros need to "forward" their trait requirements to all the fields of the struct. For MyStruct to be Debug, the type of field must also be Debug. Consider this one:

#[derive(Debug)]
struct MyStruct<T: FromStr> {
    field: T
}

We need to generate impl<T: FromStr> Debug for MyStruct<T> where T: Debug { ... } (you'll see why I picked FromStr in a second). However in this case:

#[derive(Debug)]
struct MyStruct<T> {
    field: T::Err
}

Here the field is an associated type, so the generated code actually needs to be impl<T: FromStr> Debug for MyStruct<T> where T::Err: Debug { ... }.

The derive macros actually scan the field types to see whether they need to bound T or an associated type. But if you use a type macro, this breaks. The code generation can't see through the macro, so it doesn't know what bounds to generate.

When this was discovered we couldn't decide whether to let the type macro be expanded eagerly (seems like you could get into a loop or ordering issues), just copy the macro into the where clause (derives normally don't do this because it could expand to a private type, causing type errors in generated code), or something else, so we punted and made it an error.

@durka
Copy link
Contributor

durka commented May 14, 2018

As a concrete solution, use a crate like derivative where you can fix the generated bounds:

#[derive(Derivative)]
#[derivative(Debug)]
struct MyStruct<T> {
    #[derivative(Debug(bound=""))] // or "typ!(): ::std::fmt::Debug"
    field: typ!(),
}

@XAMPPRocky XAMPPRocky added C-enhancement Category: An issue proposing an enhancement or a PR with one. A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Oct 2, 2018
@steveklabnik
Copy link
Member

Triage: the error is a bit better now:

error: `derive` cannot be used on items with type macros
 --> src/main.rs:7:12
  |
7 |     field: typ!(),
  |            ^^^^^^

error[E0392]: parameter `T` is never used
 --> src/main.rs:6:17
  |
6 | struct MyStruct<T> {
  |                 ^ unused parameter
  |
  = help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData`

@durka
Copy link
Contributor

durka commented Sep 7, 2020

Huh? That's exactly the same error. The only difference is that since rustc 1.33, there's now enough error recovery in order to also print the unused type parameter error. But that is orthogonal and doesn't get any closer to pointing out what causes the derive error.

@steveklabnik
Copy link
Member

there's now enough error recovery in order to also print the unused type parameter error.

That was all that I meant.

@cameron1024
Copy link
Contributor

I just ran into this while adding a generic parameter to an ast enum I had in a parser. I found the error message somewhat confusing because it says "derive cannot be used on items with type macros", but a few lines below I had another (non-generic) enum that used the same type macro and compiled fine.

Perhaps it's worth tweaking the error message to say "derive cannot be used on items with type parameters and type macros"?

That would have saved me a bit of head scratching 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants