Skip to content
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

Migrate mir_transform to translatable diagnostics #111004

Merged
merged 1 commit into from
May 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3351,6 +3351,7 @@ dependencies = [
"rustc_middle",
"rustc_mir_build",
"rustc_mir_dataflow",
"rustc_mir_transform",
"rustc_monomorphize",
"rustc_parse",
"rustc_passes",
Expand Down Expand Up @@ -3859,8 +3860,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",
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_driver_impl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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'
]
1 change: 1 addition & 0 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_errors/src/diagnostic_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,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>) {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_mir_transform/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 66 additions & 0 deletions compiler/rustc_mir_transform/messages.ftl
Original file line number Diff line number Diff line change
@@ -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
65 changes: 33 additions & 32 deletions compiler/rustc_mir_transform/src/check_const_item_mutation.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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
Expand All @@ -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
}
}
}
Expand All @@ -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:
Expand Down Expand Up @@ -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);
Expand Down
23 changes: 2 additions & 21 deletions compiler/rustc_mir_transform/src/check_packed_ref.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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 });
}
}
}
Expand Down
68 changes: 23 additions & 45 deletions compiler/rustc_mir_transform/src/check_unsafety.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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,
Expand Down Expand Up @@ -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) {
Expand All @@ -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
Expand All @@ -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 },
),
}
}
Expand Down
Loading