From 02f9be8524ac6e9706867e2ae0abd09bee95dc53 Mon Sep 17 00:00:00 2001 From: Oliver Middleton Date: Tue, 7 Jun 2016 22:32:48 +0100 Subject: [PATCH 01/24] Remove unzip() SizeHint hack This was using an invalid iterator so is likely to end with buggy behaviour. It also doesn't even benefit many type in std including Vec so removing it shouldn't cause any problems. --- src/libcore/iter/iterator.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index 71ca5ccdc8dfb..8c8b201700718 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -11,7 +11,6 @@ use clone::Clone; use cmp::{Ord, PartialOrd, PartialEq, Ordering}; use default::Default; -use marker; use num::{Zero, One}; use ops::{Add, FnMut, Mul}; use option::Option::{self, Some, None}; @@ -1747,23 +1746,9 @@ pub trait Iterator { FromB: Default + Extend, Self: Sized + Iterator, { - struct SizeHint(usize, Option, marker::PhantomData); - impl Iterator for SizeHint { - type Item = A; - - fn next(&mut self) -> Option { None } - fn size_hint(&self) -> (usize, Option) { - (self.0, self.1) - } - } - - let (lo, hi) = self.size_hint(); let mut ts: FromA = Default::default(); let mut us: FromB = Default::default(); - ts.extend(SizeHint(lo, hi, marker::PhantomData)); - us.extend(SizeHint(lo, hi, marker::PhantomData)); - for (t, u) in self { ts.extend(Some(t)); us.extend(Some(u)); From ee469058e1676bcf5d36c8754aa48f83733f9bdb Mon Sep 17 00:00:00 2001 From: Oliver Middleton Date: Thu, 9 Jun 2016 23:03:14 +0100 Subject: [PATCH 02/24] Implement Binary, Octal, LowerHex and UpperHex for Wrapping --- src/libcore/num/mod.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 883e9206dde1d..06398fc094e85 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -66,6 +66,34 @@ impl fmt::Display for Wrapping { } } +#[stable(feature = "wrapping_fmt", since = "1.11.0")] +impl fmt::Binary for Wrapping { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +#[stable(feature = "wrapping_fmt", since = "1.11.0")] +impl fmt::Octal for Wrapping { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +#[stable(feature = "wrapping_fmt", since = "1.11.0")] +impl fmt::LowerHex for Wrapping { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +#[stable(feature = "wrapping_fmt", since = "1.11.0")] +impl fmt::UpperHex for Wrapping { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + mod wrapping; // All these modules are technically private and only exposed for libcoretest: From 7746d7c52fdbdc37a9eb9b5d463fb327685c9f08 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 9 Jun 2016 23:50:52 +0200 Subject: [PATCH 03/24] Add error codes block code flag --- src/librustdoc/html/markdown.rs | 28 ++++++++++++++++++++++------ src/librustdoc/test.rs | 26 +++++++++++++++++++++----- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 3baf22b38ef68..ed08f2f012332 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -408,7 +408,7 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) { tests.add_test(text.to_owned(), block_info.should_panic, block_info.no_run, block_info.ignore, block_info.test_harness, - block_info.compile_fail); + block_info.compile_fail, block_info.error_codes); } } @@ -454,6 +454,7 @@ struct LangString { rust: bool, test_harness: bool, compile_fail: bool, + error_codes: Vec, } impl LangString { @@ -465,6 +466,7 @@ impl LangString { rust: true, // NB This used to be `notrust = false` test_harness: false, compile_fail: false, + error_codes: Vec::new(), } } @@ -472,9 +474,14 @@ impl LangString { let mut seen_rust_tags = false; let mut seen_other_tags = false; let mut data = LangString::all_false(); - let allow_compile_fail = match get_unstable_features_setting() { - UnstableFeatures::Allow | UnstableFeatures::Cheat=> true, - _ => false, + let mut allow_compile_fail = false; + let mut allow_error_code_check = false; + match get_unstable_features_setting() { + UnstableFeatures::Allow | UnstableFeatures::Cheat => { + allow_compile_fail = true; + allow_error_code_check = true; + } + _ => {}, }; let tokens = string.split(|c: char| @@ -493,7 +500,15 @@ impl LangString { data.compile_fail = true; seen_rust_tags = true; data.no_run = true; - }, + } + x if allow_error_code_check && x.starts_with("E") && x.len() == 5 => { + if let Ok(_) = x[1..].parse::() { + data.error_codes.push(x.to_owned()); + seen_rust_tags = true; + } else { + seen_other_tags = true; + } + } _ => { seen_other_tags = true } } } @@ -577,7 +592,7 @@ mod tests { fn test_lang_string_parse() { fn t(s: &str, should_panic: bool, no_run: bool, ignore: bool, rust: bool, test_harness: bool, - compile_fail: bool) { + compile_fail: bool, error_codes: Vec) { assert_eq!(LangString::parse(s), LangString { should_panic: should_panic, no_run: no_run, @@ -585,6 +600,7 @@ mod tests { rust: rust, test_harness: test_harness, compile_fail: compile_fail, + error_codes: error_codes, }) } diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index f0ca89097f701..cb27da0a75978 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -176,7 +176,7 @@ fn scrape_test_config(krate: &::rustc::hir::Crate) -> TestOptions { fn runtest(test: &str, cratename: &str, cfgs: Vec, libs: SearchPaths, externs: core::Externs, should_panic: bool, no_run: bool, as_test_harness: bool, - compile_fail: bool, opts: &TestOptions) { + compile_fail: bool, mut error_codes: Vec, opts: &TestOptions) { // the test harness wants its own `main` & top level functions, so // never wrap the test in `fn main() { ... }` let test = maketest(test, Some(cratename), as_test_harness, opts); @@ -232,7 +232,7 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec, libs: SearchPaths, None, codemap.clone()); let old = io::set_panic(box Sink(data.clone())); - let _bomb = Bomb(data, old.unwrap_or(box io::stdout())); + let _bomb = Bomb(data.clone(), old.unwrap_or(box io::stdout())); // Compile the code let diagnostic_handler = errors::Handler::with_emitter(true, false, box emitter); @@ -273,13 +273,28 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec, libs: SearchPaths, } else if count == 0 && compile_fail == true { panic!("test compiled while it wasn't supposed to") } + if count > 0 && error_codes.len() > 0 { + let out = String::from_utf8(data.lock().unwrap().to_vec()).unwrap(); + error_codes = error_codes.into_iter().filter(|err| !out.contains(err)).collect(); + } } Ok(()) if compile_fail => panic!("test compiled while it wasn't supposed to"), _ => {} } } - Err(_) if compile_fail == false => panic!("couldn't compile the test"), - _ => {} + Err(_) => { + if compile_fail == false { + panic!("couldn't compile the test"); + } + if error_codes.len() > 0 { + let out = String::from_utf8(data.lock().unwrap().to_vec()).unwrap(); + error_codes.retain(|err| !out.contains(err)); + } + } + } + + if error_codes.len() > 0 { + panic!("Some expected error codes were not found: {:?}", error_codes); } if no_run { return } @@ -411,7 +426,7 @@ impl Collector { pub fn add_test(&mut self, test: String, should_panic: bool, no_run: bool, should_ignore: bool, - as_test_harness: bool, compile_fail: bool) { + as_test_harness: bool, compile_fail: bool, error_codes: Vec) { let name = if self.use_headers { let s = self.current_header.as_ref().map(|s| &**s).unwrap_or(""); format!("{}_{}", s, self.cnt) @@ -442,6 +457,7 @@ impl Collector { no_run, as_test_harness, compile_fail, + error_codes, &opts); }) }); From 677aa47d68b7db8bb51c651dcb73a3225b8c7d64 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 19 Jun 2016 11:55:34 +0300 Subject: [PATCH 04/24] Document `CStr::as_ptr` dangers. --- src/libstd/ffi/c_str.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 2bc7585f5fba9..0d3e18f9b966a 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -509,6 +509,38 @@ impl CStr { /// The returned pointer will be valid for as long as `self` is and points /// to a contiguous region of memory terminated with a 0 byte to represent /// the end of the string. + /// + /// **WARNING** + /// + /// It is your responsibility to make sure that the underlying memory is not + /// freed too early. For example, the following code will cause undefined + /// behaviour when `ptr` is used inside the `unsafe` block: + /// + /// ```no_run + /// use std::ffi::{CString}; + /// + /// let ptr = CString::new("Hello").unwrap().as_ptr(); + /// unsafe { + /// // `ptr` is dangling + /// *ptr; + /// } + /// ``` + /// + /// This happens because the pointer returned by `as_ptr` does not carry any + /// lifetime information and the string is deallocated immediately after + /// the `CString::new("Hello").unwrap().as_ptr()` expression is evaluated. + /// To fix the problem, bind the string to a local variable: + /// + /// ```no_run + /// use std::ffi::{CString}; + /// + /// let hello = CString::new("Hello").unwrap(); + /// let ptr = hello.as_ptr(); + /// unsafe { + /// // `ptr` is valid because `hello` is in scope + /// *ptr; + /// } + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn as_ptr(&self) -> *const c_char { self.inner.as_ptr() From 223cd210230a2bc2769320a52a159666167c4b3c Mon Sep 17 00:00:00 2001 From: Daan Sprenkels Date: Sun, 19 Jun 2016 14:23:30 +0200 Subject: [PATCH 05/24] doc: std::ops md formatting fix --- src/libcore/ops.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 5e1210b2ff9bd..7258c8a1b6b3d 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -1608,6 +1608,7 @@ impl> RangeFrom { /// See the [`contains()`](#method.contains) method for its characterization. /// /// It cannot serve as an iterator because it doesn't have a starting point. +/// /// ``` /// fn main() { /// assert_eq!((..5), std::ops::RangeTo{ end: 5 }); From e79b672a88e297d840fc8432fc2558804074ea46 Mon Sep 17 00:00:00 2001 From: Alexander Stocko Date: Sun, 19 Jun 2016 16:11:43 -0400 Subject: [PATCH 06/24] Fixed links in book/compiler-plugins.md --- src/doc/book/compiler-plugins.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/doc/book/compiler-plugins.md b/src/doc/book/compiler-plugins.md index 2d0cc61fb11d6..5b75ad6cfa858 100644 --- a/src/doc/book/compiler-plugins.md +++ b/src/doc/book/compiler-plugins.md @@ -34,7 +34,7 @@ code that manipulates syntax trees at compile time. Let's write a plugin -[`roman_numerals.rs`](https://github.com/rust-lang/rust/tree/master/src/test/auxiliary/roman_numerals.rs) +[`roman_numerals.rs`](https://github.com/rust-lang/rust/blob/master/src/test/run-pass-fulldeps/auxiliary/roman_numerals.rs) that implements Roman numeral integer literals. ```rust,ignore @@ -166,7 +166,8 @@ quasiquote as an ordinary plugin library. Plugins can extend [Rust's lint infrastructure](../reference.html#lint-check-attributes) with additional checks for -code style, safety, etc. Now let's write a plugin [`lint_plugin_test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/auxiliary/lint_plugin_test.rs) +code style, safety, etc. Now let's write a plugin +[`lint_plugin_test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/run-pass-fulldeps/auxiliary/lint_plugin_test.rs) that warns about any item named `lintme`. ```rust,ignore From d5a27594a3abeee9a8c81cc016dfb3a5b8b51d7a Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sun, 19 Jun 2016 17:51:35 -0400 Subject: [PATCH 07/24] Add examples for `std::thread::Thread::name`. --- src/libstd/thread/mod.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index c8783a60c4117..5f74ec639f1b9 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -478,6 +478,37 @@ impl Thread { } /// Gets the thread's name. + /// + /// # Examples + /// + /// Threads by default have no name specified: + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let handler = builder.spawn(|| { + /// assert!(thread::current().name().is_none()); + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + /// + /// Thread with a specified name: + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new() + /// .name("foo".into()); + /// + /// let handler = builder.spawn(|| { + /// assert_eq!(thread::current().name(), Some("foo")) + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn name(&self) -> Option<&str> { self.cname().map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) } ) From 7f4b75e1731552954ed1fbe260f3692a225865f6 Mon Sep 17 00:00:00 2001 From: Liigo Zhuang Date: Mon, 20 Jun 2016 16:05:30 +0800 Subject: [PATCH 08/24] diagnostics comes from different places now --- src/doc/rustc-ux-guidelines.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc-ux-guidelines.md b/src/doc/rustc-ux-guidelines.md index 6fc7219cdb368..15b3bfebfac2e 100644 --- a/src/doc/rustc-ux-guidelines.md +++ b/src/doc/rustc-ux-guidelines.md @@ -56,7 +56,19 @@ Error explanations are long form descriptions of error messages provided with the compiler. They are accessible via the `--explain` flag. Each explanation comes with an example of how to trigger it and advice on how to fix it. -* All of them are accessible [online](https://github.com/rust-lang/rust/blob/master/src/librustc/diagnostics.rs). +* All of them are accessible [online](http://doc.rust-lang.org/error-index.html), + which are auto-generated from rustc source code in different places: + [librustc](https://github.com/rust-lang/rust/blob/master/src/librustc/diagnostics.rs), + [librustc_borrowck](https://github.com/rust-lang/rust/blob/master/src/librustc_borrowck/diagnostics.rs), + [librustc_const_eval](https://github.com/rust-lang/rust/blob/master/src/librustc_const_eval/diagnostics.rs), + [librustc_lint](https://github.com/rust-lang/rust/blob/master/src/librustc_lint/types.rs), + [librustc_metadata](https://github.com/rust-lang/rust/blob/master/src/librustc_metadata/diagnostics.rs), + [librustc_mir](https://github.com/rust-lang/rust/blob/master/src/librustc_mir/diagnostics.rs), + [librustc_passes](https://github.com/rust-lang/rust/blob/master/src/librustc_passes/diagnostics.rs), + [librustc_privacy](https://github.com/rust-lang/rust/blob/master/src/librustc_privacy/diagnostics.rs), + [librustc_resolve](https://github.com/rust-lang/rust/blob/master/src/librustc_resolve/diagnostics.rs), + [librustc_trans](https://github.com/rust-lang/rust/blob/master/src/librustc_trans/diagnostics.rs), + [librustc_typeck](https://github.com/rust-lang/rust/blob/master/src/librustc_typeck/diagnostics.rs). * Explanations have full markdown support. Use it, especially to highlight code with backticks. * When talking about the compiler, call it `the compiler`, not `Rust` or From 9d03128f58a2814e5d5f8080a9507245853b92ca Mon Sep 17 00:00:00 2001 From: Seo Sanghyeon Date: Mon, 20 Jun 2016 19:31:07 +0900 Subject: [PATCH 09/24] Run debuginfo tests on Travis --- src/etc/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/etc/Dockerfile b/src/etc/Dockerfile index 58fa1474444a1..f1c56d8d3960f 100644 --- a/src/etc/Dockerfile +++ b/src/etc/Dockerfile @@ -4,6 +4,8 @@ FROM ubuntu:xenial # Download stage0, see src/bootstrap/bootstrap.py # g++ # Compile LLVM binding in src/rustllvm +# gdb +# Used to run tests in src/test/debuginfo # git # Get commit hash and commit date in version string # make @@ -17,7 +19,7 @@ FROM ubuntu:xenial # FileCheck is used to run tests in src/test/codegen RUN apt-get update && apt-get -y install \ - curl g++ git make \ + curl g++ gdb git make \ libedit-dev zlib1g-dev \ llvm-3.7-tools From 3c778953d559ca420449421f9415589ef2976a18 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 20 Jun 2016 10:02:48 -0700 Subject: [PATCH 10/24] configure: Remove clang version checks We no C++ and an incredibly small amount of C code as part of the build, so there's not really much need for us to strictly check the version of compilers as we're not really stressing anything. LLVM is a pretty huge chunk of C++ but it should be the responsibility of LLVM to ensure that it can build with a particular clang/gcc version, not ours (as this logic changes over time). These version checks seem to basically just by us a regular stream of PRs every six weeks or so when a new version is releases, so they're not really buying us much. As a result, remove them and we can add then back piecemeal perhaps as a blacklist if we really need to. --- configure | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/configure b/configure index b2334740f937e..c08e8d39154dc 100755 --- a/configure +++ b/configure @@ -1040,37 +1040,6 @@ if [ -n "$CFG_ENABLE_CLANG" ] then case "$CC" in (''|*clang) - CFG_CLANG_REPORTED_VERSION=$($CFG_CC --version | grep version) - - if echo $CFG_CLANG_REPORTED_VERSION | grep -q "(based on LLVM "; then - CFG_CLANG_VERSION=$(echo $CFG_CLANG_REPORTED_VERSION | sed 's/.*(based on LLVM \(.*\))/\1/') - elif echo $CFG_CLANG_REPORTED_VERSION | grep -q "Apple LLVM"; then - CFG_OSX_CLANG_VERSION=$(echo $CFG_CLANG_REPORTED_VERSION | sed 's/.*version \(.*\) .*/\1/') - else - CFG_CLANG_VERSION=$(echo $CFG_CLANG_REPORTED_VERSION | sed 's/.*version \(.*\) .*/\1/') - fi - - if [ -n "$CFG_OSX_CLANG_VERSION" ] - then - case $CFG_OSX_CLANG_VERSION in - (7.0* | 7.1* | 7.2* | 7.3* | 8.0*) - step_msg "found ok version of APPLE CLANG: $CFG_OSX_CLANG_VERSION" - ;; - (*) - err "bad APPLE CLANG version: $CFG_OSX_CLANG_VERSION, need >=7.0" - ;; - esac - else - case $CFG_CLANG_VERSION in - (3.2* | 3.3* | 3.4* | 3.5* | 3.6* | 3.7* | 3.8* | 3.9*) - step_msg "found ok version of CLANG: $CFG_CLANG_VERSION" - ;; - (*) - err "bad CLANG version: $CFG_CLANG_VERSION, need >=3.0svn" - ;; - esac - fi - if [ -z "$CC" ] then CFG_CC="clang" From ddfaf10f6b30d19024ec4d4d3ebe4a598e1a66f2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 10 Jun 2016 00:34:46 +0200 Subject: [PATCH 11/24] Add error code flag --- src/librustc_privacy/diagnostics.rs | 10 +++++----- src/librustdoc/html/markdown.rs | 27 +++++++++++++++------------ src/librustdoc/test.rs | 2 +- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/librustc_privacy/diagnostics.rs b/src/librustc_privacy/diagnostics.rs index 36ece929413db..66afe5835bf6f 100644 --- a/src/librustc_privacy/diagnostics.rs +++ b/src/librustc_privacy/diagnostics.rs @@ -16,7 +16,7 @@ E0445: r##" A private trait was used on a public type parameter bound. Erroneous code examples: -```compile_fail +```compile_fail,E0445 #![deny(private_in_public)] trait Foo { @@ -46,7 +46,7 @@ pub fn foo (t: T) {} // ok! E0446: r##" A private type was used in a public type signature. Erroneous code example: -```compile_fail +```compile_fail,E0446 #![deny(private_in_public)] mod Foo { @@ -100,7 +100,7 @@ pub enum Foo { Since the enum is already public, adding `pub` on one its elements is unnecessary. Example: -```compile_fail +```compile_fail, enum Foo { pub Bar, // not ok! } @@ -119,7 +119,7 @@ E0450: r##" A tuple constructor was invoked while some of its fields are private. Erroneous code example: -```compile_fail +```compile_fail,E0450 mod Bar { pub struct Foo(isize); } @@ -157,7 +157,7 @@ let f = bar::Foo::new(1); E0451: r##" A struct constructor with private fields was invoked. Erroneous code example: -```compile_fail +```compile_fail,E0451 mod Bar { pub struct Foo { pub a: isize, diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index ed08f2f012332..139e1033175ea 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -605,18 +605,21 @@ mod tests { } // marker | should_panic| no_run| ignore| rust | test_harness| compile_fail - t("", false, false, false, true, false, false); - t("rust", false, false, false, true, false, false); - t("sh", false, false, false, false, false, false); - t("ignore", false, false, true, true, false, false); - t("should_panic", true, false, false, true, false, false); - t("no_run", false, true, false, true, false, false); - t("test_harness", false, false, false, true, true, false); - t("compile_fail", false, true, false, true, false, true); - t("{.no_run .example}", false, true, false, true, false, false); - t("{.sh .should_panic}", true, false, false, true, false, false); - t("{.example .rust}", false, false, false, true, false, false); - t("{.test_harness .rust}", false, false, false, true, true, false); + // | error_codes + t("", false, false, false, true, false, false, Vec::new()); + t("rust", false, false, false, true, false, false, Vec::new()); + t("sh", false, false, false, false, false, false, Vec::new()); + t("ignore", false, false, true, true, false, false, Vec::new()); + t("should_panic", true, false, false, true, false, false, Vec::new()); + t("no_run", false, true, false, true, false, false, Vec::new()); + t("test_harness", false, false, false, true, true, false, Vec::new()); + t("compile_fail", false, true, false, true, false, true, Vec::new()); + t("E0450", false, false, false, true, false, false, + vec!("E0450".to_owned())); + t("{.no_run .example}", false, true, false, true, false, false, Vec::new()); + t("{.sh .should_panic}", true, false, false, true, false, false, Vec::new()); + t("{.example .rust}", false, false, false, true, false, false, Vec::new()); + t("{.test_harness .rust}", false, false, false, true, true, false, Vec::new()); } #[test] diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index cb27da0a75978..c17af55ca10af 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -275,7 +275,7 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec, libs: SearchPaths, } if count > 0 && error_codes.len() > 0 { let out = String::from_utf8(data.lock().unwrap().to_vec()).unwrap(); - error_codes = error_codes.into_iter().filter(|err| !out.contains(err)).collect(); + error_codes.retain(|err| !out.contains(err)); } } Ok(()) if compile_fail => panic!("test compiled while it wasn't supposed to"), From 03d86ba2f55f74d72a22b3a67a50bb586fcd1930 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 19 Jun 2016 13:50:37 -0400 Subject: [PATCH 12/24] Add regression test for #23281 Closes #23281 --- src/test/compile-fail/issue-23281.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/test/compile-fail/issue-23281.rs diff --git a/src/test/compile-fail/issue-23281.rs b/src/test/compile-fail/issue-23281.rs new file mode 100644 index 0000000000000..5feeb36b1e4d4 --- /dev/null +++ b/src/test/compile-fail/issue-23281.rs @@ -0,0 +1,20 @@ +// Copyright 2016 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. + +// ignore-tidy-linelength + +pub struct Struct; + +impl Struct { + pub fn function(funs: Vec ()>) {} + //~^ ERROR the trait bound `std::ops::Fn() + 'static: std::marker::Sized` is not satisfied +} + +fn main() {} From 93c32b55e2f03bc88d3d1601626bcd9059924005 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Thu, 9 Jun 2016 18:13:16 +0300 Subject: [PATCH 13/24] trans: split trans_consume off from trans_operand. --- src/librustc_trans/mir/operand.rs | 90 +++++++++++++++++-------------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index 80ff0a92d8d46..980db76d632c1 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -164,56 +164,66 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { OperandRef { val: val, ty: ty } } - pub fn trans_operand(&mut self, + pub fn trans_consume(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>, - operand: &mir::Operand<'tcx>) + lvalue: &mir::Lvalue<'tcx>) -> OperandRef<'tcx> { - debug!("trans_operand(operand={:?})", operand); + debug!("trans_consume(lvalue={:?})", lvalue); - match *operand { - mir::Operand::Consume(ref lvalue) => { - // watch out for temporaries that do not have an - // alloca; they are handled somewhat differently - if let &mir::Lvalue::Temp(index) = lvalue { - match self.temps[index] { - TempRef::Operand(Some(o)) => { - return o; - } - TempRef::Operand(None) => { - bug!("use of {:?} before def", lvalue); - } - TempRef::Lvalue(..) => { - // use path below - } - } + // watch out for temporaries that do not have an + // alloca; they are handled somewhat differently + if let &mir::Lvalue::Temp(index) = lvalue { + match self.temps[index] { + TempRef::Operand(Some(o)) => { + return o; + } + TempRef::Operand(None) => { + bug!("use of {:?} before def", lvalue); } + TempRef::Lvalue(..) => { + // use path below + } + } + } - // Moves out of pair fields are trivial. - if let &mir::Lvalue::Projection(ref proj) = lvalue { - if let mir::Lvalue::Temp(index) = proj.base { - let temp_ref = &self.temps[index]; - if let &TempRef::Operand(Some(o)) = temp_ref { - match (o.val, &proj.elem) { - (OperandValue::Pair(a, b), - &mir::ProjectionElem::Field(ref f, ty)) => { - let llval = [a, b][f.index()]; - return OperandRef { - val: OperandValue::Immediate(llval), - ty: bcx.monomorphize(&ty) - }; - } - _ => {} - } + // Moves out of pair fields are trivial. + if let &mir::Lvalue::Projection(ref proj) = lvalue { + if let mir::Lvalue::Temp(index) = proj.base { + let temp_ref = &self.temps[index]; + if let &TempRef::Operand(Some(o)) = temp_ref { + match (o.val, &proj.elem) { + (OperandValue::Pair(a, b), + &mir::ProjectionElem::Field(ref f, ty)) => { + let llval = [a, b][f.index()]; + return OperandRef { + val: OperandValue::Immediate(llval), + ty: bcx.monomorphize(&ty) + }; } + _ => {} } } + } + } + + // for most lvalues, to consume them we just load them + // out from their home + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let ty = tr_lvalue.ty.to_ty(bcx.tcx()); + self.trans_load(bcx, tr_lvalue.llval, ty) + } - // for most lvalues, to consume them we just load them - // out from their home - let tr_lvalue = self.trans_lvalue(bcx, lvalue); - let ty = tr_lvalue.ty.to_ty(bcx.tcx()); - self.trans_load(bcx, tr_lvalue.llval, ty) + pub fn trans_operand(&mut self, + bcx: &BlockAndBuilder<'bcx, 'tcx>, + operand: &mir::Operand<'tcx>) + -> OperandRef<'tcx> + { + debug!("trans_operand(operand={:?})", operand); + + match *operand { + mir::Operand::Consume(ref lvalue) => { + self.trans_consume(bcx, lvalue) } mir::Operand::Constant(ref constant) => { From eb9cb4dbca6eab5b7a50934fe7385e1a99d9dc62 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Thu, 9 Jun 2016 18:14:47 +0300 Subject: [PATCH 14/24] trans: derefs don't need the pointer in an alloca. --- src/librustc_trans/mir/analyze.rs | 7 +++++++ src/librustc_trans/mir/lvalue.rs | 31 ++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index d1c1053ac46b2..93ac002f2a989 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -151,6 +151,13 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> { } } + // A deref projection only reads the pointer, never needs the lvalue. + if let mir::Lvalue::Projection(ref proj) = *lvalue { + if let mir::ProjectionElem::Deref = proj.elem { + return self.visit_lvalue(&proj.base, LvalueContext::Consume); + } + } + self.super_lvalue(lvalue, context); } } diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs index 0a66a147568e6..f7e159797479c 100644 --- a/src/librustc_trans/mir/lvalue.rs +++ b/src/librustc_trans/mir/lvalue.rs @@ -27,6 +27,7 @@ use Disr; use std::ptr; use super::{MirContext, TempRef}; +use super::operand::OperandValue; #[derive(Copy, Clone, Debug)] pub struct LvalueRef<'tcx> { @@ -121,6 +122,26 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let return_ty = fn_return_ty.unwrap(); LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty)) }, + mir::Lvalue::Projection(box mir::Projection { + ref base, + elem: mir::ProjectionElem::Deref + }) => { + // Load the pointer from its location. + let ptr = self.trans_consume(bcx, base); + let projected_ty = LvalueTy::from_ty(ptr.ty) + .projection_ty(tcx, &mir::ProjectionElem::Deref); + let projected_ty = bcx.monomorphize(&projected_ty); + let (llptr, llextra) = match ptr.val { + OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()), + OperandValue::Pair(llptr, llextra) => (llptr, llextra), + OperandValue::Ref(_) => bug!("Deref of by-Ref type {:?}", ptr.ty) + }; + LvalueRef { + llval: llptr, + llextra: llextra, + ty: projected_ty, + } + } mir::Lvalue::Projection(ref projection) => { let tr_base = self.trans_lvalue(bcx, &projection.base); let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem); @@ -138,15 +159,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { }; let (llprojected, llextra) = match projection.elem { - mir::ProjectionElem::Deref => { - let base_ty = tr_base.ty.to_ty(tcx); - if common::type_is_sized(tcx, projected_ty.to_ty(tcx)) { - (base::load_ty_builder(bcx, tr_base.llval, base_ty), - ptr::null_mut()) - } else { - load_fat_ptr(bcx, tr_base.llval) - } - } + mir::ProjectionElem::Deref => bug!(), mir::ProjectionElem::Field(ref field, _) => { let base_ty = tr_base.ty.to_ty(tcx); let base_repr = adt::represent_type(ccx, base_ty); From bec32eb4ff6d9f19a02441269916dec5cb348927 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Thu, 9 Jun 2016 18:15:15 +0300 Subject: [PATCH 15/24] trans: noop drops don't need their lvalue in an alloca. --- src/librustc_trans/mir/analyze.rs | 11 ++++++++++- src/librustc_trans/mir/block.rs | 7 +++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index 93ac002f2a989..65581d43f8820 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -18,6 +18,7 @@ use rustc::mir::repr::TerminatorKind; use rustc::mir::visit::{Visitor, LvalueContext}; use rustc::mir::traversal; use common::{self, Block, BlockAndBuilder}; +use glue; use super::rvalue; pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>, @@ -138,13 +139,21 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> { LvalueContext::Consume => { } LvalueContext::Store | - LvalueContext::Drop | LvalueContext::Inspect | LvalueContext::Borrow { .. } | LvalueContext::Slice { .. } | LvalueContext::Projection => { self.mark_as_lvalue(temp.index()); } + LvalueContext::Drop => { + let ty = self.mir.temp_decls[index as usize].ty; + let ty = self.bcx.monomorphize(&ty); + + // Only need the lvalue if we're actually dropping it. + if glue::type_needs_drop(self.bcx.tcx(), ty) { + self.mark_as_lvalue(index as usize); + } + } } } _ => { diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index bdcf3dd8cd418..4591fa2857811 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -196,13 +196,16 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } mir::TerminatorKind::Drop { ref location, target, unwind } => { - let lvalue = self.trans_lvalue(&bcx, location); - let ty = lvalue.ty.to_ty(bcx.tcx()); + let ty = mir.lvalue_ty(bcx.tcx(), location).to_ty(bcx.tcx()); + let ty = bcx.monomorphize(&ty); + // Double check for necessity to drop if !glue::type_needs_drop(bcx.tcx(), ty) { funclet_br(self, bcx, target); return; } + + let lvalue = self.trans_lvalue(&bcx, location); let drop_fn = glue::get_drop_glue(bcx.ccx(), ty); let drop_ty = glue::get_drop_glue_type(bcx.tcx(), ty); let llvalue = if drop_ty != ty { From 7279af86c8f6ab080e7d360e497e1c75b313d1a1 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Mon, 20 Jun 2016 23:55:14 +0300 Subject: [PATCH 16/24] trans: generalize immediate temporaries to all MIR locals. --- src/librustc/mir/repr.rs | 35 ++++++ src/librustc/ty/sty.rs | 7 ++ src/librustc_trans/mir/analyze.rs | 141 +++++++++++++----------- src/librustc_trans/mir/block.rs | 141 ++++++++++++++---------- src/librustc_trans/mir/constant.rs | 67 +++++------- src/librustc_trans/mir/lvalue.rs | 101 ++++++++---------- src/librustc_trans/mir/mod.rs | 153 +++++++++++++++++---------- src/librustc_trans/mir/operand.rs | 21 ++-- src/librustc_trans/mir/statement.rs | 53 +++++----- src/test/codegen/loads.rs | 3 + src/test/codegen/naked-functions.rs | 4 +- src/test/compile-fail/issue-26548.rs | 3 + 12 files changed, 422 insertions(+), 307 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 03ae91fefb925..a6052f9aa75a1 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -144,6 +144,40 @@ impl<'tcx> Mir<'tcx> { pub fn predecessors_for(&self, bb: BasicBlock) -> Ref> { Ref::map(self.predecessors(), |p| &p[bb]) } + + /// Maps locals (Arg's, Var's, Temp's and ReturnPointer, in that order) + /// to their index in the whole list of locals. This is useful if you + /// want to treat all locals the same instead of repeating yourself. + pub fn local_index(&self, lvalue: &Lvalue<'tcx>) -> Option { + let idx = match *lvalue { + Lvalue::Arg(arg) => arg.index(), + Lvalue::Var(var) => { + self.arg_decls.len() + + var.index() + } + Lvalue::Temp(temp) => { + self.arg_decls.len() + + self.var_decls.len() + + temp.index() + } + Lvalue::ReturnPointer => { + self.arg_decls.len() + + self.var_decls.len() + + self.temp_decls.len() + } + Lvalue::Static(_) | + Lvalue::Projection(_) => return None + }; + Some(Local::new(idx)) + } + + /// Counts the number of locals, such that that local_index + /// will always return an index smaller than this count. + pub fn count_locals(&self) -> usize { + self.arg_decls.len() + + self.var_decls.len() + + self.temp_decls.len() + 1 + } } impl<'tcx> Index for Mir<'tcx> { @@ -663,6 +697,7 @@ impl<'tcx> Debug for Statement<'tcx> { newtype_index!(Var, "var"); newtype_index!(Temp, "tmp"); newtype_index!(Arg, "arg"); +newtype_index!(Local, "local"); /// A path to a value; something that can be evaluated without /// changing or disturbing program state. diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index e2a9a95412669..7c69618068a6b 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -492,6 +492,13 @@ impl<'tcx> FnOutput<'tcx> { ty::FnDiverging => def } } + + pub fn maybe_converging(self) -> Option> { + match self { + ty::FnConverging(t) => Some(t), + ty::FnDiverging => None + } + } } pub type PolyFnOutput<'tcx> = Binder>; diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index 65581d43f8820..dac7afab6e38b 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! An analysis to determine which temporaries require allocas and +//! An analysis to determine which locals require allocas and //! which do not. use rustc_data_structures::bitvec::BitVector; @@ -21,16 +21,20 @@ use common::{self, Block, BlockAndBuilder}; use glue; use super::rvalue; -pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>, - mir: &mir::Mir<'tcx>) -> BitVector { +pub fn lvalue_locals<'bcx, 'tcx>(bcx: Block<'bcx,'tcx>, + mir: &mir::Mir<'tcx>) -> BitVector { let bcx = bcx.build(); - let mut analyzer = TempAnalyzer::new(mir, &bcx, mir.temp_decls.len()); + let mut analyzer = LocalAnalyzer::new(mir, &bcx); analyzer.visit_mir(mir); - for (index, temp_decl) in mir.temp_decls.iter().enumerate() { - let ty = bcx.monomorphize(&temp_decl.ty); - debug!("temp {:?} has type {:?}", index, ty); + let local_types = mir.arg_decls.iter().map(|a| a.ty) + .chain(mir.var_decls.iter().map(|v| v.ty)) + .chain(mir.temp_decls.iter().map(|t| t.ty)) + .chain(mir.return_ty.maybe_converging()); + for (index, ty) in local_types.enumerate() { + let ty = bcx.monomorphize(&ty); + debug!("local {} has type {:?}", index, ty); if ty.is_scalar() || ty.is_unique() || ty.is_region_ptr() || @@ -50,66 +54,87 @@ pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>, // (e.g. structs) into an alloca unconditionally, just so // that we don't have to deal with having two pathways // (gep vs extractvalue etc). - analyzer.mark_as_lvalue(index); + analyzer.mark_as_lvalue(mir::Local::new(index)); } } - analyzer.lvalue_temps + analyzer.lvalue_locals } -struct TempAnalyzer<'mir, 'bcx: 'mir, 'tcx: 'bcx> { +struct LocalAnalyzer<'mir, 'bcx: 'mir, 'tcx: 'bcx> { mir: &'mir mir::Mir<'tcx>, bcx: &'mir BlockAndBuilder<'bcx, 'tcx>, - lvalue_temps: BitVector, + lvalue_locals: BitVector, seen_assigned: BitVector } -impl<'mir, 'bcx, 'tcx> TempAnalyzer<'mir, 'bcx, 'tcx> { +impl<'mir, 'bcx, 'tcx> LocalAnalyzer<'mir, 'bcx, 'tcx> { fn new(mir: &'mir mir::Mir<'tcx>, - bcx: &'mir BlockAndBuilder<'bcx, 'tcx>, - temp_count: usize) -> TempAnalyzer<'mir, 'bcx, 'tcx> { - TempAnalyzer { + bcx: &'mir BlockAndBuilder<'bcx, 'tcx>) + -> LocalAnalyzer<'mir, 'bcx, 'tcx> { + let local_count = mir.count_locals(); + LocalAnalyzer { mir: mir, bcx: bcx, - lvalue_temps: BitVector::new(temp_count), - seen_assigned: BitVector::new(temp_count) + lvalue_locals: BitVector::new(local_count), + seen_assigned: BitVector::new(local_count) } } - fn mark_as_lvalue(&mut self, temp: usize) { - debug!("marking temp {} as lvalue", temp); - self.lvalue_temps.insert(temp); + fn mark_as_lvalue(&mut self, local: mir::Local) { + debug!("marking {:?} as lvalue", local); + self.lvalue_locals.insert(local.index()); } - fn mark_assigned(&mut self, temp: usize) { - if !self.seen_assigned.insert(temp) { - self.mark_as_lvalue(temp); + fn mark_assigned(&mut self, local: mir::Local) { + if !self.seen_assigned.insert(local.index()) { + self.mark_as_lvalue(local); } } } -impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> { +impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> { fn visit_assign(&mut self, block: mir::BasicBlock, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>) { debug!("visit_assign(block={:?}, lvalue={:?}, rvalue={:?})", block, lvalue, rvalue); - match *lvalue { - mir::Lvalue::Temp(temp) => { - self.mark_assigned(temp.index()); - if !rvalue::rvalue_creates_operand(self.mir, self.bcx, rvalue) { - self.mark_as_lvalue(temp.index()); - } - } - _ => { - self.visit_lvalue(lvalue, LvalueContext::Store); + if let Some(index) = self.mir.local_index(lvalue) { + self.mark_assigned(index); + if !rvalue::rvalue_creates_operand(self.mir, self.bcx, rvalue) { + self.mark_as_lvalue(index); } + } else { + self.visit_lvalue(lvalue, LvalueContext::Store); } self.visit_rvalue(rvalue); } + fn visit_terminator_kind(&mut self, + block: mir::BasicBlock, + kind: &mir::TerminatorKind<'tcx>) { + match *kind { + mir::TerminatorKind::Call { + func: mir::Operand::Constant(mir::Constant { + literal: mir::Literal::Item { def_id, .. }, .. + }), + ref args, .. + } if Some(def_id) == self.bcx.tcx().lang_items.box_free_fn() => { + // box_free(x) shares with `drop x` the property that it + // is not guaranteed to be statically dominated by the + // definition of x, so x must always be in an alloca. + if let mir::Operand::Consume(ref lvalue) = args[0] { + self.visit_lvalue(lvalue, LvalueContext::Drop); + } + } + _ => {} + } + + self.super_terminator_kind(block, kind); + } + fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) { @@ -117,9 +142,9 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> { // Allow uses of projections of immediate pair fields. if let mir::Lvalue::Projection(ref proj) = *lvalue { - if let mir::Lvalue::Temp(temp) = proj.base { - let ty = self.mir.temp_decls[temp].ty; - let ty = self.bcx.monomorphize(&ty); + if self.mir.local_index(&proj.base).is_some() { + let ty = self.mir.lvalue_ty(self.bcx.tcx(), &proj.base); + let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx())); if common::type_is_imm_pair(self.bcx.ccx(), ty) { if let mir::ProjectionElem::Field(..) = proj.elem { if let LvalueContext::Consume = context { @@ -130,34 +155,30 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> { } } - match *lvalue { - mir::Lvalue::Temp(temp) => { - match context { - LvalueContext::Call => { - self.mark_assigned(temp.index()); - } - LvalueContext::Consume => { - } - LvalueContext::Store | - LvalueContext::Inspect | - LvalueContext::Borrow { .. } | - LvalueContext::Slice { .. } | - LvalueContext::Projection => { - self.mark_as_lvalue(temp.index()); - } - LvalueContext::Drop => { - let ty = self.mir.temp_decls[index as usize].ty; - let ty = self.bcx.monomorphize(&ty); + if let Some(index) = self.mir.local_index(lvalue) { + match context { + LvalueContext::Call => { + self.mark_assigned(index); + } + LvalueContext::Consume => { + } + LvalueContext::Store | + LvalueContext::Inspect | + LvalueContext::Borrow { .. } | + LvalueContext::Slice { .. } | + LvalueContext::Projection => { + self.mark_as_lvalue(index); + } + LvalueContext::Drop => { + let ty = self.mir.lvalue_ty(self.bcx.tcx(), lvalue); + let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx())); - // Only need the lvalue if we're actually dropping it. - if glue::type_needs_drop(self.bcx.tcx(), ty) { - self.mark_as_lvalue(index as usize); - } + // Only need the lvalue if we're actually dropping it. + if glue::type_needs_drop(self.bcx.tcx(), ty) { + self.mark_as_lvalue(index); } } } - _ => { - } } // A deref projection only reads the pointer, never needs the lvalue. diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 4591fa2857811..7a7f1901736c5 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -32,7 +32,7 @@ use type_::Type; use rustc_data_structures::fnv::FnvHashMap; use syntax::parse::token; -use super::{MirContext, TempRef}; +use super::{MirContext, LocalRef}; use super::analyze::CleanupKind; use super::constant::Const; use super::lvalue::{LvalueRef, load_fat_ptr}; @@ -186,9 +186,43 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } mir::TerminatorKind::Return => { - bcx.with_block(|bcx| { - self.fcx.build_return_block(bcx, debug_loc); - }) + let ret = bcx.fcx().fn_ty.ret; + if ret.is_ignore() || ret.is_indirect() { + bcx.ret_void(); + return; + } + + let llval = if let Some(cast_ty) = ret.cast { + let index = mir.local_index(&mir::Lvalue::ReturnPointer).unwrap(); + let op = match self.locals[index] { + LocalRef::Operand(Some(op)) => op, + LocalRef::Operand(None) => bug!("use of return before def"), + LocalRef::Lvalue(tr_lvalue) => { + OperandRef { + val: Ref(tr_lvalue.llval), + ty: tr_lvalue.ty.to_ty(bcx.tcx()) + } + } + }; + let llslot = match op.val { + Immediate(_) | Pair(..) => { + let llscratch = build::AllocaFcx(bcx.fcx(), ret.original_ty, "ret"); + self.store_operand(&bcx, llscratch, op); + llscratch + } + Ref(llval) => llval + }; + let load = bcx.load(bcx.pointercast(llslot, cast_ty.ptr_to())); + let llalign = llalign_of_min(bcx.ccx(), ret.ty); + unsafe { + llvm::LLVMSetAlignment(load, llalign); + } + load + } else { + let op = self.trans_consume(&bcx, &mir::Lvalue::ReturnPointer); + op.pack_if_pair(&bcx).immediate() + }; + bcx.ret(llval); } mir::TerminatorKind::Unreachable => { @@ -537,7 +571,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { fn trans_argument(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>, - mut op: OperandRef<'tcx>, + op: OperandRef<'tcx>, llargs: &mut Vec, fn_ty: &FnType, next_idx: &mut usize, @@ -565,8 +599,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { self.trans_argument(bcx, imm_op(meta), llargs, fn_ty, next_idx, callee); return; } - - op = op.pack_if_pair(bcx); } let arg = &fn_ty.args[*next_idx]; @@ -583,14 +615,16 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // Force by-ref if we have to load through a cast pointer. let (mut llval, by_ref) = match op.val { - Immediate(llval) if arg.is_indirect() || arg.cast.is_some() => { - let llscratch = build::AllocaFcx(bcx.fcx(), arg.original_ty, "arg"); - bcx.store(llval, llscratch); - (llscratch, true) + Immediate(_) | Pair(..) => { + if arg.is_indirect() || arg.cast.is_some() { + let llscratch = build::AllocaFcx(bcx.fcx(), arg.original_ty, "arg"); + self.store_operand(bcx, llscratch, op); + (llscratch, true) + } else { + (op.pack_if_pair(bcx).immediate(), false) + } } - Immediate(llval) => (llval, false), - Ref(llval) => (llval, true), - Pair(..) => bug!("pairs handled above") + Ref(llval) => (llval, true) }; if by_ref && !arg.is_indirect() { @@ -776,40 +810,39 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { if fn_ret_ty.is_ignore() { return ReturnDest::Nothing; } - let dest = match *dest { - mir::Lvalue::Temp(idx) => { - let ret_ty = self.lvalue_ty(dest); - match self.temps[idx] { - TempRef::Lvalue(dest) => dest, - TempRef::Operand(None) => { - // Handle temporary lvalues, specifically Operand ones, as - // they don't have allocas - return if fn_ret_ty.is_indirect() { - // Odd, but possible, case, we have an operand temporary, - // but the calling convention has an indirect return. - let tmp = bcx.with_block(|bcx| { - base::alloc_ty(bcx, ret_ty, "tmp_ret") - }); - llargs.push(tmp); - ReturnDest::IndirectOperand(tmp, idx) - } else if is_intrinsic { - // Currently, intrinsics always need a location to store - // the result. so we create a temporary alloca for the - // result - let tmp = bcx.with_block(|bcx| { - base::alloc_ty(bcx, ret_ty, "tmp_ret") - }); - ReturnDest::IndirectOperand(tmp, idx) - } else { - ReturnDest::DirectOperand(idx) - }; - } - TempRef::Operand(Some(_)) => { - bug!("lvalue temp already assigned to"); - } + let dest = if let Some(index) = self.mir.local_index(dest) { + let ret_ty = self.lvalue_ty(dest); + match self.locals[index] { + LocalRef::Lvalue(dest) => dest, + LocalRef::Operand(None) => { + // Handle temporary lvalues, specifically Operand ones, as + // they don't have allocas + return if fn_ret_ty.is_indirect() { + // Odd, but possible, case, we have an operand temporary, + // but the calling convention has an indirect return. + let tmp = bcx.with_block(|bcx| { + base::alloc_ty(bcx, ret_ty, "tmp_ret") + }); + llargs.push(tmp); + ReturnDest::IndirectOperand(tmp, index) + } else if is_intrinsic { + // Currently, intrinsics always need a location to store + // the result. so we create a temporary alloca for the + // result + let tmp = bcx.with_block(|bcx| { + base::alloc_ty(bcx, ret_ty, "tmp_ret") + }); + ReturnDest::IndirectOperand(tmp, index) + } else { + ReturnDest::DirectOperand(index) + }; + } + LocalRef::Operand(Some(_)) => { + bug!("lvalue local already assigned to"); } } - _ => self.trans_lvalue(bcx, dest) + } else { + self.trans_lvalue(bcx, dest) }; if fn_ret_ty.is_indirect() { llargs.push(dest.llval); @@ -853,11 +886,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { match dest { Nothing => (), Store(dst) => ret_ty.store(bcx, op.immediate(), dst), - IndirectOperand(tmp, idx) => { + IndirectOperand(tmp, index) => { let op = self.trans_load(bcx, tmp, op.ty); - self.temps[idx] = TempRef::Operand(Some(op)); + self.locals[index] = LocalRef::Operand(Some(op)); } - DirectOperand(idx) => { + DirectOperand(index) => { // If there is a cast, we have to store and reload. let op = if ret_ty.cast.is_some() { let tmp = bcx.with_block(|bcx| { @@ -868,7 +901,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } else { op.unpack_if_pair(bcx) }; - self.temps[idx] = TempRef::Operand(Some(op)); + self.locals[index] = LocalRef::Operand(Some(op)); } } } @@ -879,8 +912,8 @@ enum ReturnDest { Nothing, // Store the return value to the pointer Store(ValueRef), - // Stores an indirect return value to an operand temporary lvalue - IndirectOperand(ValueRef, mir::Temp), - // Stores a direct return value to an operand temporary lvalue - DirectOperand(mir::Temp) + // Stores an indirect return value to an operand local lvalue + IndirectOperand(ValueRef, mir::Local), + // Stores a direct return value to an operand local lvalue + DirectOperand(mir::Local) } diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 316c7341ef142..30be4a9737270 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -203,17 +203,8 @@ struct MirConstContext<'a, 'tcx: 'a> { /// Type parameters for const fn and associated constants. substs: &'tcx Substs<'tcx>, - /// Arguments passed to a const fn. - args: IndexVec>, - - /// Variable values - specifically, argument bindings of a const fn. - vars: IndexVec>>, - - /// Temp values. - temps: IndexVec>>, - - /// Value assigned to Return, which is the resulting constant. - return_value: Option> + /// Values of locals in a constant or const fn. + locals: IndexVec>> } @@ -223,15 +214,17 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { substs: &'tcx Substs<'tcx>, args: IndexVec>) -> MirConstContext<'a, 'tcx> { - MirConstContext { + let mut context = MirConstContext { ccx: ccx, mir: mir, substs: substs, - args: args, - vars: IndexVec::from_elem(None, &mir.var_decls), - temps: IndexVec::from_elem(None, &mir.temp_decls), - return_value: None + locals: (0..mir.count_locals()).map(|_| None).collect(), + }; + for (i, arg) in args.into_iter().enumerate() { + let index = mir.local_index(&mir::Lvalue::Arg(mir::Arg::new(i))).unwrap(); + context.locals[index] = Some(arg); } + context } fn trans_def(ccx: &'a CrateContext<'a, 'tcx>, @@ -302,9 +295,10 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { mir::TerminatorKind::Goto { target } => target, mir::TerminatorKind::Return => { failure?; - return Ok(self.return_value.unwrap_or_else(|| { + let index = self.mir.local_index(&mir::Lvalue::ReturnPointer).unwrap(); + return Ok(self.locals[index].unwrap_or_else(|| { span_bug!(span, "no returned value in constant"); - })) + })); } mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, .. } => { @@ -366,30 +360,28 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { } fn store(&mut self, dest: &mir::Lvalue<'tcx>, value: Const<'tcx>, span: Span) { - let dest = match *dest { - mir::Lvalue::Var(var) => &mut self.vars[var], - mir::Lvalue::Temp(temp) => &mut self.temps[temp], - mir::Lvalue::ReturnPointer => &mut self.return_value, - _ => span_bug!(span, "assignment to {:?} in constant", dest) - }; - *dest = Some(value); + if let Some(index) = self.mir.local_index(dest) { + self.locals[index] = Some(value); + } else { + span_bug!(span, "assignment to {:?} in constant", dest); + } } fn const_lvalue(&self, lvalue: &mir::Lvalue<'tcx>, span: Span) -> Result, ConstEvalFailure> { let tcx = self.ccx.tcx(); + + if let Some(index) = self.mir.local_index(lvalue) { + return Ok(self.locals[index].unwrap_or_else(|| { + span_bug!(span, "{:?} not initialized", lvalue) + }).as_lvalue()); + } + let lvalue = match *lvalue { - mir::Lvalue::Var(var) => { - self.vars[var].unwrap_or_else(|| { - span_bug!(span, "{:?} not initialized", var) - }).as_lvalue() - } - mir::Lvalue::Temp(temp) => { - self.temps[temp].unwrap_or_else(|| { - span_bug!(span, "{:?} not initialized", temp) - }).as_lvalue() - } - mir::Lvalue::Arg(arg) => self.args[arg].as_lvalue(), + mir::Lvalue::Var(_) | + mir::Lvalue::Temp(_) | + mir::Lvalue::Arg(_) | + mir::Lvalue::ReturnPointer => bug!(), // handled above mir::Lvalue::Static(def_id) => { ConstLvalue { base: Base::Static(consts::get_static(self.ccx, def_id).val), @@ -397,9 +389,6 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { ty: self.mir.lvalue_ty(tcx, lvalue).to_ty(tcx) } } - mir::Lvalue::ReturnPointer => { - span_bug!(span, "accessing Lvalue::ReturnPointer in constant") - } mir::Lvalue::Projection(ref projection) => { let tr_base = self.const_lvalue(&projection.base, span)?; let projected_ty = LvalueTy::Ty { ty: tr_base.ty } diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs index f7e159797479c..ceaba2a40ca55 100644 --- a/src/librustc_trans/mir/lvalue.rs +++ b/src/librustc_trans/mir/lvalue.rs @@ -26,7 +26,7 @@ use Disr; use std::ptr; -use super::{MirContext, TempRef}; +use super::{MirContext, LocalRef}; use super::operand::OperandValue; #[derive(Copy, Clone, Debug)] @@ -88,40 +88,30 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { -> LvalueRef<'tcx> { debug!("trans_lvalue(lvalue={:?})", lvalue); - let fcx = bcx.fcx(); let ccx = bcx.ccx(); let tcx = bcx.tcx(); + + if let Some(index) = self.mir.local_index(lvalue) { + match self.locals[index] { + LocalRef::Lvalue(lvalue) => { + return lvalue; + } + LocalRef::Operand(..) => { + bug!("using operand local {:?} as lvalue", lvalue); + } + } + } + let result = match *lvalue { - mir::Lvalue::Var(var) => self.vars[var], - mir::Lvalue::Temp(temp) => match self.temps[temp] { - TempRef::Lvalue(lvalue) => - lvalue, - TempRef::Operand(..) => - bug!("using operand temp {:?} as lvalue", lvalue), - }, - mir::Lvalue::Arg(arg) => self.args[arg], + mir::Lvalue::Var(_) | + mir::Lvalue::Temp(_) | + mir::Lvalue::Arg(_) | + mir::Lvalue::ReturnPointer => bug!(), // handled above mir::Lvalue::Static(def_id) => { let const_ty = self.lvalue_ty(lvalue); LvalueRef::new_sized(consts::get_static(ccx, def_id).val, LvalueTy::from_ty(const_ty)) }, - mir::Lvalue::ReturnPointer => { - let llval = if !fcx.fn_ty.ret.is_ignore() { - bcx.with_block(|bcx| { - fcx.get_ret_slot(bcx, "") - }) - } else { - // This is a void return; that is, there’s no place to store the value and - // there cannot really be one (or storing into it doesn’t make sense, anyway). - // Ergo, we return an undef ValueRef, so we do not have to special-case every - // place using lvalues, and could use it the same way you use a regular - // ReturnPointer LValue (i.e. store into it, load from it etc). - C_undef(fcx.fn_ty.ret.original_ty.ptr_to()) - }; - let fn_return_ty = bcx.monomorphize(&self.mir.return_ty); - let return_ty = fn_return_ty.unwrap(); - LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty)) - }, mir::Lvalue::Projection(box mir::Projection { ref base, elem: mir::ProjectionElem::Deref @@ -240,44 +230,41 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } // Perform an action using the given Lvalue. - // If the Lvalue is an empty TempRef::Operand, then a temporary stack slot + // If the Lvalue is an empty LocalRef::Operand, then a temporary stack slot // is created first, then used as an operand to update the Lvalue. pub fn with_lvalue_ref(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>, lvalue: &mir::Lvalue<'tcx>, f: F) -> U where F: FnOnce(&mut Self, LvalueRef<'tcx>) -> U { - match *lvalue { - mir::Lvalue::Temp(temp) => { - match self.temps[temp] { - TempRef::Lvalue(lvalue) => f(self, lvalue), - TempRef::Operand(None) => { - let lvalue_ty = self.lvalue_ty(lvalue); - let lvalue = LvalueRef::alloca(bcx, - lvalue_ty, - "lvalue_temp"); - let ret = f(self, lvalue); - let op = self.trans_load(bcx, lvalue.llval, lvalue_ty); - self.temps[temp] = TempRef::Operand(Some(op)); - ret - } - TempRef::Operand(Some(_)) => { - // See comments in TempRef::new_operand as to why - // we always have Some in a ZST TempRef::Operand. - let ty = self.lvalue_ty(lvalue); - if common::type_is_zero_size(bcx.ccx(), ty) { - // Pass an undef pointer as no stores can actually occur. - let llptr = C_undef(type_of(bcx.ccx(), ty).ptr_to()); - f(self, LvalueRef::new_sized(llptr, LvalueTy::from_ty(ty))) - } else { - bug!("Lvalue temp already set"); - } + if let Some(index) = self.mir.local_index(lvalue) { + match self.locals[index] { + LocalRef::Lvalue(lvalue) => f(self, lvalue), + LocalRef::Operand(None) => { + let lvalue_ty = self.lvalue_ty(lvalue); + let lvalue = LvalueRef::alloca(bcx, + lvalue_ty, + "lvalue_temp"); + let ret = f(self, lvalue); + let op = self.trans_load(bcx, lvalue.llval, lvalue_ty); + self.locals[index] = LocalRef::Operand(Some(op)); + ret + } + LocalRef::Operand(Some(_)) => { + // See comments in LocalRef::new_operand as to why + // we always have Some in a ZST LocalRef::Operand. + let ty = self.lvalue_ty(lvalue); + if common::type_is_zero_size(bcx.ccx(), ty) { + // Pass an undef pointer as no stores can actually occur. + let llptr = C_undef(type_of(bcx.ccx(), ty).ptr_to()); + f(self, LvalueRef::new_sized(llptr, LvalueTy::from_ty(ty))) + } else { + bug!("Lvalue local already set"); } } } - _ => { - let lvalue = self.trans_lvalue(bcx, lvalue); - f(self, lvalue) - } + } else { + let lvalue = self.trans_lvalue(bcx, lvalue); + f(self, lvalue) } } diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index 1932a2023fac8..0db5d3ae4d131 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -84,16 +84,13 @@ pub struct MirContext<'bcx, 'tcx:'bcx> { /// Cached unreachable block unreachable_block: Option>, - /// An LLVM alloca for each MIR `VarDecl` - vars: IndexVec>, - - /// The location where each MIR `TempDecl` is stored. This is + /// The location where each MIR arg/var/tmp/ret is stored. This is /// usually an `LvalueRef` representing an alloca, but not always: /// sometimes we can skip the alloca and just store the value /// directly using an `OperandRef`, which makes for tighter LLVM /// IR. The conditions for using an `OperandRef` are as follows: /// - /// - the type of the temporary must be judged "immediate" by `type_is_immediate` + /// - the type of the local must be judged "immediate" by `type_is_immediate` /// - the operand must never be referenced indirectly /// - we should not take its address using the `&` operator /// - nor should it appear in an lvalue path like `tmp.a` @@ -102,12 +99,7 @@ pub struct MirContext<'bcx, 'tcx:'bcx> { /// /// Avoiding allocs can also be important for certain intrinsics, /// notably `expect`. - temps: IndexVec>, - - /// The arguments to the function; as args are lvalues, these are - /// always indirect, though we try to avoid creating an alloca - /// when we can (and just reuse the pointer the caller provided). - args: IndexVec>, + locals: IndexVec>, /// Debug information for MIR scopes. scopes: IndexVec @@ -119,14 +111,14 @@ impl<'blk, 'tcx> MirContext<'blk, 'tcx> { } } -enum TempRef<'tcx> { +enum LocalRef<'tcx> { Lvalue(LvalueRef<'tcx>), Operand(Option>), } -impl<'tcx> TempRef<'tcx> { +impl<'tcx> LocalRef<'tcx> { fn new_operand<'bcx>(ccx: &CrateContext<'bcx, 'tcx>, - ty: ty::Ty<'tcx>) -> TempRef<'tcx> { + ty: ty::Ty<'tcx>) -> LocalRef<'tcx> { if common::type_is_zero_size(ccx, ty) { // Zero-size temporaries aren't always initialized, which // doesn't matter because they don't contain data, but @@ -142,9 +134,9 @@ impl<'tcx> TempRef<'tcx> { val: val, ty: ty }; - TempRef::Operand(Some(op)) + LocalRef::Operand(Some(op)) } else { - TempRef::Operand(None) + LocalRef::Operand(None) } } } @@ -157,8 +149,8 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { // Analyze the temps to determine which must be lvalues // FIXME - let (lvalue_temps, cleanup_kinds) = bcx.with_block(|bcx| { - (analyze::lvalue_temps(bcx, &mir), + let (lvalue_locals, cleanup_kinds) = bcx.with_block(|bcx| { + (analyze::lvalue_locals(bcx, &mir), analyze::cleanup_kinds(bcx, &mir)) }); @@ -166,37 +158,49 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { let scopes = debuginfo::create_mir_scopes(fcx); // Allocate variable and temp allocas - let args = arg_value_refs(&bcx, &mir, &scopes); - let vars = mir.var_decls.iter() - .map(|decl| (bcx.monomorphize(&decl.ty), decl)) - .map(|(mty, decl)| { - let lvalue = LvalueRef::alloca(&bcx, mty, &decl.name.as_str()); - - let scope = scopes[decl.source_info.scope]; - if !scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo { - bcx.with_block(|bcx| { - declare_local(bcx, decl.name, mty, scope, - VariableAccess::DirectVariable { alloca: lvalue.llval }, - VariableKind::LocalVariable, decl.source_info.span); - }); - } + let locals = { + let args = arg_local_refs(&bcx, &mir, &scopes, &lvalue_locals); + let vars = mir.var_decls.iter().enumerate().map(|(i, decl)| { + let ty = bcx.monomorphize(&decl.ty); + let scope = scopes[decl.source_info.scope]; + let dbg = !scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo; + + let local = mir.local_index(&mir::Lvalue::Var(mir::Var::new(i))).unwrap(); + if !lvalue_locals.contains(local.index()) && !dbg { + return LocalRef::new_operand(bcx.ccx(), ty); + } - lvalue - }).collect(); - let temps = mir.temp_decls.iter() - .map(|decl| bcx.monomorphize(&decl.ty)) - .enumerate() - .map(|(i, mty)| if lvalue_temps.contains(i) { - TempRef::Lvalue(LvalueRef::alloca(&bcx, - mty, - &format!("temp{:?}", i))) - } else { - // If this is an immediate temp, we do not create an - // alloca in advance. Instead we wait until we see the - // definition and update the operand there. - TempRef::new_operand(bcx.ccx(), mty) - }) - .collect(); + let lvalue = LvalueRef::alloca(&bcx, ty, &decl.name.as_str()); + if dbg { + bcx.with_block(|bcx| { + declare_local(bcx, decl.name, ty, scope, + VariableAccess::DirectVariable { alloca: lvalue.llval }, + VariableKind::LocalVariable, decl.source_info.span); + }); + } + LocalRef::Lvalue(lvalue) + }); + + let locals = mir.temp_decls.iter().enumerate().map(|(i, decl)| { + (mir::Lvalue::Temp(mir::Temp::new(i)), decl.ty) + }).chain(mir.return_ty.maybe_converging().map(|ty| (mir::Lvalue::ReturnPointer, ty))); + + args.into_iter().chain(vars).chain(locals.map(|(lvalue, ty)| { + let ty = bcx.monomorphize(&ty); + let local = mir.local_index(&lvalue).unwrap(); + if lvalue == mir::Lvalue::ReturnPointer && fcx.fn_ty.ret.is_indirect() { + let llretptr = llvm::get_param(fcx.llfn, 0); + LocalRef::Lvalue(LvalueRef::new_sized(llretptr, LvalueTy::from_ty(ty))) + } else if lvalue_locals.contains(local.index()) { + LocalRef::Lvalue(LvalueRef::alloca(&bcx, ty, &format!("{:?}", lvalue))) + } else { + // If this is an immediate local, we do not create an + // alloca in advance. Instead we wait until we see the + // definition and update the operand there. + LocalRef::new_operand(bcx.ccx(), ty) + } + })).collect() + }; // Allocate a `Block` for every basic block let block_bcxs: IndexVec> = @@ -225,9 +229,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { unreachable_block: None, cleanup_kinds: cleanup_kinds, landing_pads: IndexVec::from_elem(None, mir.basic_blocks()), - vars: vars, - temps: temps, - args: args, + locals: locals, scopes: scopes }; @@ -266,10 +268,11 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { /// Produce, for each argument, a `ValueRef` pointing at the /// argument's value. As arguments are lvalues, these are always /// indirect. -fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, +fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, mir: &mir::Mir<'tcx>, - scopes: &IndexVec) - -> IndexVec> { + scopes: &IndexVec, + lvalue_locals: &BitVector) + -> Vec> { let fcx = bcx.fcx(); let tcx = bcx.tcx(); let mut idx = 0; @@ -285,6 +288,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, mir.arg_decls.iter().enumerate().map(|(arg_index, arg_decl)| { let arg_ty = bcx.monomorphize(&arg_decl.ty); + let local = mir.local_index(&mir::Lvalue::Arg(mir::Arg::new(arg_index))).unwrap(); if arg_decl.spread { // This argument (e.g. the last argument in the "rust-call" ABI) // is a tuple that was spread at the ABI level and now we have @@ -305,8 +309,8 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, let arg = &fcx.fn_ty.args[idx]; idx += 1; if common::type_is_fat_ptr(tcx, tupled_arg_ty) { - // We pass fat pointers as two words, but inside the tuple - // they are the two sub-fields of a single aggregate field. + // We pass fat pointers as two words, but inside the tuple + // they are the two sub-fields of a single aggregate field. let meta = &fcx.fn_ty.args[idx]; idx += 1; arg.store_fn_arg(bcx, &mut llarg_idx, get_dataptr(bcx, dst)); @@ -335,7 +339,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx.fcx().span.unwrap_or(DUMMY_SP)); })); } - return LvalueRef::new_sized(lltemp, LvalueTy::from_ty(arg_ty)); + return LocalRef::Lvalue(LvalueRef::new_sized(lltemp, LvalueTy::from_ty(arg_ty))); } let arg = &fcx.fn_ty.args[idx]; @@ -345,9 +349,42 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, // already put it in a temporary alloca and gave it up, unless // we emit extra-debug-info, which requires local allocas :(. // FIXME: lifetimes + if arg.pad.is_some() { + llarg_idx += 1; + } let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint); llarg_idx += 1; llarg + } else if !lvalue_locals.contains(local.index()) && + !arg.is_indirect() && arg.cast.is_none() && + arg_scope.is_none() { + if arg.is_ignore() { + return LocalRef::new_operand(bcx.ccx(), arg_ty); + } + + // We don't have to cast or keep the argument in the alloca. + // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead + // of putting everything in allocas just so we can use llvm.dbg.declare. + if arg.pad.is_some() { + llarg_idx += 1; + } + let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint); + llarg_idx += 1; + let val = if common::type_is_fat_ptr(tcx, arg_ty) { + let meta = &fcx.fn_ty.args[idx]; + idx += 1; + assert_eq!((meta.cast, meta.pad), (None, None)); + let llmeta = llvm::get_param(fcx.llfn, llarg_idx as c_uint); + llarg_idx += 1; + OperandValue::Pair(llarg, llmeta) + } else { + OperandValue::Immediate(llarg) + }; + let operand = OperandRef { + val: val, + ty: arg_ty + }; + return LocalRef::Operand(Some(operand.unpack_if_pair(bcx))); } else { let lltemp = bcx.with_block(|bcx| { base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)) @@ -441,7 +478,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx.fcx().span.unwrap_or(DUMMY_SP)); } })); - LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty)) + LocalRef::Lvalue(LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty))) }).collect() } diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index 980db76d632c1..446ac91b1f580 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -21,7 +21,7 @@ use type_::Type; use std::fmt; -use super::{MirContext, TempRef}; +use super::{MirContext, LocalRef}; /// The representation of a Rust value. The enum variant is in fact /// uniquely determined by the value's type, but is kept as a @@ -112,6 +112,8 @@ impl<'bcx, 'tcx> OperandRef<'tcx> { if let OperandValue::Immediate(llval) = self.val { // Deconstruct the immediate aggregate. if common::type_is_imm_pair(bcx.ccx(), self.ty) { + debug!("Operand::unpack_if_pair: unpacking {:?}", self); + let mut a = bcx.extract_value(llval, 0); let mut b = bcx.extract_value(llval, 1); @@ -171,17 +173,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { { debug!("trans_consume(lvalue={:?})", lvalue); - // watch out for temporaries that do not have an + // watch out for locals that do not have an // alloca; they are handled somewhat differently - if let &mir::Lvalue::Temp(index) = lvalue { - match self.temps[index] { - TempRef::Operand(Some(o)) => { + if let Some(index) = self.mir.local_index(lvalue) { + match self.locals[index] { + LocalRef::Operand(Some(o)) => { return o; } - TempRef::Operand(None) => { + LocalRef::Operand(None) => { bug!("use of {:?} before def", lvalue); } - TempRef::Lvalue(..) => { + LocalRef::Lvalue(..) => { // use path below } } @@ -189,9 +191,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // Moves out of pair fields are trivial. if let &mir::Lvalue::Projection(ref proj) = lvalue { - if let mir::Lvalue::Temp(index) = proj.base { - let temp_ref = &self.temps[index]; - if let &TempRef::Operand(Some(o)) = temp_ref { + if let Some(index) = self.mir.local_index(&proj.base) { + if let LocalRef::Operand(Some(o)) = self.locals[index] { match (o.val, &proj.elem) { (OperandValue::Pair(a, b), &mir::ProjectionElem::Field(ref f, ty)) => { diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs index d592f5ee1b936..55efa75b17336 100644 --- a/src/librustc_trans/mir/statement.rs +++ b/src/librustc_trans/mir/statement.rs @@ -13,7 +13,7 @@ use rustc::mir::repr as mir; use common::{self, BlockAndBuilder}; use super::MirContext; -use super::TempRef; +use super::LocalRef; impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { pub fn trans_statement(&mut self, @@ -27,37 +27,34 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { debug_loc.apply(bcx.fcx()); match statement.kind { mir::StatementKind::Assign(ref lvalue, ref rvalue) => { - match *lvalue { - mir::Lvalue::Temp(index) => { - match self.temps[index] { - TempRef::Lvalue(tr_dest) => { - self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc) - } - TempRef::Operand(None) => { - let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue, - debug_loc); - self.temps[index] = TempRef::Operand(Some(operand)); - bcx - } - TempRef::Operand(Some(_)) => { - let ty = self.lvalue_ty(lvalue); + if let Some(index) = self.mir.local_index(lvalue) { + match self.locals[index] { + LocalRef::Lvalue(tr_dest) => { + self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc) + } + LocalRef::Operand(None) => { + let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue, + debug_loc); + self.locals[index] = LocalRef::Operand(Some(operand)); + bcx + } + LocalRef::Operand(Some(_)) => { + let ty = self.lvalue_ty(lvalue); - if !common::type_is_zero_size(bcx.ccx(), ty) { - span_bug!(statement.source_info.span, - "operand {:?} already assigned", - rvalue); - } else { - // If the type is zero-sized, it's already been set here, - // but we still need to make sure we translate the operand - self.trans_rvalue_operand(bcx, rvalue, debug_loc).0 - } + if !common::type_is_zero_size(bcx.ccx(), ty) { + span_bug!(statement.source_info.span, + "operand {:?} already assigned", + rvalue); + } else { + // If the type is zero-sized, it's already been set here, + // but we still need to make sure we translate the operand + self.trans_rvalue_operand(bcx, rvalue, debug_loc).0 } } } - _ => { - let tr_dest = self.trans_lvalue(&bcx, lvalue); - self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc) - } + } else { + let tr_dest = self.trans_lvalue(&bcx, lvalue); + self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc) } } } diff --git a/src/test/codegen/loads.rs b/src/test/codegen/loads.rs index 21f23b6ea1861..a65a3e1bb66fe 100644 --- a/src/test/codegen/loads.rs +++ b/src/test/codegen/loads.rs @@ -11,6 +11,7 @@ // compile-flags: -C no-prepopulate-passes #![crate_type = "lib"] +#![feature(rustc_attrs)] pub struct Bytes { a: u8, @@ -21,6 +22,7 @@ pub struct Bytes { // CHECK-LABEL: @borrow #[no_mangle] +#[rustc_no_mir] // FIXME #27840 MIR has different codegen. pub fn borrow(x: &i32) -> &i32 { // CHECK: load {{(i32\*, )?}}i32** %x{{.*}}, !nonnull x @@ -28,6 +30,7 @@ pub fn borrow(x: &i32) -> &i32 { // CHECK-LABEL: @_box #[no_mangle] +#[rustc_no_mir] // FIXME #27840 MIR has different codegen. pub fn _box(x: Box) -> i32 { // CHECK: load {{(i32\*, )?}}i32** %x{{.*}}, !nonnull *x diff --git a/src/test/codegen/naked-functions.rs b/src/test/codegen/naked-functions.rs index 0a600f4acad1f..199f7f0201877 100644 --- a/src/test/codegen/naked-functions.rs +++ b/src/test/codegen/naked-functions.rs @@ -13,7 +13,7 @@ // compile-flags: -C no-prepopulate-passes #![crate_type = "lib"] -#![feature(naked_functions)] +#![feature(naked_functions, rustc_attrs)] // CHECK: Function Attrs: naked uwtable // CHECK-NEXT: define internal void @naked_empty() @@ -26,6 +26,7 @@ fn naked_empty() { // CHECK: Function Attrs: naked uwtable #[no_mangle] #[naked] +#[rustc_no_mir] // FIXME #27840 MIR has different codegen. // CHECK-NEXT: define internal void @naked_with_args(i{{[0-9]+}}) fn naked_with_args(a: isize) { // CHECK: %a = alloca i{{[0-9]+}} @@ -45,6 +46,7 @@ fn naked_with_return() -> isize { // CHECK-NEXT: define internal i{{[0-9]+}} @naked_with_args_and_return(i{{[0-9]+}}) #[no_mangle] #[naked] +#[rustc_no_mir] // FIXME #27840 MIR has different codegen. fn naked_with_args_and_return(a: isize) -> isize { // CHECK: %a = alloca i{{[0-9]+}} // CHECK: ret i{{[0-9]+}} %{{[0-9]+}} diff --git a/src/test/compile-fail/issue-26548.rs b/src/test/compile-fail/issue-26548.rs index 2919b0b3caca6..b6d5e5458ff08 100644 --- a/src/test/compile-fail/issue-26548.rs +++ b/src/test/compile-fail/issue-26548.rs @@ -10,10 +10,13 @@ // error-pattern: overflow representing the type `S` +#![feature(rustc_attrs)] + trait Mirror { type It: ?Sized; } impl Mirror for T { type It = Self; } struct S(Option<::It>); +#[rustc_no_mir] // FIXME #27840 MIR tries to represent `std::option::Option` first. fn main() { let _s = S(None); } From ebfdd110c3ae32e91627f1d75bf089560bf98c9b Mon Sep 17 00:00:00 2001 From: Oliver Middleton Date: Sat, 18 Jun 2016 18:41:13 +0100 Subject: [PATCH 17/24] rustdoc: Fix a couple of issues with src links to external crates - src links/redirects to extern fn from another crate had an extra '/'. - src links to `pub use` of a crate module had an extra '/'. - src links to renamed reexports from another crate used the new name for the link but should use the original name. --- src/librustdoc/clean/inline.rs | 10 +++++++-- src/librustdoc/html/render.rs | 15 ++++++++----- src/test/rustdoc/auxiliary/issue-34274.rs | 13 +++++++++++ .../rustdoc/auxiliary/src-links-external.rs | 11 ++++++++++ src/test/rustdoc/issue-34274.rs | 20 +++++++++++++++++ src/test/rustdoc/src-links-external.rs | 22 +++++++++++++++++++ 6 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 src/test/rustdoc/auxiliary/issue-34274.rs create mode 100644 src/test/rustdoc/auxiliary/src-links-external.rs create mode 100644 src/test/rustdoc/issue-34274.rs create mode 100644 src/test/rustdoc/src-links-external.rs diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index e49b96cbfd02e..8ffbd6be41878 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -143,8 +143,14 @@ pub fn load_attrs<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, pub fn record_extern_fqn(cx: &DocContext, did: DefId, kind: clean::TypeKind) { if let Some(tcx) = cx.tcx_opt() { let crate_name = tcx.sess.cstore.crate_name(did.krate).to_string(); - let relative = tcx.def_path(did).data.into_iter().map(|elem| { - elem.data.to_string() + let relative = tcx.def_path(did).data.into_iter().filter_map(|elem| { + // extern blocks have an empty name + let s = elem.data.to_string(); + if !s.is_empty() { + Some(s) + } else { + None + } }); let fqn = once(crate_name).chain(relative).collect(); cx.renderinfo.borrow_mut().external_paths.insert(did, (fqn, kind)); diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 493d3d6abc9f9..0d390a87d2050 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1519,20 +1519,23 @@ impl<'a> Item<'a> { // located, then we return `None`. } else { let cache = cache(); - let path = match cache.external_paths.get(&self.item.def_id) { + let external_path = match cache.external_paths.get(&self.item.def_id) { Some(path) => path, None => return None, }; - let root = match cache.extern_locations.get(&self.item.def_id.krate) { + let mut path = match cache.extern_locations.get(&self.item.def_id.krate) { Some(&(_, Remote(ref s))) => s.to_string(), Some(&(_, Local)) => self.cx.root_path.clone(), Some(&(_, Unknown)) => return None, None => return None, }; - Some(format!("{root}{path}/{file}?gotosrc={goto}", - root = root, - path = path[..path.len() - 1].join("/"), - file = item_path(shortty(self.item), self.item.name.as_ref().unwrap()), + for item in &external_path[..external_path.len() - 1] { + path.push_str(item); + path.push_str("/"); + } + Some(format!("{path}{file}?gotosrc={goto}", + path = path, + file = item_path(shortty(self.item), external_path.last().unwrap()), goto = self.item.def_id.index.as_usize())) } } diff --git a/src/test/rustdoc/auxiliary/issue-34274.rs b/src/test/rustdoc/auxiliary/issue-34274.rs new file mode 100644 index 0000000000000..72026b6085604 --- /dev/null +++ b/src/test/rustdoc/auxiliary/issue-34274.rs @@ -0,0 +1,13 @@ +// Copyright 2016 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. + +extern { + pub fn extern_c_fn(); +} diff --git a/src/test/rustdoc/auxiliary/src-links-external.rs b/src/test/rustdoc/auxiliary/src-links-external.rs new file mode 100644 index 0000000000000..94b7278e9904b --- /dev/null +++ b/src/test/rustdoc/auxiliary/src-links-external.rs @@ -0,0 +1,11 @@ +// Copyright 2016 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. + +pub struct Foo; diff --git a/src/test/rustdoc/issue-34274.rs b/src/test/rustdoc/issue-34274.rs new file mode 100644 index 0000000000000..971c89b1619ed --- /dev/null +++ b/src/test/rustdoc/issue-34274.rs @@ -0,0 +1,20 @@ +// Copyright 2016 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. + +// aux-build:issue-34274.rs +// build-aux-docs +// ignore-cross-compile + +#![crate_name = "foo"] + +extern crate issue_34274; + +// @has foo/fn.extern_c_fn.html '//a/@href' '../issue_34274/fn.extern_c_fn.html?gotosrc=' +pub use issue_34274::extern_c_fn; diff --git a/src/test/rustdoc/src-links-external.rs b/src/test/rustdoc/src-links-external.rs new file mode 100644 index 0000000000000..e9db4f519ed97 --- /dev/null +++ b/src/test/rustdoc/src-links-external.rs @@ -0,0 +1,22 @@ +// Copyright 2016 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. + +// aux-build:src-links-external.rs +// build-aux-docs +// ignore-cross-compile + +#![crate_name = "foo"] + +extern crate src_links_external; + +// @has foo/bar/index.html '//a/@href' '../src_links_external/index.html?gotosrc=' +pub use src_links_external as bar; + +// @has foo/bar/struct.Foo.html '//a/@href' '../src_links_external/struct.Foo.html?gotosrc=' From bd477503e277370bf1abc88bf51fae5f70dc9a35 Mon Sep 17 00:00:00 2001 From: Paul Jarrett Date: Mon, 20 Jun 2016 22:11:45 -0400 Subject: [PATCH 18/24] Improves organization of driver includes. --- src/librustc_driver/driver.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index e31a1f1624fe1..927953b034ba2 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -12,6 +12,7 @@ use rustc::dep_graph::DepGraph; use rustc::hir; use rustc::hir::{map as hir_map, FreevarMap, TraitMap}; use rustc::hir::def::DefMap; +use rustc::hir::lowering::lower_crate; use rustc_mir as mir; use rustc::mir::mir_map::MirMap; use rustc::session::{Session, CompileResult, compile_result_from_err_count}; @@ -30,14 +31,12 @@ use rustc_resolve as resolve; use rustc_metadata::macro_import; use rustc_metadata::creader::read_local_crates; use rustc_metadata::cstore::CStore; -use rustc_trans::back::link; -use rustc_trans::back::write; +use rustc_trans::back::{link, write}; use rustc_trans as trans; use rustc_typeck as typeck; use rustc_privacy; use rustc_plugin::registry::Registry; use rustc_plugin as plugin; -use rustc::hir::lowering::lower_crate; use rustc_passes::{ast_validation, no_asm, loops, consts, rvalues, static_recursion}; use rustc_const_eval::check_match; use super::Compilation; From 45a63d3ff611b1412f9d811cd328b648bada5ca2 Mon Sep 17 00:00:00 2001 From: Mitsunori Komatsu Date: Tue, 31 May 2016 01:53:14 +0900 Subject: [PATCH 19/24] Add message argument to `assert_eq` macro --- src/libcore/macros.rs | 13 ++++++++++++- src/test/run-pass/assert-eq-macro-success.rs | 3 +++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index a40608b0762d6..376d2792c445c 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -94,7 +94,18 @@ macro_rules! assert_eq { } } } - }) + }); + ($left:expr , $right:expr, $($arg:tt)*) => ({ + match (&($left), &($right)) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + panic!("assertion failed: `(left == right)` \ + (left: `{:?}`, right: `{:?}`): {}", left_val, right_val, + format_args!($($arg)*)) + } + } + } + }); } /// Ensure that a boolean expression is `true` at runtime. diff --git a/src/test/run-pass/assert-eq-macro-success.rs b/src/test/run-pass/assert-eq-macro-success.rs index 9662e1ff33d11..3110e22d5da13 100644 --- a/src/test/run-pass/assert-eq-macro-success.rs +++ b/src/test/run-pass/assert-eq-macro-success.rs @@ -16,4 +16,7 @@ pub fn main() { assert_eq!("abc".to_string(),"abc".to_string()); assert_eq!(Box::new(Point{x:34}),Box::new(Point{x:34})); assert_eq!(&Point{x:34},&Point{x:34}); + assert_eq!(42, 42, "foo bar"); + assert_eq!(42, 42, "a {} c", "b"); + assert_eq!(42, 42, "{x}, {y}, {z}", x = 1, y = 2, z = 3); } From 0de72bba3634efb9d7b637b274c8f2ec1a335bf4 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 21 Jun 2016 10:08:31 +0200 Subject: [PATCH 20/24] don't warn on casting byte strs to slices --- src/librustc_const_eval/eval.rs | 1 + src/test/run-pass/const-byte-str-cast.rs | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 src/test/run-pass/const-byte-str-cast.rs diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 7551bc5c234ef..34dce44004823 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -1116,6 +1116,7 @@ fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstVal, ty: ty::Ty) ty::TyRawPtr(_) => { Err(ErrKind::UnimplementedConstVal("casting a bytestr to a raw ptr")) }, + ty::TyRef(..) => Err(ErrKind::UnimplementedConstVal("casting a bytestr to slice")), _ => Err(CannotCast), }, _ => Err(CannotCast), diff --git a/src/test/run-pass/const-byte-str-cast.rs b/src/test/run-pass/const-byte-str-cast.rs new file mode 100644 index 0000000000000..2f265b9112b98 --- /dev/null +++ b/src/test/run-pass/const-byte-str-cast.rs @@ -0,0 +1,15 @@ +// 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. + +#[deny(warnings)] + +pub fn main() { + let _ = b"x" as &[u8]; +} From c02414e9bd4326d3db8d54a1cf4707089737b414 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 19 Jun 2016 15:58:40 +0200 Subject: [PATCH 21/24] Fix overflow error in thread::sleep --- src/libstd/sys/unix/thread.rs | 21 +++++++++++++++------ src/test/run-pass/sleep.rs | 25 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 src/test/run-pass/sleep.rs diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index cb34d1a5fbcd1..371319a93d2f2 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -125,16 +125,25 @@ impl Thread { } pub fn sleep(dur: Duration) { - let mut ts = libc::timespec { - tv_sec: dur.as_secs() as libc::time_t, - tv_nsec: dur.subsec_nanos() as libc::c_long, - }; + let mut secs = dur.as_secs(); + let mut nsecs = dur.subsec_nanos() as libc::c_long; // If we're awoken with a signal then the return value will be -1 and // nanosleep will fill in `ts` with the remaining time. unsafe { - while libc::nanosleep(&ts, &mut ts) == -1 { - assert_eq!(os::errno(), libc::EINTR); + while secs > 0 || nsecs > 0 { + let mut ts = libc::timespec { + tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t, + tv_nsec: nsecs, + }; + secs -= ts.tv_sec as u64; + if libc::nanosleep(&ts, &mut ts) == -1 { + assert_eq!(os::errno(), libc::EINTR); + secs += ts.tv_sec as u64; + nsecs = ts.tv_nsec; + } else { + nsecs = 0; + } } } } diff --git a/src/test/run-pass/sleep.rs b/src/test/run-pass/sleep.rs new file mode 100644 index 0000000000000..8b06b02f3cba5 --- /dev/null +++ b/src/test/run-pass/sleep.rs @@ -0,0 +1,25 @@ +// Copyright 2016 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 std::thread::{self, sleep}; +use std::time::Duration; +use std::sync::{Arc, Mutex}; +use std::u64; + +fn main() { + let finished = Arc::new(Mutex::new(false)); + let t_finished = finished.clone(); + thread::spawn(move || { + sleep(Duration::new(u64::MAX, 0)); + *t_finished.lock().unwrap() = true; + }); + sleep(Duration::from_millis(100)); + assert_eq!(*finished.lock().unwrap(), false); +} From 2a34a7b83914f3ff7d2d52827b4c92bffefb5f92 Mon Sep 17 00:00:00 2001 From: Nathan Moos Date: Mon, 23 May 2016 16:59:11 -0700 Subject: [PATCH 22/24] implemented peek_mut and unit tests --- src/libcollections/binary_heap.rs | 68 +++++++++++++++++++++++++++ src/libcollectionstest/binary_heap.rs | 18 +++++++ src/libcollectionstest/lib.rs | 1 + 3 files changed, 87 insertions(+) diff --git a/src/libcollections/binary_heap.rs b/src/libcollections/binary_heap.rs index 43c6e6e81209d..140801737bc4c 100644 --- a/src/libcollections/binary_heap.rs +++ b/src/libcollections/binary_heap.rs @@ -151,6 +151,7 @@ #![allow(missing_docs)] #![stable(feature = "rust1", since = "1.0.0")] +use core::ops::{Drop, Deref, DerefMut}; use core::iter::FromIterator; use core::mem::swap; use core::mem::size_of; @@ -218,6 +219,37 @@ pub struct BinaryHeap { data: Vec, } +/// A container object that represents the result of the [`peek_mut()`] method +/// on `BinaryHeap`. See its documentation for details. +/// +/// [`peek_mut()`]: struct.BinaryHeap.html#method.peek_mut +#[unstable(feature = "binary_heap_peek_mut", issue = "34392")] +pub struct PeekMut<'a, T: 'a + Ord> { + heap: &'a mut BinaryHeap +} + +#[unstable(feature = "binary_heap_peek_mut", issue = "34392")] +impl<'a, T: Ord> Drop for PeekMut<'a, T> { + fn drop(&mut self) { + self.heap.sift_down(0); + } +} + +#[unstable(feature = "binary_heap_peek_mut", issue = "34392")] +impl<'a, T: Ord> Deref for PeekMut<'a, T> { + type Target = T; + fn deref(&self) -> &T { + &self.heap.data[0] + } +} + +#[unstable(feature = "binary_heap_peek_mut", issue = "34392")] +impl<'a, T: Ord> DerefMut for PeekMut<'a, T> { + fn deref_mut(&mut self) -> &mut T { + &mut self.heap.data[0] + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Clone for BinaryHeap { fn clone(&self) -> Self { @@ -323,6 +355,42 @@ impl BinaryHeap { self.data.get(0) } + /// Returns a mutable reference to the greatest item in the binary heap, or + /// `None` if it is empty. + /// + /// Note: If the `PeekMut` value is leaked, the heap may be in an + /// inconsistent state. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(binary_heap_peek_mut)] + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// assert!(heap.peek_mut().is_none()); + /// + /// heap.push(1); + /// heap.push(5); + /// heap.push(2); + /// { + /// let mut val = heap.peek_mut().unwrap(); + /// *val = 0; + /// } + /// assert_eq!(heap.peek(), Some(&2)); + /// ``` + #[unstable(feature = "binary_heap_peek_mut", issue = "34392")] + pub fn peek_mut(&mut self) -> Option> { + if self.is_empty() { + None + } else { + Some(PeekMut { + heap: self + }) + } + } + /// Returns the number of elements the binary heap can hold without reallocating. /// /// # Examples diff --git a/src/libcollectionstest/binary_heap.rs b/src/libcollectionstest/binary_heap.rs index 58194fe75f79a..be933abe41fe2 100644 --- a/src/libcollectionstest/binary_heap.rs +++ b/src/libcollectionstest/binary_heap.rs @@ -81,6 +81,18 @@ fn test_peek_and_pop() { } } +#[test] +fn test_peek_mut() { + let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]; + let mut heap = BinaryHeap::from(data); + assert_eq!(heap.peek(), Some(&10)); + { + let mut top = heap.peek_mut().unwrap(); + *top -= 2; + } + assert_eq!(heap.peek(), Some(&9)); +} + #[test] fn test_push() { let mut heap = BinaryHeap::from(vec![2, 4, 9]); @@ -192,6 +204,12 @@ fn test_empty_peek() { assert!(empty.peek().is_none()); } +#[test] +fn test_empty_peek_mut() { + let mut empty = BinaryHeap::::new(); + assert!(empty.peek_mut().is_none()); +} + #[test] fn test_empty_replace() { let mut heap = BinaryHeap::new(); diff --git a/src/libcollectionstest/lib.rs b/src/libcollectionstest/lib.rs index 400d614094862..6161bad7468c6 100644 --- a/src/libcollectionstest/lib.rs +++ b/src/libcollectionstest/lib.rs @@ -12,6 +12,7 @@ #![feature(binary_heap_extras)] #![feature(binary_heap_append)] +#![feature(binary_heap_peek_mut)] #![feature(box_syntax)] #![feature(btree_append)] #![feature(btree_split_off)] From 1cc54d03270740d797c498cd29536565602dc2dd Mon Sep 17 00:00:00 2001 From: Oliver Middleton Date: Tue, 21 Jun 2016 23:30:15 +0100 Subject: [PATCH 23/24] Mark concat_idents! unstable This is mostly just a documentation fix as I don't think stability attributes have any effect on macros. --- src/libstd/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index d69789cedaf2c..26cf8a3199d1b 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -276,7 +276,7 @@ pub mod builtin { /// // fn concat_idents!(new, fun, name) { } // not usable in this way! /// # } /// ``` - #[stable(feature = "rust1", since = "1.0.0")] + #[unstable(feature = "concat_idents", issue = "29599")] #[macro_export] macro_rules! concat_idents { ($($e:ident),*) => ({ /* compiler built-in */ }) From b94b15852cd9b14160cce7f85f241691a72c18af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 20 May 2016 02:43:18 +0200 Subject: [PATCH 24/24] std: sync: Implement recv_timeout() --- src/libstd/sync/mpsc/blocking.rs | 14 +- src/libstd/sync/mpsc/mod.rs | 302 +++++++++++++++++++++++++++++-- src/libstd/sync/mpsc/oneshot.rs | 15 +- src/libstd/sync/mpsc/shared.rs | 17 +- src/libstd/sync/mpsc/stream.rs | 15 +- src/libstd/sync/mpsc/sync.rs | 75 ++++++-- 6 files changed, 396 insertions(+), 42 deletions(-) diff --git a/src/libstd/sync/mpsc/blocking.rs b/src/libstd/sync/mpsc/blocking.rs index 0e5a98591168b..4a70de0e7d8fc 100644 --- a/src/libstd/sync/mpsc/blocking.rs +++ b/src/libstd/sync/mpsc/blocking.rs @@ -16,6 +16,7 @@ use sync::Arc; use marker::{Sync, Send}; use mem; use clone::Clone; +use time::Instant; struct Inner { thread: Thread, @@ -74,7 +75,6 @@ impl SignalToken { pub unsafe fn cast_from_usize(signal_ptr: usize) -> SignalToken { SignalToken { inner: mem::transmute(signal_ptr) } } - } impl WaitToken { @@ -83,4 +83,16 @@ impl WaitToken { thread::park() } } + + /// Returns true if we wake up normally, false otherwise. + pub fn wait_max_until(self, end: Instant) -> bool { + while !self.inner.woken.load(Ordering::SeqCst) { + let now = Instant::now(); + if now >= end { + return false; + } + thread::park_timeout(end - now) + } + true + } } diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs index 63b659d8db3b7..34bc210b3c823 100644 --- a/src/libstd/sync/mpsc/mod.rs +++ b/src/libstd/sync/mpsc/mod.rs @@ -134,9 +134,9 @@ // senders. Under the hood, however, there are actually three flavors of // channels in play. // -// * Flavor::Oneshots - these channels are highly optimized for the one-send use case. -// They contain as few atomics as possible and involve one and -// exactly one allocation. +// * Flavor::Oneshots - these channels are highly optimized for the one-send use +// case. They contain as few atomics as possible and +// involve one and exactly one allocation. // * Streams - these channels are optimized for the non-shared use case. They // use a different concurrent queue that is more tailored for this // use case. The initial allocation of this flavor of channel is not @@ -148,9 +148,9 @@ // // ## Concurrent queues // -// The basic idea of Rust's Sender/Receiver types is that send() never blocks, but -// recv() obviously blocks. This means that under the hood there must be some -// shared and concurrent queue holding all of the actual data. +// The basic idea of Rust's Sender/Receiver types is that send() never blocks, +// but recv() obviously blocks. This means that under the hood there must be +// some shared and concurrent queue holding all of the actual data. // // With two flavors of channels, two flavors of queues are also used. We have // chosen to use queues from a well-known author that are abbreviated as SPSC @@ -271,6 +271,7 @@ use fmt; use mem; use cell::UnsafeCell; use marker::Reflect; +use time::{Duration, Instant}; #[unstable(feature = "mpsc_select", issue = "27800")] pub use self::select::{Select, Handle}; @@ -379,6 +380,19 @@ pub enum TryRecvError { Disconnected, } +/// This enumeration is the list of possible errors that `recv_timeout` could +/// not return data when called. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[unstable(feature = "mpsc_recv_timeout", issue = "34029")] +pub enum RecvTimeoutError { + /// This channel is currently empty, but the sender(s) have not yet + /// disconnected, so data may yet become available. + Timeout, + /// This channel's sending half has become disconnected, and there will + /// never be any more data received on this channel + Disconnected, +} + /// This enumeration is the list of the possible error outcomes for the /// `SyncSender::try_send` method. #[stable(feature = "rust1", since = "1.0.0")] @@ -838,30 +852,30 @@ impl Receiver { loop { let new_port = match *unsafe { self.inner() } { Flavor::Oneshot(ref p) => { - match unsafe { (*p.get()).recv() } { + match unsafe { (*p.get()).recv(None) } { Ok(t) => return Ok(t), - Err(oneshot::Empty) => return unreachable!(), Err(oneshot::Disconnected) => return Err(RecvError), Err(oneshot::Upgraded(rx)) => rx, + Err(oneshot::Empty) => unreachable!(), } } Flavor::Stream(ref p) => { - match unsafe { (*p.get()).recv() } { + match unsafe { (*p.get()).recv(None) } { Ok(t) => return Ok(t), - Err(stream::Empty) => return unreachable!(), Err(stream::Disconnected) => return Err(RecvError), Err(stream::Upgraded(rx)) => rx, + Err(stream::Empty) => unreachable!(), } } Flavor::Shared(ref p) => { - match unsafe { (*p.get()).recv() } { + match unsafe { (*p.get()).recv(None) } { Ok(t) => return Ok(t), - Err(shared::Empty) => return unreachable!(), Err(shared::Disconnected) => return Err(RecvError), + Err(shared::Empty) => unreachable!(), } } Flavor::Sync(ref p) => return unsafe { - (*p.get()).recv().map_err(|()| RecvError) + (*p.get()).recv(None).map_err(|_| RecvError) } }; unsafe { @@ -870,6 +884,98 @@ impl Receiver { } } + /// Attempts to wait for a value on this receiver, returning an error if the + /// corresponding channel has hung up, or if it waits more than `timeout`. + /// + /// This function will always block the current thread if there is no data + /// available and it's possible for more data to be sent. Once a message is + /// sent to the corresponding `Sender`, then this receiver will wake up and + /// return that message. + /// + /// If the corresponding `Sender` has disconnected, or it disconnects while + /// this call is blocking, this call will wake up and return `Err` to + /// indicate that no more messages can ever be received on this channel. + /// However, since channels are buffered, messages sent before the disconnect + /// will still be properly received. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(mpsc_recv_timeout)] + /// + /// use std::sync::mpsc::{self, RecvTimeoutError}; + /// use std::time::Duration; + /// + /// let (send, recv) = mpsc::channel::<()>(); + /// + /// let timeout = Duration::from_millis(100); + /// assert_eq!(Err(RecvTimeoutError::Timeout), recv.recv_timeout(timeout)); + /// ``` + #[unstable(feature = "mpsc_recv_timeout", issue = "34029")] + pub fn recv_timeout(&self, timeout: Duration) -> Result { + // Do an optimistic try_recv to avoid the performance impact of + // Instant::now() in the full-channel case. + match self.try_recv() { + Ok(result) + => Ok(result), + Err(TryRecvError::Disconnected) + => Err(RecvTimeoutError::Disconnected), + Err(TryRecvError::Empty) + => self.recv_max_until(Instant::now() + timeout) + } + } + + fn recv_max_until(&self, deadline: Instant) -> Result { + use self::RecvTimeoutError::*; + + loop { + let port_or_empty = match *unsafe { self.inner() } { + Flavor::Oneshot(ref p) => { + match unsafe { (*p.get()).recv(Some(deadline)) } { + Ok(t) => return Ok(t), + Err(oneshot::Disconnected) => return Err(Disconnected), + Err(oneshot::Upgraded(rx)) => Some(rx), + Err(oneshot::Empty) => None, + } + } + Flavor::Stream(ref p) => { + match unsafe { (*p.get()).recv(Some(deadline)) } { + Ok(t) => return Ok(t), + Err(stream::Disconnected) => return Err(Disconnected), + Err(stream::Upgraded(rx)) => Some(rx), + Err(stream::Empty) => None, + } + } + Flavor::Shared(ref p) => { + match unsafe { (*p.get()).recv(Some(deadline)) } { + Ok(t) => return Ok(t), + Err(shared::Disconnected) => return Err(Disconnected), + Err(shared::Empty) => None, + } + } + Flavor::Sync(ref p) => { + match unsafe { (*p.get()).recv(Some(deadline)) } { + Ok(t) => return Ok(t), + Err(sync::Disconnected) => return Err(Disconnected), + Err(sync::Empty) => None, + } + } + }; + + if let Some(new_port) = port_or_empty { + unsafe { + mem::swap(self.inner_mut(), new_port.inner_mut()); + } + } + + // If we're already passed the deadline, and we're here without + // data, return a timeout, else try again. + if Instant::now() >= deadline { + return Err(Timeout); + } + } + } + /// Returns an iterator that will block waiting for messages, but never /// `panic!`. It will return `None` when the channel has hung up. #[stable(feature = "rust1", since = "1.0.0")] @@ -1141,6 +1247,7 @@ mod tests { use env; use super::*; use thread; + use time::{Duration, Instant}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { @@ -1539,6 +1646,87 @@ mod tests { } } + #[test] + fn oneshot_single_thread_recv_timeout() { + let (tx, rx) = channel(); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); + } + + #[test] + fn stress_recv_timeout_two_threads() { + let (tx, rx) = channel(); + let stress = stress_factor() + 100; + let timeout = Duration::from_millis(100); + + thread::spawn(move || { + for i in 0..stress { + if i % 2 == 0 { + thread::sleep(timeout * 2); + } + tx.send(1usize).unwrap(); + } + }); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(timeout) { + Ok(n) => { + assert_eq!(n, 1usize); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, stress); + } + + #[test] + fn recv_timeout_upgrade() { + let (tx, rx) = channel::<()>(); + let timeout = Duration::from_millis(1); + let _tx_clone = tx.clone(); + + let start = Instant::now(); + assert_eq!(rx.recv_timeout(timeout), Err(RecvTimeoutError::Timeout)); + assert!(Instant::now() >= start + timeout); + } + + #[test] + fn stress_recv_timeout_shared() { + let (tx, rx) = channel(); + let stress = stress_factor() + 100; + + for i in 0..stress { + let tx = tx.clone(); + thread::spawn(move || { + thread::sleep(Duration::from_millis(i as u64 * 10)); + tx.send(1usize).unwrap(); + }); + } + + drop(tx); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(Duration::from_millis(10)) { + Ok(n) => { + assert_eq!(n, 1usize); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, stress); + } + #[test] fn recv_a_lot() { // Regression test that we don't run out of stack in scheduler context @@ -1547,6 +1735,24 @@ mod tests { for _ in 0..10000 { rx.recv().unwrap(); } } + #[test] + fn shared_recv_timeout() { + let (tx, rx) = channel(); + let total = 5; + for _ in 0..total { + let tx = tx.clone(); + thread::spawn(move|| { + tx.send(()).unwrap(); + }); + } + + for _ in 0..total { rx.recv().unwrap(); } + + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); + } + #[test] fn shared_chan_stress() { let (tx, rx) = channel(); @@ -1689,6 +1895,7 @@ mod sync_tests { use env; use thread; use super::*; + use time::Duration; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { @@ -1720,6 +1927,14 @@ mod sync_tests { assert_eq!(rx.recv().unwrap(), 1); } + #[test] + fn recv_timeout() { + let (tx, rx) = sync_channel::(1); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(1).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(1)); + } + #[test] fn smoke_threads() { let (tx, rx) = sync_channel::(0); @@ -1801,6 +2016,67 @@ mod sync_tests { } } + #[test] + fn stress_recv_timeout_two_threads() { + let (tx, rx) = sync_channel::(0); + + thread::spawn(move|| { + for _ in 0..10000 { tx.send(1).unwrap(); } + }); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(Duration::from_millis(1)) { + Ok(v) => { + assert_eq!(v, 1); + recv_count += 1; + }, + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, 10000); + } + + #[test] + fn stress_recv_timeout_shared() { + const AMT: u32 = 1000; + const NTHREADS: u32 = 8; + let (tx, rx) = sync_channel::(0); + let (dtx, drx) = sync_channel::<()>(0); + + thread::spawn(move|| { + let mut recv_count = 0; + loop { + match rx.recv_timeout(Duration::from_millis(10)) { + Ok(v) => { + assert_eq!(v, 1); + recv_count += 1; + }, + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, AMT * NTHREADS); + assert!(rx.try_recv().is_err()); + + dtx.send(()).unwrap(); + }); + + for _ in 0..NTHREADS { + let tx = tx.clone(); + thread::spawn(move|| { + for _ in 0..AMT { tx.send(1).unwrap(); } + }); + } + + drop(tx); + + drx.recv().unwrap(); + } + #[test] fn stress_shared() { const AMT: u32 = 1000; diff --git a/src/libstd/sync/mpsc/oneshot.rs b/src/libstd/sync/mpsc/oneshot.rs index cb930280964b3..7a35ea6bbaaa2 100644 --- a/src/libstd/sync/mpsc/oneshot.rs +++ b/src/libstd/sync/mpsc/oneshot.rs @@ -41,6 +41,7 @@ use sync::mpsc::Receiver; use sync::mpsc::blocking::{self, SignalToken}; use core::mem; use sync::atomic::{AtomicUsize, Ordering}; +use time::Instant; // Various states you can find a port in. const EMPTY: usize = 0; // initial state: no data, no blocked receiver @@ -136,7 +137,7 @@ impl Packet { } } - pub fn recv(&mut self) -> Result> { + pub fn recv(&mut self, deadline: Option) -> Result> { // Attempt to not block the thread (it's a little expensive). If it looks // like we're not empty, then immediately go through to `try_recv`. if self.state.load(Ordering::SeqCst) == EMPTY { @@ -145,8 +146,16 @@ impl Packet { // race with senders to enter the blocking state if self.state.compare_and_swap(EMPTY, ptr, Ordering::SeqCst) == EMPTY { - wait_token.wait(); - debug_assert!(self.state.load(Ordering::SeqCst) != EMPTY); + if let Some(deadline) = deadline { + let timed_out = !wait_token.wait_max_until(deadline); + // Try to reset the state + if timed_out { + try!(self.abort_selection().map_err(Upgraded)); + } + } else { + wait_token.wait(); + debug_assert!(self.state.load(Ordering::SeqCst) != EMPTY); + } } else { // drop the signal token, since we never blocked drop(unsafe { SignalToken::cast_from_usize(ptr) }); diff --git a/src/libstd/sync/mpsc/shared.rs b/src/libstd/sync/mpsc/shared.rs index a3779931c7bd2..baa4db7e5c0fa 100644 --- a/src/libstd/sync/mpsc/shared.rs +++ b/src/libstd/sync/mpsc/shared.rs @@ -30,6 +30,7 @@ use sync::mpsc::select::StartResult::*; use sync::mpsc::select::StartResult; use sync::{Mutex, MutexGuard}; use thread; +use time::Instant; const DISCONNECTED: isize = isize::MIN; const FUDGE: isize = 1024; @@ -66,7 +67,7 @@ impl Packet { // Creation of a packet *must* be followed by a call to postinit_lock // and later by inherit_blocker pub fn new() -> Packet { - let p = Packet { + Packet { queue: mpsc::Queue::new(), cnt: AtomicIsize::new(0), steals: 0, @@ -75,8 +76,7 @@ impl Packet { port_dropped: AtomicBool::new(false), sender_drain: AtomicIsize::new(0), select_lock: Mutex::new(()), - }; - return p; + } } // This function should be used after newly created Packet @@ -216,7 +216,7 @@ impl Packet { Ok(()) } - pub fn recv(&mut self) -> Result { + pub fn recv(&mut self, deadline: Option) -> Result { // This code is essentially the exact same as that found in the stream // case (see stream.rs) match self.try_recv() { @@ -226,7 +226,14 @@ impl Packet { let (wait_token, signal_token) = blocking::tokens(); if self.decrement(signal_token) == Installed { - wait_token.wait() + if let Some(deadline) = deadline { + let timed_out = !wait_token.wait_max_until(deadline); + if timed_out { + self.abort_selection(false); + } + } else { + wait_token.wait(); + } } match self.try_recv() { diff --git a/src/libstd/sync/mpsc/stream.rs b/src/libstd/sync/mpsc/stream.rs index e8012ca470b02..aa1254c8641f5 100644 --- a/src/libstd/sync/mpsc/stream.rs +++ b/src/libstd/sync/mpsc/stream.rs @@ -25,6 +25,7 @@ use self::Message::*; use core::cmp; use core::isize; use thread; +use time::Instant; use sync::atomic::{AtomicIsize, AtomicUsize, Ordering, AtomicBool}; use sync::mpsc::Receiver; @@ -172,7 +173,7 @@ impl Packet { Err(unsafe { SignalToken::cast_from_usize(ptr) }) } - pub fn recv(&mut self) -> Result> { + pub fn recv(&mut self, deadline: Option) -> Result> { // Optimistic preflight check (scheduling is expensive). match self.try_recv() { Err(Empty) => {} @@ -183,7 +184,15 @@ impl Packet { // initiate the blocking protocol. let (wait_token, signal_token) = blocking::tokens(); if self.decrement(signal_token).is_ok() { - wait_token.wait() + if let Some(deadline) = deadline { + let timed_out = !wait_token.wait_max_until(deadline); + if timed_out { + try!(self.abort_selection(/* was_upgrade = */ false) + .map_err(Upgraded)); + } + } else { + wait_token.wait(); + } } match self.try_recv() { @@ -332,7 +341,7 @@ impl Packet { // the internal state. match self.queue.peek() { Some(&mut GoUp(..)) => { - match self.recv() { + match self.recv(None) { Err(Upgraded(port)) => Err(port), _ => unreachable!(), } diff --git a/src/libstd/sync/mpsc/sync.rs b/src/libstd/sync/mpsc/sync.rs index b98fc2859afcc..f021689acad58 100644 --- a/src/libstd/sync/mpsc/sync.rs +++ b/src/libstd/sync/mpsc/sync.rs @@ -44,6 +44,7 @@ use sync::atomic::{Ordering, AtomicUsize}; use sync::mpsc::blocking::{self, WaitToken, SignalToken}; use sync::mpsc::select::StartResult::{self, Installed, Abort}; use sync::{Mutex, MutexGuard}; +use time::Instant; pub struct Packet { /// Only field outside of the mutex. Just done for kicks, but mainly because @@ -126,6 +127,38 @@ fn wait<'a, 'b, T>(lock: &'a Mutex>, lock.lock().unwrap() // relock } +/// Same as wait, but waiting at most until `deadline`. +fn wait_timeout_receiver<'a, 'b, T>(lock: &'a Mutex>, + deadline: Instant, + mut guard: MutexGuard<'b, State>, + success: &mut bool) + -> MutexGuard<'a, State> +{ + let (wait_token, signal_token) = blocking::tokens(); + match mem::replace(&mut guard.blocker, BlockedReceiver(signal_token)) { + NoneBlocked => {} + _ => unreachable!(), + } + drop(guard); // unlock + *success = wait_token.wait_max_until(deadline); // block + let mut new_guard = lock.lock().unwrap(); // relock + if !*success { + abort_selection(&mut new_guard); + } + new_guard +} + +fn abort_selection<'a, T>(guard: &mut MutexGuard<'a , State>) -> bool { + match mem::replace(&mut guard.blocker, NoneBlocked) { + NoneBlocked => true, + BlockedSender(token) => { + guard.blocker = BlockedSender(token); + true + } + BlockedReceiver(token) => { drop(token); false } + } +} + /// Wakes up a thread, dropping the lock at the correct time fn wakeup(token: SignalToken, guard: MutexGuard>) { // We need to be careful to wake up the waiting thread *outside* of the mutex @@ -238,22 +271,37 @@ impl Packet { // // When reading this, remember that there can only ever be one receiver at // time. - pub fn recv(&self) -> Result { + pub fn recv(&self, deadline: Option) -> Result { let mut guard = self.lock.lock().unwrap(); - // Wait for the buffer to have something in it. No need for a while loop - // because we're the only receiver. - let mut waited = false; + let mut woke_up_after_waiting = false; + // Wait for the buffer to have something in it. No need for a + // while loop because we're the only receiver. if !guard.disconnected && guard.buf.size() == 0 { - guard = wait(&self.lock, guard, BlockedReceiver); - waited = true; + if let Some(deadline) = deadline { + guard = wait_timeout_receiver(&self.lock, + deadline, + guard, + &mut woke_up_after_waiting); + } else { + guard = wait(&self.lock, guard, BlockedReceiver); + woke_up_after_waiting = true; + } + } + + // NB: Channel could be disconnected while waiting, so the order of + // these conditionals is important. + if guard.disconnected && guard.buf.size() == 0 { + return Err(Disconnected); } - if guard.disconnected && guard.buf.size() == 0 { return Err(()) } // Pick up the data, wake up our neighbors, and carry on - assert!(guard.buf.size() > 0); + assert!(guard.buf.size() > 0 || (deadline.is_some() && !woke_up_after_waiting)); + + if guard.buf.size() == 0 { return Err(Empty); } + let ret = guard.buf.dequeue(); - self.wakeup_senders(waited, guard); + self.wakeup_senders(woke_up_after_waiting, guard); Ok(ret) } @@ -392,14 +440,7 @@ impl Packet { // The return value indicates whether there's data on this port. pub fn abort_selection(&self) -> bool { let mut guard = self.lock.lock().unwrap(); - match mem::replace(&mut guard.blocker, NoneBlocked) { - NoneBlocked => true, - BlockedSender(token) => { - guard.blocker = BlockedSender(token); - true - } - BlockedReceiver(token) => { drop(token); false } - } + abort_selection(&mut guard) } }