about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libcore/option.rs12
-rw-r--r--src/librustc/hir/mod.rs2
-rw-r--r--src/librustc/mir/mod.rs4
-rw-r--r--src/librustc/session/mod.rs6
-rw-r--r--src/librustc/traits/error_reporting.rs21
-rw-r--r--src/librustc_mir/borrow_check.rs98
-rw-r--r--src/librustc_trans/back/link.rs21
-rw-r--r--src/libstd/path.rs8
-rw-r--r--src/libstd/sys/redox/condvar.rs57
-rw-r--r--src/libstd/sys/redox/fs.rs3
-rw-r--r--src/libsyntax_pos/symbol.rs2
-rw-r--r--src/test/ui/borrowck/borrowck-closures-two-mut.rs62
-rw-r--r--src/test/ui/borrowck/borrowck-closures-two-mut.stderr146
-rw-r--r--src/test/ui/issue-33941.rs15
-rw-r--r--src/test/ui/issue-33941.stderr21
-rw-r--r--src/tools/compiletest/src/header.rs12
-rw-r--r--src/tools/compiletest/src/main.rs5
-rw-r--r--src/tools/compiletest/src/runtest.rs9
18 files changed, 420 insertions, 84 deletions
diff --git a/src/libcore/option.rs b/src/libcore/option.rs
index 63c846b25ec..12e6e843056 100644
--- a/src/libcore/option.rs
+++ b/src/libcore/option.rs
@@ -634,16 +634,12 @@ impl<T> Option<T> {
     #[inline]
     #[unstable(feature = "option_filter", issue = "45860")]
     pub fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Self {
-        match self {
-            Some(x) => {
-                if predicate(&x) {
-                    Some(x)
-                } else {
-                    None
-                }
+        if let Some(x) = self {
+            if predicate(&x) {
+                return Some(x)
             }
-            None => None,
         }
+        None
     }
 
     /// Returns the option if it contains a value, otherwise returns `optb`.
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index c9b1d70e7b6..39e222230e5 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -1949,7 +1949,7 @@ impl ForeignItem_ {
 }
 
 /// A free variable referred to in a function.
-#[derive(Copy, Clone, RustcEncodable, RustcDecodable)]
+#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable)]
 pub struct Freevar {
     /// The variable being accessed free.
     pub def: Def,
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 0fa40f56196..cd017650633 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -267,10 +267,10 @@ impl<'tcx> Mir<'tcx> {
         let block = &self[location.block];
         let stmts = &block.statements;
         let idx = location.statement_index;
-        if location.statement_index < stmts.len() {
+        if idx < stmts.len() {
             &stmts[idx].source_info
         } else {
-            assert!(location.statement_index == stmts.len());
+            assert!(idx == stmts.len());
             &block.terminator().source_info
         }
     }
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index 39cf50787ef..00a91eeb9c1 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -164,11 +164,13 @@ enum DiagnosticBuilderMethod {
     // add more variants as needed to support one-time diagnostics
 }
 
-/// Diagnostic message id - used in order to avoid emitting the same message more than once
+/// Diagnostic message ID—used by `Session.one_time_diagnostics` to avoid
+/// emitting the same message more than once
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub enum DiagnosticMessageId {
+    ErrorId(u16), // EXXXX error code as integer
     LintId(lint::LintId),
-    StabilityId(u32)
+    StabilityId(u32) // issue number
 }
 
 impl Session {
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index e2b23c12cf1..106b1b08656 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -36,6 +36,7 @@ use middle::const_val;
 use rustc::lint::builtin::EXTRA_REQUIREMENT_IN_IMPL;
 use std::fmt;
 use syntax::ast;
+use session::DiagnosticMessageId;
 use ty::{self, AdtKind, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable};
 use ty::error::ExpectedFound;
 use ty::fast_reject;
@@ -219,13 +220,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 }
             }
 
-            let mut diag = struct_span_err!(
-                self.tcx.sess, obligation.cause.span, E0271,
-                "type mismatch resolving `{}`", predicate
-            );
-            self.note_type_err(&mut diag, &obligation.cause, None, values, err);
-            self.note_obligation_cause(&mut diag, obligation);
-            diag.emit();
+            let msg = format!("type mismatch resolving `{}`", predicate);
+            let error_id = (DiagnosticMessageId::ErrorId(271),
+                            Some(obligation.cause.span), msg.clone());
+            let fresh = self.tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id);
+            if fresh {
+                let mut diag = struct_span_err!(
+                    self.tcx.sess, obligation.cause.span, E0271,
+                    "type mismatch resolving `{}`", predicate
+                );
+                self.note_type_err(&mut diag, &obligation.cause, None, values, err);
+                self.note_obligation_cause(&mut diag, obligation);
+                diag.emit();
+            }
         });
     }
 
diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs
index d2524b306cf..b723b86776b 100644
--- a/src/librustc_mir/borrow_check.rs
+++ b/src/librustc_mir/borrow_check.rs
@@ -1169,8 +1169,72 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
         err.emit();
     }
 
+    /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of
+    /// the local assigned at `location`.
+    /// This is done by searching in statements succeeding `location`
+    /// and originating from `maybe_closure_span`.
+    fn find_closure_span(
+        &self,
+        maybe_closure_span: Span,
+        location: Location,
+    ) -> Option<(Span, Span)> {
+        use rustc::hir::ExprClosure;
+        use rustc::mir::AggregateKind;
+
+        let local = if let StatementKind::Assign(Lvalue::Local(local), _) =
+            self.mir[location.block].statements[location.statement_index].kind
+        {
+            local
+        } else {
+            return None;
+        };
+
+        for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
+            if maybe_closure_span != stmt.source_info.span {
+                break;
+            }
+
+            if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref lvs)) = stmt.kind {
+                if let AggregateKind::Closure(def_id, _) = **kind {
+                    debug!("find_closure_span: found closure {:?}", lvs);
+
+                    return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
+                        let args_span = if let ExprClosure(_, _, _, span, _) =
+                            self.tcx.hir.expect_expr(node_id).node
+                        {
+                            span
+                        } else {
+                            return None;
+                        };
+
+                        self.tcx
+                            .with_freevars(node_id, |freevars| {
+                                for (v, lv) in freevars.iter().zip(lvs) {
+                                    if let Operand::Consume(Lvalue::Local(l)) = *lv {
+                                        if local == l {
+                                            debug!(
+                                                "find_closure_span: found captured local {:?}",
+                                                l
+                                            );
+                                            return Some(v.span);
+                                        }
+                                    }
+                                }
+                                None
+                            })
+                            .map(|var_span| (args_span, var_span))
+                    } else {
+                        None
+                    };
+                }
+            }
+        }
+
+        None
+    }
+
     fn report_conflicting_borrow(&mut self,
-                                 _context: Context,
+                                 context: Context,
                                  common_prefix: &Lvalue,
                                  (lvalue, span): (&Lvalue, Span),
                                  gen_borrow_kind: BorrowKind,
@@ -1183,38 +1247,60 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
 
         let issued_span = self.retrieve_borrow_span(issued_borrow);
 
+        let new_closure_span = self.find_closure_span(span, context.loc);
+        let span = new_closure_span.map(|(args, _)| args).unwrap_or(span);
+        let old_closure_span = self.find_closure_span(issued_span, issued_borrow.location);
+        let issued_span = old_closure_span.map(|(args, _)| args).unwrap_or(issued_span);
+
+        let desc_lvalue = self.describe_lvalue(lvalue);
+
         // FIXME: supply non-"" `opt_via` when appropriate
         let mut err = match (gen_borrow_kind, "immutable", "mutable",
                              issued_borrow.kind, "immutable", "mutable") {
             (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) |
             (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) =>
                 self.tcx.cannot_reborrow_already_borrowed(
-                    span, &self.describe_lvalue(lvalue), "", lft, issued_span,
+                    span, &desc_lvalue, "", lft, issued_span,
                     "it", rgt, "", end_issued_loan_span, Origin::Mir),
 
             (BorrowKind::Mut, _, _, BorrowKind::Mut, _, _) =>
                 self.tcx.cannot_mutably_borrow_multiply(
-                    span, &self.describe_lvalue(lvalue), "", issued_span,
+                    span, &desc_lvalue, "", issued_span,
                     "", end_issued_loan_span, Origin::Mir),
 
             (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) =>
                 self.tcx.cannot_uniquely_borrow_by_two_closures(
-                    span, &self.describe_lvalue(lvalue), issued_span,
+                    span, &desc_lvalue, issued_span,
                     end_issued_loan_span, Origin::Mir),
 
             (BorrowKind::Unique, _, _, _, _, _) =>
                 self.tcx.cannot_uniquely_borrow_by_one_closure(
-                    span, &self.describe_lvalue(lvalue), "",
+                    span, &desc_lvalue, "",
                     issued_span, "it", "", end_issued_loan_span, Origin::Mir),
 
             (_, _, _, BorrowKind::Unique, _, _) =>
                 self.tcx.cannot_reborrow_already_uniquely_borrowed(
-                    span, &self.describe_lvalue(lvalue), "it", "",
+                    span, &desc_lvalue, "it", "",
                     issued_span, "", end_issued_loan_span, Origin::Mir),
 
             (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) =>
                 unreachable!(),
         };
+
+        if let Some((_, var_span)) = old_closure_span {
+            err.span_label(
+                var_span,
+                format!("previous borrow occurs due to use of `{}` in closure", desc_lvalue),
+            );
+        }
+
+        if let Some((_, var_span)) = new_closure_span {
+            err.span_label(
+                var_span,
+                format!("borrow occurs due to use of `{}` in closure", desc_lvalue),
+            );
+        }
+
         err.emit();
     }
 
diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs
index 1961acf53a6..1d2bfd001f1 100644
--- a/src/librustc_trans/back/link.rs
+++ b/src/librustc_trans/back/link.rs
@@ -503,31 +503,10 @@ fn link_staticlib(sess: &Session,
     if !all_native_libs.is_empty() {
         if sess.opts.prints.contains(&PrintRequest::NativeStaticLibs) {
             print_native_static_libs(sess, &all_native_libs);
-        } else {
-            // Fallback for backwards compatibility only
-            print_native_static_libs_legacy(sess, &all_native_libs);
         }
     }
 }
 
-fn print_native_static_libs_legacy(sess: &Session, all_native_libs: &[NativeLibrary]) {
-    sess.note_without_error("link against the following native artifacts when linking against \
-                             this static library");
-    sess.note_without_error("This list will not be printed by default. \
-        Please add --print=native-static-libs if you need this information");
-
-    for lib in all_native_libs.iter().filter(|l| relevant_lib(sess, l)) {
-        let name = match lib.kind {
-            NativeLibraryKind::NativeStaticNobundle |
-            NativeLibraryKind::NativeUnknown => "library",
-            NativeLibraryKind::NativeFramework => "framework",
-            // These are included, no need to print them
-            NativeLibraryKind::NativeStatic => continue,
-        };
-        sess.note_without_error(&format!("{}: {}", name, lib.name));
-    }
-}
-
 fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) {
     let lib_args: Vec<_> = all_native_libs.iter()
         .filter(|l| relevant_lib(sess, l))
diff --git a/src/libstd/path.rs b/src/libstd/path.rs
index 270878dc029..69922470cff 100644
--- a/src/libstd/path.rs
+++ b/src/libstd/path.rs
@@ -1690,11 +1690,11 @@ impl Path {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[allow(deprecated)]
     pub fn is_absolute(&self) -> bool {
-        if !cfg!(target_os = "redox") {
-            self.has_root() && (cfg!(unix) || self.prefix().is_some())
-        } else {
+        if cfg!(target_os = "redox") {
             // FIXME: Allow Redox prefixes
-            has_redox_scheme(self.as_u8_slice())
+            self.has_root() || has_redox_scheme(self.as_u8_slice())
+        } else {
+            self.has_root() && (cfg!(unix) || self.prefix().is_some())
         }
     }
 
diff --git a/src/libstd/sys/redox/condvar.rs b/src/libstd/sys/redox/condvar.rs
index fe4a89c6f3e..2a611ed7dab 100644
--- a/src/libstd/sys/redox/condvar.rs
+++ b/src/libstd/sys/redox/condvar.rs
@@ -9,12 +9,12 @@
 // except according to those terms.
 
 use cell::UnsafeCell;
-use intrinsics::{atomic_cxchg, atomic_xadd, atomic_xchg};
+use intrinsics::{atomic_cxchg, atomic_load, atomic_xadd, atomic_xchg};
 use ptr;
 use time::Duration;
 
 use sys::mutex::{mutex_unlock, Mutex};
-use sys::syscall::{futex, FUTEX_WAIT, FUTEX_WAKE, FUTEX_REQUEUE};
+use sys::syscall::{futex, TimeSpec, FUTEX_WAIT, FUTEX_WAKE, FUTEX_REQUEUE};
 
 pub struct Condvar {
     lock: UnsafeCell<*mut i32>,
@@ -63,33 +63,50 @@ impl Condvar {
     }
 
     #[inline]
-    pub fn wait(&self, mutex: &Mutex) {
-        unsafe {
-            let lock = self.lock.get();
-            let seq = self.seq.get();
-
-            if *lock != mutex.lock.get() {
-                if *lock != ptr::null_mut() {
-                    panic!("Condvar used with more than one Mutex");
-                }
+    unsafe fn wait_inner(&self, mutex: &Mutex, timeout_ptr: *const TimeSpec) -> bool {
+        let lock = self.lock.get();
+        let seq = self.seq.get();
 
-                atomic_cxchg(lock as *mut usize, 0, mutex.lock.get() as usize);
+        if *lock != mutex.lock.get() {
+            if *lock != ptr::null_mut() {
+                panic!("Condvar used with more than one Mutex");
             }
 
-            mutex_unlock(*lock);
+            atomic_cxchg(lock as *mut usize, 0, mutex.lock.get() as usize);
+        }
 
-            let _ = futex(seq, FUTEX_WAIT, *seq, 0, ptr::null_mut());
+        mutex_unlock(*lock);
 
-            while atomic_xchg(*lock, 2) != 0 {
-                let _ = futex(*lock, FUTEX_WAIT, 2, 0, ptr::null_mut());
-            }
+        let seq_before = atomic_load(seq);
+
+        let _ = futex(seq, FUTEX_WAIT, seq_before, timeout_ptr as usize, ptr::null_mut());
+
+        let seq_after = atomic_load(seq);
+
+        while atomic_xchg(*lock, 2) != 0 {
+            let _ = futex(*lock, FUTEX_WAIT, 2, 0, ptr::null_mut());
+        }
+
+        seq_before != seq_after
+    }
+
+    #[inline]
+    pub fn wait(&self, mutex: &Mutex) {
+        unsafe {
+            assert!(self.wait_inner(mutex, ptr::null()));
         }
     }
 
     #[inline]
-    pub fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool {
-        ::sys_common::util::dumb_print(format_args!("condvar wait_timeout\n"));
-        unimplemented!();
+    pub fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+        unsafe {
+            let timeout = TimeSpec {
+                tv_sec: dur.as_secs() as i64,
+                tv_nsec: dur.subsec_nanos() as i32
+            };
+
+            self.wait_inner(mutex, &timeout as *const TimeSpec)
+        }
     }
 
     #[inline]
diff --git a/src/libstd/sys/redox/fs.rs b/src/libstd/sys/redox/fs.rs
index 918893097f8..3483477d40c 100644
--- a/src/libstd/sys/redox/fs.rs
+++ b/src/libstd/sys/redox/fs.rs
@@ -437,8 +437,7 @@ pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
 }
 
 pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
-    ::sys_common::util::dumb_print(format_args!("Link\n"));
-    unimplemented!();
+    Err(Error::from_raw_os_error(syscall::ENOSYS))
 }
 
 pub fn stat(p: &Path) -> io::Result<FileAttr> {
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index 26e6f27e20f..69ddd560213 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -416,7 +416,7 @@ mod tests {
         // first one is zero:
         assert_eq!(i.intern("dog"), Symbol(0));
         // re-use gets the same entry:
-        assert_eq!(i.intern ("dog"), Symbol(0));
+        assert_eq!(i.intern("dog"), Symbol(0));
         // different string gets a different #:
         assert_eq!(i.intern("cat"), Symbol(1));
         assert_eq!(i.intern("cat"), Symbol(1));
diff --git a/src/test/ui/borrowck/borrowck-closures-two-mut.rs b/src/test/ui/borrowck/borrowck-closures-two-mut.rs
new file mode 100644
index 00000000000..182b3d75442
--- /dev/null
+++ b/src/test/ui/borrowck/borrowck-closures-two-mut.rs
@@ -0,0 +1,62 @@
+// Copyright 2017 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Tests that two closures cannot simultaneously have mutable
+// access to the variable, whether that mutable access be used
+// for direct assignment or for taking mutable ref. Issue #6801.
+
+// compile-flags: -Z emit-end-regions -Z borrowck-mir
+
+#![feature(box_syntax)]
+
+fn to_fn_mut<F: FnMut()>(f: F) -> F { f }
+
+fn a() {
+    let mut x = 3;
+    let c1 = to_fn_mut(|| x = 4);
+    let c2 = to_fn_mut(|| x = 5); //~ ERROR cannot borrow `x` as mutable more than once
+}
+
+fn set(x: &mut isize) {
+    *x = 4;
+}
+
+fn b() {
+    let mut x = 3;
+    let c1 = to_fn_mut(|| set(&mut x));
+    let c2 = to_fn_mut(|| set(&mut x)); //~ ERROR cannot borrow `x` as mutable more than once
+}
+
+fn c() {
+    let mut x = 3;
+    let c1 = to_fn_mut(|| x = 5);
+    let c2 = to_fn_mut(|| set(&mut x)); //~ ERROR cannot borrow `x` as mutable more than once
+}
+
+fn d() {
+    let mut x = 3;
+    let c1 = to_fn_mut(|| x = 5);
+    let c2 = to_fn_mut(|| { let _y = to_fn_mut(|| set(&mut x)); }); // (nested closure)
+    //~^ ERROR cannot borrow `x` as mutable more than once
+}
+
+fn g() {
+    struct Foo {
+        f: Box<isize>
+    }
+
+    let mut x: Box<_> = box Foo { f: box 3 };
+    let c1 = to_fn_mut(|| set(&mut *x.f));
+    let c2 = to_fn_mut(|| set(&mut *x.f));
+    //~^ ERROR cannot borrow `x` as mutable more than once
+}
+
+fn main() {
+}
diff --git a/src/test/ui/borrowck/borrowck-closures-two-mut.stderr b/src/test/ui/borrowck/borrowck-closures-two-mut.stderr
new file mode 100644
index 00000000000..fc8a7f2ab60
--- /dev/null
+++ b/src/test/ui/borrowck/borrowck-closures-two-mut.stderr
@@ -0,0 +1,146 @@
+error[E0499]: cannot borrow `x` as mutable more than once at a time (Ast)
+  --> $DIR/borrowck-closures-two-mut.rs:24:24
+   |
+23 |     let c1 = to_fn_mut(|| x = 4);
+   |                        -- - previous borrow occurs due to use of `x` in closure
+   |                        |
+   |                        first mutable borrow occurs here
+24 |     let c2 = to_fn_mut(|| x = 5); //~ ERROR cannot borrow `x` as mutable more than once
+   |                        ^^ - borrow occurs due to use of `x` in closure
+   |                        |
+   |                        second mutable borrow occurs here
+25 | }
+   | - first borrow ends here
+
+error[E0499]: cannot borrow `x` as mutable more than once at a time (Ast)
+  --> $DIR/borrowck-closures-two-mut.rs:34:24
+   |
+33 |     let c1 = to_fn_mut(|| set(&mut x));
+   |                        --          - previous borrow occurs due to use of `x` in closure
+   |                        |
+   |                        first mutable borrow occurs here
+34 |     let c2 = to_fn_mut(|| set(&mut x)); //~ ERROR cannot borrow `x` as mutable more than once
+   |                        ^^          - borrow occurs due to use of `x` in closure
+   |                        |
+   |                        second mutable borrow occurs here
+35 | }
+   | - first borrow ends here
+
+error[E0499]: cannot borrow `x` as mutable more than once at a time (Ast)
+  --> $DIR/borrowck-closures-two-mut.rs:40:24
+   |
+39 |     let c1 = to_fn_mut(|| x = 5);
+   |                        -- - previous borrow occurs due to use of `x` in closure
+   |                        |
+   |                        first mutable borrow occurs here
+40 |     let c2 = to_fn_mut(|| set(&mut x)); //~ ERROR cannot borrow `x` as mutable more than once
+   |                        ^^          - borrow occurs due to use of `x` in closure
+   |                        |
+   |                        second mutable borrow occurs here
+41 | }
+   | - first borrow ends here
+
+error[E0499]: cannot borrow `x` as mutable more than once at a time (Ast)
+  --> $DIR/borrowck-closures-two-mut.rs:46:24
+   |
+45 |     let c1 = to_fn_mut(|| x = 5);
+   |                        -- - previous borrow occurs due to use of `x` in closure
+   |                        |
+   |                        first mutable borrow occurs here
+46 |     let c2 = to_fn_mut(|| { let _y = to_fn_mut(|| set(&mut x)); }); // (nested closure)
+   |                        ^^                                  - borrow occurs due to use of `x` in closure
+   |                        |
+   |                        second mutable borrow occurs here
+47 |     //~^ ERROR cannot borrow `x` as mutable more than once
+48 | }
+   | - first borrow ends here
+
+error[E0499]: cannot borrow `x` as mutable more than once at a time (Ast)
+  --> $DIR/borrowck-closures-two-mut.rs:57:24
+   |
+56 |     let c1 = to_fn_mut(|| set(&mut *x.f));
+   |                        --           - previous borrow occurs due to use of `x` in closure
+   |                        |
+   |                        first mutable borrow occurs here
+57 |     let c2 = to_fn_mut(|| set(&mut *x.f));
+   |                        ^^           - borrow occurs due to use of `x` in closure
+   |                        |
+   |                        second mutable borrow occurs here
+58 |     //~^ ERROR cannot borrow `x` as mutable more than once
+59 | }
+   | - first borrow ends here
+
+error[E0499]: cannot borrow `x` as mutable more than once at a time (Mir)
+  --> $DIR/borrowck-closures-two-mut.rs:24:24
+   |
+23 |     let c1 = to_fn_mut(|| x = 4);
+   |                        -- - previous borrow occurs due to use of `x` in closure
+   |                        |
+   |                        first mutable borrow occurs here
+24 |     let c2 = to_fn_mut(|| x = 5); //~ ERROR cannot borrow `x` as mutable more than once
+   |                        ^^ - borrow occurs due to use of `x` in closure
+   |                        |
+   |                        second mutable borrow occurs here
+25 | }
+   | - first borrow ends here
+
+error[E0499]: cannot borrow `x` as mutable more than once at a time (Mir)
+  --> $DIR/borrowck-closures-two-mut.rs:34:24
+   |
+33 |     let c1 = to_fn_mut(|| set(&mut x));
+   |                        --          - previous borrow occurs due to use of `x` in closure
+   |                        |
+   |                        first mutable borrow occurs here
+34 |     let c2 = to_fn_mut(|| set(&mut x)); //~ ERROR cannot borrow `x` as mutable more than once
+   |                        ^^          - borrow occurs due to use of `x` in closure
+   |                        |
+   |                        second mutable borrow occurs here
+35 | }
+   | - first borrow ends here
+
+error[E0499]: cannot borrow `x` as mutable more than once at a time (Mir)
+  --> $DIR/borrowck-closures-two-mut.rs:40:24
+   |
+39 |     let c1 = to_fn_mut(|| x = 5);
+   |                        -- - previous borrow occurs due to use of `x` in closure
+   |                        |
+   |                        first mutable borrow occurs here
+40 |     let c2 = to_fn_mut(|| set(&mut x)); //~ ERROR cannot borrow `x` as mutable more than once
+   |                        ^^          - borrow occurs due to use of `x` in closure
+   |                        |
+   |                        second mutable borrow occurs here
+41 | }
+   | - first borrow ends here
+
+error[E0499]: cannot borrow `x` as mutable more than once at a time (Mir)
+  --> $DIR/borrowck-closures-two-mut.rs:46:24
+   |
+45 |     let c1 = to_fn_mut(|| x = 5);
+   |                        -- - previous borrow occurs due to use of `x` in closure
+   |                        |
+   |                        first mutable borrow occurs here
+46 |     let c2 = to_fn_mut(|| { let _y = to_fn_mut(|| set(&mut x)); }); // (nested closure)
+   |                        ^^                                  - borrow occurs due to use of `x` in closure
+   |                        |
+   |                        second mutable borrow occurs here
+47 |     //~^ ERROR cannot borrow `x` as mutable more than once
+48 | }
+   | - first borrow ends here
+
+error[E0499]: cannot borrow `x` as mutable more than once at a time (Mir)
+  --> $DIR/borrowck-closures-two-mut.rs:57:24
+   |
+56 |     let c1 = to_fn_mut(|| set(&mut *x.f));
+   |                        --           - previous borrow occurs due to use of `x` in closure
+   |                        |
+   |                        first mutable borrow occurs here
+57 |     let c2 = to_fn_mut(|| set(&mut *x.f));
+   |                        ^^           - borrow occurs due to use of `x` in closure
+   |                        |
+   |                        second mutable borrow occurs here
+58 |     //~^ ERROR cannot borrow `x` as mutable more than once
+59 | }
+   | - first borrow ends here
+
+error: aborting due to 10 previous errors
+
diff --git a/src/test/ui/issue-33941.rs b/src/test/ui/issue-33941.rs
new file mode 100644
index 00000000000..eb111d33b99
--- /dev/null
+++ b/src/test/ui/issue-33941.rs
@@ -0,0 +1,15 @@
+// Copyright 2017 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::collections::HashMap;
+
+fn main() {
+    for _ in HashMap::new().iter().cloned() {}
+}
diff --git a/src/test/ui/issue-33941.stderr b/src/test/ui/issue-33941.stderr
new file mode 100644
index 00000000000..5a8d1fab3f6
--- /dev/null
+++ b/src/test/ui/issue-33941.stderr
@@ -0,0 +1,21 @@
+error[E0271]: type mismatch resolving `<std::collections::hash_map::Iter<'_, _, _> as std::iter::Iterator>::Item == &_`
+  --> $DIR/issue-33941.rs:14:36
+   |
+14 |     for _ in HashMap::new().iter().cloned() {}
+   |                                    ^^^^^^ expected tuple, found reference
+   |
+   = note: expected type `(&_, &_)`
+              found type `&_`
+
+error[E0271]: type mismatch resolving `<std::collections::hash_map::Iter<'_, _, _> as std::iter::Iterator>::Item == &_`
+  --> $DIR/issue-33941.rs:14:5
+   |
+14 |     for _ in HashMap::new().iter().cloned() {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected tuple, found reference
+   |
+   = note: expected type `(&_, &_)`
+              found type `&_`
+   = note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Cloned<std::collections::hash_map::Iter<'_, _, _>>`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 19195838791..e03d9f89e5d 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -259,9 +259,9 @@ impl TestProps {
         props
     }
 
