diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index a44b408feec25..0bbdcb7f09ea0 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -1222,6 +1222,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 | ExprKind::Struct(..)
                 | ExprKind::Tup(..)
                 | ExprKind::Underscore => false,
+                // Check for unit struct constructor.
+                ExprKind::Path(..) => lower_ctx.extract_unit_struct_path(lhs).is_none(),
                 // Check for tuple struct constructor.
                 ExprKind::Call(callee, ..) => lower_ctx.extract_tuple_struct_path(callee).is_none(),
                 ExprKind::Paren(e) => {
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index d896873fadda9..7918965e04baa 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -831,7 +831,10 @@ fn coroutine_layout<'tcx>(
                     Assigned(_) => bug!("assignment does not match variant"),
                     Ineligible(_) => false,
                 })
-                .map(|local| subst_field(info.field_tys[*local].ty));
+                .map(|local| {
+                    let field_ty = subst_field(info.field_tys[*local].ty);
+                    Ty::new_maybe_uninit(tcx, field_ty)
+                });
 
             let mut variant = univariant_uninterned(
                 cx,
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index c485481b9a1eb..98e267713daf7 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -1319,7 +1319,7 @@ impl Step for CodegenBackend {
                 return None;
             }
 
-            if self.compiler.host.contains("windows") {
+            if self.compiler.host.is_windows() {
                 builder.info(
                     "dist currently disabled for windows by rustc_codegen_cranelift. skipping",
                 );
@@ -1658,7 +1658,7 @@ impl Step for Extended {
             builder.run(&mut cmd);
         }
 
-        if target.contains("windows") {
+        if target.is_windows() {
             let exe = tmp.join("exe");
             let _ = fs::remove_dir_all(&exe);
 
diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs
index 8de208cd09c3c..4b2d3e9ab4b75 100644
--- a/src/bootstrap/src/core/build_steps/llvm.rs
+++ b/src/bootstrap/src/core/build_steps/llvm.rs
@@ -283,7 +283,7 @@ impl Step for Llvm {
         };
 
         builder.update_submodule(&Path::new("src").join("llvm-project"));
-        if builder.llvm_link_shared() && target.contains("windows") {
+        if builder.llvm_link_shared() && target.is_windows() {
             panic!("shared linking to LLVM is not currently supported on {}", target.triple);
         }
 
@@ -361,7 +361,7 @@ impl Step for Llvm {
         // Disable zstd to avoid a dependency on libzstd.so.
         cfg.define("LLVM_ENABLE_ZSTD", "OFF");
 
-        if !target.contains("windows") {
+        if !target.is_windows() {
             cfg.define("LLVM_ENABLE_ZLIB", "ON");
         } else {
             cfg.define("LLVM_ENABLE_ZLIB", "OFF");
@@ -607,7 +607,7 @@ fn configure_cmake(
             cfg.define("CMAKE_SYSTEM_NAME", "DragonFly");
         } else if target.contains("freebsd") {
             cfg.define("CMAKE_SYSTEM_NAME", "FreeBSD");
-        } else if target.contains("windows") {
+        } else if target.is_windows() {
             cfg.define("CMAKE_SYSTEM_NAME", "Windows");
         } else if target.contains("haiku") {
             cfg.define("CMAKE_SYSTEM_NAME", "Haiku");
@@ -772,7 +772,7 @@ fn configure_cmake(
         && !target.contains("netbsd")
         && !target.contains("solaris")
     {
-        if target.contains("apple") || target.contains("windows") {
+        if target.contains("apple") || target.is_windows() {
             ldflags.push_all("-static-libstdc++");
         } else {
             ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++");
@@ -1295,7 +1295,7 @@ impl Step for Libunwind {
                 cfg.define("__LIBUNWIND_IS_NATIVE_ONLY", None);
                 cfg.define("NDEBUG", None);
             }
-            if self.target.contains("windows") {
+            if self.target.is_windows() {
                 cfg.define("_LIBUNWIND_HIDE_SYMBOLS", "1");
                 cfg.define("_LIBUNWIND_IS_NATIVE_ONLY", "1");
             }
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 054c3ab14c08d..28761a7ee4b26 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -1653,10 +1653,7 @@ impl<'a> Builder<'a> {
                 // flesh out rpath support more fully in the future.
                 rustflags.arg("-Zosx-rpath-install-name");
                 Some(format!("-Wl,-rpath,@loader_path/../{libdir}"))
-            } else if !target.contains("windows")
-                && !target.contains("aix")
-                && !target.contains("xous")
-            {
+            } else if !target.is_windows() && !target.contains("aix") && !target.contains("xous") {
                 rustflags.arg("-Clink-args=-Wl,-z,origin");
                 Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}"))
             } else {
@@ -1729,8 +1726,7 @@ impl<'a> Builder<'a> {
         let split_debuginfo_is_stable = target.contains("linux")
             || target.contains("apple")
             || (target.is_msvc() && self.config.rust_split_debuginfo == SplitDebuginfo::Packed)
-            || (target.contains("windows")
-                && self.config.rust_split_debuginfo == SplitDebuginfo::Off);
+            || (target.is_windows() && self.config.rust_split_debuginfo == SplitDebuginfo::Off);
 
         if !split_debuginfo_is_stable {
             rustflags.arg("-Zunstable-options");
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index c3db5641ea47e..ff51760e1932b 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -421,10 +421,10 @@ impl std::str::FromStr for SplitDebuginfo {
 impl SplitDebuginfo {
     /// Returns the default `-Csplit-debuginfo` value for the current target. See the comment for
     /// `rust.split-debuginfo` in `config.example.toml`.
-    fn default_for_platform(target: &str) -> Self {
+    fn default_for_platform(target: TargetSelection) -> Self {
         if target.contains("apple") {
             SplitDebuginfo::Unpacked
-        } else if target.contains("windows") {
+        } else if target.is_windows() {
             SplitDebuginfo::Packed
         } else {
             SplitDebuginfo::Off
@@ -527,6 +527,10 @@ impl TargetSelection {
     pub fn is_msvc(&self) -> bool {
         self.contains("msvc")
     }
+
+    pub fn is_windows(&self) -> bool {
+        self.contains("windows")
+    }
 }
 
 impl fmt::Display for TargetSelection {
@@ -1595,7 +1599,7 @@ impl Config {
                 .as_deref()
                 .map(SplitDebuginfo::from_str)
                 .map(|v| v.expect("invalid value for rust.split_debuginfo"))
-                .unwrap_or(SplitDebuginfo::default_for_platform(&config.build.triple));
+                .unwrap_or(SplitDebuginfo::default_for_platform(config.build));
             optimize = optimize_toml;
             omit_git_hash = omit_git_hash_toml;
             config.rust_new_symbol_mangling = new_symbol_mangling;
diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs
index 133792d85e8f5..d96608db22c0e 100644
--- a/src/bootstrap/src/utils/helpers.rs
+++ b/src/bootstrap/src/utils/helpers.rs
@@ -49,7 +49,7 @@ pub use t;
 /// Given an executable called `name`, return the filename for the
 /// executable for a particular target.
 pub fn exe(name: &str, target: TargetSelection) -> String {
-    if target.contains("windows") {
+    if target.is_windows() {
         format!("{name}.exe")
     } else if target.contains("uefi") {
         format!("{name}.efi")
@@ -72,7 +72,7 @@ pub fn is_debug_info(name: &str) -> bool {
 /// Returns the corresponding relative library directory that the compiler's
 /// dylibs will be found in.
 pub fn libdir(target: TargetSelection) -> &'static str {
-    if target.contains("windows") { "bin" } else { "lib" }
+    if target.is_windows() { "bin" } else { "lib" }
 }
 
 /// Adds a list of lookup paths to `cmd`'s dynamic library lookup path.
@@ -191,7 +191,7 @@ pub fn target_supports_cranelift_backend(target: TargetSelection) -> bool {
             || target.contains("aarch64")
             || target.contains("s390x")
             || target.contains("riscv64gc")
-    } else if target.contains("darwin") || target.contains("windows") {
+    } else if target.contains("darwin") || target.is_windows() {
         target.contains("x86_64")
     } else {
         false
@@ -519,7 +519,7 @@ pub fn linker_flags(
         if matches!(lld_threads, LldThreads::No) {
             args.push(format!(
                 "-Clink-arg=-Wl,{}",
-                lld_flag_no_threads(builder.config.lld_mode, target.is_msvc())
+                lld_flag_no_threads(builder.config.lld_mode, target.is_windows())
             ));
         }
     }
diff --git a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md
index 046d018543f38..669dc9358ebfc 100644
--- a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md
+++ b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md
@@ -201,7 +201,7 @@ mod bar {
 # fn main() {}
 ```
 
-Here, because `bar` is not public, `Bar` wouldn't have its own page, so there's nowhere
+Here, because `bar` is not public, `bar` wouldn't have its own page, so there's nowhere
 to link to. `rustdoc` will inline these definitions, and so we end up in the same case
 as the `#[doc(inline)]` above; `Bar` is in a `Structs` section, as if it were defined at
 the top level. If we add the `no_inline` form of the attribute:
diff --git a/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout b/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout
index b0447a5826119..47b39e5246dde 100644
--- a/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout
+++ b/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout
@@ -52,10 +52,16 @@ print-type-size     variant `Panicked`: 1024 bytes
 print-type-size         upvar `.arg`: 1024 bytes
 print-type-size type: `std::mem::ManuallyDrop<bool>`: 1 bytes, alignment: 1 bytes
 print-type-size     field `.value`: 1 bytes
+print-type-size type: `std::mem::ManuallyDrop<{async fn body@$DIR/async-awaiting-fut.rs:6:17: 6:19}>`: 1 bytes, alignment: 1 bytes
+print-type-size     field `.value`: 1 bytes
 print-type-size type: `std::mem::MaybeUninit<bool>`: 1 bytes, alignment: 1 bytes
 print-type-size     variant `MaybeUninit`: 1 bytes
 print-type-size         field `.uninit`: 0 bytes
 print-type-size         field `.value`: 1 bytes
+print-type-size type: `std::mem::MaybeUninit<{async fn body@$DIR/async-awaiting-fut.rs:6:17: 6:19}>`: 1 bytes, alignment: 1 bytes
+print-type-size     variant `MaybeUninit`: 1 bytes
+print-type-size         field `.uninit`: 0 bytes
+print-type-size         field `.value`: 1 bytes
 print-type-size type: `std::task::Poll<()>`: 1 bytes, alignment: 1 bytes
 print-type-size     discriminant: 1 bytes
 print-type-size     variant `Ready`: 0 bytes
diff --git a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr
index 95731b67ccf77..8e573b512ad43 100644
--- a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr
+++ b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr
@@ -1,7 +1,9 @@
-error[E0391]: cycle detected when computing layout of `{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}`
+error[E0391]: cycle detected when computing layout of `core::mem::maybe_uninit::MaybeUninit<{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}>`
    |
-   = note: ...which requires computing layout of `<<A as First>::Second as Second>::{opaque#0}`...
-   = note: ...which again requires computing layout of `{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}`, completing the cycle
+   = note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop<{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}>`...
+   = note: ...which requires computing layout of `{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}`...
+   = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit<<<A as First>::Second as Second>::{opaque#0}>`...
+   = note: ...which again requires computing layout of `core::mem::maybe_uninit::MaybeUninit<{async fn body@$DIR/indirect-recursion-issue-112047.rs:33:27: 35:6}>`, completing the cycle
    = note: cycle used when computing layout of `{async block@$DIR/indirect-recursion-issue-112047.rs:6:13: 8:6}`
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
diff --git a/tests/ui/coroutine/uninhabited-field.rs b/tests/ui/coroutine/uninhabited-field.rs
new file mode 100644
index 0000000000000..d9570c2fed8d4
--- /dev/null
+++ b/tests/ui/coroutine/uninhabited-field.rs
@@ -0,0 +1,37 @@
+// Test that uninhabited saved local doesn't make the entire variant uninhabited.
+// run-pass
+#![allow(unused)]
+#![feature(assert_matches)]
+#![feature(coroutine_trait)]
+#![feature(coroutines)]
+#![feature(never_type)]
+use std::assert_matches::assert_matches;
+use std::ops::Coroutine;
+use std::ops::CoroutineState;
+use std::pin::Pin;
+
+fn conjure<T>() -> T { loop {} }
+
+fn run<T>(x: bool, y: bool) {
+    let mut c = || {
+        if x {
+            let a : T;
+            if y {
+                a = conjure::<T>();
+            }
+            yield ();
+        } else {
+            let a : T;
+            if y {
+                a = conjure::<T>();
+            }
+            yield ();
+        }
+    };
+    assert_matches!(Pin::new(&mut c).resume(()), CoroutineState::Yielded(()));
+    assert_matches!(Pin::new(&mut c).resume(()), CoroutineState::Complete(()));
+}
+
+fn main() {
+    run::<!>(false, false);
+}
diff --git a/tests/ui/destructuring-assignment/bad-expr-lhs.rs b/tests/ui/destructuring-assignment/bad-expr-lhs.rs
index 53794783a3c87..90e1ac1994385 100644
--- a/tests/ui/destructuring-assignment/bad-expr-lhs.rs
+++ b/tests/ui/destructuring-assignment/bad-expr-lhs.rs
@@ -4,6 +4,4 @@ fn main() {
     (1, 2) = (3, 4);
     //~^ ERROR invalid left-hand side of assignment
     //~| ERROR invalid left-hand side of assignment
-
-    None = Some(3); //~ ERROR invalid left-hand side of assignment
 }
diff --git a/tests/ui/destructuring-assignment/bad-expr-lhs.stderr b/tests/ui/destructuring-assignment/bad-expr-lhs.stderr
index d298674748053..2916d6d9f115d 100644
--- a/tests/ui/destructuring-assignment/bad-expr-lhs.stderr
+++ b/tests/ui/destructuring-assignment/bad-expr-lhs.stderr
@@ -30,15 +30,7 @@ LL |     (1, 2) = (3, 4);
    |         |
    |         cannot assign to this expression
 
-error[E0070]: invalid left-hand side of assignment
-  --> $DIR/bad-expr-lhs.rs:8:10
-   |
-LL |     None = Some(3);
-   |     ---- ^
-   |     |
-   |     cannot assign to this expression
-
-error: aborting due to 5 previous errors
+error: aborting due to 4 previous errors
 
 Some errors have detailed explanations: E0067, E0070.
 For more information about an error, try `rustc --explain E0067`.
diff --git a/tests/ui/destructuring-assignment/non-exhaustive-destructure.rs b/tests/ui/destructuring-assignment/non-exhaustive-destructure.rs
new file mode 100644
index 0000000000000..39939f2bad634
--- /dev/null
+++ b/tests/ui/destructuring-assignment/non-exhaustive-destructure.rs
@@ -0,0 +1,4 @@
+fn main() {
+    None = Some(3);
+    //~^ ERROR refutable pattern in local binding
+}
diff --git a/tests/ui/destructuring-assignment/non-exhaustive-destructure.stderr b/tests/ui/destructuring-assignment/non-exhaustive-destructure.stderr
new file mode 100644
index 0000000000000..b9ceaa4af7ba0
--- /dev/null
+++ b/tests/ui/destructuring-assignment/non-exhaustive-destructure.stderr
@@ -0,0 +1,17 @@
+error[E0005]: refutable pattern in local binding
+  --> $DIR/non-exhaustive-destructure.rs:2:5
+   |
+LL |     None = Some(3);
+   |     ^^^^ pattern `Some(_)` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `Option<i32>`
+help: you might want to use `if let` to ignore the variant that isn't matched
+   |
+LL |     if None = Some(3) { todo!() };
+   |     ++                +++++++++++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0005`.
diff --git a/tests/ui/destructuring-assignment/struct-or-enum-variant-path.rs b/tests/ui/destructuring-assignment/struct-or-enum-variant-path.rs
index 8da7f90c5246d..f82e029983b75 100644
--- a/tests/ui/destructuring-assignment/struct-or-enum-variant-path.rs
+++ b/tests/ui/destructuring-assignment/struct-or-enum-variant-path.rs
@@ -11,17 +11,22 @@ type A = E;
 fn main() {
     let mut a;
 
+    S = S;
     (S, a) = (S, ());
 
+    E::V = E::V;
     (E::V, a) = (E::V, ());
 
+    <E>::V = E::V;
     (<E>::V, a) = (E::V, ());
+    A::V = A::V;
     (A::V, a) = (E::V, ());
 }
 
 impl S {
     fn check() {
         let a;
+        Self = S;
         (Self, a) = (S, ());
     }
 }
@@ -29,6 +34,7 @@ impl S {
 impl E {
     fn check() {
         let a;
+        Self::V = E::V;
         (Self::V, a) = (E::V, ());
     }
 }
diff --git a/tests/ui/inference/issue-103587.stderr b/tests/ui/inference/issue-103587.stderr
index b373fbfbb948c..589cb7ea7b130 100644
--- a/tests/ui/inference/issue-103587.stderr
+++ b/tests/ui/inference/issue-103587.stderr
@@ -26,14 +26,10 @@ error[E0308]: mismatched types
 LL |     if None = x { }
    |        ^^^^^^^^ expected `bool`, found `()`
    |
-help: you might have meant to use pattern matching
+help: consider adding `let`
    |
 LL |     if let None = x { }
    |        +++
-help: you might have meant to compare for equality
-   |
-LL |     if None == x { }
-   |              +
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/issues/issue-13407.rs b/tests/ui/issues/issue-13407.rs
index 7ea81ffb59e7e..7794be37b8507 100644
--- a/tests/ui/issues/issue-13407.rs
+++ b/tests/ui/issues/issue-13407.rs
@@ -4,6 +4,6 @@ mod A {
 
 fn main() {
     A::C = 1;
-    //~^ ERROR: invalid left-hand side of assignment
-    //~| ERROR: struct `C` is private
+    //~^ ERROR: mismatched types
+    //~| ERROR: unit struct `C` is private
 }
diff --git a/tests/ui/issues/issue-13407.stderr b/tests/ui/issues/issue-13407.stderr
index 54b6c640d9d79..ac2eb6581fe25 100644
--- a/tests/ui/issues/issue-13407.stderr
+++ b/tests/ui/issues/issue-13407.stderr
@@ -10,15 +10,18 @@ note: the unit struct `C` is defined here
 LL |     struct C;
    |     ^^^^^^^^^
 
-error[E0070]: invalid left-hand side of assignment
-  --> $DIR/issue-13407.rs:6:10
+error[E0308]: mismatched types
+  --> $DIR/issue-13407.rs:6:5
    |
+LL |     struct C;
+   |     -------- unit struct defined here
+...
 LL |     A::C = 1;
-   |     ---- ^
+   |     ^^^^   - this expression has type `{integer}`
    |     |
-   |     cannot assign to this expression
+   |     expected integer, found `C`
 
 error: aborting due to 2 previous errors
 
-Some errors have detailed explanations: E0070, E0603.
-For more information about an error, try `rustc --explain E0070`.
+Some errors have detailed explanations: E0308, E0603.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/print_type_sizes/coroutine_discr_placement.stdout b/tests/ui/print_type_sizes/coroutine_discr_placement.stdout
index f34a8e9a706aa..71a7f3c63815c 100644
--- a/tests/ui/print_type_sizes/coroutine_discr_placement.stdout
+++ b/tests/ui/print_type_sizes/coroutine_discr_placement.stdout
@@ -9,3 +9,9 @@ print-type-size         padding: 3 bytes
 print-type-size         local `.z`: 4 bytes, alignment: 4 bytes
 print-type-size     variant `Returned`: 0 bytes
 print-type-size     variant `Panicked`: 0 bytes
+print-type-size type: `std::mem::ManuallyDrop<i32>`: 4 bytes, alignment: 4 bytes
+print-type-size     field `.value`: 4 bytes
+print-type-size type: `std::mem::MaybeUninit<i32>`: 4 bytes, alignment: 4 bytes
+print-type-size     variant `MaybeUninit`: 4 bytes
+print-type-size         field `.uninit`: 0 bytes
+print-type-size         field `.value`: 4 bytes