Skip to content

Rollup of 7 pull requests #115370

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

Merged
merged 18 commits into from
Aug 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
4fa00c2
Explain SIGSEGV backtrace handler
workingjubilee Jul 11, 2023
1e65b5b
Add recursion detection to signal-safe backtrace
workingjubilee Jul 13, 2023
3dee977
Clarify arbitrary constants
workingjubilee Jul 15, 2023
3ed435f
discard dummy field for macro invocation when parse struct
bvanjoi Aug 21, 2023
beeb2b1
miri/diagnostics: don't forget to print_backtrace when ICEing on unex…
RalfJung Aug 27, 2023
507f10b
suggest removing `impl` in generic trait bound position
y21 Aug 29, 2023
7762ac7
handle edge-case of a recursion limit of 0
lqd Aug 29, 2023
325b585
add non-regression test for issue 115351
lqd Aug 29, 2023
7b837e0
Don't suggest adding parentheses to call an inaccessible method.
kpreid Aug 29, 2023
4e9a2a6
Remove `allow_private` entirely.
kpreid Aug 29, 2023
136f057
Make get_return_block() return Some only for HIR nodes in body
gurry Aug 30, 2023
dafea5f
Rollup merge of #113565 - workingjubilee:better-signal-handler-messag…
matthiaskrgr Aug 30, 2023
6391165
Rollup merge of #114704 - bvanjoi:fix-114636, r=compiler-errors
matthiaskrgr Aug 30, 2023
23f8625
Rollup merge of #115272 - RalfJung:miri-error-print, r=saethlin
matthiaskrgr Aug 30, 2023
2128efd
Rollup merge of #115313 - gurry:issue-114918-cycle-detected, r=compil…
matthiaskrgr Aug 30, 2023
58c6907
Rollup merge of #115347 - y21:generic-bound-impl-trait-ty, r=compiler…
matthiaskrgr Aug 30, 2023
36182f1
Rollup merge of #115355 - lqd:issue-115351, r=compiler-errors
matthiaskrgr Aug 30, 2023
ea23478
Rollup merge of #115363 - kpreid:suggest-private, r=compiler-errors
matthiaskrgr Aug 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion compiler/rustc_const_eval/src/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_const_eval/src/interpret/intern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
),
);
}
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_const_eval/src/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
);
}
}
}
Expand Down
75 changes: 9 additions & 66 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,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,
Expand Down Expand Up @@ -1440,72 +1449,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();
Expand Down
142 changes: 142 additions & 0 deletions compiler/rustc_driver_impl/src/signal_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//! Signal handler for rustc
//! Primarily used to extract a backtrace from stack overflow

use std::alloc::{alloc, Layout};
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];
let stack = unsafe {
// Collect return addresses
let depth = libc::backtrace(STACK_TRACE.as_mut_ptr(), MAX_FRAMES as i32);
if depth == 0 {
return;
}
&STACK_TRACE.as_slice()[0..(depth as _)]
};

// 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;

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() == MAX_FRAMES {
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.
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
}
10 changes: 2 additions & 8 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
true,
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)
Expand Down Expand Up @@ -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(
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_hir_typeck/src/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Ty<'tcx>>,
) -> bool {
match self.probe_for_name(
Expand All @@ -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,
}
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
13 changes: 12 additions & 1 deletion compiler/rustc_middle/src/hir/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -715,14 +717,23 @@ 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(_) => {
return None;
}
_ => {}
}

prev_hir_id = Some(hir_id);
}
None
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_mir_transform/src/const_prop_lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Loading