about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-08-19 17:44:43 +0000
committerbors <bors@rust-lang.org>2018-08-19 17:44:43 +0000
commit3ac79c718475fd29b8be34dde667b683390c2aee (patch)
tree3d44b05190197268e01ddb7a245106cfda35e825
parentbfc3b20663e1abfff0499332f9168f60c3269c33 (diff)
parent58e4b54bd49a2a554ee8a573827b9ccbf7a9b65e (diff)
downloadrust-3ac79c718475fd29b8be34dde667b683390c2aee.tar.gz
rust-3ac79c718475fd29b8be34dde667b683390c2aee.zip
Auto merge of #53258 - nikomatsakis:issue-53189-optimize-reassignment-immutable-state, r=pnkfelix
optimize reassignment immutable state

This is the "simple fix" when it comes to checking for reassignment. We just shoot for compatibility with the AST-based checker. Makes no attempt to solve #21232.

I opted for this simpler fix because I didn't want to think about complications [like the ones described here](https://github.com/rust-lang/rust/issues/21232#issuecomment-412219247).

Let's do some profiling measurements.

Fixes #53189

r? @pnkfelix
-rw-r--r--src/librustc_mir/borrow_check/mod.rs85
-rw-r--r--src/test/ui/borrowck/assign_mutable_fields.nll.stderr9
-rw-r--r--src/test/ui/borrowck/assign_mutable_fields.rs32
-rw-r--r--src/test/ui/borrowck/assign_mutable_fields.stderr21
-rw-r--r--src/test/ui/borrowck/reassignment_immutable_fields.nll.stderr44
-rw-r--r--src/test/ui/borrowck/reassignment_immutable_fields.rs30
-rw-r--r--src/test/ui/borrowck/reassignment_immutable_fields.stderr56
-rw-r--r--src/test/ui/borrowck/reassignment_immutable_fields_overlapping.nll.stderr20
-rw-r--r--src/test/ui/borrowck/reassignment_immutable_fields_overlapping.rs26
-rw-r--r--src/test/ui/borrowck/reassignment_immutable_fields_overlapping.stderr20
-rw-r--r--src/test/ui/borrowck/reassignment_immutable_fields_twice.nll.stderr38
-rw-r--r--src/test/ui/borrowck/reassignment_immutable_fields_twice.rs27
-rw-r--r--src/test/ui/borrowck/reassignment_immutable_fields_twice.stderr38
-rw-r--r--src/test/ui/did_you_mean/issue-35937.nll.stderr19
-rw-r--r--src/test/ui/immut-function-arguments.ast.nll.stderr14
-rw-r--r--src/test/ui/immut-function-arguments.mir.stderr14
-rw-r--r--src/test/ui/mut/mutable-class-fields.ast.nll.stderr11
-rw-r--r--src/test/ui/mut/mutable-class-fields.mir.stderr11
18 files changed, 425 insertions, 90 deletions
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index f96b9b8082f..ce0e76a636d 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -798,12 +798,6 @@ enum LocalMutationIsAllowed {
     No,
 }
 
-struct AccessErrorsReported {
-    mutability_error: bool,
-    #[allow(dead_code)]
-    conflict_error: bool,
-}
-
 #[derive(Copy, Clone)]
 enum InitializationRequiringAction {
     Update,
@@ -1072,7 +1066,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         kind: (ShallowOrDeep, ReadOrWrite),
         is_local_mutation_allowed: LocalMutationIsAllowed,
         flow_state: &Flows<'cx, 'gcx, 'tcx>,
-    ) -> AccessErrorsReported {
+    ) {
         let (sd, rw) = kind;
 
         if let Activation(_, borrow_index) = rw {
@@ -1082,10 +1076,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                      place: {:?} borrow_index: {:?}",
                     place_span.0, borrow_index
                 );
-                return AccessErrorsReported {
-                    mutability_error: false,
-                    conflict_error: true,
-                };
+                return;
             }
         }
 
@@ -1097,10 +1088,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 "access_place: suppressing error place_span=`{:?}` kind=`{:?}`",
                 place_span, kind
             );
-            return AccessErrorsReported {
-                mutability_error: false,
-                conflict_error: true,
-            };
+            return;
         }
 
         let mutability_error =
@@ -1122,11 +1110,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             self.access_place_error_reported
                 .insert((place_span.0.clone(), place_span.1));
         }
