From 6fed0132f314622700da9cd9f5746cfc4f4bb87f Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 2 May 2016 16:47:07 +0200 Subject: [PATCH 1/9] middle: reset loop labels while visiting closure This should fix #31754 and follow-up #25343. Before the latter, the closure was visited twice in the context of the enclosing fn, which made even a single closure with a loop label emit a warning. With this change, the closure is still visited within the context of the main fn (which is intended, since it is not a separate item) but resets the found loop labels while being visited. Fixes: #31754 --- src/librustc/middle/resolve_lifetime.rs | 7 ++++++- src/test/run-pass/issue-25343.rs | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 23eb5a56c8439..5c06231631010 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -193,7 +193,12 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> { }) } FnKind::Closure(_) => { - self.add_scope_and_walk_fn(fk, fd, b, s, fn_id) + // Closures have their own set of labels, save labels just + // like for foreign items above. + let saved = replace(&mut self.labels_in_fn, vec![]); + let result = self.add_scope_and_walk_fn(fk, fd, b, s, fn_id); + replace(&mut self.labels_in_fn, saved); + result } } } diff --git a/src/test/run-pass/issue-25343.rs b/src/test/run-pass/issue-25343.rs index 9e01d577276b8..64e7350fb8244 100644 --- a/src/test/run-pass/issue-25343.rs +++ b/src/test/run-pass/issue-25343.rs @@ -8,9 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[allow(unused)] fn main() { || { 'label: loop { } }; + + // More cases added from issue 31754 + + 'label2: loop { + break; + } + + let closure = || { + 'label2: loop {} + }; + + fn inner_fn() { + 'label2: loop {} + } } From 201d9ed0bbf05a8cc7015165a8f36bb87ef79a60 Mon Sep 17 00:00:00 2001 From: mrmiywj Date: Sun, 1 May 2016 00:33:39 +0800 Subject: [PATCH 2/9] add help on pattern guard fix too long column fix typo of help on pattern guard one nit fix compile fail --- src/librustc_const_eval/diagnostics.rs | 61 +++++++++++++++++++++----- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs index 4f5176f6b0be5..c86c22b1e0f3b 100644 --- a/src/librustc_const_eval/diagnostics.rs +++ b/src/librustc_const_eval/diagnostics.rs @@ -215,22 +215,63 @@ match Some("hi".to_string()) { The variable `s` has type `String`, and its use in the guard is as a variable of type `String`. The guard code effectively executes in a separate scope to the body of the arm, so the value would be moved into this anonymous scope and -therefore become unavailable in the body of the arm. Although this example seems -innocuous, the problem is most clear when considering functions that take their -argument by value. +therefore becomes unavailable in the body of the arm. -```compile_fail +The problem above can be solved by using the `ref` keyword. + +``` match Some("hi".to_string()) { - Some(s) if { drop(s); false } => (), - Some(s) => {}, // use s. + Some(ref s) if s.len() == 0 => {}, _ => {}, } ``` -The value would be dropped in the guard then become unavailable not only in the -body of that arm but also in all subsequent arms! The solution is to bind by -reference when using guards or refactor the entire expression, perhaps by -putting the condition inside the body of the arm. +Though this example seems innocuous and easy to solve, the problem becomes clear +when it encounters functions which consume the value: + +```compile_fail +struct A{} + +impl A { + fn consume(self) -> usize { + 0 + } +} + +fn main() { + let a = Some(A{}); + match a { + Some(y) if y.consume() > 0 => {} + _ => {} + } +} +``` + +In this situation, even the `ref` keyword cannot solve it, since borrowed +content cannot be moved. This problem cannot be solved generally. If the value +can be cloned, here is a not-so-specific solution: + +``` +#[derive(Clone)] +struct A{} + +impl A { + fn consume(self) -> usize { + 0 + } +} + +fn main() { + let a = Some(A{}); + match a{ + Some(ref y) if y.clone().consume() > 0 => {} + _ => {} + } +} +``` + +If the value will be consumed in the pattern guard, using its clone will not +move its ownership, so the code works. "##, E0009: r##" From 38a5338c63a37d09589b8cd44742b17a3913433d Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Tue, 3 May 2016 11:56:25 -0700 Subject: [PATCH 3/9] Add detailed error explanation for E0504 Removed unnecessary use of threads from E0504 Cleaned up line ending on E0504 Added more examples for E0504 Changed to erroneous code wording Switched Rc example to thread/Arc example Added comments describing why errors no longer occur --- src/librustc_borrowck/diagnostics.rs | 105 ++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/src/librustc_borrowck/diagnostics.rs b/src/librustc_borrowck/diagnostics.rs index 03fb5260c3992..1802ab43161fb 100644 --- a/src/librustc_borrowck/diagnostics.rs +++ b/src/librustc_borrowck/diagnostics.rs @@ -454,6 +454,110 @@ fn foo(a: &mut i32) { ``` "##, +E0504: r##" +This error occurs when an attempt is made to move a borrowed variable into a +closure. + +Example of erroneous code: + +```compile_fail +struct FancyNum { + num: u8 +} + +fn main() { + let fancy_num = FancyNum { num: 5 }; + let fancy_ref = &fancy_num; + + let x = move || { + println!("child function: {}", fancy_num.num); + // error: cannot move `fancy_num` into closure because it is borrowed + }; + + x(); + println!("main function: {}", fancy_ref.num); +} +``` + +Here, `fancy_num` is borrowed by `fancy_ref` and so cannot be moved into +the closure `x`. There is no way to move a value into a closure while it is +borrowed, as that would invalidate the borrow. + +If the closure can't outlive the value being moved, try using a reference +rather than moving: + +``` +struct FancyNum { + num: u8 +} + +fn main() { + let fancy_num = FancyNum { num: 5 }; + let fancy_ref = &fancy_num; + + let x = move || { + // fancy_ref is usable here because it doesn't move `fancy_num` + println!("child function: {}", fancy_ref.num); + }; + + x(); + + println!("main function: {}", fancy_num.num); +} +``` + +If the value has to be borrowed and then moved, try limiting the lifetime of +the borrow using a scoped block: + +``` +struct FancyNum { + num: u8 +} + +fn main() { + let fancy_num = FancyNum { num: 5 }; + + { + let fancy_ref = &fancy_num; + println!("main function: {}", fancy_ref.num); + // `fancy_ref` goes out of scope here + } + + let x = move || { + // `fancy_num` can be moved now (no more references exist) + println!("child function: {}", fancy_num.num); + }; + + x(); +} +``` + +If the lifetime of a reference isn't enough, such as in the case of threading, +consider using an `Arc` to create a reference-counted value: + +``` +use std::sync::Arc; +use std::thread; + +struct FancyNum { + num: u8 +} + +fn main() { + let fancy_ref1 = Arc::new(FancyNum { num: 5 }); + let fancy_ref2 = fancy_ref1.clone(); + + let x = thread::spawn(move || { + // `fancy_ref1` can be moved and has a `'static` lifetime + println!("child thread: {}", fancy_ref1.num); + }); + + x.join().expect("child thread should finish"); + println!("main thread: {}", fancy_ref2.num); +} +``` +"##, + E0506: r##" This error occurs when an attempt is made to assign to a borrowed value. @@ -661,7 +765,6 @@ register_diagnostics! { E0500, // closure requires unique access to `..` but .. is already borrowed E0502, // cannot borrow `..`.. as .. because .. is also borrowed as ... E0503, // cannot use `..` because it was mutably borrowed - E0504, // cannot move `..` into closure because it is borrowed E0505, // cannot move out of `..` because it is borrowed E0508, // cannot move out of type `..`, a non-copy fixed-size array E0509, // cannot move out of type `..`, which defines the `Drop` trait From 06f5fa4a0c0493ba76312b07e0f6d2dfa0ca9884 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Mon, 9 May 2016 11:43:33 -0700 Subject: [PATCH 4/9] Inline RawVec::cap --- src/liballoc/raw_vec.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index c407cef25e74c..8b3168b29aa4f 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -147,6 +147,7 @@ impl RawVec { /// Gets the capacity of the allocation. /// /// This will always be `usize::MAX` if `T` is zero-sized. + #[inline(always)] pub fn cap(&self) -> usize { if mem::size_of::() == 0 { !0 From 9d2c45d0e7c18542a0abbcc4ac582cc7dbe8b060 Mon Sep 17 00:00:00 2001 From: Brian Green Date: Mon, 9 May 2016 14:28:42 -0700 Subject: [PATCH 5/9] doc: Fix tiny typo in vec-alloc.md Change `fast an loose` to `fast and loose`. --- src/doc/nomicon/vec-alloc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/nomicon/vec-alloc.md b/src/doc/nomicon/vec-alloc.md index c2ae1a4eb6d26..bc60a577bd35c 100644 --- a/src/doc/nomicon/vec-alloc.md +++ b/src/doc/nomicon/vec-alloc.md @@ -150,7 +150,7 @@ LLVM needs to work with different languages' semantics and custom allocators, it can't really intimately understand allocation. Instead, the main idea behind allocation is "doesn't overlap with other stuff". That is, heap allocations, stack allocations, and globals don't randomly overlap. Yep, it's about alias -analysis. As such, Rust can technically play a bit fast an loose with the notion of +analysis. As such, Rust can technically play a bit fast and loose with the notion of an allocation as long as it's *consistent*. Getting back to the empty allocation case, there are a couple of places where From d8882e263ba07998cb06f110d10a48dec08a8742 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Mon, 9 May 2016 18:18:57 -0700 Subject: [PATCH 6/9] E0061 typo fix --- src/librustc_typeck/diagnostics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index ad91bc9399fef..05e4c79a7e8d5 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -742,7 +742,7 @@ fn f(a: u16, b: &str) {} Must always be called with exactly two arguments, e.g. `f(2, "test")`. -Note, that Rust does not have a notion of optional function arguments or +Note that Rust does not have a notion of optional function arguments or variadic functions (except for its C-FFI). "##, From de0906fe12b8663006151be7b1e32ca540710a18 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 10 May 2016 10:46:56 -0400 Subject: [PATCH 7/9] fix DFS for region error reporting This was causing terrible error reports, because the algorithm was incorrectly identifying the constraints. --- src/librustc/infer/region_inference/mod.rs | 3 -- ...region-invariant-static-error-reporting.rs | 36 +++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 src/test/compile-fail/region-invariant-static-error-reporting.rs diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs index 2f610bf2380fc..9c3865696b845 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -1240,9 +1240,6 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { orig_node_idx, node_idx); - // figure out the direction from which this node takes its - // values, and search for concrete regions etc in that direction - let dir = graph::INCOMING; process_edges(self, &mut state, graph, node_idx, dir); } diff --git a/src/test/compile-fail/region-invariant-static-error-reporting.rs b/src/test/compile-fail/region-invariant-static-error-reporting.rs new file mode 100644 index 0000000000000..ac0167e08bdd6 --- /dev/null +++ b/src/test/compile-fail/region-invariant-static-error-reporting.rs @@ -0,0 +1,36 @@ +// Copyright 2012 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. + +// This test checks that the error messages you get for this example +// at least mention `'a` and `'static`. The precise messages can drift +// over time, but this test used to exhibit some pretty bogus messages +// that were not remotely helpful. + +// error-pattern:cannot infer +// error-pattern:cannot outlive the lifetime 'a +// error-pattern:must be valid for the static lifetime +// error-pattern:cannot infer +// error-pattern:cannot outlive the lifetime 'a +// error-pattern:must be valid for the static lifetime + +struct Invariant<'a>(Option<&'a mut &'a mut ()>); + +fn mk_static() -> Invariant<'static> { Invariant(None) } + +fn unify<'a>(x: Option>, f: fn(Invariant<'a>)) { + let bad = if x.is_some() { + x.unwrap() + } else { + mk_static() + }; + f(bad); +} + +fn main() {} From a5a2f2b951ea982a666eaf52b1874d8f1b17290b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 10 May 2016 21:01:10 +0200 Subject: [PATCH 8/9] Improve "since" tag placement --- src/librustdoc/html/render.rs | 6 +++--- src/librustdoc/html/static/rustdoc.css | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 6a2a0a37c1c05..da0520ebcb84a 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1537,7 +1537,6 @@ impl<'a> Item<'a> { } } - impl<'a> fmt::Display for Item<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { debug_assert!(!self.item.is_stripped()); @@ -1575,6 +1574,9 @@ impl<'a> fmt::Display for Item<'a> { write!(fmt, "")?; // in-band write!(fmt, "")?; + if let Some(version) = self.item.stable_since() { + write!(fmt, "{}", version)?; + } write!(fmt, r##" @@ -1922,7 +1924,6 @@ fn item_function(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, generics = f.generics, where_clause = WhereClause(&f.generics), decl = f.decl)?; - render_stability_since_raw(w, it.stable_since(), None)?; document(w, cx, it) } @@ -2236,7 +2237,6 @@ fn item_struct(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, "", true)?; write!(w, "")?; - render_stability_since_raw(w, it.stable_since(), None)?; document(w, cx, it)?; let mut fields = s.fields.iter().filter(|f| { diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index 4d65b91ed421c..d256e939afcfc 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -634,6 +634,12 @@ a.test-arrow { padding-left: 10px; } +span.since { + position: initial; + font-size: 20px; + margin-right: 5px; +} + /* Media Queries */ @media (max-width: 700px) { From 0908d6688203c08e62dc587cc1a9209d8c3e2a52 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 1 May 2016 14:04:13 +0200 Subject: [PATCH 9/9] Doc improvement on std::fmt module --- src/libcollections/fmt.rs | 12 ++++++++++++ src/libcore/fmt/mod.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/libcollections/fmt.rs b/src/libcollections/fmt.rs index e30e0b213afa1..1f5d542c547ee 100644 --- a/src/libcollections/fmt.rs +++ b/src/libcollections/fmt.rs @@ -505,12 +505,24 @@ use string; /// /// # Examples /// +/// Basic usage: +/// /// ``` /// use std::fmt; /// /// let s = fmt::format(format_args!("Hello, {}!", "world")); /// assert_eq!(s, "Hello, world!".to_string()); /// ``` +/// +/// Please note that using [`format!`][format!] might be preferrable. +/// Example: +/// +/// ``` +/// let s = format!("Hello, {}!", "world"); +/// assert_eq!(s, "Hello, world!".to_string()); +/// ``` +/// +/// [format!]: ../macro.format!.html #[stable(feature = "rust1", since = "1.0.0")] pub fn format(args: Arguments) -> string::String { let mut output = string::String::new(); diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 0c824b5a8e69a..dde4d03dad8ac 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -776,6 +776,32 @@ pub trait UpperExp { /// /// * output - the buffer to write output to /// * args - the precompiled arguments generated by `format_args!` +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// use std::fmt; +/// +/// let mut output = String::new(); +/// fmt::write(&mut output, format_args!("Hello {}!", "world")) +/// .expect("Error occurred while trying to write in String"); +/// assert_eq!(output, "Hello world!"); +/// ``` +/// +/// Please note that using [`write!`][write_macro] might be preferrable. Example: +/// +/// ``` +/// use std::fmt::Write; +/// +/// let mut output = String::new(); +/// write!(&mut output, "Hello {}!", "world") +/// .expect("Error occurred while trying to write in String"); +/// assert_eq!(output, "Hello world!"); +/// ``` +/// +/// [write_macro]: ../../std/macro.write!.html #[stable(feature = "rust1", since = "1.0.0")] pub fn write(output: &mut Write, args: Arguments) -> Result { let mut formatter = Formatter {