Skip to content

Commit

Permalink
Rollup merge of rust-lang#59639 - cuviper:ignore-uninhabited, r=sanxiyn
Browse files Browse the repository at this point in the history
Never return uninhabited values at all

Functions with uninhabited return values are already marked `noreturn`,
but we were still generating return instructions for this. When running
with `C passes=lint`, LLVM prints:

    Unusual: Return statement in function with noreturn attribute

The LLVM manual makes a stronger statement about `noreturn` though:

> This produces undefined behavior at runtime if the function ever does
dynamically return.

We now mark such return values with a new `IgnoreMode::Uninhabited`, and
emit an `abort` anywhere that would have returned.

Fixes rust-lang#48227
cc rust-lang#7463 rust-lang#48229

r? @eddyb
  • Loading branch information
Centril authored Apr 3, 2019
2 parents 9274fba + fb575c0 commit 6c8d8fe
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 3 deletions.
9 changes: 7 additions & 2 deletions src/librustc_codegen_llvm/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,11 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| {
let is_return = arg_idx.is_none();
let mut arg = mk_arg_type(ty, arg_idx);
if arg.layout.is_zst() {
if is_return && arg.layout.abi.is_uninhabited() {
// Functions with uninhabited return values are marked `noreturn`,
// so we don't actually need to do anything with the value.
arg.mode = PassMode::Ignore(IgnoreMode::Uninhabited);
} else if arg.layout.is_zst() {
// For some forsaken reason, x86_64-pc-windows-gnu
// doesn't ignore zero-sized struct arguments.
// The same is true for s390x-unknown-linux-gnu
Expand Down Expand Up @@ -677,7 +681,8 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
);

let llreturn_ty = match self.ret.mode {
PassMode::Ignore(IgnoreMode::Zst) => cx.type_void(),
PassMode::Ignore(IgnoreMode::Zst)
| PassMode::Ignore(IgnoreMode::Uninhabited) => cx.type_void(),
PassMode::Ignore(IgnoreMode::CVarArgs) =>
bug!("`va_list` should never be a return type"),
PassMode::Direct(_) | PassMode::Pair(..) => {
Expand Down
6 changes: 6 additions & 0 deletions src/librustc_codegen_ssa/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,12 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
return;
}

PassMode::Ignore(IgnoreMode::Uninhabited) => {
bx.abort();
bx.unreachable();
return;
}

PassMode::Ignore(IgnoreMode::CVarArgs) => {
bug!("C-variadic arguments should never be the return type");
}
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_codegen_ssa/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,8 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
PassMode::Ignore(IgnoreMode::Zst) => {
return local(OperandRef::new_zst(bx, arg.layout));
}
PassMode::Ignore(IgnoreMode::CVarArgs) => {}
PassMode::Ignore(IgnoreMode::CVarArgs)
| PassMode::Ignore(IgnoreMode::Uninhabited) => {}
PassMode::Direct(_) => {
let llarg = bx.get_param(llarg_idx);
bx.set_value_name(llarg, &name);
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_target/abi/call/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub enum IgnoreMode {
CVarArgs,
/// A zero-sized type.
Zst,
/// An uninhabited type.
Uninhabited,
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
Expand Down
32 changes: 32 additions & 0 deletions src/test/codegen/noreturn-uninhabited.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// compile-flags: -g -C no-prepopulate-passes
// ignore-tidy-linelength

#![crate_type = "lib"]

#[derive(Clone, Copy)]
pub enum EmptyEnum {}

#[no_mangle]
pub fn empty(x: &EmptyEnum) -> EmptyEnum {
// CHECK: @empty({{.*}}) unnamed_addr #0
// CHECK-NOT: ret void
// CHECK: call void @llvm.trap()
// CHECK: unreachable
*x
}

pub struct Foo(String, EmptyEnum);

#[no_mangle]
pub fn foo(x: String, y: &EmptyEnum) -> Foo {
// CHECK: @foo({{.*}}) unnamed_addr #0
// CHECK-NOT: ret %Foo
// CHECK: call void @llvm.trap()
// CHECK: unreachable
Foo(x, *y)
}

// CHECK: attributes #0 = {{{.*}} noreturn {{.*}}}

// CHECK: DISubprogram(name: "empty", {{.*}} DIFlagNoReturn
// CHECK: DISubprogram(name: "foo", {{.*}} DIFlagNoReturn

0 comments on commit 6c8d8fe

Please sign in to comment.