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 --- src/libstd/thread/scoped_tls.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/libstd/thread') 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))] -- cgit 1.4.1-3-g733a5 From 987dc84b521a9be98099b504aa91b7baa9d22409 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 10 Jul 2015 17:46:20 -0700 Subject: std: Fix a TLS destructor bug on OSX TLS tests have been deadlocking on the OSX bots for quite some time now and this commit is the result of the investigation into what's going on. It turns out that a value in TLS which is being destroyed (e.g. the destructor is run) can be reset back to the initial state **while the destructor is running** if TLS is re-accessed. To fix this we stop calling drop_in_place on OSX and instead move the data to a temporary location on the stack. --- src/libstd/thread/local.rs | 21 ++++++++++++++-- src/test/run-pass/down-with-thread-dtors.rs | 38 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 src/test/run-pass/down-with-thread-dtors.rs (limited to 'src/libstd/thread') diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 60563340d10..e2873601a7b 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -275,6 +275,7 @@ mod imp { use cell::{Cell, UnsafeCell}; use intrinsics; + use ptr; pub struct Key { inner: UnsafeCell>, @@ -327,7 +328,6 @@ mod imp { #[cfg(target_os = "linux")] unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { use mem; - use ptr; use libc; use sys_common::thread_local as os; @@ -394,7 +394,24 @@ mod imp { // destructor as running for this thread so calls to `get` will return // `None`. (*ptr).dtor_running.set(true); - intrinsics::drop_in_place((*ptr).inner.get()); + + // The OSX implementation of TLS apparently had an odd aspect to it + // where the pointer we have may be overwritten while this destructor + // is running. Specifically if a TLS destructor re-accesses TLS it may + // trigger a re-initialization of all TLS variables, paving over at + // least some destroyed ones with initial values. + // + // This means that if we drop a TLS value in place on OSX that we could + // revert the value to its original state halfway through the + // destructor, which would be bad! + // + // Hence, we use `ptr::read` on OSX (to move to a "safe" location) + // instead of drop_in_place. + if cfg!(target_os = "macos") { + ptr::read((*ptr).inner.get()); + } else { + intrinsics::drop_in_place((*ptr).inner.get()); + } } } diff --git a/src/test/run-pass/down-with-thread-dtors.rs b/src/test/run-pass/down-with-thread-dtors.rs new file mode 100644 index 00000000000..ee835785cbe --- /dev/null +++ b/src/test/run-pass/down-with-thread-dtors.rs @@ -0,0 +1,38 @@ +// 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. + +thread_local!(static FOO: Foo = Foo); +thread_local!(static BAR: Bar = Bar(1)); +thread_local!(static BAZ: Baz = Baz); + +struct Foo; +struct Bar(i32); +struct Baz; + +impl Drop for Foo { + fn drop(&mut self) { + BAR.with(|_| {}); + } +} + +impl Drop for Bar { + fn drop(&mut self) { + assert_eq!(self.0, 1); + self.0 = 2; + BAZ.with(|_| {}); + assert_eq!(self.0, 2); + } +} + +fn main() { + std::thread::spawn(|| { + FOO.with(|_| {}); + }).join().unwrap(); +} -- cgit 1.4.1-3-g733a5 From cbdc52e9867ce550c2f8a22613106f65cf54a7ef Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 7 Jul 2015 09:35:56 -0400 Subject: Expand a bit on thread::park spurious wakeups Fixes #26475 --- src/libstd/thread/mod.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'src/libstd/thread') diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index dbb7d3233bc..3299c848ba7 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -508,9 +508,25 @@ pub fn sleep(dur: Duration) { imp::Thread::sleep(dur) } -/// Blocks unless or until the current thread's token is made available (may wake spuriously). +/// Blocks unless or until the current thread's token is made available. /// -/// See the module doc for more detail. +/// Every thread is equipped with some basic low-level blocking support, via +/// the `park()` function and the [`unpark()`][unpark] method. These can be +/// used as a more CPU-efficient implementation of a spinlock. +/// +/// [unpark]: struct.Thread.html#method.unpark +/// +/// The API is typically used by acquiring a handle to the current thread, +/// placing that handle in a shared data structure so that other threads can +/// find it, and then parking (in a loop with a check for the token actually +/// being acquired). +/// +/// A call to `park` does not guarantee that the thread will remain parked +/// forever, and callers should be prepared for this possibility. +/// +/// See the [module documentation][thread] for more detail. +/// +/// [thread]: index.html // // The implementation currently uses the trivial strategy of a Mutex+Condvar // with wakeup flag, which does not actually allow spurious wakeups. In the -- cgit 1.4.1-3-g733a5 From a700546feabd85fa06f2bbd446865c15431c0e09 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Thu, 23 Jul 2015 15:43:06 +0200 Subject: Fix `thread::catch_panic` documentation to mention its return value on success Fixes #27027. --- src/libstd/thread/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/libstd/thread') diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 3299c848ba7..20fcd309a6b 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -434,9 +434,9 @@ pub fn panicking() -> bool { /// Invokes a closure, capturing the cause of panic if one occurs. /// -/// This function will return `Ok(())` if the closure does not panic, and will -/// return `Err(cause)` if the closure panics. The `cause` returned is the -/// object with which panic was originally invoked. +/// This function will return `Ok` with the closure's result if the closure +/// does not panic, and will return `Err(cause)` if the closure panics. The +/// `cause` returned is the object with which panic was originally invoked. /// /// It is currently undefined behavior to unwind from Rust code into foreign /// code, so this function is particularly useful when Rust is called from -- 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/thread') 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 From 11c22180a790b5010140bb9712d47dd7bca7d903 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Sun, 26 Jul 2015 15:26:47 -0700 Subject: Remove the module-level documentation for thread::scoped It's deprecated and unsafe, so we shouldn't be encouraging people to use it. Move it to `std::thread::scoped` instead, since it's still useful information to anyone who is using the API. --- src/libstd/thread/mod.rs | 59 ++++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 34 deletions(-) (limited to 'src/libstd/thread') diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 20fcd309a6b..3388968c56c 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -85,33 +85,6 @@ //! value produced by the child thread, or `Err` of the value given to //! a call to `panic!` if the child panicked. //! -//! ## Scoped threads -//! -//! The `spawn` method does not allow the child and parent threads to -//! share any stack data, since that is not safe in general. However, -//! `scoped` makes it possible to share the parent's stack by forcing -//! a join before any relevant stack frames are popped: -//! -//! ```rust -//! # #![feature(scoped)] -//! use std::thread; -//! -//! let guard = thread::scoped(move || { -//! // some work here -//! }); -//! -//! // do some other work in the meantime -//! let output = guard.join(); -//! ``` -//! -//! The `scoped` function doesn't return a `Thread` directly; instead, -//! it returns a *join guard*. The join guard is an RAII-style guard -//! that will automatically join the child thread (block until it -//! terminates) when it is dropped. You can join the child thread in -//! advance by calling the `join` method on the guard, which will also -//! return the result produced by the thread. A handle to the thread -//! itself is available via the `thread` method of the join guard. -//! //! ## Configuring threads //! //! A new thread can be configured before it is spawned via the `Builder` type, @@ -288,7 +261,7 @@ impl Builder { /// upon being dropped. Because the child thread may refer to data on the /// current thread's stack (hence the "scoped" name), it cannot be detached; /// it *must* be joined before the relevant stack frame is popped. See the - /// module documentation for additional details. + /// documentation on `thread::scoped` for additional details. /// /// # Errors /// @@ -388,12 +361,30 @@ pub fn spawn(f: F) -> JoinHandle where /// Spawns a new *scoped* thread, returning a `JoinGuard` for it. /// -/// The join guard can be used to explicitly join the child thread (via -/// `join`), returning `Result`, or it will implicitly join the child -/// upon being dropped. Because the child thread may refer to data on the -/// current thread's stack (hence the "scoped" name), it cannot be detached; -/// it *must* be joined before the relevant stack frame is popped. See the -/// module documentation for additional details. +/// The `spawn` method does not allow the child and parent threads to +/// share any stack data, since that is not safe in general. However, +/// `scoped` makes it possible to share the parent's stack by forcing +/// a join before any relevant stack frames are popped: +/// +/// ```rust +/// # #![feature(scoped)] +/// use std::thread; +/// +/// let guard = thread::scoped(move || { +/// // some work here +/// }); +/// +/// // do some other work in the meantime +/// let output = guard.join(); +/// ``` +/// +/// The `scoped` function doesn't return a `Thread` directly; instead, it +/// returns a *join guard*. The join guard can be used to explicitly join +/// the child thread (via `join`), returning `Result`, or it will +/// implicitly join the child upon being dropped. Because the child thread +/// may refer to data on the current thread's stack (hence the "scoped" +/// name), it cannot be detached; it *must* be joined before the relevant +/// stack frame is popped. /// /// # Panics /// -- cgit 1.4.1-3-g733a5