Skip to content

Commit 6b066a9

Browse files
committed
thir-unsafeck: print list of missing target features when calling a function with target features outside an unsafe block
1 parent 51ba662 commit 6b066a9

File tree

6 files changed

+187
-32
lines changed

6 files changed

+187
-32
lines changed

compiler/rustc_mir_build/messages.ftl

+33-3
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,32 @@ mir_build_borrow_of_moved_value = borrow of moved value
3030
3131
mir_build_call_to_fn_with_requires_unsafe =
3232
call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block
33-
.note = can only be called if the required target features are available
33+
.help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count ->
34+
[1] feature
35+
*[count] features
36+
}: {$missing_target_features}
37+
.note = the {$build_target_features} target {$build_target_features_count ->
38+
[1] feature
39+
*[count] features
40+
} being enabled in the build configuration does not remove the requirement to list {$build_target_features_count ->
41+
[1] it
42+
*[count] them
43+
} in `#[target_feature]`
3444
.label = call to function with `#[target_feature]`
3545
3646
mir_build_call_to_fn_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
3747
call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe function or block
38-
.note = can only be called if the required target features are available
48+
.help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count ->
49+
[1] feature
50+
*[count] features
51+
}: {$missing_target_features}
52+
.note = the {$build_target_features} target {$build_target_features_count ->
53+
[1] feature
54+
*[count] features
55+
} being enabled in the build configuration does not remove the requirement to list {$build_target_features_count ->
56+
[1] it
57+
*[count] them
58+
} in `#[target_feature]`
3959
.label = call to function with `#[target_feature]`
4060
4161
mir_build_call_to_unsafe_fn_requires_unsafe =
@@ -330,7 +350,17 @@ mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_uns
330350
331351
mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe =
332352
call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block (error E0133)
333-
.note = can only be called if the required target features are available
353+
.help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count ->
354+
[1] feature
355+
*[count] features
356+
}: {$missing_target_features}
357+
.note = the {$build_target_features} target {$build_target_features_count ->
358+
[1] feature
359+
*[count] features
360+
} being enabled in the build configuration does not remove the requirement to list {$build_target_features_count ->
361+
[1] it
362+
*[count] them
363+
} in `#[target_feature]`
334364
.label = call to function with `#[target_feature]`
335365
336366
mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe =

compiler/rustc_mir_build/src/check_unsafety.rs

+67-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
use std::borrow::Cow;
2+
13
use crate::build::ExprCategory;
24
use crate::errors::*;
35
use rustc_middle::thir::visit::{self, Visitor};
46

7+
use rustc_errors::DiagnosticArgValue;
58
use rustc_hir as hir;
69
use rustc_middle::mir::BorrowKind;
710
use rustc_middle::thir::*;
@@ -392,15 +395,29 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
392395
// the call requires `unsafe`. Don't check this on wasm
393396
// targets, though. For more information on wasm see the
394397
// is_like_wasm check in hir_analysis/src/collect.rs
398+
let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
395399
if !self.tcx.sess.target.options.is_like_wasm
396-
&& !self
397-
.tcx
398-
.codegen_fn_attrs(func_did)
399-
.target_features
400+
&& !callee_features
400401
.iter()
401402
.all(|feature| self.body_target_features.contains(feature))
402403
{
403-
self.requires_unsafe(expr.span, CallToFunctionWith(func_did));
404+
let missing: Vec<_> = callee_features
405+
.iter()
406+
.copied()
407+
.filter(|feature| !self.body_target_features.contains(feature))
408+
.collect();
409+
let build_enabled = self
410+
.tcx
411+
.sess
412+
.target_features
413+
.iter()
414+
.copied()
415+
.filter(|feature| missing.contains(feature))
416+
.collect();
417+
self.requires_unsafe(
418+
expr.span,
419+
CallToFunctionWith { function: func_did, missing, build_enabled },
420+
);
404421
}
405422
}
406423
}
@@ -526,7 +543,7 @@ struct UnusedUnsafeWarning {
526543
enclosing_unsafe: Option<UnusedUnsafeEnclosing>,
527544
}
528545

