Skip to content

Commit 51ba662

Browse files
committed
Print list of missing target features when calling a function with target features outside an unsafe block
1 parent 274b524 commit 51ba662

File tree

7 files changed

+190
-94
lines changed

7 files changed

+190
-94
lines changed

compiler/rustc_middle/src/mir/query.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub enum UnsafetyViolationKind {
2727
UnsafeFn,
2828
}
2929

30-
#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
30+
#[derive(Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
3131
pub enum UnsafetyViolationDetails {
3232
CallToUnsafeFunction,
3333
UseOfInlineAssembly,
@@ -39,10 +39,17 @@ pub enum UnsafetyViolationDetails {
3939
AccessToUnionField,
4040
MutationOfLayoutConstrainedField,
4141
BorrowOfLayoutConstrainedField,
42-
CallToFunctionWith,
42+
CallToFunctionWith {
43+
/// Target features enabled in callee's `#[target_feature]` but missing in
44+
/// caller's `#[target_feature]`.
45+
missing: Vec<Symbol>,
46+
/// Target features in `missing` that are enabled at compile time
47+
/// (e.g., with `-C target-feature`).
48+
build_enabled: Vec<Symbol>,
49+
},
4350
}
4451

45-
#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
52+
#[derive(Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
4653
pub struct UnsafetyViolation {
4754
pub source_info: SourceInfo,
4855
pub lint_root: hir::HirId,

compiler/rustc_mir_transform/messages.ftl

+12-1
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,19 @@ mir_transform_requires_unsafe = {$details} is unsafe and requires unsafe {$op_in
4242
}
4343
.not_inherited = items do not inherit unsafety from separate enclosing items
4444
45+
mir_transform_target_feature_call_help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count ->
46+
[1] feature
47+
*[count] features
48+
}: {$missing_target_features}
49+
4550
mir_transform_target_feature_call_label = call to function with `#[target_feature]`
46-
mir_transform_target_feature_call_note = can only be called if the required target features are available
51+
mir_transform_target_feature_call_note = the {$build_target_features} target {$build_target_features_count ->
52+
[1] feature
53+
*[count] features
54+
} being enabled in the build configuration does not remove the requirement to list {$build_target_features_count ->
55+
[1] it
56+
*[count] them
57+
} in `#[target_feature]`
4758
4859
mir_transform_unaligned_packed_ref = reference to packed field is unaligned
4960
.note = packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses

compiler/rustc_mir_transform/src/check_unsafety.rs

+22-7
Original file line numberDiff line numberDiff line change
@@ -287,19 +287,20 @@ impl<'tcx> UnsafetyChecker<'_, 'tcx> {
287287
.safety;
288288
match safety {
289289
// `unsafe` blocks are required in safe code
290-
Safety::Safe => violations.into_iter().for_each(|&violation| {
290+
Safety::Safe => violations.into_iter().for_each(|violation| {
291291
match violation.kind {
292292
UnsafetyViolationKind::General => {}
293293
UnsafetyViolationKind::UnsafeFn => {
294294
bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
295295
}
296296
}
297-
if !self.violations.contains(&violation) {
298-
self.violations.push(violation)
297+
if !self.violations.contains(violation) {
298+
self.violations.push(violation.clone())
299299
}
300300
}),
301301
// With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s
302-
Safety::FnUnsafe => violations.into_iter().for_each(|&(mut violation)| {
302+
Safety::FnUnsafe => violations.into_iter().for_each(|violation| {
303+
let mut violation = violation.clone();
303304
violation.kind = UnsafetyViolationKind::UnsafeFn;
304305
if !self.violations.contains(&violation) {
305306
self.violations.push(violation)
@@ -367,9 +368,22 @@ impl<'tcx> UnsafetyChecker<'_, 'tcx> {
367368

368369
// Is `callee_features` a subset of `calling_features`?
369370
if !callee_features.iter().all(|feature| self_features.contains(feature)) {
371+
let missing: Vec<_> = callee_features
372+
.iter()
373+
.copied()
374+
.filter(|feature| !self_features.contains(feature))
375+
.collect();
376+
let build_enabled = self
377+
.tcx
378+
.sess
379+
.target_features
380+
.iter()
381+
.copied()
382+
.filter(|feature| missing.contains(feature))
383+
.collect();
370384
self.require_unsafe(
371385
UnsafetyViolationKind::General,
372-
UnsafetyViolationDetails::CallToFunctionWith,
386+
UnsafetyViolationDetails::CallToFunctionWith { missing, build_enabled },
373387
)
374388
}
375389
}
@@ -528,8 +542,9 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
528542
// Only suggest wrapping the entire function body in an unsafe block once
529543
let mut suggest_unsafe_block = true;
530544

531-
for &UnsafetyViolation { source_info, lint_root, kind, details } in violations.iter() {
532-
let details = errors::RequiresUnsafeDetail { violation: details, span: source_info.span };
545+
for &UnsafetyViolation { source_info, lint_root, kind, ref details } in violations.iter() {
546+
let details =
547+
errors::RequiresUnsafeDetail { violation: details.clone(), span: source_info.span };
533548

534549
match kind {
535550
UnsafetyViolationKind::General => {

compiler/rustc_mir_transform/src/errors.rs

+79-46
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
use std::borrow::Cow;
2+
13
use rustc_errors::{
2-
Applicability, DecorateLint, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Handler,
3-
IntoDiagnostic,
4+
Applicability, DecorateLint, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage,
5+
EmissionGuarantee, Handler, IntoDiagnostic,
46
};
57
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
68
use rustc_middle::mir::{AssertKind, UnsafetyViolationDetails};
@@ -9,6 +11,8 @@ use rustc_session::lint::{self, Lint};
911
use rustc_span::def_id::DefId;
1012
use rustc_span::Span;
1113

14+
use crate::fluent_generated as fluent;
15+
1216
#[derive(LintDiagnostic)]
1317
pub(crate) enum ConstMutate {
1418
#[diag(mir_transform_const_modify)]
@@ -61,72 +65,105 @@ pub(crate) struct RequiresUnsafe {
6165
impl<'sess, G: EmissionGuarantee> IntoDiagnostic<'sess, G> for RequiresUnsafe {
6266
#[track_caller]
6367
fn into_diagnostic(self, handler: &'sess Handler) -> DiagnosticBuilder<'sess, G> {
64-
let mut diag =
65-
handler.struct_diagnostic(crate::fluent_generated::mir_transform_requires_unsafe);
68+
let mut diag = handler.struct_diagnostic(fluent::mir_transform_requires_unsafe);
6669
diag.code(rustc_errors::DiagnosticId::Error("E0133".to_string()));
6770
diag.set_span(self.span);
6871
diag.span_label(self.span, self.details.label());
69-
diag.note(self.details.note());
7072
let desc = handler.eagerly_translate_to_string(self.details.label(), [].into_iter());
7173
diag.set_arg("details", desc);
7274
diag.set_arg("op_in_unsafe_fn_allowed", self.op_in_unsafe_fn_allowed);
75+
self.details.add_subdiagnostics(&mut diag);
7376
if let Some(sp) = self.enclosing {
74-
diag.span_label(sp, crate::fluent_generated::mir_transform_not_inherited);
77+
diag.span_label(sp, fluent::mir_transform_not_inherited);
7578
}
7679
diag
7780
}
7881
}
7982

80-
#[derive(Copy, Clone)]
83+
#[derive(Clone)]
8184
pub(crate) struct RequiresUnsafeDetail {
8285
pub span: Span,
8386
pub violation: UnsafetyViolationDetails,
8487
}
8588

8689
impl RequiresUnsafeDetail {
87-
fn note(self) -> DiagnosticMessage {
90+
fn add_subdiagnostics<G: EmissionGuarantee>(&self, diag: &mut DiagnosticBuilder<'_, G>) {
8891
use UnsafetyViolationDetails::*;
8992
match self.violation {
90-
CallToUnsafeFunction => crate::fluent_generated::mir_transform_call_to_unsafe_note,
91-
UseOfInlineAssembly => crate::fluent_generated::mir_transform_use_of_asm_note,
93+
CallToUnsafeFunction => {
94+
diag.note(fluent::mir_transform_call_to_unsafe_note);
95+
}
96+
UseOfInlineAssembly => {
97+
diag.note(fluent::mir_transform_use_of_asm_note);
98+
}
9299
InitializingTypeWith => {
93-
crate::fluent_generated::mir_transform_initializing_valid_range_note
100+
diag.note(fluent::mir_transform_initializing_valid_range_note);
101+
}
102+
CastOfPointerToInt => {
103+
diag.note(fluent::mir_transform_const_ptr2int_note);
104+
}
105+
UseOfMutableStatic => {
106+
diag.note(fluent::mir_transform_use_of_static_mut_note);
107+
}
108+
UseOfExternStatic => {
109+
diag.note(fluent::mir_transform_use_of_extern_static_note);
110+
}
111+
DerefOfRawPointer => {
112+
diag.note(fluent::mir_transform_deref_ptr_note);
113+
}
114+
AccessToUnionField => {
115+
diag.note(fluent::mir_transform_union_access_note);
94116
}
95-
CastOfPointerToInt => crate::fluent_generated::mir_transform_const_ptr2int_note,
96-
UseOfMutableStatic => crate::fluent_generated::mir_transform_use_of_static_mut_note,
97-
UseOfExternStatic => crate::fluent_generated::mir_transform_use_of_extern_static_note,
98-
DerefOfRawPointer => crate::fluent_generated::mir_transform_deref_ptr_note,
99-
AccessToUnionField => crate::fluent_generated::mir_transform_union_access_note,
100117
MutationOfLayoutConstrainedField => {
101-
crate::fluent_generated::mir_transform_mutation_layout_constrained_note
118+
diag.note(fluent::mir_transform_mutation_layout_constrained_note);
102119
}
103120
BorrowOfLayoutConstrainedField => {
104-
crate::fluent_generated::mir_transform_mutation_layout_constrained_borrow_note
121+
diag.note(fluent::mir_transform_mutation_layout_constrained_borrow_note);
122+
}
123+
CallToFunctionWith { ref missing, ref build_enabled } => {
124+
diag.help(fluent::mir_transform_target_feature_call_help);
125+
diag.set_arg(
126+
"missing_target_features",
127+
DiagnosticArgValue::StrListSepByAnd(
128+
missing.iter().map(|feature| Cow::from(feature.as_str())).collect(),
129+
),
130+
);
131+
diag.set_arg("missing_target_features_count", missing.len());
132+
if !build_enabled.is_empty() {
133+
diag.note(fluent::mir_transform_target_feature_call_note);
134+
diag.set_arg(
135+
"build_target_features",
136+
DiagnosticArgValue::StrListSepByAnd(
137+
build_enabled
138+
.iter()
139+
.map(|feature| Cow::from(feature.as_str()))
140+
.collect(),
141+
),
142+
);
143+
diag.set_arg("build_target_features_count", build_enabled.len());
144+
}
105145
}
106-
CallToFunctionWith => crate::fluent_generated::mir_transform_target_feature_call_note,
107146
}
108147
}
109148

110-
fn label(self) -> DiagnosticMessage {
149+
fn label(&self) -> DiagnosticMessage {
111150
use UnsafetyViolationDetails::*;
112151
match self.violation {
113-
CallToUnsafeFunction => crate::fluent_generated::mir_transform_call_to_unsafe_label,
114-
UseOfInlineAssembly => crate::fluent_generated::mir_transform_use_of_asm_label,
115-
InitializingTypeWith => {
116-
crate::fluent_generated::mir_transform_initializing_valid_range_label
117-
}
118-
CastOfPointerToInt => crate::fluent_generated::mir_transform_const_ptr2int_label,
119-
UseOfMutableStatic => crate::fluent_generated::mir_transform_use_of_static_mut_label,
120-
UseOfExternStatic => crate::fluent_generated::mir_transform_use_of_extern_static_label,
121-
DerefOfRawPointer => crate::fluent_generated::mir_transform_deref_ptr_label,
122-
AccessToUnionField => crate::fluent_generated::mir_transform_union_access_label,
152+
CallToUnsafeFunction => fluent::mir_transform_call_to_unsafe_label,
153+
UseOfInlineAssembly => fluent::mir_transform_use_of_asm_label,
154+
InitializingTypeWith => fluent::mir_transform_initializing_valid_range_label,
155+
CastOfPointerToInt => fluent::mir_transform_const_ptr2int_label,
156+
UseOfMutableStatic => fluent::mir_transform_use_of_static_mut_label,
157+
UseOfExternStatic => fluent::mir_transform_use_of_extern_static_label,
158+
DerefOfRawPointer => fluent::mir_transform_deref_ptr_label,
159+
AccessToUnionField => fluent::mir_transform_union_access_label,
123160
MutationOfLayoutConstrainedField => {
124-
crate::fluent_generated::mir_transform_mutation_layout_constrained_label
161+
fluent::mir_transform_mutation_layout_constrained_label
125162
}
126163
BorrowOfLayoutConstrainedField => {
127-
crate::fluent_generated::mir_transform_mutation_layout_constrained_borrow_label
164+
fluent::mir_transform_mutation_layout_constrained_borrow_label
128165
}
129-
CallToFunctionWith => crate::fluent_generated::mir_transform_target_feature_call_label,
166+
CallToFunctionWith { .. } => fluent::mir_transform_target_feature_call_label,
130167
}
131168
}
132169
}
@@ -151,12 +188,12 @@ impl<'a> DecorateLint<'a, ()> for UnsafeOpInUnsafeFn {
151188
let desc = handler.eagerly_translate_to_string(self.details.label(), [].into_iter());
152189
diag.set_arg("details", desc);
153190
diag.span_label(self.details.span, self.details.label());
154-
diag.note(self.details.note());
191+
self.details.add_subdiagnostics(diag);
155192

156193
if let Some((start, end, fn_sig)) = self.suggest_unsafe_block {
157-
diag.span_note(fn_sig, crate::fluent_generated::mir_transform_note);
194+
diag.span_note(fn_sig, fluent::mir_transform_note);
158195
diag.tool_only_multipart_suggestion(
159-
crate::fluent_generated::mir_transform_suggestion,
196+
fluent::mir_transform_suggestion,
160197
vec![(start, " unsafe {".into()), (end, "}".into())],
161198
Applicability::MaybeIncorrect,
162199
);
@@ -166,7 +203,7 @@ impl<'a> DecorateLint<'a, ()> for UnsafeOpInUnsafeFn {
166203
}
167204

168205
fn msg(&self) -> DiagnosticMessage {
169-
crate::fluent_generated::mir_transform_unsafe_op_in_unsafe_fn
206+
fluent::mir_transform_unsafe_op_in_unsafe_fn
170207
}
171208
}
172209

@@ -193,12 +230,8 @@ impl<'a, P: std::fmt::Debug> DecorateLint<'a, ()> for AssertLint<P> {
193230

194231
fn msg(&self) -> DiagnosticMessage {
195232
match self {
196-
AssertLint::ArithmeticOverflow(..) => {
197-
crate::fluent_generated::mir_transform_arithmetic_overflow
198-
}
199-
AssertLint::UnconditionalPanic(..) => {
200-
crate::fluent_generated::mir_transform_operation_will_panic
201-
}
233+
AssertLint::ArithmeticOverflow(..) => fluent::mir_transform_arithmetic_overflow,
234+
AssertLint::UnconditionalPanic(..) => fluent::mir_transform_operation_will_panic,
202235
}
203236
}
204237
}
@@ -255,19 +288,19 @@ impl<'a> DecorateLint<'a, ()> for MustNotSupend<'_, '_> {
255288
self,
256289
diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
257290
) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
258-
diag.span_label(self.yield_sp, crate::fluent_generated::_subdiag::label);
291+
diag.span_label(self.yield_sp, fluent::_subdiag::label);
259292
if let Some(reason) = self.reason {
260293
diag.subdiagnostic(reason);
261294
}
262-
diag.span_help(self.src_sp, crate::fluent_generated::_subdiag::help);
295+
diag.span_help(self.src_sp, fluent::_subdiag::help);
263296
diag.set_arg("pre", self.pre);
264297
diag.set_arg("def_path", self.tcx.def_path_str(self.def_id));
265298
diag.set_arg("post", self.post);
266299
diag
267300
}
268301

269302
fn msg(&self) -> rustc_errors::DiagnosticMessage {
270-
crate::fluent_generated::mir_transform_must_not_suspend
303+
fluent::mir_transform_must_not_suspend
271304
}
272305
}
273306

0 commit comments

Comments
 (0)