From 9b9206980ec46c9a6425d01e015fc64c3f8f7788 Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Wed, 13 Aug 2025 13:23:18 +0000 Subject: Add test for issue 122734 --- tests/codegen-llvm/issues/issue-122734-match-eq.rs | 78 ++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/codegen-llvm/issues/issue-122734-match-eq.rs (limited to 'tests/codegen-llvm') diff --git a/tests/codegen-llvm/issues/issue-122734-match-eq.rs b/tests/codegen-llvm/issues/issue-122734-match-eq.rs new file mode 100644 index 00000000000..89858972677 --- /dev/null +++ b/tests/codegen-llvm/issues/issue-122734-match-eq.rs @@ -0,0 +1,78 @@ +//@ min-llvm-version: 21 +//@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled +//! Tests that matching + eq on `Option` produces a simple compare with no branching + +#![crate_type = "lib"] + +#[derive(PartialEq)] +pub enum TwoNum { + A, + B, +} + +#[derive(PartialEq)] +pub enum ThreeNum { + A, + B, + C, +} + +// CHECK-LABEL: @match_two +#[no_mangle] +pub fn match_two(a: Option, b: Option) -> bool { + // CHECK-NEXT: start: + // CHECK-NEXT: icmp eq i8 + // CHECK-NEXT: ret + match (a, b) { + (Some(x), Some(y)) => x == y, + (Some(_), None) => false, + (None, Some(_)) => false, + (None, None) => true, + } +} + +// CHECK-LABEL: @match_three +#[no_mangle] +pub fn match_three(a: Option, b: Option) -> bool { + // CHECK-NEXT: start: + // CHECK-NEXT: icmp eq + // CHECK-NEXT: ret + match (a, b) { + (Some(x), Some(y)) => x == y, + (Some(_), None) => false, + (None, Some(_)) => false, + (None, None) => true, + } +} + +// CHECK-LABEL: @match_two_ref +#[no_mangle] +pub fn match_two_ref(a: &Option, b: &Option) -> bool { + // CHECK-NEXT: start: + // CHECK-NEXT: load i8 + // CHECK-NEXT: load i8 + // CHECK-NEXT: icmp eq i8 + // CHECK-NEXT: ret + match (a, b) { + (Some(x), Some(y)) => x == y, + (Some(_), None) => false, + (None, Some(_)) => false, + (None, None) => true, + } +} + +// CHECK-LABEL: @match_three_ref +#[no_mangle] +pub fn match_three_ref(a: &Option, b: &Option) -> bool { + // CHECK-NEXT: start: + // CHECK-NEXT: load i8 + // CHECK-NEXT: load i8 + // CHECK-NEXT: icmp eq + // CHECK-NEXT: ret + match (a, b) { + (Some(x), Some(y)) => x == y, + (Some(_), None) => false, + (None, Some(_)) => false, + (None, None) => true, + } +} -- cgit 1.4.1-3-g733a5 From 1cd7080c3a7f29297675a72a157575ae12717304 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 7 May 2025 07:57:01 +0000 Subject: Add -Zindirect-branch-cs-prefix (from draft PR) --- compiler/rustc_codegen_llvm/src/context.rs | 9 +++++++++ compiler/rustc_interface/src/tests.rs | 1 + compiler/rustc_session/src/options.rs | 2 ++ tests/codegen-llvm/indirect-branch-cs-prefix.rs | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+) create mode 100644 tests/codegen-llvm/indirect-branch-cs-prefix.rs (limited to 'tests/codegen-llvm') diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index b0f3494ea68..ea3a21dfdb5 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -462,6 +462,15 @@ pub(crate) unsafe fn create_module<'ll>( } } + if sess.opts.unstable_opts.indirect_branch_cs_prefix { + llvm::add_module_flag_u32( + llmod, + llvm::ModuleFlagMergeBehavior::Override, + "indirect_branch_cs_prefix", + 1, + ); + } + match (sess.opts.unstable_opts.small_data_threshold, sess.target.small_data_threshold_support()) { // Set up the small-data optimization limit for architectures that use diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 0a764808f95..4425877308a 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -807,6 +807,7 @@ fn test_unstable_options_tracking_hash() { tracked!(hint_mostly_unused, true); tracked!(human_readable_cgu_names, true); tracked!(incremental_ignore_spans, true); + tracked!(indirect_branch_cs_prefix, true); tracked!(inline_mir, Some(true)); tracked!(inline_mir_hint_threshold, Some(123)); tracked!(inline_mir_threshold, Some(123)); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 0e112edc733..fce4d18a1d8 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2295,6 +2295,8 @@ options! { - hashes of green query instances - hash collisions of query keys - hash collisions when creating dep-nodes"), + indirect_branch_cs_prefix: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], + "add cs prefix to call and jmp to indirect thunk (default: no)"), inline_llvm: bool = (true, parse_bool, [TRACKED], "enable LLVM inlining (default: yes)"), inline_mir: Option = (None, parse_opt_bool, [TRACKED], diff --git a/tests/codegen-llvm/indirect-branch-cs-prefix.rs b/tests/codegen-llvm/indirect-branch-cs-prefix.rs new file mode 100644 index 00000000000..a00b63a4970 --- /dev/null +++ b/tests/codegen-llvm/indirect-branch-cs-prefix.rs @@ -0,0 +1,18 @@ +// Test that the `indirect_branch_cs_prefix` module attribute is (not) emitted when the +// `-Zindirect-branch-cs-prefix` flag is (not) set. + +//@ add-core-stubs +//@ revisions: unset set +//@ needs-llvm-components: x86 +//@ compile-flags: --target x86_64-unknown-linux-gnu +//@ [set] compile-flags: -Zindirect-branch-cs-prefix + +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +// unset-NOT: !{{[0-9]+}} = !{i32 4, !"indirect_branch_cs_prefix", i32 1} +// set: !{{[0-9]+}} = !{i32 4, !"indirect_branch_cs_prefix", i32 1} -- cgit 1.4.1-3-g733a5 From 1a29d9c23ff8df46197f5ebd3df15fe99904d312 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Wed, 7 May 2025 15:38:14 +0200 Subject: Add `-Zindirect-branch-cs-prefix` option This is intended to be used for Linux kernel RETPOLINE builds. Signed-off-by: Miguel Ojeda --- compiler/rustc_session/messages.ftl | 2 ++ compiler/rustc_session/src/errors.rs | 4 ++++ compiler/rustc_session/src/options.rs | 2 +- compiler/rustc_session/src/session.rs | 6 +++++ .../compiler-flags/indirect-branch-cs-prefix.md | 19 +++++++++++++++ .../x86_64-indirect-branch-cs-prefix.rs | 27 ++++++++++++++++++++++ tests/codegen-llvm/indirect-branch-cs-prefix.rs | 8 +++---- .../requires-x86-or-x86_64.aarch64.stderr | 4 ++++ .../requires-x86-or-x86_64.rs | 21 +++++++++++++++++ 9 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 src/doc/unstable-book/src/compiler-flags/indirect-branch-cs-prefix.md create mode 100644 tests/assembly-llvm/x86_64-indirect-branch-cs-prefix.rs create mode 100644 tests/ui/invalid-compile-flags/indirect-branch-cs-prefix/requires-x86-or-x86_64.aarch64.stderr create mode 100644 tests/ui/invalid-compile-flags/indirect-branch-cs-prefix/requires-x86-or-x86_64.rs (limited to 'tests/codegen-llvm') diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 528c52eace7..80754964c43 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -49,6 +49,8 @@ session_hexadecimal_float_literal_not_supported = hexadecimal float literal is n session_incompatible_linker_flavor = linker flavor `{$flavor}` is incompatible with the current target .note = compatible flavors are: {$compatible_list} +session_indirect_branch_cs_prefix_requires_x86_or_x86_64 = `-Zindirect-branch-cs-prefix` is only supported on x86 and x86_64 + session_instrumentation_not_supported = {$us} instrumentation is not supported for this target session_int_literal_too_large = integer literal is too large diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index bf95014843d..9471807df01 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -471,6 +471,10 @@ pub(crate) struct FunctionReturnRequiresX86OrX8664; #[diag(session_function_return_thunk_extern_requires_non_large_code_model)] pub(crate) struct FunctionReturnThunkExternRequiresNonLargeCodeModel; +#[derive(Diagnostic)] +#[diag(session_indirect_branch_cs_prefix_requires_x86_or_x86_64)] +pub(crate) struct IndirectBranchCsPrefixRequiresX86OrX8664; + #[derive(Diagnostic)] #[diag(session_unsupported_regparm)] pub(crate) struct UnsupportedRegparm { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index fce4d18a1d8..6d5be2d92cd 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2296,7 +2296,7 @@ options! { - hash collisions of query keys - hash collisions when creating dep-nodes"), indirect_branch_cs_prefix: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], - "add cs prefix to call and jmp to indirect thunk (default: no)"), + "add `cs` prefix to `call` and `jmp` to indirect thunks (default: no)"), inline_llvm: bool = (true, parse_bool, [TRACKED], "enable LLVM inlining (default: yes)"), inline_mir: Option = (None, parse_opt_bool, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index c6956cf5f23..c8f4b511a7e 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1368,6 +1368,12 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } } + if sess.opts.unstable_opts.indirect_branch_cs_prefix { + if sess.target.arch != "x86" && sess.target.arch != "x86_64" { + sess.dcx().emit_err(errors::IndirectBranchCsPrefixRequiresX86OrX8664); + } + } + if let Some(regparm) = sess.opts.unstable_opts.regparm { if regparm > 3 { sess.dcx().emit_err(errors::UnsupportedRegparm { regparm }); diff --git a/src/doc/unstable-book/src/compiler-flags/indirect-branch-cs-prefix.md b/src/doc/unstable-book/src/compiler-flags/indirect-branch-cs-prefix.md new file mode 100644 index 00000000000..040e2d41701 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/indirect-branch-cs-prefix.md @@ -0,0 +1,19 @@ +# `indirect-branch-cs-prefix` + +The tracking issue for this feature is: https://github.com/rust-lang/rust/issues/116852. + +------------------------ + +Option `-Zindirect-branch-cs-prefix` controls whether a `cs` prefix is added to +`call` and `jmp` to indirect thunks. + +It is equivalent to [Clang]'s and [GCC]'s `-mindirect-branch-cs-prefix`. The +Linux kernel uses it for RETPOLINE builds. For details, see +[LLVM commit 6f867f910283] ("[X86] Support ``-mindirect-branch-cs-prefix`` for +call and jmp to indirect thunk") which introduces the feature. + +Only x86 and x86_64 are supported. + +[Clang]: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mindirect-branch-cs-prefix +[GCC]: https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-mindirect-branch-cs-prefix +[LLVM commit 6f867f910283]: https://github.com/llvm/llvm-project/commit/6f867f9102838ebe314c1f3661fdf95700386e5a diff --git a/tests/assembly-llvm/x86_64-indirect-branch-cs-prefix.rs b/tests/assembly-llvm/x86_64-indirect-branch-cs-prefix.rs new file mode 100644 index 00000000000..8e4470ee451 --- /dev/null +++ b/tests/assembly-llvm/x86_64-indirect-branch-cs-prefix.rs @@ -0,0 +1,27 @@ +// Test that the `cs` prefix is (not) added into a `call` and a `jmp` to the +// indirect thunk when the `-Zindirect-branch-cs-prefix` flag is (not) set. + +//@ revisions: unset set +//@ assembly-output: emit-asm +//@ compile-flags: -Copt-level=3 -Cunsafe-allow-abi-mismatch=retpoline,retpoline-external-thunk,indirect-branch-cs-prefix -Zretpoline-external-thunk +//@ [set] compile-flags: -Zindirect-branch-cs-prefix +//@ only-x86_64 +//@ ignore-apple Symbol is called `___x86_indirect_thunk` (Darwin's extra underscore) + +#![crate_type = "lib"] + +// CHECK-LABEL: foo: +#[no_mangle] +pub fn foo(g: fn()) { + // unset-NOT: cs + // unset: callq {{__x86_indirect_thunk.*}} + // set: cs + // set-NEXT: callq {{__x86_indirect_thunk.*}} + g(); + + // unset-NOT: cs + // unset: jmp {{__x86_indirect_thunk.*}} + // set: cs + // set-NEXT: jmp {{__x86_indirect_thunk.*}} + g(); +} diff --git a/tests/codegen-llvm/indirect-branch-cs-prefix.rs b/tests/codegen-llvm/indirect-branch-cs-prefix.rs index a00b63a4970..df25008d5f0 100644 --- a/tests/codegen-llvm/indirect-branch-cs-prefix.rs +++ b/tests/codegen-llvm/indirect-branch-cs-prefix.rs @@ -1,5 +1,5 @@ -// Test that the `indirect_branch_cs_prefix` module attribute is (not) emitted when the -// `-Zindirect-branch-cs-prefix` flag is (not) set. +// Test that the `indirect_branch_cs_prefix` module attribute is (not) +// emitted when the `-Zindirect-branch-cs-prefix` flag is (not) set. //@ add-core-stubs //@ revisions: unset set @@ -11,8 +11,8 @@ #![feature(no_core, lang_items)] #![no_core] -#[lang = "sized"] -trait Sized {} +extern crate minicore; +use minicore::*; // unset-NOT: !{{[0-9]+}} = !{i32 4, !"indirect_branch_cs_prefix", i32 1} // set: !{{[0-9]+}} = !{i32 4, !"indirect_branch_cs_prefix", i32 1} diff --git a/tests/ui/invalid-compile-flags/indirect-branch-cs-prefix/requires-x86-or-x86_64.aarch64.stderr b/tests/ui/invalid-compile-flags/indirect-branch-cs-prefix/requires-x86-or-x86_64.aarch64.stderr new file mode 100644 index 00000000000..e3f7871da35 --- /dev/null +++ b/tests/ui/invalid-compile-flags/indirect-branch-cs-prefix/requires-x86-or-x86_64.aarch64.stderr @@ -0,0 +1,4 @@ +error: `-Zindirect-branch-cs-prefix` is only supported on x86 and x86_64 + +error: aborting due to 1 previous error + diff --git a/tests/ui/invalid-compile-flags/indirect-branch-cs-prefix/requires-x86-or-x86_64.rs b/tests/ui/invalid-compile-flags/indirect-branch-cs-prefix/requires-x86-or-x86_64.rs new file mode 100644 index 00000000000..bc81c993d26 --- /dev/null +++ b/tests/ui/invalid-compile-flags/indirect-branch-cs-prefix/requires-x86-or-x86_64.rs @@ -0,0 +1,21 @@ +//@ revisions: x86 x86_64 aarch64 + +//@ compile-flags: -Zindirect-branch-cs-prefix + +//@[x86] check-pass +//@[x86] needs-llvm-components: x86 +//@[x86] compile-flags: --target i686-unknown-linux-gnu + +//@[x86_64] check-pass +//@[x86_64] needs-llvm-components: x86 +//@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu + +//@[aarch64] check-fail +//@[aarch64] needs-llvm-components: aarch64 +//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu + +#![feature(no_core)] +#![no_core] +#![no_main] + +//[aarch64]~? ERROR `-Zindirect-branch-cs-prefix` is only supported on x86 and x86_64 -- cgit 1.4.1-3-g733a5 From 3ef065bf872ce62a18336dca0daf47b3e9f7da64 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Wed, 18 Jun 2025 12:53:34 +0000 Subject: Implement the #[sanitize(..)] attribute This change implements the #[sanitize(..)] attribute, which opts to replace the currently unstable #[no_sanitize]. Essentially the new attribute works similar as #[no_sanitize], just with more flexible options regarding where it is applied. E.g. it is possible to turn a certain sanitizer either on or off: `#[sanitize(address = "on|off")]` This attribute now also applies to more places, e.g. it is possible to turn off a sanitizer for an entire module or impl block: ```rust \#[sanitize(address = "off")] mod foo { fn unsanitized(..) {} #[sanitize(address = "on")] fn sanitized(..) {} } \#[sanitize(thread = "off")] impl MyTrait for () { ... } ``` This attribute is enabled behind the unstable `sanitize` feature. --- compiler/rustc_codegen_ssa/messages.ftl | 3 + compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 102 ++++++++++- compiler/rustc_codegen_ssa/src/errors.rs | 8 + compiler/rustc_feature/src/builtin_attrs.rs | 4 + compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_middle/src/query/erase.rs | 1 + compiler/rustc_middle/src/query/mod.rs | 12 +- compiler/rustc_passes/messages.ftl | 6 + compiler/rustc_passes/src/check_attr.rs | 44 ++++- compiler/rustc_passes/src/errors.rs | 17 ++ tests/codegen-llvm/sanitizer/sanitize-off.rs | 118 +++++++++++++ tests/ui/feature-gates/feature-gate-sanitize.rs | 4 + .../ui/feature-gates/feature-gate-sanitize.stderr | 13 ++ tests/ui/sanitize-attr/invalid-sanitize.rs | 22 +++ tests/ui/sanitize-attr/invalid-sanitize.stderr | 82 +++++++++ tests/ui/sanitize-attr/valid-sanitize.rs | 115 +++++++++++++ tests/ui/sanitize-attr/valid-sanitize.stderr | 190 +++++++++++++++++++++ tests/ui/sanitizer/inline-always-sanitize.rs | 15 ++ tests/ui/sanitizer/inline-always-sanitize.stderr | 15 ++ tests/ui/sanitizer/inline-always.rs | 1 - tests/ui/sanitizer/inline-always.stderr | 4 +- 21 files changed, 771 insertions(+), 7 deletions(-) create mode 100644 tests/codegen-llvm/sanitizer/sanitize-off.rs create mode 100644 tests/ui/feature-gates/feature-gate-sanitize.rs create mode 100644 tests/ui/feature-gates/feature-gate-sanitize.stderr create mode 100644 tests/ui/sanitize-attr/invalid-sanitize.rs create mode 100644 tests/ui/sanitize-attr/invalid-sanitize.stderr create mode 100644 tests/ui/sanitize-attr/valid-sanitize.rs create mode 100644 tests/ui/sanitize-attr/valid-sanitize.stderr create mode 100644 tests/ui/sanitizer/inline-always-sanitize.rs create mode 100644 tests/ui/sanitizer/inline-always-sanitize.stderr (limited to 'tests/codegen-llvm') diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index b6cfea88363..75cc60587b8 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -174,6 +174,9 @@ codegen_ssa_invalid_monomorphization_unsupported_symbol_of_size = invalid monomo codegen_ssa_invalid_no_sanitize = invalid argument for `no_sanitize` .note = expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread` +codegen_ssa_invalid_sanitize = invalid argument for `sanitize` + .note = expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread` + codegen_ssa_invalid_windows_subsystem = invalid windows subsystem `{$subsystem}`, only `windows` and `console` are allowed codegen_ssa_ld64_unimplemented_modifier = `as-needed` modifier not implemented yet for ld64 diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index af70f0deb07..068559497dd 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -162,6 +162,7 @@ fn parse_patchable_function_entry( struct InterestingAttributeDiagnosticSpans { link_ordinal: Option, no_sanitize: Option, + sanitize: Option, inline: Option, no_mangle: Option, } @@ -335,6 +336,7 @@ fn process_builtin_attrs( codegen_fn_attrs.no_sanitize |= parse_no_sanitize_attr(tcx, attr).unwrap_or_default(); } + sym::sanitize => interesting_spans.sanitize = Some(attr.span()), sym::instruction_set => { codegen_fn_attrs.instruction_set = parse_instruction_set_attr(tcx, attr) } @@ -358,6 +360,8 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.sess.opts.unstable_opts.min_function_alignment); + // Compute the disabled sanitizers. + codegen_fn_attrs.no_sanitize |= tcx.disabled_sanitizers_for(did); // On trait methods, inherit the `#[align]` of the trait's method prototype. codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.inherited_align(did)); @@ -463,6 +467,17 @@ fn check_result( lint.span_note(inline_span, "inlining requested here"); }) } + if !codegen_fn_attrs.no_sanitize.is_empty() + && codegen_fn_attrs.inline.always() + && let (Some(sanitize_span), Some(inline_span)) = + (interesting_spans.sanitize, interesting_spans.inline) + { + let hir_id = tcx.local_def_id_to_hir_id(did); + tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, sanitize_span, |lint| { + lint.primary_message("setting `sanitize` off will have no effect after inlining"); + lint.span_note(inline_span, "inlining requested here"); + }) + } // error when specifying link_name together with link_ordinal if let Some(_) = codegen_fn_attrs.link_name @@ -585,6 +600,84 @@ fn opt_trait_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option { } } +/// For an attr that has the `sanitize` attribute, read the list of +/// disabled sanitizers. +fn parse_sanitize_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> SanitizerSet { + let mut result = SanitizerSet::empty(); + if let Some(list) = attr.meta_item_list() { + for item in list.iter() { + let MetaItemInner::MetaItem(set) = item else { + tcx.dcx().emit_err(errors::InvalidSanitize { span: attr.span() }); + break; + }; + let segments = set.path.segments.iter().map(|x| x.ident.name).collect::>(); + match segments.as_slice() { + [sym::address] if set.value_str() == Some(sym::off) => { + result |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS + } + [sym::address] if set.value_str() == Some(sym::on) => { + result &= !SanitizerSet::ADDRESS; + result &= !SanitizerSet::KERNELADDRESS; + } + [sym::cfi] if set.value_str() == Some(sym::off) => result |= SanitizerSet::CFI, + [sym::cfi] if set.value_str() == Some(sym::on) => result &= !SanitizerSet::CFI, + [sym::kcfi] if set.value_str() == Some(sym::off) => result |= SanitizerSet::KCFI, + [sym::kcfi] if set.value_str() == Some(sym::on) => result &= !SanitizerSet::KCFI, + [sym::memory] if set.value_str() == Some(sym::off) => { + result |= SanitizerSet::MEMORY + } + [sym::memory] if set.value_str() == Some(sym::on) => { + result &= !SanitizerSet::MEMORY + } + [sym::memtag] if set.value_str() == Some(sym::off) => { + result |= SanitizerSet::MEMTAG + } + [sym::memtag] if set.value_str() == Some(sym::on) => { + result &= !SanitizerSet::MEMTAG + } + [sym::shadow_call_stack] if set.value_str() == Some(sym::off) => { + result |= SanitizerSet::SHADOWCALLSTACK + } + [sym::shadow_call_stack] if set.value_str() == Some(sym::on) => { + result &= !SanitizerSet::SHADOWCALLSTACK + } + [sym::thread] if set.value_str() == Some(sym::off) => { + result |= SanitizerSet::THREAD + } + [sym::thread] if set.value_str() == Some(sym::on) => { + result &= !SanitizerSet::THREAD + } + [sym::hwaddress] if set.value_str() == Some(sym::off) => { + result |= SanitizerSet::HWADDRESS + } + [sym::hwaddress] if set.value_str() == Some(sym::on) => { + result &= !SanitizerSet::HWADDRESS + } + _ => { + tcx.dcx().emit_err(errors::InvalidSanitize { span: attr.span() }); + } + } + } + } + result +} + +fn disabled_sanitizers_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerSet { + // Check for a sanitize annotation directly on this def. + if let Some(attr) = tcx.get_attr(did, sym::sanitize) { + return parse_sanitize_attr(tcx, attr); + } + + // Otherwise backtrack. + match tcx.opt_local_parent(did) { + // Check the parent (recursively). + Some(parent) => tcx.disabled_sanitizers_for(parent), + // We reached the crate root without seeing an attribute, so + // there is no sanitizers to exclude. + None => SanitizerSet::empty(), + } +} + /// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller /// applied to the method prototype. fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { @@ -709,6 +802,11 @@ pub fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { } pub(crate) fn provide(providers: &mut Providers) { - *providers = - Providers { codegen_fn_attrs, should_inherit_track_caller, inherited_align, ..*providers }; + *providers = Providers { + codegen_fn_attrs, + should_inherit_track_caller, + inherited_align, + disabled_sanitizers_for, + ..*providers + }; } diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 7ac830bcda9..913e0025f2e 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1128,6 +1128,14 @@ pub(crate) struct InvalidNoSanitize { pub span: Span, } +#[derive(Diagnostic)] +#[diag(codegen_ssa_invalid_sanitize)] +#[note] +pub(crate) struct InvalidSanitize { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(codegen_ssa_target_feature_safe_trait)] pub(crate) struct TargetFeatureSafeTrait { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 6fbedaf5b10..14fbf12f150 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -745,6 +745,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(List: &["address, kcfi, memory, thread"]), DuplicatesOk, EncodeCrossCrate::No, experimental!(no_sanitize) ), + gated!( + sanitize, Normal, template!(List: &[r#"address = "on|off""#, r#"kernel_address = "on|off""#, r#"cfi = "on|off""#, r#"hwaddress = "on|off""#, r#"kcfi = "on|off""#, r#"memory = "on|off""#, r#"memtag = "on|off""#, r#"shadow_call_stack = "on|off""#, r#"thread = "on|off""#]), ErrorPreceding, + EncodeCrossCrate::No, sanitize, experimental!(sanitize), + ), gated!( coverage, Normal, template!(OneOf: &[sym::off, sym::on]), ErrorPreceding, EncodeCrossCrate::No, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 07f928b8c88..4fb4b7fc8b7 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -626,6 +626,8 @@ declare_features! ( (unstable, return_type_notation, "1.70.0", Some(109417)), /// Allows `extern "rust-cold"`. (unstable, rust_cold_cc, "1.63.0", Some(97544)), + /// Allows the use of the `sanitize` attribute. + (unstable, sanitize, "CURRENT_RUSTC_VERSION", Some(39699)), /// Allows the use of SIMD types in functions declared in `extern` blocks. (unstable, simd_ffi, "1.0.0", Some(27731)), /// Allows specialization of implementations (RFC 1210). diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index a8b357bf105..ea62461ebeb 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -343,6 +343,7 @@ trivial! { rustc_span::Symbol, rustc_span::Ident, rustc_target::spec::PanicStrategy, + rustc_target::spec::SanitizerSet, rustc_type_ir::Variance, u32, usize, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index f4d0120a2e7..3bb8353f49e 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -100,7 +100,7 @@ use rustc_session::lint::LintExpectationId; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, Span, Symbol}; -use rustc_target::spec::PanicStrategy; +use rustc_target::spec::{PanicStrategy, SanitizerSet}; use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir}; use crate::infer::canonical::{self, Canonical}; @@ -2686,6 +2686,16 @@ rustc_queries! { desc { |tcx| "looking up anon const kind of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } + + /// Checks for the nearest `#[sanitize(xyz = "off")]` or + /// `#[sanitize(xyz = "on")]` on this def and any enclosing defs, up to the + /// crate root. + /// + /// Returns the set of sanitizers that is explicitly disabled for this def. + query disabled_sanitizers_for(key: LocalDefId) -> SanitizerSet { + desc { |tcx| "checking what set of sanitizers are enabled on `{}`", tcx.def_path_str(key) } + feedable + } } rustc_with_all_queries! { define_callbacks! } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 7481b0ea960..0e9d556afdd 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -542,6 +542,12 @@ passes_rustc_pub_transparent = attribute should be applied to `#[repr(transparent)]` types .label = not a `#[repr(transparent)]` type +passes_sanitize_attribute_not_allowed = + sanitize attribute not allowed here + .not_fn_impl_mod = not a function, impl block, or module + .no_body = function has no body + .help = sanitize attribute can be applied to a function (with body), impl block, or module + passes_should_be_applied_to_fn = attribute should be applied to a function definition .label = {$on_crate -> diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 0e28c51e981..e51f3657eaa 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -261,6 +261,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::no_sanitize, ..] => { self.check_no_sanitize(attr, span, target) } + [sym::sanitize, ..] => { + self.check_sanitize(attr, span, target) + } [sym::thread_local, ..] => self.check_thread_local(attr, span, target), [sym::doc, ..] => self.check_doc_attrs( attr, @@ -518,6 +521,46 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + /// Checks that the `#[sanitize(..)]` attribute is applied to a + /// function/closure/method, or to an impl block or module. + fn check_sanitize(&self, attr: &Attribute, target_span: Span, target: Target) { + let mut not_fn_impl_mod = None; + let mut no_body = None; + + if let Some(list) = attr.meta_item_list() { + for item in list.iter() { + let MetaItemInner::MetaItem(set) = item else { + return; + }; + let segments = set.path.segments.iter().map(|x| x.ident.name).collect::>(); + match target { + Target::Fn + | Target::Closure + | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) + | Target::Impl { .. } + | Target::Mod => return, + Target::Static if matches!(segments.as_slice(), [sym::address]) => return, + + // These are "functions", but they aren't allowed because they don't + // have a body, so the usual explanation would be confusing. + Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { + no_body = Some(target_span); + } + + _ => { + not_fn_impl_mod = Some(target_span); + } + } + } + self.dcx().emit_err(errors::SanitizeAttributeNotAllowed { + attr_span: attr.span(), + not_fn_impl_mod, + no_body, + help: (), + }); + } + } + /// Checks if `#[naked]` is applied to a function definition. fn check_naked(&self, hir_id: HirId, target: Target) { match target { @@ -561,7 +604,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } } - /// Checks if `#[collapse_debuginfo]` is applied to a macro. fn check_collapse_debuginfo(&self, attr: &Attribute, span: Span, target: Target) { match target { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index c5d5155d0e5..bd608dc2fee 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1499,6 +1499,23 @@ pub(crate) struct NoSanitize<'a> { pub attr_str: &'a str, } +/// "sanitize attribute not allowed here" +#[derive(Diagnostic)] +#[diag(passes_sanitize_attribute_not_allowed)] +pub(crate) struct SanitizeAttributeNotAllowed { + #[primary_span] + pub attr_span: Span, + /// "not a function, impl block, or module" + #[label(passes_not_fn_impl_mod)] + pub not_fn_impl_mod: Option, + /// "function has no body" + #[label(passes_no_body)] + pub no_body: Option, + /// "sanitize attribute can be applied to a function (with body), impl block, or module" + #[help] + pub help: (), +} + // FIXME(jdonszelmann): move back to rustc_attr #[derive(Diagnostic)] #[diag(passes_rustc_const_stable_indirect_pairing)] diff --git a/tests/codegen-llvm/sanitizer/sanitize-off.rs b/tests/codegen-llvm/sanitizer/sanitize-off.rs new file mode 100644 index 00000000000..0b0c01ed962 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/sanitize-off.rs @@ -0,0 +1,118 @@ +// Verifies that the `#[sanitize(address = "off")]` attribute can be used to +// selectively disable sanitizer instrumentation. +// +//@ needs-sanitizer-address +//@ compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static -Copt-level=0 + +#![crate_type = "lib"] +#![feature(sanitize)] + +// CHECK: @UNSANITIZED = constant{{.*}} no_sanitize_address +// CHECK-NOT: @__asan_global_SANITIZED +#[no_mangle] +#[sanitize(address = "off")] +pub static UNSANITIZED: u32 = 0; + +// CHECK: @__asan_global_SANITIZED +#[no_mangle] +pub static SANITIZED: u32 = 0; + +// CHECK-LABEL: ; sanitize_off::unsanitized +// CHECK-NEXT: ; Function Attrs: +// CHECK-NOT: sanitize_address +// CHECK: start: +// CHECK-NOT: call void @__asan_report_load +// CHECK: } +#[sanitize(address = "off")] +pub fn unsanitized(b: &mut u8) -> u8 { + *b +} + +// CHECK-LABEL: ; sanitize_off::sanitized +// CHECK-NEXT: ; Function Attrs: +// CHECK: sanitize_address +// CHECK: start: +// CHECK: call void @__asan_report_load +// CHECK: } +pub fn sanitized(b: &mut u8) -> u8 { + *b +} + +#[sanitize(address = "off")] +pub mod foo { + // CHECK-LABEL: ; sanitize_off::foo::unsanitized + // CHECK-NEXT: ; Function Attrs: + // CHECK-NOT: sanitize_address + // CHECK: start: + // CHECK-NOT: call void @__asan_report_load + // CHECK: } + pub fn unsanitized(b: &mut u8) -> u8 { + *b + } + + // CHECK-LABEL: ; sanitize_off::foo::sanitized + // CHECK-NEXT: ; Function Attrs: + // CHECK: sanitize_address + // CHECK: start: + // CHECK: call void @__asan_report_load + // CHECK: } + #[sanitize(address = "on")] + pub fn sanitized(b: &mut u8) -> u8 { + *b + } +} + +pub trait MyTrait { + fn unsanitized(&self, b: &mut u8) -> u8; + fn sanitized(&self, b: &mut u8) -> u8; + + // CHECK-LABEL: ; sanitize_off::MyTrait::unsanitized_default + // CHECK-NEXT: ; Function Attrs: + // CHECK-NOT: sanitize_address + // CHECK: start: + // CHECK-NOT: call void @__asan_report_load + // CHECK: } + #[sanitize(address = "off")] + fn unsanitized_default(&self, b: &mut u8) -> u8 { + *b + } + + // CHECK-LABEL: ; sanitize_off::MyTrait::sanitized_default + // CHECK-NEXT: ; Function Attrs: + // CHECK: sanitize_address + // CHECK: start: + // CHECK: call void @__asan_report_load + // CHECK: } + fn sanitized_default(&self, b: &mut u8) -> u8 { + *b + } +} + +#[sanitize(address = "off")] +impl MyTrait for () { + // CHECK-LABEL: ; <() as sanitize_off::MyTrait>::unsanitized + // CHECK-NEXT: ; Function Attrs: + // CHECK-NOT: sanitize_address + // CHECK: start: + // CHECK-NOT: call void @__asan_report_load + // CHECK: } + fn unsanitized(&self, b: &mut u8) -> u8 { + *b + } + + // CHECK-LABEL: ; <() as sanitize_off::MyTrait>::sanitized + // CHECK-NEXT: ; Function Attrs: + // CHECK: sanitize_address + // CHECK: start: + // CHECK: call void @__asan_report_load + // CHECK: } + #[sanitize(address = "on")] + fn sanitized(&self, b: &mut u8) -> u8 { + *b + } +} + +pub fn expose_trait(b: &mut u8) -> u8 { + <() as MyTrait>::unsanitized_default(&(), b); + <() as MyTrait>::sanitized_default(&(), b) +} diff --git a/tests/ui/feature-gates/feature-gate-sanitize.rs b/tests/ui/feature-gates/feature-gate-sanitize.rs new file mode 100644 index 00000000000..19656544da0 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-sanitize.rs @@ -0,0 +1,4 @@ +#[sanitize(address = "on")] +//~^ ERROR the `#[sanitize]` attribute is an experimental feature +fn main() { +} diff --git a/tests/ui/feature-gates/feature-gate-sanitize.stderr b/tests/ui/feature-gates/feature-gate-sanitize.stderr new file mode 100644 index 00000000000..a8e9b4608ac --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-sanitize.stderr @@ -0,0 +1,13 @@ +error[E0658]: the `#[sanitize]` attribute is an experimental feature + --> $DIR/feature-gate-sanitize.rs:1:1 + | +LL | #[sanitize(address = "on")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #39699 for more information + = help: add `#![feature(sanitize)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/sanitize-attr/invalid-sanitize.rs b/tests/ui/sanitize-attr/invalid-sanitize.rs new file mode 100644 index 00000000000..49dc01c8daa --- /dev/null +++ b/tests/ui/sanitize-attr/invalid-sanitize.rs @@ -0,0 +1,22 @@ +#![feature(sanitize)] + +#[sanitize(brontosaurus = "off")] //~ ERROR invalid argument +fn main() { +} + +#[sanitize(address = "off")] //~ ERROR multiple `sanitize` attributes +#[sanitize(address = "off")] +fn multiple_consistent() {} + +#[sanitize(address = "on")] //~ ERROR multiple `sanitize` attributes +#[sanitize(address = "off")] +fn multiple_inconsistent() {} + +#[sanitize(address = "bogus")] //~ ERROR invalid argument for `sanitize` +fn wrong_value() {} + +#[sanitize = "off"] //~ ERROR malformed `sanitize` attribute input +fn name_value () {} + +#[sanitize] //~ ERROR malformed `sanitize` attribute input +fn just_word() {} diff --git a/tests/ui/sanitize-attr/invalid-sanitize.stderr b/tests/ui/sanitize-attr/invalid-sanitize.stderr new file mode 100644 index 00000000000..bd36ce67b96 --- /dev/null +++ b/tests/ui/sanitize-attr/invalid-sanitize.stderr @@ -0,0 +1,82 @@ +error: malformed `sanitize` attribute input + --> $DIR/invalid-sanitize.rs:18:1 + | +LL | #[sanitize = "off"] + | ^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL - #[sanitize = "off"] +LL + #[sanitize(address = "on|off")] + | +LL - #[sanitize = "off"] +LL + #[sanitize(cfi = "on|off")] + | +LL - #[sanitize = "off"] +LL + #[sanitize(hwaddress = "on|off")] + | +LL - #[sanitize = "off"] +LL + #[sanitize(kcfi = "on|off")] + | + = and 5 other candidates + +error: malformed `sanitize` attribute input + --> $DIR/invalid-sanitize.rs:21:1 + | +LL | #[sanitize] + | ^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[sanitize(address = "on|off")] + | ++++++++++++++++++++ +LL | #[sanitize(cfi = "on|off")] + | ++++++++++++++++ +LL | #[sanitize(hwaddress = "on|off")] + | ++++++++++++++++++++++ +LL | #[sanitize(kcfi = "on|off")] + | +++++++++++++++++ + = and 5 other candidates + +error: multiple `sanitize` attributes + --> $DIR/invalid-sanitize.rs:7:1 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/invalid-sanitize.rs:8:1 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple `sanitize` attributes + --> $DIR/invalid-sanitize.rs:11:1 + | +LL | #[sanitize(address = "on")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/invalid-sanitize.rs:12:1 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: invalid argument for `sanitize` + --> $DIR/invalid-sanitize.rs:3:1 + | +LL | #[sanitize(brontosaurus = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread` + +error: invalid argument for `sanitize` + --> $DIR/invalid-sanitize.rs:15:1 + | +LL | #[sanitize(address = "bogus")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/sanitize-attr/valid-sanitize.rs b/tests/ui/sanitize-attr/valid-sanitize.rs new file mode 100644 index 00000000000..ebe76fcba04 --- /dev/null +++ b/tests/ui/sanitize-attr/valid-sanitize.rs @@ -0,0 +1,115 @@ +//! Tests where the `#[sanitize(..)]` attribute can and cannot be used. + +#![feature(sanitize)] +#![feature(extern_types)] +#![feature(impl_trait_in_assoc_type)] +#![warn(unused_attributes)] +#![sanitize(address = "off", thread = "on")] + +#[sanitize(address = "off", thread = "on")] +mod submod {} + +#[sanitize(address = "off")] +static FOO: u32 = 0; + +#[sanitize(thread = "off")] //~ ERROR sanitize attribute not allowed here +static BAR: u32 = 0; + +#[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here +type MyTypeAlias = (); + +#[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here +trait MyTrait { + #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here + const TRAIT_ASSOC_CONST: u32; + + #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here + type TraitAssocType; + + #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here + fn trait_method(&self); + + #[sanitize(address = "off", thread = "on")] + fn trait_method_with_default(&self) {} + + #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here + fn trait_assoc_fn(); +} + +#[sanitize(address = "off")] +impl MyTrait for () { + const TRAIT_ASSOC_CONST: u32 = 0; + + #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here + type TraitAssocType = Self; + + #[sanitize(address = "off", thread = "on")] + fn trait_method(&self) {} + #[sanitize(address = "off", thread = "on")] + fn trait_method_with_default(&self) {} + #[sanitize(address = "off", thread = "on")] + fn trait_assoc_fn() {} +} + +trait HasAssocType { + type T; + fn constrain_assoc_type() -> Self::T; +} + +impl HasAssocType for () { + #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here + type T = impl Copy; + fn constrain_assoc_type() -> Self::T {} +} + +#[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here +struct MyStruct { + #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here + field: u32, +} + +#[sanitize(address = "off", thread = "on")] +impl MyStruct { + #[sanitize(address = "off", thread = "on")] + fn method(&self) {} + #[sanitize(address = "off", thread = "on")] + fn assoc_fn() {} +} + +extern "C" { + #[sanitize(address = "off", thread = "on")] //~ ERROR sanitize attribute not allowed here + static X: u32; + + #[sanitize(address = "off", thread = "on")] //~ ERROR sanitize attribute not allowed here + type T; + + #[sanitize(address = "off", thread = "on")] //~ ERROR sanitize attribute not allowed here + fn foreign_fn(); +} + +#[sanitize(address = "off", thread = "on")] +fn main() { + #[sanitize(address = "off", thread = "on")] //~ ERROR sanitize attribute not allowed here + let _ = (); + + // Currently not allowed on let statements, even if they bind to a closure. + // It might be nice to support this as a special case someday, but trying + // to define the precise boundaries of that special case might be tricky. + #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here + let _let_closure = || (); + + // In situations where attributes can already be applied to expressions, + // the sanitize attribute is allowed on closure expressions. + let _closure_tail_expr = { + #[sanitize(address = "off", thread = "on")] + || () + }; + + match () { + #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here + () => (), + } + + #[sanitize(address = "off")] //~ ERROR sanitize attribute not allowed here + return (); +} diff --git a/tests/ui/sanitize-attr/valid-sanitize.stderr b/tests/ui/sanitize-attr/valid-sanitize.stderr new file mode 100644 index 00000000000..ff9fe63eaf5 --- /dev/null +++ b/tests/ui/sanitize-attr/valid-sanitize.stderr @@ -0,0 +1,190 @@ +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:15:1 + | +LL | #[sanitize(thread = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | static BAR: u32 = 0; + | -------------------- not a function, impl block, or module + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:18:1 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | type MyTypeAlias = (); + | ---------------------- not a function, impl block, or module + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:21:1 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | / trait MyTrait { +LL | | #[sanitize(address = "off")] +LL | | const TRAIT_ASSOC_CONST: u32; +... | +LL | | fn trait_assoc_fn(); +LL | | } + | |_- not a function, impl block, or module + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:65:1 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | / struct MyStruct { +LL | | #[sanitize(address = "off")] +LL | | field: u32, +LL | | } + | |_- not a function, impl block, or module + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:67:5 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | field: u32, + | ---------- not a function, impl block, or module + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:92:5 + | +LL | #[sanitize(address = "off", thread = "on")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = (); + | ----------- not a function, impl block, or module + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:98:5 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _let_closure = || (); + | ------------------------- not a function, impl block, or module + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:109:9 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | () => (), + | -------- not a function, impl block, or module + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:113:5 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | return (); + | --------- not a function, impl block, or module + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:23:5 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const TRAIT_ASSOC_CONST: u32; + | ----------------------------- not a function, impl block, or module + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:26:5 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | type TraitAssocType; + | -------------------- not a function, impl block, or module + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:29:5 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn trait_method(&self); + | ----------------------- function has no body + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:35:5 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn trait_assoc_fn(); + | -------------------- function has no body + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:43:5 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | type TraitAssocType = Self; + | --------------------------- not a function, impl block, or module + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:60:5 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | type T = impl Copy; + | ------------------- not a function, impl block, or module + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:80:5 + | +LL | #[sanitize(address = "off", thread = "on")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | static X: u32; + | -------------- not a function, impl block, or module + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:83:5 + | +LL | #[sanitize(address = "off", thread = "on")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | type T; + | ------- not a function, impl block, or module + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: sanitize attribute not allowed here + --> $DIR/valid-sanitize.rs:86:5 + | +LL | #[sanitize(address = "off", thread = "on")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn foreign_fn(); + | ---------------- function has no body + | + = help: sanitize attribute can be applied to a function (with body), impl block, or module + +error: aborting due to 18 previous errors + diff --git a/tests/ui/sanitizer/inline-always-sanitize.rs b/tests/ui/sanitizer/inline-always-sanitize.rs new file mode 100644 index 00000000000..2f1c8bb9c5b --- /dev/null +++ b/tests/ui/sanitizer/inline-always-sanitize.rs @@ -0,0 +1,15 @@ +//@ check-pass + +#![feature(sanitize)] + +#[inline(always)] +//~^ NOTE inlining requested here +#[sanitize(address = "off")] +//~^ WARN setting `sanitize` off will have no effect after inlining +//~| NOTE on by default +fn x() { +} + +fn main() { + x() +} diff --git a/tests/ui/sanitizer/inline-always-sanitize.stderr b/tests/ui/sanitizer/inline-always-sanitize.stderr new file mode 100644 index 00000000000..ed479472169 --- /dev/null +++ b/tests/ui/sanitizer/inline-always-sanitize.stderr @@ -0,0 +1,15 @@ +warning: setting `sanitize` off will have no effect after inlining + --> $DIR/inline-always-sanitize.rs:7:1 + | +LL | #[sanitize(address = "off")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: inlining requested here + --> $DIR/inline-always-sanitize.rs:5:1 + | +LL | #[inline(always)] + | ^^^^^^^^^^^^^^^^^ + = note: `#[warn(inline_no_sanitize)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/sanitizer/inline-always.rs b/tests/ui/sanitizer/inline-always.rs index d92daee3026..a868cd761db 100644 --- a/tests/ui/sanitizer/inline-always.rs +++ b/tests/ui/sanitizer/inline-always.rs @@ -1,7 +1,6 @@ //@ check-pass #![feature(no_sanitize)] - #[inline(always)] //~^ NOTE inlining requested here #[no_sanitize(address)] diff --git a/tests/ui/sanitizer/inline-always.stderr b/tests/ui/sanitizer/inline-always.stderr index 74fba3c0e0e..2ce48b0101d 100644 --- a/tests/ui/sanitizer/inline-always.stderr +++ b/tests/ui/sanitizer/inline-always.stderr @@ -1,11 +1,11 @@ warning: `no_sanitize` will have no effect after inlining - --> $DIR/inline-always.rs:7:1 + --> $DIR/inline-always.rs:6:1 | LL | #[no_sanitize(address)] | ^^^^^^^^^^^^^^^^^^^^^^^ | note: inlining requested here - --> $DIR/inline-always.rs:5:1 + --> $DIR/inline-always.rs:4:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 95bdb34494ad795f552cab7a0eb7bfd2e98ef033 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Wed, 18 Jun 2025 13:47:44 +0000 Subject: Remove the no_sanitize attribute in favor of sanitize This removes the #[no_sanitize] attribute, which was behind an unstable feature named no_sanitize. Instead, we introduce the sanitize attribute which is more powerful and allows to be extended in the future (instead of just focusing on turning sanitizers off). This also makes sanitize(kernel_address = ..) attribute work with -Zsanitize=address To do it the same as how clang disables address sanitizer, we now disable ASAN on sanitize(kernel_address = "off") and KASAN on sanitize(address = "off"). The same was added to clang in https://reviews.llvm.org/D44981. --- compiler/rustc_codegen_ssa/messages.ftl | 5 +- compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 78 ++++++--------------- compiler/rustc_codegen_ssa/src/errors.rs | 8 --- compiler/rustc_feature/src/builtin_attrs.rs | 5 -- compiler/rustc_feature/src/removed.rs | 3 + compiler/rustc_feature/src/unstable.rs | 2 - compiler/rustc_lint_defs/src/builtin.rs | 12 ++-- .../rustc_middle/src/middle/codegen_fn_attrs.rs | 4 +- compiler/rustc_passes/messages.ftl | 4 -- compiler/rustc_passes/src/check_attr.rs | 39 ----------- compiler/rustc_passes/src/errors.rs | 11 --- compiler/rustc_span/src/symbol.rs | 1 + src/doc/rustc-dev-guide/src/sanitizers.md | 2 +- .../src/language-features/no-sanitize.md | 29 -------- .../src/language-features/sanitize.md | 73 ++++++++++++++++++++ .../cfi/emit-type-checks-attr-no-sanitize.rs | 18 ----- .../cfi/emit-type-checks-attr-sanitize-off.rs | 18 +++++ .../sanitizer/kasan-emits-instrumentation.rs | 4 +- .../emit-kcfi-operand-bundle-attr-no-sanitize.rs | 27 -------- .../emit-kcfi-operand-bundle-attr-sanitize-off.rs | 27 ++++++++ .../codegen-llvm/sanitizer/no-sanitize-inlining.rs | 31 --------- tests/codegen-llvm/sanitizer/no-sanitize.rs | 39 ----------- .../sanitizer/sanitize-off-asan-kasan.rs | 42 ++++++++++++ .../sanitizer/sanitize-off-inlining.rs | 31 +++++++++ .../sanitizer/sanitize-off-kasan-asan.rs | 29 ++++++++ tests/codegen-llvm/sanitizer/sanitize-off.rs | 20 ++++++ tests/codegen-llvm/sanitizer/scs-attr-check.rs | 4 +- tests/mir-opt/inline/inline_compatibility.rs | 22 +++--- tests/ui/attributes/malformed-attrs.rs | 4 +- tests/ui/attributes/malformed-attrs.stderr | 18 ++++- tests/ui/attributes/no-sanitize.rs | 45 ------------ tests/ui/attributes/no-sanitize.stderr | 80 ---------------------- tests/ui/feature-gates/feature-gate-no_sanitize.rs | 4 -- .../feature-gates/feature-gate-no_sanitize.stderr | 13 ---- tests/ui/feature-gates/feature-gate-sanitize.rs | 3 + .../ui/feature-gates/feature-gate-sanitize.stderr | 16 ++++- tests/ui/invalid/invalid-no-sanitize.rs | 5 -- tests/ui/invalid/invalid-no-sanitize.stderr | 10 --- tests/ui/sanitize-attr/invalid-sanitize.stderr | 4 +- tests/ui/sanitizer/inline-always-sanitize.rs | 2 +- tests/ui/sanitizer/inline-always.rs | 14 ---- tests/ui/sanitizer/inline-always.stderr | 15 ---- triagebot.toml | 4 +- 43 files changed, 329 insertions(+), 496 deletions(-) delete mode 100644 src/doc/unstable-book/src/language-features/no-sanitize.md create mode 100644 src/doc/unstable-book/src/language-features/sanitize.md delete mode 100644 tests/codegen-llvm/sanitizer/cfi/emit-type-checks-attr-no-sanitize.rs create mode 100644 tests/codegen-llvm/sanitizer/cfi/emit-type-checks-attr-sanitize-off.rs delete mode 100644 tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-no-sanitize.rs create mode 100644 tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-sanitize-off.rs delete mode 100644 tests/codegen-llvm/sanitizer/no-sanitize-inlining.rs delete mode 100644 tests/codegen-llvm/sanitizer/no-sanitize.rs create mode 100644 tests/codegen-llvm/sanitizer/sanitize-off-asan-kasan.rs create mode 100644 tests/codegen-llvm/sanitizer/sanitize-off-inlining.rs create mode 100644 tests/codegen-llvm/sanitizer/sanitize-off-kasan-asan.rs delete mode 100644 tests/ui/attributes/no-sanitize.rs delete mode 100644 tests/ui/attributes/no-sanitize.stderr delete mode 100644 tests/ui/feature-gates/feature-gate-no_sanitize.rs delete mode 100644 tests/ui/feature-gates/feature-gate-no_sanitize.stderr delete mode 100644 tests/ui/invalid/invalid-no-sanitize.rs delete mode 100644 tests/ui/invalid/invalid-no-sanitize.stderr delete mode 100644 tests/ui/sanitizer/inline-always.rs delete mode 100644 tests/ui/sanitizer/inline-always.stderr (limited to 'tests/codegen-llvm') diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 75cc60587b8..42ba0154192 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -171,11 +171,8 @@ codegen_ssa_invalid_monomorphization_unsupported_symbol = invalid monomorphizati codegen_ssa_invalid_monomorphization_unsupported_symbol_of_size = invalid monomorphization of `{$name}` intrinsic: unsupported {$symbol} from `{$in_ty}` with element `{$in_elem}` of size `{$size}` to `{$ret_ty}` -codegen_ssa_invalid_no_sanitize = invalid argument for `no_sanitize` - .note = expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread` - codegen_ssa_invalid_sanitize = invalid argument for `sanitize` - .note = expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread` + .note = expected one of: `address`, `kernel_address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow_call_stack`, or `thread` codegen_ssa_invalid_windows_subsystem = invalid windows subsystem `{$subsystem}`, only `windows` and `console` are allowed diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 068559497dd..bf14c02a09c 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -77,32 +77,6 @@ fn parse_instruction_set_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option, attr: &Attribute) -> Option { - let list = attr.meta_item_list()?; - let mut sanitizer_set = SanitizerSet::empty(); - - for item in list.iter() { - match item.name() { - Some(sym::address) => { - sanitizer_set |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS - } - Some(sym::cfi) => sanitizer_set |= SanitizerSet::CFI, - Some(sym::kcfi) => sanitizer_set |= SanitizerSet::KCFI, - Some(sym::memory) => sanitizer_set |= SanitizerSet::MEMORY, - Some(sym::memtag) => sanitizer_set |= SanitizerSet::MEMTAG, - Some(sym::shadow_call_stack) => sanitizer_set |= SanitizerSet::SHADOWCALLSTACK, - Some(sym::thread) => sanitizer_set |= SanitizerSet::THREAD, - Some(sym::hwaddress) => sanitizer_set |= SanitizerSet::HWADDRESS, - _ => { - tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() }); - } - } - } - - Some(sanitizer_set) -} - // FIXME(jdonszelmann): remove when patchable_function_entry becomes a parsed attr fn parse_patchable_function_entry( tcx: TyCtxt<'_>, @@ -161,7 +135,6 @@ fn parse_patchable_function_entry( #[derive(Default)] struct InterestingAttributeDiagnosticSpans { link_ordinal: Option, - no_sanitize: Option, sanitize: Option, inline: Option, no_mangle: Option, @@ -331,11 +304,6 @@ fn process_builtin_attrs( codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED } sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL, - sym::no_sanitize => { - interesting_spans.no_sanitize = Some(attr.span()); - codegen_fn_attrs.no_sanitize |= - parse_no_sanitize_attr(tcx, attr).unwrap_or_default(); - } sym::sanitize => interesting_spans.sanitize = Some(attr.span()), sym::instruction_set => { codegen_fn_attrs.instruction_set = parse_instruction_set_attr(tcx, attr) @@ -459,21 +427,10 @@ fn check_result( if !codegen_fn_attrs.no_sanitize.is_empty() && codegen_fn_attrs.inline.always() && let (Some(no_sanitize_span), Some(inline_span)) = - (interesting_spans.no_sanitize, interesting_spans.inline) - { - let hir_id = tcx.local_def_id_to_hir_id(did); - tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| { - lint.primary_message("`no_sanitize` will have no effect after inlining"); - lint.span_note(inline_span, "inlining requested here"); - }) - } - if !codegen_fn_attrs.no_sanitize.is_empty() - && codegen_fn_attrs.inline.always() - && let (Some(sanitize_span), Some(inline_span)) = (interesting_spans.sanitize, interesting_spans.inline) { let hir_id = tcx.local_def_id_to_hir_id(did); - tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, sanitize_span, |lint| { + tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| { lint.primary_message("setting `sanitize` off will have no effect after inlining"); lint.span_note(inline_span, "inlining requested here"); }) @@ -601,9 +558,14 @@ fn opt_trait_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option { } /// For an attr that has the `sanitize` attribute, read the list of -/// disabled sanitizers. -fn parse_sanitize_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> SanitizerSet { - let mut result = SanitizerSet::empty(); +/// disabled sanitizers. `current_attr` holds the information about +/// previously parsed attributes. +fn parse_sanitize_attr( + tcx: TyCtxt<'_>, + attr: &Attribute, + current_attr: SanitizerSet, +) -> SanitizerSet { + let mut result = current_attr; if let Some(list) = attr.meta_item_list() { for item in list.iter() { let MetaItemInner::MetaItem(set) = item else { @@ -612,10 +574,13 @@ fn parse_sanitize_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> SanitizerSet { }; let segments = set.path.segments.iter().map(|x| x.ident.name).collect::>(); match segments.as_slice() { - [sym::address] if set.value_str() == Some(sym::off) => { + // Similar to clang, sanitize(address = ..) and + // sanitize(kernel_address = ..) control both ASan and KASan + // Source: https://reviews.llvm.org/D44981. + [sym::address] | [sym::kernel_address] if set.value_str() == Some(sym::off) => { result |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS } - [sym::address] if set.value_str() == Some(sym::on) => { + [sym::address] | [sym::kernel_address] if set.value_str() == Some(sym::on) => { result &= !SanitizerSet::ADDRESS; result &= !SanitizerSet::KERNELADDRESS; } @@ -663,19 +628,20 @@ fn parse_sanitize_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> SanitizerSet { } fn disabled_sanitizers_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerSet { - // Check for a sanitize annotation directly on this def. - if let Some(attr) = tcx.get_attr(did, sym::sanitize) { - return parse_sanitize_attr(tcx, attr); - } - - // Otherwise backtrack. - match tcx.opt_local_parent(did) { + // Backtrack to the crate root. + let disabled = match tcx.opt_local_parent(did) { // Check the parent (recursively). Some(parent) => tcx.disabled_sanitizers_for(parent), // We reached the crate root without seeing an attribute, so // there is no sanitizers to exclude. None => SanitizerSet::empty(), + }; + + // Check for a sanitize annotation directly on this def. + if let Some(attr) = tcx.get_attr(did, sym::sanitize) { + return parse_sanitize_attr(tcx, attr, disabled); } + disabled } /// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 913e0025f2e..209c78ddeda 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1120,14 +1120,6 @@ impl IntoDiagArg for ExpectedPointerMutability { } } -#[derive(Diagnostic)] -#[diag(codegen_ssa_invalid_no_sanitize)] -#[note] -pub(crate) struct InvalidNoSanitize { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(codegen_ssa_invalid_sanitize)] #[note] diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 14fbf12f150..9749028adb0 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -740,11 +740,6 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(List: &["set"], "https://doc.rust-lang.org/reference/attributes/codegen.html#the-instruction_set-attribute"), ErrorPreceding, EncodeCrossCrate::No ), - gated!( - no_sanitize, Normal, - template!(List: &["address, kcfi, memory, thread"]), DuplicatesOk, - EncodeCrossCrate::No, experimental!(no_sanitize) - ), gated!( sanitize, Normal, template!(List: &[r#"address = "on|off""#, r#"kernel_address = "on|off""#, r#"cfi = "on|off""#, r#"hwaddress = "on|off""#, r#"kcfi = "on|off""#, r#"memory = "on|off""#, r#"memtag = "on|off""#, r#"shadow_call_stack = "on|off""#, r#"thread = "on|off""#]), ErrorPreceding, EncodeCrossCrate::No, sanitize, experimental!(sanitize), diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 04f261ada06..e37fc6b7bfc 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -190,6 +190,9 @@ declare_features! ( (removed, no_coverage, "1.74.0", Some(84605), Some("renamed to `coverage_attribute`"), 114656), /// Allows `#[no_debug]`. (removed, no_debug, "1.43.0", Some(29721), Some("removed due to lack of demand"), 69667), + // Allows the use of `no_sanitize` attribute. + /// The feature was renamed to `sanitize` and the attribute to `#[sanitize(xyz = "on|off")]` + (removed, no_sanitize, "CURRENT_RUSTC_VERSION", Some(39699), Some(r#"renamed to sanitize(xyz = "on|off")"#), 142681), /// Note: this feature was previously recorded in a separate /// `STABLE_REMOVED` list because it, uniquely, was once stable but was /// then removed. But there was no utility storing it separately, so now diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 4fb4b7fc8b7..8444d1d7351 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -592,8 +592,6 @@ declare_features! ( (unstable, new_range, "1.86.0", Some(123741)), /// Allows `#![no_core]`. (unstable, no_core, "1.3.0", Some(29639)), - /// Allows the use of `no_sanitize` attribute. - (unstable, no_sanitize, "1.42.0", Some(39699)), /// Allows using the `non_exhaustive_omitted_patterns` lint. (unstable, non_exhaustive_omitted_patterns_lint, "1.57.0", Some(89554)), /// Allows `for` binders in where-clauses diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a0083a7df3d..97aa1065967 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2301,18 +2301,18 @@ declare_lint! { declare_lint! { /// The `inline_no_sanitize` lint detects incompatible use of - /// [`#[inline(always)]`][inline] and [`#[no_sanitize(...)]`][no_sanitize]. + /// [`#[inline(always)]`][inline] and [`#[sanitize(xyz = "off")]`][sanitize]. /// /// [inline]: https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute - /// [no_sanitize]: https://doc.rust-lang.org/nightly/unstable-book/language-features/no-sanitize.html + /// [sanitize]: https://doc.rust-lang.org/nightly/unstable-book/language-features/no-sanitize.html /// /// ### Example /// /// ```rust - /// #![feature(no_sanitize)] + /// #![feature(sanitize)] /// /// #[inline(always)] - /// #[no_sanitize(address)] + /// #[sanitize(address = "off")] /// fn x() {} /// /// fn main() { @@ -2325,11 +2325,11 @@ declare_lint! { /// ### Explanation /// /// The use of the [`#[inline(always)]`][inline] attribute prevents the - /// the [`#[no_sanitize(...)]`][no_sanitize] attribute from working. + /// the [`#[sanitize(xyz = "off")]`][sanitize] attribute from working. /// Consider temporarily removing `inline` attribute. pub INLINE_NO_SANITIZE, Warn, - "detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`", + r#"detects incompatible use of `#[inline(always)]` and `#[sanitize(... = "off")]`"#, } declare_lint! { diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 7d2fc0995aa..da17ac04c76 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -61,8 +61,8 @@ pub struct CodegenFnAttrs { /// The `#[link_section = "..."]` attribute, or what executable section this /// should be placed in. pub link_section: Option, - /// The `#[no_sanitize(...)]` attribute. Indicates sanitizers for which - /// instrumentation should be disabled inside the annotated function. + /// The `#[sanitize(xyz = "off")]` attribute. Indicates sanitizers for which + /// instrumentation should be disabled inside the function. pub no_sanitize: SanitizerSet, /// The `#[instruction_set(set)]` attribute. Indicates if the generated code should /// be generated against a specific instruction set. Only usable on architectures which allow diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 0e9d556afdd..57e55794f5f 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -444,10 +444,6 @@ passes_no_main_function = .teach_note = If you don't know the basics of Rust, you can go look to the Rust Book to get started: https://doc.rust-lang.org/book/ .non_function_main = non-function item at `crate::main` is found -passes_no_sanitize = - `#[no_sanitize({$attr_str})]` should be applied to {$accepted_kind} - .label = not {$accepted_kind} - passes_non_exhaustive_with_default_field_values = `#[non_exhaustive]` can't be used to annotate items with default field values .label = this struct has default field values diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index e51f3657eaa..e4c94650b8e 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -258,9 +258,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::diagnostic, sym::on_unimplemented, ..] => { self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) } - [sym::no_sanitize, ..] => { - self.check_no_sanitize(attr, span, target) - } [sym::sanitize, ..] => { self.check_sanitize(attr, span, target) } @@ -485,42 +482,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_no_sanitize(&self, attr: &Attribute, span: Span, target: Target) { - if let Some(list) = attr.meta_item_list() { - for item in list.iter() { - let sym = item.name(); - match sym { - Some(s @ sym::address | s @ sym::hwaddress) => { - let is_valid = - matches!(target, Target::Fn | Target::Method(..) | Target::Static); - if !is_valid { - self.dcx().emit_err(errors::NoSanitize { - attr_span: item.span(), - defn_span: span, - accepted_kind: "a function or static", - attr_str: s.as_str(), - }); - } - } - _ => { - let is_valid = matches!(target, Target::Fn | Target::Method(..)); - if !is_valid { - self.dcx().emit_err(errors::NoSanitize { - attr_span: item.span(), - defn_span: span, - accepted_kind: "a function", - attr_str: &match sym { - Some(name) => name.to_string(), - None => "...".to_string(), - }, - }); - } - } - } - } - } - } - /// Checks that the `#[sanitize(..)]` attribute is applied to a /// function/closure/method, or to an impl block or module. fn check_sanitize(&self, attr: &Attribute, target_span: Span, target: Target) { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index bd608dc2fee..c54b7d1d77c 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1488,17 +1488,6 @@ pub(crate) struct AttrCrateLevelOnlySugg { pub attr: Span, } -#[derive(Diagnostic)] -#[diag(passes_no_sanitize)] -pub(crate) struct NoSanitize<'a> { - #[primary_span] - pub attr_span: Span, - #[label] - pub defn_span: Span, - pub accepted_kind: &'a str, - pub attr_str: &'a str, -} - /// "sanitize attribute not allowed here" #[derive(Diagnostic)] #[diag(passes_sanitize_attribute_not_allowed)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f4a6d0f5891..8522338be27 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1257,6 +1257,7 @@ symbols! { iterator, iterator_collect_fn, kcfi, + kernel_address, keylocker_x86, keyword, kind, diff --git a/src/doc/rustc-dev-guide/src/sanitizers.md b/src/doc/rustc-dev-guide/src/sanitizers.md index 29d9056c15d..34c78d4d952 100644 --- a/src/doc/rustc-dev-guide/src/sanitizers.md +++ b/src/doc/rustc-dev-guide/src/sanitizers.md @@ -45,7 +45,7 @@ implementation: [marked][sanitizer-attribute] with appropriate LLVM attribute: `SanitizeAddress`, `SanitizeHWAddress`, `SanitizeMemory`, or `SanitizeThread`. By default all functions are instrumented, but this - behaviour can be changed with `#[no_sanitize(...)]`. + behaviour can be changed with `#[sanitize(xyz = "on|off")]`. * The decision whether to perform instrumentation or not is possible only at a function granularity. In the cases were those decision differ between diff --git a/src/doc/unstable-book/src/language-features/no-sanitize.md b/src/doc/unstable-book/src/language-features/no-sanitize.md deleted file mode 100644 index 28c683934d4..00000000000 --- a/src/doc/unstable-book/src/language-features/no-sanitize.md +++ /dev/null @@ -1,29 +0,0 @@ -# `no_sanitize` - -The tracking issue for this feature is: [#39699] - -[#39699]: https://github.com/rust-lang/rust/issues/39699 - ------------------------- - -The `no_sanitize` attribute can be used to selectively disable sanitizer -instrumentation in an annotated function. This might be useful to: avoid -instrumentation overhead in a performance critical function, or avoid -instrumenting code that contains constructs unsupported by given sanitizer. - -The precise effect of this annotation depends on particular sanitizer in use. -For example, with `no_sanitize(thread)`, the thread sanitizer will no longer -instrument non-atomic store / load operations, but it will instrument atomic -operations to avoid reporting false positives and provide meaning full stack -traces. - -## Examples - -``` rust -#![feature(no_sanitize)] - -#[no_sanitize(address)] -fn foo() { - // ... -} -``` diff --git a/src/doc/unstable-book/src/language-features/sanitize.md b/src/doc/unstable-book/src/language-features/sanitize.md new file mode 100644 index 00000000000..fcd099cb36e --- /dev/null +++ b/src/doc/unstable-book/src/language-features/sanitize.md @@ -0,0 +1,73 @@ +# `sanitize` + +The tracking issue for this feature is: [#39699] + +[#39699]: https://github.com/rust-lang/rust/issues/39699 + +------------------------ + +The `sanitize` attribute can be used to selectively disable or enable sanitizer +instrumentation in an annotated function. This might be useful to: avoid +instrumentation overhead in a performance critical function, or avoid +instrumenting code that contains constructs unsupported by given sanitizer. + +The precise effect of this annotation depends on particular sanitizer in use. +For example, with `sanitize(thread = "off")`, the thread sanitizer will no +longer instrument non-atomic store / load operations, but it will instrument +atomic operations to avoid reporting false positives and provide meaning full +stack traces. + +This attribute was previously named `no_sanitize`. + +## Examples + +``` rust +#![feature(sanitize)] + +#[sanitize(address = "off")] +fn foo() { + // ... +} +``` + +It is also possible to disable sanitizers for entire modules and enable them +for single items or functions. + +```rust +#![feature(sanitize)] + +#[sanitize(address = "off")] +mod foo { + fn unsanitized() { + // ... + } + + #[sanitize(address = "on")] + fn sanitized() { + // ... + } +} +``` + +It's also applicable to impl blocks. + +```rust +#![feature(sanitize)] + +trait MyTrait { + fn foo(&self); + fn bar(&self); +} + +#[sanitize(address = "off")] +impl MyTrait for () { + fn foo(&self) { + // ... + } + + #[sanitize(address = "on")] + fn bar(&self) { + // ... + } +} +``` diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-attr-no-sanitize.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-attr-no-sanitize.rs deleted file mode 100644 index 71ccdc8ca62..00000000000 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-attr-no-sanitize.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Verifies that pointer type membership tests for indirect calls are omitted. -// -//@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 - -#![crate_type = "lib"] -#![feature(no_sanitize)] - -#[no_sanitize(cfi)] -pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { - // CHECK-LABEL: emit_type_checks_attr_no_sanitize::foo - // CHECK: Function Attrs: {{.*}} - // CHECK-LABEL: define{{.*}}foo{{.*}}!type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} - // CHECK: start: - // CHECK-NEXT: {{%.+}} = call i32 %f(i32{{.*}} %arg) - // CHECK-NEXT: ret i32 {{%.+}} - f(arg) -} diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-attr-sanitize-off.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-attr-sanitize-off.rs new file mode 100644 index 00000000000..651afb33228 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-attr-sanitize-off.rs @@ -0,0 +1,18 @@ +// Verifies that pointer type membership tests for indirect calls are omitted. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 + +#![crate_type = "lib"] +#![feature(sanitize)] + +#[sanitize(cfi = "off")] +pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { + // CHECK-LABEL: emit_type_checks_attr_sanitize_off::foo + // CHECK: Function Attrs: {{.*}} + // CHECK-LABEL: define{{.*}}foo{{.*}}!type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + // CHECK: start: + // CHECK-NEXT: {{%.+}} = call i32 %f(i32{{.*}} %arg) + // CHECK-NEXT: ret i32 {{%.+}} + f(arg) +} diff --git a/tests/codegen-llvm/sanitizer/kasan-emits-instrumentation.rs b/tests/codegen-llvm/sanitizer/kasan-emits-instrumentation.rs index 774c9ab53f1..c70aae1703e 100644 --- a/tests/codegen-llvm/sanitizer/kasan-emits-instrumentation.rs +++ b/tests/codegen-llvm/sanitizer/kasan-emits-instrumentation.rs @@ -13,7 +13,7 @@ //@[x86_64] needs-llvm-components: x86 #![crate_type = "rlib"] -#![feature(no_core, no_sanitize, lang_items)] +#![feature(no_core, sanitize, lang_items)] #![no_core] extern crate minicore; @@ -25,7 +25,7 @@ use minicore::*; // CHECK: start: // CHECK-NOT: call void @__asan_report_load // CHECK: } -#[no_sanitize(address)] +#[sanitize(address = "off")] pub fn unsanitized(b: &mut u8) -> u8 { *b } diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-no-sanitize.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-no-sanitize.rs deleted file mode 100644 index 02c31fb8e9b..00000000000 --- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-no-sanitize.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Verifies that KCFI operand bundles are omitted. -// -//@ add-core-stubs -//@ revisions: aarch64 x86_64 -//@ [aarch64] compile-flags: --target aarch64-unknown-none -//@ [aarch64] needs-llvm-components: aarch64 -//@ [x86_64] compile-flags: --target x86_64-unknown-none -//@ [x86_64] needs-llvm-components: x86 -//@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 - -#![crate_type = "lib"] -#![feature(no_core, no_sanitize, lang_items)] -#![no_core] - -extern crate minicore; -use minicore::*; - -#[no_sanitize(kcfi)] -pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { - // CHECK-LABEL: emit_kcfi_operand_bundle_attr_no_sanitize::foo - // CHECK: Function Attrs: {{.*}} - // CHECK-LABEL: define{{.*}}foo{{.*}}!{{|kcfi_type}} !{{[0-9]+}} - // CHECK: start: - // CHECK-NOT: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg){{.*}}[ "kcfi"(i32 {{[-0-9]+}}) ] - // CHECK: ret i32 {{%.+}} - f(arg) -} diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-sanitize-off.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-sanitize-off.rs new file mode 100644 index 00000000000..2581784ce3e --- /dev/null +++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-sanitize-off.rs @@ -0,0 +1,27 @@ +// Verifies that KCFI operand bundles are omitted. +// +//@ add-core-stubs +//@ revisions: aarch64 x86_64 +//@ [aarch64] compile-flags: --target aarch64-unknown-none +//@ [aarch64] needs-llvm-components: aarch64 +//@ [x86_64] compile-flags: --target x86_64-unknown-none +//@ [x86_64] needs-llvm-components: x86 +//@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 + +#![crate_type = "lib"] +#![feature(no_core, sanitize, lang_items)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[sanitize(kcfi = "off")] +pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { + // CHECK-LABEL: emit_kcfi_operand_bundle_attr_sanitize_off::foo + // CHECK: Function Attrs: {{.*}} + // CHECK-LABEL: define{{.*}}foo{{.*}}!{{|kcfi_type}} !{{[0-9]+}} + // CHECK: start: + // CHECK-NOT: {{%.+}} = call {{(noundef )*}}i32 %f(i32 {{(noundef )*}}%arg){{.*}}[ "kcfi"(i32 {{[-0-9]+}}) ] + // CHECK: ret i32 {{%.+}} + f(arg) +} diff --git a/tests/codegen-llvm/sanitizer/no-sanitize-inlining.rs b/tests/codegen-llvm/sanitizer/no-sanitize-inlining.rs deleted file mode 100644 index 4bd832d2ab1..00000000000 --- a/tests/codegen-llvm/sanitizer/no-sanitize-inlining.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Verifies that no_sanitize attribute prevents inlining when -// given sanitizer is enabled, but has no effect on inlining otherwise. -// -//@ needs-sanitizer-address -//@ needs-sanitizer-leak -//@ revisions: ASAN LSAN -//@ compile-flags: -Copt-level=3 -Zmir-opt-level=4 -Ctarget-feature=-crt-static -//@[ASAN] compile-flags: -Zsanitizer=address -//@[LSAN] compile-flags: -Zsanitizer=leak - -#![crate_type = "lib"] -#![feature(no_sanitize)] - -// ASAN-LABEL: define void @test -// ASAN: call {{.*}} @random_inline -// ASAN: } -// -// LSAN-LABEL: define void @test -// LSAN-NOT: call -// LSAN: } -#[no_mangle] -pub fn test(n: &mut u32) { - random_inline(n); -} - -#[no_sanitize(address)] -#[inline] -#[no_mangle] -pub fn random_inline(n: &mut u32) { - *n = 42; -} diff --git a/tests/codegen-llvm/sanitizer/no-sanitize.rs b/tests/codegen-llvm/sanitizer/no-sanitize.rs deleted file mode 100644 index 2a309f6b9c6..00000000000 --- a/tests/codegen-llvm/sanitizer/no-sanitize.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Verifies that no_sanitize attribute can be used to -// selectively disable sanitizer instrumentation. -// -//@ needs-sanitizer-address -//@ compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static -Copt-level=0 - -#![crate_type = "lib"] -#![feature(no_sanitize)] - -// CHECK: @UNSANITIZED = constant{{.*}} no_sanitize_address -// CHECK-NOT: @__asan_global_UNSANITIZED -#[no_mangle] -#[no_sanitize(address)] -pub static UNSANITIZED: u32 = 0; - -// CHECK: @__asan_global_SANITIZED -#[no_mangle] -pub static SANITIZED: u32 = 0; - -// CHECK-LABEL: ; no_sanitize::unsanitized -// CHECK-NEXT: ; Function Attrs: -// CHECK-NOT: sanitize_address -// CHECK: start: -// CHECK-NOT: call void @__asan_report_load -// CHECK: } -#[no_sanitize(address)] -pub fn unsanitized(b: &mut u8) -> u8 { - *b -} - -// CHECK-LABEL: ; no_sanitize::sanitized -// CHECK-NEXT: ; Function Attrs: -// CHECK: sanitize_address -// CHECK: start: -// CHECK: call void @__asan_report_load -// CHECK: } -pub fn sanitized(b: &mut u8) -> u8 { - *b -} diff --git a/tests/codegen-llvm/sanitizer/sanitize-off-asan-kasan.rs b/tests/codegen-llvm/sanitizer/sanitize-off-asan-kasan.rs new file mode 100644 index 00000000000..37549aba447 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/sanitize-off-asan-kasan.rs @@ -0,0 +1,42 @@ +// Verifies that the `#[sanitize(address = "off")]` attribute also turns off +// the kernel address sanitizer. +// +//@ add-core-stubs +//@ compile-flags: -Zsanitizer=kernel-address -Ctarget-feature=-crt-static -Copt-level=0 +//@ revisions: aarch64 riscv64imac riscv64gc x86_64 +//@[aarch64] compile-flags: --target aarch64-unknown-none +//@[aarch64] needs-llvm-components: aarch64 +//@[riscv64imac] compile-flags: --target riscv64imac-unknown-none-elf +//@[riscv64imac] needs-llvm-components: riscv +//@[riscv64gc] compile-flags: --target riscv64gc-unknown-none-elf +//@[riscv64gc] needs-llvm-components: riscv +//@[x86_64] compile-flags: --target x86_64-unknown-none +//@[x86_64] needs-llvm-components: x86 + +#![crate_type = "rlib"] +#![feature(no_core, sanitize, lang_items)] +#![no_core] + +extern crate minicore; +use minicore::*; + +// CHECK-LABEL: ; sanitize_off_asan_kasan::unsanitized +// CHECK-NEXT: ; Function Attrs: +// CHECK-NOT: sanitize_address +// CHECK: start: +// CHECK-NOT: call void @__asan_report_load +// CHECK: } +#[sanitize(address = "off")] +pub fn unsanitized(b: &mut u8) -> u8 { + *b +} + +// CHECK-LABEL: ; sanitize_off_asan_kasan::sanitized +// CHECK-NEXT: ; Function Attrs: +// CHECK: sanitize_address +// CHECK: start: +// CHECK: call void @__asan_report_load +// CHECK: } +pub fn sanitized(b: &mut u8) -> u8 { + *b +} diff --git a/tests/codegen-llvm/sanitizer/sanitize-off-inlining.rs b/tests/codegen-llvm/sanitizer/sanitize-off-inlining.rs new file mode 100644 index 00000000000..69771827c3a --- /dev/null +++ b/tests/codegen-llvm/sanitizer/sanitize-off-inlining.rs @@ -0,0 +1,31 @@ +// Verifies that sanitize(xyz = "off") attribute prevents inlining when +// given sanitizer is enabled, but has no effect on inlining otherwise. +// +//@ needs-sanitizer-address +//@ needs-sanitizer-leak +//@ revisions: ASAN LSAN +//@ compile-flags: -Copt-level=3 -Zmir-opt-level=4 -Ctarget-feature=-crt-static +//@[ASAN] compile-flags: -Zsanitizer=address +//@[LSAN] compile-flags: -Zsanitizer=leak + +#![crate_type = "lib"] +#![feature(sanitize)] + +// ASAN-LABEL: define void @test +// ASAN: call {{.*}} @random_inline +// ASAN: } +// +// LSAN-LABEL: define void @test +// LSAN-NOT: call +// LSAN: } +#[no_mangle] +pub fn test(n: &mut u32) { + random_inline(n); +} + +#[sanitize(address = "off")] +#[inline] +#[no_mangle] +pub fn random_inline(n: &mut u32) { + *n = 42; +} diff --git a/tests/codegen-llvm/sanitizer/sanitize-off-kasan-asan.rs b/tests/codegen-llvm/sanitizer/sanitize-off-kasan-asan.rs new file mode 100644 index 00000000000..94945f2b2e6 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/sanitize-off-kasan-asan.rs @@ -0,0 +1,29 @@ +// Verifies that the `#[sanitize(kernel_address = "off")]` attribute also turns off +// the address sanitizer. +// +//@ needs-sanitizer-address +//@ compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static -Copt-level=0 + +#![crate_type = "lib"] +#![feature(sanitize)] + +// CHECK-LABEL: ; sanitize_off_kasan_asan::unsanitized +// CHECK-NEXT: ; Function Attrs: +// CHECK-NOT: sanitize_address +// CHECK: start: +// CHECK-NOT: call void @__asan_report_load +// CHECK: } +#[sanitize(kernel_address = "off")] +pub fn unsanitized(b: &mut u8) -> u8 { + *b +} + +// CHECK-LABEL: ; sanitize_off_kasan_asan::sanitized +// CHECK-NEXT: ; Function Attrs: +// CHECK: sanitize_address +// CHECK: start: +// CHECK: call void @__asan_report_load +// CHECK: } +pub fn sanitized(b: &mut u8) -> u8 { + *b +} diff --git a/tests/codegen-llvm/sanitizer/sanitize-off.rs b/tests/codegen-llvm/sanitizer/sanitize-off.rs index 0b0c01ed962..9f3f7cd9df7 100644 --- a/tests/codegen-llvm/sanitizer/sanitize-off.rs +++ b/tests/codegen-llvm/sanitizer/sanitize-off.rs @@ -116,3 +116,23 @@ pub fn expose_trait(b: &mut u8) -> u8 { <() as MyTrait>::unsanitized_default(&(), b); <() as MyTrait>::sanitized_default(&(), b) } + +#[sanitize(address = "off")] +pub mod outer { + #[sanitize(thread = "off")] + pub mod inner { + // CHECK-LABEL: ; sanitize_off::outer::inner::unsanitized + // CHECK-NEXT: ; Function Attrs: + // CHECK-NOT: sanitize_address + // CHECK: start: + // CHECK-NOT: call void @__asan_report_load + // CHECK: } + pub fn unsanitized() { + let xs = [0, 1, 2, 3]; + // Avoid optimizing everything out. + let xs = std::hint::black_box(xs.as_ptr()); + let code = unsafe { *xs.offset(4) }; + std::process::exit(code); + } + } +} diff --git a/tests/codegen-llvm/sanitizer/scs-attr-check.rs b/tests/codegen-llvm/sanitizer/scs-attr-check.rs index 6f4cbc2c0a6..f726503503c 100644 --- a/tests/codegen-llvm/sanitizer/scs-attr-check.rs +++ b/tests/codegen-llvm/sanitizer/scs-attr-check.rs @@ -5,7 +5,7 @@ //@ compile-flags: -Zsanitizer=shadow-call-stack #![crate_type = "lib"] -#![feature(no_sanitize)] +#![feature(sanitize)] // CHECK: ; sanitizer_scs_attr_check::scs // CHECK-NEXT: ; Function Attrs:{{.*}}shadowcallstack @@ -13,5 +13,5 @@ pub fn scs() {} // CHECK: ; sanitizer_scs_attr_check::no_scs // CHECK-NOT: ; Function Attrs:{{.*}}shadowcallstack -#[no_sanitize(shadow_call_stack)] +#[sanitize(shadow_call_stack = "off")] pub fn no_scs() {} diff --git a/tests/mir-opt/inline/inline_compatibility.rs b/tests/mir-opt/inline/inline_compatibility.rs index 1bb102ccda5..a31153dedc9 100644 --- a/tests/mir-opt/inline/inline_compatibility.rs +++ b/tests/mir-opt/inline/inline_compatibility.rs @@ -3,7 +3,7 @@ //@ compile-flags: -Cpanic=abort #![crate_type = "lib"] -#![feature(no_sanitize)] +#![feature(sanitize)] #![feature(c_variadic)] #[inline] @@ -37,22 +37,22 @@ pub unsafe fn f2() { } #[inline] -#[no_sanitize(address)] -pub unsafe fn no_sanitize() {} +#[sanitize(address = "off")] +pub unsafe fn sanitize_off() {} -// CHECK-LABEL: fn inlined_no_sanitize() +// CHECK-LABEL: fn inlined_sanitize_off() // CHECK: bb0: { // CHECK-NEXT: return; -#[no_sanitize(address)] -pub unsafe fn inlined_no_sanitize() { - no_sanitize(); +#[sanitize(address = "off")] +pub unsafe fn inlined_sanitize_off() { + sanitize_off(); } -// CHECK-LABEL: fn not_inlined_no_sanitize() +// CHECK-LABEL: fn not_inlined_sanitize_off() // CHECK: bb0: { -// CHECK-NEXT: no_sanitize() -pub unsafe fn not_inlined_no_sanitize() { - no_sanitize(); +// CHECK-NEXT: sanitize_off() +pub unsafe fn not_inlined_sanitize_off() { + sanitize_off(); } // CHECK-LABEL: fn not_inlined_c_variadic() diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs index 3293f75fba9..90ca007451e 100644 --- a/tests/ui/attributes/malformed-attrs.rs +++ b/tests/ui/attributes/malformed-attrs.rs @@ -12,7 +12,7 @@ #![feature(min_generic_const_args)] #![feature(ffi_const, ffi_pure)] #![feature(coverage_attribute)] -#![feature(no_sanitize)] +#![feature(sanitize)] #![feature(marker_trait_attr)] #![feature(thread_local)] #![feature(must_not_suspend)] @@ -89,7 +89,7 @@ //~^ ERROR malformed #[coverage] //~^ ERROR malformed `coverage` attribute input -#[no_sanitize] +#[sanitize] //~^ ERROR malformed #[ignore()] //~^ ERROR valid forms for the attribute are diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 9c31765149b..d1d9fef2a40 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -49,11 +49,23 @@ LL | #[crate_name] | = note: for more information, visit -error: malformed `no_sanitize` attribute input +error: malformed `sanitize` attribute input --> $DIR/malformed-attrs.rs:92:1 | -LL | #[no_sanitize] - | ^^^^^^^^^^^^^^ help: must be of the form: `#[no_sanitize(address, kcfi, memory, thread)]` +LL | #[sanitize] + | ^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[sanitize(address = "on|off")] + | ++++++++++++++++++++ +LL | #[sanitize(cfi = "on|off")] + | ++++++++++++++++ +LL | #[sanitize(hwaddress = "on|off")] + | ++++++++++++++++++++++ +LL | #[sanitize(kcfi = "on|off")] + | +++++++++++++++++ + = and 5 other candidates error: malformed `instruction_set` attribute input --> $DIR/malformed-attrs.rs:106:1 diff --git a/tests/ui/attributes/no-sanitize.rs b/tests/ui/attributes/no-sanitize.rs deleted file mode 100644 index ddf909be63a..00000000000 --- a/tests/ui/attributes/no-sanitize.rs +++ /dev/null @@ -1,45 +0,0 @@ -#![feature(no_sanitize)] -#![feature(stmt_expr_attributes)] -#![deny(unused_attributes)] -#![allow(dead_code)] - -fn invalid() { - #[no_sanitize(memory)] //~ ERROR `#[no_sanitize(memory)]` should be applied to a function - { - 1 - }; -} - -#[no_sanitize(memory)] //~ ERROR `#[no_sanitize(memory)]` should be applied to a function -type InvalidTy = (); - -#[no_sanitize(memory)] //~ ERROR `#[no_sanitize(memory)]` should be applied to a function -mod invalid_module {} - -fn main() { - let _ = #[no_sanitize(memory)] //~ ERROR `#[no_sanitize(memory)]` should be applied to a function - (|| 1); -} - -#[no_sanitize(memory)] //~ ERROR `#[no_sanitize(memory)]` should be applied to a function -struct F; - -#[no_sanitize(memory)] //~ ERROR `#[no_sanitize(memory)]` should be applied to a function -impl F { - #[no_sanitize(memory)] - fn valid(&self) {} -} - -#[no_sanitize(address, memory)] //~ ERROR `#[no_sanitize(memory)]` should be applied to a function -static INVALID : i32 = 0; - -#[no_sanitize(memory)] -fn valid() {} - -#[no_sanitize(address)] -static VALID : i32 = 0; - -#[no_sanitize("address")] -//~^ ERROR `#[no_sanitize(...)]` should be applied to a function -//~| ERROR invalid argument for `no_sanitize` -static VALID2 : i32 = 0; diff --git a/tests/ui/attributes/no-sanitize.stderr b/tests/ui/attributes/no-sanitize.stderr deleted file mode 100644 index 8d5fbb109ea..00000000000 --- a/tests/ui/attributes/no-sanitize.stderr +++ /dev/null @@ -1,80 +0,0 @@ -error: `#[no_sanitize(memory)]` should be applied to a function - --> $DIR/no-sanitize.rs:7:19 - | -LL | #[no_sanitize(memory)] - | ^^^^^^ -LL | / { -LL | | 1 -LL | | }; - | |_____- not a function - -error: `#[no_sanitize(memory)]` should be applied to a function - --> $DIR/no-sanitize.rs:13:15 - | -LL | #[no_sanitize(memory)] - | ^^^^^^ -LL | type InvalidTy = (); - | -------------------- not a function - -error: `#[no_sanitize(memory)]` should be applied to a function - --> $DIR/no-sanitize.rs:16:15 - | -LL | #[no_sanitize(memory)] - | ^^^^^^ -LL | mod invalid_module {} - | --------------------- not a function - -error: `#[no_sanitize(memory)]` should be applied to a function - --> $DIR/no-sanitize.rs:20:27 - | -LL | let _ = #[no_sanitize(memory)] - | ^^^^^^ -LL | (|| 1); - | ------ not a function - -error: `#[no_sanitize(memory)]` should be applied to a function - --> $DIR/no-sanitize.rs:24:15 - | -LL | #[no_sanitize(memory)] - | ^^^^^^ -LL | struct F; - | --------- not a function - -error: `#[no_sanitize(memory)]` should be applied to a function - --> $DIR/no-sanitize.rs:27:15 - | -LL | #[no_sanitize(memory)] - | ^^^^^^ -LL | / impl F { -LL | | #[no_sanitize(memory)] -LL | | fn valid(&self) {} -LL | | } - | |_- not a function - -error: `#[no_sanitize(memory)]` should be applied to a function - --> $DIR/no-sanitize.rs:33:24 - | -LL | #[no_sanitize(address, memory)] - | ^^^^^^ -LL | static INVALID : i32 = 0; - | ------------------------- not a function - -error: `#[no_sanitize(...)]` should be applied to a function - --> $DIR/no-sanitize.rs:42:15 - | -LL | #[no_sanitize("address")] - | ^^^^^^^^^ -... -LL | static VALID2 : i32 = 0; - | ------------------------ not a function - -error: invalid argument for `no_sanitize` - --> $DIR/no-sanitize.rs:42:15 - | -LL | #[no_sanitize("address")] - | ^^^^^^^^^ - | - = note: expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread` - -error: aborting due to 9 previous errors - diff --git a/tests/ui/feature-gates/feature-gate-no_sanitize.rs b/tests/ui/feature-gates/feature-gate-no_sanitize.rs deleted file mode 100644 index 5ac014f1c5b..00000000000 --- a/tests/ui/feature-gates/feature-gate-no_sanitize.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[no_sanitize(address)] -//~^ ERROR the `#[no_sanitize]` attribute is an experimental feature -fn main() { -} diff --git a/tests/ui/feature-gates/feature-gate-no_sanitize.stderr b/tests/ui/feature-gates/feature-gate-no_sanitize.stderr deleted file mode 100644 index a33bf6a9e40..00000000000 --- a/tests/ui/feature-gates/feature-gate-no_sanitize.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0658]: the `#[no_sanitize]` attribute is an experimental feature - --> $DIR/feature-gate-no_sanitize.rs:1:1 - | -LL | #[no_sanitize(address)] - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #39699 for more information - = help: add `#![feature(no_sanitize)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-sanitize.rs b/tests/ui/feature-gates/feature-gate-sanitize.rs index 19656544da0..40098d93272 100644 --- a/tests/ui/feature-gates/feature-gate-sanitize.rs +++ b/tests/ui/feature-gates/feature-gate-sanitize.rs @@ -1,3 +1,6 @@ +//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION" +#![feature(no_sanitize)] //~ ERROR feature has been removed + #[sanitize(address = "on")] //~^ ERROR the `#[sanitize]` attribute is an experimental feature fn main() { diff --git a/tests/ui/feature-gates/feature-gate-sanitize.stderr b/tests/ui/feature-gates/feature-gate-sanitize.stderr index a8e9b4608ac..7c38b351916 100644 --- a/tests/ui/feature-gates/feature-gate-sanitize.stderr +++ b/tests/ui/feature-gates/feature-gate-sanitize.stderr @@ -1,5 +1,14 @@ +error[E0557]: feature has been removed + --> $DIR/feature-gate-sanitize.rs:2:12 + | +LL | #![feature(no_sanitize)] + | ^^^^^^^^^^^ feature has been removed + | + = note: removed in CURRENT_RUSTC_VERSION; see for more information + = note: renamed to sanitize(xyz = "on|off") + error[E0658]: the `#[sanitize]` attribute is an experimental feature - --> $DIR/feature-gate-sanitize.rs:1:1 + --> $DIR/feature-gate-sanitize.rs:4:1 | LL | #[sanitize(address = "on")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,6 +17,7 @@ LL | #[sanitize(address = "on")] = help: add `#![feature(sanitize)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0557, E0658. +For more information about an error, try `rustc --explain E0557`. diff --git a/tests/ui/invalid/invalid-no-sanitize.rs b/tests/ui/invalid/invalid-no-sanitize.rs deleted file mode 100644 index b52e3cc83fa..00000000000 --- a/tests/ui/invalid/invalid-no-sanitize.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![feature(no_sanitize)] - -#[no_sanitize(brontosaurus)] //~ ERROR invalid argument -fn main() { -} diff --git a/tests/ui/invalid/invalid-no-sanitize.stderr b/tests/ui/invalid/invalid-no-sanitize.stderr deleted file mode 100644 index b1c80438b31..00000000000 --- a/tests/ui/invalid/invalid-no-sanitize.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: invalid argument for `no_sanitize` - --> $DIR/invalid-no-sanitize.rs:3:15 - | -LL | #[no_sanitize(brontosaurus)] - | ^^^^^^^^^^^^ - | - = note: expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread` - -error: aborting due to 1 previous error - diff --git a/tests/ui/sanitize-attr/invalid-sanitize.stderr b/tests/ui/sanitize-attr/invalid-sanitize.stderr index bd36ce67b96..4bf81770b89 100644 --- a/tests/ui/sanitize-attr/invalid-sanitize.stderr +++ b/tests/ui/sanitize-attr/invalid-sanitize.stderr @@ -68,7 +68,7 @@ error: invalid argument for `sanitize` LL | #[sanitize(brontosaurus = "off")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread` + = note: expected one of: `address`, `kernel_address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow_call_stack`, or `thread` error: invalid argument for `sanitize` --> $DIR/invalid-sanitize.rs:15:1 @@ -76,7 +76,7 @@ error: invalid argument for `sanitize` LL | #[sanitize(address = "bogus")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread` + = note: expected one of: `address`, `kernel_address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow_call_stack`, or `thread` error: aborting due to 6 previous errors diff --git a/tests/ui/sanitizer/inline-always-sanitize.rs b/tests/ui/sanitizer/inline-always-sanitize.rs index 2f1c8bb9c5b..d6ee214e9b3 100644 --- a/tests/ui/sanitizer/inline-always-sanitize.rs +++ b/tests/ui/sanitizer/inline-always-sanitize.rs @@ -5,7 +5,7 @@ #[inline(always)] //~^ NOTE inlining requested here #[sanitize(address = "off")] -//~^ WARN setting `sanitize` off will have no effect after inlining +//~^ WARN setting `sanitize` off will have no effect after inlining //~| NOTE on by default fn x() { } diff --git a/tests/ui/sanitizer/inline-always.rs b/tests/ui/sanitizer/inline-always.rs deleted file mode 100644 index a868cd761db..00000000000 --- a/tests/ui/sanitizer/inline-always.rs +++ /dev/null @@ -1,14 +0,0 @@ -//@ check-pass - -#![feature(no_sanitize)] -#[inline(always)] -//~^ NOTE inlining requested here -#[no_sanitize(address)] -//~^ WARN will have no effect after inlining -//~| NOTE on by default -fn x() { -} - -fn main() { - x() -} diff --git a/tests/ui/sanitizer/inline-always.stderr b/tests/ui/sanitizer/inline-always.stderr deleted file mode 100644 index 2ce48b0101d..00000000000 --- a/tests/ui/sanitizer/inline-always.stderr +++ /dev/null @@ -1,15 +0,0 @@ -warning: `no_sanitize` will have no effect after inlining - --> $DIR/inline-always.rs:6:1 - | -LL | #[no_sanitize(address)] - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -note: inlining requested here - --> $DIR/inline-always.rs:4:1 - | -LL | #[inline(always)] - | ^^^^^^^^^^^^^^^^^ - = note: `#[warn(inline_no_sanitize)]` on by default - -warning: 1 warning emitted - diff --git a/triagebot.toml b/triagebot.toml index 2f31a30019b..777ef928e5d 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -579,7 +579,7 @@ trigger_files = [ "src/doc/unstable-book/src/compiler-flags/sanitizer.md", "src/doc/unstable-book/src/language-features/cfg-sanitize.md", "src/doc/unstable-book/src/language-features/cfi-encoding.md", - "src/doc/unstable-book/src/language-features/no-sanitize.md", + "src/doc/unstable-book/src/language-features/sanitize.md", "tests/codegen-llvm/sanitizer", "tests/codegen-llvm/split-lto-unit.rs", "tests/codegen-llvm/stack-probes-inline.rs", @@ -1209,7 +1209,7 @@ cc = ["@rust-lang/project-exploit-mitigations", "@rcvalle"] [mentions."src/doc/unstable-book/src/language-features/cfi-encoding.md"] cc = ["@rust-lang/project-exploit-mitigations", "@rcvalle"] -[mentions."src/doc/unstable-book/src/language-features/no-sanitize.md"] +[mentions."src/doc/unstable-book/src/language-features/sanitize.md"] cc = ["@rust-lang/project-exploit-mitigations", "@rcvalle"] [mentions."src/doc/rustc/src/check-cfg.md"] -- cgit 1.4.1-3-g733a5 From 9d08596a2e315cdaa13a4c98e30c8b14853a9e9a Mon Sep 17 00:00:00 2001 From: Caiweiran Date: Mon, 28 Jul 2025 16:46:25 +0000 Subject: tests: fix RISC-V failures and adjust transmute-scalar.rs target Resolve several ./x test failures on RISC-V caused by ABI and codegen differences. Update multiple codegen-llvm tests for compatibility, and explicitly set the target for transmute-scalar.rs to x86_64 to ensure consistent behavior across hosts. --- tests/codegen-llvm/enum/enum-aggregate.rs | 8 ++++---- tests/codegen-llvm/enum/enum-match.rs | 2 +- tests/codegen-llvm/enum/enum-transparent-extract.rs | 2 +- tests/codegen-llvm/repeat-operand-zero-len.rs | 4 ++-- tests/codegen-llvm/transmute-scalar.rs | 13 +++---------- tests/codegen-llvm/uninhabited-transparent-return-abi.rs | 2 +- 6 files changed, 12 insertions(+), 19 deletions(-) (limited to 'tests/codegen-llvm') diff --git a/tests/codegen-llvm/enum/enum-aggregate.rs b/tests/codegen-llvm/enum/enum-aggregate.rs index 0161e5f3fa1..f58d7ef12b6 100644 --- a/tests/codegen-llvm/enum/enum-aggregate.rs +++ b/tests/codegen-llvm/enum/enum-aggregate.rs @@ -27,7 +27,7 @@ fn make_none_bool() -> Option { #[no_mangle] fn make_some_ordering(x: Ordering) -> Option { - // CHECK-LABEL: i8 @make_some_ordering(i8 %x) + // CHECK-LABEL: i8 @make_some_ordering(i8{{( signext)?}} %x) // CHECK-NEXT: start: // CHECK-NEXT: ret i8 %x Some(x) @@ -35,7 +35,7 @@ fn make_some_ordering(x: Ordering) -> Option { #[no_mangle] fn make_some_u16(x: u16) -> Option { - // CHECK-LABEL: { i16, i16 } @make_some_u16(i16 %x) + // CHECK-LABEL: { i16, i16 } @make_some_u16(i16{{( zeroext)?}} %x) // CHECK-NEXT: start: // CHECK-NEXT: %0 = insertvalue { i16, i16 } { i16 1, i16 poison }, i16 %x, 1 // CHECK-NEXT: ret { i16, i16 } %0 @@ -52,7 +52,7 @@ fn make_none_u16() -> Option { #[no_mangle] fn make_some_nzu32(x: NonZero) -> Option> { - // CHECK-LABEL: i32 @make_some_nzu32(i32 %x) + // CHECK-LABEL: i32 @make_some_nzu32(i32{{( signext)?}} %x) // CHECK-NEXT: start: // CHECK-NEXT: ret i32 %x Some(x) @@ -114,7 +114,7 @@ fn make_uninhabited_err_indirectly(n: Never) -> Result { fn make_fully_uninhabited_result(v: u32, n: Never) -> Result<(u32, Never), (Never, u32)> { // Actually reaching this would be UB, so we don't actually build a result. - // CHECK-LABEL: { i32, i32 } @make_fully_uninhabited_result(i32 %v) + // CHECK-LABEL: { i32, i32 } @make_fully_uninhabited_result(i32{{( signext)?}} %v) // CHECK-NEXT: start: // CHECK-NEXT: call void @llvm.trap() // CHECK-NEXT: call void @llvm.trap() diff --git a/tests/codegen-llvm/enum/enum-match.rs b/tests/codegen-llvm/enum/enum-match.rs index 091c4e9adf4..20e2006e3eb 100644 --- a/tests/codegen-llvm/enum/enum-match.rs +++ b/tests/codegen-llvm/enum/enum-match.rs @@ -739,7 +739,7 @@ pub enum Tricky { const _: () = assert!(std::intrinsics::discriminant_value(&Tricky::V100) == 100); -// CHECK-LABEL: define noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @discriminant6(i8 noundef %e) +// CHECK-LABEL: define noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @discriminant6(i8 noundef{{( zeroext)?}} %e) // CHECK-NEXT: start: // CHECK-NEXT: %[[REL_VAR:.+]] = add i8 %e, -66 // CHECK-NEXT: %[[IS_NICHE:.+]] = icmp ult i8 %[[REL_VAR]], -56 diff --git a/tests/codegen-llvm/enum/enum-transparent-extract.rs b/tests/codegen-llvm/enum/enum-transparent-extract.rs index c5efb8d472b..1435e6ec802 100644 --- a/tests/codegen-llvm/enum/enum-transparent-extract.rs +++ b/tests/codegen-llvm/enum/enum-transparent-extract.rs @@ -9,7 +9,7 @@ pub enum Never {} #[no_mangle] pub fn make_unmake_result_never(x: i32) -> i32 { - // CHECK-LABEL: define i32 @make_unmake_result_never(i32 %x) + // CHECK-LABEL: define i32 @make_unmake_result_never(i32{{( signext)?}} %x) // CHECK: start: // CHECK-NEXT: ret i32 %x diff --git a/tests/codegen-llvm/repeat-operand-zero-len.rs b/tests/codegen-llvm/repeat-operand-zero-len.rs index b4cec42a07c..8d2a0e77d60 100644 --- a/tests/codegen-llvm/repeat-operand-zero-len.rs +++ b/tests/codegen-llvm/repeat-operand-zero-len.rs @@ -11,7 +11,7 @@ #[repr(transparent)] pub struct Wrapper([T; N]); -// CHECK-LABEL: define {{.+}}do_repeat{{.+}}(i32 noundef %x) +// CHECK-LABEL: define {{.+}}do_repeat{{.+}}(i32 noundef{{( signext)?}} %x) // CHECK-NEXT: start: // CHECK-NOT: alloca // CHECK-NEXT: ret void @@ -23,6 +23,6 @@ pub fn do_repeat(x: T) -> Wrapper { // CHECK-LABEL: @trigger_repeat_zero_len #[no_mangle] pub fn trigger_repeat_zero_len() -> Wrapper { - // CHECK: call void {{.+}}do_repeat{{.+}}(i32 noundef 4) + // CHECK: call void {{.+}}do_repeat{{.+}}(i32 noundef{{( signext)?}} 4) do_repeat(4) } diff --git a/tests/codegen-llvm/transmute-scalar.rs b/tests/codegen-llvm/transmute-scalar.rs index ce1b0558b2e..21f7287047c 100644 --- a/tests/codegen-llvm/transmute-scalar.rs +++ b/tests/codegen-llvm/transmute-scalar.rs @@ -1,8 +1,9 @@ //@ add-core-stubs -//@ compile-flags: -C opt-level=0 -C no-prepopulate-passes +//@ compile-flags: -C opt-level=0 -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu +//@ needs-llvm-components: x86 #![crate_type = "lib"] -#![feature(no_core, repr_simd, arm_target_feature, mips_target_feature, s390x_target_feature)] +#![feature(no_core, repr_simd)] #![no_core] extern crate minicore; @@ -117,11 +118,7 @@ struct S([i64; 1]); // CHECK-NEXT: %[[TEMP:.+]] = load i64, ptr %[[RET]] // CHECK-NEXT: ret i64 %[[TEMP]] #[no_mangle] -#[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))] -#[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))] #[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))] -#[cfg_attr(target_arch = "mips", target_feature(enable = "msa"))] -#[cfg_attr(target_arch = "s390x", target_feature(enable = "vector"))] pub extern "C" fn single_element_simd_to_scalar(b: S) -> i64 { unsafe { mem::transmute(b) } } @@ -133,11 +130,7 @@ pub extern "C" fn single_element_simd_to_scalar(b: S) -> i64 { // CHECK-NEXT: %[[TEMP:.+]] = load <1 x i64>, ptr %[[RET]] // CHECK-NEXT: ret <1 x i64> %[[TEMP]] #[no_mangle] -#[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))] -#[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))] #[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))] -#[cfg_attr(target_arch = "mips", target_feature(enable = "msa"))] -#[cfg_attr(target_arch = "s390x", target_feature(enable = "vector"))] pub extern "C" fn scalar_to_single_element_simd(b: i64) -> S { unsafe { mem::transmute(b) } } diff --git a/tests/codegen-llvm/uninhabited-transparent-return-abi.rs b/tests/codegen-llvm/uninhabited-transparent-return-abi.rs index 83d9a7a32ab..507cd7ae2a6 100644 --- a/tests/codegen-llvm/uninhabited-transparent-return-abi.rs +++ b/tests/codegen-llvm/uninhabited-transparent-return-abi.rs @@ -36,7 +36,7 @@ pub fn test_uninhabited_ret_by_ref() { pub fn test_uninhabited_ret_by_ref_with_arg(rsi: u32) { // CHECK: %_2 = alloca [24 x i8], align {{8|4}} // CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 24, )?}}ptr nonnull %_2) - // CHECK-NEXT: call void @opaque_with_arg({{.*}} sret([24 x i8]) {{.*}} %_2, i32 noundef %rsi) #2 + // CHECK-NEXT: call void @opaque_with_arg({{.*}} sret([24 x i8]) {{.*}} %_2, i32 noundef{{( signext)?}} %rsi) #2 // CHECK-NEXT: unreachable unsafe { opaque_with_arg(rsi); -- cgit 1.4.1-3-g733a5 From d25910eaeb3eab25d3bbf9b9815e6d215c202d1f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 19 Aug 2025 22:16:58 +0200 Subject: make `prefetch` intrinsics safe --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 8 ++- compiler/rustc_hir_analysis/src/check/intrinsic.rs | 6 +- library/core/src/intrinsics/mod.rs | 28 ++++----- src/tools/miri/tests/pass/prefetch.rs | 28 ++++----- tests/codegen-llvm/intrinsics/prefetch.rs | 72 ++++++++++------------ 5 files changed, 70 insertions(+), 72 deletions(-) (limited to 'tests/codegen-llvm') diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 4935f8d7dff..06c3d8ed6bc 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -330,10 +330,16 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { _ => bug!(), }; let ptr = args[0].immediate(); + let locality = fn_args.const_at(1).to_value().valtree.unwrap_leaf().to_u32() as i32; self.call_intrinsic( "llvm.prefetch", &[self.val_ty(ptr)], - &[ptr, self.const_i32(rw), args[1].immediate(), self.const_i32(cache_type)], + &[ + ptr, + self.const_i32(rw), + self.const_i32(locality), + self.const_i32(cache_type), + ], ) } sym::carrying_mul_add => { diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index f50aed0b3c2..cfc6bc2f3a0 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -136,6 +136,10 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::round_ties_even_f64 | sym::round_ties_even_f128 | sym::autodiff + | sym::prefetch_read_data + | sym::prefetch_write_data + | sym::prefetch_read_instruction + | sym::prefetch_write_instruction | sym::const_eval_select => hir::Safety::Safe, _ => hir::Safety::Unsafe, }; @@ -218,7 +222,7 @@ pub(crate) fn check_intrinsic_type( | sym::prefetch_write_data | sym::prefetch_read_instruction | sym::prefetch_write_instruction => { - (1, 0, vec![Ty::new_imm_ptr(tcx, param(0)), tcx.types.i32], tcx.types.unit) + (1, 1, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.unit) } sym::needs_drop => (1, 0, vec![], tcx.types.bool), diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 346c7825b74..904aa52c784 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -265,17 +265,16 @@ pub unsafe fn atomic_singlethreadfence(); /// Prefetches have no effect on the behavior of the program but can change its performance /// characteristics. /// -/// The `locality` argument must be a constant integer and is a temporal locality specifier -/// ranging from (0) - no locality, to (3) - extremely local keep in cache. +/// The `LOCALITY` argument is a temporal locality specifier ranging from (0) - no locality, +/// to (3) - extremely local keep in cache. /// /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] #[rustc_nounwind] #[miri::intrinsic_fallback_is_spec] -pub unsafe fn prefetch_read_data(data: *const T, locality: i32) { +pub const fn prefetch_read_data(data: *const T) { // This operation is a no-op, unless it is overridden by the backend. let _ = data; - let _ = locality; } /// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction @@ -283,17 +282,16 @@ pub unsafe fn prefetch_read_data(data: *const T, locality: i32) { /// Prefetches have no effect on the behavior of the program but can change its performance /// characteristics. /// -/// The `locality` argument must be a constant integer and is a temporal locality specifier -/// ranging from (0) - no locality, to (3) - extremely local keep in cache. +/// The `LOCALITY` argument is a temporal locality specifier ranging from (0) - no locality, +/// to (3) - extremely local keep in cache. /// /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] #[rustc_nounwind] #[miri::intrinsic_fallback_is_spec] -pub unsafe fn prefetch_write_data(data: *const T, locality: i32) { +pub const fn prefetch_write_data(data: *const T) { // This operation is a no-op, unless it is overridden by the backend. let _ = data; - let _ = locality; } /// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction @@ -301,17 +299,16 @@ pub unsafe fn prefetch_write_data(data: *const T, locality: i32) { /// Prefetches have no effect on the behavior of the program but can change its performance /// characteristics. /// -/// The `locality` argument must be a constant integer and is a temporal locality specifier -/// ranging from (0) - no locality, to (3) - extremely local keep in cache. +/// The `LOCALITY` argument is a temporal locality specifier ranging from (0) - no locality, +/// to (3) - extremely local keep in cache. /// /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] #[rustc_nounwind] #[miri::intrinsic_fallback_is_spec] -pub unsafe fn prefetch_read_instruction(data: *const T, locality: i32) { +pub const fn prefetch_read_instruction(data: *const T) { // This operation is a no-op, unless it is overridden by the backend. let _ = data; - let _ = locality; } /// The `prefetch` intrinsic is a hint to the code generator to insert a prefetch instruction @@ -319,17 +316,16 @@ pub unsafe fn prefetch_read_instruction(data: *const T, locality: i32) { /// Prefetches have no effect on the behavior of the program but can change its performance /// characteristics. /// -/// The `locality` argument must be a constant integer and is a temporal locality specifier -/// ranging from (0) - no locality, to (3) - extremely local keep in cache. +/// The `LOCALITY` argument is a temporal locality specifier ranging from (0) - no locality, +/// to (3) - extremely local keep in cache. /// /// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] #[rustc_nounwind] #[miri::intrinsic_fallback_is_spec] -pub unsafe fn prefetch_write_instruction(data: *const T, locality: i32) { +pub const fn prefetch_write_instruction(data: *const T) { // This operation is a no-op, unless it is overridden by the backend. let _ = data; - let _ = locality; } /// Executes a breakpoint trap, for inspection by a debugger. diff --git a/src/tools/miri/tests/pass/prefetch.rs b/src/tools/miri/tests/pass/prefetch.rs index 09352695989..99c75c38bde 100644 --- a/src/tools/miri/tests/pass/prefetch.rs +++ b/src/tools/miri/tests/pass/prefetch.rs @@ -1,23 +1,23 @@ #![feature(core_intrinsics)] +// Test that these intrinsics work. Their behavior should be a no-op. + fn main() { static X: [u8; 8] = [0; 8]; - unsafe { - ::std::intrinsics::prefetch_read_data(::std::ptr::null::(), 1); - ::std::intrinsics::prefetch_read_data(::std::ptr::dangling::(), 2); - ::std::intrinsics::prefetch_read_data(X.as_ptr(), 3); + ::std::intrinsics::prefetch_read_data::<_, 1>(::std::ptr::null::()); + ::std::intrinsics::prefetch_read_data::<_, 2>(::std::ptr::dangling::()); + ::std::intrinsics::prefetch_read_data::<_, 3>(X.as_ptr()); - ::std::intrinsics::prefetch_write_data(::std::ptr::null::(), 1); - ::std::intrinsics::prefetch_write_data(::std::ptr::dangling::(), 2); - ::std::intrinsics::prefetch_write_data(X.as_ptr(), 3); + ::std::intrinsics::prefetch_write_data::<_, 1>(::std::ptr::null::()); + ::std::intrinsics::prefetch_write_data::<_, 2>(::std::ptr::dangling::()); + ::std::intrinsics::prefetch_write_data::<_, 3>(X.as_ptr()); - ::std::intrinsics::prefetch_read_instruction(::std::ptr::null::(), 1); - ::std::intrinsics::prefetch_read_instruction(::std::ptr::dangling::(), 2); - ::std::intrinsics::prefetch_read_instruction(X.as_ptr(), 3); + ::std::intrinsics::prefetch_read_instruction::<_, 1>(::std::ptr::null::()); + ::std::intrinsics::prefetch_read_instruction::<_, 2>(::std::ptr::dangling::()); + ::std::intrinsics::prefetch_read_instruction::<_, 3>(X.as_ptr()); - ::std::intrinsics::prefetch_write_instruction(::std::ptr::null::(), 1); - ::std::intrinsics::prefetch_write_instruction(::std::ptr::dangling::(), 2); - ::std::intrinsics::prefetch_write_instruction(X.as_ptr(), 3); - } + ::std::intrinsics::prefetch_write_instruction::<_, 1>(::std::ptr::null::()); + ::std::intrinsics::prefetch_write_instruction::<_, 2>(::std::ptr::dangling::()); + ::std::intrinsics::prefetch_write_instruction::<_, 3>(X.as_ptr()); } diff --git a/tests/codegen-llvm/intrinsics/prefetch.rs b/tests/codegen-llvm/intrinsics/prefetch.rs index 3f9f21c85cb..41877872019 100644 --- a/tests/codegen-llvm/intrinsics/prefetch.rs +++ b/tests/codegen-llvm/intrinsics/prefetch.rs @@ -9,56 +9,48 @@ use std::intrinsics::{ #[no_mangle] pub fn check_prefetch_read_data(data: &[i8]) { - unsafe { - // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 0, i32 1) - prefetch_read_data(data.as_ptr(), 0); - // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 1, i32 1) - prefetch_read_data(data.as_ptr(), 1); - // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 2, i32 1) - prefetch_read_data(data.as_ptr(), 2); - // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 3, i32 1) - prefetch_read_data(data.as_ptr(), 3); - } + // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 0, i32 1) + prefetch_read_data::<_, 0>(data.as_ptr()); + // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 1, i32 1) + prefetch_read_data::<_, 1>(data.as_ptr()); + // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 2, i32 1) + prefetch_read_data::<_, 2>(data.as_ptr()); + // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 3, i32 1) + prefetch_read_data::<_, 3>(data.as_ptr()); } #[no_mangle] pub fn check_prefetch_write_data(data: &[i8]) { - unsafe { - // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 0, i32 1) - prefetch_write_data(data.as_ptr(), 0); - // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 1, i32 1) - prefetch_write_data(data.as_ptr(), 1); - // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 2, i32 1) - prefetch_write_data(data.as_ptr(), 2); - // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 3, i32 1) - prefetch_write_data(data.as_ptr(), 3); - } + // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 0, i32 1) + prefetch_write_data::<_, 0>(data.as_ptr()); + // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 1, i32 1) + prefetch_write_data::<_, 1>(data.as_ptr()); + // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 2, i32 1) + prefetch_write_data::<_, 2>(data.as_ptr()); + // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 3, i32 1) + prefetch_write_data::<_, 3>(data.as_ptr()); } #[no_mangle] pub fn check_prefetch_read_instruction(data: &[i8]) { - unsafe { - // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 0, i32 0) - prefetch_read_instruction(data.as_ptr(), 0); - // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 1, i32 0) - prefetch_read_instruction(data.as_ptr(), 1); - // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 2, i32 0) - prefetch_read_instruction(data.as_ptr(), 2); - // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 3, i32 0) - prefetch_read_instruction(data.as_ptr(), 3); - } + // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 0, i32 0) + prefetch_read_instruction::<_, 0>(data.as_ptr()); + // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 1, i32 0) + prefetch_read_instruction::<_, 1>(data.as_ptr()); + // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 2, i32 0) + prefetch_read_instruction::<_, 2>(data.as_ptr()); + // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 0, i32 3, i32 0) + prefetch_read_instruction::<_, 3>(data.as_ptr()); } #[no_mangle] pub fn check_prefetch_write_instruction(data: &[i8]) { - unsafe { - // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 0, i32 0) - prefetch_write_instruction(data.as_ptr(), 0); - // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 1, i32 0) - prefetch_write_instruction(data.as_ptr(), 1); - // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 2, i32 0) - prefetch_write_instruction(data.as_ptr(), 2); - // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 3, i32 0) - prefetch_write_instruction(data.as_ptr(), 3); - } + // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 0, i32 0) + prefetch_write_instruction::<_, 0>(data.as_ptr()); + // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 1, i32 0) + prefetch_write_instruction::<_, 1>(data.as_ptr()); + // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 2, i32 0) + prefetch_write_instruction::<_, 2>(data.as_ptr()); + // CHECK: call void @llvm.prefetch{{.*}}({{.*}}, i32 1, i32 3, i32 0) + prefetch_write_instruction::<_, 3>(data.as_ptr()); } -- cgit 1.4.1-3-g733a5 From 609c38d15c94f82d1a93d88d196765e81688d601 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 15:37:09 +0200 Subject: update some s390x codegen tests By using `minicore`, `&raw` and removing use of `link_llvm_intrinsics` --- tests/assembly-llvm/s390x-vector-abi.rs | 49 +++++++++++---------------------- tests/codegen-llvm/s390x-simd.rs | 38 +++++++++++++------------ tests/ui/abi/simd-abi-checks-s390x.rs | 18 ++++++------ 3 files changed, 46 insertions(+), 59 deletions(-) (limited to 'tests/codegen-llvm') diff --git a/tests/assembly-llvm/s390x-vector-abi.rs b/tests/assembly-llvm/s390x-vector-abi.rs index fcf42664034..c9c3266a18f 100644 --- a/tests/assembly-llvm/s390x-vector-abi.rs +++ b/tests/assembly-llvm/s390x-vector-abi.rs @@ -1,4 +1,5 @@ //@ revisions: z10 z10_vector z13 z13_no_vector +//@ add-core-stubs // ignore-tidy-linelength //@ assembly-output: emit-asm //@ compile-flags: -Copt-level=3 -Z merge-functions=disabled @@ -18,24 +19,8 @@ // Cases where vector feature is disabled are rejected. // See tests/ui/simd-abi-checks-s390x.rs for test for them. -#[lang = "pointee_sized"] -pub trait PointeeSized {} - -#[lang = "meta_sized"] -pub trait MetaSized: PointeeSized {} - -#[lang = "sized"] -pub trait Sized: MetaSized {} -#[lang = "copy"] -pub trait Copy {} -#[lang = "freeze"] -pub trait Freeze {} - -impl Copy for [T; N] {} - -#[lang = "phantom_data"] -pub struct PhantomData; -impl Copy for PhantomData {} +extern crate minicore; +use minicore::*; #[repr(simd)] pub struct i8x8([i8; 8]); @@ -52,8 +37,6 @@ pub struct WrapperWithZst(T, PhantomData<()>); #[repr(transparent)] pub struct TransparentWrapper(T); -impl Copy for i8 {} -impl Copy for i64 {} impl Copy for i8x8 {} impl Copy for i8x16 {} impl Copy for i8x32 {} @@ -221,7 +204,7 @@ unsafe extern "C" fn vector_transparent_wrapper_ret_large( #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_arg_small(x: i8x8) -> i64 { - unsafe { *(&x as *const i8x8 as *const i64) } + unsafe { *(&raw const x as *const i64) } } // CHECK-LABEL: vector_arg: // CHECK: vlgvg %r2, %v24, 0 @@ -229,7 +212,7 @@ unsafe extern "C" fn vector_arg_small(x: i8x8) -> i64 { #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_arg(x: i8x16) -> i64 { - unsafe { *(&x as *const i8x16 as *const i64) } + unsafe { *(&raw const x as *const i64) } } // CHECK-LABEL: vector_arg_large: // CHECK: lg %r2, 0(%r2) @@ -237,7 +220,7 @@ unsafe extern "C" fn vector_arg(x: i8x16) -> i64 { #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_arg_large(x: i8x32) -> i64 { - unsafe { *(&x as *const i8x32 as *const i64) } + unsafe { *(&raw const x as *const i64) } } // CHECK-LABEL: vector_wrapper_arg_small: @@ -246,7 +229,7 @@ unsafe extern "C" fn vector_arg_large(x: i8x32) -> i64 { #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { - unsafe { *(&x as *const Wrapper as *const i64) } + unsafe { *(&raw const x as *const i64) } } // CHECK-LABEL: vector_wrapper_arg: // CHECK: vlgvg %r2, %v24, 0 @@ -254,7 +237,7 @@ unsafe extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { - unsafe { *(&x as *const Wrapper as *const i64) } + unsafe { *(&raw const x as *const i64) } } // CHECK-LABEL: vector_wrapper_arg_large: // CHECK: lg %r2, 0(%r2) @@ -262,7 +245,7 @@ unsafe extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_wrapper_arg_large(x: Wrapper) -> i64 { - unsafe { *(&x as *const Wrapper as *const i64) } + unsafe { *(&raw const x as *const i64) } } // https://github.com/rust-lang/rust/pull/131586#discussion_r1837071121 @@ -272,7 +255,7 @@ unsafe extern "C" fn vector_wrapper_arg_large(x: Wrapper) -> i64 { #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_wrapper_padding_arg(x: WrapperAlign16) -> i64 { - unsafe { *(&x as *const WrapperAlign16 as *const i64) } + unsafe { *(&raw const x as *const i64) } } // CHECK-LABEL: vector_wrapper_with_zst_arg_small: @@ -282,7 +265,7 @@ unsafe extern "C" fn vector_wrapper_padding_arg(x: WrapperAlign16) -> i64 #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_wrapper_with_zst_arg_small(x: WrapperWithZst) -> i64 { - unsafe { *(&x as *const WrapperWithZst as *const i64) } + unsafe { *(&raw const x as *const i64) } } // CHECK-LABEL: vector_wrapper_with_zst_arg: // CHECK: lg %r2, 0(%r2) @@ -290,7 +273,7 @@ unsafe extern "C" fn vector_wrapper_with_zst_arg_small(x: WrapperWithZst) #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_wrapper_with_zst_arg(x: WrapperWithZst) -> i64 { - unsafe { *(&x as *const WrapperWithZst as *const i64) } + unsafe { *(&raw const x as *const i64) } } // CHECK-LABEL: vector_wrapper_with_zst_arg_large: // CHECK: lg %r2, 0(%r2) @@ -298,7 +281,7 @@ unsafe extern "C" fn vector_wrapper_with_zst_arg(x: WrapperWithZst) -> i6 #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_wrapper_with_zst_arg_large(x: WrapperWithZst) -> i64 { - unsafe { *(&x as *const WrapperWithZst as *const i64) } + unsafe { *(&raw const x as *const i64) } } // CHECK-LABEL: vector_transparent_wrapper_arg_small: @@ -307,7 +290,7 @@ unsafe extern "C" fn vector_wrapper_with_zst_arg_large(x: WrapperWithZst) #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { - unsafe { *(&x as *const TransparentWrapper as *const i64) } + unsafe { *(&raw const x as *const i64) } } // CHECK-LABEL: vector_transparent_wrapper_arg: // CHECK: vlgvg %r2, %v24, 0 @@ -315,7 +298,7 @@ unsafe extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper< #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { - unsafe { *(&x as *const TransparentWrapper as *const i64) } + unsafe { *(&raw const x as *const i64) } } // CHECK-LABEL: vector_transparent_wrapper_arg_large: // CHECK: lg %r2, 0(%r2) @@ -323,5 +306,5 @@ unsafe extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_transparent_wrapper_arg_large(x: TransparentWrapper) -> i64 { - unsafe { *(&x as *const TransparentWrapper as *const i64) } + unsafe { *(&raw const x as *const i64) } } diff --git a/tests/codegen-llvm/s390x-simd.rs b/tests/codegen-llvm/s390x-simd.rs index ac39357519e..464c1be11f1 100644 --- a/tests/codegen-llvm/s390x-simd.rs +++ b/tests/codegen-llvm/s390x-simd.rs @@ -6,7 +6,7 @@ #![crate_type = "rlib"] #![feature(no_core, asm_experimental_arch)] -#![feature(s390x_target_feature, simd_ffi, link_llvm_intrinsics, repr_simd)] +#![feature(s390x_target_feature, simd_ffi, intrinsics, repr_simd)] #![no_core] extern crate minicore; @@ -30,16 +30,20 @@ struct f32x4([f32; 4]); #[repr(simd)] struct f64x2([f64; 2]); -#[allow(improper_ctypes)] -extern "C" { - #[link_name = "llvm.smax.v16i8"] - fn vmxb(a: i8x16, b: i8x16) -> i8x16; - #[link_name = "llvm.smax.v8i16"] - fn vmxh(a: i16x8, b: i16x8) -> i16x8; - #[link_name = "llvm.smax.v4i32"] - fn vmxf(a: i32x4, b: i32x4) -> i32x4; - #[link_name = "llvm.smax.v2i64"] - fn vmxg(a: i64x2, b: i64x2) -> i64x2; +impl Copy for i8x16 {} +impl Copy for i16x8 {} +impl Copy for i32x4 {} +impl Copy for i64x2 {} + +#[rustc_intrinsic] +unsafe fn simd_ge(x: T, y: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_select(mask: M, a: V, b: V) -> V; + +#[inline(always)] +unsafe fn simd_max(a: T, b: T) -> T { + simd_select(simd_ge::(a, b), a, b) } // CHECK-LABEL: define <16 x i8> @max_i8x16 @@ -48,7 +52,7 @@ extern "C" { #[no_mangle] #[target_feature(enable = "vector")] pub unsafe extern "C" fn max_i8x16(a: i8x16, b: i8x16) -> i8x16 { - vmxb(a, b) + simd_max(a, b) } // CHECK-LABEL: define <8 x i16> @max_i16x8 @@ -57,7 +61,7 @@ pub unsafe extern "C" fn max_i8x16(a: i8x16, b: i8x16) -> i8x16 { #[no_mangle] #[target_feature(enable = "vector")] pub unsafe extern "C" fn max_i16x8(a: i16x8, b: i16x8) -> i16x8 { - vmxh(a, b) + simd_max(a, b) } // CHECK-LABEL: define <4 x i32> @max_i32x4 @@ -66,7 +70,7 @@ pub unsafe extern "C" fn max_i16x8(a: i16x8, b: i16x8) -> i16x8 { #[no_mangle] #[target_feature(enable = "vector")] pub unsafe extern "C" fn max_i32x4(a: i32x4, b: i32x4) -> i32x4 { - vmxf(a, b) + simd_max(a, b) } // CHECK-LABEL: define <2 x i64> @max_i64x2 @@ -75,7 +79,7 @@ pub unsafe extern "C" fn max_i32x4(a: i32x4, b: i32x4) -> i32x4 { #[no_mangle] #[target_feature(enable = "vector")] pub unsafe extern "C" fn max_i64x2(a: i64x2, b: i64x2) -> i64x2 { - vmxg(a, b) + simd_max(a, b) } // CHECK-LABEL: define <4 x float> @choose_f32x4 @@ -108,7 +112,7 @@ pub unsafe extern "C" fn max_wrapper_i8x16(a: Wrapper, b: Wrapper) // CHECK: call <16 x i8> @llvm.smax.v16i8 // CHECK-SAME: <16 x i8> // CHECK-SAME: <16 x i8> - Wrapper(vmxb(a.0, b.0)) + Wrapper(simd_max(a.0, b.0)) } #[no_mangle] @@ -122,7 +126,7 @@ pub unsafe extern "C" fn max_wrapper_i64x2(a: Wrapper, b: Wrapper) // CHECK: call <2 x i64> @llvm.smax.v2i64 // CHECK-SAME: <2 x i64> // CHECK-SAME: <2 x i64> - Wrapper(vmxg(a.0, b.0)) + Wrapper(simd_max(a.0, b.0)) } #[no_mangle] diff --git a/tests/ui/abi/simd-abi-checks-s390x.rs b/tests/ui/abi/simd-abi-checks-s390x.rs index 2d4eb7a350f..877a25e8b08 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.rs +++ b/tests/ui/abi/simd-abi-checks-s390x.rs @@ -110,47 +110,47 @@ extern "C" fn vector_transparent_wrapper_ret_large( #[no_mangle] extern "C" fn vector_arg_small(x: i8x8) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - unsafe { *(&x as *const i8x8 as *const i64) } + unsafe { *(&raw const x as *const i64) } } #[no_mangle] extern "C" fn vector_arg(x: i8x16) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - unsafe { *(&x as *const i8x16 as *const i64) } + unsafe { *(&raw const x as *const i64) } } #[no_mangle] extern "C" fn vector_arg_large(x: i8x32) -> i64 { // Ok - unsafe { *(&x as *const i8x32 as *const i64) } + unsafe { *(&raw const x as *const i64) } } #[no_mangle] extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - unsafe { *(&x as *const Wrapper as *const i64) } + unsafe { *(&raw const x as *const i64) } } #[no_mangle] extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - unsafe { *(&x as *const Wrapper as *const i64) } + unsafe { *(&raw const x as *const i64) } } #[no_mangle] extern "C" fn vector_wrapper_arg_large(x: Wrapper) -> i64 { // Ok - unsafe { *(&x as *const Wrapper as *const i64) } + unsafe { *(&raw const x as *const i64) } } #[no_mangle] extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - unsafe { *(&x as *const TransparentWrapper as *const i64) } + unsafe { *(&raw const x as *const i64) } } #[no_mangle] extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { //~^ ERROR requires the `vector` target feature, which is not enabled - unsafe { *(&x as *const TransparentWrapper as *const i64) } + unsafe { *(&raw const x as *const i64) } } #[no_mangle] extern "C" fn vector_transparent_wrapper_arg_large(x: TransparentWrapper) -> i64 { // Ok - unsafe { *(&x as *const TransparentWrapper as *const i64) } + unsafe { *(&raw const x as *const i64) } } -- cgit 1.4.1-3-g733a5 From 2da0ec345300464c9881e8556dcf233d3cc9a724 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Wed, 20 Aug 2025 14:34:48 +0300 Subject: Enforce correct number of arguments for `"x86-interrupt"` functions --- compiler/rustc_ast_passes/messages.ftl | 4 +++ compiler/rustc_ast_passes/src/ast_validation.rs | 11 ++++++++ compiler/rustc_ast_passes/src/errors.rs | 9 +++++++ tests/codegen-llvm/abi-x86-interrupt.rs | 2 +- tests/codegen-llvm/naked-asan.rs | 4 +-- tests/ui/abi/cannot-be-called.avr.stderr | 18 ++++++------- tests/ui/abi/cannot-be-called.i686.stderr | 24 ++++++++--------- tests/ui/abi/cannot-be-called.msp430.stderr | 18 ++++++------- tests/ui/abi/cannot-be-called.riscv32.stderr | 24 ++++++++--------- tests/ui/abi/cannot-be-called.riscv64.stderr | 24 ++++++++--------- tests/ui/abi/cannot-be-called.rs | 6 +++-- tests/ui/abi/cannot-be-called.x64.stderr | 24 ++++++++--------- tests/ui/abi/cannot-be-called.x64_win.stderr | 24 ++++++++--------- tests/ui/abi/cannot-be-coroutine.i686.stderr | 8 +++--- tests/ui/abi/cannot-be-coroutine.rs | 2 +- tests/ui/abi/cannot-be-coroutine.x64.stderr | 8 +++--- tests/ui/abi/cannot-be-coroutine.x64_win.stderr | 8 +++--- .../ui/abi/interrupt-invalid-signature.i686.stderr | 30 +++++++++++++++++----- tests/ui/abi/interrupt-invalid-signature.rs | 18 ++++++++++++- .../ui/abi/interrupt-invalid-signature.x64.stderr | 30 +++++++++++++++++----- tests/ui/abi/interrupt-returns-never-or-unit.rs | 4 +-- .../feature-gate-abi-x86-interrupt.rs | 10 ++++---- .../feature-gate-abi-x86-interrupt.stderr | 10 ++++---- 23 files changed, 197 insertions(+), 123 deletions(-) (limited to 'tests/codegen-llvm') diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 73cbcdd30ce..c0679c1b8ff 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -20,6 +20,10 @@ ast_passes_abi_must_not_have_return_type= .note = functions with the {$abi} ABI cannot have a return type .help = remove the return type +ast_passes_abi_x86_interrupt = + invalid signature for `extern "x86-interrupt"` function + .note = functions with the "x86-interrupt" ABI must be have either 1 or 2 parameters (but found {$param_count}) + ast_passes_assoc_const_without_body = associated constant in `impl` without body .suggestion = provide a definition for the constant diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 0c72f319007..f85993b3378 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -405,6 +405,17 @@ impl<'a> AstValidator<'a> { if let InterruptKind::X86 = interrupt_kind { // "x86-interrupt" is special because it does have arguments. // FIXME(workingjubilee): properly lint on acceptable input types. + let inputs = &sig.decl.inputs; + let param_count = inputs.len(); + if !matches!(param_count, 1 | 2) { + let mut spans: Vec = + inputs.iter().map(|arg| arg.span).collect(); + if spans.is_empty() { + spans = vec![sig.span]; + } + self.dcx().emit_err(errors::AbiX86Interrupt { spans, param_count }); + } + if let FnRetTy::Ty(ref ret_ty) = sig.decl.output && match &ret_ty.kind { TyKind::Never => false, diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 5ecc0d21411..b9b2d271954 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -891,3 +891,12 @@ pub(crate) struct AbiMustNotHaveReturnType { pub span: Span, pub abi: ExternAbi, } + +#[derive(Diagnostic)] +#[diag(ast_passes_abi_x86_interrupt)] +#[note] +pub(crate) struct AbiX86Interrupt { + #[primary_span] + pub spans: Vec, + pub param_count: usize, +} diff --git a/tests/codegen-llvm/abi-x86-interrupt.rs b/tests/codegen-llvm/abi-x86-interrupt.rs index 9a1ded2c9e3..b5c495803d8 100644 --- a/tests/codegen-llvm/abi-x86-interrupt.rs +++ b/tests/codegen-llvm/abi-x86-interrupt.rs @@ -15,4 +15,4 @@ use minicore::*; // CHECK: define x86_intrcc void @has_x86_interrupt_abi #[no_mangle] -pub extern "x86-interrupt" fn has_x86_interrupt_abi() {} +pub extern "x86-interrupt" fn has_x86_interrupt_abi(_p: *const u8) {} diff --git a/tests/codegen-llvm/naked-asan.rs b/tests/codegen-llvm/naked-asan.rs index 46218cf79d6..a57e55d1366 100644 --- a/tests/codegen-llvm/naked-asan.rs +++ b/tests/codegen-llvm/naked-asan.rs @@ -18,10 +18,10 @@ pub fn caller() { unsafe { asm!("call {}", sym page_fault_handler) } } -// CHECK: declare x86_intrcc void @page_fault_handler(){{.*}}#[[ATTRS:[0-9]+]] +// CHECK: declare x86_intrcc void @page_fault_handler(ptr {{.*}}, i64{{.*}}){{.*}}#[[ATTRS:[0-9]+]] #[unsafe(naked)] #[no_mangle] -pub extern "x86-interrupt" fn page_fault_handler() { +pub extern "x86-interrupt" fn page_fault_handler(_: u64, _: u64) { naked_asm!("ud2") } diff --git a/tests/ui/abi/cannot-be-called.avr.stderr b/tests/ui/abi/cannot-be-called.avr.stderr index 1129893cbfa..ed3fa4a20c5 100644 --- a/tests/ui/abi/cannot-be-called.avr.stderr +++ b/tests/ui/abi/cannot-be-called.avr.stderr @@ -19,53 +19,53 @@ LL | extern "riscv-interrupt-s" fn riscv_s() {} error[E0570]: "x86-interrupt" is not a supported ABI for the current target --> $DIR/cannot-be-called.rs:45:8 | -LL | extern "x86-interrupt" fn x86() {} +LL | extern "x86-interrupt" fn x86(_x: *const u8) {} | ^^^^^^^^^^^^^^^ error[E0570]: "msp430-interrupt" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:70:25 + --> $DIR/cannot-be-called.rs:72:25 | LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^ error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:76:26 + --> $DIR/cannot-be-called.rs:78:26 | LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) { | ^^^^^^^^^^^^^^^^^^^ error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:82:26 + --> $DIR/cannot-be-called.rs:84:26 | LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) { | ^^^^^^^^^^^^^^^^^^^ error[E0570]: "x86-interrupt" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:88:22 + --> $DIR/cannot-be-called.rs:90:22 | LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { | ^^^^^^^^^^^^^^^ error: functions with the "avr-interrupt" ABI cannot be called - --> $DIR/cannot-be-called.rs:50:5 + --> $DIR/cannot-be-called.rs:52:5 | LL | avr(); | ^^^^^ | note: an `extern "avr-interrupt"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:50:5 + --> $DIR/cannot-be-called.rs:52:5 | LL | avr(); | ^^^^^ error: functions with the "avr-interrupt" ABI cannot be called - --> $DIR/cannot-be-called.rs:66:5 + --> $DIR/cannot-be-called.rs:68:5 | LL | f() | ^^^ | note: an `extern "avr-interrupt"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:66:5 + --> $DIR/cannot-be-called.rs:68:5 | LL | f() | ^^^ diff --git a/tests/ui/abi/cannot-be-called.i686.stderr b/tests/ui/abi/cannot-be-called.i686.stderr index 024d5e2e93d..6ccb10c44fd 100644 --- a/tests/ui/abi/cannot-be-called.i686.stderr +++ b/tests/ui/abi/cannot-be-called.i686.stderr @@ -23,49 +23,49 @@ LL | extern "riscv-interrupt-s" fn riscv_s() {} | ^^^^^^^^^^^^^^^^^^^ error[E0570]: "avr-interrupt" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:64:22 + --> $DIR/cannot-be-called.rs:66:22 | LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { | ^^^^^^^^^^^^^^^ error[E0570]: "msp430-interrupt" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:70:25 + --> $DIR/cannot-be-called.rs:72:25 | LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^ error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:76:26 + --> $DIR/cannot-be-called.rs:78:26 | LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) { | ^^^^^^^^^^^^^^^^^^^ error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:82:26 + --> $DIR/cannot-be-called.rs:84:26 | LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) { | ^^^^^^^^^^^^^^^^^^^ error: functions with the "x86-interrupt" ABI cannot be called - --> $DIR/cannot-be-called.rs:58:5 + --> $DIR/cannot-be-called.rs:60:5 | -LL | x86(); - | ^^^^^ +LL | x86(&raw const BYTE); + | ^^^^^^^^^^^^^^^^^^^^ | note: an `extern "x86-interrupt"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:58:5 + --> $DIR/cannot-be-called.rs:60:5 | -LL | x86(); - | ^^^^^ +LL | x86(&raw const BYTE); + | ^^^^^^^^^^^^^^^^^^^^ error: functions with the "x86-interrupt" ABI cannot be called - --> $DIR/cannot-be-called.rs:90:5 + --> $DIR/cannot-be-called.rs:92:5 | LL | f() | ^^^ | note: an `extern "x86-interrupt"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:90:5 + --> $DIR/cannot-be-called.rs:92:5 | LL | f() | ^^^ diff --git a/tests/ui/abi/cannot-be-called.msp430.stderr b/tests/ui/abi/cannot-be-called.msp430.stderr index 52d7d792510..0bd5bb40b71 100644 --- a/tests/ui/abi/cannot-be-called.msp430.stderr +++ b/tests/ui/abi/cannot-be-called.msp430.stderr @@ -19,53 +19,53 @@ LL | extern "riscv-interrupt-s" fn riscv_s() {} error[E0570]: "x86-interrupt" is not a supported ABI for the current target --> $DIR/cannot-be-called.rs:45:8 | -LL | extern "x86-interrupt" fn x86() {} +LL | extern "x86-interrupt" fn x86(_x: *const u8) {} | ^^^^^^^^^^^^^^^ error[E0570]: "avr-interrupt" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:64:22 + --> $DIR/cannot-be-called.rs:66:22 | LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { | ^^^^^^^^^^^^^^^ error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:76:26 + --> $DIR/cannot-be-called.rs:78:26 | LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) { | ^^^^^^^^^^^^^^^^^^^ error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:82:26 + --> $DIR/cannot-be-called.rs:84:26 | LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) { | ^^^^^^^^^^^^^^^^^^^ error[E0570]: "x86-interrupt" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:88:22 + --> $DIR/cannot-be-called.rs:90:22 | LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { | ^^^^^^^^^^^^^^^ error: functions with the "msp430-interrupt" ABI cannot be called - --> $DIR/cannot-be-called.rs:52:5 + --> $DIR/cannot-be-called.rs:54:5 | LL | msp430(); | ^^^^^^^^ | note: an `extern "msp430-interrupt"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:52:5 + --> $DIR/cannot-be-called.rs:54:5 | LL | msp430(); | ^^^^^^^^ error: functions with the "msp430-interrupt" ABI cannot be called - --> $DIR/cannot-be-called.rs:72:5 + --> $DIR/cannot-be-called.rs:74:5 | LL | f() | ^^^ | note: an `extern "msp430-interrupt"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:72:5 + --> $DIR/cannot-be-called.rs:74:5 | LL | f() | ^^^ diff --git a/tests/ui/abi/cannot-be-called.riscv32.stderr b/tests/ui/abi/cannot-be-called.riscv32.stderr index 119d93bd58e..6d763bd6379 100644 --- a/tests/ui/abi/cannot-be-called.riscv32.stderr +++ b/tests/ui/abi/cannot-be-called.riscv32.stderr @@ -13,71 +13,71 @@ LL | extern "avr-interrupt" fn avr() {} error[E0570]: "x86-interrupt" is not a supported ABI for the current target --> $DIR/cannot-be-called.rs:45:8 | -LL | extern "x86-interrupt" fn x86() {} +LL | extern "x86-interrupt" fn x86(_x: *const u8) {} | ^^^^^^^^^^^^^^^ error[E0570]: "avr-interrupt" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:64:22 + --> $DIR/cannot-be-called.rs:66:22 | LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { | ^^^^^^^^^^^^^^^ error[E0570]: "msp430-interrupt" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:70:25 + --> $DIR/cannot-be-called.rs:72:25 | LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^ error[E0570]: "x86-interrupt" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:88:22 + --> $DIR/cannot-be-called.rs:90:22 | LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { | ^^^^^^^^^^^^^^^ error: functions with the "riscv-interrupt-m" ABI cannot be called - --> $DIR/cannot-be-called.rs:54:5 + --> $DIR/cannot-be-called.rs:56:5 | LL | riscv_m(); | ^^^^^^^^^ | note: an `extern "riscv-interrupt-m"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:54:5 + --> $DIR/cannot-be-called.rs:56:5 | LL | riscv_m(); | ^^^^^^^^^ error: functions with the "riscv-interrupt-s" ABI cannot be called - --> $DIR/cannot-be-called.rs:56:5 + --> $DIR/cannot-be-called.rs:58:5 | LL | riscv_s(); | ^^^^^^^^^ | note: an `extern "riscv-interrupt-s"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:56:5 + --> $DIR/cannot-be-called.rs:58:5 | LL | riscv_s(); | ^^^^^^^^^ error: functions with the "riscv-interrupt-m" ABI cannot be called - --> $DIR/cannot-be-called.rs:78:5 + --> $DIR/cannot-be-called.rs:80:5 | LL | f() | ^^^ | note: an `extern "riscv-interrupt-m"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:78:5 + --> $DIR/cannot-be-called.rs:80:5 | LL | f() | ^^^ error: functions with the "riscv-interrupt-s" ABI cannot be called - --> $DIR/cannot-be-called.rs:84:5 + --> $DIR/cannot-be-called.rs:86:5 | LL | f() | ^^^ | note: an `extern "riscv-interrupt-s"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:84:5 + --> $DIR/cannot-be-called.rs:86:5 | LL | f() | ^^^ diff --git a/tests/ui/abi/cannot-be-called.riscv64.stderr b/tests/ui/abi/cannot-be-called.riscv64.stderr index 119d93bd58e..6d763bd6379 100644 --- a/tests/ui/abi/cannot-be-called.riscv64.stderr +++ b/tests/ui/abi/cannot-be-called.riscv64.stderr @@ -13,71 +13,71 @@ LL | extern "avr-interrupt" fn avr() {} error[E0570]: "x86-interrupt" is not a supported ABI for the current target --> $DIR/cannot-be-called.rs:45:8 | -LL | extern "x86-interrupt" fn x86() {} +LL | extern "x86-interrupt" fn x86(_x: *const u8) {} | ^^^^^^^^^^^^^^^ error[E0570]: "avr-interrupt" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:64:22 + --> $DIR/cannot-be-called.rs:66:22 | LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { | ^^^^^^^^^^^^^^^ error[E0570]: "msp430-interrupt" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:70:25 + --> $DIR/cannot-be-called.rs:72:25 | LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^ error[E0570]: "x86-interrupt" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:88:22 + --> $DIR/cannot-be-called.rs:90:22 | LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { | ^^^^^^^^^^^^^^^ error: functions with the "riscv-interrupt-m" ABI cannot be called - --> $DIR/cannot-be-called.rs:54:5 + --> $DIR/cannot-be-called.rs:56:5 | LL | riscv_m(); | ^^^^^^^^^ | note: an `extern "riscv-interrupt-m"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:54:5 + --> $DIR/cannot-be-called.rs:56:5 | LL | riscv_m(); | ^^^^^^^^^ error: functions with the "riscv-interrupt-s" ABI cannot be called - --> $DIR/cannot-be-called.rs:56:5 + --> $DIR/cannot-be-called.rs:58:5 | LL | riscv_s(); | ^^^^^^^^^ | note: an `extern "riscv-interrupt-s"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:56:5 + --> $DIR/cannot-be-called.rs:58:5 | LL | riscv_s(); | ^^^^^^^^^ error: functions with the "riscv-interrupt-m" ABI cannot be called - --> $DIR/cannot-be-called.rs:78:5 + --> $DIR/cannot-be-called.rs:80:5 | LL | f() | ^^^ | note: an `extern "riscv-interrupt-m"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:78:5 + --> $DIR/cannot-be-called.rs:80:5 | LL | f() | ^^^ error: functions with the "riscv-interrupt-s" ABI cannot be called - --> $DIR/cannot-be-called.rs:84:5 + --> $DIR/cannot-be-called.rs:86:5 | LL | f() | ^^^ | note: an `extern "riscv-interrupt-s"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:84:5 + --> $DIR/cannot-be-called.rs:86:5 | LL | f() | ^^^ diff --git a/tests/ui/abi/cannot-be-called.rs b/tests/ui/abi/cannot-be-called.rs index af979d65d33..b0267cfa37e 100644 --- a/tests/ui/abi/cannot-be-called.rs +++ b/tests/ui/abi/cannot-be-called.rs @@ -42,9 +42,11 @@ extern "riscv-interrupt-m" fn riscv_m() {} //[x64,x64_win,i686,avr,msp430]~^ ERROR is not a supported ABI extern "riscv-interrupt-s" fn riscv_s() {} //[x64,x64_win,i686,avr,msp430]~^ ERROR is not a supported ABI -extern "x86-interrupt" fn x86() {} +extern "x86-interrupt" fn x86(_x: *const u8) {} //[riscv32,riscv64,avr,msp430]~^ ERROR is not a supported ABI +static BYTE: u8 = 0; + /* extern "interrupt" calls */ fn call_the_interrupts() { avr(); @@ -55,7 +57,7 @@ fn call_the_interrupts() { //[riscv32,riscv64]~^ ERROR functions with the "riscv-interrupt-m" ABI cannot be called riscv_s(); //[riscv32,riscv64]~^ ERROR functions with the "riscv-interrupt-s" ABI cannot be called - x86(); + x86(&raw const BYTE); //[x64,x64_win,i686]~^ ERROR functions with the "x86-interrupt" ABI cannot be called } diff --git a/tests/ui/abi/cannot-be-called.x64.stderr b/tests/ui/abi/cannot-be-called.x64.stderr index 024d5e2e93d..6ccb10c44fd 100644 --- a/tests/ui/abi/cannot-be-called.x64.stderr +++ b/tests/ui/abi/cannot-be-called.x64.stderr @@ -23,49 +23,49 @@ LL | extern "riscv-interrupt-s" fn riscv_s() {} | ^^^^^^^^^^^^^^^^^^^ error[E0570]: "avr-interrupt" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:64:22 + --> $DIR/cannot-be-called.rs:66:22 | LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { | ^^^^^^^^^^^^^^^ error[E0570]: "msp430-interrupt" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:70:25 + --> $DIR/cannot-be-called.rs:72:25 | LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^ error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:76:26 + --> $DIR/cannot-be-called.rs:78:26 | LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) { | ^^^^^^^^^^^^^^^^^^^ error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:82:26 + --> $DIR/cannot-be-called.rs:84:26 | LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) { | ^^^^^^^^^^^^^^^^^^^ error: functions with the "x86-interrupt" ABI cannot be called - --> $DIR/cannot-be-called.rs:58:5 + --> $DIR/cannot-be-called.rs:60:5 | -LL | x86(); - | ^^^^^ +LL | x86(&raw const BYTE); + | ^^^^^^^^^^^^^^^^^^^^ | note: an `extern "x86-interrupt"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:58:5 + --> $DIR/cannot-be-called.rs:60:5 | -LL | x86(); - | ^^^^^ +LL | x86(&raw const BYTE); + | ^^^^^^^^^^^^^^^^^^^^ error: functions with the "x86-interrupt" ABI cannot be called - --> $DIR/cannot-be-called.rs:90:5 + --> $DIR/cannot-be-called.rs:92:5 | LL | f() | ^^^ | note: an `extern "x86-interrupt"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:90:5 + --> $DIR/cannot-be-called.rs:92:5 | LL | f() | ^^^ diff --git a/tests/ui/abi/cannot-be-called.x64_win.stderr b/tests/ui/abi/cannot-be-called.x64_win.stderr index 024d5e2e93d..6ccb10c44fd 100644 --- a/tests/ui/abi/cannot-be-called.x64_win.stderr +++ b/tests/ui/abi/cannot-be-called.x64_win.stderr @@ -23,49 +23,49 @@ LL | extern "riscv-interrupt-s" fn riscv_s() {} | ^^^^^^^^^^^^^^^^^^^ error[E0570]: "avr-interrupt" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:64:22 + --> $DIR/cannot-be-called.rs:66:22 | LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { | ^^^^^^^^^^^^^^^ error[E0570]: "msp430-interrupt" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:70:25 + --> $DIR/cannot-be-called.rs:72:25 | LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^ error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:76:26 + --> $DIR/cannot-be-called.rs:78:26 | LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) { | ^^^^^^^^^^^^^^^^^^^ error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target - --> $DIR/cannot-be-called.rs:82:26 + --> $DIR/cannot-be-called.rs:84:26 | LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) { | ^^^^^^^^^^^^^^^^^^^ error: functions with the "x86-interrupt" ABI cannot be called - --> $DIR/cannot-be-called.rs:58:5 + --> $DIR/cannot-be-called.rs:60:5 | -LL | x86(); - | ^^^^^ +LL | x86(&raw const BYTE); + | ^^^^^^^^^^^^^^^^^^^^ | note: an `extern "x86-interrupt"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:58:5 + --> $DIR/cannot-be-called.rs:60:5 | -LL | x86(); - | ^^^^^ +LL | x86(&raw const BYTE); + | ^^^^^^^^^^^^^^^^^^^^ error: functions with the "x86-interrupt" ABI cannot be called - --> $DIR/cannot-be-called.rs:90:5 + --> $DIR/cannot-be-called.rs:92:5 | LL | f() | ^^^ | note: an `extern "x86-interrupt"` function can only be called using inline assembly - --> $DIR/cannot-be-called.rs:90:5 + --> $DIR/cannot-be-called.rs:92:5 | LL | f() | ^^^ diff --git a/tests/ui/abi/cannot-be-coroutine.i686.stderr b/tests/ui/abi/cannot-be-coroutine.i686.stderr index 8c9292b6a32..230847ff269 100644 --- a/tests/ui/abi/cannot-be-coroutine.i686.stderr +++ b/tests/ui/abi/cannot-be-coroutine.i686.stderr @@ -1,13 +1,13 @@ error: functions with the "x86-interrupt" ABI cannot be `async` --> $DIR/cannot-be-coroutine.rs:52:1 | -LL | async extern "x86-interrupt" fn x86() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | async extern "x86-interrupt" fn x86(_p: *mut ()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `async` keyword from this definition | -LL - async extern "x86-interrupt" fn x86() { -LL + extern "x86-interrupt" fn x86() { +LL - async extern "x86-interrupt" fn x86(_p: *mut ()) { +LL + extern "x86-interrupt" fn x86(_p: *mut ()) { | error: requires `ResumeTy` lang_item diff --git a/tests/ui/abi/cannot-be-coroutine.rs b/tests/ui/abi/cannot-be-coroutine.rs index 7270a55f69e..e3d3d45c632 100644 --- a/tests/ui/abi/cannot-be-coroutine.rs +++ b/tests/ui/abi/cannot-be-coroutine.rs @@ -49,6 +49,6 @@ async extern "riscv-interrupt-s" fn riscv_s() { //[riscv32,riscv64]~^ ERROR functions with the "riscv-interrupt-s" ABI cannot be `async` } -async extern "x86-interrupt" fn x86() { +async extern "x86-interrupt" fn x86(_p: *mut ()) { //[x64,x64_win,i686]~^ ERROR functions with the "x86-interrupt" ABI cannot be `async` } diff --git a/tests/ui/abi/cannot-be-coroutine.x64.stderr b/tests/ui/abi/cannot-be-coroutine.x64.stderr index 8c9292b6a32..230847ff269 100644 --- a/tests/ui/abi/cannot-be-coroutine.x64.stderr +++ b/tests/ui/abi/cannot-be-coroutine.x64.stderr @@ -1,13 +1,13 @@ error: functions with the "x86-interrupt" ABI cannot be `async` --> $DIR/cannot-be-coroutine.rs:52:1 | -LL | async extern "x86-interrupt" fn x86() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | async extern "x86-interrupt" fn x86(_p: *mut ()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `async` keyword from this definition | -LL - async extern "x86-interrupt" fn x86() { -LL + extern "x86-interrupt" fn x86() { +LL - async extern "x86-interrupt" fn x86(_p: *mut ()) { +LL + extern "x86-interrupt" fn x86(_p: *mut ()) { | error: requires `ResumeTy` lang_item diff --git a/tests/ui/abi/cannot-be-coroutine.x64_win.stderr b/tests/ui/abi/cannot-be-coroutine.x64_win.stderr index 8c9292b6a32..230847ff269 100644 --- a/tests/ui/abi/cannot-be-coroutine.x64_win.stderr +++ b/tests/ui/abi/cannot-be-coroutine.x64_win.stderr @@ -1,13 +1,13 @@ error: functions with the "x86-interrupt" ABI cannot be `async` --> $DIR/cannot-be-coroutine.rs:52:1 | -LL | async extern "x86-interrupt" fn x86() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | async extern "x86-interrupt" fn x86(_p: *mut ()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove the `async` keyword from this definition | -LL - async extern "x86-interrupt" fn x86() { -LL + extern "x86-interrupt" fn x86() { +LL - async extern "x86-interrupt" fn x86(_p: *mut ()) { +LL + extern "x86-interrupt" fn x86(_p: *mut ()) { | error: requires `ResumeTy` lang_item diff --git a/tests/ui/abi/interrupt-invalid-signature.i686.stderr b/tests/ui/abi/interrupt-invalid-signature.i686.stderr index 86f2e097c37..df8e318bf0a 100644 --- a/tests/ui/abi/interrupt-invalid-signature.i686.stderr +++ b/tests/ui/abi/interrupt-invalid-signature.i686.stderr @@ -1,15 +1,31 @@ error: invalid signature for `extern "x86-interrupt"` function - --> $DIR/interrupt-invalid-signature.rs:83:40 + --> $DIR/interrupt-invalid-signature.rs:83:53 | -LL | extern "x86-interrupt" fn x86_ret() -> u8 { - | ^^ +LL | extern "x86-interrupt" fn x86_ret(_p: *const u8) -> u8 { + | ^^ | = note: functions with the "x86-interrupt" ABI cannot have a return type help: remove the return type - --> $DIR/interrupt-invalid-signature.rs:83:40 + --> $DIR/interrupt-invalid-signature.rs:83:53 | -LL | extern "x86-interrupt" fn x86_ret() -> u8 { - | ^^ +LL | extern "x86-interrupt" fn x86_ret(_p: *const u8) -> u8 { + | ^^ -error: aborting due to 1 previous error +error: invalid signature for `extern "x86-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:89:1 + | +LL | extern "x86-interrupt" fn x86_0() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: functions with the "x86-interrupt" ABI must be have either 1 or 2 parameters (but found 0) + +error: invalid signature for `extern "x86-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:100:33 + | +LL | extern "x86-interrupt" fn x86_3(_p1: *const u8, _p2: *const u8, _p3: *const u8) { + | ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ + | + = note: functions with the "x86-interrupt" ABI must be have either 1 or 2 parameters (but found 3) + +error: aborting due to 3 previous errors diff --git a/tests/ui/abi/interrupt-invalid-signature.rs b/tests/ui/abi/interrupt-invalid-signature.rs index e389285b069..09bda0d5faf 100644 --- a/tests/ui/abi/interrupt-invalid-signature.rs +++ b/tests/ui/abi/interrupt-invalid-signature.rs @@ -80,11 +80,27 @@ extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { } #[cfg(any(x64,i686))] -extern "x86-interrupt" fn x86_ret() -> u8 { +extern "x86-interrupt" fn x86_ret(_p: *const u8) -> u8 { //[x64,i686]~^ ERROR invalid signature 1 } +#[cfg(any(x64,i686))] +extern "x86-interrupt" fn x86_0() { + //[x64,i686]~^ ERROR invalid signature +} + +#[cfg(any(x64,i686))] +extern "x86-interrupt" fn x86_1(_p1: *const u8) { } + +#[cfg(any(x64,i686))] +extern "x86-interrupt" fn x86_2(_p1: *const u8, _p2: *const u8) { } + +#[cfg(any(x64,i686))] +extern "x86-interrupt" fn x86_3(_p1: *const u8, _p2: *const u8, _p3: *const u8) { + //[x64,i686]~^ ERROR invalid signature +} + /* extern "interrupt" fnptrs with invalid signatures */ diff --git a/tests/ui/abi/interrupt-invalid-signature.x64.stderr b/tests/ui/abi/interrupt-invalid-signature.x64.stderr index 86f2e097c37..df8e318bf0a 100644 --- a/tests/ui/abi/interrupt-invalid-signature.x64.stderr +++ b/tests/ui/abi/interrupt-invalid-signature.x64.stderr @@ -1,15 +1,31 @@ error: invalid signature for `extern "x86-interrupt"` function - --> $DIR/interrupt-invalid-signature.rs:83:40 + --> $DIR/interrupt-invalid-signature.rs:83:53 | -LL | extern "x86-interrupt" fn x86_ret() -> u8 { - | ^^ +LL | extern "x86-interrupt" fn x86_ret(_p: *const u8) -> u8 { + | ^^ | = note: functions with the "x86-interrupt" ABI cannot have a return type help: remove the return type - --> $DIR/interrupt-invalid-signature.rs:83:40 + --> $DIR/interrupt-invalid-signature.rs:83:53 | -LL | extern "x86-interrupt" fn x86_ret() -> u8 { - | ^^ +LL | extern "x86-interrupt" fn x86_ret(_p: *const u8) -> u8 { + | ^^ -error: aborting due to 1 previous error +error: invalid signature for `extern "x86-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:89:1 + | +LL | extern "x86-interrupt" fn x86_0() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: functions with the "x86-interrupt" ABI must be have either 1 or 2 parameters (but found 0) + +error: invalid signature for `extern "x86-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:100:33 + | +LL | extern "x86-interrupt" fn x86_3(_p1: *const u8, _p2: *const u8, _p3: *const u8) { + | ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ + | + = note: functions with the "x86-interrupt" ABI must be have either 1 or 2 parameters (but found 3) + +error: aborting due to 3 previous errors diff --git a/tests/ui/abi/interrupt-returns-never-or-unit.rs b/tests/ui/abi/interrupt-returns-never-or-unit.rs index 8e224229a0b..104b36363d0 100644 --- a/tests/ui/abi/interrupt-returns-never-or-unit.rs +++ b/tests/ui/abi/interrupt-returns-never-or-unit.rs @@ -56,7 +56,7 @@ extern "riscv-interrupt-s" fn riscv_s_ret_never() -> ! { } #[cfg(any(x64,i686))] -extern "x86-interrupt" fn x86_ret_never() -> ! { +extern "x86-interrupt" fn x86_ret_never(_p: *const u8) -> ! { loop {} } @@ -83,7 +83,7 @@ extern "riscv-interrupt-s" fn riscv_s_ret_unit() -> () { } #[cfg(any(x64,i686))] -extern "x86-interrupt" fn x86_ret_unit() -> () { +extern "x86-interrupt" fn x86_ret_unit(_x: *const u8) -> () { () } diff --git a/tests/ui/feature-gates/feature-gate-abi-x86-interrupt.rs b/tests/ui/feature-gates/feature-gate-abi-x86-interrupt.rs index c4fdb5f427c..0abdf0c5309 100644 --- a/tests/ui/feature-gates/feature-gate-abi-x86-interrupt.rs +++ b/tests/ui/feature-gates/feature-gate-abi-x86-interrupt.rs @@ -7,22 +7,22 @@ extern crate minicore; use minicore::*; -extern "x86-interrupt" fn f7() {} //~ ERROR "x86-interrupt" ABI is experimental +extern "x86-interrupt" fn f7(_p: *const u8) {} //~ ERROR "x86-interrupt" ABI is experimental trait Tr { - extern "x86-interrupt" fn m7(); //~ ERROR "x86-interrupt" ABI is experimental - extern "x86-interrupt" fn dm7() {} //~ ERROR "x86-interrupt" ABI is experimental + extern "x86-interrupt" fn m7(_p: *const u8); //~ ERROR "x86-interrupt" ABI is experimental + extern "x86-interrupt" fn dm7(_p: *const u8) {} //~ ERROR "x86-interrupt" ABI is experimental } struct S; // Methods in trait impl impl Tr for S { - extern "x86-interrupt" fn m7() {} //~ ERROR "x86-interrupt" ABI is experimental + extern "x86-interrupt" fn m7(_p: *const u8) {} //~ ERROR "x86-interrupt" ABI is experimental } // Methods in inherent impl impl S { - extern "x86-interrupt" fn im7() {} //~ ERROR "x86-interrupt" ABI is experimental + extern "x86-interrupt" fn im7(_p: *const u8) {} //~ ERROR "x86-interrupt" ABI is experimental } type A7 = extern "x86-interrupt" fn(); //~ ERROR "x86-interrupt" ABI is experimental diff --git a/tests/ui/feature-gates/feature-gate-abi-x86-interrupt.stderr b/tests/ui/feature-gates/feature-gate-abi-x86-interrupt.stderr index 67211d402c6..b53917dda61 100644 --- a/tests/ui/feature-gates/feature-gate-abi-x86-interrupt.stderr +++ b/tests/ui/feature-gates/feature-gate-abi-x86-interrupt.stderr @@ -1,7 +1,7 @@ error[E0658]: the extern "x86-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-x86-interrupt.rs:10:8 | -LL | extern "x86-interrupt" fn f7() {} +LL | extern "x86-interrupt" fn f7(_p: *const u8) {} | ^^^^^^^^^^^^^^^ | = note: see issue #40180 for more information @@ -11,7 +11,7 @@ LL | extern "x86-interrupt" fn f7() {} error[E0658]: the extern "x86-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-x86-interrupt.rs:12:12 | -LL | extern "x86-interrupt" fn m7(); +LL | extern "x86-interrupt" fn m7(_p: *const u8); | ^^^^^^^^^^^^^^^ | = note: see issue #40180 for more information @@ -21,7 +21,7 @@ LL | extern "x86-interrupt" fn m7(); error[E0658]: the extern "x86-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-x86-interrupt.rs:13:12 | -LL | extern "x86-interrupt" fn dm7() {} +LL | extern "x86-interrupt" fn dm7(_p: *const u8) {} | ^^^^^^^^^^^^^^^ | = note: see issue #40180 for more information @@ -31,7 +31,7 @@ LL | extern "x86-interrupt" fn dm7() {} error[E0658]: the extern "x86-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-x86-interrupt.rs:20:12 | -LL | extern "x86-interrupt" fn m7() {} +LL | extern "x86-interrupt" fn m7(_p: *const u8) {} | ^^^^^^^^^^^^^^^ | = note: see issue #40180 for more information @@ -41,7 +41,7 @@ LL | extern "x86-interrupt" fn m7() {} error[E0658]: the extern "x86-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-x86-interrupt.rs:25:12 | -LL | extern "x86-interrupt" fn im7() {} +LL | extern "x86-interrupt" fn im7(_p: *const u8) {} | ^^^^^^^^^^^^^^^ | = note: see issue #40180 for more information -- cgit 1.4.1-3-g733a5 From 8ea3b093819aabd92a605b42989341da0c97c0d6 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Thu, 17 Jul 2025 21:47:00 +0100 Subject: Pass `alloc-variant-zeroed` to LLVM --- compiler/rustc_codegen_llvm/src/attributes.rs | 10 ++++++++++ compiler/rustc_feature/src/builtin_attrs.rs | 4 ++++ compiler/rustc_span/src/symbol.rs | 1 + library/alloc/src/alloc.rs | 1 + tests/codegen-llvm/vec-calloc.rs | 20 ++++++++++++++++++++ 5 files changed, 36 insertions(+) (limited to 'tests/codegen-llvm') diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index c548f467583..984db43c67f 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -436,6 +436,16 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED) { to_add.push(create_alloc_family_attr(cx.llcx)); + if let Some(zv) = + cx.tcx.get_attr(instance.def_id(), rustc_span::sym::rustc_allocator_zeroed_variant) + && let Some(name) = zv.value_str() + { + to_add.push(llvm::CreateAttrStringValue( + cx.llcx, + "alloc-variant-zeroed", + &mangle_internal_symbol(cx.tcx, name.as_str()), + )); + } // apply to argument place instead of function let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx); attributes::apply_to_llfn(llfn, AttributePlace::Argument(1), &[alloc_align]); diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index ab6b8f92802..6cd0c43a3e0 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -993,6 +993,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, ), + rustc_attr!( + rustc_allocator_zeroed_variant, Normal, template!(NameValueStr: "function"), ErrorPreceding, + EncodeCrossCrate::Yes, + ), gated!( default_lib_allocator, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, allocator_internals, experimental!(default_lib_allocator), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index acbed7a9eed..21ba6c2779d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1816,6 +1816,7 @@ symbols! { rustc_align, rustc_allocator, rustc_allocator_zeroed, + rustc_allocator_zeroed_variant, rustc_allow_const_fn_unstable, rustc_allow_incoherent_impl, rustc_allowed_through_unstable_modules, diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index c9b98fa4e5a..76630a746dd 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -17,6 +17,7 @@ unsafe extern "Rust" { #[rustc_allocator] #[rustc_nounwind] #[rustc_std_internal_symbol] + #[rustc_allocator_zeroed_variant = "__rust_alloc_zeroed"] fn __rust_alloc(size: usize, align: usize) -> *mut u8; #[rustc_deallocator] #[rustc_nounwind] diff --git a/tests/codegen-llvm/vec-calloc.rs b/tests/codegen-llvm/vec-calloc.rs index d1c320ead01..15971bbfa00 100644 --- a/tests/codegen-llvm/vec-calloc.rs +++ b/tests/codegen-llvm/vec-calloc.rs @@ -1,4 +1,6 @@ +//@ revisions: normal llvm21 //@ compile-flags: -Copt-level=3 -Z merge-functions=disabled +//@ [llvm21] min-llvm-version: 21 //@ only-x86_64 #![crate_type = "lib"] @@ -176,6 +178,24 @@ pub fn vec_option_i32(n: usize) -> Vec> { vec![None; n] } +// LLVM21-LABEL: @vec_array +#[cfg(llvm21)] +#[no_mangle] +pub fn vec_array(n: usize) -> Vec<[u32; 1_000_000]> { + // LLVM21-NOT: call {{.*}}alloc::vec::from_elem + // LLVM21-NOT: call {{.*}}reserve + // LLVM21-NOT: call {{.*}}__rust_alloc( + + // LLVM21: call {{.*}}__rust_alloc_zeroed( + + // LLVM21-NOT: call {{.*}}alloc::vec::from_elem + // LLVM21-NOT: call {{.*}}reserve + // LLVM21-NOT: call {{.*}}__rust_alloc( + + // LLVM21: ret void + vec![[0; 1_000_000]; 3] +} + // Ensure that __rust_alloc_zeroed gets the right attributes for LLVM to optimize it away. // CHECK: declare noalias noundef ptr @{{.*}}__rust_alloc_zeroed(i64 noundef, i64 allocalign noundef) unnamed_addr [[RUST_ALLOC_ZEROED_ATTRS:#[0-9]+]] -- cgit 1.4.1-3-g733a5 From d71ed8d19bcfdbd6eb3bf2620071f2ab95470a57 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 11 Aug 2025 17:18:23 +0200 Subject: Tell LLVM about read-only captures `&Freeze` parameters are not only `readonly` within the function, but any captures of the pointer can also only be used for reads. This can now be encoded using the `captures(address, read_provenance)` attribute. --- compiler/rustc_codegen_llvm/src/abi.rs | 7 ++++++- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 8 ++++++++ compiler/rustc_target/src/callconv/mod.rs | 1 + compiler/rustc_ty_utils/src/abi.rs | 1 + tests/codegen-llvm/function-arguments.rs | 20 ++++++++++---------- tests/codegen-llvm/range-attribute.rs | 2 +- tests/codegen-llvm/read-only-capture-opt.rs | 18 ++++++++++++++++++ tests/ui/abi/debug.generic.stderr | 2 +- tests/ui/abi/debug.riscv64.stderr | 2 +- 10 files changed, 48 insertions(+), 14 deletions(-) create mode 100644 tests/codegen-llvm/read-only-capture-opt.rs (limited to 'tests/codegen-llvm') diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 043123fcab2..399f8b6e762 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -42,12 +42,13 @@ trait ArgAttributesExt { const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 1] = [(ArgAttribute::InReg, llvm::AttributeKind::InReg)]; -const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 5] = [ +const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 6] = [ (ArgAttribute::NoAlias, llvm::AttributeKind::NoAlias), (ArgAttribute::NoCapture, llvm::AttributeKind::NoCapture), (ArgAttribute::NonNull, llvm::AttributeKind::NonNull), (ArgAttribute::ReadOnly, llvm::AttributeKind::ReadOnly), (ArgAttribute::NoUndef, llvm::AttributeKind::NoUndef), + (ArgAttribute::CapturesReadOnly, llvm::AttributeKind::CapturesReadOnly), ]; fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 8]> { @@ -83,6 +84,10 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&' } for (attr, llattr) in OPTIMIZATION_ATTRIBUTES { if regular.contains(attr) { + // captures(address, read_provenance) is only available since LLVM 21. + if attr == ArgAttribute::CapturesReadOnly && llvm_util::get_version() < (21, 0, 0) { + continue; + } attrs.push(llattr.create_attr(cx.llcx)); } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 55d26a97e2d..2461f70a86e 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -251,6 +251,7 @@ pub(crate) enum AttributeKind { Writable = 42, DeadOnUnwind = 43, DeadOnReturn = 44, + CapturesReadOnly = 45, } /// LLVMIntPredicate diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index e4fe1fc2e42..e699e4b9c13 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -278,6 +278,7 @@ enum class LLVMRustAttributeKind { Writable = 42, DeadOnUnwind = 43, DeadOnReturn = 44, + CapturesReadOnly = 45, }; static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) { @@ -376,6 +377,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) { #else report_fatal_error("DeadOnReturn attribute requires LLVM 21 or later"); #endif + case LLVMRustAttributeKind::CapturesReadOnly: + report_fatal_error("Should be handled separately"); } report_fatal_error("bad LLVMRustAttributeKind"); } @@ -430,6 +433,11 @@ LLVMRustCreateAttrNoValue(LLVMContextRef C, LLVMRustAttributeKind RustAttr) { if (RustAttr == LLVMRustAttributeKind::NoCapture) { return wrap(Attribute::getWithCaptureInfo(*unwrap(C), CaptureInfo::none())); } + if (RustAttr == LLVMRustAttributeKind::CapturesReadOnly) { + return wrap(Attribute::getWithCaptureInfo( + *unwrap(C), CaptureInfo(CaptureComponents::Address | + CaptureComponents::ReadProvenance))); + } #endif return wrap(Attribute::get(*unwrap(C), fromRust(RustAttr))); } diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 63e56744aec..5f2a6f7ba38 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -119,6 +119,7 @@ mod attr_impl { const ReadOnly = 1 << 4; const InReg = 1 << 5; const NoUndef = 1 << 6; + const CapturesReadOnly = 1 << 7; } } rustc_data_structures::external_bitflags_debug! { ArgAttribute } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 89d1dd8cf23..e4ed084b073 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -356,6 +356,7 @@ fn arg_attrs_for_rust_scalar<'tcx>( if matches!(kind, PointerKind::SharedRef { frozen: true }) && !is_return { attrs.set(ArgAttribute::ReadOnly); + attrs.set(ArgAttribute::CapturesReadOnly); } } } diff --git a/tests/codegen-llvm/function-arguments.rs b/tests/codegen-llvm/function-arguments.rs index a3fafbe6f82..db508682862 100644 --- a/tests/codegen-llvm/function-arguments.rs +++ b/tests/codegen-llvm/function-arguments.rs @@ -80,7 +80,7 @@ pub fn option_nonzero_int(x: Option>) -> Option> { x } -// CHECK: @readonly_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1) +// CHECK: @readonly_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn readonly_borrow(_: &i32) {} @@ -91,12 +91,12 @@ pub fn readonly_borrow_ret() -> &'static i32 { loop {} } -// CHECK: @static_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1) +// CHECK: @static_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1) // static borrow may be captured #[no_mangle] pub fn static_borrow(_: &'static i32) {} -// CHECK: @named_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1) +// CHECK: @named_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1) // borrow with named lifetime may be captured #[no_mangle] pub fn named_borrow<'r>(_: &'r i32) {} @@ -129,7 +129,7 @@ pub fn mutable_borrow_ret() -> &'static mut i32 { // . pub fn mutable_notunpin_borrow(_: &mut NotUnpin) {} -// CHECK: @notunpin_borrow(ptr noalias noundef readonly align 4 dereferenceable(4) %_1) +// CHECK: @notunpin_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(4) %_1) // But `&NotUnpin` behaves perfectly normal. #[no_mangle] pub fn notunpin_borrow(_: &NotUnpin) {} @@ -138,12 +138,12 @@ pub fn notunpin_borrow(_: &NotUnpin) {} #[no_mangle] pub fn indirect_struct(_: S) {} -// CHECK: @borrowed_struct(ptr noalias noundef readonly align 4 dereferenceable(32) %_1) +// CHECK: @borrowed_struct(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable(32) %_1) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn borrowed_struct(_: &S) {} -// CHECK: @option_borrow(ptr noalias noundef readonly align 4 dereferenceable_or_null(4) %_x) +// CHECK: @option_borrow(ptr noalias noundef readonly align 4{{( captures\(address, read_provenance\))?}} dereferenceable_or_null(4) %_x) #[no_mangle] pub fn option_borrow(_x: Option<&i32>) {} @@ -185,7 +185,7 @@ pub fn _box(x: Box) -> Box { // With a custom allocator, it should *not* have `noalias`. (See // for why.) The second argument is the allocator, // which is a reference here that still carries `noalias` as usual. -// CHECK: @_box_custom(ptr noundef nonnull align 4 %x.0, ptr noalias noundef nonnull readonly align 1 %x.1) +// CHECK: @_box_custom(ptr noundef nonnull align 4 %x.0, ptr noalias noundef nonnull readonly align 1{{( captures\(address, read_provenance\))?}} %x.1) #[no_mangle] pub fn _box_custom(x: Box) { drop(x) @@ -208,7 +208,7 @@ pub fn struct_return() -> S { #[no_mangle] pub fn helper(_: usize) {} -// CHECK: @slice(ptr noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef %_1.1) +// CHECK: @slice(ptr noalias noundef nonnull readonly align 1{{( captures\(address, read_provenance\))?}} %_1.0, [[USIZE]] noundef %_1.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn slice(_: &[u8]) {} @@ -227,7 +227,7 @@ pub fn unsafe_slice(_: &[UnsafeInner]) {} #[no_mangle] pub fn raw_slice(_: *const [u8]) {} -// CHECK: @str(ptr noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef %_1.1) +// CHECK: @str(ptr noalias noundef nonnull readonly align 1{{( captures\(address, read_provenance\))?}} %_1.0, [[USIZE]] noundef %_1.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn str(_: &[u8]) {} @@ -259,7 +259,7 @@ pub fn trait_option(x: Option>) -> Option &[u16] { x diff --git a/tests/codegen-llvm/range-attribute.rs b/tests/codegen-llvm/range-attribute.rs index b81ff9ab3e2..865d36d4747 100644 --- a/tests/codegen-llvm/range-attribute.rs +++ b/tests/codegen-llvm/range-attribute.rs @@ -67,7 +67,7 @@ pub fn enum2_value(x: Enum2) -> Enum2 { x } -// CHECK: noundef [[USIZE]] @takes_slice(ptr noalias noundef nonnull readonly align 4 %x.0, [[USIZE]] noundef %x.1) +// CHECK: noundef [[USIZE]] @takes_slice(ptr {{.*}} %x.0, [[USIZE]] noundef %x.1) #[no_mangle] pub fn takes_slice(x: &[i32]) -> usize { x.len() diff --git a/tests/codegen-llvm/read-only-capture-opt.rs b/tests/codegen-llvm/read-only-capture-opt.rs new file mode 100644 index 00000000000..78d56f8efc2 --- /dev/null +++ b/tests/codegen-llvm/read-only-capture-opt.rs @@ -0,0 +1,18 @@ +//@ compile-flags: -C opt-level=3 -Z mir-opt-level=0 +//@ min-llvm-version: 21 + +#![crate_type = "lib"] + +unsafe extern "C" { + safe fn do_something(p: &i32); +} + +#[unsafe(no_mangle)] +pub fn test() -> i32 { + // CHECK-LABEL: @test( + // CHECK: ret i32 0 + let i = 0; + do_something(&i); + do_something(&i); + i +} diff --git a/tests/ui/abi/debug.generic.stderr b/tests/ui/abi/debug.generic.stderr index 3b29efc8102..8375de3e817 100644 --- a/tests/ui/abi/debug.generic.stderr +++ b/tests/ui/abi/debug.generic.stderr @@ -939,7 +939,7 @@ error: fn_abi_of(assoc_test) = FnAbi { }, mode: Direct( ArgAttributes { - regular: NoAlias | NonNull | ReadOnly | NoUndef, + regular: NoAlias | NonNull | ReadOnly | NoUndef | CapturesReadOnly, arg_ext: None, pointee_size: Size(2 bytes), pointee_align: Some( diff --git a/tests/ui/abi/debug.riscv64.stderr b/tests/ui/abi/debug.riscv64.stderr index 2417396de2f..bddf0aea3d7 100644 --- a/tests/ui/abi/debug.riscv64.stderr +++ b/tests/ui/abi/debug.riscv64.stderr @@ -939,7 +939,7 @@ error: fn_abi_of(assoc_test) = FnAbi { }, mode: Direct( ArgAttributes { - regular: NoAlias | NonNull | ReadOnly | NoUndef, + regular: NoAlias | NonNull | ReadOnly | NoUndef | CapturesReadOnly, arg_ext: None, pointee_size: Size(2 bytes), pointee_align: Some( -- cgit 1.4.1-3-g733a5 From 9e28de2720e658b015e4609001bad7b11eac1e6d Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Thu, 14 Aug 2025 00:59:11 -0400 Subject: Add codegen regression tests Most of these regressions concern elimination of panics and bounds checks that were fixed upstream by LLVM. --- .../issues/and-masked-comparison-131162.rs | 17 ++++++++++++ .../issues/assert-for-loop-bounds-check-71997.rs | 18 +++++++++++++ .../issues/elided-division-by-zero-check-74917.rs | 13 +++++++++ .../issues/for-loop-inner-assert-91109.rs | 18 +++++++++++++ .../issues/iter-max-no-unwrap-failed-129583.rs | 31 ++++++++++++++++++++++ .../issues/matches-logical-or-141497.rs | 25 +++++++++++++++++ .../issues/no-bounds-check-after-assert-110971.rs | 14 ++++++++++ .../issues/no-panic-for-pop-after-assert-71257.rs | 19 +++++++++++++ .../issues/num-is-digit-to-digit-59352.rs | 14 ++++++++++ .../issues/slice-index-bounds-check-80075.rs | 13 +++++++++ 10 files changed, 182 insertions(+) create mode 100644 tests/codegen-llvm/issues/and-masked-comparison-131162.rs create mode 100644 tests/codegen-llvm/issues/assert-for-loop-bounds-check-71997.rs create mode 100644 tests/codegen-llvm/issues/elided-division-by-zero-check-74917.rs create mode 100644 tests/codegen-llvm/issues/for-loop-inner-assert-91109.rs create mode 100644 tests/codegen-llvm/issues/iter-max-no-unwrap-failed-129583.rs create mode 100644 tests/codegen-llvm/issues/matches-logical-or-141497.rs create mode 100644 tests/codegen-llvm/issues/no-bounds-check-after-assert-110971.rs create mode 100644 tests/codegen-llvm/issues/no-panic-for-pop-after-assert-71257.rs create mode 100644 tests/codegen-llvm/issues/num-is-digit-to-digit-59352.rs create mode 100644 tests/codegen-llvm/issues/slice-index-bounds-check-80075.rs (limited to 'tests/codegen-llvm') diff --git a/tests/codegen-llvm/issues/and-masked-comparison-131162.rs b/tests/codegen-llvm/issues/and-masked-comparison-131162.rs new file mode 100644 index 00000000000..bdf021092fd --- /dev/null +++ b/tests/codegen-llvm/issues/and-masked-comparison-131162.rs @@ -0,0 +1,17 @@ +//@ compile-flags: -Copt-level=3 +//@ min-llvm-version: 20 + +#![crate_type = "lib"] + +// CHECK-LABEL: @issue_131162 +#[no_mangle] +pub fn issue_131162(a1: usize, a2: usize) -> bool { + const MASK: usize = 1; + + // CHECK-NOT: xor + // CHECK-NOT: trunc + // CHECK-NOT: and i1 + // CHECK: icmp + // CHECK-NEXT: ret + (a1 & !MASK) == (a2 & !MASK) && (a1 & MASK) == (a2 & MASK) +} diff --git a/tests/codegen-llvm/issues/assert-for-loop-bounds-check-71997.rs b/tests/codegen-llvm/issues/assert-for-loop-bounds-check-71997.rs new file mode 100644 index 00000000000..a0c64d607a8 --- /dev/null +++ b/tests/codegen-llvm/issues/assert-for-loop-bounds-check-71997.rs @@ -0,0 +1,18 @@ +// Tests that there's no bounds check within for-loop after asserting that +// the range start and end are within bounds. + +//@ compile-flags: -Copt-level=3 + +#![crate_type = "lib"] + +// CHECK-LABEL: @no_bounds_check_after_assert +#[no_mangle] +fn no_bounds_check_after_assert(slice: &[u64], start: usize, end: usize) -> u64 { + // CHECK-NOT: panic_bounds_check + let mut total = 0; + assert!(start < end && start < slice.len() && end <= slice.len()); + for i in start..end { + total += slice[i]; + } + total +} diff --git a/tests/codegen-llvm/issues/elided-division-by-zero-check-74917.rs b/tests/codegen-llvm/issues/elided-division-by-zero-check-74917.rs new file mode 100644 index 00000000000..9e890e14852 --- /dev/null +++ b/tests/codegen-llvm/issues/elided-division-by-zero-check-74917.rs @@ -0,0 +1,13 @@ +// Tests that there is no check for dividing by zero since the +// denominator, `(x - y)`, will always be greater than 0 since `x > y`. + +//@ compile-flags: -Copt-level=3 + +#![crate_type = "lib"] + +// CHECK-LABEL: @issue_74917 +#[no_mangle] +pub fn issue_74917(x: u16, y: u16) -> u16 { + // CHECK-NOT: panic + if x > y { 100 / (x - y) } else { 100 } +} diff --git a/tests/codegen-llvm/issues/for-loop-inner-assert-91109.rs b/tests/codegen-llvm/issues/for-loop-inner-assert-91109.rs new file mode 100644 index 00000000000..bffbcd7d069 --- /dev/null +++ b/tests/codegen-llvm/issues/for-loop-inner-assert-91109.rs @@ -0,0 +1,18 @@ +// Tests that there's no bounds check for the inner loop after the assert. + +//@ compile-flags: -Copt-level=3 + +#![crate_type = "lib"] + +// CHECK-LABEL: @zero +#[no_mangle] +pub fn zero(d: &mut [Vec]) { + // CHECK-NOT: panic_bounds_check + let n = d.len(); + for i in 0..n { + assert!(d[i].len() == n); + for j in 0..n { + d[i][j] = 0; + } + } +} diff --git a/tests/codegen-llvm/issues/iter-max-no-unwrap-failed-129583.rs b/tests/codegen-llvm/issues/iter-max-no-unwrap-failed-129583.rs new file mode 100644 index 00000000000..4d3fa4993ef --- /dev/null +++ b/tests/codegen-llvm/issues/iter-max-no-unwrap-failed-129583.rs @@ -0,0 +1,31 @@ +// Tests that `unwrap` is optimized out when the slice has a known length. +// The iterator may unroll for values smaller than a certain threshold so we +// use a larger value to prevent unrolling. + +//@ compile-flags: -Copt-level=3 +//@ min-llvm-version: 20 + +#![crate_type = "lib"] + +// CHECK-LABEL: @infallible_max_not_unrolled +#[no_mangle] +pub fn infallible_max_not_unrolled(x: &[u8; 1024]) -> u8 { + // CHECK-NOT: panic + // CHECK-NOT: unwrap_failed + *x.iter().max().unwrap() +} + +// CHECK-LABEL: @infallible_max_unrolled +#[no_mangle] +pub fn infallible_max_unrolled(x: &[u8; 10]) -> u8 { + // CHECK-NOT: panic + // CHECK-NOT: unwrap_failed + *x.iter().max().unwrap() +} + +// CHECK-LABEL: @may_panic_max +#[no_mangle] +pub fn may_panic_max(x: &[u8]) -> u8 { + // CHECK: unwrap_failed + *x.iter().max().unwrap() +} diff --git a/tests/codegen-llvm/issues/matches-logical-or-141497.rs b/tests/codegen-llvm/issues/matches-logical-or-141497.rs new file mode 100644 index 00000000000..348f62096a5 --- /dev/null +++ b/tests/codegen-llvm/issues/matches-logical-or-141497.rs @@ -0,0 +1,25 @@ +// Tests that `matches!` optimizes the same as +// `f == FrameType::Inter || f == FrameType::Switch`. + +//@ compile-flags: -Copt-level=3 +//@ min-llvm-version: 21 + +#![crate_type = "lib"] + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum FrameType { + Key = 0, + Inter = 1, + Intra = 2, + Switch = 3, +} + +// CHECK-LABEL: @is_inter_or_switch +#[no_mangle] +pub fn is_inter_or_switch(f: FrameType) -> bool { + // CHECK-NEXT: start: + // CHECK-NEXT: and i8 + // CHECK-NEXT: icmp + // CHECK-NEXT: ret + matches!(f, FrameType::Inter | FrameType::Switch) +} diff --git a/tests/codegen-llvm/issues/no-bounds-check-after-assert-110971.rs b/tests/codegen-llvm/issues/no-bounds-check-after-assert-110971.rs new file mode 100644 index 00000000000..aa4002f176d --- /dev/null +++ b/tests/codegen-llvm/issues/no-bounds-check-after-assert-110971.rs @@ -0,0 +1,14 @@ +// Tests that the slice access for `j` doesn't have a bounds check panic after +// being asserted as less than half of the slice length. + +//@ compile-flags: -Copt-level=3 + +#![crate_type = "lib"] + +// CHECK-LABEL: @check_only_assert_panic +#[no_mangle] +pub fn check_only_assert_panic(arr: &[u32], j: usize) -> u32 { + // CHECK-NOT: panic_bounds_check + assert!(j < arr.len() / 2); + arr[j] +} diff --git a/tests/codegen-llvm/issues/no-panic-for-pop-after-assert-71257.rs b/tests/codegen-llvm/issues/no-panic-for-pop-after-assert-71257.rs new file mode 100644 index 00000000000..68877c28d6c --- /dev/null +++ b/tests/codegen-llvm/issues/no-panic-for-pop-after-assert-71257.rs @@ -0,0 +1,19 @@ +// Tests that the `unwrap` branch is optimized out from the `pop` since the +// length has already been validated. + +//@ compile-flags: -Copt-level=3 + +#![crate_type = "lib"] + +pub enum Foo { + First(usize), + Second(usize), +} + +// CHECK-LABEL: @check_only_one_panic +#[no_mangle] +pub fn check_only_one_panic(v: &mut Vec) -> Foo { + // CHECK-COUNT-1: call{{.+}}panic + assert!(v.len() == 1); + v.pop().unwrap() +} diff --git a/tests/codegen-llvm/issues/num-is-digit-to-digit-59352.rs b/tests/codegen-llvm/issues/num-is-digit-to-digit-59352.rs new file mode 100644 index 00000000000..1d23eb2cdf9 --- /dev/null +++ b/tests/codegen-llvm/issues/num-is-digit-to-digit-59352.rs @@ -0,0 +1,14 @@ +// Tests that there's no panic on unwrapping `to_digit` call after checking +// with `is_digit`. + +//@ compile-flags: -Copt-level=3 + +#![crate_type = "lib"] + +// CHECK-LABEL: @num_to_digit_slow +#[no_mangle] +pub fn num_to_digit_slow(num: char) -> u32 { + // CHECK-NOT: br + // CHECK-NOT: panic + if num.is_digit(8) { num.to_digit(8).unwrap() } else { 0 } +} diff --git a/tests/codegen-llvm/issues/slice-index-bounds-check-80075.rs b/tests/codegen-llvm/issues/slice-index-bounds-check-80075.rs new file mode 100644 index 00000000000..ecb5eb787c7 --- /dev/null +++ b/tests/codegen-llvm/issues/slice-index-bounds-check-80075.rs @@ -0,0 +1,13 @@ +// Tests that no bounds check panic is generated for `j` since +// `j <= i < data.len()`. + +//@ compile-flags: -Copt-level=3 + +#![crate_type = "lib"] + +// CHECK-LABEL: @issue_80075 +#[no_mangle] +pub fn issue_80075(data: &[u8], i: usize, j: usize) -> u8 { + // CHECK-NOT: panic_bounds_check + if i < data.len() && j <= i { data[j] } else { 0 } +} -- cgit 1.4.1-3-g733a5 From 377a0c88a9bbfa8a389163b2ac3de38e64d762dc Mon Sep 17 00:00:00 2001 From: Karl Meakin Date: Fri, 25 Jul 2025 22:17:24 +0100 Subject: Consolidate panicking functions in `slice/index.rs` Consolidate all the panicking functions in `slice/index.rs` to use a single `slice_index_fail` function, similar to how it is done in `str/traits.rs`. --- library/core/src/slice/index.rs | 200 +++++++++++---------- library/coretests/tests/slice.rs | 10 +- src/tools/miri/tests/panic/oob_subslice.stderr | 2 +- .../binary-search-index-no-bound-check.rs | 3 +- tests/codegen-llvm/integer-overflow.rs | 4 +- .../issue-113757-bounds-check-after-cmp-max.rs | 2 +- tests/codegen-llvm/issues/issue-27130.rs | 4 +- .../issues/issue-69101-bounds-check.rs | 6 +- .../issue-73396-bounds-check-after-position.rs | 18 +- .../slice-last-elements-optimization.rs | 11 +- tests/codegen-llvm/slice-reverse.rs | 8 +- ...ce_index_range.PreCodegen.after.panic-abort.mir | 69 ++++++- ...e_index_range.PreCodegen.after.panic-unwind.mir | 69 ++++++- 13 files changed, 269 insertions(+), 137 deletions(-) (limited to 'tests/codegen-llvm') diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index ae360df80f6..98091e9fe83 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -34,53 +34,44 @@ where #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] -const fn slice_start_index_len_fail(index: usize, len: usize) -> ! { - const_panic!( - "slice start index is out of range for slice", - "range start index {index} out of range for slice of length {len}", - index: usize, - len: usize, - ) -} +const fn slice_index_fail(start: usize, end: usize, len: usize) -> ! { + if start > len { + const_panic!( + "slice start index is out of range for slice", + "range start index {start} out of range for slice of length {len}", + start: usize, + len: usize, + ) + } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] -#[cfg_attr(feature = "panic_immediate_abort", inline)] -#[track_caller] -const fn slice_end_index_len_fail(index: usize, len: usize) -> ! { - const_panic!( - "slice end index is out of range for slice", - "range end index {index} out of range for slice of length {len}", - index: usize, - len: usize, - ) -} + if end > len { + const_panic!( + "slice end index is out of range for slice", + "range end index {end} out of range for slice of length {len}", + end: usize, + len: usize, + ) + } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] -#[cfg_attr(feature = "panic_immediate_abort", inline)] -#[track_caller] -const fn slice_index_order_fail(index: usize, end: usize) -> ! { + if start > end { + const_panic!( + "slice index start is larger than end", + "slice index starts at {start} but ends at {end}", + start: usize, + end: usize, + ) + } + + // Only reachable if the range was a `RangeInclusive` or a + // `RangeToInclusive`, with `end == len`. const_panic!( - "slice index start is larger than end", - "slice index starts at {index} but ends at {end}", - index: usize, + "slice end index is out of range for slice", + "range end index {end} out of range for slice of length {len}", end: usize, + len: usize, ) } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] -#[cfg_attr(feature = "panic_immediate_abort", inline)] -#[track_caller] -const fn slice_start_index_overflow_fail() -> ! { - panic!("attempted to index slice from after maximum usize"); -} - -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] -#[cfg_attr(feature = "panic_immediate_abort", inline)] -#[track_caller] -const fn slice_end_index_overflow_fail() -> ! { - panic!("attempted to index slice up to maximum usize"); -} - // The UbChecks are great for catching bugs in the unsafe methods, but including // them in safe indexing is unnecessary and hurts inlining and debug runtime perf. // Both the safe and unsafe public methods share these helpers, @@ -341,7 +332,7 @@ unsafe impl const SliceIndex<[T]> for ops::IndexRange { // SAFETY: `self` is checked to be valid and in bounds above. unsafe { &*get_offset_len_noubcheck(slice, self.start(), self.len()) } } else { - slice_end_index_len_fail(self.end(), slice.len()) + slice_index_fail(self.start(), self.end(), slice.len()) } } @@ -351,7 +342,7 @@ unsafe impl const SliceIndex<[T]> for ops::IndexRange { // SAFETY: `self` is checked to be valid and in bounds above. unsafe { &mut *get_offset_len_mut_noubcheck(slice, self.start(), self.len()) } } else { - slice_end_index_len_fail(self.end(), slice.len()) + slice_index_fail(self.start(), self.end(), slice.len()) } } } @@ -436,26 +427,27 @@ unsafe impl const SliceIndex<[T]> for ops::Range { #[inline(always)] fn index(self, slice: &[T]) -> &[T] { // Using checked_sub is a safe way to get `SubUnchecked` in MIR - let Some(new_len) = usize::checked_sub(self.end, self.start) else { - slice_index_order_fail(self.start, self.end) - }; - if self.end > slice.len() { - slice_end_index_len_fail(self.end, slice.len()); + if let Some(new_len) = usize::checked_sub(self.end, self.start) + && self.end <= slice.len() + { + // SAFETY: `self` is checked to be valid and in bounds above. + unsafe { &*get_offset_len_noubcheck(slice, self.start, new_len) } + } else { + slice_index_fail(self.start, self.end, slice.len()) } - // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { &*get_offset_len_noubcheck(slice, self.start, new_len) } } #[inline] fn index_mut(self, slice: &mut [T]) -> &mut [T] { - let Some(new_len) = usize::checked_sub(self.end, self.start) else { - slice_index_order_fail(self.start, self.end) - }; - if self.end > slice.len() { - slice_end_index_len_fail(self.end, slice.len()); + // Using checked_sub is a safe way to get `SubUnchecked` in MIR + if let Some(new_len) = usize::checked_sub(self.end, self.start) + && self.end <= slice.len() + { + // SAFETY: `self` is checked to be valid and in bounds above. + unsafe { &mut *get_offset_len_mut_noubcheck(slice, self.start, new_len) } + } else { + slice_index_fail(self.start, self.end, slice.len()) } - // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { &mut *get_offset_len_mut_noubcheck(slice, self.start, new_len) } } } @@ -567,7 +559,7 @@ unsafe impl const SliceIndex<[T]> for ops::RangeFrom { #[inline] fn index(self, slice: &[T]) -> &[T] { if self.start > slice.len() { - slice_start_index_len_fail(self.start, slice.len()); + slice_index_fail(self.start, slice.len(), slice.len()) } // SAFETY: `self` is checked to be valid and in bounds above. unsafe { &*self.get_unchecked(slice) } @@ -576,7 +568,7 @@ unsafe impl const SliceIndex<[T]> for ops::RangeFrom { #[inline] fn index_mut(self, slice: &mut [T]) -> &mut [T] { if self.start > slice.len() { - slice_start_index_len_fail(self.start, slice.len()); + slice_index_fail(self.start, slice.len(), slice.len()) } // SAFETY: `self` is checked to be valid and in bounds above. unsafe { &mut *self.get_unchecked_mut(slice) } @@ -690,18 +682,32 @@ unsafe impl const SliceIndex<[T]> for ops::RangeInclusive { #[inline] fn index(self, slice: &[T]) -> &[T] { - if *self.end() == usize::MAX { - slice_end_index_overflow_fail(); + let Self { mut start, mut end, exhausted } = self; + let len = slice.len(); + if end < len { + end = end + 1; + start = if exhausted { end } else { start }; + if let Some(new_len) = usize::checked_sub(end, start) { + // SAFETY: `self` is checked to be valid and in bounds above. + unsafe { return &*get_offset_len_noubcheck(slice, start, new_len) } + } } - self.into_slice_range().index(slice) + slice_index_fail(start, end, slice.len()) } #[inline] fn index_mut(self, slice: &mut [T]) -> &mut [T] { - if *self.end() == usize::MAX { - slice_end_index_overflow_fail(); + let Self { mut start, mut end, exhausted } = self; + let len = slice.len(); + if end < len { + end = end + 1; + start = if exhausted { end } else { start }; + if let Some(new_len) = usize::checked_sub(end, start) { + // SAFETY: `self` is checked to be valid and in bounds above. + unsafe { return &mut *get_offset_len_mut_noubcheck(slice, start, new_len) } + } } - self.into_slice_range().index_mut(slice) + slice_index_fail(start, end, slice.len()) } } @@ -852,28 +858,26 @@ where { let len = bounds.end; - let start = match range.start_bound() { - ops::Bound::Included(&start) => start, - ops::Bound::Excluded(start) => { - start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail()) - } - ops::Bound::Unbounded => 0, - }; - let end = match range.end_bound() { - ops::Bound::Included(end) => { - end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail()) - } + ops::Bound::Included(&end) if end >= len => slice_index_fail(0, end, len), + // Cannot overflow because `end < len` implies `end < usize::MAX`. + ops::Bound::Included(&end) => end + 1, + + ops::Bound::Excluded(&end) if end > len => slice_index_fail(0, end, len), ops::Bound::Excluded(&end) => end, ops::Bound::Unbounded => len, }; - if start > end { - slice_index_order_fail(start, end); - } - if end > len { - slice_end_index_len_fail(end, len); - } + let start = match range.start_bound() { + ops::Bound::Excluded(&start) if start >= end => slice_index_fail(start, end, len), + // Cannot overflow because `start < end` implies `start < usize::MAX`. + ops::Bound::Excluded(&start) => start + 1, + + ops::Bound::Included(&start) if start > end => slice_index_fail(start, end, len), + ops::Bound::Included(&start) => start, + + ops::Bound::Unbounded => 0, + }; ops::Range { start, end } } @@ -982,25 +986,27 @@ pub(crate) fn into_slice_range( len: usize, (start, end): (ops::Bound, ops::Bound), ) -> ops::Range { - use ops::Bound; - let start = match start { - Bound::Included(start) => start, - Bound::Excluded(start) => { - start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail()) - } - Bound::Unbounded => 0, - }; - let end = match end { - Bound::Included(end) => { - end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail()) - } - Bound::Excluded(end) => end, - Bound::Unbounded => len, + ops::Bound::Included(end) if end >= len => slice_index_fail(0, end, len), + // Cannot overflow because `end < len` implies `end < usize::MAX`. + ops::Bound::Included(end) => end + 1, + + ops::Bound::Excluded(end) if end > len => slice_index_fail(0, end, len), + ops::Bound::Excluded(end) => end, + + ops::Bound::Unbounded => len, }; - // Don't bother with checking `start < end` and `end <= len` - // since these checks are handled by `Range` impls + let start = match start { + ops::Bound::Excluded(start) if start >= end => slice_index_fail(start, end, len), + // Cannot overflow because `start < end` implies `start < usize::MAX`. + ops::Bound::Excluded(start) => start + 1, + + ops::Bound::Included(start) if start > end => slice_index_fail(start, end, len), + ops::Bound::Included(start) => start, + + ops::Bound::Unbounded => 0, + }; start..end } diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index 992f24cb18f..110c4e5f3b4 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -1492,28 +1492,28 @@ mod slice_index { // note: using 0 specifically ensures that the result of overflowing is 0..0, // so that `get` doesn't simply return None for the wrong reason. bad: data[0 ..= usize::MAX]; - message: "maximum usize"; + message: "out of range"; } in mod rangetoinclusive_overflow { data: [0, 1]; bad: data[..= usize::MAX]; - message: "maximum usize"; + message: "out of range"; } in mod boundpair_overflow_end { data: [0; 1]; bad: data[(Bound::Unbounded, Bound::Included(usize::MAX))]; - message: "maximum usize"; + message: "out of range"; } in mod boundpair_overflow_start { data: [0; 1]; bad: data[(Bound::Excluded(usize::MAX), Bound::Unbounded)]; - message: "maximum usize"; + message: "out of range"; } } // panic_cases! } @@ -2008,7 +2008,7 @@ fn test_copy_within_panics_src_inverted() { bytes.copy_within(2..1, 0); } #[test] -#[should_panic(expected = "attempted to index slice up to maximum usize")] +#[should_panic(expected = "out of range")] fn test_copy_within_panics_src_out_of_bounds() { let mut bytes = *b"Hello, World!"; // an inclusive range ending at usize::MAX would make src_end overflow diff --git a/src/tools/miri/tests/panic/oob_subslice.stderr b/src/tools/miri/tests/panic/oob_subslice.stderr index f8270f4ad4d..e1e5bd33d31 100644 --- a/src/tools/miri/tests/panic/oob_subslice.stderr +++ b/src/tools/miri/tests/panic/oob_subslice.stderr @@ -1,5 +1,5 @@ thread 'main' ($TID) panicked at tests/panic/oob_subslice.rs:LL:CC: -range end index 5 out of range for slice of length 4 +range end index 4 out of range for slice of length 4 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect diff --git a/tests/codegen-llvm/binary-search-index-no-bound-check.rs b/tests/codegen-llvm/binary-search-index-no-bound-check.rs index d59c0beec64..8322c4179bd 100644 --- a/tests/codegen-llvm/binary-search-index-no-bound-check.rs +++ b/tests/codegen-llvm/binary-search-index-no-bound-check.rs @@ -8,8 +8,7 @@ #[no_mangle] pub fn binary_search_index_no_bounds_check(s: &[u8]) -> u8 { // CHECK-NOT: panic - // CHECK-NOT: slice_start_index_len_fail - // CHECK-NOT: slice_end_index_len_fail + // CHECK-NOT: slice_index_fail // CHECK-NOT: panic_bounds_check if let Ok(idx) = s.binary_search(&b'\\') { s[idx] } else { 42 } } diff --git a/tests/codegen-llvm/integer-overflow.rs b/tests/codegen-llvm/integer-overflow.rs index 80362247a86..df7845be06d 100644 --- a/tests/codegen-llvm/integer-overflow.rs +++ b/tests/codegen-llvm/integer-overflow.rs @@ -10,7 +10,7 @@ pub struct S1<'a> { // CHECK-LABEL: @slice_no_index_order #[no_mangle] pub fn slice_no_index_order<'a>(s: &'a mut S1, n: usize) -> &'a [u8] { - // CHECK-NOT: slice_index_order_fail + // CHECK-COUNT-1: slice_index_fail let d = &s.data[s.position..s.position + n]; s.position += n; return d; @@ -19,6 +19,6 @@ pub fn slice_no_index_order<'a>(s: &'a mut S1, n: usize) -> &'a [u8] { // CHECK-LABEL: @test_check #[no_mangle] pub fn test_check<'a>(s: &'a mut S1, x: usize, y: usize) -> &'a [u8] { - // CHECK: slice_index_order_fail + // CHECK-COUNT-1: slice_index_fail &s.data[x..y] } diff --git a/tests/codegen-llvm/issues/issue-113757-bounds-check-after-cmp-max.rs b/tests/codegen-llvm/issues/issue-113757-bounds-check-after-cmp-max.rs index d495adf9980..0db8a5220ec 100644 --- a/tests/codegen-llvm/issues/issue-113757-bounds-check-after-cmp-max.rs +++ b/tests/codegen-llvm/issues/issue-113757-bounds-check-after-cmp-max.rs @@ -5,7 +5,7 @@ use std::cmp::max; // CHECK-LABEL: @foo -// CHECK-NOT: slice_start_index_len_fail +// CHECK-NOT: slice_index_fail // CHECK-NOT: unreachable #[no_mangle] pub fn foo(v: &mut Vec, size: usize) -> Option<&mut [u8]> { diff --git a/tests/codegen-llvm/issues/issue-27130.rs b/tests/codegen-llvm/issues/issue-27130.rs index 594e02af097..3e53c5cffd6 100644 --- a/tests/codegen-llvm/issues/issue-27130.rs +++ b/tests/codegen-llvm/issues/issue-27130.rs @@ -6,7 +6,7 @@ #[no_mangle] pub fn trim_in_place(a: &mut &[u8]) { while a.first() == Some(&42) { - // CHECK-NOT: slice_index_order_fail + // CHECK-NOT: slice_index_fail *a = &a[1..]; } } @@ -15,7 +15,7 @@ pub fn trim_in_place(a: &mut &[u8]) { #[no_mangle] pub fn trim_in_place2(a: &mut &[u8]) { while let Some(&42) = a.first() { - // CHECK-NOT: slice_index_order_fail + // CHECK-COUNT-1: slice_index_fail *a = &a[2..]; } } diff --git a/tests/codegen-llvm/issues/issue-69101-bounds-check.rs b/tests/codegen-llvm/issues/issue-69101-bounds-check.rs index 953b79aa263..f1857a9ce89 100644 --- a/tests/codegen-llvm/issues/issue-69101-bounds-check.rs +++ b/tests/codegen-llvm/issues/issue-69101-bounds-check.rs @@ -10,7 +10,7 @@ // CHECK-LABEL: @already_sliced_no_bounds_check #[no_mangle] pub fn already_sliced_no_bounds_check(a: &[u8], b: &[u8], c: &mut [u8]) { - // CHECK: slice_end_index_len_fail + // CHECK: slice_index_fail // CHECK-NOT: panic_bounds_check let _ = (&a[..2048], &b[..2048], &mut c[..2048]); for i in 0..1024 { @@ -21,7 +21,7 @@ pub fn already_sliced_no_bounds_check(a: &[u8], b: &[u8], c: &mut [u8]) { // CHECK-LABEL: @already_sliced_no_bounds_check_exact #[no_mangle] pub fn already_sliced_no_bounds_check_exact(a: &[u8], b: &[u8], c: &mut [u8]) { - // CHECK: slice_end_index_len_fail + // CHECK: slice_index_fail // CHECK-NOT: panic_bounds_check let _ = (&a[..1024], &b[..1024], &mut c[..1024]); for i in 0..1024 { @@ -33,7 +33,7 @@ pub fn already_sliced_no_bounds_check_exact(a: &[u8], b: &[u8], c: &mut [u8]) { // CHECK-LABEL: @already_sliced_bounds_check #[no_mangle] pub fn already_sliced_bounds_check(a: &[u8], b: &[u8], c: &mut [u8]) { - // CHECK: slice_end_index_len_fail + // CHECK: slice_index_fail // CHECK: panic_bounds_check let _ = (&a[..1023], &b[..2048], &mut c[..2048]); for i in 0..1024 { diff --git a/tests/codegen-llvm/issues/issue-73396-bounds-check-after-position.rs b/tests/codegen-llvm/issues/issue-73396-bounds-check-after-position.rs index 1e2c25babe0..8a2200478aa 100644 --- a/tests/codegen-llvm/issues/issue-73396-bounds-check-after-position.rs +++ b/tests/codegen-llvm/issues/issue-73396-bounds-check-after-position.rs @@ -8,8 +8,7 @@ #[no_mangle] pub fn position_slice_to_no_bounds_check(s: &[u8]) -> &[u8] { // CHECK-NOT: panic - // CHECK-NOT: slice_start_index_len_fail - // CHECK-NOT: slice_end_index_len_fail + // CHECK-NOT: slice_index_fail // CHECK-NOT: panic_bounds_check // CHECK-NOT: unreachable if let Some(idx) = s.iter().position(|b| *b == b'\\') { &s[..idx] } else { s } @@ -19,8 +18,7 @@ pub fn position_slice_to_no_bounds_check(s: &[u8]) -> &[u8] { #[no_mangle] pub fn position_slice_from_no_bounds_check(s: &[u8]) -> &[u8] { // CHECK-NOT: panic - // CHECK-NOT: slice_start_index_len_fail - // CHECK-NOT: slice_end_index_len_fail + // CHECK-NOT: slice_index_fail // CHECK-NOT: panic_bounds_check // CHECK-NOT: unreachable if let Some(idx) = s.iter().position(|b| *b == b'\\') { &s[idx..] } else { s } @@ -30,8 +28,7 @@ pub fn position_slice_from_no_bounds_check(s: &[u8]) -> &[u8] { #[no_mangle] pub fn position_index_no_bounds_check(s: &[u8]) -> u8 { // CHECK-NOT: panic - // CHECK-NOT: slice_start_index_len_fail - // CHECK-NOT: slice_end_index_len_fail + // CHECK-NOT: slice_index_fail // CHECK-NOT: panic_bounds_check // CHECK-NOT: unreachable if let Some(idx) = s.iter().position(|b| *b == b'\\') { s[idx] } else { 42 } @@ -40,8 +37,7 @@ pub fn position_index_no_bounds_check(s: &[u8]) -> u8 { #[no_mangle] pub fn rposition_slice_to_no_bounds_check(s: &[u8]) -> &[u8] { // CHECK-NOT: panic - // CHECK-NOT: slice_start_index_len_fail - // CHECK-NOT: slice_end_index_len_fail + // CHECK-NOT: slice_index_fail // CHECK-NOT: panic_bounds_check // CHECK-NOT: unreachable if let Some(idx) = s.iter().rposition(|b| *b == b'\\') { &s[..idx] } else { s } @@ -51,8 +47,7 @@ pub fn rposition_slice_to_no_bounds_check(s: &[u8]) -> &[u8] { #[no_mangle] pub fn rposition_slice_from_no_bounds_check(s: &[u8]) -> &[u8] { // CHECK-NOT: panic - // CHECK-NOT: slice_start_index_len_fail - // CHECK-NOT: slice_end_index_len_fail + // CHECK-NOT: slice_index_fail // CHECK-NOT: panic_bounds_check // CHECK-NOT: unreachable if let Some(idx) = s.iter().rposition(|b| *b == b'\\') { &s[idx..] } else { s } @@ -62,8 +57,7 @@ pub fn rposition_slice_from_no_bounds_check(s: &[u8]) -> &[u8] { #[no_mangle] pub fn rposition_index_no_bounds_check(s: &[u8]) -> u8 { // CHECK-NOT: panic - // CHECK-NOT: slice_start_index_len_fail - // CHECK-NOT: slice_end_index_len_fail + // CHECK-NOT: slice_index_fail // CHECK-NOT: panic_bounds_check // CHECK-NOT: unreachable if let Some(idx) = s.iter().rposition(|b| *b == b'\\') { s[idx] } else { 42 } diff --git a/tests/codegen-llvm/slice-last-elements-optimization.rs b/tests/codegen-llvm/slice-last-elements-optimization.rs index b90f91d7b17..d982cda709d 100644 --- a/tests/codegen-llvm/slice-last-elements-optimization.rs +++ b/tests/codegen-llvm/slice-last-elements-optimization.rs @@ -1,19 +1,18 @@ //@ compile-flags: -Copt-level=3 -//@ only-x86_64 //@ min-llvm-version: 20 #![crate_type = "lib"] // This test verifies that LLVM 20 properly optimizes the bounds check // when accessing the last few elements of a slice with proper conditions. // Previously, this would generate an unreachable branch to -// slice_start_index_len_fail even when the bounds check was provably safe. +// slice_index_fail even when the bounds check was provably safe. // CHECK-LABEL: @last_four_initial( #[no_mangle] pub fn last_four_initial(s: &[u8]) -> &[u8] { - // Previously this would generate a branch to slice_start_index_len_fail + // Previously this would generate a branch to slice_index_fail // that is unreachable. The LLVM 20 fix should eliminate this branch. - // CHECK-NOT: slice_start_index_len_fail + // CHECK-NOT: slice_index_fail // CHECK-NOT: unreachable let start = if s.len() <= 4 { 0 } else { s.len() - 4 }; &s[start..] @@ -23,7 +22,7 @@ pub fn last_four_initial(s: &[u8]) -> &[u8] { #[no_mangle] pub fn last_four_optimized(s: &[u8]) -> &[u8] { // This version was already correctly optimized before the fix in LLVM 20. - // CHECK-NOT: slice_start_index_len_fail + // CHECK-NOT: slice_index_fail // CHECK-NOT: unreachable if s.len() <= 4 { &s[0..] } else { &s[s.len() - 4..] } } @@ -32,6 +31,6 @@ pub fn last_four_optimized(s: &[u8]) -> &[u8] { // CHECK-LABEL: @test_bounds_check_happens( #[no_mangle] pub fn test_bounds_check_happens(s: &[u8], i: usize) -> &[u8] { - // CHECK: slice_start_index_len_fail + // CHECK: slice_index_fail &s[i..] } diff --git a/tests/codegen-llvm/slice-reverse.rs b/tests/codegen-llvm/slice-reverse.rs index e58d1c1d9d8..c31cff5010b 100644 --- a/tests/codegen-llvm/slice-reverse.rs +++ b/tests/codegen-llvm/slice-reverse.rs @@ -8,10 +8,10 @@ #[no_mangle] pub fn slice_reverse_u8(slice: &mut [u8]) { // CHECK-NOT: panic_bounds_check - // CHECK-NOT: slice_end_index_len_fail + // CHECK-NOT: slice_index_fail // CHECK: shufflevector <{{[0-9]+}} x i8> // CHECK-NOT: panic_bounds_check - // CHECK-NOT: slice_end_index_len_fail + // CHECK-NOT: slice_index_fail slice.reverse(); } @@ -19,9 +19,9 @@ pub fn slice_reverse_u8(slice: &mut [u8]) { #[no_mangle] pub fn slice_reverse_i32(slice: &mut [i32]) { // CHECK-NOT: panic_bounds_check - // CHECK-NOT: slice_end_index_len_fail + // CHECK-NOT: slice_index_fail // CHECK: shufflevector <{{[0-9]+}} x i32> // CHECK-NOT: panic_bounds_check - // CHECK-NOT: slice_end_index_len_fail + // CHECK-NOT: slice_index_fail slice.reverse(); } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir index 731f6438a6e..2df2c4b85b8 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir @@ -4,14 +4,81 @@ fn slice_index_range(_1: &[u32], _2: std::ops::Range) -> &[u32] { debug slice => _1; debug index => _2; let mut _0: &[u32]; + let mut _3: usize; + let mut _4: usize; scope 1 (inlined #[track_caller] core::slice::index::> for [u32]>::index) { + scope 2 (inlined #[track_caller] as SliceIndex<[u32]>>::index) { + let mut _7: usize; + let mut _8: bool; + let mut _9: *const [u32]; + let _12: *const [u32]; + let mut _13: usize; + let mut _14: !; + scope 3 (inlined core::num::::checked_sub) { + let mut _5: bool; + let mut _6: usize; + } + scope 4 (inlined core::slice::index::get_offset_len_noubcheck::) { + let _10: *const u32; + scope 5 { + let _11: *const u32; + scope 6 { + } + } + } + } } bb0: { - _0 = as SliceIndex<[u32]>>::index(move _2, move _1) -> [return: bb1, unwind unreachable]; + _3 = move (_2.0: usize); + _4 = move (_2.1: usize); + StorageLive(_5); + _5 = Lt(copy _4, copy _3); + switchInt(move _5) -> [0: bb1, otherwise: bb4]; } bb1: { + _6 = SubUnchecked(copy _4, copy _3); + StorageDead(_5); + StorageLive(_8); + StorageLive(_7); + _7 = PtrMetadata(copy _1); + _8 = Le(copy _4, move _7); + switchInt(move _8) -> [0: bb2, otherwise: bb3]; + } + + bb2: { + StorageDead(_7); + goto -> bb5; + } + + bb3: { + StorageDead(_7); + StorageLive(_12); + StorageLive(_9); + _9 = &raw const (*_1); + StorageLive(_10); + StorageLive(_11); + _10 = copy _9 as *const u32 (PtrToPtr); + _11 = Offset(copy _10, copy _3); + _12 = *const [u32] from (copy _11, copy _6); + StorageDead(_11); + StorageDead(_10); + StorageDead(_9); + _0 = &(*_12); + StorageDead(_12); + StorageDead(_8); return; } + + bb4: { + StorageDead(_5); + goto -> bb5; + } + + bb5: { + StorageLive(_13); + _13 = PtrMetadata(copy _1); + _14 = core::slice::index::slice_index_fail(move _3, move _4, move _13) -> unwind unreachable; + } } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir index d879d06bb4e..d4b86b9633a 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir @@ -4,14 +4,81 @@ fn slice_index_range(_1: &[u32], _2: std::ops::Range) -> &[u32] { debug slice => _1; debug index => _2; let mut _0: &[u32]; + let mut _3: usize; + let mut _4: usize; scope 1 (inlined #[track_caller] core::slice::index::> for [u32]>::index) { + scope 2 (inlined #[track_caller] as SliceIndex<[u32]>>::index) { + let mut _7: usize; + let mut _8: bool; + let mut _9: *const [u32]; + let _12: *const [u32]; + let mut _13: usize; + let mut _14: !; + scope 3 (inlined core::num::::checked_sub) { + let mut _5: bool; + let mut _6: usize; + } + scope 4 (inlined core::slice::index::get_offset_len_noubcheck::) { + let _10: *const u32; + scope 5 { + let _11: *const u32; + scope 6 { + } + } + } + } } bb0: { - _0 = as SliceIndex<[u32]>>::index(move _2, move _1) -> [return: bb1, unwind continue]; + _3 = move (_2.0: usize); + _4 = move (_2.1: usize); + StorageLive(_5); + _5 = Lt(copy _4, copy _3); + switchInt(move _5) -> [0: bb1, otherwise: bb4]; } bb1: { + _6 = SubUnchecked(copy _4, copy _3); + StorageDead(_5); + StorageLive(_8); + StorageLive(_7); + _7 = PtrMetadata(copy _1); + _8 = Le(copy _4, move _7); + switchInt(move _8) -> [0: bb2, otherwise: bb3]; + } + + bb2: { + StorageDead(_7); + goto -> bb5; + } + + bb3: { + StorageDead(_7); + StorageLive(_12); + StorageLive(_9); + _9 = &raw const (*_1); + StorageLive(_10); + StorageLive(_11); + _10 = copy _9 as *const u32 (PtrToPtr); + _11 = Offset(copy _10, copy _3); + _12 = *const [u32] from (copy _11, copy _6); + StorageDead(_11); + StorageDead(_10); + StorageDead(_9); + _0 = &(*_12); + StorageDead(_12); + StorageDead(_8); return; } + + bb4: { + StorageDead(_5); + goto -> bb5; + } + + bb5: { + StorageLive(_13); + _13 = PtrMetadata(copy _1); + _14 = core::slice::index::slice_index_fail(move _3, move _4, move _13) -> unwind continue; + } } -- cgit 1.4.1-3-g733a5