From 4fa00c25076c34d7b6ddad9b918c6cd64ae783c3 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 11 Jul 2023 00:51:39 -0700 Subject: [PATCH 01/11] Explain SIGSEGV backtrace handler ...to both compiler contributors and other rustc invokers. The previous error messaging was extremely unfriendly, and potentially both confusing and alarming. The entire modules is extracted into a new file to help ease of reading, and plenty of comments get layered in. The design of the messaging is focused on preventing the overall purpose of the output from being too opaque in common cases, so users understand why it is there. There's not an immediate need for it being actionable, but some suggestions are offered anyways. --- compiler/rustc_driver_impl/src/lib.rs | 75 +++--------------- .../rustc_driver_impl/src/signal_handler.rs | 76 +++++++++++++++++++ 2 files changed, 85 insertions(+), 66 deletions(-) create mode 100644 compiler/rustc_driver_impl/src/signal_handler.rs diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 2e26ce111f0..026dfd8ee90 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -78,6 +78,15 @@ pub mod pretty; #[macro_use] mod print; mod session_diagnostics; +#[cfg(all(unix, any(target_env = "gnu", target_os = "macos")))] +mod signal_handler; + +#[cfg(not(all(unix, any(target_env = "gnu", target_os = "macos"))))] +mod signal_handler { + /// On platforms which don't support our signal handler's requirements, + /// simply use the default signal handler provided by std. + pub(super) fn install() {} +} use crate::session_diagnostics::{ RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch, @@ -1427,72 +1436,6 @@ pub fn init_env_logger(handler: &EarlyErrorHandler, env: &str) { } } -#[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() {} -} - pub fn main() -> ! { let start_time = Instant::now(); let start_rss = get_resident_set_size(); diff --git a/compiler/rustc_driver_impl/src/signal_handler.rs b/compiler/rustc_driver_impl/src/signal_handler.rs new file mode 100644 index 00000000000..1bf5c5af866 --- /dev/null +++ b/compiler/rustc_driver_impl/src/signal_handler.rs @@ -0,0 +1,76 @@ +//! Signal handler for rustc +//! Primarily used to extract a backtrace from stack overflow + +use std::alloc::{alloc, Layout}; +use std::{mem, ptr}; + +extern "C" { + fn backtrace_symbols_fd(buffer: *const *mut libc::c_void, size: libc::c_int, fd: libc::c_int); +} + +/// Signal handler installed for SIGSEGV +extern "C" fn print_stack_trace(_: libc::c_int) { + const MAX_FRAMES: usize = 256; + // Reserve data segment so we don't have to malloc in a signal handler, which might fail + // in incredibly undesirable and unexpected ways due to e.g. the allocator deadlocking + static mut STACK_TRACE: [*mut libc::c_void; MAX_FRAMES] = [ptr::null_mut(); MAX_FRAMES]; + unsafe { + // Collect return addresses + let depth = libc::backtrace(STACK_TRACE.as_mut_ptr(), MAX_FRAMES as i32); + if depth == 0 { + return; + } + // Just a stack trace is cryptic. Explain what we're doing. + write_raw_err("error: rustc interrupted by SIGSEGV, printing stack trace:\n\n"); + // Elaborate return addrs into symbols and write them directly to stderr + backtrace_symbols_fd(STACK_TRACE.as_ptr(), depth, libc::STDERR_FILENO); + if depth > 22 { + // We probably just scrolled that "we got SIGSEGV" message off the terminal + write_raw_err("\nerror: stack trace dumped due to SIGSEGV, possible stack overflow"); + }; + write_raw_err("\nerror: please report a bug to https://github.com/rust-lang/rust\n"); + } +} + +/// Write without locking stderr. +/// +/// Only acceptable because everything will end soon anyways. +fn write_raw_err(input: &str) { + // We do not care how many bytes we actually get out. SIGSEGV comes for our head. + // Splash stderr with letters of our own blood to warn our friends about the monster. + let _ = unsafe { libc::write(libc::STDERR_FILENO, input.as_ptr().cast(), input.len()) }; +} + +/// When SIGSEGV is delivered to the process, print a stack trace and then exit. +pub(super) fn install() { + unsafe { + let alt_stack_size: usize = min_sigstack_size() + 64 * 1024; + let mut alt_stack: libc::stack_t = 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, ptr::null_mut()); + + let mut sa: libc::sigaction = 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, 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 +} From 1e65b5b741719cc4a7bee80fc4c5299a95f8d631 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 12 Jul 2023 23:08:56 -0700 Subject: [PATCH 02/11] Add recursion detection to signal-safe backtrace This cleans up the formatting of the backtrace considerably, dodging the fact that a backtrace with recursion normally emits hundreds of lines. Instead, simply write repeated symbols, and add how many times the recursion occurred. Also, it makes it look more like the "normal" backtrace. Some fine details in the code are important for reducing the amount of possible panic branches. --- .../rustc_driver_impl/src/signal_handler.rs | 105 ++++++++++++++---- 1 file changed, 85 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_driver_impl/src/signal_handler.rs b/compiler/rustc_driver_impl/src/signal_handler.rs index 1bf5c5af866..5971562627c 100644 --- a/compiler/rustc_driver_impl/src/signal_handler.rs +++ b/compiler/rustc_driver_impl/src/signal_handler.rs @@ -2,43 +2,108 @@ //! Primarily used to extract a backtrace from stack overflow use std::alloc::{alloc, Layout}; -use std::{mem, ptr}; +use std::{fmt, mem, ptr}; extern "C" { fn backtrace_symbols_fd(buffer: *const *mut libc::c_void, size: libc::c_int, fd: libc::c_int); } +fn backtrace_stderr(buffer: &[*mut libc::c_void]) { + let size = buffer.len().try_into().unwrap_or_default(); + unsafe { backtrace_symbols_fd(buffer.as_ptr(), size, libc::STDERR_FILENO) }; +} + +/// Unbuffered, unsynchronized writer to stderr. +/// +/// Only acceptable because everything will end soon anyways. +struct RawStderr(()); + +impl fmt::Write for RawStderr { + fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { + let ret = unsafe { libc::write(libc::STDERR_FILENO, s.as_ptr().cast(), s.len()) }; + if ret == -1 { Err(fmt::Error) } else { Ok(()) } + } +} + +/// We don't really care how many bytes we actually get out. SIGSEGV comes for our head. +/// Splash stderr with letters of our own blood to warn our friends about the monster. +macro raw_errln($tokens:tt) { + let _ = ::core::fmt::Write::write_fmt(&mut RawStderr(()), format_args!($tokens)); + let _ = ::core::fmt::Write::write_char(&mut RawStderr(()), '\n'); +} + /// Signal handler installed for SIGSEGV extern "C" fn print_stack_trace(_: libc::c_int) { const MAX_FRAMES: usize = 256; // Reserve data segment so we don't have to malloc in a signal handler, which might fail // in incredibly undesirable and unexpected ways due to e.g. the allocator deadlocking static mut STACK_TRACE: [*mut libc::c_void; MAX_FRAMES] = [ptr::null_mut(); MAX_FRAMES]; - unsafe { + let stack = unsafe { // Collect return addresses let depth = libc::backtrace(STACK_TRACE.as_mut_ptr(), MAX_FRAMES as i32); if depth == 0 { return; } - // Just a stack trace is cryptic. Explain what we're doing. - write_raw_err("error: rustc interrupted by SIGSEGV, printing stack trace:\n\n"); - // Elaborate return addrs into symbols and write them directly to stderr - backtrace_symbols_fd(STACK_TRACE.as_ptr(), depth, libc::STDERR_FILENO); - if depth > 22 { - // We probably just scrolled that "we got SIGSEGV" message off the terminal - write_raw_err("\nerror: stack trace dumped due to SIGSEGV, possible stack overflow"); - }; - write_raw_err("\nerror: please report a bug to https://github.com/rust-lang/rust\n"); - } -} + &STACK_TRACE.as_slice()[0..(depth as _)] + }; -/// Write without locking stderr. -/// -/// Only acceptable because everything will end soon anyways. -fn write_raw_err(input: &str) { - // We do not care how many bytes we actually get out. SIGSEGV comes for our head. - // Splash stderr with letters of our own blood to warn our friends about the monster. - let _ = unsafe { libc::write(libc::STDERR_FILENO, input.as_ptr().cast(), input.len()) }; + // Just a stack trace is cryptic. Explain what we're doing. + raw_errln!("error: rustc interrupted by SIGSEGV, printing backtrace\n"); + let mut written = 1; + let mut consumed = 0; + // Begin elaborating return addrs into symbols and writing them directly to stderr + // Most backtraces are stack overflow, most stack overflows are from recursion + // Check for cycles before writing 250 lines of the same ~5 symbols + let cycled = |(runner, walker)| runner == walker; + let mut cyclic = false; + if let Some(period) = stack.iter().skip(1).step_by(2).zip(stack).position(cycled) { + let period = period.saturating_add(1); // avoid "what if wrapped?" branches + let Some(offset) = stack.iter().skip(period).zip(stack).position(cycled) else { + // impossible. + return; + }; + + // Count matching trace slices, else we could miscount "biphasic cycles" + // with the same period + loop entry but a different inner loop + let next_cycle = stack[offset..].chunks_exact(period).skip(1); + let cycles = 1 + next_cycle + .zip(stack[offset..].chunks_exact(period)) + .filter(|(next, prev)| next == prev) + .count(); + backtrace_stderr(&stack[..offset]); + written += offset; + consumed += offset; + if cycles > 1 { + raw_errln!("\n### cycle encountered after {offset} frames with period {period}"); + backtrace_stderr(&stack[consumed..consumed + period]); + raw_errln!("### recursed {cycles} times\n"); + written += period + 4; + consumed += period * cycles; + cyclic = true; + }; + } + let rem = &stack[consumed..]; + backtrace_stderr(rem); + raw_errln!(""); + written += rem.len() + 1; + + if cyclic || stack.len() == 256 { + // technically speculation, but assert it with confidence anyway. + // rustc only arrived in this signal handler because bad things happened + // and this message is for explaining it's not the programmer's fault + raw_errln!("note: rustc unexpectedly overflowed its stack! this is a bug"); + written += 1; + } + if stack.len() == 256 { + raw_errln!("note: maximum backtrace depth reached, frames may have been lost"); + written += 1; + } + raw_errln!("note: we would appreciate a report at https://github.com/rust-lang/rust"); + written += 1; + if written > 24 { + // We probably just scrolled the earlier "we got SIGSEGV" message off the terminal + raw_errln!("note: backtrace dumped due to SIGSEGV! resuming signal"); + }; } /// When SIGSEGV is delivered to the process, print a stack trace and then exit. From 3dee9775a8c94e701a08f7b2df2c444f353d8699 Mon Sep 17 00:00:00 2001 From: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Sat, 15 Jul 2023 01:07:14 -0700 Subject: [PATCH 03/11] Clarify arbitrary constants First, we reuse the `MAX_FRAMES` constant. Co-authored-by: erikdesjardins Then we choose an arbitrary recursion depth for the other case. In this case, I used 2d20. Honest. --- compiler/rustc_driver_impl/src/signal_handler.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_driver_impl/src/signal_handler.rs b/compiler/rustc_driver_impl/src/signal_handler.rs index 5971562627c..deca1082221 100644 --- a/compiler/rustc_driver_impl/src/signal_handler.rs +++ b/compiler/rustc_driver_impl/src/signal_handler.rs @@ -87,14 +87,15 @@ extern "C" fn print_stack_trace(_: libc::c_int) { raw_errln!(""); written += rem.len() + 1; - if cyclic || stack.len() == 256 { + let random_depth = || 8 * 16; // chosen by random diceroll (2d20) + if cyclic || stack.len() > random_depth() { // technically speculation, but assert it with confidence anyway. // rustc only arrived in this signal handler because bad things happened // and this message is for explaining it's not the programmer's fault raw_errln!("note: rustc unexpectedly overflowed its stack! this is a bug"); written += 1; } - if stack.len() == 256 { + if stack.len() == MAX_FRAMES { raw_errln!("note: maximum backtrace depth reached, frames may have been lost"); written += 1; } From 3ed435f8cbb1bde962b777edebc5637353b5b7ae Mon Sep 17 00:00:00 2001 From: bohan Date: Mon, 21 Aug 2023 21:05:01 +0800 Subject: [PATCH 04/11] discard dummy field for macro invocation when parse struct --- compiler/rustc_parse/src/parser/item.rs | 16 ++---- .../ui/parser/macro/macro-expand-to-field.rs | 33 +++++++----- .../parser/macro/macro-expand-to-field.stderr | 51 +++++++++---------- 3 files changed, 48 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 24c65d061f9..b76cf9bf8c2 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1851,21 +1851,11 @@ impl<'a> Parser<'a> { attrs: AttrVec, ) -> PResult<'a, FieldDef> { let name = self.parse_field_ident(adt_ty, lo)?; - // Parse the macro invocation and recover if self.token.kind == token::Not { if let Err(mut err) = self.unexpected::() { - err.subdiagnostic(MacroExpandsToAdtField { adt_ty }).emit(); - self.bump(); - self.parse_delim_args()?; - return Ok(FieldDef { - span: DUMMY_SP, - ident: None, - vis, - id: DUMMY_NODE_ID, - ty: self.mk_ty(DUMMY_SP, TyKind::Err), - attrs, - is_placeholder: false, - }); + // Encounter the macro invocation + err.subdiagnostic(MacroExpandsToAdtField { adt_ty }); + return Err(err); } } self.expect_field_ty_separator()?; diff --git a/tests/ui/parser/macro/macro-expand-to-field.rs b/tests/ui/parser/macro/macro-expand-to-field.rs index 155872f7a5d..533511ecf5a 100644 --- a/tests/ui/parser/macro/macro-expand-to-field.rs +++ b/tests/ui/parser/macro/macro-expand-to-field.rs @@ -1,5 +1,7 @@ // compile-flags: --crate-type=lib +// https://github.com/rust-lang/rust/issues/113766 + macro_rules! field { ($name:ident:$type:ty) => { $name:$type @@ -13,15 +15,14 @@ macro_rules! variant { } struct Struct { + //~^ NOTE while parsing this struct field!(bar:u128), //~^ NOTE macros cannot expand to struct fields //~| ERROR unexpected token: `!` //~| NOTE unexpected token after this a: u32, b: u32, - field!(recovers:()), //~ NOTE macros cannot expand to struct fields - //~^ ERROR unexpected token: `!` - //~^^ NOTE unexpected token after this + field!(recovers:()), } enum EnumVariant { @@ -35,7 +36,7 @@ enum EnumVariant { //~^ NOTE macros cannot expand to enum variants //~| ERROR unexpected token: `!` //~| NOTE unexpected token after this - Data { + Data { //~ NOTE while parsing this struct field!(x:u32), //~^ NOTE macros cannot expand to struct fields //~| ERROR unexpected token: `!` @@ -44,27 +45,35 @@ enum EnumVariant { } enum EnumVariantField { - Named { + Named { //~ NOTE while parsing this struct field!(oopsies:()), //~^ NOTE macros cannot expand to struct fields //~| ERROR unexpected token: `!` //~| unexpected token after this field!(oopsies2:()), - //~^ NOTE macros cannot expand to struct fields - //~| ERROR unexpected token: `!` - //~| unexpected token after this }, } union Union { + //~^ NOTE while parsing this union A: u32, field!(oopsies:()), //~^ NOTE macros cannot expand to union fields //~| ERROR unexpected token: `!` - //~| unexpected token after this + //~| NOTE unexpected token after this B: u32, field!(recovers:()), - //~^ NOTE macros cannot expand to union fields - //~| ERROR unexpected token: `!` - //~| unexpected token after this } + +// https://github.com/rust-lang/rust/issues/114636 + +#[derive(Debug)] +pub struct Lazy { + //~^ NOTE while parsing this struct + unreachable!() + //~^ NOTE macros cannot expand to struct fields + //~| ERROR unexpected token: `!` + //~| NOTE unexpected token after this +} + +fn main() {} diff --git a/tests/ui/parser/macro/macro-expand-to-field.stderr b/tests/ui/parser/macro/macro-expand-to-field.stderr index adcd032f5c0..0bb71800081 100644 --- a/tests/ui/parser/macro/macro-expand-to-field.stderr +++ b/tests/ui/parser/macro/macro-expand-to-field.stderr @@ -1,21 +1,16 @@ error: unexpected token: `!` - --> $DIR/macro-expand-to-field.rs:16:10 + --> $DIR/macro-expand-to-field.rs:19:10 | +LL | struct Struct { + | ------ while parsing this struct +LL | LL | field!(bar:u128), | ^ unexpected token after this | = note: macros cannot expand to struct fields error: unexpected token: `!` - --> $DIR/macro-expand-to-field.rs:22:10 - | -LL | field!(recovers:()), - | ^ unexpected token after this - | - = note: macros cannot expand to struct fields - -error: unexpected token: `!` - --> $DIR/macro-expand-to-field.rs:28:12 + --> $DIR/macro-expand-to-field.rs:29:12 | LL | variant!(whoops), | ^ unexpected token after this @@ -23,7 +18,7 @@ LL | variant!(whoops), = note: macros cannot expand to enum variants error: unexpected token: `!` - --> $DIR/macro-expand-to-field.rs:34:12 + --> $DIR/macro-expand-to-field.rs:35:12 | LL | variant!(recovers), | ^ unexpected token after this @@ -31,44 +26,46 @@ LL | variant!(recovers), = note: macros cannot expand to enum variants error: unexpected token: `!` - --> $DIR/macro-expand-to-field.rs:39:14 + --> $DIR/macro-expand-to-field.rs:40:14 | +LL | Data { + | ---- while parsing this struct LL | field!(x:u32), | ^ unexpected token after this | = note: macros cannot expand to struct fields error: unexpected token: `!` - --> $DIR/macro-expand-to-field.rs:48:14 + --> $DIR/macro-expand-to-field.rs:49:14 | +LL | Named { + | ----- while parsing this struct LL | field!(oopsies:()), | ^ unexpected token after this | = note: macros cannot expand to struct fields error: unexpected token: `!` - --> $DIR/macro-expand-to-field.rs:52:14 - | -LL | field!(oopsies2:()), - | ^ unexpected token after this - | - = note: macros cannot expand to struct fields - -error: unexpected token: `!` - --> $DIR/macro-expand-to-field.rs:61:10 + --> $DIR/macro-expand-to-field.rs:60:10 | +LL | union Union { + | ----- while parsing this union +... LL | field!(oopsies:()), | ^ unexpected token after this | = note: macros cannot expand to union fields error: unexpected token: `!` - --> $DIR/macro-expand-to-field.rs:66:10 + --> $DIR/macro-expand-to-field.rs:73:16 | -LL | field!(recovers:()), - | ^ unexpected token after this +LL | pub struct Lazy { + | ---- while parsing this struct +LL | +LL | unreachable!() + | ^ unexpected token after this | - = note: macros cannot expand to union fields + = note: macros cannot expand to struct fields -error: aborting due to 9 previous errors +error: aborting due to 7 previous errors From beeb2b13cc40a550d2bc60361d6fa77cfb02e698 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 27 Aug 2023 14:41:35 +0200 Subject: [PATCH 05/11] miri/diagnostics: don't forget to print_backtrace when ICEing on unexpected errors then also use the new helper in a few other places --- .../src/interpret/eval_context.rs | 23 ++++++- .../rustc_const_eval/src/interpret/intern.rs | 3 +- .../src/interpret/validity.rs | 7 +- .../src/const_prop_lint.rs | 3 +- src/tools/miri/src/diagnostics.rs | 65 ++++++++----------- 5 files changed, 57 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 90848dbfbc7..bd3d87470c9 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -25,8 +25,8 @@ use super::{ Scalar, StackPopJump, }; use crate::errors::{self, ErroneousConstUsed}; -use crate::fluent_generated as fluent; use crate::util; +use crate::{fluent_generated as fluent, ReportErrorExt}; pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> { /// Stores the `Machine` instance. @@ -432,6 +432,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .map_or(CRATE_HIR_ID, |def_id| self.tcx.hir().local_def_id_to_hir_id(def_id)) } + /// Turn the given error into a human-readable string. Expects the string to be printed, so if + /// `RUSTC_CTFE_BACKTRACE` is set this will show a backtrace of the rustc internals that + /// triggered the error. + /// + /// This is NOT the preferred way to render an error; use `report` from `const_eval` instead. + /// However, this is useful when error messages appear in ICEs. + pub fn format_error(&self, e: InterpErrorInfo<'tcx>) -> String { + let (e, backtrace) = e.into_parts(); + backtrace.print_backtrace(); + // FIXME(fee1-dead), HACK: we want to use the error as title therefore we can just extract the + // label and arguments from the InterpError. + let handler = &self.tcx.sess.parse_sess.span_diagnostic; + #[allow(rustc::untranslatable_diagnostic)] + let mut diag = self.tcx.sess.struct_allow(""); + let msg = e.diagnostic_message(); + e.add_args(handler, &mut diag); + let s = handler.eagerly_translate_to_string(msg, diag.args()); + diag.cancel(); + s + } + #[inline(always)] pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>] { M::stack(self) diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 910c3ca5d0a..42950d1ffb0 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -378,7 +378,8 @@ pub fn intern_const_alloc_recursive< ecx.tcx.sess.delay_span_bug( ecx.tcx.span, format!( - "error during interning should later cause validation failure: {error:?}" + "error during interning should later cause validation failure: {}", + ecx.format_error(error), ), ); } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index d3f05af1c72..0d08d6be919 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -911,9 +911,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Complain about any other kind of error -- those are bad because we'd like to // report them in a way that shows *where* in the value the issue lies. Err(err) => { - let (err, backtrace) = err.into_parts(); - backtrace.print_backtrace(); - bug!("Unexpected Undefined Behavior error during validation: {err:?}"); + bug!( + "Unexpected Undefined Behavior error during validation: {}", + self.format_error(err) + ); } } } diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index da8913d604b..755b3985791 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -273,7 +273,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // dedicated error variants should be introduced instead. assert!( !error.kind().formatted_string(), - "const-prop encountered formatting error: {error:?}", + "const-prop encountered formatting error: {}", + self.ecx.format_error(error), ); None } diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index f3285ccf917..799a9a8e4be 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -1,9 +1,8 @@ -use std::fmt; +use std::fmt::{self, Write}; use std::num::NonZeroU64; use log::trace; -use rustc_const_eval::ReportErrorExt; use rustc_errors::DiagnosticMessage; use rustc_span::{source_map::DUMMY_SP, SpanData, Symbol}; use rustc_target::abi::{Align, Size}; @@ -271,10 +270,13 @@ pub fn report_error<'tcx, 'mir>( }; (title, helps) } else { - #[rustfmt::skip] let title = match e.kind() { - UndefinedBehavior(UndefinedBehaviorInfo::ValidationError(e)) if matches!(e.kind, ValidationErrorKind::PointerAsInt { .. } | ValidationErrorKind::PartialPointer) => - bug!("This validation error should be impossible in Miri: {:?}", e.kind), + UndefinedBehavior(UndefinedBehaviorInfo::ValidationError(validation_err)) + if matches!(validation_err.kind, ValidationErrorKind::PointerAsInt { .. } | ValidationErrorKind::PartialPointer) => + { + ecx.handle_ice(); // print interpreter backtrace + bug!("This validation error should be impossible in Miri: {}", ecx.format_error(e)); + } UndefinedBehavior(_) => "Undefined Behavior", ResourceExhaustion(_) => @@ -290,8 +292,10 @@ pub fn report_error<'tcx, 'mir>( InvalidProgramInfo::Layout(..) ) => "post-monomorphization error", - kind => - bug!("This error should be impossible in Miri: {kind:?}"), + _ => { + ecx.handle_ice(); // print interpreter backtrace + bug!("This error should be impossible in Miri: {}", ecx.format_error(e)); + } }; #[rustfmt::skip] let helps = match e.kind() { @@ -333,30 +337,22 @@ pub fn report_error<'tcx, 'mir>( let stacktrace = ecx.generate_stacktrace(); let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine); - let (e, backtrace) = e.into_parts(); - backtrace.print_backtrace(); - // We want to dump the allocation if this is `InvalidUninitBytes`. Since `add_args` consumes - // the `InterpError`, we extract the variables it before that. - let extra = match e { - UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some((alloc_id, access)))) => - Some((alloc_id, access)), - _ => None, - }; + // We want to dump the allocation if this is `InvalidUninitBytes`. Since `format_error` consumes `e`, we compute the outut early. + let mut extra = String::new(); + match e.kind() { + UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some((alloc_id, access)))) => { + writeln!( + extra, + "Uninitialized memory occurred at {alloc_id:?}{range:?}, in this allocation:", + range = access.bad, + ).unwrap(); + writeln!(extra, "{:?}", ecx.dump_alloc(*alloc_id)).unwrap(); + } + _ => {} + } - // FIXME(fee1-dead), HACK: we want to use the error as title therefore we can just extract the - // label and arguments from the InterpError. - let e = { - let handler = &ecx.tcx.sess.parse_sess.span_diagnostic; - let mut diag = ecx.tcx.sess.struct_allow(""); - let msg = e.diagnostic_message(); - e.add_args(handler, &mut diag); - let s = handler.eagerly_translate_to_string(msg, diag.args()); - diag.cancel(); - s - }; - - msg.insert(0, e); + msg.insert(0, ecx.format_error(e)); report_msg( DiagLevel::Error, @@ -375,6 +371,8 @@ pub fn report_error<'tcx, 'mir>( ); } + eprint!("{extra}"); // newlines are already in the string + // Debug-dump all locals. for (i, frame) in ecx.active_thread_stack().iter().enumerate() { trace!("-------------------"); @@ -385,15 +383,6 @@ pub fn report_error<'tcx, 'mir>( } } - // Extra output to help debug specific issues. - if let Some((alloc_id, access)) = extra { - eprintln!( - "Uninitialized memory occurred at {alloc_id:?}{range:?}, in this allocation:", - range = access.bad, - ); - eprintln!("{:?}", ecx.dump_alloc(alloc_id)); - } - None } From 507f10baee9ea0b884785aa4f54e3ffa6a1d82b2 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Tue, 29 Aug 2023 17:10:27 +0200 Subject: [PATCH 06/11] suggest removing `impl` in generic trait bound position --- compiler/rustc_parse/src/parser/ty.rs | 36 +++++++++++++------ .../suggest-removing-impl.rs | 14 ++++++++ .../suggest-removing-impl.stderr | 26 ++++++++++++++ 3 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 tests/ui/associated-type-bounds/suggest-removing-impl.rs create mode 100644 tests/ui/associated-type-bounds/suggest-removing-impl.stderr diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 661113666cd..a25b0f1f893 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -891,18 +891,32 @@ impl<'a> Parser<'a> { // that we do not use the try operator when parsing the type because // if it fails then we get a parser error which we don't want (we're trying // to recover from errors, not make more). - let path = if self.may_recover() - && matches!(ty.kind, TyKind::Ptr(..) | TyKind::Ref(..)) - && let TyKind::Path(_, path) = &ty.peel_refs().kind { - // Just get the indirection part of the type. - let span = ty.span.until(path.span); + let path = if self.may_recover() { + let (span, message, sugg, path, applicability) = match &ty.kind { + TyKind::Ptr(..) | TyKind::Ref(..) if let TyKind::Path(_, path) = &ty.peel_refs().kind => { + ( + ty.span.until(path.span), + "consider removing the indirection", + "", + path, + Applicability::MaybeIncorrect + ) + } + TyKind::ImplTrait(_, bounds) + if let [GenericBound::Trait(tr, ..), ..] = bounds.as_slice() => + { + ( + ty.span.until(tr.span), + "use the trait bounds directly", + "", + &tr.trait_ref.path, + Applicability::MachineApplicable + ) + } + _ => return Err(err) + }; - err.span_suggestion_verbose( - span, - "consider removing the indirection", - "", - Applicability::MaybeIncorrect, - ); + err.span_suggestion_verbose(span, message, sugg, applicability); path.clone() } else { diff --git a/tests/ui/associated-type-bounds/suggest-removing-impl.rs b/tests/ui/associated-type-bounds/suggest-removing-impl.rs new file mode 100644 index 00000000000..242cd85727d --- /dev/null +++ b/tests/ui/associated-type-bounds/suggest-removing-impl.rs @@ -0,0 +1,14 @@ +trait Tr { + type Assoc: impl Sized; + //~^ ERROR expected a trait, found type + //~| HELP use the trait bounds directly + + fn fn_with_generics() + where + T: impl Sized + //~^ ERROR expected a trait, found type + //~| HELP use the trait bounds directly + {} +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/suggest-removing-impl.stderr b/tests/ui/associated-type-bounds/suggest-removing-impl.stderr new file mode 100644 index 00000000000..875b2db6deb --- /dev/null +++ b/tests/ui/associated-type-bounds/suggest-removing-impl.stderr @@ -0,0 +1,26 @@ +error: expected a trait, found type + --> $DIR/suggest-removing-impl.rs:2:17 + | +LL | type Assoc: impl Sized; + | ^^^^^^^^^^ + | +help: use the trait bounds directly + | +LL - type Assoc: impl Sized; +LL + type Assoc: Sized; + | + +error: expected a trait, found type + --> $DIR/suggest-removing-impl.rs:8:12 + | +LL | T: impl Sized + | ^^^^^^^^^^ + | +help: use the trait bounds directly + | +LL - T: impl Sized +LL + T: Sized + | + +error: aborting due to 2 previous errors + From 7762ac7bb5ac10046a5a9ee838480a78bf150237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Tue, 29 Aug 2023 19:02:24 +0000 Subject: [PATCH 07/11] handle edge-case of a recursion limit of 0 --- .../rustc_trait_selection/src/solve/search_graph/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index 49ebfa4e6cb..ca3c64b428e 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -51,9 +51,13 @@ pub(super) struct SearchGraph<'tcx> { impl<'tcx> SearchGraph<'tcx> { pub(super) fn new(tcx: TyCtxt<'tcx>, mode: SolverMode) -> SearchGraph<'tcx> { + let local_overflow_limit = { + let recursion_limit = tcx.recursion_limit().0; + if recursion_limit == 0 { 0 } else { recursion_limit.ilog2() as usize } + }; Self { mode, - local_overflow_limit: tcx.recursion_limit().0.ilog2() as usize, + local_overflow_limit, stack: Default::default(), provisional_cache: ProvisionalCache::empty(), } From 325b585259871c99093b2a2e9463f941b8aa0ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Tue, 29 Aug 2023 19:03:08 +0000 Subject: [PATCH 08/11] add non-regression test for issue 115351 --- .../recursion-limit-zero-issue-115351.rs | 12 +++++++++ .../recursion-limit-zero-issue-115351.stderr | 27 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/ui/traits/new-solver/overflow/recursion-limit-zero-issue-115351.rs create mode 100644 tests/ui/traits/new-solver/overflow/recursion-limit-zero-issue-115351.stderr diff --git a/tests/ui/traits/new-solver/overflow/recursion-limit-zero-issue-115351.rs b/tests/ui/traits/new-solver/overflow/recursion-limit-zero-issue-115351.rs new file mode 100644 index 00000000000..539c9614e82 --- /dev/null +++ b/tests/ui/traits/new-solver/overflow/recursion-limit-zero-issue-115351.rs @@ -0,0 +1,12 @@ +//~ ERROR overflow evaluating the requirement `Self well-formed` +//~| ERROR overflow evaluating the requirement `Self: Trait` + +// This is a non-regression test for issue #115351, where a recursion limit of 0 caused an ICE. +// compile-flags: -Ztrait-solver=next --crate-type=lib +// check-fail + +#![recursion_limit = "0"] +trait Trait {} +impl Trait for u32 {} +//~^ ERROR overflow evaluating the requirement `u32: Trait` +//~| ERROR overflow evaluating the requirement `u32 well-formed` diff --git a/tests/ui/traits/new-solver/overflow/recursion-limit-zero-issue-115351.stderr b/tests/ui/traits/new-solver/overflow/recursion-limit-zero-issue-115351.stderr new file mode 100644 index 00000000000..16b25d90ace --- /dev/null +++ b/tests/ui/traits/new-solver/overflow/recursion-limit-zero-issue-115351.stderr @@ -0,0 +1,27 @@ +error[E0275]: overflow evaluating the requirement `Self: Trait` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`recursion_limit_zero_issue_115351`) + +error[E0275]: overflow evaluating the requirement `Self well-formed` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`recursion_limit_zero_issue_115351`) + +error[E0275]: overflow evaluating the requirement `u32: Trait` + --> $DIR/recursion-limit-zero-issue-115351.rs:10:16 + | +LL | impl Trait for u32 {} + | ^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`recursion_limit_zero_issue_115351`) + +error[E0275]: overflow evaluating the requirement `u32 well-formed` + --> $DIR/recursion-limit-zero-issue-115351.rs:10:16 + | +LL | impl Trait for u32 {} + | ^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`recursion_limit_zero_issue_115351`) + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0275`. From 7b837e075a335d482c583b574ebaf5006738655e Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Tue, 29 Aug 2023 13:41:33 -0700 Subject: [PATCH 09/11] Don't suggest adding parentheses to call an inaccessible method. Previously, the test code would emit E0615, thus revealing the existence of private methods that the programmer probably does not care about. Now it ignores their existence instead, producing error E0609 (no field). The motivating example is: ```rust let x = std::rc::Rc::new(()); x.inner; ``` which would previously mention the private method `Rc::inner()`, even though `Rc` intentionally has no public methods so that it can be a transparent smart pointer for any `T`. --- compiler/rustc_hir_typeck/src/expr.rs | 2 +- tests/ui/error-codes/E0609-private-method.rs | 16 ++++++++++++++++ tests/ui/error-codes/E0609-private-method.stderr | 9 +++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/ui/error-codes/E0609-private-method.rs create mode 100644 tests/ui/error-codes/E0609-private-method.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 7cea40fdd64..72c58bf1c2d 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2314,7 +2314,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { field, base_ty, expr.hir_id, - true, + false, // don't mention or suggest non-accessible methods expected.only_has_type(self), ) { self.ban_take_value_of_method(expr, base_ty, field) diff --git a/tests/ui/error-codes/E0609-private-method.rs b/tests/ui/error-codes/E0609-private-method.rs new file mode 100644 index 00000000000..dfa97ad9a6f --- /dev/null +++ b/tests/ui/error-codes/E0609-private-method.rs @@ -0,0 +1,16 @@ +// This error is an E0609 and *not* an E0615 because the fact that the method exists is not +// relevant. +mod foo { + pub struct Foo { + x: u32, + } + + impl Foo { + fn method(&self) {} + } +} + +fn main() { + let f = foo::Foo { x: 0 }; + f.method; //~ ERROR E0609 +} diff --git a/tests/ui/error-codes/E0609-private-method.stderr b/tests/ui/error-codes/E0609-private-method.stderr new file mode 100644 index 00000000000..d2a11e90627 --- /dev/null +++ b/tests/ui/error-codes/E0609-private-method.stderr @@ -0,0 +1,9 @@ +error[E0609]: no field `method` on type `Foo` + --> $DIR/E0609-private-method.rs:15:7 + | +LL | f.method; + | ^^^^^^ unknown field + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0609`. From 4e9a2a6ff64371a4141c106973a748cfe9625f3d Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Tue, 29 Aug 2023 15:33:12 -0700 Subject: [PATCH 10/11] Remove `allow_private` entirely. --- compiler/rustc_hir_typeck/src/expr.rs | 10 ++-------- compiler/rustc_hir_typeck/src/method/mod.rs | 5 ++--- compiler/rustc_hir_typeck/src/method/suggest.rs | 3 +-- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 72c58bf1c2d..a3b8c391e02 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2310,13 +2310,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let guar = if field.name == kw::Empty { self.tcx.sess.delay_span_bug(field.span, "field name with no name") - } else if self.method_exists( - field, - base_ty, - expr.hir_id, - false, // don't mention or suggest non-accessible methods - expected.only_has_type(self), - ) { + } else if self.method_exists(field, base_ty, expr.hir_id, expected.only_has_type(self)) { self.ban_take_value_of_method(expr, base_ty, field) } else if !base_ty.is_primitive_ty() { self.ban_nonexisting_field(field, base, expr, base_ty) @@ -2501,7 +2495,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err = self.private_field_err(field, base_did); // Also check if an accessible method exists, which is often what is meant. - if self.method_exists(field, expr_t, expr.hir_id, false, return_ty) + if self.method_exists(field, expr_t, expr.hir_id, return_ty) && !self.expr_in_place(expr.hir_id) { self.suggest_method_call( diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 6dd131aa283..86a0e95de1d 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -89,14 +89,13 @@ pub enum CandidateSource { } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Determines whether the type `self_ty` supports a method name `method_name` or not. + /// Determines whether the type `self_ty` supports a visible method named `method_name` or not. #[instrument(level = "debug", skip(self))] pub fn method_exists( &self, method_name: Ident, self_ty: Ty<'tcx>, call_expr_id: hir::HirId, - allow_private: bool, return_type: Option>, ) -> bool { match self.probe_for_name( @@ -118,7 +117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Err(NoMatch(..)) => false, Err(Ambiguity(..)) => true, - Err(PrivateMatch(..)) => allow_private, + Err(PrivateMatch(..)) => false, Err(IllegalSizedBound { .. }) => true, Err(BadReturnType) => false, } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 72a04a02bf4..07c48ec6392 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2361,8 +2361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(output_ty) => self.resolve_vars_if_possible(output_ty), _ => return, }; - let method_exists = - self.method_exists(item_name, output_ty, call.hir_id, true, return_type); + let method_exists = self.method_exists(item_name, output_ty, call.hir_id, return_type); debug!("suggest_await_before_method: is_method_exist={}", method_exists); if method_exists { err.span_suggestion_verbose( From 136f0579d8a6cfacbdbfbac4c6a10b8447fcdf9e Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Wed, 30 Aug 2023 07:40:08 +0530 Subject: [PATCH 11/11] Make get_return_block() return Some only for HIR nodes in body Fixes # 114918 --- compiler/rustc_middle/src/hir/map/mod.rs | 13 +++++++++++- .../issue-114918/const-in-fn-return-type.rs | 10 ++++++++++ .../const-in-fn-return-type.stderr | 9 +++++++++ .../const-in-impl-fn-return-type.rs | 20 +++++++++++++++++++ .../const-in-impl-fn-return-type.stderr | 9 +++++++++ .../issue-114918/const-in-struct-type-arg.rs | 12 +++++++++++ .../const-in-struct-type-arg.stderr | 9 +++++++++ .../const-in-trait-fn-return-type.rs | 13 ++++++++++++ .../const-in-trait-fn-return-type.stderr | 9 +++++++++ 9 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 tests/ui/typeck/issue-114918/const-in-fn-return-type.rs create mode 100644 tests/ui/typeck/issue-114918/const-in-fn-return-type.stderr create mode 100644 tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs create mode 100644 tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.stderr create mode 100644 tests/ui/typeck/issue-114918/const-in-struct-type-arg.rs create mode 100644 tests/ui/typeck/issue-114918/const-in-struct-type-arg.stderr create mode 100644 tests/ui/typeck/issue-114918/const-in-trait-fn-return-type.rs create mode 100644 tests/ui/typeck/issue-114918/const-in-trait-fn-return-type.stderr diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 467962b39bb..81de70c03d4 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -701,6 +701,8 @@ impl<'hir> Map<'hir> { // expressions. ignore_tail = true; } + + let mut prev_hir_id = None; while let Some((hir_id, node)) = iter.next() { if let (Some((_, next_node)), false) = (iter.peek(), ignore_tail) { match next_node { @@ -715,7 +717,14 @@ impl<'hir> Map<'hir> { | Node::ForeignItem(_) | Node::TraitItem(_) | Node::Expr(Expr { kind: ExprKind::Closure { .. }, .. }) - | Node::ImplItem(_) => return Some(hir_id), + | Node::ImplItem(_) + // The input node `id` must be enclosed in the method's body as opposed + // to some other place such as its return type (fixes #114918). + // We verify that indirectly by checking that the previous node is the + // current node's body + if node.body_id().map(|b| b.hir_id) == prev_hir_id => { + return Some(hir_id) + } // Ignore `return`s on the first iteration Node::Expr(Expr { kind: ExprKind::Loop(..) | ExprKind::Ret(..), .. }) | Node::Local(_) => { @@ -723,6 +732,8 @@ impl<'hir> Map<'hir> { } _ => {} } + + prev_hir_id = Some(hir_id); } None } diff --git a/tests/ui/typeck/issue-114918/const-in-fn-return-type.rs b/tests/ui/typeck/issue-114918/const-in-fn-return-type.rs new file mode 100644 index 00000000000..d939633290e --- /dev/null +++ b/tests/ui/typeck/issue-114918/const-in-fn-return-type.rs @@ -0,0 +1,10 @@ +// Regression test for #114918 +// Test that a const generic enclosed in a block within a return type +// produces a type mismatch error instead of triggering a const eval cycle + +#[allow(unused_braces)] +fn func() -> [u8; { () } ] { //~ ERROR mismatched types + loop {} +} + +fn main() {} diff --git a/tests/ui/typeck/issue-114918/const-in-fn-return-type.stderr b/tests/ui/typeck/issue-114918/const-in-fn-return-type.stderr new file mode 100644 index 00000000000..88ed96e148c --- /dev/null +++ b/tests/ui/typeck/issue-114918/const-in-fn-return-type.stderr @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + --> $DIR/const-in-fn-return-type.rs:6:21 + | +LL | fn func() -> [u8; { () } ] { + | ^^ expected `usize`, found `()` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs new file mode 100644 index 00000000000..a1b9a7eba4d --- /dev/null +++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs @@ -0,0 +1,20 @@ +// Regression test for #114918 +// Test that a const generic enclosed in a block within the return type +// of an impl fn produces a type mismatch error instead of triggering +// a const eval cycle + + +trait Trait { + fn func() -> [ (); N ]; +} + +struct S {} + +#[allow(unused_braces)] +impl Trait for S { + fn func() -> [ (); { () }] { //~ ERROR mismatched types + N + } +} + +fn main() {} diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.stderr b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.stderr new file mode 100644 index 00000000000..9843651b1e6 --- /dev/null +++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.stderr @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + --> $DIR/const-in-impl-fn-return-type.rs:15:40 + | +LL | fn func() -> [ (); { () }] { + | ^^ expected `usize`, found `()` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/issue-114918/const-in-struct-type-arg.rs b/tests/ui/typeck/issue-114918/const-in-struct-type-arg.rs new file mode 100644 index 00000000000..9eee4ab3d4c --- /dev/null +++ b/tests/ui/typeck/issue-114918/const-in-struct-type-arg.rs @@ -0,0 +1,12 @@ +// Regression test for #114918 +// Test that a const generic enclosed in a block in a struct's type arg +// produces a type mismatch error instead of triggering a const eval cycle + +#[allow(unused_braces)] +struct S { + arr: [u8; N] +} + +fn main() { + let s = S::<{ () }> { arr: [5, 6, 7]}; //~ ERROR mismatched types +} diff --git a/tests/ui/typeck/issue-114918/const-in-struct-type-arg.stderr b/tests/ui/typeck/issue-114918/const-in-struct-type-arg.stderr new file mode 100644 index 00000000000..3307e76d957 --- /dev/null +++ b/tests/ui/typeck/issue-114918/const-in-struct-type-arg.stderr @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + --> $DIR/const-in-struct-type-arg.rs:11:19 + | +LL | let s = S::<{ () }> { arr: [5, 6, 7]}; + | ^^ expected `usize`, found `()` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/issue-114918/const-in-trait-fn-return-type.rs b/tests/ui/typeck/issue-114918/const-in-trait-fn-return-type.rs new file mode 100644 index 00000000000..8e2eec34911 --- /dev/null +++ b/tests/ui/typeck/issue-114918/const-in-trait-fn-return-type.rs @@ -0,0 +1,13 @@ +// Regression test for #114918 +// Test that a const generic enclosed in a block within the return type +// of a trait method produces a type mismatch error instead of triggering +// a const eval cycle + +#[allow(unused_braces)] +trait Trait { + fn func() -> [ (); { () }] { //~ ERROR mismatched types + N + } +} + +fn main() {} diff --git a/tests/ui/typeck/issue-114918/const-in-trait-fn-return-type.stderr b/tests/ui/typeck/issue-114918/const-in-trait-fn-return-type.stderr new file mode 100644 index 00000000000..6bc0de77a62 --- /dev/null +++ b/tests/ui/typeck/issue-114918/const-in-trait-fn-return-type.stderr @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + --> $DIR/const-in-trait-fn-return-type.rs:8:40 + | +LL | fn func() -> [ (); { () }] { + | ^^ expected `usize`, found `()` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`.