Skip to content

rust: macros: allow overriding the name of module param constants #877

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

Open
wants to merge 1 commit into
base: rust
Choose a base branch
from

Conversation

phi-gamma
Copy link

Currently, module! emits constants to access module parameters
with the same name as the parameter:

const {param_name}: __{name}_{param_name} = __{name}_{param_name};

Since a const is visible throughout the file it is defined in,
this can cause clashes with let bindings, e. g. when trying to
create a binding let foobar = 42 in a module with a parameter
of the same name:

    10 | / module! {
    11 | |     type: ParamName,
    12 | |     name: "param_name",
    13 | |     author: "A. U. Thor",
    ...  |
    21 | |     },
    22 | | }
       | |_- constant defined here
    ...
    25 |       let foobar = 42;
       |           ^^^^^^   -- this expression has type `{integer}`
       |           |
       |           expected integer, found struct `__param_name_foobar`
       |           `foobar` is interpreted as a constant, not a new binding
       |           help: introduce a new binding instead: `other_foobar`

Likewise when binding to foobar in a match pattern.

This PR proposes to extend module! to allow specifying a distinct
name for the constant:

module! {
    type: ParamName,
    name: "param_name",
    author: "A. U. Thor",
    license: "GPL",
    params: {
        foobar = MODPARAM_FOO_BAR : bool {
            default: true,
            permissions: 0o644,
            description: "distinct const name",
        },
    },
}

So when loading the module one would still pass foobar=0 but in the
module code the parameter would be accessible as MODPARAM_FOO_BAR.
That would avoid the identifier clashing with pattern matching while
at the same time being more idiomatic Rust.

The param = PARAM : type { … syntax is just what I came up with
without giving it much thought. It might as well be a key: value
setting though then we’d have to find a name for the key, const
perhaps?

I’m aware of #423 and from a casual glance at the changes (@nbdd0121
please correct me if I’m wrong) it doesn’t appear to address this
issue.

Copy link

@adamrk adamrk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a good idea. How about changing one of the examples to use it so that it's running in the CI?

Also, for the syntax what do you think about something like

        MODPARAM_FOO_BAR : bool {
            command_line_name: "foobar",
            default: true,
            permissions: 0o644,
            description: "distinct const name",
        },

where command_line_name is optional? I'm just thinking it makes it clearer what the extra name is for.

@nbdd0121
Copy link
Member

I wonder if the current API for modparam should be rethought instead.

Currently we generate a struct for each param and a const for each generated struct and expose a read method on it. But we could (and probably should) generate a single struct and a const MODPARAM of that struct.

For example, for a module with two params foo and bar, we currently use foo.read(), bar.read() to access them, but it could be MODPARAM.foo(), MODPARAM.bar() instead.

@phi-gamma
Copy link
Author

phi-gamma commented Sep 26, 2022 via email

@phi-gamma phi-gamma force-pushed the module-param-constname branch from 108bfcd to a547a27 Compare September 26, 2022 21:02
@adamrk
Copy link

adamrk commented Sep 27, 2022

@phi-gamma - what do you think about @nbdd0121's suggestion above? I think it wouldn't be that big a change. Instead of creating this struct on each loop, we just create one struct at the end with a method for each read_func generated in the loops.

@phi-gamma
Copy link
Author

phi-gamma commented Sep 27, 2022 via email

@adamrk
Copy link

adamrk commented Sep 29, 2022

Yeah, supporting the fields to be in any order would be great too.

@nbdd0121
Copy link
Member

I think it's intentional decision by @ojeda

@adamrk
Copy link

adamrk commented Sep 29, 2022

Oh, I didn't realize. I seem to remember we wanted to keep the macros to just the essentials for the initial patch set - was it just that or is there some other reason?

@ojeda
Copy link
Member

ojeda commented Oct 3, 2022

Yeah, we wanted to keep this minimal in the beginning. As for the ordering, the advantage of allowing any order is that it is a bit less annoying to use (harder to write?). The disadvantage is that we will have different orders which is less consistent (harder to read?).

@phi-gamma phi-gamma force-pushed the module-param-constname branch from a547a27 to bdea1a1 Compare October 5, 2022 12:36
@phi-gamma
Copy link
Author

phi-gamma commented Oct 5, 2022

@nbdd0121, @adamrk: Latest revision switched to the single struct version.

Maybe a module would be more appropriate than an empty struct? With that struct we’re
essentially namespacing the accessor functions out of the crate scope so to me a module
seems a more obvious approach. Something like this.

Instead of creating a struct + constant pair for each parameter,
expose the parameters as methods over a single struct `MODPARAM`.
For example the parameter `foobar` becomes available at the Rust
end as `MODPARAM.foobar()`.

The rationale is that currently the `module!` macro generates a
constant with the name of the parameter which can lead to
surprising behavior when attempting to create a let binding of
the same name:

    module! {
        type: ParamName,
        // ...
        params: {
            foobar : bool {
                default: true,
                permissions: 0o644,
                description: "test",
            },
        },
    }

    fn clash() {
        let foobar = 42;
    }

This causes rustc to error out because `foobar` is treated as a
pattern on the left hand side of the let expression:

    10 | / module! {
    11 | |     type: ParamName,
    12 | |     name: "param_name",
    13 | |     author: "A. U. Thor",
    ...  |
    21 | |     },
    22 | | }
       | |_- constant defined here
    ...
    25 |       let foobar = 42;
       |           ^^^^^^   -- this expression has type `{integer}`
       |           |
       |           expected integer, found struct `__param_name_foobar`
       |           `foobar` is interpreted as a constant, not a new binding
       |           help: introduce a new binding instead: `other_foobar`

Essentially, having a parameter `foobar` prevents the creation of
a binding of the same name anywhere else in the module.

Signed-off-by: Philipp Gesang <phg@phi-gamma.net>
Suggested-by: Gary Guo <gary@garyguo.net>
@phi-gamma phi-gamma force-pushed the module-param-constname branch from bdea1a1 to f9a866a Compare October 5, 2022 13:09
@adamrk
Copy link

adamrk commented Oct 5, 2022

I'd expect that when most Rust devs see a macro that looks like a struct they're going to be surprised that the fields can't be reordered. So I'd support reordering if it can be done cleanly (maybe leave for a separate PR).

If that doesn't work out we should probably at least put a warning about it in the doc comment or the compilation error when the macro fails.

@adamrk
Copy link

adamrk commented Oct 5, 2022

Latest version looks good to me. A mod instead of a struct seems fine too - I guess the only difference for the module author is . versus ::, right?

@phi-gamma
Copy link
Author

I guess the only difference for the module author is . versus ::, right?

Can’t think of a difference for the author except maybe that you can’t take a reference
to a module. We could go a step further and move the rest of the parameter related
definitions into the module scope. Keeps things a bit more hygienic overall.

@adamrk
Copy link

adamrk commented Oct 25, 2022

From Jonathan Corbet's review of the example module:

This macro is fussy about the ordering of the various fields and will complain if the developer gets that wrong.

Sounds to me like that's a vote in favor of letting the fields be reordered?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants