diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index e8b4cb343794c..e021761a59231 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -541,6 +541,21 @@ impl NoArgsAttributeParser for RustcNoMirInlineParser { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoMirInline; } +pub(crate) struct RustcNoWritableParser; + +impl NoArgsAttributeParser for RustcNoWritableParser { + const PATH: &[Symbol] = &[sym::rustc_no_writable]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Closure), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Method(MethodKind::Trait { body: true })), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoWritable; +} + pub(crate) struct RustcLintQueryInstabilityParser; impl NoArgsAttributeParser for RustcLintQueryInstabilityParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 259a73de59853..4fef40481259e 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -309,6 +309,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index a6e841e440a21..935906669b9e3 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -38,11 +38,12 @@ trait ArgAttributesExt { const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 1] = [(ArgAttribute::InReg, llvm::AttributeKind::InReg)]; -const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 4] = [ +const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 5] = [ (ArgAttribute::NoAlias, llvm::AttributeKind::NoAlias), (ArgAttribute::NonNull, llvm::AttributeKind::NonNull), (ArgAttribute::ReadOnly, llvm::AttributeKind::ReadOnly), (ArgAttribute::NoUndef, llvm::AttributeKind::NoUndef), + (ArgAttribute::Writable, llvm::AttributeKind::Writable), ]; const CAPTURES_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 3] = [ diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index d74450e67cf1d..7fb68b795c96b 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1414,6 +1414,9 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_scalable_vector, Normal, template!(List: &["count"]), WarnFollowing, EncodeCrossCrate::Yes, "`#[rustc_scalable_vector]` defines a scalable vector type" ), + rustc_attr!( + rustc_no_writable, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, "`#[rustc_no_writable]` stops the compiler from adding the `writable` flag in LLVM, thus under Tree Borrows, mutable retags no longer count as writes" + ), // ========================================================================== // Internal attributes, Testing: diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index a18ddff947099..fa26ac8cfd677 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1489,6 +1489,9 @@ pub enum AttributeKind { /// Represents `#[rustc_no_mir_inline]` RustcNoMirInline, + /// Represents `#[rustc_no_writable]` + RustcNoWritable, + /// Represents `#[rustc_non_const_trait_method]`. RustcNonConstTraitMethod, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index c19fc6976c6e6..c49629a2504a8 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -161,6 +161,7 @@ impl AttributeKind { RustcNoImplicitAutorefs => Yes, RustcNoImplicitBounds => No, RustcNoMirInline => Yes, + RustcNoWritable => Yes, RustcNonConstTraitMethod => No, // should be reported via other queries like `constness` RustcNonnullOptimizationGuaranteed => Yes, RustcNounwind => No, diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index f9d583bdf18c7..ba46751e68db8 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -834,6 +834,7 @@ fn test_unstable_options_tracking_hash() { tracked!(no_profiler_runtime, true); tracked!(no_trait_vptr, true); tracked!(no_unique_section_names, true); + tracked!(no_writable, true); tracked!(offload, vec![Offload::Device]); tracked!(on_broken_pipe, OnBrokenPipe::Kill); tracked!(osx_rpath_install_name, true); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 9945802d70c14..bc31b281e986b 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -357,6 +357,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcNoImplicitAutorefs | AttributeKind::RustcNoImplicitBounds | AttributeKind::RustcNoMirInline + | AttributeKind::RustcNoWritable | AttributeKind::RustcNonConstTraitMethod | AttributeKind::RustcNonnullOptimizationGuaranteed | AttributeKind::RustcNounwind diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index fb1b3c8679481..fb97015560090 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2530,6 +2530,8 @@ options! { "disable generation of trait vptr in vtable for upcasting"), no_unique_section_names: bool = (false, parse_bool, [TRACKED], "do not use unique names for text and data sections when -Z function-sections is used"), + no_writable: bool = (false, parse_bool, [TRACKED], + "do not insert the writable LLVM attribute; mutable retags don't count as writes under Tree Borrows"), normalize_docs: bool = (false, parse_bool, [TRACKED], "normalize associated items in rustdoc when generating documentation"), offload: Vec = (Vec::new(), parse_offload, [TRACKED], diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 738c9b975fd00..d887fdea52b5f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1751,6 +1751,7 @@ symbols! { rustc_no_implicit_autorefs, rustc_no_implicit_bounds, rustc_no_mir_inline, + rustc_no_writable, rustc_non_const_trait_method, rustc_nonnull_optimization_guaranteed, rustc_nounwind, diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 7dc270795281a..f8c82faaf8bd8 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -110,9 +110,9 @@ mod attr_impl { // The subset of llvm::Attribute needed for arguments, packed into a bitfield. #[derive(Clone, Copy, Default, Hash, PartialEq, Eq, HashStable_Generic)] - pub struct ArgAttribute(u8); + pub struct ArgAttribute(u16); bitflags::bitflags! { - impl ArgAttribute: u8 { + impl ArgAttribute: u16 { const CapturesNone = 0b111; const CapturesAddress = 0b110; const CapturesReadOnly = 0b100; @@ -121,6 +121,7 @@ mod attr_impl { const ReadOnly = 1 << 5; const InReg = 1 << 6; const NoUndef = 1 << 7; + const Writable = 1 << 8; } } 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 5008794bcb191..54b6031a80a66 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -2,8 +2,8 @@ use std::{assert_matches, iter}; use rustc_abi::Primitive::Pointer; use rustc_abi::{Align, BackendRepr, ExternAbi, PointerKind, Scalar, Size}; -use rustc_hir as hir; use rustc_hir::lang_items::LangItem; +use rustc_hir::{self as hir, find_attr}; use rustc_middle::bug; use rustc_middle::middle::deduced_param_attrs::DeducedParamAttrs; use rustc_middle::query::Providers; @@ -355,6 +355,7 @@ fn arg_attrs_for_rust_scalar<'tcx>( offset: Size, is_return: bool, drop_target_pointee: Option>, + def_id: Option, ) -> ArgAttributes { let mut attrs = ArgAttributes::new(); @@ -430,6 +431,15 @@ fn arg_attrs_for_rust_scalar<'tcx>( // (see ). if no_alias && !is_return { attrs.set(ArgAttribute::NoAlias); + + // set writable if no_alias is set, it's a mutable reference and the feature is not disabled + let no_writable = match def_id { + Some(def_id) => find_attr!(tcx, def_id, RustcNoWritable), + None => false, // If no def_id exists, there can't exist an attribute for that def_id so rustc_no_writable can't be set + } || tcx.sess.opts.unstable_opts.no_writable; + if matches!(kind, PointerKind::MutableRef { .. }) && !no_writable { + attrs.set(ArgAttribute::Writable); + } } if matches!(kind, PointerKind::SharedRef { frozen: true }) && !is_return { @@ -624,6 +634,7 @@ fn fn_abi_new_uncached<'tcx>( // Only set `drop_target_pointee` for the data part of a wide pointer. // See `arg_attrs_for_rust_scalar` docs for more information. drop_target_pointee.filter(|_| offset == Size::ZERO), + determined_fn_def_id, ) })) }; diff --git a/src/doc/unstable-book/src/compiler-flags/no-writable.md b/src/doc/unstable-book/src/compiler-flags/no-writable.md new file mode 100644 index 0000000000000..164b82d2cc24e --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/no-writable.md @@ -0,0 +1,5 @@ +# `no-writable` + +--- + +This flag will globally stop the compiler from inserting the [writable](https://llvm.org/docs/LangRef.html#writable) LLVM flag. It also stops [Miri](github.com/rust-lang/miri) from testing for undefined behavior when inserting writes. It has the same effect as applying `#[rustc_no_writable]` to every function. diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 4808e53698c68..7c6eb00082063 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -529,6 +529,7 @@ Some native rustc `-Z` flags are also very relevant for Miri: sets this flag per default. * `-Zmir-emit-retag` controls whether `Retag` statements are emitted. Miri enables this per default because it is needed for [Stacked Borrows] and [Tree Borrows]. +* `-Zno-writable` disables the strong mode globally, thus disabling the tracking of spurious writes. This also stops the compiler from adding the `writable` attribute. This only has an effect in Miri if [Tree Borrows] are enabled. Moreover, Miri recognizes some environment variables: diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index e73c870508bd0..3a8dae22f4cbf 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -519,6 +519,7 @@ fn main() -> ExitCode { miri_config.borrow_tracker = Some(BorrowTrackerMethod::TreeBorrows(TreeBorrowsParams { precise_interior_mut: true, + writable: true, })); } else if arg == "-Zmiri-tree-borrows-no-precise-interior-mut" { match &mut miri_config.borrow_tracker { @@ -530,6 +531,16 @@ fn main() -> ExitCode { "`-Zmiri-tree-borrows` is required before `-Zmiri-tree-borrows-no-precise-interior-mut`" ), }; + } else if arg == "-Zno-writable" { + match &mut miri_config.borrow_tracker { + Some(BorrowTrackerMethod::TreeBorrows(params)) => { + params.writable = false; + } + _ => + eprintln!( + "Warning: `-Zno-writable` only has an effect in Miri if `-Zmiri-tree-borrows` is before it. The flag still will have an effect in the compiler." + ), + }; } else if arg == "-Zmiri-disable-data-race-detector" { miri_config.data_race_detector = false; miri_config.weak_memory_emulation = false; diff --git a/src/tools/miri/src/borrow_tracker/mod.rs b/src/tools/miri/src/borrow_tracker/mod.rs index 9aa0ce48e09a0..3f392bd6687be 100644 --- a/src/tools/miri/src/borrow_tracker/mod.rs +++ b/src/tools/miri/src/borrow_tracker/mod.rs @@ -224,6 +224,7 @@ pub enum BorrowTrackerMethod { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct TreeBorrowsParams { pub precise_interior_mut: bool, + pub writable: bool, } impl BorrowTrackerMethod { diff --git a/tests/assembly-llvm/reg-struct-return.rs b/tests/assembly-llvm/reg-struct-return.rs index d364954abe30d..e61cb4d62a265 100644 --- a/tests/assembly-llvm/reg-struct-return.rs +++ b/tests/assembly-llvm/reg-struct-return.rs @@ -13,6 +13,7 @@ //@ needs-llvm-components: x86 #![feature(no_core)] +#![feature(rustc_attrs)] #![no_std] #![no_core] #![crate_type = "lib"] @@ -131,6 +132,7 @@ mod Large { } #[unsafe(no_mangle)] + #[rustc_no_writable] // as we do not want to test this here, disable it pub unsafe extern "C" fn large_caller(dst: &mut LargeStruct) { // CHECK-LABEL: large_caller // CHECK: calll large diff --git a/tests/codegen-llvm/drop-in-place-noalias.rs b/tests/codegen-llvm/drop-in-place-noalias.rs index bff2f52781f23..cb834b51cc778 100644 --- a/tests/codegen-llvm/drop-in-place-noalias.rs +++ b/tests/codegen-llvm/drop-in-place-noalias.rs @@ -7,7 +7,7 @@ use std::marker::PhantomPinned; -// CHECK: define internal void @{{.*}}core{{.*}}ptr{{.*}}drop_in_place{{.*}}StructUnpin{{.*}}(ptr noalias noundef align 4 dereferenceable(12) %{{.+}}) +// CHECK: define internal void @{{.*}}core{{.*}}ptr{{.*}}drop_in_place{{.*}}StructUnpin{{.*}}(ptr noalias noundef writable align 4 dereferenceable(12) %{{.+}}) // CHECK: define internal void @{{.*}}core{{.*}}ptr{{.*}}drop_in_place{{.*}}StructNotUnpin{{.*}}(ptr noundef nonnull align 4 %{{.+}}) diff --git a/tests/codegen-llvm/function-arguments.rs b/tests/codegen-llvm/function-arguments.rs index 80e6ac7bb0f03..d46c9fce9dc0e 100644 --- a/tests/codegen-llvm/function-arguments.rs +++ b/tests/codegen-llvm/function-arguments.rs @@ -106,16 +106,22 @@ pub fn named_borrow<'r>(_: &'r i32) {} #[no_mangle] pub fn unsafe_borrow(_: &UnsafeInner) {} -// CHECK: @mutable_unsafe_borrow(ptr noalias noundef align 2 dereferenceable(2) %_1) +// CHECK: @mutable_unsafe_borrow(ptr noalias noundef writable align 2 dereferenceable(2) %_1) // ... unless this is a mutable borrow, those never alias #[no_mangle] pub fn mutable_unsafe_borrow(_: &mut UnsafeInner) {} -// CHECK: @mutable_borrow(ptr noalias noundef align 4 dereferenceable(4) %_1) +// CHECK: @mutable_borrow(ptr noalias noundef writable align 4 dereferenceable(4) %_1) // FIXME #25759 This should also have `nocapture` #[no_mangle] pub fn mutable_borrow(_: &mut i32) {} +// CHECK: @mutable_borrow_no_writable(ptr noalias noundef align 4 dereferenceable(4) %_1) +// checks that rustc_no_writable removes the writable attribute +#[no_mangle] +#[rustc_no_writable] +pub fn mutable_borrow_no_writable(_: &mut i32) {} + // CHECK: noundef nonnull align 4 ptr @mutable_borrow_ret() #[no_mangle] pub fn mutable_borrow_ret() -> &'static mut i32 { @@ -147,7 +153,7 @@ pub fn borrowed_struct(_: &S) {} #[no_mangle] pub fn option_borrow(_x: Option<&i32>) {} -// CHECK: @option_borrow_mut(ptr noalias noundef align 4 dereferenceable_or_null(4) %_x) +// CHECK: @option_borrow_mut(ptr noalias noundef writable align 4 dereferenceable_or_null(4) %_x) #[no_mangle] pub fn option_borrow_mut(_x: Option<&mut i32>) {} @@ -216,7 +222,7 @@ pub fn helper(_: usize) {} pub fn slice(_: &[u8]) {} // CHECK: @mutable_slice( -// CHECK-SAME: ptr noalias noundef nonnull %_1.0, +// CHECK-SAME: ptr noalias noundef nonnull writable %_1.0, // CHECK-SAME: [[USIZE]] noundef range({{i32 0, -2147483648|i64 0, -9223372036854775808}}) %_1.1) // FIXME #25759 This should also have `nocapture` #[no_mangle] @@ -271,7 +277,7 @@ pub fn trait_box_pin2(_: Box) {} // Same for mutable references (with a non-zero minimal size so that we also see the // `dereferenceable` disappear). -// CHECK: @trait_mutref(ptr noalias noundef align 4 dereferenceable(4){{( %_1.0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %_1.1)?}}) +// CHECK: @trait_mutref(ptr noalias noundef writable align 4 dereferenceable(4){{( %_1.0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %_1.1)?}}) #[no_mangle] pub fn trait_mutref(_: &mut (i32, dyn Drop + Unpin + UnsafeUnpin)) {} // CHECK: @trait_mutref_pin1(ptr noundef nonnull align 4{{( %_1.0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %_1.1)?}}) diff --git a/tests/codegen-llvm/no-writable.rs b/tests/codegen-llvm/no-writable.rs new file mode 100644 index 0000000000000..b00a6fb795346 --- /dev/null +++ b/tests/codegen-llvm/no-writable.rs @@ -0,0 +1,11 @@ +//! The tests here test that the `-Zno-writable` flag has the desired effect. +//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes -Zno-writable +#![crate_type = "lib"] + +// CHECK: @mutable_borrow(ptr noalias noundef align 4 dereferenceable(4) %_1) +#[no_mangle] +pub fn mutable_borrow(_: &mut i32) {} + +// CHECK: @option_borrow_mut(ptr noalias noundef align 4 dereferenceable_or_null(4) %_x) +#[no_mangle] +pub fn option_borrow_mut(_x: Option<&mut i32>) {}