From 91d799eab0d7f6784fb4366182b5007cf055519d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 19 Jun 2015 14:57:06 -0700 Subject: msvc: Implement runtime support for unwinding Now that LLVM has been updated, the only remaining roadblock to implementing unwinding for MSVC is to fill out the runtime support in `std::rt::unwind::seh`. This commit does precisely that, fixing up some other bits and pieces along the way: * The `seh` unwinding module now uses `RaiseException` to initiate a panic. * The `rust_try.ll` file was rewritten for MSVC (as it's quite different) and is located at `rust_try_msvc_64.ll`, only included on MSVC builds for now. * The personality function for all landing pads generated by LLVM is hard-wired to `__C_specific_handler` instead of the standard `rust_eh_personality` lang item. This is required to get LLVM to emit SEH unwinding information instead of DWARF unwinding information. This also means that on MSVC the `rust_eh_personality` function is entirely unused (but is defined as it's a lang item). More details about how panicking works on SEH can be found in the `rust_try_msvc_64.ll` or `seh.rs` files, but I'm always open to adding more comments! A key aspect of this PR is missing, however, which is that **unwinding is still turned off by default for MSVC**. There is a [bug in llvm][llvm-bug] which causes optimizations to inline enough landing pads that LLVM chokes. If the compiler is optimized at `-O1` (where inlining isn't enabled) then it can bootstrap with unwinding enabled, but when optimized at `-O2` (inlining is enabled) then it hits a fatal LLVM error. [llvm-bug]: https://llvm.org/bugs/show_bug.cgi?id=23884 --- src/libstd/rt/unwind/seh.rs | 131 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 122 insertions(+), 9 deletions(-) (limited to 'src/libstd/rt') diff --git a/src/libstd/rt/unwind/seh.rs b/src/libstd/rt/unwind/seh.rs index a72c1debe14..632ab4f8e25 100644 --- a/src/libstd/rt/unwind/seh.rs +++ b/src/libstd/rt/unwind/seh.rs @@ -8,23 +8,136 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx) +//! +//! On Windows (currently only on MSVC), the default exception handling +//! mechanism is Structured Exception Handling (SEH). This is quite different +//! than Dwarf-based exception handling (e.g. what other unix platforms use) in +//! terms of compiler internals, so LLVM is required to have a good deal of +//! extra support for SEH. Currently this support is somewhat lacking, so what's +//! here is the bare bones of SEH support. +//! +//! In a nutshell, what happens here is: +//! +//! 1. The `panic` function calls the standard Windows function `RaiseException` +//! with a Rust-specific code, triggering the unwinding process. +//! 2. All landing pads generated by the compiler (just "cleanup" landing pads) +//! use the personality function `__C_specific_handler`, a function in the +//! CRT, and the unwinding code in Windows will use this personality function +//! to execute all cleanup code on the stack. +//! 3. Eventually the "catch" code in `rust_try` (located in +//! src/rt/rust_try_msvc_64.ll) is executed, which will ensure that the +//! exception being caught is indeed a Rust exception, returning control back +//! into Rust. +//! +//! Some specific differences from the gcc-based exception handling are: +//! +//! * Rust has no custom personality function, it is instead *always* +//! __C_specific_handler, so the filtering is done in a C++-like manner +//! instead of in the personality function itself. Note that the specific +//! syntax for this (found in the rust_try_msvc_64.ll) is taken from an LLVM +//! test case for SEH. +//! * We've got some data to transmit across the unwinding boundary, +//! specifically a `Box`. In Dwarf-based unwinding this +//! data is part of the payload of the exception, but I have not currently +//! figured out how to do this with LLVM's bindings. Judging by some comments +//! in the LLVM test cases this may not even be possible currently with LLVM, +//! so this is just abandoned entirely. Instead the data is stored in a +//! thread-local in `panic` and retrieved during `cleanup`. +//! +//! So given all that, the bindings here are pretty small, + +#![allow(bad_style)] + use prelude::v1::*; use any::Any; -use intrinsics; -use libc::c_void; +use libc::{c_ulong, DWORD, c_void}; +use sys_common::thread_local::StaticKey; + +// 0x R U S T +const RUST_PANIC: DWORD = 0x52555354; +static PANIC_DATA: StaticKey = StaticKey::new(None); + +// This function is provided by kernel32.dll +extern "system" { + fn RaiseException(dwExceptionCode: DWORD, + dwExceptionFlags: DWORD, + nNumberOfArguments: DWORD, + lpArguments: *const c_ulong); +} + +#[repr(C)] +pub struct EXCEPTION_POINTERS { + ExceptionRecord: *mut EXCEPTION_RECORD, + ContextRecord: *mut CONTEXT, +} + +enum CONTEXT {} + +#[repr(C)] +struct EXCEPTION_RECORD { + ExceptionCode: DWORD, + ExceptionFlags: DWORD, + ExceptionRecord: *mut _EXCEPTION_RECORD, + ExceptionAddress: *mut c_void, + NumberParameters: DWORD, + ExceptionInformation: [*mut c_ulong; EXCEPTION_MAXIMUM_PARAMETERS], +} -pub unsafe fn panic(_data: Box) -> ! { - intrinsics::abort(); +enum _EXCEPTION_RECORD {} + +const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; + +pub unsafe fn panic(data: Box) -> ! { + // See module docs above for an explanation of why `data` is stored in a + // thread local instead of being passed as an argument to the + // `RaiseException` function (which can in theory carry along arbitrary + // data). + let exception = Box::new(data); + rtassert!(PANIC_DATA.get().is_null()); + PANIC_DATA.set(Box::into_raw(exception) as *mut u8); + + RaiseException(RUST_PANIC, 0, 0, 0 as *const _); + rtabort!("could not unwind stack"); } -pub unsafe fn cleanup(_ptr: *mut c_void) -> Box { - intrinsics::abort(); +pub unsafe fn cleanup(ptr: *mut c_void) -> Box { + // The `ptr` here actually corresponds to the code of the exception, and our + // real data is stored in our thread local. + rtassert!(ptr as DWORD == RUST_PANIC); + + let data = PANIC_DATA.get() as *mut Box; + PANIC_DATA.set(0 as *mut u8); + rtassert!(!data.is_null()); + + *Box::from_raw(data) } +// This is required by the compiler to exist (e.g. it's a lang item), but it's +// never actually called by the compiler because __C_specific_handler is the +// personality function that is always used. Hence this is just an aborting +// stub. #[lang = "eh_personality"] -#[no_mangle] -pub extern fn rust_eh_personality() {} +fn rust_eh_personality() { + unsafe { ::intrinsics::abort() } +} +// This is a function referenced from `rust_try_msvc_64.ll` which is used to +// filter the exceptions being caught by that function. +// +// In theory local variables can be accessed through the `rbp` parameter of this +// function, but a comment in an LLVM test case indicates that this is not +// implemented in LLVM, so this is just an idempotent function which doesn't +// ferry along any other information. +// +// This function just takes a look at the current EXCEPTION_RECORD being thrown +// to ensure that it's code is RUST_PANIC, which was set by the call to +// `RaiseException` above in the `panic` function. #[no_mangle] -pub extern fn rust_eh_personality_catch() {} +pub extern fn __rust_try_filter(eh_ptrs: *mut EXCEPTION_POINTERS, + _rbp: *mut c_void) -> i32 { + unsafe { + ((*(*eh_ptrs).ExceptionRecord).ExceptionCode == RUST_PANIC) as i32 + } +} -- cgit 1.4.1-3-g733a5 From 0b7c4f57f6ba59dabe4db2808fe45e8bd8bbce22 Mon Sep 17 00:00:00 2001 From: Alex Newman Date: Tue, 30 Jun 2015 20:37:11 -0700 Subject: Add netbsd amd64 support --- configure | 4 ++ mk/cfg/x86_64-unknown-netbsd.mk | 22 +++++++ src/compiletest/util.rs | 1 + src/doc/reference.md | 2 +- src/etc/snapshot.py | 11 ++-- src/liblibc/lib.rs | 28 +++++---- src/librustc_back/arm.rs | 2 +- src/librustc_back/mips.rs | 2 +- src/librustc_back/mipsel.rs | 2 +- src/librustc_back/target/mod.rs | 2 + src/librustc_back/target/netbsd_base.rs | 32 ++++++++++ src/librustc_back/target/x86_64_unknown_netbsd.rs | 29 +++++++++ src/librustc_back/x86.rs | 2 +- src/librustc_back/x86_64.rs | 2 +- src/librustdoc/flock.rs | 1 + src/libstd/dynamic_lib.rs | 2 + src/libstd/env.rs | 12 ++++ src/libstd/net/tcp.rs | 2 +- src/libstd/net/udp.rs | 4 +- src/libstd/os/mod.rs | 1 + src/libstd/os/netbsd/mod.rs | 20 +++++++ src/libstd/os/netbsd/raw.rs | 71 +++++++++++++++++++++++ src/libstd/rt/args.rs | 1 + src/libstd/rt/libunwind.rs | 2 +- src/libstd/rtdeps.rs | 1 + src/libstd/sys/common/stack.rs | 2 + src/libstd/sys/unix/backtrace.rs | 1 + src/libstd/sys/unix/c.rs | 10 +++- src/libstd/sys/unix/mod.rs | 1 + src/libstd/sys/unix/os.rs | 15 +---- src/libstd/sys/unix/process.rs | 1 + src/libstd/sys/unix/stack_overflow.rs | 2 + src/libstd/sys/unix/sync.rs | 1 + src/libstd/sys/unix/thread.rs | 6 +- src/libstd/sys/unix/thread_local.rs | 2 + src/libstd/sys/unix/time.rs | 1 + src/libstd/thread/scoped_tls.rs | 3 + src/libsyntax/abi.rs | 2 + src/test/parse-fail/issue-5806.rs | 1 + src/test/run-pass/intrinsic-alignment.rs | 1 + src/test/run-pass/rec-align-u64.rs | 1 + src/test/run-pass/tcp-stress.rs | 3 +- src/test/run-pass/x86stdcall.rs | 1 + 43 files changed, 271 insertions(+), 41 deletions(-) create mode 100644 mk/cfg/x86_64-unknown-netbsd.mk create mode 100644 src/librustc_back/target/netbsd_base.rs create mode 100644 src/librustc_back/target/x86_64_unknown_netbsd.rs create mode 100644 src/libstd/os/netbsd/mod.rs create mode 100644 src/libstd/os/netbsd/raw.rs (limited to 'src/libstd/rt') diff --git a/configure b/configure index 5d4a017b6fb..1d3611f88f0 100755 --- a/configure +++ b/configure @@ -405,6 +405,10 @@ case $CFG_OSTYPE in CFG_OSTYPE=unknown-openbsd ;; + NetBSD) + CFG_OSTYPE=unknown-netbsd + ;; + Darwin) CFG_OSTYPE=apple-darwin ;; diff --git a/mk/cfg/x86_64-unknown-netbsd.mk b/mk/cfg/x86_64-unknown-netbsd.mk new file mode 100644 index 00000000000..401b0fb7ab0 --- /dev/null +++ b/mk/cfg/x86_64-unknown-netbsd.mk @@ -0,0 +1,22 @@ +# x86_64-unknown-netbsd configuration +CC_x86_64-unknown-netbsd=$(CC) +CXX_x86_64-unknown-netbsd=$(CXX) +CPP_x86_64-unknown-netbsd=$(CPP) +AR_x86_64-unknown-netbsd=$(AR) +CFG_LIB_NAME_x86_64-unknown-netbsd=lib$(1).so +CFG_STATIC_LIB_NAME_x86_64-unknown-netbsd=lib$(1).a +CFG_LIB_GLOB_x86_64-unknown-netbsd=lib$(1)-*.so +CFG_LIB_DSYM_GLOB_x86_64-unknown-netbsd=$(1)-*.dylib.dSYM +CFG_JEMALLOC_CFLAGS_x86_64-unknown-netbsd := -I/usr/local/include $(CFLAGS) +CFG_GCCISH_CFLAGS_x86_64-unknown-netbsd := -Wall -Werror -g -fPIC -I/usr/local/include $(CFLAGS) +CFG_GCCISH_LINK_FLAGS_x86_64-unknown-netbsd := -shared -fPIC -g -pthread -lrt +CFG_GCCISH_DEF_FLAG_x86_64-unknown-netbsd := -Wl,--export-dynamic,--dynamic-list= +CFG_LLC_FLAGS_x86_64-unknown-netbsd := +CFG_INSTALL_NAME_x86_64-unknown-netbsd = +CFG_EXE_SUFFIX_x86_64-unknown-netbsd := +CFG_WINDOWSY_x86_64-unknown-netbsd := +CFG_UNIXY_x86_64-unknown-netbsd := 1 +CFG_LDPATH_x86_64-unknown-netbsd := +CFG_RUN_x86_64-unknown-netbsd=$(2) +CFG_RUN_TARG_x86_64-unknown-netbsd=$(call CFG_RUN_x86_64-unknown-netbsd,,$(2)) +CFG_GNU_TRIPLE_x86_64-unknown-netbsd := x86_64-unknown-netbsd diff --git a/src/compiletest/util.rs b/src/compiletest/util.rs index 184d62db451..13d6c029ff5 100644 --- a/src/compiletest/util.rs +++ b/src/compiletest/util.rs @@ -21,6 +21,7 @@ const OS_TABLE: &'static [(&'static str, &'static str)] = &[ ("ios", "ios"), ("linux", "linux"), ("mingw32", "windows"), + ("netbsd", "netbsd"), ("openbsd", "openbsd"), ("win32", "windows"), ("windows", "windows"), diff --git a/src/doc/reference.md b/src/doc/reference.md index 7e28651c6aa..650e1d5541e 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2023,7 +2023,7 @@ The following configurations must be defined by the implementation: as a configuration itself, like `unix` or `windows`. * `target_os = "..."`. Operating system of the target, examples include `"windows"`, `"macos"`, `"ios"`, `"linux"`, `"android"`, `"freebsd"`, `"dragonfly"`, - `"bitrig"` or `"openbsd"`. + `"bitrig"` , `"openbsd"` or `"netbsd"`. * `target_pointer_width = "..."`. Target pointer width in bits. This is set to `"32"` for targets with 32-bit pointers, and likewise set to `"64"` for 64-bit pointers. diff --git a/src/etc/snapshot.py b/src/etc/snapshot.py index 0349ccf9b66..6d62a45c703 100644 --- a/src/etc/snapshot.py +++ b/src/etc/snapshot.py @@ -41,13 +41,14 @@ download_dir_base = "dl" download_unpack_base = os.path.join(download_dir_base, "unpack") snapshot_files = { + "bitrig": ["bin/rustc"], + "dragonfly": ["bin/rustc"], + "freebsd": ["bin/rustc"], "linux": ["bin/rustc"], "macos": ["bin/rustc"], - "winnt": ["bin/rustc.exe"], - "freebsd": ["bin/rustc"], - "dragonfly": ["bin/rustc"], - "bitrig": ["bin/rustc"], + "netbsd": ["bin/rustc"], "openbsd": ["bin/rustc"], + "winnt": ["bin/rustc.exe"], } winnt_runtime_deps_32 = ["libgcc_s_dw2-1.dll", "libstdc++-6.dll"] @@ -103,6 +104,8 @@ def get_kernel(triple): return "dragonfly" if os_name == "bitrig": return "bitrig" + if os_name == "netbsd": + return "netbsd" if os_name == "openbsd": return "openbsd" return "linux" diff --git a/src/liblibc/lib.rs b/src/liblibc/lib.rs index 6e01796ad82..f66811e561a 100644 --- a/src/liblibc/lib.rs +++ b/src/liblibc/lib.rs @@ -1322,7 +1322,7 @@ pub mod types { } } - #[cfg(any(target_os = "bitrig", target_os = "openbsd"))] + #[cfg(any(target_os = "bitrig", target_os = "netbsd", target_os ="openbsd"))] pub mod os { pub mod common { pub mod posix01 { @@ -1351,7 +1351,7 @@ pub mod types { pub __unused7: *mut c_void, } - #[cfg(target_os = "openbsd")] + #[cfg(any(target_os = "netbsd", target_os="openbsd"))] #[repr(C)] #[derive(Copy, Clone)] pub struct glob_t { pub gl_pathc: c_int, @@ -4323,7 +4323,7 @@ pub mod consts { } } - #[cfg(any(target_os = "bitrig", target_os = "openbsd"))] + #[cfg(any(target_os = "bitrig", target_os = "netbsd", target_os = "openbsd"))] pub mod os { pub mod c95 { use types::os::arch::c95::{c_int, c_uint}; @@ -5568,6 +5568,7 @@ pub mod funcs { target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd", target_os = "nacl"))] pub mod posix88 { @@ -5584,6 +5585,7 @@ pub mod funcs { target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd", target_os = "android", target_os = "ios", @@ -5602,6 +5604,7 @@ pub mod funcs { target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd", target_os = "android", target_os = "ios", @@ -5889,6 +5892,7 @@ pub mod funcs { target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd", target_os = "nacl"))] pub mod posix01 { @@ -5901,6 +5905,7 @@ pub mod funcs { target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd", target_os = "android", target_os = "ios", @@ -6019,16 +6024,17 @@ pub mod funcs { } - #[cfg(any(target_os = "windows", - target_os = "linux", - target_os = "android", - target_os = "macos", + #[cfg(any(target_os = "android", + target_os = "bitrig", + target_os = "dragonfly", target_os = "ios", target_os = "freebsd", - target_os = "dragonfly", - target_os = "bitrig", + target_os = "linux", + target_os = "macos", + target_os = "nacl", + target_os = "netbsd", target_os = "openbsd", - target_os = "nacl"))] + target_os = "windows"))] pub mod posix08 { pub mod unistd { } @@ -6115,6 +6121,7 @@ pub mod funcs { target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] pub mod bsd44 { use types::common::c95::{c_void}; @@ -6192,6 +6199,7 @@ pub mod funcs { #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] pub mod extra { } diff --git a/src/librustc_back/arm.rs b/src/librustc_back/arm.rs index 7325e4e7a2e..9e288f6ddb2 100644 --- a/src/librustc_back/arm.rs +++ b/src/librustc_back/arm.rs @@ -61,7 +61,7 @@ pub fn get_target_strs(target_triple: String, target_os: abi::Os) -> target_strs -a:0:64-n32".to_string() } - abi::OsFreebsd | abi::OsDragonfly | abi::OsBitrig | abi::OsOpenbsd => { + abi::OsFreebsd | abi::OsDragonfly | abi::OsBitrig | abi::OsOpenbsd | abi::OsNetbsd => { "e-p:32:32:32\ -i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64\ -f32:32:32-f64:64:64\ diff --git a/src/librustc_back/mips.rs b/src/librustc_back/mips.rs index b46150f75d0..e1edff817d6 100644 --- a/src/librustc_back/mips.rs +++ b/src/librustc_back/mips.rs @@ -56,7 +56,7 @@ pub fn get_target_strs(target_triple: String, target_os: abi::Os) -> target_strs -a:0:64-n32".to_string() } - abi::OsFreebsd | abi::OsDragonfly | abi::OsBitrig | abi::OsOpenbsd => { + abi::OsBitrig | abi::OsDragonfly | abi::OsFreebsd | abi::OsNetbsd | abi::OsOpenbsd => { "E-p:32:32:32\ -i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64\ -f32:32:32-f64:64:64\ diff --git a/src/librustc_back/mipsel.rs b/src/librustc_back/mipsel.rs index c7fa7aa879a..ca52a9e56ff 100644 --- a/src/librustc_back/mipsel.rs +++ b/src/librustc_back/mipsel.rs @@ -56,7 +56,7 @@ pub fn get_target_strs(target_triple: String, target_os: abi::Os) -> target_strs -a:0:64-n32".to_string() } - abi::OsFreebsd | abi::OsDragonfly | abi::OsBitrig | abi::OsOpenbsd => { + abi::OsFreebsd | abi::OsDragonfly | abi::OsBitrig | abi::OsOpenbsd | abi::OsNetbsd => { "e-p:32:32:32\ -i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64\ -f32:32:32-f64:64:64\ diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index a42f861d190..bc5f306cd35 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -58,6 +58,7 @@ mod dragonfly_base; mod freebsd_base; mod linux_base; mod openbsd_base; +mod netbsd_base; mod windows_base; mod windows_msvc_base; @@ -368,6 +369,7 @@ impl Target { x86_64_unknown_bitrig, x86_64_unknown_openbsd, + x86_64_unknown_netbsd, x86_64_apple_darwin, i686_apple_darwin, diff --git a/src/librustc_back/target/netbsd_base.rs b/src/librustc_back/target/netbsd_base.rs new file mode 100644 index 00000000000..0f2ab32be24 --- /dev/null +++ b/src/librustc_back/target/netbsd_base.rs @@ -0,0 +1,32 @@ +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use target::TargetOptions; +use std::default::Default; + +pub fn opts() -> TargetOptions { + TargetOptions { + linker: "cc".to_string(), + dynamic_linking: true, + executables: true, + morestack: false, + linker_is_gnu: true, + has_rpath: true, + pre_link_args: vec!( + // GNU-style linkers will use this to omit linking to libraries + // which don't actually fulfill any relocations, but only for + // libraries which follow this flag. Thus, use it before + // specifying libraries to link to. + "-Wl,--as-needed".to_string(), + ), + position_independent_executables: true, + .. Default::default() + } +} diff --git a/src/librustc_back/target/x86_64_unknown_netbsd.rs b/src/librustc_back/target/x86_64_unknown_netbsd.rs new file mode 100644 index 00000000000..3f5bd39949a --- /dev/null +++ b/src/librustc_back/target/x86_64_unknown_netbsd.rs @@ -0,0 +1,29 @@ +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use target::Target; + +pub fn target() -> Target { + let mut base = super::netbsd_base::opts(); + base.pre_link_args.push("-m64".to_string()); + + Target { + data_layout: "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-\ + f32:32:32-f64:64:64-v64:64:64-v128:128:128-a:0:64-\ + s0:64:64-f80:128:128-n8:16:32:64-S128".to_string(), + llvm_target: "x86_64-unknown-netbsd".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + arch: "x86_64".to_string(), + target_os: "netbsd".to_string(), + target_env: "".to_string(), + options: base, + } +} diff --git a/src/librustc_back/x86.rs b/src/librustc_back/x86.rs index 1c6eacc3559..46e0a83ac03 100644 --- a/src/librustc_back/x86.rs +++ b/src/librustc_back/x86.rs @@ -45,7 +45,7 @@ pub fn get_target_strs(target_triple: String, target_os: abi::Os) "e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32".to_string() } - abi::OsFreebsd | abi::OsDragonfly | abi::OsBitrig | abi::OsOpenbsd => { + abi::OsFreebsd | abi::OsDragonfly | abi::OsBitrig | abi::OsOpenbsd | abi::OsNetbsd => { "e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32".to_string() } diff --git a/src/librustc_back/x86_64.rs b/src/librustc_back/x86_64.rs index d016bd12c69..abdcd564442 100644 --- a/src/librustc_back/x86_64.rs +++ b/src/librustc_back/x86_64.rs @@ -47,7 +47,7 @@ pub fn get_target_strs(target_triple: String, target_os: abi::Os) -> target_strs s0:64:64-f80:128:128-n8:16:32:64-S128".to_string() } - abi::OsFreebsd | abi::OsDragonfly | abi::OsBitrig | abi::OsOpenbsd => { + abi::OsBitrig | abi::OsDragonfly | abi::OsFreebsd | abi::OsNetbsd | abi::OsOpenbsd => { "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-\ f32:32:32-f64:64:64-v64:64:64-v128:128:128-a:0:64-\ s0:64:64-f80:128:128-n8:16:32:64-S128".to_string() diff --git a/src/librustdoc/flock.rs b/src/librustdoc/flock.rs index 760fa329fd9..847e28d2bc5 100644 --- a/src/librustdoc/flock.rs +++ b/src/librustdoc/flock.rs @@ -68,6 +68,7 @@ mod imp { #[cfg(any(target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] mod os { use libc; diff --git a/src/libstd/dynamic_lib.rs b/src/libstd/dynamic_lib.rs index a17d121e60a..3621d18daed 100644 --- a/src/libstd/dynamic_lib.rs +++ b/src/libstd/dynamic_lib.rs @@ -160,6 +160,7 @@ mod tests { target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] fn test_errors_do_not_crash() { // Open /dev/null as a library to get an error, and make sure @@ -179,6 +180,7 @@ mod tests { target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] mod dl { use prelude::v1::*; diff --git a/src/libstd/env.rs b/src/libstd/env.rs index 2e00e126e23..7c2cd615303 100644 --- a/src/libstd/env.rs +++ b/src/libstd/env.rs @@ -633,6 +633,7 @@ pub mod consts { /// - freebsd /// - dragonfly /// - bitrig + /// - netbsd /// - openbsd /// - android /// - windows @@ -759,6 +760,17 @@ mod os { pub const EXE_EXTENSION: &'static str = ""; } +#[cfg(target_os = "netbsd")] +mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "netbsd"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ""; + pub const EXE_EXTENSION: &'static str = ""; +} + #[cfg(target_os = "openbsd")] mod os { pub const FAMILY: &'static str = "unix"; diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index 222059e4c0e..085ba286dc3 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -904,7 +904,7 @@ mod tests { // FIXME: re-enabled bitrig/openbsd tests once their socket timeout code // no longer has rounding errors. - #[cfg_attr(any(target_os = "bitrig", target_os = "openbsd"), ignore)] + #[cfg_attr(any(target_os = "bitrig", target_os = "netbsd", target_os = "openbsd"), ignore)] #[test] fn timeouts() { let addr = next_test_ip4(); diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index c3cf9895205..0545175d9ae 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -360,9 +360,9 @@ mod tests { assert_eq!(format!("{:?}", udpsock), compare); } - // FIXME: re-enabled bitrig/openbsd tests once their socket timeout code + // FIXME: re-enabled bitrig/openbsd/netbsd tests once their socket timeout code // no longer has rounding errors. - #[cfg_attr(any(target_os = "bitrig", target_os = "openbsd"), ignore)] + #[cfg_attr(any(target_os = "bitrig", target_os = "netbsd", target_os = "openbsd"), ignore)] #[test] fn timeouts() { let addr = next_test_ip4(); diff --git a/src/libstd/os/mod.rs b/src/libstd/os/mod.rs index cc4b1c944e7..859cb900460 100644 --- a/src/libstd/os/mod.rs +++ b/src/libstd/os/mod.rs @@ -24,6 +24,7 @@ #[cfg(target_os = "linux")] pub mod linux; #[cfg(target_os = "macos")] pub mod macos; #[cfg(target_os = "nacl")] pub mod nacl; +#[cfg(target_os = "netbsd")] pub mod netbsd; #[cfg(target_os = "openbsd")] pub mod openbsd; pub mod raw; diff --git a/src/libstd/os/netbsd/mod.rs b/src/libstd/os/netbsd/mod.rs new file mode 100644 index 00000000000..bdb003b877b --- /dev/null +++ b/src/libstd/os/netbsd/mod.rs @@ -0,0 +1,20 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! OpenBSD-specific definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] + +pub mod raw; + +pub mod fs { + #![stable(feature = "raw_ext", since = "1.1.0")] + pub use sys::fs::MetadataExt; +} diff --git a/src/libstd/os/netbsd/raw.rs b/src/libstd/os/netbsd/raw.rs new file mode 100644 index 00000000000..f9898dfbdb5 --- /dev/null +++ b/src/libstd/os/netbsd/raw.rs @@ -0,0 +1,71 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! NetBSD/OpenBSD-specific raw type definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] + +use os::raw::c_long; +use os::unix::raw::{uid_t, gid_t}; + +#[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = i64; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = i32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type fflags_t = u32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type mode_t = u32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = i64; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; + +#[repr(C)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub struct stat { + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: mode_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_dev: dev_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: ino_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: nlink_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: uid_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: gid_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: dev_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: off_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: blkcnt_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: blksize_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_flags: fflags_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gen: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_birthtime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_birthtime_nsec: c_long, +} diff --git a/src/libstd/rt/args.rs b/src/libstd/rt/args.rs index d23a124a6ec..52697f00264 100644 --- a/src/libstd/rt/args.rs +++ b/src/libstd/rt/args.rs @@ -44,6 +44,7 @@ pub fn clone() -> Option>> { imp::clone() } target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] mod imp { use prelude::v1::*; diff --git a/src/libstd/rt/libunwind.rs b/src/libstd/rt/libunwind.rs index 8f75ae5ef5c..d99b31c9f2b 100644 --- a/src/libstd/rt/libunwind.rs +++ b/src/libstd/rt/libunwind.rs @@ -106,7 +106,7 @@ extern {} #[link(name = "unwind", kind = "static")] extern {} -#[cfg(any(target_os = "android", target_os = "openbsd"))] +#[cfg(any(target_os = "android", target_os = "netbsd", target_os = "openbsd"))] #[link(name = "gcc")] extern {} diff --git a/src/libstd/rtdeps.rs b/src/libstd/rtdeps.rs index be674c83e22..a395dbf8995 100644 --- a/src/libstd/rtdeps.rs +++ b/src/libstd/rtdeps.rs @@ -39,6 +39,7 @@ extern {} #[cfg(any(target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] #[link(name = "pthread")] extern {} diff --git a/src/libstd/sys/common/stack.rs b/src/libstd/sys/common/stack.rs index 11982ebc572..002e3b20c35 100644 --- a/src/libstd/sys/common/stack.rs +++ b/src/libstd/sys/common/stack.rs @@ -202,6 +202,7 @@ pub unsafe fn record_sp_limit(limit: usize) { target_arch = "powerpc", all(target_arch = "arm", target_os = "ios"), target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] unsafe fn target_record_sp_limit(_: usize) { } @@ -299,6 +300,7 @@ pub unsafe fn get_sp_limit() -> usize { target_arch = "powerpc", all(target_arch = "arm", target_os = "ios"), target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] #[inline(always)] unsafe fn target_get_sp_limit() -> usize { diff --git a/src/libstd/sys/unix/backtrace.rs b/src/libstd/sys/unix/backtrace.rs index b23a3eee1a1..a5d1595cfeb 100644 --- a/src/libstd/sys/unix/backtrace.rs +++ b/src/libstd/sys/unix/backtrace.rs @@ -363,6 +363,7 @@ fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void, let selfname = if cfg!(target_os = "freebsd") || cfg!(target_os = "dragonfly") || cfg!(target_os = "bitrig") || + cfg!(target_os = "netbsd") || cfg!(target_os = "openbsd") { env::current_exe().ok() } else { diff --git a/src/libstd/sys/unix/c.rs b/src/libstd/sys/unix/c.rs index 99a6731c57d..eeecf7f50f7 100644 --- a/src/libstd/sys/unix/c.rs +++ b/src/libstd/sys/unix/c.rs @@ -34,6 +34,7 @@ use libc; target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] pub const FIOCLEX: libc::c_ulong = 0x20006601; @@ -60,6 +61,7 @@ pub const _SC_GETPW_R_SIZE_MAX: libc::c_int = 70; target_os = "dragonfly"))] pub const _SC_GETPW_R_SIZE_MAX: libc::c_int = 71; #[cfg(any(target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] pub const _SC_GETPW_R_SIZE_MAX: libc::c_int = 101; #[cfg(target_os = "android")] @@ -82,6 +84,7 @@ pub struct passwd { target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] pub struct passwd { pub pw_name: *mut libc::c_char, @@ -321,6 +324,7 @@ mod signal_os { target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] mod signal_os { use libc; @@ -348,7 +352,7 @@ mod signal_os { pub struct sigset_t { bits: [u32; 4], } - #[cfg(any(target_os = "bitrig", target_os = "openbsd"))] + #[cfg(any(target_os = "bitrig", target_os = "netbsd", target_os = "openbsd"))] pub type sigset_t = libc::c_uint; // This structure has more fields, but we're not all that interested in @@ -365,7 +369,7 @@ mod signal_os { pub _status: libc::c_int, pub si_addr: *mut libc::c_void } - #[cfg(any(target_os = "bitrig", target_os = "openbsd"))] + #[cfg(any(target_os = "bitrig", target_os = "netbsd", target_os = "openbsd"))] #[repr(C)] pub struct siginfo { pub si_signo: libc::c_int, @@ -375,7 +379,7 @@ mod signal_os { } #[cfg(any(target_os = "macos", target_os = "ios", - target_os = "bitrig", target_os = "openbsd"))] + target_os = "bitrig", target_os = "netbsd", target_os = "openbsd"))] #[repr(C)] pub struct sigaction { pub sa_sigaction: sighandler_t, diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index c1a4e8cee9e..6fd20b940bb 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -26,6 +26,7 @@ use ops::Neg; #[cfg(target_os = "linux")] pub use os::linux as platform; #[cfg(target_os = "macos")] pub use os::macos as platform; #[cfg(target_os = "nacl")] pub use os::nacl as platform; +#[cfg(target_os = "netbsd")] pub use os::netbsd as platform; #[cfg(target_os = "openbsd")] pub use os::openbsd as platform; pub mod backtrace; diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index 5178d7b8fb1..334dd6b5f18 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -51,23 +51,13 @@ pub fn errno() -> i32 { __error() } - #[cfg(target_os = "bitrig")] - fn errno_location() -> *const c_int { - extern { - fn __errno() -> *const c_int; - } - unsafe { - __errno() - } - } - #[cfg(target_os = "dragonfly")] unsafe fn errno_location() -> *const c_int { extern { fn __dfly_error() -> *const c_int; } __dfly_error() } - #[cfg(target_os = "openbsd")] + #[cfg(any(target_os = "bitrig", target_os = "netbsd", target_os = "openbsd"))] unsafe fn errno_location() -> *const c_int { extern { fn __errno() -> *const c_int; } __errno() @@ -214,7 +204,7 @@ pub fn current_exe() -> io::Result { ::fs::read_link("/proc/curproc/file") } -#[cfg(any(target_os = "bitrig", target_os = "openbsd"))] +#[cfg(any(target_os = "bitrig", target_os = "netbsd", target_os = "openbsd"))] pub fn current_exe() -> io::Result { use sync::StaticMutex; static LOCK: StaticMutex = StaticMutex::new(); @@ -356,6 +346,7 @@ pub fn args() -> Args { target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] pub fn args() -> Args { use rt; diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index 695d0ddfaaf..cc78dd4e5ef 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -423,6 +423,7 @@ fn translate_status(status: c_int) -> ExitStatus { target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] mod imp { pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 } diff --git a/src/libstd/sys/unix/stack_overflow.rs b/src/libstd/sys/unix/stack_overflow.rs index 52494a17b9d..62689c39255 100644 --- a/src/libstd/sys/unix/stack_overflow.rs +++ b/src/libstd/sys/unix/stack_overflow.rs @@ -35,6 +35,7 @@ impl Drop for Handler { #[cfg(any(target_os = "linux", target_os = "macos", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] mod imp { use sys_common::stack; @@ -149,6 +150,7 @@ mod imp { #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd")))] mod imp { use libc; diff --git a/src/libstd/sys/unix/sync.rs b/src/libstd/sys/unix/sync.rs index 41e1e206a42..9c8a1f4ca40 100644 --- a/src/libstd/sys/unix/sync.rs +++ b/src/libstd/sys/unix/sync.rs @@ -55,6 +55,7 @@ extern { #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] mod os { use libc; diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index bb0e12e8df8..17804c8d81f 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -105,6 +105,7 @@ impl Thread { #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] pub fn set_name(name: &str) { extern { @@ -162,6 +163,7 @@ impl Drop for Thread { #[cfg(all(not(target_os = "linux"), not(target_os = "macos"), not(target_os = "bitrig"), + not(target_os = "netbsd"), not(target_os = "openbsd")))] pub mod guard { pub unsafe fn current() -> usize { 0 } @@ -173,6 +175,7 @@ pub mod guard { #[cfg(any(target_os = "linux", target_os = "macos", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] #[allow(unused_imports)] pub mod guard { @@ -193,6 +196,7 @@ pub mod guard { #[cfg(any(target_os = "macos", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] unsafe fn get_stack_start() -> *mut libc::c_void { current() as *mut libc::c_void @@ -258,7 +262,7 @@ pub mod guard { pthread_get_stacksize_np(pthread_self())) as usize } - #[cfg(any(target_os = "openbsd", target_os = "bitrig"))] + #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "bitrig"))] pub unsafe fn current() -> usize { #[repr(C)] struct stack_t { diff --git a/src/libstd/sys/unix/thread_local.rs b/src/libstd/sys/unix/thread_local.rs index 3afe84b2580..7238adfcc56 100644 --- a/src/libstd/sys/unix/thread_local.rs +++ b/src/libstd/sys/unix/thread_local.rs @@ -46,6 +46,7 @@ type pthread_key_t = ::libc::c_ulong; #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd"))] type pthread_key_t = ::libc::c_int; @@ -54,6 +55,7 @@ type pthread_key_t = ::libc::c_int; target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd")))] type pthread_key_t = ::libc::c_uint; diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs index 6b84baeca7d..db0d0f15061 100644 --- a/src/libstd/sys/unix/time.rs +++ b/src/libstd/sys/unix/time.rs @@ -80,6 +80,7 @@ mod inner { // OpenBSD provide it via libc #[cfg(not(any(target_os = "android", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd", target_env = "musl")))] #[link(name = "rt")] diff --git a/src/libstd/thread/scoped_tls.rs b/src/libstd/thread/scoped_tls.rs index 679902ec7ab..c2fad0aa89c 100644 --- a/src/libstd/thread/scoped_tls.rs +++ b/src/libstd/thread/scoped_tls.rs @@ -104,6 +104,7 @@ macro_rules! __scoped_thread_local_inner { #[cfg_attr(not(any(windows, target_os = "android", target_os = "ios", + target_os = "netbsd", target_os = "openbsd", target_arch = "aarch64")), thread_local)] @@ -215,6 +216,7 @@ impl ScopedKey { #[cfg(not(any(windows, target_os = "android", target_os = "ios", + target_os = "netbsd", target_os = "openbsd", target_arch = "aarch64", no_elf_tls)))] @@ -238,6 +240,7 @@ mod imp { #[cfg(any(windows, target_os = "android", target_os = "ios", + target_os = "netbsd", target_os = "openbsd", target_arch = "aarch64", no_elf_tls))] diff --git a/src/libsyntax/abi.rs b/src/libsyntax/abi.rs index 27e331893e5..50c86a80b4a 100644 --- a/src/libsyntax/abi.rs +++ b/src/libsyntax/abi.rs @@ -25,6 +25,7 @@ pub enum Os { OsiOS, OsDragonfly, OsBitrig, + OsNetbsd, OsOpenbsd, } @@ -138,6 +139,7 @@ impl fmt::Display for Os { OsFreebsd => "freebsd".fmt(f), OsDragonfly => "dragonfly".fmt(f), OsBitrig => "bitrig".fmt(f), + OsNetbsd => "netbsd".fmt(f), OsOpenbsd => "openbsd".fmt(f), } } diff --git a/src/test/parse-fail/issue-5806.rs b/src/test/parse-fail/issue-5806.rs index f5442313904..f6606a58eca 100644 --- a/src/test/parse-fail/issue-5806.rs +++ b/src/test/parse-fail/issue-5806.rs @@ -11,6 +11,7 @@ // ignore-windows // ignore-freebsd // ignore-openbsd +// ignore-netbsd // ignore-bitrig // compile-flags: -Z parse-only diff --git a/src/test/run-pass/intrinsic-alignment.rs b/src/test/run-pass/intrinsic-alignment.rs index fa97ef8fcd3..ef69946d7aa 100644 --- a/src/test/run-pass/intrinsic-alignment.rs +++ b/src/test/run-pass/intrinsic-alignment.rs @@ -22,6 +22,7 @@ mod rusti { target_os = "macos", target_os = "freebsd", target_os = "dragonfly", + target_os = "netbsd", target_os = "openbsd"))] mod m { #[main] diff --git a/src/test/run-pass/rec-align-u64.rs b/src/test/run-pass/rec-align-u64.rs index bae95bcb50f..fc032aa3ff0 100644 --- a/src/test/run-pass/rec-align-u64.rs +++ b/src/test/run-pass/rec-align-u64.rs @@ -40,6 +40,7 @@ struct Outer { target_os = "macos", target_os = "freebsd", target_os = "dragonfly", + target_os = "netbsd", target_os = "openbsd"))] mod m { #[cfg(target_arch = "x86")] diff --git a/src/test/run-pass/tcp-stress.rs b/src/test/run-pass/tcp-stress.rs index c47b95bec2b..dca65f03f69 100644 --- a/src/test/run-pass/tcp-stress.rs +++ b/src/test/run-pass/tcp-stress.rs @@ -9,8 +9,9 @@ // except according to those terms. // ignore-android needs extra network permissions -// ignore-openbsd system ulimit (Too many open files) // ignore-bitrig system ulimit (Too many open files) +// ignore-netbsd system ulimit (Too many open files) +// ignore-openbsd system ulimit (Too many open files) use std::io::prelude::*; use std::net::{TcpListener, TcpStream}; diff --git a/src/test/run-pass/x86stdcall.rs b/src/test/run-pass/x86stdcall.rs index b0bfb5c29c1..62cbb76c309 100644 --- a/src/test/run-pass/x86stdcall.rs +++ b/src/test/run-pass/x86stdcall.rs @@ -35,6 +35,7 @@ pub fn main() { target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", + target_os = "netbsd", target_os = "openbsd", target_os = "android"))] pub fn main() { } -- cgit 1.4.1-3-g733a5 From 83ee47b054deb5939be20d7d6ce03ad33d005424 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 30 Jun 2015 21:55:00 -0700 Subject: windows: Don't link rust_builtin This library has no shims which are actually needed on Windows now, so translate that last easy one into Rust and then don't link it at all on Windows. --- src/libstd/rt/util.rs | 15 +++++++++++---- src/libstd/rtdeps.rs | 4 ++-- src/libtest/lib.rs | 20 +++++++++++++++++--- src/rt/rust_builtin.c | 36 +++++------------------------------- 4 files changed, 35 insertions(+), 40 deletions(-) (limited to 'src/libstd/rt') diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index 04f36d99c8e..031fda089c8 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -13,7 +13,6 @@ use io::prelude::*; use env; use fmt; use intrinsics; -use libc::uintptr_t; use sync::atomic::{self, Ordering}; use sys::stdio::Stderr; @@ -22,10 +21,18 @@ use sys::stdio::Stderr; /// can't run correctly un-altered. Valgrind is there to help /// you notice weirdness in normal, un-doctored code paths! pub fn running_on_valgrind() -> bool { - extern { - fn rust_running_on_valgrind() -> uintptr_t; + return on_valgrind(); + #[cfg(windows)] + fn on_valgrind() -> bool { false } + + #[cfg(unix)] + fn on_valgrind() -> bool { + use libc::uintptr_t; + extern { + fn rust_running_on_valgrind() -> uintptr_t; + } + unsafe { rust_running_on_valgrind() != 0 } } - unsafe { rust_running_on_valgrind() != 0 } } /// Valgrind has a fixed-sized array (size around 2000) of segment descriptors diff --git a/src/libstd/rtdeps.rs b/src/libstd/rtdeps.rs index be674c83e22..b7839423e52 100644 --- a/src/libstd/rtdeps.rs +++ b/src/libstd/rtdeps.rs @@ -12,8 +12,8 @@ //! the standard library This varies per-platform, but these libraries are //! necessary for running libstd. -// All platforms need to link to rustrt -#[cfg(not(test))] +// A few small shims in C that haven't been translated to Rust yet +#[cfg(all(not(test), not(windows)))] #[link(name = "rust_builtin", kind = "static")] extern {} diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 0a3c350086c..724c0b2a892 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -872,7 +872,7 @@ fn run_tests(opts: &TestOpts, #[allow(deprecated)] fn get_concurrency() -> usize { - match env::var("RUST_TEST_THREADS") { + return match env::var("RUST_TEST_THREADS") { Ok(s) => { let opt_n: Option = s.parse().ok(); match opt_n { @@ -884,10 +884,24 @@ fn get_concurrency() -> usize { if std::rt::util::limit_thread_creation_due_to_osx_and_valgrind() { 1 } else { - extern { fn rust_get_num_cpus() -> libc::uintptr_t; } - unsafe { rust_get_num_cpus() as usize } + num_cpus() } } + }; + + #[cfg(windows)] + fn num_cpus() -> usize { + unsafe { + let mut sysinfo = std::mem::zeroed(); + libc::GetSystemInfo(&mut sysinfo); + sysinfo.dwNumberOfProcessors as usize + } + } + + #[cfg(unix)] + fn num_cpus() -> usize { + extern { fn rust_get_num_cpus() -> libc::uintptr_t; } + unsafe { rust_get_num_cpus() as usize } } } diff --git a/src/rt/rust_builtin.c b/src/rt/rust_builtin.c index 1a2917a1dd6..76a3debef59 100644 --- a/src/rt/rust_builtin.c +++ b/src/rt/rust_builtin.c @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#if !defined(_WIN32) + #include #include #include @@ -15,7 +17,6 @@ #include -#if !defined(_WIN32) #include #include #include @@ -23,12 +24,6 @@ #include #include #include -#else -#include -#include -#include -#include -#endif #ifdef __APPLE__ #include @@ -41,17 +36,8 @@ /* Foreign builtins. */ //include valgrind.h after stdint.h so that uintptr_t is defined for msys2 w64 -#ifndef _WIN32 #include "valgrind/valgrind.h" -#endif - -#if defined(_MSC_VER) -# define RUST_BUILTIN_API __declspec(dllexport) -#else -# define RUST_BUILTIN_API -#endif -#ifndef _WIN32 char* rust_list_dir_val(struct dirent* entry_ptr) { return entry_ptr->d_name; @@ -92,17 +78,8 @@ int rust_dirent_t_size() { return sizeof(struct dirent); } -#endif - -#if defined(_WIN32) -int -get_num_cpus() { - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - return (int) sysinfo.dwNumberOfProcessors; -} -#elif defined(__BSD__) +#if defined(__BSD__) int get_num_cpus() { /* swiped from http://stackoverflow.com/questions/150355/ @@ -136,7 +113,6 @@ get_num_cpus() { } #endif -RUST_BUILTIN_API uintptr_t rust_get_num_cpus() { return get_num_cpus(); @@ -144,11 +120,7 @@ rust_get_num_cpus() { uintptr_t rust_running_on_valgrind() { -#ifdef _WIN32 - return 0; -#else return RUNNING_ON_VALGRIND; -#endif } #if defined(__DragonFly__) @@ -484,6 +456,8 @@ const char * rust_current_exe() { #endif +#endif // !defined(_WIN32) + // // Local Variables: // mode: C++ -- cgit 1.4.1-3-g733a5 From d68b152c3e2feb6ee18bdf2c992098376dbb528c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 16 Jul 2015 11:59:53 -0700 Subject: std: Be resilient to failure in pthread_getattr_np This can fail on linux for various reasons, such as the /proc filesystem not being mounted. There are already many cases where we can't set up stack guards, so just don't worry about this case and communicate that no guard was enabled. I've confirmed that this allows the compiler to run in a chroot without /proc mounted. Closes #22642 --- src/libstd/rt/mod.rs | 4 +- src/libstd/sys/common/thread_info.rs | 8 ++-- src/libstd/sys/unix/thread.rs | 88 ++++++++++++++++++++---------------- src/libstd/sys/windows/thread.rs | 7 +-- 4 files changed, 60 insertions(+), 47 deletions(-) (limited to 'src/libstd/rt') diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 1729d20da20..0ac0d03e19d 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -96,7 +96,7 @@ fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize { // own fault handlers if we hit it. sys_common::stack::record_os_managed_stack_bounds(my_stack_bottom, my_stack_top); - sys::thread::guard::init(); + let main_guard = sys::thread::guard::init(); sys::stack_overflow::init(); // Next, set up the current Thread with the guard information we just @@ -104,7 +104,7 @@ fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize { // but we just do this to name the main thread and to give it correct // info about the stack bounds. let thread: Thread = NewThread::new(Some("
".to_string())); - thread_info::set(sys::thread::guard::main(), thread); + thread_info::set(main_guard, thread); // By default, some platforms will send a *signal* when a EPIPE error // would otherwise be delivered. This runtime doesn't install a SIGPIPE diff --git a/src/libstd/sys/common/thread_info.rs b/src/libstd/sys/common/thread_info.rs index ae55bae37aa..bb47c946e49 100644 --- a/src/libstd/sys/common/thread_info.rs +++ b/src/libstd/sys/common/thread_info.rs @@ -18,7 +18,7 @@ use thread::Thread; use thread::LocalKeyState; struct ThreadInfo { - stack_guard: usize, + stack_guard: Option, thread: Thread, } @@ -33,7 +33,7 @@ impl ThreadInfo { THREAD_INFO.with(move |c| { if c.borrow().is_none() { *c.borrow_mut() = Some(ThreadInfo { - stack_guard: 0, + stack_guard: None, thread: NewThread::new(None), }) } @@ -47,10 +47,10 @@ pub fn current_thread() -> Option { } pub fn stack_guard() -> Option { - ThreadInfo::with(|info| info.stack_guard) + ThreadInfo::with(|info| info.stack_guard).and_then(|o| o) } -pub fn set(stack_guard: usize, thread: Thread) { +pub fn set(stack_guard: Option, thread: Thread) { THREAD_INFO.with(|c| assert!(c.borrow().is_none())); THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo{ stack_guard: stack_guard, diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index 17804c8d81f..6be61f06926 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -166,9 +166,10 @@ impl Drop for Thread { not(target_os = "netbsd"), not(target_os = "openbsd")))] pub mod guard { - pub unsafe fn current() -> usize { 0 } - pub unsafe fn main() -> usize { 0 } - pub unsafe fn init() {} + use prelude::v1::*; + + pub unsafe fn current() -> Option { None } + pub unsafe fn init() -> Option { None } } @@ -179,6 +180,8 @@ pub mod guard { target_os = "openbsd"))] #[allow(unused_imports)] pub mod guard { + use prelude::v1::*; + use libc::{self, pthread_t}; use libc::funcs::posix88::mman::mmap; use libc::consts::os::posix88::{PROT_NONE, @@ -191,31 +194,38 @@ pub mod guard { use super::{pthread_self, pthread_attr_destroy}; use sys::os; - // These are initialized in init() and only read from after - static mut GUARD_PAGE: usize = 0; - #[cfg(any(target_os = "macos", target_os = "bitrig", target_os = "netbsd", target_os = "openbsd"))] - unsafe fn get_stack_start() -> *mut libc::c_void { - current() as *mut libc::c_void + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + current().map(|s| s as *mut libc::c_void) } #[cfg(any(target_os = "linux", target_os = "android"))] - unsafe fn get_stack_start() -> *mut libc::c_void { + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + use super::pthread_attr_init; + + let mut ret = None; let mut attr: libc::pthread_attr_t = mem::zeroed(); - assert_eq!(pthread_getattr_np(pthread_self(), &mut attr), 0); - let mut stackaddr = ptr::null_mut(); - let mut stacksize = 0; - assert_eq!(pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0); + assert_eq!(pthread_attr_init(&mut attr), 0); + if pthread_getattr_np(pthread_self(), &mut attr) == 0 { + let mut stackaddr = ptr::null_mut(); + let mut stacksize = 0; + assert_eq!(pthread_attr_getstack(&attr, &mut stackaddr, + &mut stacksize), 0); + ret = Some(stackaddr); + } assert_eq!(pthread_attr_destroy(&mut attr), 0); - stackaddr + ret } - pub unsafe fn init() { + pub unsafe fn init() -> Option { let psize = os::page_size(); - let mut stackaddr = get_stack_start(); + let mut stackaddr = match get_stack_start() { + Some(addr) => addr, + None => return None, + }; // Ensure stackaddr is page aligned! A parent process might // have reset RLIMIT_STACK to be non-page aligned. The @@ -245,25 +255,21 @@ pub mod guard { let offset = if cfg!(target_os = "linux") {2} else {1}; - GUARD_PAGE = stackaddr as usize + offset * psize; - } - - pub unsafe fn main() -> usize { - GUARD_PAGE + Some(stackaddr as usize + offset * psize) } #[cfg(target_os = "macos")] - pub unsafe fn current() -> usize { + pub unsafe fn current() -> Option { extern { fn pthread_get_stackaddr_np(thread: pthread_t) -> *mut libc::c_void; fn pthread_get_stacksize_np(thread: pthread_t) -> libc::size_t; } - (pthread_get_stackaddr_np(pthread_self()) as libc::size_t - - pthread_get_stacksize_np(pthread_self())) as usize + Some((pthread_get_stackaddr_np(pthread_self()) as libc::size_t - + pthread_get_stacksize_np(pthread_self())) as usize) } #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "bitrig"))] - pub unsafe fn current() -> usize { + pub unsafe fn current() -> Option { #[repr(C)] struct stack_t { ss_sp: *mut libc::c_void, @@ -280,30 +286,36 @@ pub mod guard { assert_eq!(pthread_stackseg_np(pthread_self(), &mut current_stack), 0); let extra = if cfg!(target_os = "bitrig") {3} else {1} * os::page_size(); - if pthread_main_np() == 1 { + Some(if pthread_main_np() == 1 { // main thread current_stack.ss_sp as usize - current_stack.ss_size as usize + extra } else { // new thread current_stack.ss_sp as usize - current_stack.ss_size as usize - } + }) } #[cfg(any(target_os = "linux", target_os = "android"))] - pub unsafe fn current() -> usize { + pub unsafe fn current() -> Option { + use super::pthread_attr_init; + + let mut ret = None; let mut attr: libc::pthread_attr_t = mem::zeroed(); - assert_eq!(pthread_getattr_np(pthread_self(), &mut attr), 0); - let mut guardsize = 0; - assert_eq!(pthread_attr_getguardsize(&attr, &mut guardsize), 0); - if guardsize == 0 { - panic!("there is no guard page"); + assert_eq!(pthread_attr_init(&mut attr), 0); + if pthread_getattr_np(pthread_self(), &mut attr) == 0 { + let mut guardsize = 0; + assert_eq!(pthread_attr_getguardsize(&attr, &mut guardsize), 0); + if guardsize == 0 { + panic!("there is no guard page"); + } + let mut stackaddr = ptr::null_mut(); + let mut size = 0; + assert_eq!(pthread_attr_getstack(&attr, &mut stackaddr, &mut size), 0); + + ret = Some(stackaddr as usize + guardsize as usize); } - let mut stackaddr = ptr::null_mut(); - let mut size = 0; - assert_eq!(pthread_attr_getstack(&attr, &mut stackaddr, &mut size), 0); assert_eq!(pthread_attr_destroy(&mut attr), 0); - - stackaddr as usize + guardsize as usize + return ret } #[cfg(any(target_os = "linux", target_os = "android"))] diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs index 50dfee4ab10..42805c2ac52 100644 --- a/src/libstd/sys/windows/thread.rs +++ b/src/libstd/sys/windows/thread.rs @@ -86,7 +86,8 @@ impl Thread { } pub mod guard { - pub unsafe fn main() -> usize { 0 } - pub unsafe fn current() -> usize { 0 } - pub unsafe fn init() {} + use prelude::v1::*; + + pub unsafe fn current() -> Option { None } + pub unsafe fn init() -> Option { None } } -- cgit 1.4.1-3-g733a5 From c35b2bd226736925961ca6853b2ef29e8094cd90 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 20 Jul 2015 13:27:38 -0700 Subject: trans: Move rust_try into the compiler This commit moves the IR files in the distribution, rust_try.ll, rust_try_msvc_64.ll, and rust_try_msvc_32.ll into the compiler from the main distribution. There's a few reasons for this change: * LLVM changes its IR syntax from time to time, so it's very difficult to have these files build across many LLVM versions simultaneously. We'll likely want to retain this ability for quite some time into the future. * The implementation of these files is closely tied to the compiler and runtime itself, so it makes sense to fold it into a location which can do more platform-specific checks for various implementation details (such as MSVC 32 vs 64-bit). * This removes LLVM as a build-time dependency of the standard library. This may end up becoming very useful if we move towards building the standard library with Cargo. In the immediate future, however, this commit should restore compatibility with LLVM 3.5 and 3.6. --- mk/rt.mk | 18 -- src/libcore/intrinsics.rs | 6 + src/librustc/middle/lang_items.rs | 2 + src/librustc/middle/weak_lang_items.rs | 2 +- src/librustc_llvm/lib.rs | 6 +- src/librustc_trans/trans/build.rs | 4 + src/librustc_trans/trans/builder.rs | 6 + src/librustc_trans/trans/callee.rs | 17 +- src/librustc_trans/trans/cleanup.rs | 52 +---- src/librustc_trans/trans/closure.rs | 16 +- src/librustc_trans/trans/common.rs | 51 +++++ src/librustc_trans/trans/context.rs | 7 + src/librustc_trans/trans/declare.rs | 21 +- src/librustc_trans/trans/foreign.rs | 4 +- src/librustc_trans/trans/intrinsic.rs | 344 ++++++++++++++++++++++++++++++- src/librustc_trans/trans/meth.rs | 4 +- src/librustc_trans/trans/monomorphize.rs | 7 +- src/librustc_typeck/check/mod.rs | 15 ++ src/libstd/rt/unwind/gcc.rs | 62 +++--- src/libstd/rt/unwind/mod.rs | 19 +- src/libstd/rt/unwind/seh.rs | 5 +- src/rt/rust_try.ll | 54 ----- src/rt/rust_try_msvc_32.ll | 42 ---- src/rt/rust_try_msvc_64.ll | 80 ------- src/rustllvm/RustWrapper.cpp | 3 +- 25 files changed, 519 insertions(+), 328 deletions(-) delete mode 100644 src/rt/rust_try.ll delete mode 100644 src/rt/rust_try_msvc_32.ll delete mode 100644 src/rt/rust_try_msvc_64.ll (limited to 'src/libstd/rt') diff --git a/mk/rt.mk b/mk/rt.mk index c70f9e8a37a..69277e774e4 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -54,15 +54,6 @@ NATIVE_DEPS_miniz_$(1) = miniz.c NATIVE_DEPS_rust_builtin_$(1) := rust_builtin.c \ rust_android_dummy.c NATIVE_DEPS_rustrt_native_$(1) := arch/$$(HOST_$(1))/record_sp.S -ifeq ($$(findstring msvc,$(1)),msvc) -ifeq ($$(findstring i686,$(1)),i686) -NATIVE_DEPS_rustrt_native_$(1) += rust_try_msvc_32.ll -else -NATIVE_DEPS_rustrt_native_$(1) += rust_try_msvc_64.ll -endif -else -NATIVE_DEPS_rustrt_native_$(1) += rust_try.ll -endif NATIVE_DEPS_rust_test_helpers_$(1) := rust_test_helpers.c NATIVE_DEPS_morestack_$(1) := arch/$$(HOST_$(1))/morestack.S @@ -76,14 +67,6 @@ NATIVE_DEPS_morestack_$(1) := arch/$$(HOST_$(1))/morestack.S RT_OUTPUT_DIR_$(1) := $(1)/rt -$$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.ll $$(MKFILE_DEPS) \ - $$(LLVM_CONFIG_$$(CFG_BUILD)) - @mkdir -p $$(@D) - @$$(call E, compile: $$@) - $$(Q)$$(LLC_$$(CFG_BUILD)) $$(CFG_LLC_FLAGS_$(1)) \ - -filetype=obj -mtriple=$$(CFG_LLVM_TARGET_$(1)) \ - -relocation-model=pic -o $$@ $$< - $$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.c $$(MKFILE_DEPS) @mkdir -p $$(@D) @$$(call E, compile: $$@) @@ -122,7 +105,6 @@ define THIRD_PARTY_LIB OBJS_$(2)_$(1) := $$(NATIVE_DEPS_$(2)_$(1):%=$$(RT_OUTPUT_DIR_$(1))/%) OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.c=.o) OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.cpp=.o) -OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.ll=.o) OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.S=.o) NATIVE_$(2)_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),$(2)) $$(RT_OUTPUT_DIR_$(1))/$$(NATIVE_$(2)_$(1)): $$(OBJS_$(2)_$(1)) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 74901553149..ef022179772 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -602,4 +602,10 @@ extern "rust-intrinsic" { /// Returns the value of the discriminant for the variant in 'v', /// cast to a `u64`; if `T` has no discriminant, returns 0. pub fn discriminant_value(v: &T) -> u64; + + /// Rust's "try catch" construct which invokes the function pointer `f` with + /// the data pointer `data`, returning the exception payload if an exception + /// is thrown (aka the thread panics). + #[cfg(not(stage0))] + pub fn try(f: fn(*mut u8), data: *mut u8) -> *mut u8; } diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index cf528e0c8a9..f7cd94f30af 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -326,6 +326,8 @@ lets_do_this! { StartFnLangItem, "start", start_fn; EhPersonalityLangItem, "eh_personality", eh_personality; + EhPersonalityCatchLangItem, "eh_personality_catch", eh_personality_catch; + MSVCTryFilterLangItem, "msvc_try_filter", msvc_try_filter; ExchangeHeapLangItem, "exchange_heap", exchange_heap; OwnedBoxLangItem, "owned_box", owned_box; diff --git a/src/librustc/middle/weak_lang_items.rs b/src/librustc/middle/weak_lang_items.rs index 60a9ffc7d2e..72fda9a7ae0 100644 --- a/src/librustc/middle/weak_lang_items.rs +++ b/src/librustc/middle/weak_lang_items.rs @@ -119,7 +119,7 @@ impl<'a, 'v> Visitor<'v> for Context<'a> { ) } weak_lang_items! { - panic_fmt, PanicFmtLangItem, rust_begin_unwind; + panic_fmt, PanicFmtLangItem, rust_begin_unwind; stack_exhausted, StackExhaustedLangItem, rust_stack_exhausted; eh_personality, EhPersonalityLangItem, rust_eh_personality; } diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 7734704b021..83f8619c5ee 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -134,7 +134,7 @@ pub enum DLLStorageClassTypes { } bitflags! { - flags Attribute : u32 { + flags Attribute : u64 { const ZExt = 1 << 0, const SExt = 1 << 1, const NoReturn = 1 << 2, @@ -161,6 +161,7 @@ bitflags! { const ReturnsTwice = 1 << 29, const UWTable = 1 << 30, const NonLazyBind = 1 << 31, + const OptimizeNone = 1 << 42, } } @@ -2193,7 +2194,8 @@ pub fn ConstFCmp(pred: RealPredicate, v1: ValueRef, v2: ValueRef) -> ValueRef { pub fn SetFunctionAttribute(fn_: ValueRef, attr: Attribute) { unsafe { - LLVMAddFunctionAttribute(fn_, FunctionIndex as c_uint, attr.bits() as uint64_t) + LLVMAddFunctionAttribute(fn_, FunctionIndex as c_uint, + attr.bits() as uint64_t) } } diff --git a/src/librustc_trans/trans/build.rs b/src/librustc_trans/trans/build.rs index 3e4452a23b9..5a3fcc8d27f 100644 --- a/src/librustc_trans/trans/build.rs +++ b/src/librustc_trans/trans/build.rs @@ -1042,6 +1042,10 @@ pub fn LandingPad(cx: Block, ty: Type, pers_fn: ValueRef, B(cx).landing_pad(ty, pers_fn, num_clauses, cx.fcx.llfn) } +pub fn AddClause(cx: Block, landing_pad: ValueRef, clause: ValueRef) { + B(cx).add_clause(landing_pad, clause) +} + pub fn SetCleanup(cx: Block, landing_pad: ValueRef) { B(cx).set_cleanup(landing_pad) } diff --git a/src/librustc_trans/trans/builder.rs b/src/librustc_trans/trans/builder.rs index e39fc18dc7b..107ae378ac4 100644 --- a/src/librustc_trans/trans/builder.rs +++ b/src/librustc_trans/trans/builder.rs @@ -937,6 +937,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + pub fn add_clause(&self, landing_pad: ValueRef, clause: ValueRef) { + unsafe { + llvm::LLVMAddClause(landing_pad, clause); + } + } + pub fn set_cleanup(&self, landing_pad: ValueRef) { self.count_insn("setcleanup"); unsafe { diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index debc8dd59c0..7900000d3a9 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -620,16 +620,17 @@ pub fn trans_lang_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, }, ArgVals(args), dest) } -/// This behemoth of a function translates function calls. Unfortunately, in order to generate more -/// efficient LLVM output at -O0, it has quite a complex signature (refactoring this into two -/// functions seems like a good idea). +/// This behemoth of a function translates function calls. Unfortunately, in +/// order to generate more efficient LLVM output at -O0, it has quite a complex +/// signature (refactoring this into two functions seems like a good idea). /// -/// In particular, for lang items, it is invoked with a dest of None, and in that case the return -/// value contains the result of the fn. The lang item must not return a structural type or else -/// all heck breaks loose. +/// In particular, for lang items, it is invoked with a dest of None, and in +/// that case the return value contains the result of the fn. The lang item must +/// not return a structural type or else all heck breaks loose. /// -/// For non-lang items, `dest` is always Some, and hence the result is written into memory -/// somewhere. Nonetheless we return the actual return value of the function. +/// For non-lang items, `dest` is always Some, and hence the result is written +/// into memory somewhere. Nonetheless we return the actual return value of the +/// function. pub fn trans_call_inner<'a, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, debug_loc: DebugLoc, get_callee: F, diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs index 1891320313a..37722d5a549 100644 --- a/src/librustc_trans/trans/cleanup.rs +++ b/src/librustc_trans/trans/cleanup.rs @@ -122,11 +122,9 @@ pub use self::Heap::*; use llvm::{BasicBlockRef, ValueRef}; use trans::base; use trans::build; -use trans::callee; use trans::common; -use trans::common::{Block, FunctionContext, ExprId, NodeIdAndSpan}; +use trans::common::{Block, FunctionContext, NodeIdAndSpan}; use trans::debuginfo::{DebugLoc, ToDebugLoc}; -use trans::declare; use trans::glue; use middle::region; use trans::type_::Type; @@ -833,53 +831,7 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx &[Type::i8p(self.ccx), Type::i32(self.ccx)], false); - // The exception handling personality function. - // - // If our compilation unit has the `eh_personality` lang item somewhere - // within it, then we just need to translate that. Otherwise, we're - // building an rlib which will depend on some upstream implementation of - // this function, so we just codegen a generic reference to it. We don't - // specify any of the types for the function, we just make it a symbol - // that LLVM can later use. - // - // Note that MSVC is a little special here in that we don't use the - // `eh_personality` lang item at all. Currently LLVM has support for - // both Dwarf and SEH unwind mechanisms for MSVC targets and uses the - // *name of the personality function* to decide what kind of unwind side - // tables/landing pads to emit. It looks like Dwarf is used by default, - // injecting a dependency on the `_Unwind_Resume` symbol for resuming - // an "exception", but for MSVC we want to force SEH. This means that we - // can't actually have the personality function be our standard - // `rust_eh_personality` function, but rather we wired it up to the - // CRT's custom personality function, which forces LLVM to consider - // landing pads as "landing pads for SEH". - let target = &self.ccx.sess().target.target; - let llpersonality = match pad_bcx.tcx().lang_items.eh_personality() { - Some(def_id) if !target.options.is_like_msvc => { - callee::trans_fn_ref(pad_bcx.ccx(), def_id, ExprId(0), - pad_bcx.fcx.param_substs).val - } - _ => { - let mut personality = self.ccx.eh_personality().borrow_mut(); - match *personality { - Some(llpersonality) => llpersonality, - None => { - let name = if !target.options.is_like_msvc { - "rust_eh_personality" - } else if target.arch == "x86" { - "_except_handler3" - } else { - "__C_specific_handler" - }; - let fty = Type::variadic_func(&[], &Type::i32(self.ccx)); - let f = declare::declare_cfn(self.ccx, name, fty, - self.ccx.tcx().types.i32); - *personality = Some(f); - f - } - } - } - }; + let llpersonality = pad_bcx.fcx.eh_personality(); // The only landing pad clause will be 'cleanup' let llretval = build::LandingPad(pad_bcx, llretty, llpersonality, 1); diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs index d813e9dbf40..f00029ec2ff 100644 --- a/src/librustc_trans/trans/closure.rs +++ b/src/librustc_trans/trans/closure.rs @@ -163,11 +163,10 @@ pub fn get_or_create_declaration_if_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tc mangle_internal_name_by_path_and_seq(path, "closure") }); - // Currently there’s only a single user of get_or_create_declaration_if_closure and it - // unconditionally defines the function, therefore we use define_* here. - let llfn = declare::define_internal_rust_fn(ccx, &symbol[..], function_type).unwrap_or_else(||{ - ccx.sess().bug(&format!("symbol `{}` already defined", symbol)); - }); + // Currently there’s only a single user of + // get_or_create_declaration_if_closure and it unconditionally defines the + // function, therefore we use define_* here. + let llfn = declare::define_internal_rust_fn(ccx, &symbol[..], function_type); // set an inline hint for all closures attributes::inline(llfn, attributes::InlineAttr::Hint); @@ -388,11 +387,8 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( // Create the by-value helper. let function_name = link::mangle_internal_name_by_type_and_seq(ccx, llonce_fn_ty, "once_shim"); - let lloncefn = declare::define_internal_rust_fn(ccx, &function_name[..], llonce_fn_ty) - .unwrap_or_else(||{ - ccx.sess().bug(&format!("symbol `{}` already defined", function_name)); - }); - + let lloncefn = declare::define_internal_rust_fn(ccx, &function_name, + llonce_fn_ty); let sig = tcx.erase_late_bound_regions(&llonce_bare_fn_ty.sig); let (block_arena, fcx): (TypedArena<_>, FunctionContext); block_arena = TypedArena::new(); diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index d7d3be699cb..1e87053c2ae 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -25,6 +25,7 @@ use middle::lang_items::LangItem; use middle::subst::{self, Substs}; use trans::base; use trans::build; +use trans::callee; use trans::cleanup; use trans::consts; use trans::datum; @@ -479,6 +480,56 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> { pub fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { type_needs_drop_given_env(self.ccx.tcx(), ty, &self.param_env) } + + pub fn eh_personality(&self) -> ValueRef { + // The exception handling personality function. + // + // If our compilation unit has the `eh_personality` lang item somewhere + // within it, then we just need to translate that. Otherwise, we're + // building an rlib which will depend on some upstream implementation of + // this function, so we just codegen a generic reference to it. We don't + // specify any of the types for the function, we just make it a symbol + // that LLVM can later use. + // + // Note that MSVC is a little special here in that we don't use the + // `eh_personality` lang item at all. Currently LLVM has support for + // both Dwarf and SEH unwind mechanisms for MSVC targets and uses the + // *name of the personality function* to decide what kind of unwind side + // tables/landing pads to emit. It looks like Dwarf is used by default, + // injecting a dependency on the `_Unwind_Resume` symbol for resuming + // an "exception", but for MSVC we want to force SEH. This means that we + // can't actually have the personality function be our standard + // `rust_eh_personality` function, but rather we wired it up to the + // CRT's custom personality function, which forces LLVM to consider + // landing pads as "landing pads for SEH". + let target = &self.ccx.sess().target.target; + match self.ccx.tcx().lang_items.eh_personality() { + Some(def_id) if !target.options.is_like_msvc => { + callee::trans_fn_ref(self.ccx, def_id, ExprId(0), + self.param_substs).val + } + _ => { + let mut personality = self.ccx.eh_personality().borrow_mut(); + match *personality { + Some(llpersonality) => llpersonality, + None => { + let name = if !target.options.is_like_msvc { + "rust_eh_personality" + } else if target.arch == "x86" { + "_except_handler3" + } else { + "__C_specific_handler" + }; + let fty = Type::variadic_func(&[], &Type::i32(self.ccx)); + let f = declare::declare_cfn(self.ccx, name, fty, + self.ccx.tcx().types.i32); + *personality = Some(f); + f + } + } + } + } + } } // Basic block context. We create a block context for each basic block diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 5a4bd7ff3a1..760a4ae827a 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -142,6 +142,7 @@ pub struct LocalCrateContext<'tcx> { dbg_cx: Option>, eh_personality: RefCell>, + rust_try_fn: RefCell>, intrinsics: RefCell>, @@ -461,6 +462,7 @@ impl<'tcx> LocalCrateContext<'tcx> { closure_vals: RefCell::new(FnvHashMap()), dbg_cx: dbg_cx, eh_personality: RefCell::new(None), + rust_try_fn: RefCell::new(None), intrinsics: RefCell::new(FnvHashMap()), n_llvm_insns: Cell::new(0), trait_cache: RefCell::new(FnvHashMap()), @@ -726,6 +728,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &self.local.eh_personality } + pub fn rust_try_fn<'a>(&'a self) -> &'a RefCell> { + &self.local.rust_try_fn + } + fn intrinsics<'a>(&'a self) -> &'a RefCell> { &self.local.intrinsics } @@ -923,6 +929,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option void); ifn!("llvm.expect.i1", fn(i1, i1) -> i1); + ifn!("llvm.eh.typeid.for", fn(i8p) -> t_i32); // Some intrinsics were introduced in later versions of LLVM, but they have // fallbacks in libc or libm and such. diff --git a/src/librustc_trans/trans/declare.rs b/src/librustc_trans/trans/declare.rs index b29da9d560f..c802de91e38 100644 --- a/src/librustc_trans/trans/declare.rs +++ b/src/librustc_trans/trans/declare.rs @@ -176,8 +176,8 @@ pub fn define_global(ccx: &CrateContext, name: &str, ty: Type) -> Option Option { +pub fn define_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, + fn_type: Type, output: ty::FnOutput) -> Option { if get_defined_value(ccx, name).is_some() { None } else { @@ -224,20 +224,21 @@ pub fn define_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, /// Declare a Rust function with an intention to define it. /// /// Use this function when you intend to define a function. This function will -/// return None if the name already has a definition associated with it. In that -/// case an error should be reported to the user, because it usually happens due -/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes). -pub fn define_internal_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, - fn_type: ty::Ty<'tcx>) -> Option { +/// return panic if the name already has a definition associated with it. This +/// can happen with #[no_mangle] or #[export_name], for example. +pub fn define_internal_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + name: &str, + fn_type: ty::Ty<'tcx>) -> ValueRef { if get_defined_value(ccx, name).is_some() { - None + ccx.sess().fatal(&format!("symbol `{}` already defined", name)) } else { - Some(declare_internal_rust_fn(ccx, name, fn_type)) + declare_internal_rust_fn(ccx, name, fn_type) } } -/// Get defined or externally defined (AvailableExternally linkage) value by name. +/// Get defined or externally defined (AvailableExternally linkage) value by +/// name. fn get_defined_value(ccx: &CrateContext, name: &str) -> Option { debug!("get_defined_value(name={:?})", name); let namebuf = CString::new(name).unwrap_or_else(|_|{ diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs index 9e8c0189a97..e102e3cd062 100644 --- a/src/librustc_trans/trans/foreign.rs +++ b/src/librustc_trans/trans/foreign.rs @@ -627,9 +627,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ccx.tcx().map.path_to_string(id), id, t); - let llfn = declare::define_internal_rust_fn(ccx, &ps[..], t).unwrap_or_else(||{ - ccx.sess().bug(&format!("symbol `{}` already defined", ps)); - }); + let llfn = declare::define_internal_rust_fn(ccx, &ps, t); attributes::from_fn_attrs(ccx, attrs, llfn); base::trans_fn(ccx, decl, body, llfn, param_substs, id, &[]); llfn diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index b449c3ad060..e78218fd10d 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -10,6 +10,7 @@ #![allow(non_upper_case_globals)] +use arena::TypedArena; use llvm; use llvm::{SequentiallyConsistent, Acquire, Release, AtomicXchg, ValueRef, TypeKind}; use middle::subst; @@ -23,6 +24,7 @@ use trans::cleanup::CleanupMethods; use trans::common::*; use trans::datum::*; use trans::debuginfo::DebugLoc; +use trans::declare; use trans::expr; use trans::glue; use trans::type_of::*; @@ -31,7 +33,8 @@ use trans::machine; use trans::machine::llsize_of; use trans::type_::Type; use middle::ty::{self, Ty, HasTypeFlags}; -use syntax::abi::RustIntrinsic; +use middle::subst::Substs; +use syntax::abi::{self, RustIntrinsic}; use syntax::ast; use syntax::parse::token; @@ -302,6 +305,42 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, } } + let call_debug_location = DebugLoc::At(call_info.id, call_info.span); + + // For `try` we need some custom control flow + if &name[..] == "try" { + if let callee::ArgExprs(ref exprs) = args { + let (func, data) = if exprs.len() != 2 { + ccx.sess().bug("expected two exprs as arguments for \ + `try` intrinsic"); + } else { + (&exprs[0], &exprs[1]) + }; + + // translate arguments + let func = unpack_datum!(bcx, expr::trans(bcx, func)); + let func = unpack_datum!(bcx, func.to_rvalue_datum(bcx, "func")); + let data = unpack_datum!(bcx, expr::trans(bcx, data)); + let data = unpack_datum!(bcx, data.to_rvalue_datum(bcx, "data")); + + let dest = match dest { + expr::SaveIn(d) => d, + expr::Ignore => alloc_ty(bcx, tcx.mk_mut_ptr(tcx.types.i8), + "try_result"), + }; + + // do the invoke + bcx = try_intrinsic(bcx, func.val, data.val, dest, + call_debug_location); + + fcx.pop_and_trans_custom_cleanup_scope(bcx, cleanup_scope); + return Result::new(bcx, dest); + } else { + ccx.sess().bug("expected two exprs as arguments for \ + `try` intrinsic"); + } + } + // Push the arguments. let mut llargs = Vec::new(); bcx = callee::trans_args(bcx, @@ -314,8 +353,6 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean(); - let call_debug_location = DebugLoc::At(call_info.id, call_info.span); - // These are the only intrinsic functions that diverge. if &name[..] == "abort" { let llfn = ccx.get_intrinsic(&("llvm.trap")); @@ -989,3 +1026,304 @@ fn with_overflow_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ret } } + +fn try_intrinsic<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + func: ValueRef, + data: ValueRef, + dest: ValueRef, + dloc: DebugLoc) -> Block<'blk, 'tcx> { + if bcx.sess().no_landing_pads() { + Call(bcx, func, &[data], None, dloc); + Store(bcx, C_null(Type::i8p(bcx.ccx())), dest); + bcx + } else if bcx.sess().target.target.options.is_like_msvc { + trans_msvc_try(bcx, func, data, dest, dloc) + } else { + trans_gnu_try(bcx, func, data, dest, dloc) + } +} + +// MSVC's definition of the `rust_try` function. The exact implementation here +// is a little different than the GNU (standard) version below, not only because +// of the personality function but also because of the other fiddly bits about +// SEH. LLVM also currently requires us to structure this a very particular way +// as explained below. +// +// Like with the GNU version we generate a shim wrapper +fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + func: ValueRef, + data: ValueRef, + dest: ValueRef, + dloc: DebugLoc) -> Block<'blk, 'tcx> { + let llfn = get_rust_try_fn(bcx.fcx, &mut |try_fn_ty, output| { + let ccx = bcx.ccx(); + let dloc = DebugLoc::None; + let rust_try = declare::define_internal_rust_fn(ccx, "__rust_try", + try_fn_ty); + let (fcx, block_arena); + block_arena = TypedArena::new(); + fcx = new_fn_ctxt(ccx, rust_try, ast::DUMMY_NODE_ID, false, + output, ccx.tcx().mk_substs(Substs::trans_empty()), + None, &block_arena); + let bcx = init_function(&fcx, true, output); + let then = fcx.new_temp_block("then"); + let catch = fcx.new_temp_block("catch"); + let catch_return = fcx.new_temp_block("catch-return"); + let catch_resume = fcx.new_temp_block("catch-resume"); + let personality = fcx.eh_personality(); + + let eh_typeid_for = ccx.get_intrinsic(&"llvm.eh.typeid.for"); + let rust_try_filter = match bcx.tcx().lang_items.msvc_try_filter() { + Some(did) => callee::trans_fn_ref(ccx, did, ExprId(0), + bcx.fcx.param_substs).val, + None => bcx.sess().bug("msvc_try_filter not defined"), + }; + + // Type indicator for the exception being thrown, not entirely sure + // what's going on here but it's what all the examples in LLVM use. + let lpad_ty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], + false); + + llvm::SetFunctionAttribute(rust_try, llvm::Attribute::NoInline); + llvm::SetFunctionAttribute(rust_try, llvm::Attribute::OptimizeNone); + let func = llvm::get_param(rust_try, 0); + let data = llvm::get_param(rust_try, 1); + + // Invoke the function, specifying our two temporary landing pads as the + // ext point. After the invoke we've terminated our basic block. + Invoke(bcx, func, &[data], then.llbb, catch.llbb, None, dloc); + + // All the magic happens in this landing pad, and this is basically the + // only landing pad in rust tagged with "catch" to indicate that we're + // catching an exception. The other catch handlers in the GNU version + // below just catch *all* exceptions, but that's because most exceptions + // are already filtered out by the gnu personality function. + // + // For MSVC we're just using a standard personality function that we + // can't customize (e.g. _except_handler3 or __C_specific_handler), so + // we need to do the exception filtering ourselves. This is currently + // performed by the `__rust_try_filter` function. This function, + // specified in the landingpad instruction, will be invoked by Windows + // SEH routines and will return whether the exception in question can be + // caught (aka the Rust runtime is the one that threw the exception). + // + // To get this to compile (currently LLVM segfaults if it's not in this + // particular structure), when the landingpad is executing we test to + // make sure that the ID of the exception being thrown is indeed the one + // that we were expecting. If it's not, we resume the exception, and + // otherwise we return the pointer that we got Full disclosure: It's not + // clear to me what this `llvm.eh.typeid` stuff is doing *other* then + // just allowing LLVM to compile this file without segfaulting. I would + // expect the entire landing pad to just be: + // + // %vals = landingpad ... + // %ehptr = extractvalue { i8*, i32 } %vals, 0 + // ret i8* %ehptr + // + // but apparently LLVM chokes on this, so we do the more complicated + // thing to placate it. + let vals = LandingPad(catch, lpad_ty, personality, 1); + let rust_try_filter = BitCast(catch, rust_try_filter, Type::i8p(ccx)); + AddClause(catch, vals, rust_try_filter); + let ehptr = ExtractValue(catch, vals, 0); + let sel = ExtractValue(catch, vals, 1); + let filter_sel = Call(catch, eh_typeid_for, &[rust_try_filter], None, + dloc); + let is_filter = ICmp(catch, llvm::IntEQ, sel, filter_sel, dloc); + CondBr(catch, is_filter, catch_return.llbb, catch_resume.llbb, dloc); + + // Our "catch-return" basic block is where we've determined that we + // actually need to catch this exception, in which case we just return + // the exception pointer. + Ret(catch_return, ehptr, dloc); + + // The "catch-resume" block is where we're running this landing pad but + // we actually need to not catch the exception, so just resume the + // exception to return. + Resume(catch_resume, vals); + + // On the successful branch we just return null. + Ret(then, C_null(Type::i8p(ccx)), dloc); + + return rust_try + }); + + // Note that no invoke is used here because by definition this function + // can't panic (that's what it's catching). + let ret = Call(bcx, llfn, &[func, data], None, dloc); + Store(bcx, ret, dest); + return bcx; +} + +// Definition of the standard "try" function for Rust using the GNU-like model +// of exceptions (e.g. the normal semantics of LLVM's landingpad and invoke +// instructions). +// +// This translation is a little surprising for two reasons: +// +// 1. We always call a shim function instead of inlining the call to `invoke` +// manually here. This is done because in LLVM we're only allowed to have one +// personality per function definition. The call to the `try` intrinsic is +// being inlined into the function calling it, and that function may already +// have other personality functions in play. By calling a shim we're +// guaranteed that our shim will have the right personality function. +// +// 2. Instead of making one shim (explained above), we make two shims! The +// reason for this has to do with the technical details about the +// implementation of unwinding in the runtime, but the tl;dr; is that the +// outer shim's personality function says "catch rust exceptions" and the +// inner shim's landing pad will not `resume` the exception being thrown. +// This means that the outer shim's landing pad is never run and the inner +// shim's return value is the return value of the whole call. +// +// The double-shim aspect is currently done for implementation ease on the +// runtime side of things, and more info can be found in +// src/libstd/rt/unwind/gcc.rs. +fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + func: ValueRef, + data: ValueRef, + dest: ValueRef, + dloc: DebugLoc) -> Block<'blk, 'tcx> { + let llfn = get_rust_try_fn(bcx.fcx, &mut |try_fn_ty, output| { + let ccx = bcx.ccx(); + let dloc = DebugLoc::None; + + // Type indicator for the exception being thrown, not entirely sure + // what's going on here but it's what all the examples in LLVM use. + let lpad_ty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], + false); + + // Define the "inner try" shim + let rust_try_inner = declare::define_internal_rust_fn(ccx, + "__rust_try_inner", + try_fn_ty); + trans_rust_try(ccx, rust_try_inner, lpad_ty, bcx.fcx.eh_personality(), + output, dloc, &mut |bcx, then, catch| { + let func = llvm::get_param(rust_try_inner, 0); + let data = llvm::get_param(rust_try_inner, 1); + Invoke(bcx, func, &[data], then.llbb, catch.llbb, None, dloc); + C_null(Type::i8p(ccx)) + }); + + // Define the "outer try" shim. + let rust_try = declare::define_internal_rust_fn(ccx, "__rust_try", + try_fn_ty); + let catch_pers = match bcx.tcx().lang_items.eh_personality_catch() { + Some(did) => callee::trans_fn_ref(ccx, did, ExprId(0), + bcx.fcx.param_substs).val, + None => bcx.tcx().sess.bug("eh_personality_catch not defined"), + }; + trans_rust_try(ccx, rust_try, lpad_ty, catch_pers, output, dloc, + &mut |bcx, then, catch| { + let func = llvm::get_param(rust_try, 0); + let data = llvm::get_param(rust_try, 1); + Invoke(bcx, rust_try_inner, &[func, data], then.llbb, catch.llbb, + None, dloc) + }); + return rust_try + }); + + // Note that no invoke is used here because by definition this function + // can't panic (that's what it's catching). + let ret = Call(bcx, llfn, &[func, data], None, dloc); + Store(bcx, ret, dest); + return bcx; + + // Translates both the inner and outer shims described above. The only + // difference between these two is the function invoked and the personality + // involved, so a common routine is shared. + // + // bcx: + // invoke %func(%args...) normal %normal unwind %unwind + // + // normal: + // ret null + // + // unwind: + // (ptr, _) = landingpad + // br (ptr != null), done, reraise + // + // done: + // ret ptr + // + // reraise: + // resume + // + // Note that the branch checking for `null` here isn't actually necessary, + // it's just an unfortunate hack to make sure that LLVM doesn't optimize too + // much. If this were not present, then LLVM would correctly deduce that our + // inner shim should be tagged with `nounwind` (as it catches all + // exceptions) and then the outer shim's `invoke` will be translated to just + // a simple call, destroying that entry for the personality function. + // + // To ensure that both shims always have an `invoke` this check against null + // confuses LLVM enough to the point that it won't infer `nounwind` and + // we'll proceed as normal. + fn trans_rust_try<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + llfn: ValueRef, + lpad_ty: Type, + personality: ValueRef, + output: ty::FnOutput<'tcx>, + dloc: DebugLoc, + invoke: &mut FnMut(Block, Block, Block) -> ValueRef) { + let (fcx, block_arena); + block_arena = TypedArena::new(); + fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, false, + output, ccx.tcx().mk_substs(Substs::trans_empty()), + None, &block_arena); + let bcx = init_function(&fcx, true, output); + let then = bcx.fcx.new_temp_block("then"); + let catch = bcx.fcx.new_temp_block("catch"); + let reraise = bcx.fcx.new_temp_block("reraise"); + let catch_return = bcx.fcx.new_temp_block("catch-return"); + + let invoke_ret = invoke(bcx, then, catch); + Ret(then, invoke_ret, dloc); + let vals = LandingPad(catch, lpad_ty, personality, 1); + AddClause(catch, vals, C_null(Type::i8p(ccx))); + let ptr = ExtractValue(catch, vals, 0); + let valid = ICmp(catch, llvm::IntNE, ptr, C_null(Type::i8p(ccx)), dloc); + CondBr(catch, valid, catch_return.llbb, reraise.llbb, dloc); + Ret(catch_return, ptr, dloc); + Resume(reraise, vals); + } +} + +// Helper to generate the `Ty` associated with `rust_Try` +fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>, + f: &mut FnMut(Ty<'tcx>, + ty::FnOutput<'tcx>) -> ValueRef) + -> ValueRef { + let ccx = fcx.ccx; + if let Some(llfn) = *ccx.rust_try_fn().borrow() { + return llfn + } + + // Define the types up front for the signatures of the rust_try and + // rust_try_inner functions. + let tcx = ccx.tcx(); + let i8p = tcx.mk_mut_ptr(tcx.types.i8); + let fn_ty = tcx.mk_bare_fn(ty::BareFnTy { + unsafety: ast::Unsafety::Unsafe, + abi: abi::Rust, + sig: ty::Binder(ty::FnSig { + inputs: vec![i8p], + output: ty::FnOutput::FnConverging(tcx.mk_nil()), + variadic: false, + }), + }); + let fn_ty = tcx.mk_fn(None, fn_ty); + let output = ty::FnOutput::FnConverging(i8p); + let try_fn_ty = tcx.mk_bare_fn(ty::BareFnTy { + unsafety: ast::Unsafety::Unsafe, + abi: abi::Rust, + sig: ty::Binder(ty::FnSig { + inputs: vec![fn_ty, i8p], + output: output, + variadic: false, + }), + }); + let rust_try = f(tcx.mk_fn(None, try_fn_ty), output); + *ccx.rust_try_fn().borrow_mut() = Some(rust_try); + return rust_try +} diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 1fa996f76b9..8901361b279 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -550,9 +550,7 @@ fn trans_object_shim<'a, 'tcx>( let shim_fn_ty = tcx.mk_fn(None, fty); let method_bare_fn_ty = tcx.mk_fn(None, method_ty); let function_name = link::mangle_internal_name_by_type_and_seq(ccx, shim_fn_ty, "object_shim"); - let llfn = declare::define_internal_rust_fn(ccx, &function_name, shim_fn_ty).unwrap_or_else(||{ - ccx.sess().bug(&format!("symbol `{}` already defined", function_name)); - }); + let llfn = declare::define_internal_rust_fn(ccx, &function_name, shim_fn_ty); let sig = ccx.tcx().erase_late_bound_regions(&fty.sig); diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs index 98fe57ec314..217181da142 100644 --- a/src/librustc_trans/trans/monomorphize.rs +++ b/src/librustc_trans/trans/monomorphize.rs @@ -137,10 +137,9 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let lldecl = if abi != abi::Rust { foreign::decl_rust_fn_with_foreign_abi(ccx, mono_ty, &s[..]) } else { - // FIXME(nagisa): perhaps needs a more fine grained selection? See setup_lldecl below. - declare::define_internal_rust_fn(ccx, &s[..], mono_ty).unwrap_or_else(||{ - ccx.sess().bug(&format!("symbol `{}` already defined", s)); - }) + // FIXME(nagisa): perhaps needs a more fine grained selection? See + // setup_lldecl below. + declare::define_internal_rust_fn(ccx, &s, mono_ty) }; ccx.monomorphized().borrow_mut().insert(hash_id.take().unwrap(), lldecl); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9042cedccc8..17140db904f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -5096,6 +5096,21 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) { ty::BrAnon(0))), param(ccx, 0))], tcx.types.u64), + "try" => { + let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8); + let fn_ty = ty::BareFnTy { + unsafety: ast::Unsafety::Normal, + abi: abi::Rust, + sig: ty::Binder(FnSig { + inputs: vec![mut_u8], + output: ty::FnOutput::FnConverging(tcx.mk_nil()), + variadic: false, + }), + }; + let fn_ty = tcx.mk_bare_fn(fn_ty); + (0, vec![tcx.mk_fn(None, fn_ty), mut_u8], mut_u8) + } + ref other => { span_err!(tcx.sess, it.span, E0093, "unrecognized intrinsic function: `{}`", *other); diff --git a/src/libstd/rt/unwind/gcc.rs b/src/libstd/rt/unwind/gcc.rs index 84c6d6864a9..87941e79b2f 100644 --- a/src/libstd/rt/unwind/gcc.rs +++ b/src/libstd/rt/unwind/gcc.rs @@ -8,10 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![allow(private_no_mangle_fns)] + use prelude::v1::*; use any::Any; -use libc::c_void; use rt::libunwind as uw; struct Exception { @@ -41,7 +42,7 @@ pub unsafe fn panic(data: Box) -> ! { } } -pub unsafe fn cleanup(ptr: *mut c_void) -> Box { +pub unsafe fn cleanup(ptr: *mut u8) -> Box { let my_ep = ptr as *mut Exception; rtdebug!("caught {}", (*my_ep).uwe.exception_class); let cause = (*my_ep).cause.take(); @@ -89,7 +90,7 @@ pub mod eabi { use rt::libunwind as uw; use libc::c_int; - extern "C" { + extern { fn __gcc_personality_v0(version: c_int, actions: uw::_Unwind_Action, exception_class: uw::_Unwind_Exception_Class, @@ -98,9 +99,8 @@ pub mod eabi { -> uw::_Unwind_Reason_Code; } - #[lang="eh_personality"] - #[no_mangle] // referenced from rust_try.ll - #[allow(private_no_mangle_fns)] + #[lang = "eh_personality"] + #[no_mangle] extern fn rust_eh_personality( version: c_int, actions: uw::_Unwind_Action, @@ -115,8 +115,9 @@ pub mod eabi { } } - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality_catch( + #[cfg_attr(not(stage0), lang = "eh_personality_catch")] + #[no_mangle] + pub extern fn rust_eh_personality_catch( _version: c_int, actions: uw::_Unwind_Action, _exception_class: uw::_Unwind_Exception_Class, @@ -142,7 +143,7 @@ pub mod eabi { use rt::libunwind as uw; use libc::c_int; - extern "C" { + extern { fn __gcc_personality_sj0(version: c_int, actions: uw::_Unwind_Action, exception_class: uw::_Unwind_Exception_Class, @@ -151,9 +152,9 @@ pub mod eabi { -> uw::_Unwind_Reason_Code; } - #[lang="eh_personality"] - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality( + #[lang = "eh_personality"] + #[no_mangle] + pub extern fn rust_eh_personality( version: c_int, actions: uw::_Unwind_Action, exception_class: uw::_Unwind_Exception_Class, @@ -167,8 +168,9 @@ pub mod eabi { } } - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality_catch( + #[cfg_attr(not(stage0), lang = "eh_personality_catch")] + #[no_mangle] + pub extern fn rust_eh_personality_catch( _version: c_int, actions: uw::_Unwind_Action, _exception_class: uw::_Unwind_Exception_Class, @@ -196,17 +198,16 @@ pub mod eabi { use rt::libunwind as uw; use libc::c_int; - extern "C" { + extern { fn __gcc_personality_v0(state: uw::_Unwind_State, ue_header: *mut uw::_Unwind_Exception, context: *mut uw::_Unwind_Context) -> uw::_Unwind_Reason_Code; } - #[lang="eh_personality"] - #[no_mangle] // referenced from rust_try.ll - #[allow(private_no_mangle_fns)] - extern "C" fn rust_eh_personality( + #[lang = "eh_personality"] + #[no_mangle] + extern fn rust_eh_personality( state: uw::_Unwind_State, ue_header: *mut uw::_Unwind_Exception, context: *mut uw::_Unwind_Context @@ -217,8 +218,9 @@ pub mod eabi { } } - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality_catch( + #[cfg_attr(not(stage0), lang = "eh_personality_catch")] + #[no_mangle] + pub extern fn rust_eh_personality_catch( state: uw::_Unwind_State, _ue_header: *mut uw::_Unwind_Exception, _context: *mut uw::_Unwind_Context @@ -266,7 +268,7 @@ pub mod eabi { } type _Unwind_Personality_Fn = - extern "C" fn( + extern fn( version: c_int, actions: uw::_Unwind_Action, exception_class: uw::_Unwind_Exception_Class, @@ -274,7 +276,7 @@ pub mod eabi { context: *mut uw::_Unwind_Context ) -> uw::_Unwind_Reason_Code; - extern "C" { + extern { fn __gcc_personality_seh0( exceptionRecord: *mut EXCEPTION_RECORD, establisherFrame: *mut c_void, @@ -291,10 +293,9 @@ pub mod eabi { ) -> EXCEPTION_DISPOSITION; } - #[lang="eh_personality"] - #[no_mangle] // referenced from rust_try.ll - #[allow(private_no_mangle_fns)] - extern "C" fn rust_eh_personality( + #[lang = "eh_personality"] + #[no_mangle] + extern fn rust_eh_personality( exceptionRecord: *mut EXCEPTION_RECORD, establisherFrame: *mut c_void, contextRecord: *mut CONTEXT, @@ -307,15 +308,16 @@ pub mod eabi { } } - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality_catch( + #[cfg_attr(not(stage0), lang = "eh_personality_catch")] + #[no_mangle] + pub extern fn rust_eh_personality_catch( exceptionRecord: *mut EXCEPTION_RECORD, establisherFrame: *mut c_void, contextRecord: *mut CONTEXT, dispatcherContext: *mut DISPATCHER_CONTEXT ) -> EXCEPTION_DISPOSITION { - extern "C" fn inner( + extern fn inner( _version: c_int, actions: uw::_Unwind_Action, _exception_class: uw::_Unwind_Exception_Class, diff --git a/src/libstd/rt/unwind/mod.rs b/src/libstd/rt/unwind/mod.rs index c403976745a..db2310ba361 100644 --- a/src/libstd/rt/unwind/mod.rs +++ b/src/libstd/rt/unwind/mod.rs @@ -69,7 +69,6 @@ use cmp; use panicking; use fmt; use intrinsics; -use libc::c_void; use mem; use sync::atomic::{self, Ordering}; use sys_common::mutex::Mutex; @@ -127,7 +126,7 @@ extern {} /// run. pub unsafe fn try(f: F) -> Result<(), Box> { let mut f = Some(f); - return inner_try(try_fn::, &mut f as *mut _ as *mut c_void); + return inner_try(try_fn::, &mut f as *mut _ as *mut u8); // If an inner function were not used here, then this generic function `try` // uses the native symbol `rust_try`, for which the code is statically @@ -140,11 +139,12 @@ pub unsafe fn try(f: F) -> Result<(), Box> { // `dllexport`, but it's easier to not have conditional `src/rt/rust_try.ll` // files and instead just have this non-generic shim the compiler can take // care of exposing correctly. - unsafe fn inner_try(f: extern fn(*mut c_void), data: *mut c_void) + #[cfg(not(stage0))] + unsafe fn inner_try(f: fn(*mut u8), data: *mut u8) -> Result<(), Box> { let prev = PANICKING.with(|s| s.get()); PANICKING.with(|s| s.set(false)); - let ep = rust_try(f, data); + let ep = intrinsics::try(f, data); PANICKING.with(|s| s.set(prev)); if ep.is_null() { Ok(()) @@ -152,8 +152,13 @@ pub unsafe fn try(f: F) -> Result<(), Box> { Err(imp::cleanup(ep)) } } + #[cfg(stage0)] + unsafe fn inner_try(f: fn(*mut u8), data: *mut u8) + -> Result<(), Box> { + Ok(f(data)) + } - extern fn try_fn(opt_closure: *mut c_void) { + fn try_fn(opt_closure: *mut u8) { let opt_closure = opt_closure as *mut Option; unsafe { (*opt_closure).take().unwrap()(); } } @@ -163,8 +168,8 @@ pub unsafe fn try(f: F) -> Result<(), Box> { // When f(...) returns normally, the return value is null. // When f(...) throws, the return value is a pointer to the caught // exception object. - fn rust_try(f: extern fn(*mut c_void), - data: *mut c_void) -> *mut c_void; + fn rust_try(f: extern fn(*mut u8), + data: *mut u8) -> *mut u8; } } diff --git a/src/libstd/rt/unwind/seh.rs b/src/libstd/rt/unwind/seh.rs index 632ab4f8e25..ed44f9a8bda 100644 --- a/src/libstd/rt/unwind/seh.rs +++ b/src/libstd/rt/unwind/seh.rs @@ -102,7 +102,7 @@ pub unsafe fn panic(data: Box) -> ! { rtabort!("could not unwind stack"); } -pub unsafe fn cleanup(ptr: *mut c_void) -> Box { +pub unsafe fn cleanup(ptr: *mut u8) -> Box { // The `ptr` here actually corresponds to the code of the exception, and our // real data is stored in our thread local. rtassert!(ptr as DWORD == RUST_PANIC); @@ -135,8 +135,9 @@ fn rust_eh_personality() { // to ensure that it's code is RUST_PANIC, which was set by the call to // `RaiseException` above in the `panic` function. #[no_mangle] +#[lang = "msvc_try_filter"] pub extern fn __rust_try_filter(eh_ptrs: *mut EXCEPTION_POINTERS, - _rbp: *mut c_void) -> i32 { + _rbp: *mut u8) -> i32 { unsafe { ((*(*eh_ptrs).ExceptionRecord).ExceptionCode == RUST_PANIC) as i32 } diff --git a/src/rt/rust_try.ll b/src/rt/rust_try.ll deleted file mode 100644 index 8643131d0fb..00000000000 --- a/src/rt/rust_try.ll +++ /dev/null @@ -1,54 +0,0 @@ -; Copyright 2013 The Rust Project Developers. See the COPYRIGHT -; file at the top-level directory of this distribution and at -; http://rust-lang.org/COPYRIGHT. -; -; Licensed under the Apache License, Version 2.0 or the MIT license -; , at your -; option. This file may not be copied, modified, or distributed -; except according to those terms. - -; Rust's try-catch -; When f(...) returns normally, the return value is null. -; When f(...) throws, the return value is a pointer to the caught exception object. - -; See also: libstd/rt/unwind/mod.rs - -define i8* @rust_try(void (i8*)* %f, i8* %env) - personality i8* bitcast (i32 (...)* @rust_eh_personality_catch to i8*) -{ - - %1 = invoke i8* @rust_try_inner(void (i8*)* %f, i8* %env) - to label %normal - unwind label %catch - -normal: - ret i8* %1 - -catch: - landingpad { i8*, i32 } catch i8* null - ; rust_try_inner's landing pad does not resume unwinds, so execution will - ; never reach here - ret i8* null -} - -define internal i8* @rust_try_inner(void (i8*)* %f, i8* %env) - personality i8* bitcast (i32 (...)* @rust_eh_personality to i8*) -{ - - invoke void %f(i8* %env) - to label %normal - unwind label %catch - -normal: - ret i8* null - -catch: - %1 = landingpad { i8*, i32 } catch i8* null - ; extract and return pointer to the exception object - %2 = extractvalue { i8*, i32 } %1, 0 - ret i8* %2 -} - -declare i32 @rust_eh_personality(...) -declare i32 @rust_eh_personality_catch(...) diff --git a/src/rt/rust_try_msvc_32.ll b/src/rt/rust_try_msvc_32.ll deleted file mode 100644 index bdee53b136e..00000000000 --- a/src/rt/rust_try_msvc_32.ll +++ /dev/null @@ -1,42 +0,0 @@ -; Copyright 2015 The Rust Project Developers. See the COPYRIGHT -; file at the top-level directory of this distribution and at -; http://rust-lang.org/COPYRIGHT. -; -; Licensed under the Apache License, Version 2.0 or the MIT license -; , at your -; option. This file may not be copied, modified, or distributed -; except according to those terms. - -; For more comments about what's going on here see rust_try_msvc_64.ll. The only -; difference between that and this file is the personality function used as it's -; different for 32-bit MSVC than it is for 64-bit. - -define i8* @rust_try(void (i8*)* %f, i8* %env) - personality i8* bitcast (i32 (...)* @_except_handler3 to i8*) -{ - invoke void %f(i8* %env) - to label %normal - unwind label %catch - -normal: - ret i8* null -catch: - %vals = landingpad { i8*, i32 } - catch i8* bitcast (i32 (i8*, i8*)* @__rust_try_filter to i8*) - %ehptr = extractvalue { i8*, i32 } %vals, 0 - %sel = extractvalue { i8*, i32 } %vals, 1 - %filter_sel = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @__rust_try_filter to i8*)) - %is_filter = icmp eq i32 %sel, %filter_sel - br i1 %is_filter, label %catch-return, label %catch-resume - -catch-return: - ret i8* %ehptr - -catch-resume: - resume { i8*, i32 } %vals -} - -declare i32 @_except_handler3(...) -declare i32 @__rust_try_filter(i8*, i8*) -declare i32 @llvm.eh.typeid.for(i8*) readnone nounwind diff --git a/src/rt/rust_try_msvc_64.ll b/src/rt/rust_try_msvc_64.ll deleted file mode 100644 index c38e6081bf2..00000000000 --- a/src/rt/rust_try_msvc_64.ll +++ /dev/null @@ -1,80 +0,0 @@ -; Copyright 2015 The Rust Project Developers. See the COPYRIGHT -; file at the top-level directory of this distribution and at -; http://rust-lang.org/COPYRIGHT. -; -; Licensed under the Apache License, Version 2.0 or the MIT license -; , at your -; option. This file may not be copied, modified, or distributed -; except according to those terms. - -; 64-bit MSVC's definition of the `rust_try` function. This function can't be -; defined in Rust as it's a "try-catch" block that's not expressible in Rust's -; syntax, so we're using LLVM to produce an object file with the associated -; handler. -; -; To use the correct system implementation details, this file is separate from -; the standard rust_try.ll as we need specifically use the __C_specific_handler -; personality function or otherwise LLVM doesn't emit SEH handling tables. -; There's also a few fiddly bits about SEH right now in LLVM that require us to -; structure this a fairly particular way! -; -; See also: src/libstd/rt/unwind/seh.rs - -define i8* @rust_try(void (i8*)* %f, i8* %env) - personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) -{ - invoke void %f(i8* %env) - to label %normal - unwind label %catch - -normal: - ret i8* null - -; Here's where most of the magic happens, this is the only landing pad in rust -; tagged with "catch" to indicate that we're catching an exception. The other -; catch handlers in rust_try.ll just catch *all* exceptions, but that's because -; most exceptions are already filtered out by their personality function. -; -; For MSVC we're just using a standard personality function that we can't -; customize, so we need to do the exception filtering ourselves, and this is -; currently performed by the `__rust_try_filter` function. This function, -; specified in the landingpad instruction, will be invoked by Windows SEH -; routines and will return whether the exception in question can be caught (aka -; the Rust runtime is the one that threw the exception). -; -; To get this to compile (currently LLVM segfaults if it's not in this -; particular structure), when the landingpad is executing we test to make sure -; that the ID of the exception being thrown is indeed the one that we were -; expecting. If it's not, we resume the exception, and otherwise we return the -; pointer that we got -; -; Full disclosure: It's not clear to me what this `llvm.eh.typeid` stuff is -; doing *other* then just allowing LLVM to compile this file without -; segfaulting. I would expect the entire landing pad to just be: -; -; %vals = landingpad ... -; %ehptr = extractvalue { i8*, i32 } %vals, 0 -; ret i8* %ehptr -; -; but apparently LLVM chokes on this, so we do the more complicated thing to -; placate it. -catch: - %vals = landingpad { i8*, i32 } - catch i8* bitcast (i32 (i8*, i8*)* @__rust_try_filter to i8*) - %ehptr = extractvalue { i8*, i32 } %vals, 0 - %sel = extractvalue { i8*, i32 } %vals, 1 - %filter_sel = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @__rust_try_filter to i8*)) - %is_filter = icmp eq i32 %sel, %filter_sel - br i1 %is_filter, label %catch-return, label %catch-resume - -catch-return: - ret i8* %ehptr - -catch-resume: - resume { i8*, i32 } %vals -} - -declare i32 @__C_specific_handler(...) -declare i32 @__rust_try_filter(i8*, i8*) -declare i32 @llvm.eh.typeid.for(i8*) readnone nounwind diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 163e95b890f..5007af0e777 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -120,7 +120,8 @@ extern "C" void LLVMAddDereferenceableCallSiteAttr(LLVMValueRef Instr, unsigned idx, B))); } -extern "C" void LLVMAddFunctionAttribute(LLVMValueRef Fn, unsigned index, uint64_t Val) { +extern "C" void LLVMAddFunctionAttribute(LLVMValueRef Fn, unsigned index, + uint64_t Val) { Function *A = unwrap(Fn); AttrBuilder B; B.addRawValue(Val); -- cgit 1.4.1-3-g733a5 From 6fa17b43d351ed4f9093cf80f4044d1208044241 Mon Sep 17 00:00:00 2001 From: Eli Friedman Date: Thu, 25 Jun 2015 14:48:36 -0700 Subject: Rewrite the improper_ctypes lint. Makes the lint a bit more accurate, and improves the quality of the diagnostic messages by explicitly returning an error message. The new lint is also a little more aggressive: specifically, it now rejects tuples, and it recurses into function pointers. --- src/librustc/middle/ty.rs | 136 +---------- src/librustc_lint/builtin.rs | 301 +++++++++++++++++++++--- src/librustc_lint/lib.rs | 1 + src/librustc_llvm/lib.rs | 2 +- src/librustc_trans/back/msvc/registry.rs | 3 +- src/libstd/rt/unwind/gcc.rs | 11 +- src/libstd/sys/windows/stack_overflow.rs | 2 + src/libstd/thread/local.rs | 4 +- src/test/compile-fail/issue-14309.rs | 2 +- src/test/compile-fail/issue-16250.rs | 2 +- src/test/compile-fail/lint-ctypes-enum.rs | 6 +- src/test/compile-fail/lint-ctypes.rs | 39 ++- src/test/compile-fail/warn-foreign-int-types.rs | 6 +- src/test/run-make/execution-engine/test.rs | 7 +- 14 files changed, 345 insertions(+), 177 deletions(-) (limited to 'src/libstd/rt') diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 17a76f6eed9..64e707f6264 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3967,7 +3967,6 @@ def_type_content_sets! { None = 0b0000_0000__0000_0000__0000, // Things that are interior to the value (first nibble): - InteriorUnsized = 0b0000_0000__0000_0000__0001, InteriorUnsafe = 0b0000_0000__0000_0000__0010, InteriorParam = 0b0000_0000__0000_0000__0100, // InteriorAll = 0b00000000__00000000__1111, @@ -3977,18 +3976,9 @@ def_type_content_sets! { OwnsDtor = 0b0000_0000__0000_0010__0000, OwnsAll = 0b0000_0000__1111_1111__0000, - // Things that are reachable by the value in any way (fourth nibble): - ReachesBorrowed = 0b0000_0010__0000_0000__0000, - ReachesMutable = 0b0000_1000__0000_0000__0000, - ReachesFfiUnsafe = 0b0010_0000__0000_0000__0000, - ReachesAll = 0b0011_1111__0000_0000__0000, - // Things that mean drop glue is necessary NeedsDrop = 0b0000_0000__0000_0111__0000, - // Things that prevent values from being considered sized - Nonsized = 0b0000_0000__0000_0000__0001, - // All bits All = 0b1111_1111__1111_1111__1111 } @@ -4007,10 +3997,6 @@ impl TypeContents { self.intersects(TC::OwnsOwned) } - pub fn is_sized(&self, _: &ctxt) -> bool { - !self.intersects(TC::Nonsized) - } - pub fn interior_param(&self) -> bool { self.intersects(TC::InteriorParam) } @@ -4019,29 +4005,13 @@ impl TypeContents { self.intersects(TC::InteriorUnsafe) } - pub fn interior_unsized(&self) -> bool { - self.intersects(TC::InteriorUnsized) - } - pub fn needs_drop(&self, _: &ctxt) -> bool { self.intersects(TC::NeedsDrop) } /// Includes only those bits that still apply when indirected through a `Box` pointer pub fn owned_pointer(&self) -> TypeContents { - TC::OwnsOwned | ( - *self & (TC::OwnsAll | TC::ReachesAll)) - } - - /// Includes only those bits that still apply when indirected through a reference (`&`) - pub fn reference(&self, bits: TypeContents) -> TypeContents { - bits | ( - *self & TC::ReachesAll) - } - - /// Includes only those bits that still apply when indirected through a raw pointer (`*`) - pub fn unsafe_pointer(&self) -> TypeContents { - *self & TC::ReachesAll + TC::OwnsOwned | (*self & TC::OwnsAll) } pub fn union(v: &[T], mut f: F) -> TypeContents where @@ -4129,7 +4099,7 @@ impl<'tcx> TyS<'tcx> { let result = match ty.sty { // usize and isize are ffi-unsafe TyUint(ast::TyUs) | TyInt(ast::TyIs) => { - TC::ReachesFfiUnsafe + TC::None } // Scalar and unique types are sendable, and durable @@ -4140,20 +4110,19 @@ impl<'tcx> TyS<'tcx> { } TyBox(typ) => { - TC::ReachesFfiUnsafe | tc_ty(cx, typ, cache).owned_pointer() + tc_ty(cx, typ, cache).owned_pointer() } - TyTrait(box TraitTy { ref bounds, .. }) => { - object_contents(bounds) | TC::ReachesFfiUnsafe | TC::Nonsized + TyTrait(_) => { + TC::All - TC::InteriorParam } - TyRawPtr(ref mt) => { - tc_ty(cx, mt.ty, cache).unsafe_pointer() + TyRawPtr(_) => { + TC::None } - TyRef(r, ref mt) => { - tc_ty(cx, mt.ty, cache).reference(borrowed_contents(*r, mt.mutbl)) | - TC::ReachesFfiUnsafe + TyRef(_, _) => { + TC::None } TyArray(ty, _) => { @@ -4161,19 +4130,15 @@ impl<'tcx> TyS<'tcx> { } TySlice(ty) => { - tc_ty(cx, ty, cache) | TC::Nonsized + tc_ty(cx, ty, cache) } - TyStr => TC::Nonsized, + TyStr => TC::None, TyStruct(did, substs) => { let flds = cx.struct_fields(did, substs); let mut res = TypeContents::union(&flds[..], - |f| tc_mt(cx, f.mt, cache)); - - if !cx.lookup_repr_hints(did).contains(&attr::ReprExtern) { - res = res | TC::ReachesFfiUnsafe; - } + |f| tc_ty(cx, f.mt.ty, cache)); if cx.has_dtor(did) { res = res | TC::OwnsDtor; @@ -4182,7 +4147,6 @@ impl<'tcx> TyS<'tcx> { } TyClosure(did, substs) => { - // FIXME(#14449): `borrowed_contents` below assumes `&mut` closure. let param_env = cx.empty_parameter_environment(); let infcx = infer::new_infer_ctxt(cx, &cx.tables, Some(param_env), false); let upvars = infcx.closure_upvars(did, substs).unwrap(); @@ -4208,44 +4172,6 @@ impl<'tcx> TyS<'tcx> { res = res | TC::OwnsDtor; } - if !variants.is_empty() { - let repr_hints = cx.lookup_repr_hints(did); - if repr_hints.len() > 1 { - // this is an error later on, but this type isn't safe - res = res | TC::ReachesFfiUnsafe; - } - - match repr_hints.get(0) { - Some(h) => if !h.is_ffi_safe() { - res = res | TC::ReachesFfiUnsafe; - }, - // ReprAny - None => { - res = res | TC::ReachesFfiUnsafe; - - // We allow ReprAny enums if they are eligible for - // the nullable pointer optimization and the - // contained type is an `extern fn` - - if variants.len() == 2 { - let mut data_idx = 0; - - if variants[0].args.is_empty() { - data_idx = 1; - } - - if variants[data_idx].args.len() == 1 { - match variants[data_idx].args[0].sty { - TyBareFn(..) => { res = res - TC::ReachesFfiUnsafe; } - _ => { } - } - } - } - } - } - } - - apply_lang_items(cx, did, res) } @@ -4264,14 +4190,6 @@ impl<'tcx> TyS<'tcx> { result } - fn tc_mt<'tcx>(cx: &ctxt<'tcx>, - mt: TypeAndMut<'tcx>, - cache: &mut FnvHashMap, TypeContents>) -> TypeContents - { - let mc = TC::ReachesMutable.when(mt.mutbl == MutMutable); - mc | tc_ty(cx, mt.ty, cache) - } - fn apply_lang_items(cx: &ctxt, did: ast::DefId, tc: TypeContents) -> TypeContents { if Some(did) == cx.lang_items.unsafe_cell_type() { @@ -4280,32 +4198,6 @@ impl<'tcx> TyS<'tcx> { tc } } - - /// Type contents due to containing a reference with - /// the region `region` and borrow kind `bk`. - fn borrowed_contents(region: ty::Region, - mutbl: ast::Mutability) - -> TypeContents { - let b = match mutbl { - ast::MutMutable => TC::ReachesMutable, - ast::MutImmutable => TC::None, - }; - b | (TC::ReachesBorrowed).when(region != ty::ReStatic) - } - - fn object_contents(bounds: &ExistentialBounds) -> TypeContents { - // These are the type contents of the (opaque) interior. We - // make no assumptions (other than that it cannot have an - // in-scope type parameter within, which makes no sense). - let mut tc = TC::All - TC::InteriorParam; - for bound in &bounds.builtin_bounds { - tc = tc - match bound { - BoundSync | BoundSend | BoundCopy => TC::None, - BoundSized => TC::Nonsized, - }; - } - return tc; - } } fn impls_bound<'a>(&'tcx self, param_env: &ParameterEnvironment<'a,'tcx>, @@ -4399,10 +4291,6 @@ impl<'tcx> TyS<'tcx> { result } - pub fn is_ffi_safe(&'tcx self, cx: &ctxt<'tcx>) -> bool { - !self.type_contents(cx).intersects(TC::ReachesFfiUnsafe) - } - // True if instantiating an instance of `r_ty` requires an instance of `r_ty`. pub fn is_instantiable(&'tcx self, cx: &ctxt<'tcx>) -> bool { fn type_requires<'tcx>(cx: &ctxt<'tcx>, seen: &mut Vec, diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index ae95466e6e6..6289d505881 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -32,23 +32,21 @@ #![allow(deprecated)] use metadata::{csearch, decoder}; +use middle::{cfg, def, infer, pat_util, stability, traits}; use middle::def::*; -use middle::infer; use middle::subst::Substs; use middle::ty::{self, Ty}; -use middle::traits; -use middle::{def, pat_util, stability}; use middle::const_eval::{eval_const_expr_partial, ConstVal}; use middle::const_eval::EvalHint::ExprTypeChecked; -use middle::cfg; use rustc::ast_map; -use util::nodemap::{FnvHashMap, NodeSet}; +use util::nodemap::{FnvHashMap, FnvHashSet, NodeSet}; use lint::{Level, Context, LintPass, LintArray, Lint}; use std::collections::{HashSet, BitSet}; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::{cmp, slice}; use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64}; +use std::rc::Rc; use syntax::{abi, ast}; use syntax::ast_util::{self, is_shift_binop, local_def}; @@ -405,43 +403,288 @@ struct ImproperCTypesVisitor<'a, 'tcx: 'a> { cx: &'a Context<'a, 'tcx> } +enum FfiResult { + FfiSafe, + FfiUnsafe(&'static str), + FfiBadStruct(ast::DefId, &'static str), + FfiBadEnum(ast::DefId, &'static str) +} + +/// Check if this enum can be safely exported based on the +/// "nullable pointer optimization". Currently restricted +/// to function pointers and references, but could be +/// expanded to cover NonZero raw pointers and newtypes. +/// FIXME: This duplicates code in trans. +fn is_repr_nullable_ptr<'tcx>(variants: &Vec>>) -> bool { + if variants.len() == 2 { + let mut data_idx = 0; + + if variants[0].args.is_empty() { + data_idx = 1; + } else if !variants[1].args.is_empty() { + return false; + } + + if variants[data_idx].args.len() == 1 { + match variants[data_idx].args[0].sty { + ty::TyBareFn(None, _) => { return true; } + ty::TyRef(..) => { return true; } + _ => { } + } + } + } + false +} + impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { - fn check_def(&mut self, sp: Span, id: ast::NodeId) { - match self.cx.tcx.def_map.borrow().get(&id).unwrap().full_def() { - def::DefPrimTy(ast::TyInt(ast::TyIs)) => { - self.cx.span_lint(IMPROPER_CTYPES, sp, - "found rust type `isize` in foreign module, while \ - libc::c_int or libc::c_long should be used"); + /// Check if the given type is "ffi-safe" (has a stable, well-defined + /// representation which can be exported to C code). + fn check_type_for_ffi(&self, + cache: &mut FnvHashSet>, + ty: Ty<'tcx>) + -> FfiResult { + use self::FfiResult::*; + let cx = &self.cx.tcx; + + // Protect against infinite recursion, for example + // `struct S(*mut S);`. + // FIXME: A recursion limit is necessary as well, for irregular + // recusive types. + if !cache.insert(ty) { + return FfiSafe; + } + + match ty.sty { + ty::TyStruct(did, substs) => { + if !cx.lookup_repr_hints(did).contains(&attr::ReprExtern) { + return FfiUnsafe( + "found struct without foreign-function-safe \ + representation annotation in foreign module, \ + consider adding a #[repr(C)] attribute to \ + the type"); + } + + // We can't completely trust repr(C) markings; make sure the + // fields are actually safe. + let fields = cx.struct_fields(did, substs); + + if fields.is_empty() { + return FfiUnsafe( + "found zero-size struct in foreign module, consider \ + adding a member to this struct"); + } + + for field in fields { + let field_ty = infer::normalize_associated_type(cx, &field.mt.ty); + let r = self.check_type_for_ffi(cache, field_ty); + match r { + FfiSafe => {} + FfiBadStruct(..) | FfiBadEnum(..) => { return r; } + FfiUnsafe(s) => { return FfiBadStruct(did, s); } + } + } + FfiSafe } - def::DefPrimTy(ast::TyUint(ast::TyUs)) => { - self.cx.span_lint(IMPROPER_CTYPES, sp, - "found rust type `usize` in foreign module, while \ - libc::c_uint or libc::c_ulong should be used"); + ty::TyEnum(did, substs) => { + let variants = cx.substd_enum_variants(did, substs); + if variants.is_empty() { + // Empty enums are okay... although sort of useless. + return FfiSafe + } + + // Check for a repr() attribute to specify the size of the + // discriminant. + let repr_hints = cx.lookup_repr_hints(did); + match &**repr_hints { + [] => { + // Special-case types like `Option`. + if !is_repr_nullable_ptr(&variants) { + return FfiUnsafe( + "found enum without foreign-function-safe \ + representation annotation in foreign module, \ + consider adding a #[repr(...)] attribute to \ + the type") + } + } + [ref hint] => { + if !hint.is_ffi_safe() { + // FIXME: This shouldn't be reachable: we should check + // this earlier. + return FfiUnsafe( + "enum has unexpected #[repr(...)] attribute") + } + + // Enum with an explicitly sized discriminant; either + // a C-style enum or a discriminated union. + + // The layout of enum variants is implicitly repr(C). + // FIXME: Is that correct? + } + _ => { + // FIXME: This shouldn't be reachable: we should check + // this earlier. + return FfiUnsafe( + "enum has too many #[repr(...)] attributes"); + } + } + + // Check the contained variants. + for variant in variants { + for arg in &variant.args { + let arg = infer::normalize_associated_type(cx, arg); + let r = self.check_type_for_ffi(cache, arg); + match r { + FfiSafe => {} + FfiBadStruct(..) | FfiBadEnum(..) => { return r; } + FfiUnsafe(s) => { return FfiBadEnum(did, s); } + } + } + } + FfiSafe + } + + ty::TyInt(ast::TyIs) => { + FfiUnsafe("found Rust type `isize` in foreign module, while \ + `libc::c_int` or `libc::c_long` should be used") + } + ty::TyUint(ast::TyUs) => { + FfiUnsafe("found Rust type `usize` in foreign module, while \ + `libc::c_uint` or `libc::c_ulong` should be used") + } + ty::TyChar => { + FfiUnsafe("found Rust type `char` in foreign module, while \ + `u32` or `libc::wchar_t` should be used") + } + + // Primitive types with a stable representation. + ty::TyBool | ty::TyInt(..) | ty::TyUint(..) | + ty::TyFloat(..) => FfiSafe, + + ty::TyBox(..) => { + FfiUnsafe("found Rust type Box<_> in foreign module, \ + consider using a raw pointer instead") + } + + ty::TySlice(_) => { + FfiUnsafe("found Rust slice type in foreign module, \ + consider using a raw pointer instead") + } + + ty::TyTrait(..) => { + FfiUnsafe("found Rust trait type in foreign module, \ + consider using a raw pointer instead") + } + + ty::TyStr => { + FfiUnsafe("found Rust type `str` in foreign module; \ + consider using a `*const libc::c_char`") } - def::DefTy(..) => { - let tty = match self.cx.tcx.ast_ty_to_ty_cache.borrow().get(&id) { - Some(&t) => t, - None => panic!("ast_ty_to_ty_cache was incomplete after typeck!") - }; - if !tty.is_ffi_safe(self.cx.tcx) { - self.cx.span_lint(IMPROPER_CTYPES, sp, - "found type without foreign-function-safe \ - representation annotation in foreign module, consider \ - adding a #[repr(...)] attribute to the type"); + ty::TyTuple(_) => { + FfiUnsafe("found Rust tuple type in foreign module; \ + consider using a struct instead`") + } + + ty::TyRawPtr(ref m) | ty::TyRef(_, ref m) => { + self.check_type_for_ffi(cache, m.ty) + } + + ty::TyArray(ty, _) => { + self.check_type_for_ffi(cache, ty) + } + + ty::TyBareFn(None, bare_fn) => { + match bare_fn.abi { + abi::Rust | + abi::RustIntrinsic | + abi::RustCall => { + return FfiUnsafe( + "found function pointer with Rust calling \ + convention in foreign module; consider using an \ + `extern` function pointer") + } + _ => {} } + + let sig = cx.erase_late_bound_regions(&bare_fn.sig); + match sig.output { + ty::FnDiverging => {} + ty::FnConverging(output) => { + if !output.is_nil() { + let r = self.check_type_for_ffi(cache, output); + match r { + FfiSafe => {} + _ => { return r; } + } + } + } + } + for arg in sig.inputs { + let r = self.check_type_for_ffi(cache, arg); + match r { + FfiSafe => {} + _ => { return r; } + } + } + FfiSafe + } + + ty::TyParam(..) | ty::TyInfer(..) | ty::TyError | + ty::TyClosure(..) | ty::TyProjection(..) | + ty::TyBareFn(Some(_), _) => { + panic!("Unexpected type in foreign function") + } + } + } + + fn check_def(&mut self, sp: Span, id: ast::NodeId) { + let tty = match self.cx.tcx.ast_ty_to_ty_cache.borrow().get(&id) { + Some(&t) => t, + None => panic!("ast_ty_to_ty_cache was incomplete after typeck!") + }; + let tty = infer::normalize_associated_type(self.cx.tcx, &tty); + + match ImproperCTypesVisitor::check_type_for_ffi(self, &mut FnvHashSet(), tty) { + FfiResult::FfiSafe => {} + FfiResult::FfiUnsafe(s) => { + self.cx.span_lint(IMPROPER_CTYPES, sp, s); + } + FfiResult::FfiBadStruct(_, s) => { + // FIXME: This diagnostic is difficult to read, and doesn't + // point at the relevant field. + self.cx.span_lint(IMPROPER_CTYPES, sp, + &format!("found non-foreign-function-safe member in \ + struct marked #[repr(C)]: {}", s)); + } + FfiResult::FfiBadEnum(_, s) => { + // FIXME: This diagnostic is difficult to read, and doesn't + // point at the relevant variant. + self.cx.span_lint(IMPROPER_CTYPES, sp, + &format!("found non-foreign-function-safe member in \ + enum: {}", s)); } - _ => () } } } impl<'a, 'tcx, 'v> Visitor<'v> for ImproperCTypesVisitor<'a, 'tcx> { fn visit_ty(&mut self, ty: &ast::Ty) { - if let ast::TyPath(..) = ty.node { - self.check_def(ty.span, ty.id); + match ty.node { + ast::TyPath(..) | + ast::TyBareFn(..) => self.check_def(ty.span, ty.id), + ast::TyVec(..) => { + self.cx.span_lint(IMPROPER_CTYPES, ty.span, + "found Rust slice type in foreign module, consider \ + using a raw pointer instead"); + } + ast::TyFixedLengthVec(ref ty, _) => self.visit_ty(ty), + ast::TyTup(..) => { + self.cx.span_lint(IMPROPER_CTYPES, ty.span, + "found Rust tuple type in foreign module; \ + consider using a struct instead`") + } + _ => visit::walk_ty(self, ty) } - visit::walk_ty(self, ty); } } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 54c1a79e10a..8d6dfb92987 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -38,6 +38,7 @@ #![feature(ref_slice)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] +#![feature(slice_patterns)] #![feature(staged_api)] #![feature(str_char)] diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 83f8619c5ee..9ee046915da 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -685,7 +685,7 @@ extern { pub fn LLVMGetArrayLength(ArrayTy: TypeRef) -> c_uint; pub fn LLVMGetPointerAddressSpace(PointerTy: TypeRef) -> c_uint; pub fn LLVMGetPointerToGlobal(EE: ExecutionEngineRef, V: ValueRef) - -> *const (); + -> *const c_void; pub fn LLVMGetVectorSize(VectorTy: TypeRef) -> c_uint; /* Operations on other types */ diff --git a/src/librustc_trans/back/msvc/registry.rs b/src/librustc_trans/back/msvc/registry.rs index 97fd7f99d19..21078641c1f 100644 --- a/src/librustc_trans/back/msvc/registry.rs +++ b/src/librustc_trans/back/msvc/registry.rs @@ -13,6 +13,7 @@ use std::ffi::{OsString, OsStr}; use std::os::windows::prelude::*; use std::ops::RangeFrom; use libc::{DWORD, LPCWSTR, LONG, LPDWORD, LPBYTE, ERROR_SUCCESS}; +use libc::c_void; const HKEY_LOCAL_MACHINE: HKEY = 0x80000002 as HKEY; const KEY_WOW64_32KEY: REGSAM = 0x0200; @@ -32,7 +33,7 @@ pub type HKEY = *mut __HKEY__; pub type PHKEY = *mut HKEY; pub type REGSAM = DWORD; pub type LPWSTR = *mut u16; -pub type PFILETIME = *mut (); +pub type PFILETIME = *mut c_void; #[link(name = "advapi32")] extern "system" { diff --git a/src/libstd/rt/unwind/gcc.rs b/src/libstd/rt/unwind/gcc.rs index 87941e79b2f..59fc8df6107 100644 --- a/src/libstd/rt/unwind/gcc.rs +++ b/src/libstd/rt/unwind/gcc.rs @@ -251,12 +251,11 @@ pub mod eabi { use rt::libunwind as uw; use libc::{c_void, c_int}; - #[repr(C)] - pub struct EXCEPTION_RECORD; - #[repr(C)] - pub struct CONTEXT; - #[repr(C)] - pub struct DISPATCHER_CONTEXT; + // Fake definitions; these are actually complicated structs, + // but we don't use the contents here. + pub type EXCEPTION_RECORD = c_void; + pub type CONTEXT = c_void; + pub type DISPATCHER_CONTEXT = c_void; #[repr(C)] #[derive(Copy, Clone)] diff --git a/src/libstd/sys/windows/stack_overflow.rs b/src/libstd/sys/windows/stack_overflow.rs index cf827848db5..491b53c4ed9 100644 --- a/src/libstd/sys/windows/stack_overflow.rs +++ b/src/libstd/sys/windows/stack_overflow.rs @@ -82,6 +82,7 @@ pub unsafe fn make_handler() -> Handler { Handler { _data: 0 as *mut libc::c_void } } +#[repr(C)] pub struct EXCEPTION_RECORD { pub ExceptionCode: DWORD, pub ExceptionFlags: DWORD, @@ -91,6 +92,7 @@ pub struct EXCEPTION_RECORD { pub ExceptionInformation: [LPVOID; EXCEPTION_MAXIMUM_PARAMETERS] } +#[repr(C)] pub struct EXCEPTION_POINTERS { pub ExceptionRecord: *mut EXCEPTION_RECORD, pub ContextRecord: LPVOID diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index e2873601a7b..11b375dcce2 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -335,13 +335,13 @@ mod imp { #[linkage = "extern_weak"] static __dso_handle: *mut u8; #[linkage = "extern_weak"] - static __cxa_thread_atexit_impl: *const (); + static __cxa_thread_atexit_impl: *const libc::c_void; } if !__cxa_thread_atexit_impl.is_null() { type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8), arg: *mut u8, dso_handle: *mut u8) -> libc::c_int; - mem::transmute::<*const (), F>(__cxa_thread_atexit_impl) + mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl) (dtor, t, &__dso_handle as *const _ as *mut _); return } diff --git a/src/test/compile-fail/issue-14309.rs b/src/test/compile-fail/issue-14309.rs index 9225889ef63..56261c34a03 100644 --- a/src/test/compile-fail/issue-14309.rs +++ b/src/test/compile-fail/issue-14309.rs @@ -37,7 +37,7 @@ struct D { } extern "C" { - fn foo(x: A); //~ ERROR found type without foreign-function-safe + fn foo(x: A); //~ ERROR found struct without foreign-function-safe fn bar(x: B); //~ ERROR foreign-function-safe fn baz(x: C); fn qux(x: A2); //~ ERROR foreign-function-safe diff --git a/src/test/compile-fail/issue-16250.rs b/src/test/compile-fail/issue-16250.rs index c886c97a636..b5aa3568122 100644 --- a/src/test/compile-fail/issue-16250.rs +++ b/src/test/compile-fail/issue-16250.rs @@ -11,7 +11,7 @@ #![deny(warnings)] extern { - pub fn foo(x: (isize)); //~ ERROR found rust type `isize` in foreign module + pub fn foo(x: (isize)); //~ ERROR found Rust type `isize` in foreign module } fn main() { diff --git a/src/test/compile-fail/lint-ctypes-enum.rs b/src/test/compile-fail/lint-ctypes-enum.rs index dea933085de..e35dadbea9d 100644 --- a/src/test/compile-fail/lint-ctypes-enum.rs +++ b/src/test/compile-fail/lint-ctypes-enum.rs @@ -18,9 +18,9 @@ enum T { E, F, G } extern { fn zf(x: Z); - fn uf(x: U); //~ ERROR found type without foreign-function-safe - fn bf(x: B); //~ ERROR found type without foreign-function-safe - fn tf(x: T); //~ ERROR found type without foreign-function-safe + fn uf(x: U); //~ ERROR found enum without foreign-function-safe + fn bf(x: B); //~ ERROR found enum without foreign-function-safe + fn tf(x: T); //~ ERROR found enum without foreign-function-safe } pub fn main() { } diff --git a/src/test/compile-fail/lint-ctypes.rs b/src/test/compile-fail/lint-ctypes.rs index 3f25e9c7b76..614f8e6fde8 100644 --- a/src/test/compile-fail/lint-ctypes.rs +++ b/src/test/compile-fail/lint-ctypes.rs @@ -13,14 +13,45 @@ extern crate libc; +trait Mirror { type It; } +impl Mirror for T { type It = Self; } +#[repr(C)] +pub struct StructWithProjection(*mut ::It); +#[repr(C)] +pub struct StructWithProjectionAndLifetime<'a>( + &'a mut as Mirror>::It +); +pub type I32Pair = (i32, i32); +#[repr(C)] +pub struct ZeroSize; +pub type RustFn = fn(); +pub type RustBadRet = extern fn() -> Box; + extern { - pub fn bare_type1(size: isize); //~ ERROR: found rust type - pub fn bare_type2(size: usize); //~ ERROR: found rust type - pub fn ptr_type1(size: *const isize); //~ ERROR: found rust type - pub fn ptr_type2(size: *const usize); //~ ERROR: found rust type + pub fn bare_type1(size: isize); //~ ERROR: found Rust type + pub fn bare_type2(size: usize); //~ ERROR: found Rust type + pub fn ptr_type1(size: *const isize); //~ ERROR: found Rust type + pub fn ptr_type2(size: *const usize); //~ ERROR: found Rust type + pub fn slice_type(p: &[u32]); //~ ERROR: found Rust slice type + pub fn str_type(p: &str); //~ ERROR: found Rust type + pub fn box_type(p: Box); //~ ERROR found Rust type + pub fn char_type(p: char); //~ ERROR found Rust type + pub fn trait_type(p: &Clone); //~ ERROR found Rust trait type + pub fn tuple_type(p: (i32, i32)); //~ ERROR found Rust tuple type + pub fn tuple_type2(p: I32Pair); //~ ERROR found Rust tuple type + pub fn zero_size(p: ZeroSize); //~ ERROR found zero-size struct + pub fn fn_type(p: RustFn); //~ ERROR found function pointer with Rust + pub fn fn_type2(p: fn()); //~ ERROR found function pointer with Rust + pub fn fn_contained(p: RustBadRet); //~ ERROR: found Rust type pub fn good1(size: *const libc::c_int); pub fn good2(size: *const libc::c_uint); + pub fn good3(fptr: Option); + pub fn good4(aptr: &[u8; 4 as usize]); + pub fn good5(s: StructWithProjection); + pub fn good6(s: StructWithProjectionAndLifetime); + pub fn good7(fptr: extern fn() -> ()); + pub fn good8(fptr: extern fn() -> !); } fn main() { diff --git a/src/test/compile-fail/warn-foreign-int-types.rs b/src/test/compile-fail/warn-foreign-int-types.rs index 9ff0f011e1d..b77f25a0a34 100644 --- a/src/test/compile-fail/warn-foreign-int-types.rs +++ b/src/test/compile-fail/warn-foreign-int-types.rs @@ -13,9 +13,9 @@ mod xx { extern { - pub fn strlen(str: *const u8) -> usize; //~ ERROR found rust type `usize` - pub fn foo(x: isize, y: usize); //~ ERROR found rust type `isize` - //~^ ERROR found rust type `usize` + pub fn strlen(str: *const u8) -> usize; //~ ERROR found Rust type `usize` + pub fn foo(x: isize, y: usize); //~ ERROR found Rust type `isize` + //~^ ERROR found Rust type `usize` } } diff --git a/src/test/run-make/execution-engine/test.rs b/src/test/run-make/execution-engine/test.rs index 8af3844e62e..771fce31023 100644 --- a/src/test/run-make/execution-engine/test.rs +++ b/src/test/run-make/execution-engine/test.rs @@ -9,7 +9,9 @@ // except according to those terms. #![feature(rustc_private)] +#![feature(libc)] +extern crate libc; extern crate rustc; extern crate rustc_driver; extern crate rustc_lint; @@ -29,6 +31,7 @@ use rustc::session::config::{self, basic_options, build_configuration, Input, Op use rustc::session::build_session; use rustc_driver::driver; use rustc_resolve::MakeGlobMap; +use libc::c_void; use syntax::diagnostics::registry::Registry; @@ -111,7 +114,7 @@ impl ExecutionEngine { } /// Returns a raw pointer to the named function. - pub fn get_function(&mut self, name: &str) -> Option<*const ()> { + pub fn get_function(&mut self, name: &str) -> Option<*const c_void> { let s = CString::new(name.as_bytes()).unwrap(); for &m in &self.modules { @@ -128,7 +131,7 @@ impl ExecutionEngine { } /// Returns a raw pointer to the named global item. - pub fn get_global(&mut self, name: &str) -> Option<*const ()> { + pub fn get_global(&mut self, name: &str) -> Option<*const c_void> { let s = CString::new(name.as_bytes()).unwrap(); for &m in &self.modules { -- cgit 1.4.1-3-g733a5