529-
#[derive(Clone, Copy, PartialEq)]
546+
#[derive(Clone, PartialEq)]
530547
enum UnsafeOpKind {
531548
CallToUnsafeFunction(Option<DefId>),
532549
UseOfInlineAssembly,
@@ -537,7 +554,15 @@ enum UnsafeOpKind {
537554
AccessToUnionField,
538555
MutationOfLayoutConstrainedField,
539556
BorrowOfLayoutConstrainedField,
540-
CallToFunctionWith(DefId),
557+
CallToFunctionWith {
558+
function: DefId,
559+
/// Target features enabled in callee's `#[target_feature]` but missing in
560+
/// caller's `#[target_feature]`.
561+
missing: Vec<Symbol>,
562+
/// Target features in `missing` that are enabled at compile time
563+
/// (e.g., with `-C target-feature`).
564+
build_enabled: Vec<Symbol>,
565+
},
541566
}
542567

543568
use UnsafeOpKind::*;
@@ -658,13 +683,22 @@ impl UnsafeOpKind {
658683
unsafe_not_inherited_note,
659684
},
660685
),
661-
CallToFunctionWith(did) => tcx.emit_spanned_lint(
686+
CallToFunctionWith { function, missing, build_enabled } => tcx.emit_spanned_lint(
662687
UNSAFE_OP_IN_UNSAFE_FN,
663688
hir_id,
664689
span,
665690
UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
666691
span,
667-
function: &with_no_trimmed_paths!(tcx.def_path_str(*did)),
692+
function: &with_no_trimmed_paths!(tcx.def_path_str(*function)),
693+
missing_target_features: DiagnosticArgValue::StrListSepByAnd(
694+
missing.iter().map(|feature| Cow::from(feature.as_str())).collect(),
695+
),
696+
missing_target_features_count: missing.len(),
697+
note: if build_enabled.is_empty() { None } else { Some(()) },
698+
build_target_features: DiagnosticArgValue::StrListSepByAnd(
699+
build_enabled.iter().map(|feature| Cow::from(feature.as_str())).collect(),
700+
),
701+
build_target_features_count: build_enabled.len(),
668702
unsafe_not_inherited_note,
669703
},
670704
),
@@ -821,18 +855,38 @@ impl UnsafeOpKind {
821855
unsafe_not_inherited_note,
822856
});
823857
}
824-
CallToFunctionWith(did) if unsafe_op_in_unsafe_fn_allowed => {
858+
CallToFunctionWith { function, missing, build_enabled }
859+
if unsafe_op_in_unsafe_fn_allowed =>
860+
{
825861
tcx.sess.emit_err(CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
826862
span,
863+
missing_target_features: DiagnosticArgValue::StrListSepByAnd(
864+
missing.iter().map(|feature| Cow::from(feature.as_str())).collect(),
865+
),
866+
missing_target_features_count: missing.len(),
867+
note: if build_enabled.is_empty() { None } else { Some(()) },
868+
build_target_features: DiagnosticArgValue::StrListSepByAnd(
869+
build_enabled.iter().map(|feature| Cow::from(feature.as_str())).collect(),
870+
),
871+
build_target_features_count: build_enabled.len(),
827872
unsafe_not_inherited_note,
828-
function: &tcx.def_path_str(*did),
873+
function: &tcx.def_path_str(*function),
829874
});
830875
}
831-
CallToFunctionWith(did) => {
876+
CallToFunctionWith { function, missing, build_enabled } => {
832877
tcx.sess.emit_err(CallToFunctionWithRequiresUnsafe {
833878
span,
879+
missing_target_features: DiagnosticArgValue::StrListSepByAnd(
880+
missing.iter().map(|feature| Cow::from(feature.as_str())).collect(),
881+
),
882+
missing_target_features_count: missing.len(),
883+
note: if build_enabled.is_empty() { None } else { Some(()) },
884+
build_target_features: DiagnosticArgValue::StrListSepByAnd(
885+
build_enabled.iter().map(|feature| Cow::from(feature.as_str())).collect(),
886+
),
887+
build_target_features_count: build_enabled.len(),
834888
unsafe_not_inherited_note,
835-
function: &tcx.def_path_str(*did),
889+
function: &tcx.def_path_str(*function),
836890
});
837891
}
838892
}

compiler/rustc_mir_build/src/errors.rs

