Auto merge of #115370 - matthiaskrgr:rollup-l0e1zuj, r=matthiaskrgr
Rollup of 7 pull requests Successful merges: - #113565 (Make SIGSEGV handler emit nicer backtraces) - #114704 (parser: not insert dummy field in struct) - #115272 (miri/diagnostics: don't forget to print_backtrace when ICEing on unexpected errors) - #115313 (Make `get_return_block()` return `Some` only for HIR nodes in body) - #115347 (suggest removing `impl` in generic trait bound position) - #115355 (new solver: handle edge case of a recursion limit of 0) - #115363 (Don't suggest adding parentheses to call an inaccessible method.) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
7659abc63d
30 changed files with 498 additions and 188 deletions
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
@ -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();
|
||||
|
|
142
compiler/rustc_driver_impl/src/signal_handler.rs
Normal file
142
compiler/rustc_driver_impl/src/signal_handler.rs
Normal file
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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::<FieldDef>() {
|
||||
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()?;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
14
tests/ui/associated-type-bounds/suggest-removing-impl.rs
Normal file
14
tests/ui/associated-type-bounds/suggest-removing-impl.rs
Normal file
|
@ -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<T>()
|
||||
where
|
||||
T: impl Sized
|
||||
//~^ ERROR expected a trait, found type
|
||||
//~| HELP use the trait bounds directly
|
||||
{}
|
||||
}
|
||||
|
||||
fn main() {}
|
26
tests/ui/associated-type-bounds/suggest-removing-impl.stderr
Normal file
26
tests/ui/associated-type-bounds/suggest-removing-impl.stderr
Normal file
|
@ -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
|
||||
|
16
tests/ui/error-codes/E0609-private-method.rs
Normal file
16
tests/ui/error-codes/E0609-private-method.rs
Normal file
|
@ -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
|
||||
}
|
9
tests/ui/error-codes/E0609-private-method.stderr
Normal file
9
tests/ui/error-codes/E0609-private-method.stderr
Normal file
|
@ -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`.
|
|
@ -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() {}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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`
|
|
@ -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`.
|
10
tests/ui/typeck/issue-114918/const-in-fn-return-type.rs
Normal file
10
tests/ui/typeck/issue-114918/const-in-fn-return-type.rs
Normal file
|
@ -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() {}
|
|
@ -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`.
|
20
tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs
Normal file
20
tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs
Normal file
|
@ -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<const N: u32>() -> [ (); N ];
|
||||
}
|
||||
|
||||
struct S {}
|
||||
|
||||
#[allow(unused_braces)]
|
||||
impl Trait for S {
|
||||
fn func<const N: u32>() -> [ (); { () }] { //~ ERROR mismatched types
|
||||
N
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,9 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/const-in-impl-fn-return-type.rs:15:40
|
||||
|
|
||||
LL | fn func<const N: u32>() -> [ (); { () }] {
|
||||
| ^^ expected `usize`, found `()`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
12
tests/ui/typeck/issue-114918/const-in-struct-type-arg.rs
Normal file
12
tests/ui/typeck/issue-114918/const-in-struct-type-arg.rs
Normal file
|
@ -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<const N: usize> {
|
||||
arr: [u8; N]
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = S::<{ () }> { arr: [5, 6, 7]}; //~ ERROR mismatched types
|
||||
}
|
|
@ -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`.
|
|
@ -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<const N: u32>() -> [ (); { () }] { //~ ERROR mismatched types
|
||||
N
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,9 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/const-in-trait-fn-return-type.rs:8:40
|
||||
|
|
||||
LL | fn func<const N: u32>() -> [ (); { () }] {
|
||||
| ^^ expected `usize`, found `()`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
Loading…
Add table
Reference in a new issue