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 define a function name using concat_idents!() #12249

Closed
kmcallister opened this issue Feb 13, 2014 · 17 comments
Closed

Can't define a function name using concat_idents!() #12249

kmcallister opened this issue Feb 13, 2014 · 17 comments
Labels
A-syntaxext Area: Syntax extensions

Comments

@kmcallister
Copy link
Contributor

I want mymacro!(bar) to expand to fn foo_bar() { }.

#[feature(macro_rules)];

macro_rules! mymacro( ($x:ident) => (
    fn concat_idents!(foo_, $x)() { }
))

mymacro!(bar)

fn main() {
    foo_bar();
}

gives

foo.rs:4:21: 4:22 error: expected `(` but found `!`
foo.rs:4     fn concat_idents!(foo_, $x)() { }

on

rustc 0.9 (d3b3c66 2014-01-12 19:44:26 -0700)
host: x86_64-unknown-linux-gnu
@lifthrasiir
Copy link
Contributor

A duplicate of #4365. Should we make this an RFC?

@kmcallister
Copy link
Contributor Author

I think it's a pretty essential feature. Even C macros can do this with ##.

To give a more realistic use case, Servo has functions like

pub fn noncontent_left(&self) -> Au { ... }
pub fn noncontent_right(&self) -> Au { ... }
pub fn noncontent_top(&self) -> Au { ... }
pub fn noncontent_bottom(&self) -> Au { ... }

pub fn noncontent_inline_left(&self) -> Au { ... }
pub fn noncontent_inline_right(&self) -> Au { ... }
pub fn noncontent_inline_top(&self) -> Au { ... }
pub fn noncontent_inline_bottom(&self) -> Au { ... }

pub fn merge_noncontent_inline_left(&self, other_box: &Box) { ... }
pub fn merge_noncontent_inline_right(&self, other_box: &Box) { ... }
pub fn merge_noncontent_inline_top(&self, other_box: &Box) { ... }
pub fn merge_noncontent_inline_bottom(&self, other_box: &Box) { ... }

and I generate these from macro invocations

def_noncontent!(left,  noncontent_left,  noncontent_inline_left,  merge_noncontent_inline_left)
def_noncontent!(right, noncontent_right, noncontent_inline_right, merge_noncontent_inline_right)

but would much rather have

def_noncontent!(left)
def_noncontent!(right)

@arjantop
Copy link
Contributor

Just ran into this problem wanting to generate two tests for encoder I'm implementing. One checking if encoding is correct and the other if encode . decode = id. Now i basicaly have to pass two test names that only differ in prefixes.

@lpy
Copy link
Contributor

lpy commented Apr 11, 2014

cc @lpy

@huonw
Copy link
Member

huonw commented Apr 11, 2014

cc #13294

@nrc
Copy link
Member

nrc commented Apr 27, 2014

+1 for implementing this, seems like the kind of thing macros would be useful for

@kmcallister
Copy link
Contributor Author

Another use case: a macro used to define both foo() and foo_mut(), which have similar code that calls bar() / baz() or bar_mut() / baz_mut() respectively.

@dobkeratops
Copy link

I just ran into this. a few of uses I had in mind- convenience for making a single-function trait, rolling accessors for emulating anon structs; convenience for rolling double-dispatch traits to emulate overloading.
( I'd also be interested in a syntax rules to convert indents to/from camel/snake case for these .. working with the naming conventions)

@emberian
Copy link
Member

This is really just due to how hacky and awful macro expansion is. Macro invocation is only allowed in certain predesignated places in the ast.

@kmcallister
Copy link
Contributor Author

Note that this can't be implemented if we enforce hygiene for all macros (including syntax extensions).

@emberian
Copy link
Member

A more wholesome design is needed, I don't want to keep extending the macro system ad-hoc. The monthish I spent around and with @jbclements inspired me to want to take ownership of macros.

@trws
Copy link

trws commented Jul 23, 2014

Leveraging some of the recent code from @jbclements in the macro_rules implementation this can technically be done in a user-defined syntax extension. The method is... well, it's utterly horrifying, but it works for now.

#[feature(macro_rules)];

macro_rules! mymacro( ($x:ident) => (
    expand_string_to_expr!(concat!("fn foo_", stringify!($x),"() { }"))
))

mymacro!(bar)

fn main() {
    foo_bar();
}

Where expand_string_to_expr! is a registered macro leveraging the ParserAnyMacro type from macro_rules.rs as below.

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("expand_string_to_expr", expand_string_to_expr);
}

fn expand_string_to_expr(cx: &mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult> {
    use syntax::print::pprust;

    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), Vec::from_slice(tts));
    let arg = cx.expand_expr(parser.parse_expr());
    let expr_string = match arg.node {
        ast::ExprLit(spanned) => {
            match spanned.node {
                ast::LitStr(ref s, _) => s.to_string(),
                _ => {
                    cx.span_err(sp, format!(
                            "expected string literal but got `{}`",
                            pprust::lit_to_string(&*spanned)).as_slice());
                    return DummyResult::expr(sp)
                }
            }
        }
        _ => {
            cx.span_err(sp, format!(
                    "expected string literal but got `{}`",
                    pprust::expr_to_string(&*arg)).as_slice());
            return DummyResult::expr(sp)
        }
    };
    if !parser.eat(&token::EOF) {
        cx.span_err(parser.span, "only one string literal allowed");
        return DummyResult::expr(sp);
    }

    let mut p = parse::new_parser_from_source_str(cx.parse_sess(), cx.cfg(), "string_expr".to_string(), expr_string);
    return box ParserAnyMacro{
        parser: std::cell::RefCell::new(p),
    } as Box<MacResult>
}

@SkylerLipthay
Copy link
Contributor

In case anyone wants a slightly simpler solution to this problem while waiting for better macros, I made a syntax plugin that's sure to break once after each nightly release: interpolate_idents. I would be very cautious to use this for anything.

@retep998
Copy link
Member

retep998 commented Nov 7, 2015

This is an issue that I find frustrating when working on winapi, making macros needlessly more complicated with extra parameters just because concat_idents! is completely useless. This is by far the most common deficiency of macros I see people run into, and I think it would be worth the additional complexity to support this sort of thing.

We've already had a working PR that was closed due to needing an RFC and then an RFC that was closed due to trying to avoid jamming up the macro system with 1.0 approaching.

Now that Rust has had several stable versions and the magical new macro system that people are heralding is nowhere in sight still, maybe we can consider opening an RFC for allowing macros in ident positions and actually having a discussion instead of just shutting it down immediately.

@nrc
Copy link
Member

nrc commented Nov 8, 2015

macro reform is in the works right now, expect an internals post soon addressing things to fix (although a plan will take a bit longer). Concatenating identifiers is definitely on the agenda. It is a hard problem though - hygiene considerations means there is no easy solution.

@wchargin
Copy link
Contributor

wchargin commented Nov 9, 2015

Awesome, thanks for working on this. Definitely a 👍 from me. Looking forward to it.

@steveklabnik
Copy link
Member

Closing in favor of #29599

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-syntaxext Area: Syntax extensions
Projects
None yet
Development

No branches or pull requests