+22-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::{
22
fluent_generated as fluent,
33
thir::pattern::{deconstruct_pat::WitnessPat, MatchCheckCtxt},
44
};
5+
use rustc_errors::DiagnosticArgValue;
56
use rustc_errors::{
67
error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
78
Handler, IntoDiagnostic, MultiSpan, SubdiagnosticMessage,
@@ -124,11 +125,17 @@ pub struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe {
124125

125126
#[derive(LintDiagnostic)]
126127
#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe)]
127-
#[note]
128+
#[help]
128129
pub struct UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe<'a> {
129130
#[label]
130131
pub span: Span,
131132
pub function: &'a str,
133+
pub missing_target_features: DiagnosticArgValue<'a>,
134+
pub missing_target_features_count: usize,
135+
#[note]
136+
pub note: Option<()>,
137+
pub build_target_features: DiagnosticArgValue<'a>,
138+
pub build_target_features_count: usize,
132139
#[subdiagnostic]
133140
pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
134141
}
@@ -369,24 +376,36 @@ pub struct BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed
369376

370377
#[derive(Diagnostic)]
371378
#[diag(mir_build_call_to_fn_with_requires_unsafe, code = "E0133")]
372-
#[note]
379+
#[help]
373380
pub struct CallToFunctionWithRequiresUnsafe<'a> {
374381
#[primary_span]
375382
#[label]
376383
pub span: Span,
377384
pub function: &'a str,
385+
pub missing_target_features: DiagnosticArgValue<'a>,
386+
pub missing_target_features_count: usize,
387+
#[note]
388+
pub note: Option<()>,
389+
pub build_target_features: DiagnosticArgValue<'a>,
390+
pub build_target_features_count: usize,
378391
#[subdiagnostic]
379392
pub unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
380393
}
381394

382395
#[derive(Diagnostic)]
383396
#[diag(mir_build_call_to_fn_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")]
384-
#[note]
397+
#[help]
385398
pub struct CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed<'a> {
386399
#[primary_span]
387400
#[label]
388401
pub span: Span,
389402
pub function: &'a str,
403+
pub missing_target_features: DiagnosticArgValue<'a>,
404+
pub missing_target_features_count: usize,
405+
#[note]
406+
pub note: Option<()>,
407+
pub build_target_features: DiagnosticArgValue<'a>,
408+
pub build_target_features_count: usize,
390409
#[subdiagnostic]
391410
pub unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
392411
}

tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.mir.stderr

+20-1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,25 @@ LL | const _: () = sse2_and_fxsr();
9191
= help: in order for the call to be safe, the context requires the following additional target features: sse2 and fxsr
9292
= note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]`
9393

94-
error: aborting due to 11 previous errors
94+
error: call to function with `#[target_feature]` is unsafe and requires unsafe block (error E0133)
95+
--> $DIR/safe-calls.rs:82:5
96+
|
97+
LL | sse2();
98+
| ^^^^^^ call to function with `#[target_feature]`
99+
|
100+
= help: in order for the call to be safe, the context requires the following additional target feature: sse2
101+
= note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]`
102+
note: an unsafe function restricts its caller, but its body is safe by default
103+
--> $DIR/safe-calls.rs:81:1
104+
|
105+
LL | unsafe fn needs_unsafe_block() {
106+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
107+
note: the lint level is defined here
108+
--> $DIR/safe-calls.rs:78:8
109+
|
110+
LL | #[deny(unsafe_op_in_unsafe_fn)]
111+
| ^^^^^^^^^^^^^^^^^^^^^^
112+
113+
error: aborting due to 12 previous errors
95114

96115
For more information about this error, try `rustc --explain E0133`.

tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs

+9
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,13 @@ const _: () = sse2_and_fxsr();
7575
//[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
7676
//[thir]~^^ ERROR call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe
7777

78+
#[deny(unsafe_op_in_unsafe_fn)]
79+
#[target_feature(enable = "avx")]
80+
#[target_feature(enable = "bmi2")]
81+
unsafe fn needs_unsafe_block() {
82+
sse2();
83+
//[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
84+
//[thir]~^^ ERROR call to function `sse2` with `#[target_feature]` is unsafe
85+
}
86+
7887
fn main() {}

0 commit comments

Comments
 (0)