From a4707a4f3b38bd6aae7a452300ef5c65da19e524 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Thu, 8 May 2025 19:43:33 +0000
Subject: [PATCH] Do not ICE when reassigning in GatherLocalsVisitor on the bad
 path

---
 .../rustc_hir_typeck/src/gather_locals.rs     | 14 +++++++++++--
 tests/ui/typeck/gather-locals-twice.rs        |  7 +++++++
 tests/ui/typeck/gather-locals-twice.stderr    | 20 +++++++++++++++++++
 3 files changed, 39 insertions(+), 2 deletions(-)
 create mode 100644 tests/ui/typeck/gather-locals-twice.rs
 create mode 100644 tests/ui/typeck/gather-locals-twice.stderr

diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs
index a8bbc89dbded5..956671fc66ed2 100644
--- a/compiler/rustc_hir_typeck/src/gather_locals.rs
+++ b/compiler/rustc_hir_typeck/src/gather_locals.rs
@@ -105,16 +105,26 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
     }
 
     fn assign(&mut self, span: Span, nid: HirId, ty_opt: Option<Ty<'tcx>>) -> Ty<'tcx> {
+        // We evaluate expressions twice occasionally in diagnostics for better
+        // type information or because it needs type information out-of-order.
+        // In order to not ICE and not lead to knock-on ambiguity errors, if we
+        // try to re-assign a type to a local, then just take out the previous
+        // type and delay a bug.
+        if let Some(&local) = self.fcx.locals.borrow_mut().get(&nid) {
+            self.fcx.dcx().span_delayed_bug(span, "evaluated expression more than once");
+            return local;
+        }
+
         match ty_opt {
             None => {
                 // Infer the variable's type.
                 let var_ty = self.fcx.next_ty_var(span);
-                assert_eq!(self.fcx.locals.borrow_mut().insert(nid, var_ty), None);
+                self.fcx.locals.borrow_mut().insert(nid, var_ty);
                 var_ty
             }
             Some(typ) => {
                 // Take type that the user specified.
-                assert_eq!(self.fcx.locals.borrow_mut().insert(nid, typ), None);
+                self.fcx.locals.borrow_mut().insert(nid, typ);
                 typ
             }
         }
diff --git a/tests/ui/typeck/gather-locals-twice.rs b/tests/ui/typeck/gather-locals-twice.rs
new file mode 100644
index 0000000000000..e9351146f4564
--- /dev/null
+++ b/tests/ui/typeck/gather-locals-twice.rs
@@ -0,0 +1,7 @@
+// Regression test for <https://github.com/rust-lang/rust/issues/140785>.
+
+fn main() {
+    () += { let x; };
+    //~^ ERROR binary assignment operation `+=` cannot be applied to type `()`
+    //~| ERROR invalid left-hand side of assignment
+}
diff --git a/tests/ui/typeck/gather-locals-twice.stderr b/tests/ui/typeck/gather-locals-twice.stderr
new file mode 100644
index 0000000000000..7598ef8e7ea4c
--- /dev/null
+++ b/tests/ui/typeck/gather-locals-twice.stderr
@@ -0,0 +1,20 @@
+error[E0368]: binary assignment operation `+=` cannot be applied to type `()`
+  --> $DIR/gather-locals-twice.rs:4:5
+   |
+LL |     () += { let x; };
+   |     --^^^^^^^^^^^^^^
+   |     |
+   |     cannot use `+=` on type `()`
+
+error[E0067]: invalid left-hand side of assignment
+  --> $DIR/gather-locals-twice.rs:4:8
+   |
+LL |     () += { let x; };
+   |     -- ^^
+   |     |
+   |     cannot assign to this expression
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0067, E0368.
+For more information about an error, try `rustc --explain E0067`.