mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
Rollup merge of #111004 - clubby789:migrate-mir-transform, r=oli-obk
Migrate `mir_transform` to translatable diagnostics cc #100717
This commit is contained in:
commit
68594142b1
@ -3353,6 +3353,7 @@ dependencies = [
|
||||
"rustc_middle",
|
||||
"rustc_mir_build",
|
||||
"rustc_mir_dataflow",
|
||||
"rustc_mir_transform",
|
||||
"rustc_monomorphize",
|
||||
"rustc_parse",
|
||||
"rustc_passes",
|
||||
@ -3861,8 +3862,10 @@ dependencies = [
|
||||
"rustc_const_eval",
|
||||
"rustc_data_structures",
|
||||
"rustc_errors",
|
||||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_mir_dataflow",
|
||||
"rustc_serialize",
|
||||
|
@ -51,6 +51,7 @@ rustc_interface = { path = "../rustc_interface" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_hir_analysis = { path = "../rustc_hir_analysis" }
|
||||
rustc_mir_transform = { path = "../rustc_mir_transform" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2"
|
||||
@ -64,5 +65,8 @@ features = [
|
||||
[features]
|
||||
llvm = ['rustc_interface/llvm']
|
||||
max_level_info = ['rustc_log/max_level_info']
|
||||
rustc_use_parallel_compiler = ['rustc_data_structures/rustc_use_parallel_compiler', 'rustc_interface/rustc_use_parallel_compiler',
|
||||
'rustc_middle/rustc_use_parallel_compiler']
|
||||
rustc_use_parallel_compiler = [
|
||||
'rustc_data_structures/rustc_use_parallel_compiler',
|
||||
'rustc_interface/rustc_use_parallel_compiler',
|
||||
'rustc_middle/rustc_use_parallel_compiler'
|
||||
]
|
||||
|
@ -99,6 +99,7 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[
|
||||
rustc_middle::DEFAULT_LOCALE_RESOURCE,
|
||||
rustc_mir_build::DEFAULT_LOCALE_RESOURCE,
|
||||
rustc_mir_dataflow::DEFAULT_LOCALE_RESOURCE,
|
||||
rustc_mir_transform::DEFAULT_LOCALE_RESOURCE,
|
||||
rustc_monomorphize::DEFAULT_LOCALE_RESOURCE,
|
||||
rustc_parse::DEFAULT_LOCALE_RESOURCE,
|
||||
rustc_passes::DEFAULT_LOCALE_RESOURCE,
|
||||
|
@ -571,6 +571,14 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
|
||||
Some((diagnostic, handler))
|
||||
}
|
||||
|
||||
/// Retrieves the [`Handler`] if available
|
||||
pub fn handler(&self) -> Option<&Handler> {
|
||||
match self.inner.state {
|
||||
DiagnosticBuilderState::Emittable(handler) => Some(handler),
|
||||
DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Buffers the diagnostic for later emission,
|
||||
/// unless handler has disabled such buffering.
|
||||
pub fn buffer(self, buffered_diagnostics: &mut Vec<Diagnostic>) {
|
||||
|
@ -24,6 +24,8 @@ rustc_session = { path = "../rustc_session" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
rustc_trait_selection = { path = "../rustc_trait_selection" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
|
||||
[dev-dependencies]
|
||||
coverage_test_macros = { path = "src/coverage/test_macros" }
|
||||
|
66
compiler/rustc_mir_transform/messages.ftl
Normal file
66
compiler/rustc_mir_transform/messages.ftl
Normal file
@ -0,0 +1,66 @@
|
||||
mir_transform_const_modify = attempting to modify a `const` item
|
||||
.note = each usage of a `const` item creates a new temporary; the original `const` item will not be modified
|
||||
|
||||
mir_transform_const_mut_borrow = taking a mutable reference to a `const` item
|
||||
.note = each usage of a `const` item creates a new temporary
|
||||
.note2 = the mutable reference will refer to this temporary, not the original `const` item
|
||||
.note3 = mutable reference created due to call to this method
|
||||
|
||||
mir_transform_const_defined_here = `const` item defined here
|
||||
|
||||
mir_transform_unaligned_packed_ref = reference to packed field is unaligned
|
||||
.note = packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
|
||||
.note_ub = creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
|
||||
.help = copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
|
||||
|
||||
mir_transform_unused_unsafe = unnecessary `unsafe` block
|
||||
.label = because it's nested under this `unsafe` block
|
||||
|
||||
mir_transform_requires_unsafe = {$details} is unsafe and requires unsafe {$op_in_unsafe_fn_allowed ->
|
||||
[true] function or block
|
||||
*[false] block
|
||||
}
|
||||
.not_inherited = items do not inherit unsafety from separate enclosing items
|
||||
|
||||
mir_transform_call_to_unsafe_label = call to unsafe function
|
||||
mir_transform_call_to_unsafe_note = consult the function's documentation for information on how to avoid undefined behavior
|
||||
mir_transform_use_of_asm_label = use of inline assembly
|
||||
mir_transform_use_of_asm_note = inline assembly is entirely unchecked and can cause undefined behavior
|
||||
mir_transform_initializing_valid_range_label = initializing type with `rustc_layout_scalar_valid_range` attr
|
||||
mir_transform_initializing_valid_range_note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
|
||||
mir_transform_const_ptr2int_label = cast of pointer to int
|
||||
mir_transform_const_ptr2int_note = casting pointers to integers in constants
|
||||
mir_transform_use_of_static_mut_label = use of mutable static
|
||||
mir_transform_use_of_static_mut_note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
|
||||
mir_transform_use_of_extern_static_label = use of extern static
|
||||
mir_transform_use_of_extern_static_note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
|
||||
mir_transform_deref_ptr_label = dereference of raw pointer
|
||||
mir_transform_deref_ptr_note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
|
||||
mir_transform_union_access_label = access to union field
|
||||
mir_transform_union_access_note = the field may not be properly initialized: using uninitialized data will cause undefined behavior
|
||||
mir_transform_mutation_layout_constrained_label = mutation of layout constrained field
|
||||
mir_transform_mutation_layout_constrained_note = mutating layout constrained fields cannot statically be checked for valid values
|
||||
mir_transform_mutation_layout_constrained_borrow_label = borrow of layout constrained field with interior mutability
|
||||
mir_transform_mutation_layout_constrained_borrow_note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values
|
||||
mir_transform_target_feature_call_label = call to function with `#[target_feature]`
|
||||
mir_transform_target_feature_call_note = can only be called if the required target features are available
|
||||
|
||||
mir_transform_unsafe_op_in_unsafe_fn = {$details} is unsafe and requires unsafe block (error E0133)
|
||||
|
||||
mir_transform_arithmetic_overflow = this arithmetic operation will overflow
|
||||
mir_transform_operation_will_panic = this operation will panic at runtime
|
||||
|
||||
mir_transform_ffi_unwind_call = call to {$foreign ->
|
||||
[true] foreign function
|
||||
*[false] function pointer
|
||||
} with FFI-unwind ABI
|
||||
|
||||
mir_transform_fn_item_ref = taking a reference to a function item does not give a function pointer
|
||||
.suggestion = cast `{$ident}` to obtain a function pointer
|
||||
|
||||
mir_transform_must_not_suspend = {$pre}`{$def_path}`{$post} held across a suspend point, but should not be
|
||||
.label = the value is held across this suspend point
|
||||
.note = {$reason}
|
||||
.help = consider using a block (`{"{ ... }"}`) to shrink the value's scope, ending before the suspend point
|
||||
|
||||
mir_transform_simd_shuffle_last_const = last argument of `simd_shuffle` is required to be a `const` item
|
@ -1,11 +1,12 @@
|
||||
use rustc_errors::{DiagnosticBuilder, DiagnosticMessage};
|
||||
use rustc_hir::HirId;
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::lint::builtin::CONST_ITEM_MUTATION;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::MirLint;
|
||||
use crate::{errors, MirLint};
|
||||
|
||||
pub struct CheckConstItemMutation;
|
||||
|
||||
@ -58,16 +59,14 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_const_item_usage(
|
||||
/// If we should lint on this usage, return the [`HirId`], source [`Span`]
|
||||
/// and [`Span`] of the const item to use in the lint.
|
||||
fn should_lint_const_item_usage(
|
||||
&self,
|
||||
place: &Place<'tcx>,
|
||||
const_item: DefId,
|
||||
location: Location,
|
||||
msg: impl Into<DiagnosticMessage>,
|
||||
decorate: impl for<'a, 'b> FnOnce(
|
||||
&'a mut DiagnosticBuilder<'b, ()>,
|
||||
) -> &'a mut DiagnosticBuilder<'b, ()>,
|
||||
) {
|
||||
) -> Option<(HirId, Span, Span)> {
|
||||
// Don't lint on borrowing/assigning when a dereference is involved.
|
||||
// If we 'leave' the temporary via a dereference, we must
|
||||
// be modifying something else
|
||||
@ -83,16 +82,9 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> {
|
||||
.assert_crate_local()
|
||||
.lint_root;
|
||||
|
||||
self.tcx.struct_span_lint_hir(
|
||||
CONST_ITEM_MUTATION,
|
||||
lint_root,
|
||||
source_info.span,
|
||||
msg,
|
||||
|lint| {
|
||||
decorate(lint)
|
||||
.span_note(self.tcx.def_span(const_item), "`const` item defined here")
|
||||
},
|
||||
);
|
||||
Some((lint_root, source_info.span, self.tcx.def_span(const_item)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,10 +96,14 @@ impl<'tcx> Visitor<'tcx> for ConstMutationChecker<'_, 'tcx> {
|
||||
// Assigning directly to a constant (e.g. `FOO = true;`) is a hard error,
|
||||
// so emitting a lint would be redundant.
|
||||
if !lhs.projection.is_empty() {
|
||||
if let Some(def_id) = self.is_const_item_without_destructor(lhs.local) {
|
||||
self.lint_const_item_usage(&lhs, def_id, loc, "attempting to modify a `const` item",|lint| {
|
||||
lint.note("each usage of a `const` item creates a new temporary; the original `const` item will not be modified")
|
||||
})
|
||||
if let Some(def_id) = self.is_const_item_without_destructor(lhs.local)
|
||||
&& let Some((lint_root, span, item)) = self.should_lint_const_item_usage(&lhs, def_id, loc) {
|
||||
self.tcx.emit_spanned_lint(
|
||||
CONST_ITEM_MUTATION,
|
||||
lint_root,
|
||||
span,
|
||||
errors::ConstMutate::Modify { konst: item }
|
||||
);
|
||||
}
|
||||
}
|
||||
// We are looking for MIR of the form:
|
||||
@ -143,17 +139,22 @@ impl<'tcx> Visitor<'tcx> for ConstMutationChecker<'_, 'tcx> {
|
||||
});
|
||||
let lint_loc =
|
||||
if method_did.is_some() { self.body.terminator_loc(loc.block) } else { loc };
|
||||
self.lint_const_item_usage(place, def_id, lint_loc, "taking a mutable reference to a `const` item", |lint| {
|
||||
lint
|
||||
.note("each usage of a `const` item creates a new temporary")
|
||||
.note("the mutable reference will refer to this temporary, not the original `const` item");
|
||||
|
||||
if let Some((method_did, _substs)) = method_did {
|
||||
lint.span_note(self.tcx.def_span(method_did), "mutable reference created due to call to this method");
|
||||
}
|
||||
|
||||
lint
|
||||
});
|
||||
let method_call = if let Some((method_did, _)) = method_did {
|
||||
Some(self.tcx.def_span(method_did))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some((lint_root, span, item)) =
|
||||
self.should_lint_const_item_usage(place, def_id, lint_loc)
|
||||
{
|
||||
self.tcx.emit_spanned_lint(
|
||||
CONST_ITEM_MUTATION,
|
||||
lint_root,
|
||||
span,
|
||||
errors::ConstMutate::MutBorrow { method_call, konst: item },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.super_rvalue(rvalue, loc);
|
||||
|
@ -1,10 +1,9 @@
|
||||
use rustc_errors::struct_span_err;
|
||||
use rustc_middle::mir::visit::{PlaceContext, Visitor};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
|
||||
use crate::util;
|
||||
use crate::MirLint;
|
||||
use crate::{errors, util};
|
||||
|
||||
pub struct CheckPackedRef;
|
||||
|
||||
@ -49,25 +48,7 @@ impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> {
|
||||
// shouldn't do.
|
||||
span_bug!(self.source_info.span, "builtin derive created an unaligned reference");
|
||||
} else {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
self.source_info.span,
|
||||
E0793,
|
||||
"reference to packed field is unaligned"
|
||||
)
|
||||
.note(
|
||||
"packed structs are only aligned by one byte, and many modern architectures \
|
||||
penalize unaligned field accesses"
|
||||
)
|
||||
.note(
|
||||
"creating a misaligned reference is undefined behavior (even if that \
|
||||
reference is never dereferenced)",
|
||||
).help(
|
||||
"copy the field contents to a local variable, or replace the \
|
||||
reference with a raw pointer and use `read_unaligned`/`write_unaligned` \
|
||||
(loads and stores via `*p` must be properly aligned even when using raw pointers)"
|
||||
)
|
||||
.emit();
|
||||
self.tcx.sess.emit_err(errors::UnalignedPackedRef { span: self.source_info.span });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
use rustc_data_structures::unord::{UnordItems, UnordSet};
|
||||
use rustc_errors::struct_span_err;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
@ -15,6 +14,8 @@ use rustc_session::lint::Level;
|
||||
|
||||
use std::ops::Bound;
|
||||
|
||||
use crate::errors;
|
||||
|
||||
pub struct UnsafetyChecker<'a, 'tcx> {
|
||||
body: &'a Body<'tcx>,
|
||||
body_did: LocalDefId,
|
||||
@ -509,21 +510,12 @@ fn unsafety_check_result(tcx: TyCtxt<'_>, def: LocalDefId) -> &UnsafetyCheckResu
|
||||
|
||||
fn report_unused_unsafe(tcx: TyCtxt<'_>, kind: UnusedUnsafe, id: HirId) {
|
||||
let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id));
|
||||
let msg = "unnecessary `unsafe` block";
|
||||
tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, msg, |lint| {
|
||||
lint.span_label(span, msg);
|
||||
match kind {
|
||||
UnusedUnsafe::Unused => {}
|
||||
UnusedUnsafe::InUnsafeBlock(id) => {
|
||||
lint.span_label(
|
||||
tcx.sess.source_map().guess_head_span(tcx.hir().span(id)),
|
||||
"because it's nested under this `unsafe` block",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
lint
|
||||
});
|
||||
let nested_parent = if let UnusedUnsafe::InUnsafeBlock(id) = kind {
|
||||
Some(tcx.sess.source_map().guess_head_span(tcx.hir().span(id)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
tcx.emit_spanned_lint(UNUSED_UNSAFE, id, span, errors::UnusedUnsafe { span, nested_parent });
|
||||
}
|
||||
|
||||
pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
||||
@ -537,26 +529,11 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
||||
let UnsafetyCheckResult { violations, unused_unsafes, .. } = tcx.unsafety_check_result(def_id);
|
||||
|
||||
for &UnsafetyViolation { source_info, lint_root, kind, details } in violations.iter() {
|
||||
let (description, note) = details.description_and_note();
|
||||
let details = errors::RequiresUnsafeDetail { violation: details, span: source_info.span };
|
||||
|
||||
match kind {
|
||||
UnsafetyViolationKind::General => {
|
||||
// once
|
||||
let unsafe_fn_msg = if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) {
|
||||
" function or"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let mut err = struct_span_err!(
|
||||
tcx.sess,
|
||||
source_info.span,
|
||||
E0133,
|
||||
"{} is unsafe and requires unsafe{} block",
|
||||
description,
|
||||
unsafe_fn_msg,
|
||||
);
|
||||
err.span_label(source_info.span, description).note(note);
|
||||
let op_in_unsafe_fn_allowed = unsafe_op_in_unsafe_fn_allowed(tcx, lint_root);
|
||||
let note_non_inherited = tcx.hir().parent_iter(lint_root).find(|(id, node)| {
|
||||
if let Node::Expr(block) = node
|
||||
&& let ExprKind::Block(block, _) = block.kind
|
||||
@ -572,22 +549,23 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
||||
false
|
||||
}
|
||||
});
|
||||
if let Some((id, _)) = note_non_inherited {
|
||||
let span = tcx.hir().span(id);
|
||||
err.span_label(
|
||||
tcx.sess.source_map().guess_head_span(span),
|
||||
"items do not inherit unsafety from separate enclosing items",
|
||||
);
|
||||
}
|
||||
|
||||
err.emit();
|
||||
let enclosing = if let Some((id, _)) = note_non_inherited {
|
||||
Some(tcx.sess.source_map().guess_head_span(tcx.hir().span(id)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
tcx.sess.emit_err(errors::RequiresUnsafe {
|
||||
span: source_info.span,
|
||||
enclosing,
|
||||
details,
|
||||
op_in_unsafe_fn_allowed,
|
||||
});
|
||||
}
|
||||
UnsafetyViolationKind::UnsafeFn => tcx.struct_span_lint_hir(
|
||||
UnsafetyViolationKind::UnsafeFn => tcx.emit_spanned_lint(
|
||||
UNSAFE_OP_IN_UNSAFE_FN,
|
||||
lint_root,
|
||||
source_info.span,
|
||||
format!("{} is unsafe and requires unsafe block (error E0133)", description,),
|
||||
|lint| lint.span_label(source_info.span, description).note(note),
|
||||
errors::UnsafeOpInUnsafeFn { details },
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
//! Propagates constants for early reporting of statically known
|
||||
//! assertion failures
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use either::Left;
|
||||
|
||||
use rustc_const_eval::interpret::Immediate;
|
||||
@ -17,7 +19,6 @@ use rustc_middle::ty::InternalSubsts;
|
||||
use rustc_middle::ty::{
|
||||
self, ConstInt, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeVisitableExt,
|
||||
};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
|
||||
use rustc_trait_selection::traits;
|
||||
@ -25,6 +26,7 @@ use rustc_trait_selection::traits;
|
||||
use crate::const_prop::CanConstProp;
|
||||
use crate::const_prop::ConstPropMachine;
|
||||
use crate::const_prop::ConstPropMode;
|
||||
use crate::errors::AssertLint;
|
||||
use crate::MirLint;
|
||||
|
||||
/// The maximum number of bytes that we'll allocate space for a local or the return value.
|
||||
@ -311,18 +313,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn report_assert_as_lint(
|
||||
&self,
|
||||
lint: &'static lint::Lint,
|
||||
location: Location,
|
||||
message: &'static str,
|
||||
panic: AssertKind<impl std::fmt::Debug>,
|
||||
) {
|
||||
let source_info = self.body().source_info(location);
|
||||
fn report_assert_as_lint(&self, source_info: &SourceInfo, lint: AssertLint<impl Debug>) {
|
||||
if let Some(lint_root) = self.lint_root(*source_info) {
|
||||
self.tcx.struct_span_lint_hir(lint, lint_root, source_info.span, message, |lint| {
|
||||
lint.span_label(source_info.span, format!("{:?}", panic))
|
||||
});
|
||||
self.tcx.emit_spanned_lint(lint.lint(), lint_root, source_info.span, lint);
|
||||
}
|
||||
}
|
||||
|
||||
@ -335,11 +328,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
// `AssertKind` only has an `OverflowNeg` variant, so make sure that is
|
||||
// appropriate to use.
|
||||
assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow");
|
||||
let source_info = self.body().source_info(location);
|
||||
self.report_assert_as_lint(
|
||||
lint::builtin::ARITHMETIC_OVERFLOW,
|
||||
location,
|
||||
"this arithmetic operation will overflow",
|
||||
AssertKind::OverflowNeg(val.to_const_int()),
|
||||
source_info,
|
||||
AssertLint::ArithmeticOverflow(
|
||||
source_info.span,
|
||||
AssertKind::OverflowNeg(val.to_const_int()),
|
||||
),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
@ -370,23 +365,23 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
let r_bits = r.to_scalar().to_bits(right_size).ok();
|
||||
if r_bits.map_or(false, |b| b >= left_size.bits() as u128) {
|
||||
debug!("check_binary_op: reporting assert for {:?}", location);
|
||||
let source_info = self.body().source_info(location);
|
||||
let panic = AssertKind::Overflow(
|
||||
op,
|
||||
match l {
|
||||
Some(l) => l.to_const_int(),
|
||||
// Invent a dummy value, the diagnostic ignores it anyway
|
||||
None => ConstInt::new(
|
||||
ScalarInt::try_from_uint(1_u8, left_size).unwrap(),
|
||||
left_ty.is_signed(),
|
||||
left_ty.is_ptr_sized_integral(),
|
||||
),
|
||||
},
|
||||
r.to_const_int(),
|
||||
);
|
||||
self.report_assert_as_lint(
|
||||
lint::builtin::ARITHMETIC_OVERFLOW,
|
||||
location,
|
||||
"this arithmetic operation will overflow",
|
||||
AssertKind::Overflow(
|
||||
op,
|
||||
match l {
|
||||
Some(l) => l.to_const_int(),
|
||||
// Invent a dummy value, the diagnostic ignores it anyway
|
||||
None => ConstInt::new(
|
||||
ScalarInt::try_from_uint(1_u8, left_size).unwrap(),
|
||||
left_ty.is_signed(),
|
||||
left_ty.is_ptr_sized_integral(),
|
||||
),
|
||||
},
|
||||
r.to_const_int(),
|
||||
),
|
||||
source_info,
|
||||
AssertLint::ArithmeticOverflow(source_info.span, panic),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
@ -398,11 +393,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, &l, &r)?;
|
||||
Ok(overflow)
|
||||
})? {
|
||||
let source_info = self.body().source_info(location);
|
||||
self.report_assert_as_lint(
|
||||
lint::builtin::ARITHMETIC_OVERFLOW,
|
||||
location,
|
||||
"this arithmetic operation will overflow",
|
||||
AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()),
|
||||
source_info,
|
||||
AssertLint::ArithmeticOverflow(
|
||||
source_info.span,
|
||||
AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()),
|
||||
),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
@ -543,11 +540,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
// Need proper const propagator for these.
|
||||
_ => return None,
|
||||
};
|
||||
let source_info = self.body().source_info(location);
|
||||
self.report_assert_as_lint(
|
||||
lint::builtin::UNCONDITIONAL_PANIC,
|
||||
location,
|
||||
"this operation will panic at runtime",
|
||||
msg,
|
||||
source_info,
|
||||
AssertLint::UnconditionalPanic(source_info.span, msg),
|
||||
);
|
||||
}
|
||||
|
||||
|
245
compiler/rustc_mir_transform/src/errors.rs
Normal file
245
compiler/rustc_mir_transform/src/errors.rs
Normal file
@ -0,0 +1,245 @@
|
||||
use rustc_errors::{
|
||||
DecorateLint, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Handler, IntoDiagnostic,
|
||||
};
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::mir::{AssertKind, UnsafetyViolationDetails};
|
||||
use rustc_session::lint::{self, Lint};
|
||||
use rustc_span::Span;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
pub(crate) enum ConstMutate {
|
||||
#[diag(mir_transform_const_modify)]
|
||||
#[note]
|
||||
Modify {
|
||||
#[note(mir_transform_const_defined_here)]
|
||||
konst: Span,
|
||||
},
|
||||
#[diag(mir_transform_const_mut_borrow)]
|
||||
#[note]
|
||||
#[note(mir_transform_note2)]
|
||||
MutBorrow {
|
||||
#[note(mir_transform_note3)]
|
||||
method_call: Option<Span>,
|
||||
#[note(mir_transform_const_defined_here)]
|
||||
konst: Span,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_transform_unaligned_packed_ref, code = "E0793")]
|
||||
#[note]
|
||||
#[note(mir_transform_note_ub)]
|
||||
#[help]
|
||||
pub(crate) struct UnalignedPackedRef {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_transform_unused_unsafe)]
|
||||
pub(crate) struct UnusedUnsafe {
|
||||
#[label(mir_transform_unused_unsafe)]
|
||||
pub span: Span,
|
||||
#[label]
|
||||
pub nested_parent: Option<Span>,
|
||||
}
|
||||
|
||||
pub(crate) struct RequiresUnsafe {
|
||||
pub span: Span,
|
||||
pub details: RequiresUnsafeDetail,
|
||||
pub enclosing: Option<Span>,
|
||||
pub op_in_unsafe_fn_allowed: bool,
|
||||
}
|
||||
|
||||
// The primary message for this diagnostic should be '{$label} is unsafe and...',
|
||||
// so we need to eagerly translate the label here, which isn't supported by the derive API
|
||||
// We could also exhaustively list out the primary messages for all unsafe violations,
|
||||
// but this would result in a lot of duplication.
|
||||
impl<'sess, G: EmissionGuarantee> IntoDiagnostic<'sess, G> for RequiresUnsafe {
|
||||
#[track_caller]
|
||||
fn into_diagnostic(self, handler: &'sess Handler) -> DiagnosticBuilder<'sess, G> {
|
||||
let mut diag =
|
||||
handler.struct_diagnostic(crate::fluent_generated::mir_transform_requires_unsafe);
|
||||
diag.code(rustc_errors::DiagnosticId::Error("E0133".to_string()));
|
||||
diag.set_span(self.span);
|
||||
diag.span_label(self.span, self.details.label());
|
||||
diag.note(self.details.note());
|
||||
let desc = handler.eagerly_translate_to_string(self.details.label(), [].into_iter());
|
||||
diag.set_arg("details", desc);
|
||||
diag.set_arg("op_in_unsafe_fn_allowed", self.op_in_unsafe_fn_allowed);
|
||||
if let Some(sp) = self.enclosing {
|
||||
diag.span_label(sp, crate::fluent_generated::mir_transform_not_inherited);
|
||||
}
|
||||
diag
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct RequiresUnsafeDetail {
|
||||
pub span: Span,
|
||||
pub violation: UnsafetyViolationDetails,
|
||||
}
|
||||
|
||||
impl RequiresUnsafeDetail {
|
||||
fn note(self) -> DiagnosticMessage {
|
||||
use UnsafetyViolationDetails::*;
|
||||
match self.violation {
|
||||
CallToUnsafeFunction => crate::fluent_generated::mir_transform_call_to_unsafe_note,
|
||||
UseOfInlineAssembly => crate::fluent_generated::mir_transform_use_of_asm_note,
|
||||
InitializingTypeWith => {
|
||||
crate::fluent_generated::mir_transform_initializing_valid_range_note
|
||||
}
|
||||
CastOfPointerToInt => crate::fluent_generated::mir_transform_const_ptr2int_note,
|
||||
UseOfMutableStatic => crate::fluent_generated::mir_transform_use_of_static_mut_note,
|
||||
UseOfExternStatic => crate::fluent_generated::mir_transform_use_of_extern_static_note,
|
||||
DerefOfRawPointer => crate::fluent_generated::mir_transform_deref_ptr_note,
|
||||
AccessToUnionField => crate::fluent_generated::mir_transform_union_access_note,
|
||||
MutationOfLayoutConstrainedField => {
|
||||
crate::fluent_generated::mir_transform_mutation_layout_constrained_note
|
||||
}
|
||||
BorrowOfLayoutConstrainedField => {
|
||||
crate::fluent_generated::mir_transform_mutation_layout_constrained_borrow_note
|
||||
}
|
||||
CallToFunctionWith => crate::fluent_generated::mir_transform_target_feature_call_note,
|
||||
}
|
||||
}
|
||||
|
||||
fn label(self) -> DiagnosticMessage {
|
||||
use UnsafetyViolationDetails::*;
|
||||
match self.violation {
|
||||
CallToUnsafeFunction => crate::fluent_generated::mir_transform_call_to_unsafe_label,
|
||||
UseOfInlineAssembly => crate::fluent_generated::mir_transform_use_of_asm_label,
|
||||
InitializingTypeWith => {
|
||||
crate::fluent_generated::mir_transform_initializing_valid_range_label
|
||||
}
|
||||
CastOfPointerToInt => crate::fluent_generated::mir_transform_const_ptr2int_label,
|
||||
UseOfMutableStatic => crate::fluent_generated::mir_transform_use_of_static_mut_label,
|
||||
UseOfExternStatic => crate::fluent_generated::mir_transform_use_of_extern_static_label,
|
||||
DerefOfRawPointer => crate::fluent_generated::mir_transform_deref_ptr_label,
|
||||
AccessToUnionField => crate::fluent_generated::mir_transform_union_access_label,
|
||||
MutationOfLayoutConstrainedField => {
|
||||
crate::fluent_generated::mir_transform_mutation_layout_constrained_label
|
||||
}
|
||||
BorrowOfLayoutConstrainedField => {
|
||||
crate::fluent_generated::mir_transform_mutation_layout_constrained_borrow_label
|
||||
}
|
||||
CallToFunctionWith => crate::fluent_generated::mir_transform_target_feature_call_label,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UnsafeOpInUnsafeFn {
|
||||
pub details: RequiresUnsafeDetail,
|
||||
}
|
||||
|
||||
impl<'a> DecorateLint<'a, ()> for UnsafeOpInUnsafeFn {
|
||||
#[track_caller]
|
||||
fn decorate_lint<'b>(
|
||||
self,
|
||||
diag: &'b mut DiagnosticBuilder<'a, ()>,
|
||||
) -> &'b mut DiagnosticBuilder<'a, ()> {
|
||||
let desc = diag
|
||||
.handler()
|
||||
.expect("lint should not yet be emitted")
|
||||
.eagerly_translate_to_string(self.details.label(), [].into_iter());
|
||||
diag.set_arg("details", desc);
|
||||
diag.span_label(self.details.span, self.details.label());
|
||||
diag.note(self.details.note());
|
||||
diag
|
||||
}
|
||||
|
||||
fn msg(&self) -> DiagnosticMessage {
|
||||
crate::fluent_generated::mir_transform_unsafe_op_in_unsafe_fn
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum AssertLint<P> {
|
||||
ArithmeticOverflow(Span, AssertKind<P>),
|
||||
UnconditionalPanic(Span, AssertKind<P>),
|
||||
}
|
||||
|
||||
impl<'a, P: std::fmt::Debug> DecorateLint<'a, ()> for AssertLint<P> {
|
||||
fn decorate_lint<'b>(
|
||||
self,
|
||||
diag: &'b mut DiagnosticBuilder<'a, ()>,
|
||||
) -> &'b mut DiagnosticBuilder<'a, ()> {
|
||||
diag.span_label(self.span(), format!("{:?}", self.panic()));
|
||||
diag
|
||||
}
|
||||
|
||||
fn msg(&self) -> DiagnosticMessage {
|
||||
match self {
|
||||
AssertLint::ArithmeticOverflow(..) => {
|
||||
crate::fluent_generated::mir_transform_arithmetic_overflow
|
||||
}
|
||||
AssertLint::UnconditionalPanic(..) => {
|
||||
crate::fluent_generated::mir_transform_operation_will_panic
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> AssertLint<P> {
|
||||
pub fn lint(&self) -> &'static Lint {
|
||||
match self {
|
||||
AssertLint::ArithmeticOverflow(..) => lint::builtin::ARITHMETIC_OVERFLOW,
|
||||
AssertLint::UnconditionalPanic(..) => lint::builtin::UNCONDITIONAL_PANIC,
|
||||
}
|
||||
}
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
AssertLint::ArithmeticOverflow(sp, _) | AssertLint::UnconditionalPanic(sp, _) => *sp,
|
||||
}
|
||||
}
|
||||
pub fn panic(&self) -> &AssertKind<P> {
|
||||
match self {
|
||||
AssertLint::ArithmeticOverflow(_, p) | AssertLint::UnconditionalPanic(_, p) => p,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_transform_ffi_unwind_call)]
|
||||
pub(crate) struct FfiUnwindCall {
|
||||
#[label(mir_transform_ffi_unwind_call)]
|
||||
pub span: Span,
|
||||
pub foreign: bool,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_transform_fn_item_ref)]
|
||||
pub(crate) struct FnItemRef {
|
||||
#[suggestion(code = "{sugg}", applicability = "unspecified")]
|
||||
pub span: Span,
|
||||
pub sugg: String,
|
||||
pub ident: String,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_transform_must_not_suspend)]
|
||||
pub(crate) struct MustNotSupend<'a> {
|
||||
#[label]
|
||||
pub yield_sp: Span,
|
||||
#[subdiagnostic]
|
||||
pub reason: Option<MustNotSuspendReason>,
|
||||
#[help]
|
||||
pub src_sp: Span,
|
||||
pub pre: &'a str,
|
||||
pub def_path: String,
|
||||
pub post: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[note(mir_transform_note)]
|
||||
pub(crate) struct MustNotSuspendReason {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_transform_simd_shuffle_last_const)]
|
||||
pub(crate) struct SimdShuffleLastConst {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
@ -8,6 +8,8 @@ use rustc_session::lint::builtin::FFI_UNWIND_CALLS;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_target::spec::PanicStrategy;
|
||||
|
||||
use crate::errors;
|
||||
|
||||
fn abi_can_unwind(abi: Abi) -> bool {
|
||||
use Abi::*;
|
||||
match abi {
|
||||
@ -107,13 +109,13 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool {
|
||||
.lint_root;
|
||||
let span = terminator.source_info.span;
|
||||
|
||||
let msg = match fn_def_id {
|
||||
Some(_) => "call to foreign function with FFI-unwind ABI",
|
||||
None => "call to function pointer with FFI-unwind ABI",
|
||||
};
|
||||
tcx.struct_span_lint_hir(FFI_UNWIND_CALLS, lint_root, span, msg, |lint| {
|
||||
lint.span_label(span, msg)
|
||||
});
|
||||
let foreign = fn_def_id.is_some();
|
||||
tcx.emit_spanned_lint(
|
||||
FFI_UNWIND_CALLS,
|
||||
lint_root,
|
||||
span,
|
||||
errors::FfiUnwindCall { span, foreign },
|
||||
);
|
||||
|
||||
tainted = true;
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
use itertools::Itertools;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::*;
|
||||
@ -8,7 +7,7 @@ use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES;
|
||||
use rustc_span::{symbol::sym, Span};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use crate::MirLint;
|
||||
use crate::{errors, MirLint};
|
||||
|
||||
pub struct FunctionItemReferences;
|
||||
|
||||
@ -174,27 +173,21 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> {
|
||||
let num_args = fn_sig.inputs().map_bound(|inputs| inputs.len()).skip_binder();
|
||||
let variadic = if fn_sig.c_variadic() { ", ..." } else { "" };
|
||||
let ret = if fn_sig.output().skip_binder().is_unit() { "" } else { " -> _" };
|
||||
self.tcx.struct_span_lint_hir(
|
||||
let sugg = format!(
|
||||
"{} as {}{}fn({}{}){}",
|
||||
if params.is_empty() { ident.clone() } else { format!("{}::<{}>", ident, params) },
|
||||
unsafety,
|
||||
abi,
|
||||
vec!["_"; num_args].join(", "),
|
||||
variadic,
|
||||
ret,
|
||||
);
|
||||
|
||||
self.tcx.emit_spanned_lint(
|
||||
FUNCTION_ITEM_REFERENCES,
|
||||
lint_root,
|
||||
span,
|
||||
"taking a reference to a function item does not give a function pointer",
|
||||
|lint| {
|
||||
lint.span_suggestion(
|
||||
span,
|
||||
format!("cast `{}` to obtain a function pointer", ident),
|
||||
format!(
|
||||
"{} as {}{}fn({}{}){}",
|
||||
if params.is_empty() { ident } else { format!("{}::<{}>", ident, params) },
|
||||
unsafety,
|
||||
abi,
|
||||
vec!["_"; num_args].join(", "),
|
||||
variadic,
|
||||
ret,
|
||||
),
|
||||
Applicability::Unspecified,
|
||||
)
|
||||
},
|
||||
errors::FnItemRef { span, sugg, ident },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,7 @@
|
||||
//! Otherwise it drops all the values in scope at the last suspension point.
|
||||
|
||||
use crate::deref_separator::deref_finder;
|
||||
use crate::errors;
|
||||
use crate::simplify;
|
||||
use crate::MirPass;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
@ -1891,36 +1892,21 @@ fn check_must_not_suspend_def(
|
||||
data: SuspendCheckData<'_>,
|
||||
) -> bool {
|
||||
if let Some(attr) = tcx.get_attr(def_id, sym::must_not_suspend) {
|
||||
let msg = rustc_errors::DelayDm(|| {
|
||||
format!(
|
||||
"{}`{}`{} held across a suspend point, but should not be",
|
||||
data.descr_pre,
|
||||
tcx.def_path_str(def_id),
|
||||
data.descr_post,
|
||||
)
|
||||
let reason = attr.value_str().map(|s| errors::MustNotSuspendReason {
|
||||
span: data.source_span,
|
||||
reason: s.as_str().to_string(),
|
||||
});
|
||||
tcx.struct_span_lint_hir(
|
||||
tcx.emit_spanned_lint(
|
||||
rustc_session::lint::builtin::MUST_NOT_SUSPEND,
|
||||
hir_id,
|
||||
data.source_span,
|
||||
msg,
|
||||
|lint| {
|
||||
// add span pointing to the offending yield/await
|
||||
lint.span_label(data.yield_span, "the value is held across this suspend point");
|
||||
|
||||
// Add optional reason note
|
||||
if let Some(note) = attr.value_str() {
|
||||
// FIXME(guswynn): consider formatting this better
|
||||
lint.span_note(data.source_span, note.as_str());
|
||||
}
|
||||
|
||||
// Add some quick suggestions on what to do
|
||||
// FIXME: can `drop` work as a suggestion here as well?
|
||||
lint.span_help(
|
||||
data.source_span,
|
||||
"consider using a block (`{ ... }`) \
|
||||
to shrink the value's scope, ending before the suspend point",
|
||||
)
|
||||
errors::MustNotSupend {
|
||||
yield_sp: data.yield_span,
|
||||
reason,
|
||||
src_sp: data.source_span,
|
||||
pre: data.descr_pre,
|
||||
def_path: tcx.def_path_str(def_id),
|
||||
post: data.descr_post,
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(drain_filter)]
|
||||
#![feature(let_chains)]
|
||||
@ -69,6 +71,7 @@ pub mod dump_mir;
|
||||
mod early_otherwise_branch;
|
||||
mod elaborate_box_derefs;
|
||||
mod elaborate_drops;
|
||||
mod errors;
|
||||
mod ffi_unwind_calls;
|
||||
mod function_item_references;
|
||||
mod generator;
|
||||
@ -105,6 +108,11 @@ use rustc_const_eval::transform::promote_consts;
|
||||
use rustc_const_eval::transform::validate;
|
||||
use rustc_mir_dataflow::rustc_peek;
|
||||
|
||||
use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
|
||||
use rustc_fluent_macro::fluent_messages;
|
||||
|
||||
fluent_messages! { "../messages.ftl" }
|
||||
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
check_unsafety::provide(providers);
|
||||
coverage::query::provide(providers);
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Lowers intrinsic calls
|
||||
|
||||
use crate::MirPass;
|
||||
use crate::{errors, MirPass};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::subst::SubstsRef;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
@ -310,11 +310,7 @@ fn resolve_rust_intrinsic<'tcx>(
|
||||
}
|
||||
|
||||
fn validate_simd_shuffle<'tcx>(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) {
|
||||
match &args[2] {
|
||||
Operand::Constant(_) => {} // all good
|
||||
_ => {
|
||||
let msg = "last argument of `simd_shuffle` is required to be a `const` item";
|
||||
tcx.sess.span_err(span, msg);
|
||||
}
|
||||
if !matches!(args[2], Operand::Constant(_)) {
|
||||
tcx.sess.emit_err(errors::SimdShuffleLastConst { span });
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user