Code change:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. --Excessive (extremely excessive) chaining of operators causes a stack overflow in rustc. In my experimenting I found that this happens with precisely 6115 chained operators in the playground, but it appears to depend on your compilation environment as it took approx. 8000 on my machine. This is easily avoided by splitting the chain (or having an iota of sanity), but I would expect to at least exit gracefully in this case. The code below uses only the Not () operator, but at least the Neg () operator also works, as does mixing the two (ie ). <details<summaryCode</summary</details<details<summaryLocal (Windows) Error Message</summary</detailsThe playground error message was too long to include here, but a copy can be found in this gist: <!-- If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. --The above code was tested in the playground (Debug, Stable, v1.68.1), and a version with 8000 chained operators crashed on my local machine with the following :
Technically, it did (in the Windows case). The stack overflow was caught by Rust's handler and a message emitted to explain that it had reached stack overflow, instead of simply exiting without remark. This fulfills the letter of your expectation for it to "exit gracefully", but perhaps not your intent. You may wish to clarify how graceful an exit you were hoping for. It may also be interesting to know if you somehow found a way to use this to cause a stack overflow that did not emit such a message. I am not sure how to interpret the Playground case, honestly, perhaps that qualifies... Possibly we can manage to avoid overflowing, in any case. I only make such a pedantic note because it can be hard to do better in the face of resource exhaustion.
That's a very fair point. I had actually done the playground one first after someone on Reddit mentioned this, and only tried on windows while writing this. What's weird is that while windows was able to at least give me a general idea of what happened, the playground straight died with a SIGSEGV.
The way the Playground one goes, I really wondered if there is something that happens due to libc??? But no, the backtrace is really thousands of lines like this:
Disregard that, I was talking nonsense and looking at the wrong values. Stack offset is only a bit over ~1KB but consistently is about ~1KB each time. I think the problem is more just "no attempt to guard the final page" or something like that.
Well, I see this note in So that might be a possible fix here (if it's not already called in there).
Diagnosing stack overflows is imperfect on Linux. It's always a at first, and then we have a signal handler that tries to decide if that fault looks like it hit the stack guard page so we can report a nicer abort. But the kernel manages the guard page in the main thread with its dynamically growing stack, so we have to try to guess where that may be. We should do better with fixed thread stacks -- but I think also messes with these heuristics.
Example now compiles with... more operators, since , but still meets its demise and that grody error. Why? Because it hits the parser: Note that the signal handler dumps that huge error message on Linux because we install a custom signal handler, which feels like it was written in the Medieval Rust Era or something. On Windows, we simply wind up using std's signal handler, which prints that simple message. So I guess to resolve this bug we should improve the error message or simply delete that code.
I have opened to make the error message less cryptic, when that merges I think we should declare victory on this.
Victory declared.

Related discussion:
text: } } <del> #[cfg(all(unix, any(target_env = "gnu", target_os = "macos")))] mod signal_handler { extern "C" { fn backtrace_symbols_fd( buffer: *const *mut libc::c_void, size: libc::c_int, fd: libc::c_int, ); } extern "C" fn print_stack_trace(_: libc::c_int) { const MAX_FRAMES: usize = 256; static mut STACK_TRACE: [*mut libc::c_void; MAX_FRAMES] = [std::ptr::null_mut(); MAX_FRAMES]; unsafe { let depth = libc::backtrace(STACK_TRACE.as_mut_ptr(), MAX_FRAMES as i32); if depth == 0 { return; } backtrace_symbols_fd(STACK_TRACE.as_ptr(), depth, 2); } } /// When an error signal (such as SIGABRT or SIGSEGV) is delivered to the /// process, print a stack trace and then exit. pub(super) fn install() { use std::alloc::{alloc, Layout}; unsafe { let alt_stack_size: usize = min_sigstack_size() + 64 * 1024; let mut alt_stack: libc::stack_t = std::mem::zeroed(); alt_stack.ss_sp = alloc(Layout::from_size_align(alt_stack_size, 1).unwrap()).cast(); alt_stack.ss_size = alt_stack_size; libc::sigaltstack(&alt_stack, std::ptr::null_mut()); let mut sa: libc::sigaction = std::mem::zeroed(); sa.sa_sigaction = print_stack_trace as libc::sighandler_t; sa.sa_flags = libc::SA_NODEFER | libc::SA_RESETHAND | libc::SA_ONSTACK; libc::sigemptyset(&mut sa.sa_mask); libc::sigaction(libc::SIGSEGV, &sa, std::ptr::null_mut()); } } /// Modern kernels on modern hardware can have dynamic signal stack sizes. #[cfg(any(target_os = "linux", target_os = "android"))] fn min_sigstack_size() -> usize { const AT_MINSIGSTKSZ: core::ffi::c_ulong = 51; let dynamic_sigstksz = unsafe { libc::getauxval(AT_MINSIGSTKSZ) }; // If getauxval couldn't find the entry, it returns 0, // so take the higher of the "constant" and auxval. // This transparently supports older kernels which don't provide AT_MINSIGSTKSZ libc::MINSIGSTKSZ.max(dynamic_sigstksz as _) } /// Not all OS support hardware where this is needed. #[cfg(not(any(target_os = "linux", target_os = "android")))] fn min_sigstack_size() -> usize { libc::MINSIGSTKSZ } } #[cfg(not(all(unix, any(target_env = "gnu", target_os = "macos"))))] mod signal_handler { pub(super) fn install() {} } </del> pub fn main() -> ! { let start_time = Instant::now(); let start_rss = get_resident_set_size();