rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.96k stars 12.68k forks source link

The macro recursion limit is too low #22552

Closed carllerche closed 7 years ago

carllerche commented 9 years ago

I started hitting the recursion limit with the following macro (which isn't actually complete yet either)

    use nixtest::assert_errno_eq;
    use libc::c_int;

    macro_rules! check_errno {
        ($errno:ident) => {{
            assert_errno_eq(stringify!($errno), $errno as c_int);
        }};

        ($errno:ident, $($rest:ident),+) => {{
            check_errno!($errno);
            check_errno!($($rest),*);
        }};
    }

    check_errno!(
        EPERM,
        ENOENT,
        ESRCH,
        EINTR,
        EIO,
        ENXIO,
        E2BIG,
        ENOEXEC,
        EBADF,
        ECHILD,
        EAGAIN,
        ENOMEM,
        EACCES,
        EFAULT,
        ENOTBLK,
        EBUSY,
        EEXIST,
        EXDEV,
        ENODEV,
        ENOTDIR,
        EISDIR,
        EINVAL,
        ENFILE,
        EMFILE,
        ENOTTY,
        ETXTBSY,
        EFBIG,
        ENOSPC,
        ESPIPE,
        EROFS,
        EMLINK,
        EPIPE,
        EDOM,
        ERANGE,
        EDEADLK,
        ENAMETOOLONG,
        ENOLCK,
        ENOSYS,
        ENOTEMPTY,
        ELOOP,
        ENOMSG,
        EIDRM,
        ECHRNG,
        EL2NSYNC,
        EL3HLT,
        EL3RST,
        ELNRNG,
        EUNATCH,
        ENOCSI,
        EL2HLT,
        EBADE,
        EBADR,
        EXFULL,
        ENOANO,
        EBADRQC,
        EBADSLT,
        EBFONT,
        ENOSTR,
        ENODATA,
        ETIME,
        ENOSR,
        ENONET,
        ENOPKG,
        EREMOTE,
        ENOLINK,
        EADV,
        ESRMNT,
        ECOMM,
        EPROTO,
        EMULTIHOP,
        EDOTDOT,
        EBADMSG,
        EOVERFLOW,
        ENOTUNIQ,
        EBADFD,
        EREMCHG,
        ELIBACC,
        ELIBBAD,
        ELIBSCN,
        ELIBMAX,
        ELIBEXEC,
        EILSEQ,
        ERESTART,
        ESTRPIPE,
        EUSERS,
        ENOTSOCK,
        EDESTADDRREQ,
        EMSGSIZE,
        EPROTOTYPE,
        ENOPROTOOPT,
        EPROTONOSUPPORT,
        ESOCKTNOSUPPORT,
        EOPNOTSUPP,
        EPFNOSUPPORT
eddyb commented 9 years ago

You don't need recursion for this macro, was your original usecase more complicated? Also, the example is missing at least a closing ).

carllerche commented 9 years ago

Yeah, it got truncated when copying :P How would I implement the macro without recursion?

kennytm commented 9 years ago

@carllerche

macro_rules! check_errno {
    ($($errno:ident),+) => {{
        $( assert_errno_eq(stringify!($errno), $errno as c_int); )+
    }}
}
steveklabnik commented 8 years ago

/cc @rust-lang/lang, @rust-lang/compiler is there any discussion on what this limit should be?

eddyb commented 8 years ago

@steveklabnik IMO we should implement macro expansion without recursing in the compiler. That would mean it could potentially never terminate, but at least we wouldn't have errors like this. It could also be much more efficient, by reusing ranges of tokens instead of copying them over and over again.

jonas-schievink commented 8 years ago

@eddyb Isn't the recursion limit supposed to prevent never-terminating compilations? Being able to stack-overflow the compiler with a #![recursion_limit = "5000"] sounds bad. (I always thought it limits recursive user code, be it macros, monomorphizations, etc. and not rustc-internal recursion)

That said, the recursion limit isn't doing a very good job if this is what it's supposed to do:

macro_rules! m {
    ( $( $i:ident )* ) => {
        m!( $($i)* $($i)* )
    };
}

fn main() {
    let i = m!(m);
}

This hangs, but doesn't cause a stack overflow. Memory usage slowly increases starting at about 100 MB.

eddyb commented 8 years ago

Macros and monomorphization both recurse in the Rust compiler and would otherwise end up in stack overflows.

Mark-Simulacrum commented 7 years ago

This has been encountered in the wild once more (#38541).

I'm going to mark this as E-easy since I think the primary thing that needs to change is just this https://github.com/rust-lang/rust/blob/master/src/libsyntax/ext/expand.rs#L1041 line, which needs to be updated to a larger value. I'm going to suggest 1024.

@jseyfried Do you agree with the easy designation?

sirideain commented 7 years ago

I would be interested in taking this one as a good way to figure out the process of contributing.

Mark-Simulacrum commented 7 years ago

@sirideain Great! Let us know if you need assistance.

eddyb commented 7 years ago

cc @nikomatsakis Another good use for stacker!