-
-        AccessErrorsReported {
-            mutability_error,
-            conflict_error,
-        }
     }
 
     fn check_access_for_conflict(
@@ -1275,23 +1258,30 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             }
         }
 
-        let errors_reported = self.access_place(
+        // Special case: you can assign a immutable local variable
+        // (e.g., `x = ...`) so long as it has never been initialized
+        // before (at this point in the flow).
+        if let &Place::Local(local) = place_span.0 {
+            if let Mutability::Not = self.mir.local_decls[local].mutability {
+                // check for reassignments to immutable local variables
+                self.check_if_reassignment_to_immutable_state(
+                    context,
+                    local,
+                    place_span,
+                    flow_state,
+                );
+                return;
+            }
+        }
+
+        // Otherwise, use the normal access permission rules.
+        self.access_place(
             context,
             place_span,
             (kind, Write(WriteKind::Mutate)),
-            // We want immutable upvars to cause an "assignment to immutable var"
-            // error, not an "reassignment of immutable var" error, because the
-            // latter can't find a good previous assignment span.
-            //
-            // There's probably a better way to do this.
-            LocalMutationIsAllowed::ExceptUpvars,
+            LocalMutationIsAllowed::No,
             flow_state,
         );
-
-        if !errors_reported.mutability_error {
-            // check for reassignments to immutable local variables
-            self.check_if_reassignment_to_immutable_state(context, place_span, flow_state);
-        }
     }
 
     fn consume_rvalue(
@@ -1590,27 +1580,20 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
     fn check_if_reassignment_to_immutable_state(
         &mut self,
         context: Context,
-        (place, span): (&Place<'tcx>, Span),
+        local: Local,
+        place_span: (&Place<'tcx>, Span),
         flow_state: &Flows<'cx, 'gcx, 'tcx>,
     ) {
-        debug!("check_if_reassignment_to_immutable_state({:?})", place);
-        // determine if this path has a non-mut owner (and thus needs checking).
-        let err_place = match self.is_mutable(place, LocalMutationIsAllowed::No) {
-            Ok(..) => return,
-            Err(place) => place,
-        };
-        debug!(
-            "check_if_reassignment_to_immutable_state({:?}) - is an imm local",
-            place
-        );
-
-        for i in flow_state.ever_inits.iter_incoming() {
-            let init = self.move_data.inits[i];
-            let init_place = &self.move_data.move_paths[init.path].place;
-            if places_conflict::places_conflict(self.tcx, self.mir, &init_place, place, Deep) {
-                self.report_illegal_reassignment(context, (place, span), init.span, err_place);
-                break;
-            }
+        debug!("check_if_reassignment_to_immutable_state({:?})", local);
+
+        // Check if any of the initializiations of `local` have happened yet:
+        let mpi = self.move_data.rev_lookup.find_local(local);
+        let init_indices = &self.move_data.init_path_map[mpi];
+        let first_init_index = init_indices.iter().find(|ii| flow_state.ever_inits.contains(ii));
+        if let Some(&init_index) = first_init_index {
+            // And, if so, report an error.
+            let init = &self.move_data.inits[init_index];
+            self.report_illegal_reassignment(context, place_span, init.span, place_span.0);
         }
     }
 
diff --git a/src/test/ui/borrowck/assign_mutable_fields.nll.stderr b/src/test/ui/borrowck/assign_mutable_fields.nll.stderr
new file mode 100644
index 00000000000..51470753472
--- /dev/null
+++ b/src/test/ui/borrowck/assign_mutable_fields.nll.stderr
@@ -0,0 +1,9 @@
+error[E0381]: use of possibly uninitialized variable: `x`
+  --> $DIR/assign_mutable_fields.rs:29:10
+   |
+LL |     drop(x); //~ ERROR
+   |          ^ use of possibly uninitialized `x`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0381`.
diff --git a/src/test/ui/borrowck/assign_mutable_fields.rs b/src/test/ui/borrowck/assign_mutable_fields.rs
new file mode 100644
index 00000000000..4e41f44ef5b
--- /dev/null
+++ b/src/test/ui/borrowck/assign_mutable_fields.rs
@@ -0,0 +1,32 @@
+// 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.
+
+// Currently, we permit you to assign to individual fields of a mut
+// var, but we do not permit you to use the complete var afterwards.
+// We hope to fix this at some point.
+//
+// FIXME(#21232)
+
+fn assign_both_fields_and_use() {
+    let mut x: (u32, u32);
+    x.0 = 1;
+    x.1 = 22;
+    drop(x.0); //~ ERROR
+    drop(x.1); //~ ERROR
+}
+
+fn assign_both_fields_the_use_var() {
+    let mut x: (u32, u32);
+    x.0 = 1;
+    x.1 = 22;
+    drop(x); //~ ERROR
+}
+
+fn main() { }
diff --git a/src/test/ui/borrowck/assign_mutable_fields.stderr b/src/test/ui/borrowck/assign_mutable_fields.stderr
new file mode 100644
index 00000000000..677887babd0
--- /dev/null
+++ b/src/test/ui/borrowck/assign_mutable_fields.stderr
@@ -0,0 +1,21 @@
+error[E0381]: use of possibly uninitialized variable: `x.0`
+  --> $DIR/assign_mutable_fields.rs:21:10
+   |
+LL |     drop(x.0); //~ ERROR
+   |          ^^^ use of possibly uninitialized `x.0`
+
+error[E0381]: use of possibly uninitialized variable: `x.1`
+  --> $DIR/assign_mutable_fields.rs:22:10
+   |
+LL |     drop(x.1); //~ ERROR
+   |          ^^^ use of possibly uninitialized `x.1`
+
+error[E0381]: use of possibly uninitialized variable: `x`
+  --> $DIR/assign_mutable_fields.rs:29:10
+   |
+LL |     drop(x); //~ ERROR
+   |          ^ use of possibly uninitialized `x`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0381`.
diff --git a/src/test/ui/borrowck/reassignment_immutable_fields.nll.stderr b/src/test/ui/borrowck/reassignment_immutable_fields.nll.stderr
new file mode 100644
index 00000000000..97f3bf5b81f
--- /dev/null
+++ b/src/test/ui/borrowck/reassignment_immutable_fields.nll.stderr
@@ -0,0 +1,44 @@
+error[E0594]: cannot assign to `x.0`, as `x` is not declared as mutable
+  --> $DIR/reassignment_immutable_fields.rs:17:5
+   |
+LL |     let x: (u32, u32);
+   |         - help: consider changing this to be mutable: `mut x`
+LL |     x.0 = 1; //~ ERROR
+   |     ^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `x.1`, as `x` is not declared as mutable
+  --> $DIR/reassignment_immutable_fields.rs:18:5
+   |
+LL |     let x: (u32, u32);
+   |         - help: consider changing this to be mutable: `mut x`
+LL |     x.0 = 1; //~ ERROR
+LL |     x.1 = 22; //~ ERROR
+   |     ^^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `x.0`, as `x` is not declared as mutable
+  --> $DIR/reassignment_immutable_fields.rs:25:5
+   |
+LL |     let x: (u32, u32);
+   |         - help: consider changing this to be mutable: `mut x`
+LL |     x.0 = 1; //~ ERROR
+   |     ^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `x.1`, as `x` is not declared as mutable
+  --> $DIR/reassignment_immutable_fields.rs:26:5
+   |
+LL |     let x: (u32, u32);
+   |         - help: consider changing this to be mutable: `mut x`
+LL |     x.0 = 1; //~ ERROR
+LL |     x.1 = 22; //~ ERROR
+   |     ^^^^^^^^ cannot assign
+
+error[E0381]: use of possibly uninitialized variable: `x`
+  --> $DIR/reassignment_immutable_fields.rs:27:10
+   |
+LL |     drop(x); //~ ERROR
+   |          ^ use of possibly uninitialized `x`
+
+error: aborting due to 5 previous errors
+
+Some errors occurred: E0381, E0594.
+For more information about an error, try `rustc --explain E0381`.
diff --git a/src/test/ui/borrowck/reassignment_immutable_fields.rs b/src/test/ui/borrowck/reassignment_immutable_fields.rs
new file mode 100644
index 00000000000..c49ae2f9567
--- /dev/null
+++ b/src/test/ui/borrowck/reassignment_immutable_fields.rs
@@ -0,0 +1,30 @@
+// 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.
+
+// This test is currently disallowed, but we hope someday to support it.
+//
+// FIXME(#21232)
+
+fn assign_both_fields_and_use() {
+    let x: (u32, u32);
+    x.0 = 1; //~ ERROR
+    x.1 = 22; //~ ERROR
+    drop(x.0); //~ ERROR
+    drop(x.1); //~ ERROR
+}
+
+fn assign_both_fields_the_use_var() {
+    let x: (u32, u32);
+    x.0 = 1; //~ ERROR
+    x.1 = 22; //~ ERROR
+    drop(x); //~ ERROR
+}
+
+fn main() { }
diff --git a/src/test/ui/borrowck/reassignment_immutable_fields.stderr b/src/test/ui/borrowck/reassignment_immutable_fields.stderr
new file mode 100644
index 00000000000..54d12f8fae8
--- /dev/null
+++ b/src/test/ui/borrowck/reassignment_immutable_fields.stderr
@@ -0,0 +1,56 @@
+error[E0594]: cannot assign to field `x.0` of immutable binding
+  --> $DIR/reassignment_immutable_fields.rs:17:5
+   |
+LL |     let x: (u32, u32);
+   |         - consider changing this to `mut x`
+LL |     x.0 = 1; //~ ERROR
+   |     ^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to field `x.1` of immutable binding
+  --> $DIR/reassignment_immutable_fields.rs:18:5
+   |
+LL |     let x: (u32, u32);
+   |         - consider changing this to `mut x`
+LL |     x.0 = 1; //~ ERROR
+LL |     x.1 = 22; //~ ERROR
+   |     ^^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0381]: use of possibly uninitialized variable: `x.0`
+  --> $DIR/reassignment_immutable_fields.rs:19:10
+   |
+LL |     drop(x.0); //~ ERROR
+   |          ^^^ use of possibly uninitialized `x.0`
+
+error[E0381]: use of possibly uninitialized variable: `x.1`
+  --> $DIR/reassignment_immutable_fields.rs:20:10
+   |
+LL |     drop(x.1); //~ ERROR
+   |          ^^^ use of possibly uninitialized `x.1`
+
+error[E0594]: cannot assign to field `x.0` of immutable binding
+  --> $DIR/reassignment_immutable_fields.rs:25:5
+   |
+LL |     let x: (u32, u32);
+   |         - consider changing this to `mut x`
+LL |     x.0 = 1; //~ ERROR
+   |     ^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to field `x.1` of immutable binding
+  --> $DIR/reassignment_immutable_fields.rs:26:5
+   |
+LL |     let x: (u32, u32);
+   |         - consider changing this to `mut x`
+LL |     x.0 = 1; //~ ERROR
+LL |     x.1 = 22; //~ ERROR
+   |     ^^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0381]: use of possibly uninitialized variable: `x`
+  --> $DIR/reassignment_immutable_fields.rs:27:10
+   |
+LL |     drop(x); //~ ERROR
+   |          ^ use of possibly uninitialized `x`
+
+error: aborting due to 7 previous errors
+
+Some errors occurred: E0381, E0594.
+For more information about an error, try `rustc --explain E0381`.
diff --git a/src/test/ui/borrowck/reassignment_immutable_fields_overlapping.nll.stderr b/src/test/ui/borrowck/reassignment_immutable_fields_overlapping.nll.stderr
new file mode 100644
index 00000000000..c433d6e25c9
--- /dev/null
+++ b/src/test/ui/borrowck/reassignment_immutable_fields_overlapping.nll.stderr
@@ -0,0 +1,20 @@
+error[E0594]: cannot assign to `x.a`, as `x` is not declared as mutable
+  --> $DIR/reassignment_immutable_fields_overlapping.rs:22:5
+   |
+LL |     let x: Foo;
+   |         - help: consider changing this to be mutable: `mut x`
+LL |     x.a = 1;  //~ ERROR
+   |     ^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `x.b`, as `x` is not declared as mutable
+  --> $DIR/reassignment_immutable_fields_overlapping.rs:23:5
+   |
+LL |     let x: Foo;
+   |         - help: consider changing this to be mutable: `mut x`
+LL |     x.a = 1;  //~ ERROR
+LL |     x.b = 22; //~ ERROR
+   |     ^^^^^^^^ cannot assign
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0594`.
diff --git a/src/test/ui/borrowck/reassignment_immutable_fields_overlapping.rs b/src/test/ui/borrowck/reassignment_immutable_fields_overlapping.rs
new file mode 100644
index 00000000000..add23ec8f24
--- /dev/null
+++ b/src/test/ui/borrowck/reassignment_immutable_fields_overlapping.rs
@@ -0,0 +1,26 @@
+// 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.
+
+// This should never be allowed -- `foo.a` and `foo.b` are
+// overlapping, so since `x` is not `mut` we should not permit
+// reassignment.
+
+union Foo {
+    a: u32,
+    b: u32,
+}
+
+unsafe fn overlapping_fields() {
+    let x: Foo;
+    x.a = 1;  //~ ERROR
+    x.b = 22; //~ ERROR
+}
+
+fn main() { }
diff --git a/src/test/ui/borrowck/reassignment_immutable_fields_overlapping.stderr b/src/test/ui/borrowck/reassignment_immutable_fields_overlapping.stderr
new file mode 100644
index 00000000000..70849905f92
--- /dev/null
+++ b/src/test/ui/borrowck/reassignment_immutable_fields_overlapping.stderr
@@ -0,0 +1,20 @@
+error[E0594]: cannot assign to field `x.a` of immutable binding
+  --> $DIR/reassignment_immutable_fields_overlapping.rs:22:5
+   |
+LL |     let x: Foo;
+   |         - consider changing this to `mut x`
+LL |     x.a = 1;  //~ ERROR
+   |     ^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to field `x.b` of immutable binding
+  --> $DIR/reassignment_immutable_fields_overlapping.rs:23:5
+   |
+LL |     let x: Foo;
+   |         - consider changing this to `mut x`
+LL |     x.a = 1;  //~ ERROR
+LL |     x.b = 22; //~ ERROR
+   |     ^^^^^^^^ cannot mutably borrow field of immutable binding
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0594`.
diff --git a/src/test/ui/borrowck/reassignment_immutable_fields_twice.nll.stderr b/src/test/ui/borrowck/reassignment_immutable_fields_twice.nll.stderr
new file mode 100644
index 00000000000..2160ae20c42
--- /dev/null
+++ b/src/test/ui/borrowck/reassignment_immutable_fields_twice.nll.stderr
@@ -0,0 +1,38 @@
+error[E0594]: cannot assign to `x.0`, as `x` is not declared as mutable
+  --> $DIR/reassignment_immutable_fields_twice.rs:17:5
+   |
+LL |     let x: (u32, u32);
+   |         - help: consider changing this to be mutable: `mut x`
+LL |     x = (22, 44);
+LL |     x.0 = 1; //~ ERROR
+   |     ^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `x.0`, as `x` is not declared as mutable
+  --> $DIR/reassignment_immutable_fields_twice.rs:22:5
+   |
+LL |     let x: (u32, u32);
+   |         - help: consider changing this to be mutable: `mut x`
+LL |     x.0 = 1; //~ ERROR
+   |     ^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `x.0`, as `x` is not declared as mutable
+  --> $DIR/reassignment_immutable_fields_twice.rs:23:5
+   |
+LL |     let x: (u32, u32);
+   |         - help: consider changing this to be mutable: `mut x`
+LL |     x.0 = 1; //~ ERROR
+LL |     x.0 = 22; //~ ERROR
+   |     ^^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `x.1`, as `x` is not declared as mutable
+  --> $DIR/reassignment_immutable_fields_twice.rs:24:5
+   |
+LL |     let x: (u32, u32);
+   |         - help: consider changing this to be mutable: `mut x`
+...
+LL |     x.1 = 44; //~ ERROR
+   |     ^^^^^^^^ cannot assign
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0594`.
diff --git a/src/test/ui/borrowck/reassignment_immutable_fields_twice.rs b/src/test/ui/borrowck/reassignment_immutable_fields_twice.rs
new file mode 100644
index 00000000000..c7e7e5c4533
--- /dev/null
+++ b/src/test/ui/borrowck/reassignment_immutable_fields_twice.rs
@@ -0,0 +1,27 @@
+// 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.
+
+// This should never be allowed -- since `x` is not `mut`, so `x.0`
+// cannot be assigned twice.
+
+fn var_then_field() {
+    let x: (u32, u32);
+    x = (22, 44);
+    x.0 = 1; //~ ERROR
+}
+
+fn same_field_twice() {
+    let x: (u32, u32);
+    x.0 = 1; //~ ERROR
+    x.0 = 22; //~ ERROR
+    x.1 = 44; //~ ERROR
+}
+
+fn main() { }
diff --git a/src/test/ui/borrowck/reassignment_immutable_fields_twice.stderr b/src/test/ui/borrowck/reassignment_immutable_fields_twice.stderr
new file mode 100644
index 00000000000..b3c013d8b1f
--- /dev/null
+++ b/src/test/ui/borrowck/reassignment_immutable_fields_twice.stderr
@@ -0,0 +1,38 @@
+error[E0594]: cannot assign to field `x.0` of immutable binding
+  --> $DIR/reassignment_immutable_fields_twice.rs:17:5
+   |
+LL |     let x: (u32, u32);
+   |         - consider changing this to `mut x`
+LL |     x = (22, 44);
+LL |     x.0 = 1; //~ ERROR
+   |     ^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to field `x.0` of immutable binding
+  --> $DIR/reassignment_immutable_fields_twice.rs:22:5
+   |
+LL |     let x: (u32, u32);
+   |         - consider changing this to `mut x`
+LL |     x.0 = 1; //~ ERROR
+   |     ^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to field `x.0` of immutable binding
+  --> $DIR/reassignment_immutable_fields_twice.rs:23:5
+   |
+LL |     let x: (u32, u32);
+   |         - consider changing this to `mut x`
+LL |     x.0 = 1; //~ ERROR
+LL |     x.0 = 22; //~ ERROR
+   |     ^^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to field `x.1` of immutable binding
+  --> $DIR/reassignment_immutable_fields_twice.rs:24:5
+   |
+LL |     let x: (u32, u32);
+   |         - consider changing this to `mut x`
+...
+LL |     x.1 = 44; //~ ERROR
+   |     ^^^^^^^^ cannot mutably borrow field of immutable binding
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0594`.
diff --git a/src/test/ui/did_you_mean/issue-35937.nll.stderr b/src/test/ui/did_you_mean/issue-35937.nll.stderr
index 804e5f0531f..34bdf48e2a6 100644
--- a/src/test/ui/did_you_mean/issue-35937.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-35937.nll.stderr
@@ -6,26 +6,23 @@ LL |     let f = Foo { v: Vec::new() };
 LL |     f.v.push("cat".to_string()); //~ ERROR cannot borrow
    |     ^^^ cannot borrow as mutable
 
-error[E0384]: cannot assign twice to immutable variable `s`
+error[E0594]: cannot assign to `s.x`, as `s` is not declared as mutable
   --> $DIR/issue-35937.rs:26:5
    |
 LL |     let s = S { x: 42 };
-   |         -
-   |         |
-   |         first assignment to `s`
-   |         consider changing this to `mut s`
+   |         - help: consider changing this to be mutable: `mut s`
 LL |     s.x += 1; //~ ERROR cannot assign
-   |     ^^^^^^^^ cannot assign twice to immutable variable
+   |     ^^^^^^^^ cannot assign
 
-error[E0384]: cannot assign to immutable argument `s`
+error[E0594]: cannot assign to `s.x`, as `s` is not declared as mutable
   --> $DIR/issue-35937.rs:30:5
    |
 LL | fn bar(s: S) {
-   |        - consider changing this to `mut s`
+   |        - help: consider changing this to be mutable: `mut s`
 LL |     s.x += 1; //~ ERROR cannot assign
-   |     ^^^^^^^^ cannot assign to immutable argument
+   |     ^^^^^^^^ cannot assign
 
 error: aborting due to 3 previous errors
 
-Some errors occurred: E0384, E0596.
-For more information about an error, try `rustc --explain E0384`.
+Some errors occurred: E0594, E0596.
+For more information about an error, try `rustc --explain E0594`.
diff --git a/src/test/ui/immut-function-arguments.ast.nll.stderr b/src/test/ui/immut-function-arguments.ast.nll.stderr
index c8236340627..d33814a0fc5 100644
--- a/src/test/ui/immut-function-arguments.ast.nll.stderr
+++ b/src/test/ui/immut-function-arguments.ast.nll.stderr
@@ -1,19 +1,19 @@
-error[E0384]: cannot assign to immutable argument `y`
+error[E0594]: cannot assign to `*y`, as `y` is not declared as mutable
   --> $DIR/immut-function-arguments.rs:15:5
    |
 LL | fn f(y: Box<isize>) {
-   |      - consider changing this to `mut y`
+   |      - help: consider changing this to be mutable: `mut y`
 LL |     *y = 5; //[ast]~ ERROR cannot assign
-   |     ^^^^^^ cannot assign to immutable argument
+   |     ^^^^^^ cannot assign
 
-error[E0384]: cannot assign to immutable argument `q`
+error[E0594]: cannot assign to `*q`, as `q` is not declared as mutable
   --> $DIR/immut-function-arguments.rs:20:35
    |
 LL |     let _frob = |q: Box<isize>| { *q = 2; }; //[ast]~ ERROR cannot assign
-   |                  -                ^^^^^^ cannot assign to immutable argument
+   |                  -                ^^^^^^ cannot assign
    |                  |
-   |                  consider changing this to `mut q`
+   |                  help: consider changing this to be mutable: `mut q`
 
 error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0384`.
+For more information about this error, try `rustc --explain E0594`.
diff --git a/src/test/ui/immut-function-arguments.mir.stderr b/src/test/ui/immut-function-arguments.mir.stderr
index c8236340627..d33814a0fc5 100644
--- a/src/test/ui/immut-function-arguments.mir.stderr
+++ b/src/test/ui/immut-function-arguments.mir.stderr
@@ -1,19 +1,19 @@
-error[E0384]: cannot assign to immutable argument `y`
+error[E0594]: cannot assign to `*y`, as `y` is not declared as mutable
   --> $DIR/immut-function-arguments.rs:15:5
    |
 LL | fn f(y: Box<isize>) {
-   |      - consider changing this to `mut y`
+   |      - help: consider changing this to be mutable: `mut y`
 LL |     *y = 5; //[ast]~ ERROR cannot assign
-   |     ^^^^^^ cannot assign to immutable argument
+   |     ^^^^^^ cannot assign
 
-error[E0384]: cannot assign to immutable argument `q`
+error[E0594]: cannot assign to `*q`, as `q` is not declared as mutable
   --> $DIR/immut-function-arguments.rs:20:35
    |
 LL |     let _frob = |q: Box<isize>| { *q = 2; }; //[ast]~ ERROR cannot assign
-   |                  -                ^^^^^^ cannot assign to immutable argument
+   |                  -                ^^^^^^ cannot assign
    |                  |
-   |                  consider changing this to `mut q`
+   |                  help: consider changing this to be mutable: `mut q`
 
 error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0384`.
+For more information about this error, try `rustc --explain E0594`.
diff --git a/src/test/ui/mut/mutable-class-fields.ast.nll.stderr b/src/test/ui/mut/mutable-class-fields.ast.nll.stderr
index 1d1e58de587..033a3bd6cb4 100644
--- a/src/test/ui/mut/mutable-class-fields.ast.nll.stderr
+++ b/src/test/ui/mut/mutable-class-fields.ast.nll.stderr
@@ -1,14 +1,11 @@
-error[E0384]: cannot assign twice to immutable variable `nyan`
+error[E0594]: cannot assign to `nyan.how_hungry`, as `nyan` is not declared as mutable
   --> $DIR/mutable-class-fields.rs:28:3
    |
 LL |   let nyan : cat = cat(52, 99);
-   |       ----
-   |       |
-   |       first assignment to `nyan`
-   |       consider changing this to `mut nyan`
+   |       ---- help: consider changing this to be mutable: `mut nyan`
 LL |   nyan.how_hungry = 0; //[ast]~ ERROR cannot assign
-   |   ^^^^^^^^^^^^^^^^^^^ cannot assign twice to immutable variable
+   |   ^^^^^^^^^^^^^^^^^^^ cannot assign
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0384`.
+For more information about this error, try `rustc --explain E0594`.
diff --git a/src/test/ui/mut/mutable-class-fields.mir.stderr b/src/test/ui/mut/mutable-class-fields.mir.stderr
index 1d1e58de587..033a3bd6cb4 100644
--- a/src/test/ui/mut/mutable-class-fields.mir.stderr
+++ b/src/test/ui/mut/mutable-class-fields.mir.stderr
@@ -1,14 +1,11 @@
-error[E0384]: cannot assign twice to immutable variable `nyan`
+error[E0594]: cannot assign to `nyan.how_hungry`, as `nyan` is not declared as mutable
   --> $DIR/mutable-class-fields.rs:28:3
    |
 LL |   let nyan : cat = cat(52, 99);
-   |       ----
-   |       |
-   |       first assignment to `nyan`
-   |       consider changing this to `mut nyan`
+   |       ---- help: consider changing this to be mutable: `mut nyan`
 LL |   nyan.how_hungry = 0; //[ast]~ ERROR cannot assign
-   |   ^^^^^^^^^^^^^^^^^^^ cannot assign twice to immutable variable
+   |   ^^^^^^^^^^^^^^^^^^^ cannot assign
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0384`.
+For more information about this error, try `rustc --explain E0594`.