-    pub fn from_file(testfile: &Path, config: &Config) -> Self {
+    pub fn from_file(testfile: &Path, cfg: Option<&str>, config: &Config) -> Self {
         let mut props = TestProps::new();
-        props.load_from(testfile, None, config);
+        props.load_from(testfile, cfg, config);
         props
     }
 
@@ -269,10 +269,10 @@ impl TestProps {
     /// tied to a particular revision `foo` (indicated by writing
     /// `//[foo]`), then the property is ignored unless `cfg` is
     /// `Some("foo")`.
-    pub fn load_from(&mut self,
-                     testfile: &Path,
-                     cfg: Option<&str>,
-                     config: &Config) {
+    fn load_from(&mut self,
+                 testfile: &Path,
+                 cfg: Option<&str>,
+                 config: &Config) {
         iter_header(testfile,
                     cfg,
                     &mut |ln| {
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index 9fb6a3f5e07..6da37df1927 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -493,6 +493,7 @@ fn stamp(config: &Config, testpaths: &TestPaths) -> PathBuf {
                              config.stage_id);
     config.build_base.canonicalize()
           .unwrap_or_else(|_| config.build_base.clone())
+          .join(&testpaths.relative_dir)
           .join(stamp_name)
 }
 
@@ -524,6 +525,10 @@ fn up_to_date(config: &Config, testpaths: &TestPaths, props: &EarlyProps) -> boo
         let lib = lib.unwrap();
         inputs.push(mtime(&lib.path()));
     }
+    if let Some(ref rustdoc_path) = config.rustdoc_path {
+        inputs.push(mtime(&rustdoc_path));
+        inputs.push(mtime(&rust_src_dir.join("src/etc/htmldocck.py")));
+    }
     inputs.iter().any(|input| *input > stamp)
 }
 
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 80ca0afe72b..2ff3eb7678f 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -69,7 +69,7 @@ pub fn run(config: Config, testpaths: &TestPaths) {
         print!("\n\n");
     }
     debug!("running {:?}", testpaths.file.display());
-    let base_props = TestProps::from_file(&testpaths.file, &config);
+    let base_props = TestProps::from_file(&testpaths.file, None, &config);
 
     let base_cx = TestCx { config: &config,
                            props: &base_props,
@@ -81,8 +81,9 @@ pub fn run(config: Config, testpaths: &TestPaths) {
         base_cx.run_revision()
     } else {
         for revision in &base_props.revisions {
-            let mut revision_props = base_props.clone();
-            revision_props.load_from(&testpaths.file, Some(revision), &config);
+            let revision_props = TestProps::from_file(&testpaths.file,
+                                                      Some(revision),
+                                                      &config);
             let rev_cx = TestCx {
                 config: &config,
                 props: &revision_props,
@@ -2614,4 +2615,4 @@ fn read2_abbreviated(mut child: Child) -> io::Result<Output> {
         stdout: stdout.into_bytes(),
         stderr: stderr.into_bytes(),
     })
-}
\ No newline at end of file
+}