Skip to content

Commit

Permalink
Auto merge of rust-lang#106294 - Nilstrieb:noundef-everything, r=nikic
Browse files Browse the repository at this point in the history
Put `noundef` on all scalars that don't allow uninit

Previously, it was only put on scalars with range validity invariants like bool, was uninit was obviously invalid for those.

Since then, we have normatively declared all uninit primitives to be undefined behavior and can therefore put `noundef` on them.

The remaining concern was the `mem::uninitialized` function, which cause quite a lot of UB in the older parts of the ecosystem. After rust-lang#99182, this function now doesn't return uninit values anymore, making users of it safe from this change.

The only real sources of UB where people could encounter uninit primitives are `MaybeUninit::uninit().assume_init()`, which has always be clear in the docs about being UB and from heap allocations (like reading from the spare capacity of a vec). This is hopefully rare enough to not break anything.

cc `@nagisa` `@scottmcm` `@nikic`
  • Loading branch information
bors committed Jan 17, 2023
2 parents 38a76f3 + f125538 commit 3984bc5
Show file tree
Hide file tree
Showing 39 changed files with 130 additions and 109 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
layout: TyAndLayout<'tcx>,
offset: Size,
) {
if !scalar.is_always_valid(bx) {
if !scalar.is_uninit_valid() {
bx.noundef_metadata(load);
}

Expand Down
8 changes: 1 addition & 7 deletions compiler/rustc_ty_utils/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,7 @@ fn adjust_for_rust_scalar<'tcx>(
return;
}

// Scalars which have invalid values cannot be undef.
if !scalar.is_always_valid(&cx) {
if !scalar.is_uninit_valid() {
attrs.set(ArgAttribute::NoUndef);
}

Expand All @@ -246,11 +245,6 @@ fn adjust_for_rust_scalar<'tcx>(
PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO,
};

// `Box`, `&T`, and `&mut T` cannot be undef.
// Note that this only applies to the value of the pointer itself;
// this attribute doesn't make it UB for the pointed-to data to be undef.
attrs.set(ArgAttribute::NoUndef);

// The aliasing rules for `Box<T>` are still not decided, but currently we emit
// `noalias` for it. This can be turned off using an unstable flag.
// See https://github.com/rust-lang/unsafe-code-guidelines/issues/326
Expand Down
2 changes: 1 addition & 1 deletion tests/codegen/abi-sysv64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// of the sysv64 abi.
//
// needs-llvm-components: x86
// compile-flags: -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu
// compile-flags: -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu -Copt-level=0

#![crate_type = "lib"]
#![no_core]
Expand Down
2 changes: 1 addition & 1 deletion tests/codegen/abi-x86-interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// of the x86-interrupt abi.

// needs-llvm-components: x86
// compile-flags: -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu
// compile-flags: -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu -Copt-level=0

#![crate_type = "lib"]
#![no_core]
Expand Down
2 changes: 1 addition & 1 deletion tests/codegen/adjustments.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// compile-flags: -C no-prepopulate-passes
// compile-flags: -C no-prepopulate-passes -Copt-level=0

#![crate_type = "lib"]

Expand Down
2 changes: 1 addition & 1 deletion tests/codegen/box-maybe-uninit-llvm14.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ pub fn box_uninitialized2() -> Box<MaybeUninit<[usize; 1024 * 1024]>> {
// Hide the LLVM 15+ `allocalign` attribute in the declaration of __rust_alloc
// from the CHECK-NOT above. We don't check the attributes here because we can't rely
// on all of them being set until LLVM 15.
// CHECK: declare noalias{{.*}} @__rust_alloc(i{{[0-9]+}}, i{{[0-9]+.*}})
// CHECK: declare noalias{{.*}} @__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+.*}} noundef)
2 changes: 1 addition & 1 deletion tests/codegen/box-maybe-uninit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ pub fn box_uninitialized2() -> Box<MaybeUninit<[usize; 1024 * 1024]>> {

// Hide the `allocalign` attribute in the declaration of __rust_alloc
// from the CHECK-NOT above, and also verify the attributes got set reasonably.
// CHECK: declare noalias ptr @__rust_alloc(i{{[0-9]+}}, i{{[0-9]+}} allocalign) unnamed_addr [[RUST_ALLOC_ATTRS:#[0-9]+]]
// CHECK: declare noalias noundef ptr @__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+}} allocalign noundef) unnamed_addr [[RUST_ALLOC_ATTRS:#[0-9]+]]

// CHECK-DAG: attributes [[RUST_ALLOC_ATTRS]] = { {{.*}} allockind("alloc,uninitialized,aligned") allocsize(0) uwtable "alloc-family"="__rust_alloc" {{.*}} }
2 changes: 1 addition & 1 deletion tests/codegen/c-variadic.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// ignore-wasm32-bare compiled with panic=abort by default
// compile-flags: -C no-prepopulate-passes
// compile-flags: -C no-prepopulate-passes -Copt-level=0
//

#![crate_type = "lib"]
Expand Down
2 changes: 1 addition & 1 deletion tests/codegen/call-llvm-intrinsics.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// compile-flags: -C no-prepopulate-passes
// compile-flags: -C no-prepopulate-passes -Copt-level=0

// ignore-riscv64

Expand Down
8 changes: 4 additions & 4 deletions tests/codegen/comparison-operators-newtype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::cmp::Ordering;
pub struct Foo(u16);

// CHECK-LABEL: @check_lt
// CHECK-SAME: (i16 %[[A:.+]], i16 %[[B:.+]])
// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
#[no_mangle]
pub fn check_lt(a: Foo, b: Foo) -> bool {
// CHECK: %[[R:.+]] = icmp ult i16 %[[A]], %[[B]]
Expand All @@ -22,7 +22,7 @@ pub fn check_lt(a: Foo, b: Foo) -> bool {
}

// CHECK-LABEL: @check_le
// CHECK-SAME: (i16 %[[A:.+]], i16 %[[B:.+]])
// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
#[no_mangle]
pub fn check_le(a: Foo, b: Foo) -> bool {
// CHECK: %[[R:.+]] = icmp ule i16 %[[A]], %[[B]]
Expand All @@ -31,7 +31,7 @@ pub fn check_le(a: Foo, b: Foo) -> bool {
}

// CHECK-LABEL: @check_gt
// CHECK-SAME: (i16 %[[A:.+]], i16 %[[B:.+]])
// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
#[no_mangle]
pub fn check_gt(a: Foo, b: Foo) -> bool {
// CHECK: %[[R:.+]] = icmp ugt i16 %[[A]], %[[B]]
Expand All @@ -40,7 +40,7 @@ pub fn check_gt(a: Foo, b: Foo) -> bool {
}

// CHECK-LABEL: @check_ge
// CHECK-SAME: (i16 %[[A:.+]], i16 %[[B:.+]])
// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
#[no_mangle]
pub fn check_ge(a: Foo, b: Foo) -> bool {
// CHECK: %[[R:.+]] = icmp uge i16 %[[A]], %[[B]]
Expand Down
10 changes: 5 additions & 5 deletions tests/codegen/dllimports/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// This test is for *-windows-msvc only.
// This test is for *-windows-msvc only.
// only-windows
// ignore-gnu

Expand All @@ -15,10 +15,10 @@ extern crate wrapper;
// CHECK: @static_global1 = external local_unnamed_addr global i32
// CHECK: @static_global2 = external local_unnamed_addr global i32

// CHECK: declare dllimport i32 @dylib_func1(i32)
// CHECK: declare dllimport i32 @dylib_func2(i32)
// CHECK: declare i32 @static_func1(i32)
// CHECK: declare i32 @static_func2(i32)
// CHECK: declare dllimport noundef i32 @dylib_func1(i32 noundef)
// CHECK: declare dllimport noundef i32 @dylib_func2(i32 noundef)
// CHECK: declare noundef i32 @static_func1(i32 noundef)
// CHECK: declare noundef i32 @static_func2(i32 noundef)

#[link(name = "dummy", kind="dylib")]
extern "C" {
Expand Down
6 changes: 3 additions & 3 deletions tests/codegen/enum-match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub enum Enum0 {
B,
}

// CHECK: define i8 @match0{{.*}}
// CHECK: define noundef i8 @match0{{.*}}
// CHECK-NEXT: start:
// CHECK-NEXT: %1 = icmp eq i8 %0, 2
// CHECK-NEXT: %2 = and i8 %0, 1
Expand All @@ -32,7 +32,7 @@ pub enum Enum1 {
C,
}

// CHECK: define i8 @match1{{.*}}
// CHECK: define noundef i8 @match1{{.*}}
// CHECK-NEXT: start:
// CHECK-NEXT: [[DISCR:%.*]] = {{.*}}call i8 @llvm.usub.sat.i8(i8 %0, i8 1)
// CHECK-NEXT: switch i8 [[DISCR]], label {{.*}} [
Expand Down Expand Up @@ -88,7 +88,7 @@ pub enum Enum2 {
E,
}

// CHECK: define i8 @match2{{.*}}
// CHECK: define noundef i8 @match2{{.*}}
// CHECK-NEXT: start:
// CHECK-NEXT: %1 = add i8 %0, 2
// CHECK-NEXT: %2 = zext i8 %1 to i64
Expand Down
12 changes: 6 additions & 6 deletions tests/codegen/fastcall-inreg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,27 @@ trait Sized {}
trait Copy {}

pub mod tests {
// CHECK: @f1(i32 inreg %_1, i32 inreg %_2, i32 %_3)
// CHECK: @f1(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 noundef %_3)
#[no_mangle]
pub extern "fastcall" fn f1(_: i32, _: i32, _: i32) {}

// CHECK: @f2({{i32\*|ptr}} inreg %_1, {{i32\*|ptr}} inreg %_2, {{i32\*|ptr}} %_3)
// CHECK: @f2({{i32\*|ptr}} inreg noundef %_1, {{i32\*|ptr}} inreg noundef %_2, {{i32\*|ptr}} noundef %_3)
#[no_mangle]
pub extern "fastcall" fn f2(_: *const i32, _: *const i32, _: *const i32) {}

// CHECK: @f3(float %_1, i32 inreg %_2, i32 inreg %_3, i32 %_4)
// CHECK: @f3(float noundef %_1, i32 inreg noundef %_2, i32 inreg noundef %_3, i32 noundef %_4)
#[no_mangle]
pub extern "fastcall" fn f3(_: f32, _: i32, _: i32, _: i32) {}

// CHECK: @f4(i32 inreg %_1, float %_2, i32 inreg %_3, i32 %_4)
// CHECK: @f4(i32 inreg noundef %_1, float noundef %_2, i32 inreg noundef %_3, i32 noundef %_4)
#[no_mangle]
pub extern "fastcall" fn f4(_: i32, _: f32, _: i32, _: i32) {}

// CHECK: @f5(i64 %_1, i32 %_2)
// CHECK: @f5(i64 noundef %_1, i32 noundef %_2)
#[no_mangle]
pub extern "fastcall" fn f5(_: i64, _: i32) {}

// CHECK: @f6(i1 inreg noundef zeroext %_1, i32 inreg %_2, i32 %_3)
// CHECK: @f6(i1 inreg noundef zeroext %_1, i32 inreg noundef %_2, i32 noundef %_3)
#[no_mangle]
pub extern "fastcall" fn f6(_: bool, _: i32, _: i32) {}
}
4 changes: 2 additions & 2 deletions tests/codegen/fewer-names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@

#[no_mangle]
pub fn sum(x: u32, y: u32) -> u32 {
// YES-LABEL: define{{.*}}i32 @sum(i32 %0, i32 %1)
// YES-LABEL: define{{.*}}i32 @sum(i32 noundef %0, i32 noundef %1)
// YES-NEXT: %3 = add i32 %1, %0
// YES-NEXT: ret i32 %3

// NO-LABEL: define{{.*}}i32 @sum(i32 %x, i32 %y)
// NO-LABEL: define{{.*}}i32 @sum(i32 noundef %x, i32 noundef %y)
// NO-NEXT: start:
// NO-NEXT: %z = add i32 %y, %x
// NO-NEXT: ret i32 %z
Expand Down
2 changes: 1 addition & 1 deletion tests/codegen/frame-pointer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// compile-flags: --crate-type=rlib
// compile-flags: --crate-type=rlib -Copt-level=0
// revisions: aarch64-apple aarch64-linux force x64-apple x64-linux
// [aarch64-apple] needs-llvm-components: aarch64
// [aarch64-apple] compile-flags: --target=aarch64-apple-darwin
Expand Down
58 changes: 42 additions & 16 deletions tests/codegen/function-arguments.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// compile-flags: -O -C no-prepopulate-passes

#![crate_type = "lib"]
#![feature(rustc_attrs)]

use std::mem::MaybeUninit;
use std::num::NonZeroU64;
use std::marker::PhantomPinned;
use std::ptr::NonNull;

pub struct S {
_field: [i32; 8],
Expand Down Expand Up @@ -61,7 +61,7 @@ pub fn maybeuninit_char(x: MaybeUninit<char>) -> MaybeUninit<char> {
x
}

// CHECK: i64 @int(i64 %x)
// CHECK: noundef i64 @int(i64 noundef %x)
#[no_mangle]
pub fn int(x: u64) -> u64 {
x
Expand All @@ -73,7 +73,7 @@ pub fn nonzero_int(x: NonZeroU64) -> NonZeroU64 {
x
}

// CHECK: i64 @option_nonzero_int(i64 %x)
// CHECK: noundef i64 @option_nonzero_int(i64 noundef %x)
#[no_mangle]
pub fn option_nonzero_int(x: Option<NonZeroU64>) -> Option<NonZeroU64> {
x
Expand Down Expand Up @@ -138,11 +138,27 @@ pub fn indirect_struct(_: S) {
pub fn borrowed_struct(_: &S) {
}

// CHECK: @raw_struct({{%S\*|ptr}} %_1)
// CHECK: @option_borrow({{i32\*|ptr}} noalias noundef readonly align 4 dereferenceable_or_null(4) %x)
#[no_mangle]
pub fn option_borrow(x: Option<&i32>) {
}

// CHECK: @option_borrow_mut({{i32\*|ptr}} noalias noundef align 4 dereferenceable_or_null(4) %x)
#[no_mangle]
pub fn option_borrow_mut(x: Option<&mut i32>) {
}

// CHECK: @raw_struct({{%S\*|ptr}} noundef %_1)
#[no_mangle]
pub fn raw_struct(_: *const S) {
}

// CHECK: @raw_option_nonnull_struct({{i32\*|ptr}} noundef %_1)
#[no_mangle]
pub fn raw_option_nonnull_struct(_: Option<NonNull<S>>) {
}


// `Box` can get deallocated during execution of the function, so it should
// not get `dereferenceable`.
// CHECK: noundef nonnull align 4 {{i32\*|ptr}} @_box({{i32\*|ptr}} noalias noundef nonnull align 4 %x)
Expand All @@ -160,35 +176,35 @@ pub fn struct_return() -> S {
}

// Hack to get the correct size for the length part in slices
// CHECK: @helper([[USIZE:i[0-9]+]] %_1)
// CHECK: @helper([[USIZE:i[0-9]+]] noundef %_1)
#[no_mangle]
pub fn helper(_: usize) {
}

// CHECK: @slice({{\[0 x i8\]\*|ptr}} noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] %_1.1)
// CHECK: @slice({{\[0 x i8\]\*|ptr}} noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn slice(_: &[u8]) {
}

// CHECK: @mutable_slice({{\[0 x i8\]\*|ptr}} noalias noundef nonnull align 1 %_1.0, [[USIZE]] %_1.1)
// CHECK: @mutable_slice({{\[0 x i8\]\*|ptr}} noalias noundef nonnull align 1 %_1.0, [[USIZE]] noundef %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn mutable_slice(_: &mut [u8]) {
}

// CHECK: @unsafe_slice({{\[0 x i16\]\*|ptr}} noundef nonnull align 2 %_1.0, [[USIZE]] %_1.1)
// CHECK: @unsafe_slice({{\[0 x i16\]\*|ptr}} noundef nonnull align 2 %_1.0, [[USIZE]] noundef %_1.1)
// unsafe interior means this isn't actually readonly and there may be aliases ...
#[no_mangle]
pub fn unsafe_slice(_: &[UnsafeInner]) {
}

// CHECK: @raw_slice({{\[0 x i8\]\*|ptr}} %_1.0, [[USIZE]] %_1.1)
// CHECK: @raw_slice({{\[0 x i8\]\*|ptr}} noundef %_1.0, [[USIZE]] noundef %_1.1)
#[no_mangle]
pub fn raw_slice(_: *const [u8]) {
}

// CHECK: @str({{\[0 x i8\]\*|ptr}} noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] %_1.1)
// CHECK: @str({{\[0 x i8\]\*|ptr}} noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn str(_: &[u8]) {
Expand All @@ -197,26 +213,36 @@ pub fn str(_: &[u8]) {
// CHECK: @trait_borrow({{\{\}\*|ptr}} noundef nonnull align 1 %_1.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn trait_borrow(_: &Drop) {
pub fn trait_borrow(_: &dyn Drop) {
}

// CHECK: @option_trait_borrow({{i8\*|ptr}} noundef align 1 %x.0, {{i8\*|ptr}} %x.1)
#[no_mangle]
pub fn option_trait_borrow(x: Option<&dyn Drop>) {
}

// CHECK: @option_trait_borrow_mut({{i8\*|ptr}} noundef align 1 %x.0, {{i8\*|ptr}} %x.1)
#[no_mangle]
pub fn option_trait_borrow_mut(x: Option<&mut dyn Drop>) {
}

// CHECK: @trait_raw({{\{\}\*|ptr}} %_1.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
// CHECK: @trait_raw({{\{\}\*|ptr}} noundef %_1.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
#[no_mangle]
pub fn trait_raw(_: *const Drop) {
pub fn trait_raw(_: *const dyn Drop) {
}

// CHECK: @trait_box({{\{\}\*|ptr}} noalias noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
#[no_mangle]
pub fn trait_box(_: Box<Drop>) {
pub fn trait_box(_: Box<dyn Drop>) {
}

// CHECK: { {{i8\*|ptr}}, {{i8\*|ptr}} } @trait_option({{i8\*|ptr}} noalias noundef align 1 %x.0, {{i8\*|ptr}} %x.1)
#[no_mangle]
pub fn trait_option(x: Option<Box<Drop>>) -> Option<Box<Drop>> {
pub fn trait_option(x: Option<Box<dyn Drop>>) -> Option<Box<dyn Drop>> {
x
}

// CHECK: { {{\[0 x i16\]\*|ptr}}, [[USIZE]] } @return_slice({{\[0 x i16\]\*|ptr}} noalias noundef nonnull readonly align 2 %x.0, [[USIZE]] %x.1)
// CHECK: { {{\[0 x i16\]\*|ptr}}, [[USIZE]] } @return_slice({{\[0 x i16\]\*|ptr}} noalias noundef nonnull readonly align 2 %x.0, [[USIZE]] noundef %x.1)
#[no_mangle]
pub fn return_slice(x: &[u16]) -> &[u16] {
x
Expand Down
2 changes: 1 addition & 1 deletion tests/codegen/intrinsics/const_eval_select.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// compile-flags: -C no-prepopulate-passes
// compile-flags: -C no-prepopulate-passes -Copt-level=0

#![crate_type = "lib"]
#![feature(const_eval_select)]
Expand Down
3 changes: 2 additions & 1 deletion tests/codegen/intrinsics/mask.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// compile-flags: -Copt-level=0
#![crate_type = "lib"]
#![feature(core_intrinsics)]

Expand All @@ -6,6 +7,6 @@
#[no_mangle]
pub fn mask_ptr(ptr: *const u16, mask: usize) -> *const u16 {
// CHECK: call
// CHECK-SAME: @llvm.ptrmask.{{p0|p0i8}}.[[WORD]]({{ptr|i8\*}} {{%ptr|%0}}, [[WORD]] %mask)
// CHECK-SAME: @llvm.ptrmask.{{p0|p0i8}}.[[WORD]]({{ptr|i8\*}} {{%ptr|%1}}, [[WORD]] %mask)
core::intrinsics::ptr_mask(ptr, mask)
}
2 changes: 1 addition & 1 deletion tests/codegen/issue-32031.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// compile-flags: -C no-prepopulate-passes
// compile-flags: -C no-prepopulate-passes -Copt-level=0

#![crate_type = "lib"]

Expand Down
2 changes: 1 addition & 1 deletion tests/codegen/issue-58881.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// compile-flags: -C no-prepopulate-passes
// compile-flags: -C no-prepopulate-passes -Copt-level=0
//
// only-x86_64
// ignore-windows
Expand Down
Loading

0 comments on commit 3984bc5

Please sign in to comment.