diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs
index 3da215fe6c019..a0a381638c061 100644
--- a/compiler/rustc_codegen_cranelift/example/mini_core.rs
+++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs
@@ -55,26 +55,26 @@ impl<T: ?Sized> LegacyReceiver for &mut T {}
 impl<T: ?Sized> LegacyReceiver for Box<T> {}
 
 #[lang = "copy"]
-pub unsafe trait Copy {}
-
-unsafe impl Copy for bool {}
-unsafe impl Copy for u8 {}
-unsafe impl Copy for u16 {}
-unsafe impl Copy for u32 {}
-unsafe impl Copy for u64 {}
-unsafe impl Copy for u128 {}
-unsafe impl Copy for usize {}
-unsafe impl Copy for i8 {}
-unsafe impl Copy for i16 {}
-unsafe impl Copy for i32 {}
-unsafe impl Copy for isize {}
-unsafe impl Copy for f32 {}
-unsafe impl Copy for f64 {}
-unsafe impl Copy for char {}
-unsafe impl<'a, T: ?Sized> Copy for &'a T {}
-unsafe impl<T: ?Sized> Copy for *const T {}
-unsafe impl<T: ?Sized> Copy for *mut T {}
-unsafe impl<T: Copy> Copy for Option<T> {}
+pub trait Copy {}
+
+impl Copy for bool {}
+impl Copy for u8 {}
+impl Copy for u16 {}
+impl Copy for u32 {}
+impl Copy for u64 {}
+impl Copy for u128 {}
+impl Copy for usize {}
+impl Copy for i8 {}
+impl Copy for i16 {}
+impl Copy for i32 {}
+impl Copy for isize {}
+impl Copy for f32 {}
+impl Copy for f64 {}
+impl Copy for char {}
+impl<'a, T: ?Sized> Copy for &'a T {}
+impl<T: ?Sized> Copy for *const T {}
+impl<T: ?Sized> Copy for *mut T {}
+impl<T: Copy> Copy for Option<T> {}
 
 #[lang = "sync"]
 pub unsafe trait Sync {}
diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs
index c887598f6e902..cdd151613df84 100644
--- a/compiler/rustc_codegen_gcc/example/mini_core.rs
+++ b/compiler/rustc_codegen_gcc/example/mini_core.rs
@@ -52,24 +52,24 @@ impl<T: ?Sized> LegacyReceiver for &mut T {}
 impl<T: ?Sized, A: Allocator> LegacyReceiver for Box<T, A> {}
 
 #[lang = "copy"]
-pub unsafe trait Copy {}
-
-unsafe impl Copy for bool {}
-unsafe impl Copy for u8 {}
-unsafe impl Copy for u16 {}
-unsafe impl Copy for u32 {}
-unsafe impl Copy for u64 {}
-unsafe impl Copy for usize {}
-unsafe impl Copy for i8 {}
-unsafe impl Copy for i16 {}
-unsafe impl Copy for i32 {}
-unsafe impl Copy for isize {}
-unsafe impl Copy for f32 {}
-unsafe impl Copy for f64 {}
-unsafe impl Copy for char {}
-unsafe impl<'a, T: ?Sized> Copy for &'a T {}
-unsafe impl<T: ?Sized> Copy for *const T {}
-unsafe impl<T: ?Sized> Copy for *mut T {}
+pub trait Copy {}
+
+impl Copy for bool {}
+impl Copy for u8 {}
+impl Copy for u16 {}
+impl Copy for u32 {}
+impl Copy for u64 {}
+impl Copy for usize {}
+impl Copy for i8 {}
+impl Copy for i16 {}
+impl Copy for i32 {}
+impl Copy for isize {}
+impl Copy for f32 {}
+impl Copy for f64 {}
+impl Copy for char {}
+impl<'a, T: ?Sized> Copy for &'a T {}
+impl<T: ?Sized> Copy for *const T {}
+impl<T: ?Sized> Copy for *mut T {}
 
 #[lang = "sync"]
 pub unsafe trait Sync {}
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index f8b3ba79c0d59..35d18d0206dbb 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -2745,6 +2745,15 @@ fn add_upstream_rust_crates(
         .find(|(ty, _)| *ty == crate_type)
         .expect("failed to find crate type in dependency format list");
 
+    if sess.target.is_like_aix {
+        // Unlike ELF linkers, AIX doesn't feature `DT_SONAME` to override
+        // the dependency name when outputing a shared library. Thus, `ld` will
+        // use the full path to shared libraries as the dependency if passed it
+        // by default unless `noipath` is passed.
+        // https://www.ibm.com/docs/en/aix/7.3?topic=l-ld-command.
+        cmd.link_or_cc_arg("-bnoipath");
+    }
+
     for &cnum in &codegen_results.crate_info.used_crates {
         // We may not pass all crates through to the linker. Some crates may appear statically in
         // an existing dylib, meaning we'll pick up all the symbols from the dylib.
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 3b49bc41ffe43..2eea65125b038 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -103,7 +103,7 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran
     }
 
     let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did);
-    match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) {
+    match type_allowed_to_implement_copy(tcx, param_env, self_type, cause, impl_header.safety) {
         Ok(()) => Ok(()),
         Err(CopyImplementationError::InfringingFields(fields)) => {
             let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
@@ -123,6 +123,12 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran
             let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
             Err(tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span }))
         }
+        Err(CopyImplementationError::HasUnsafeFields) => {
+            let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
+            Err(tcx
+                .dcx()
+                .span_delayed_bug(span, format!("cannot implement `Copy` for `{}`", self_type)))
+        }
     }
 }
 
diff --git a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs
index d66114a50d78b..86839e4033034 100644
--- a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs
@@ -3,7 +3,7 @@
 
 use rustc_errors::codes::*;
 use rustc_errors::struct_span_code_err;
-use rustc_hir::Safety;
+use rustc_hir::{LangItem, Safety};
 use rustc_middle::ty::ImplPolarity::*;
 use rustc_middle::ty::print::PrintTraitRefExt as _;
 use rustc_middle::ty::{ImplTraitHeader, TraitDef, TyCtxt};
@@ -20,7 +20,19 @@ pub(super) fn check_item(
         tcx.generics_of(def_id).own_params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle");
     let trait_ref = trait_header.trait_ref.instantiate_identity();
 
-    match (trait_def.safety, unsafe_attr, trait_header.safety, trait_header.polarity) {
+    let is_copy = tcx.is_lang_item(trait_def.def_id, LangItem::Copy);
+    let trait_def_safety = if is_copy {
+        // If `Self` has unsafe fields, `Copy` is unsafe to implement.
+        if trait_header.trait_ref.skip_binder().self_ty().has_unsafe_fields() {
+            rustc_hir::Safety::Unsafe
+        } else {
+            rustc_hir::Safety::Safe
+        }
+    } else {
+        trait_def.safety
+    };
+
+    match (trait_def_safety, unsafe_attr, trait_header.safety, trait_header.polarity) {
         (Safety::Safe, None, Safety::Unsafe, Positive | Reservation) => {
             let span = tcx.def_span(def_id);
             return Err(struct_span_code_err!(
@@ -48,12 +60,22 @@ pub(super) fn check_item(
                 "the trait `{}` requires an `unsafe impl` declaration",
                 trait_ref.print_trait_sugared()
             )
-            .with_note(format!(
-                "the trait `{}` enforces invariants that the compiler can't check. \
-                    Review the trait documentation and make sure this implementation \
-                    upholds those invariants before adding the `unsafe` keyword",
-                trait_ref.print_trait_sugared()
-            ))
+            .with_note(if is_copy {
+                format!(
+                    "the trait `{}` cannot be safely implemented for `{}` \
+                        because it has unsafe fields. Review the invariants \
+                        of those fields before adding an `unsafe impl`",
+                    trait_ref.print_trait_sugared(),
+                    trait_ref.self_ty(),
+                )
+            } else {
+                format!(
+                    "the trait `{}` enforces invariants that the compiler can't check. \
+                        Review the trait documentation and make sure this implementation \
+                        upholds those invariants before adding the `unsafe` keyword",
+                    trait_ref.print_trait_sugared()
+                )
+            })
             .with_span_suggestion_verbose(
                 span.shrink_to_lo(),
                 "add `unsafe` to this trait implementation",
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index e715a7f7e1588..9037caf0066fe 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -454,20 +454,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         closure_kind: hir::ClosureKind,
         projection: ty::PolyProjectionPredicate<'tcx>,
     ) -> Option<ExpectedSig<'tcx>> {
-        let tcx = self.tcx;
-
-        let trait_def_id = projection.trait_def_id(tcx);
+        let def_id = projection.projection_def_id();
 
         // For now, we only do signature deduction based off of the `Fn` and `AsyncFn` traits,
         // for closures and async closures, respectively.
         match closure_kind {
-            hir::ClosureKind::Closure
-                if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() =>
-            {
+            hir::ClosureKind::Closure if self.tcx.is_lang_item(def_id, LangItem::FnOnceOutput) => {
                 self.extract_sig_from_projection(cause_span, projection)
             }
             hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async)
-                if self.tcx.async_fn_trait_kind_from_def_id(trait_def_id).is_some() =>
+                if self.tcx.is_lang_item(def_id, LangItem::AsyncFnOnceOutput) =>
             {
                 self.extract_sig_from_projection(cause_span, projection)
             }
@@ -475,7 +471,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // `F: FnOnce() -> Fut, Fut: Future<Output = T>` style bound. Let's still
             // guide inference here, since it's beneficial for the user.
             hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async)
-                if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() =>
+                if self.tcx.is_lang_item(def_id, LangItem::FnOnceOutput) =>
             {
                 self.extract_sig_from_projection_and_future_bound(cause_span, projection)
             }
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index ec08519892203..093cc16fb4c18 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -625,6 +625,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
             cx.param_env,
             ty,
             traits::ObligationCause::misc(item.span, item.owner_id.def_id),
+            hir::Safety::Safe,
         )
         .is_ok()
         {
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 474062218c97a..3fbc23924f59a 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -980,11 +980,7 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
     }
 
     fn has_unsafe_fields(self) -> bool {
-        if let ty::Adt(adt_def, ..) = self.kind() {
-            adt_def.all_fields().any(|x| x.safety == hir::Safety::Unsafe)
-        } else {
-            false
-        }
+        Ty::has_unsafe_fields(self)
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 57054bd1a0b24..b9a45ea3c2c5a 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -1288,6 +1288,15 @@ impl<'tcx> Ty<'tcx> {
         }
     }
 
+    /// Checks whether this type is an ADT that has unsafe fields.
+    pub fn has_unsafe_fields(self) -> bool {
+        if let ty::Adt(adt_def, ..) = self.kind() {
+            adt_def.all_fields().any(|x| x.safety == hir::Safety::Unsafe)
+        } else {
+            false
+        }
+    }
+
     /// Get morphology of the async drop glue, needed for types which do not
     /// use async drop. To get async drop glue morphology for a definition see
     /// [`TyCtxt::async_drop_glue_morphology`]. Used for `AsyncDestruct::Destructor`
diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
index 561229cf7255b..f844e8fbe0304 100644
--- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
@@ -2,19 +2,186 @@
 
 use std::borrow::Cow;
 use std::cell::RefCell;
+use std::ffi::OsString;
+use std::path::PathBuf;
 use std::sync::OnceLock;
 use std::{io, ops, str};
 
 use regex::Regex;
-use rustc_graphviz as dot;
+use rustc_hir::def_id::DefId;
 use rustc_index::bit_set::BitSet;
-use rustc_middle::mir::{self, BasicBlock, Body, Location, graphviz_safe_def_name};
+use rustc_middle::mir::{
+    self, BasicBlock, Body, Location, create_dump_file, dump_enabled, graphviz_safe_def_name,
+    traversal,
+};
+use rustc_middle::ty::TyCtxt;
+use rustc_middle::ty::print::with_no_trimmed_paths;
+use rustc_span::symbol::{Symbol, sym};
+use tracing::debug;
+use {rustc_ast as ast, rustc_graphviz as dot};
 
 use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext};
 use super::{Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor};
+use crate::errors::{
+    DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
+};
+
+/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
+/// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are
+/// the same.
+pub(super) fn write_graphviz_results<'tcx, A>(
+    tcx: TyCtxt<'tcx>,
+    body: &Body<'tcx>,
+    results: &mut Results<'tcx, A>,
+    pass_name: Option<&'static str>,
+) -> std::io::Result<()>
+where
+    A: Analysis<'tcx>,
+    A::Domain: DebugWithContext<A>,
+{
+    use std::fs;
+    use std::io::Write;
+
+    let def_id = body.source.def_id();
+    let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else {
+        // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
+        return Ok(());
+    };
+
+    let file = try {
+        match attrs.output_path(A::NAME) {
+            Some(path) => {
+                debug!("printing dataflow results for {:?} to {}", def_id, path.display());
+                if let Some(parent) = path.parent() {
+                    fs::create_dir_all(parent)?;
+                }
+                fs::File::create_buffered(&path)?
+            }
+
+            None if dump_enabled(tcx, A::NAME, def_id) => {
+                create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
+            }
+
+            _ => return Ok(()),
+        }
+    };
+    let mut file = match file {
+        Ok(f) => f,
+        Err(e) => return Err(e),
+    };
+
+    let style = match attrs.formatter {
+        Some(sym::two_phase) => OutputStyle::BeforeAndAfter,
+        _ => OutputStyle::AfterOnly,
+    };
+
+    let mut buf = Vec::new();
+
+    let graphviz = Formatter::new(body, results, style);
+    let mut render_opts =
+        vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())];
+    if tcx.sess.opts.unstable_opts.graphviz_dark_mode {
+        render_opts.push(dot::RenderOption::DarkTheme);
+    }
+    let r = with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts));
+
+    let lhs = try {
+        r?;
+        file.write_all(&buf)?;
+    };
+
+    lhs
+}
+
+#[derive(Default)]
+struct RustcMirAttrs {
+    basename_and_suffix: Option<PathBuf>,
+    formatter: Option<Symbol>,
+}
+
+impl RustcMirAttrs {
+    fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result<Self, ()> {
+        let mut result = Ok(());
+        let mut ret = RustcMirAttrs::default();
+
+        let rustc_mir_attrs = tcx
+            .get_attrs(def_id, sym::rustc_mir)
+            .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
+
+        for attr in rustc_mir_attrs {
+            let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) {
+                Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
+                    let path = PathBuf::from(s.to_string());
+                    match path.file_name() {
+                        Some(_) => Ok(path),
+                        None => {
+                            tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() });
+                            Err(())
+                        }
+                    }
+                })
+            } else if attr.has_name(sym::borrowck_graphviz_format) {
+                Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
+                    sym::gen_kill | sym::two_phase => Ok(s),
+                    _ => {
+                        tcx.dcx().emit_err(UnknownFormatter { span: attr.span() });
+                        Err(())
+                    }
+                })
+            } else {
+                Ok(())
+            };
+
+            result = result.and(attr_result);
+        }
+
+        result.map(|()| ret)
+    }
+
+    fn set_field<T>(
+        field: &mut Option<T>,
+        tcx: TyCtxt<'_>,
+        attr: &ast::MetaItemInner,
+        mapper: impl FnOnce(Symbol) -> Result<T, ()>,
+    ) -> Result<(), ()> {
+        if field.is_some() {
+            tcx.dcx()
+                .emit_err(DuplicateValuesFor { span: attr.span(), name: attr.name_or_empty() });
+
+            return Err(());
+        }
+
+        if let Some(s) = attr.value_str() {
+            *field = Some(mapper(s)?);
+            Ok(())
+        } else {
+            tcx.dcx()
+                .emit_err(RequiresAnArgument { span: attr.span(), name: attr.name_or_empty() });
+            Err(())
+        }
+    }
+
+    /// Returns the path where dataflow results should be written, or `None`
+    /// `borrowck_graphviz_postflow` was not specified.
+    ///
+    /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
+    ///
+    /// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
+    fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
+        let mut ret = self.basename_and_suffix.as_ref().cloned()?;
+        let suffix = ret.file_name().unwrap(); // Checked when parsing attrs
+
+        let mut file_name: OsString = analysis_name.into();
+        file_name.push("_");
+        file_name.push(suffix);
+        ret.set_file_name(file_name);
+
+        Some(ret)
+    }
+}
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub(crate) enum OutputStyle {
+enum OutputStyle {
     AfterOnly,
     BeforeAndAfter,
 }
@@ -28,7 +195,7 @@ impl OutputStyle {
     }
 }
 
-pub(crate) struct Formatter<'mir, 'tcx, A>
+struct Formatter<'mir, 'tcx, A>
 where
     A: Analysis<'tcx>,
 {
@@ -45,12 +212,12 @@ impl<'mir, 'tcx, A> Formatter<'mir, 'tcx, A>
 where
     A: Analysis<'tcx>,
 {
-    pub(crate) fn new(
+    fn new(
         body: &'mir Body<'tcx>,
         results: &'mir mut Results<'tcx, A>,
         style: OutputStyle,
     ) -> Self {
-        let reachable = mir::traversal::reachable_as_bitset(body);
+        let reachable = traversal::reachable_as_bitset(body);
         Formatter { cursor: results.as_results_cursor(body).into(), style, reachable }
     }
 
@@ -61,7 +228,7 @@ where
 
 /// A pair of a basic block and an index into that basic blocks `successors`.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub(crate) struct CfgEdge {
+struct CfgEdge {
     source: BasicBlock,
     index: usize,
 }
@@ -520,7 +687,7 @@ struct StateDiffCollector<D> {
 
 impl<D> StateDiffCollector<D> {
     fn run<'tcx, A>(
-        body: &mir::Body<'tcx>,
+        body: &Body<'tcx>,
         block: BasicBlock,
         results: &mut Results<'tcx, A>,
         style: OutputStyle,
diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs
index caff2a81ff306..7f5a68e884eaf 100644
--- a/compiler/rustc_mir_dataflow/src/framework/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs
@@ -42,7 +42,7 @@ use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, Terminator
 use rustc_middle::ty::TyCtxt;
 use tracing::error;
 
-use self::results::write_graphviz_results;
+use self::graphviz::write_graphviz_results;
 use super::fmt::DebugWithContext;
 
 mod cursor;
diff --git a/compiler/rustc_mir_dataflow/src/framework/results.rs b/compiler/rustc_mir_dataflow/src/framework/results.rs
index c4321c454e6ef..a7dbd99b8abde 100644
--- a/compiler/rustc_mir_dataflow/src/framework/results.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/results.rs
@@ -1,22 +1,9 @@
 //! Dataflow analysis results.
 
-use std::ffi::OsString;
-use std::path::PathBuf;
-
-use rustc_hir::def_id::DefId;
 use rustc_index::IndexVec;
-use rustc_middle::mir::{self, BasicBlock, create_dump_file, dump_enabled, traversal};
-use rustc_middle::ty::TyCtxt;
-use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_span::symbol::{Symbol, sym};
-use tracing::debug;
-use {rustc_ast as ast, rustc_graphviz as dot};
+use rustc_middle::mir::{BasicBlock, Body, traversal};
 
-use super::fmt::DebugWithContext;
-use super::{Analysis, ResultsCursor, ResultsVisitor, graphviz, visit_results};
-use crate::errors::{
-    DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
-};
+use super::{Analysis, ResultsCursor, ResultsVisitor, visit_results};
 use crate::framework::cursor::ResultsHandle;
 
 pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>;
@@ -41,16 +28,13 @@ where
     /// `Results` is also used outside the cursor.
     pub fn as_results_cursor<'mir>(
         &'mir mut self,
-        body: &'mir mir::Body<'tcx>,
+        body: &'mir Body<'tcx>,
     ) -> ResultsCursor<'mir, 'tcx, A> {
         ResultsCursor::new(body, ResultsHandle::BorrowedMut(self))
     }
 
     /// Creates a `ResultsCursor` that takes ownership of the `Results`.
-    pub fn into_results_cursor<'mir>(
-        self,
-        body: &'mir mir::Body<'tcx>,
-    ) -> ResultsCursor<'mir, 'tcx, A> {
+    pub fn into_results_cursor<'mir>(self, body: &'mir Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
         ResultsCursor::new(body, ResultsHandle::Owned(self))
     }
 
@@ -61,7 +45,7 @@ where
 
     pub fn visit_with<'mir>(
         &mut self,
-        body: &'mir mir::Body<'tcx>,
+        body: &'mir Body<'tcx>,
         blocks: impl IntoIterator<Item = BasicBlock>,
         vis: &mut impl ResultsVisitor<'mir, 'tcx, A>,
     ) {
@@ -70,166 +54,10 @@ where
 
     pub fn visit_reachable_with<'mir>(
         &mut self,
-        body: &'mir mir::Body<'tcx>,
+        body: &'mir Body<'tcx>,
         vis: &mut impl ResultsVisitor<'mir, 'tcx, A>,
     ) {
         let blocks = traversal::reachable(body);
         visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
     }
 }
-
-// Graphviz
-
-/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
-/// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are
-/// the same.
-pub(super) fn write_graphviz_results<'tcx, A>(
-    tcx: TyCtxt<'tcx>,
-    body: &mir::Body<'tcx>,
-    results: &mut Results<'tcx, A>,
-    pass_name: Option<&'static str>,
-) -> std::io::Result<()>
-where
-    A: Analysis<'tcx>,
-    A::Domain: DebugWithContext<A>,
-{
-    use std::fs;
-    use std::io::Write;
-
-    let def_id = body.source.def_id();
-    let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else {
-        // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
-        return Ok(());
-    };
-
-    let file = try {
-        match attrs.output_path(A::NAME) {
-            Some(path) => {
-                debug!("printing dataflow results for {:?} to {}", def_id, path.display());
-                if let Some(parent) = path.parent() {
-                    fs::create_dir_all(parent)?;
-                }
-                fs::File::create_buffered(&path)?
-            }
-
-            None if dump_enabled(tcx, A::NAME, def_id) => {
-                create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
-            }
-
-            _ => return Ok(()),
-        }
-    };
-    let mut file = match file {
-        Ok(f) => f,
-        Err(e) => return Err(e),
-    };
-
-    let style = match attrs.formatter {
-        Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter,
-        _ => graphviz::OutputStyle::AfterOnly,
-    };
-
-    let mut buf = Vec::new();
-
-    let graphviz = graphviz::Formatter::new(body, results, style);
-    let mut render_opts =
-        vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())];
-    if tcx.sess.opts.unstable_opts.graphviz_dark_mode {
-        render_opts.push(dot::RenderOption::DarkTheme);
-    }
-    let r = with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts));
-
-    let lhs = try {
-        r?;
-        file.write_all(&buf)?;
-    };
-
-    lhs
-}
-
-#[derive(Default)]
-struct RustcMirAttrs {
-    basename_and_suffix: Option<PathBuf>,
-    formatter: Option<Symbol>,
-}
-
-impl RustcMirAttrs {
-    fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result<Self, ()> {
-        let mut result = Ok(());
-        let mut ret = RustcMirAttrs::default();
-
-        let rustc_mir_attrs = tcx
-            .get_attrs(def_id, sym::rustc_mir)
-            .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
-
-        for attr in rustc_mir_attrs {
-            let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) {
-                Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
-                    let path = PathBuf::from(s.to_string());
-                    match path.file_name() {
-                        Some(_) => Ok(path),
-                        None => {
-                            tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() });
-                            Err(())
-                        }
-                    }
-                })
-            } else if attr.has_name(sym::borrowck_graphviz_format) {
-                Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
-                    sym::gen_kill | sym::two_phase => Ok(s),
-                    _ => {
-                        tcx.dcx().emit_err(UnknownFormatter { span: attr.span() });
-                        Err(())
-                    }
-                })
-            } else {
-                Ok(())
-            };
-
-            result = result.and(attr_result);
-        }
-
-        result.map(|()| ret)
-    }
-
-    fn set_field<T>(
-        field: &mut Option<T>,
-        tcx: TyCtxt<'_>,
-        attr: &ast::MetaItemInner,
-        mapper: impl FnOnce(Symbol) -> Result<T, ()>,
-    ) -> Result<(), ()> {
-        if field.is_some() {
-            tcx.dcx()
-                .emit_err(DuplicateValuesFor { span: attr.span(), name: attr.name_or_empty() });
-
-            return Err(());
-        }
-
-        if let Some(s) = attr.value_str() {
-            *field = Some(mapper(s)?);
-            Ok(())
-        } else {
-            tcx.dcx()
-                .emit_err(RequiresAnArgument { span: attr.span(), name: attr.name_or_empty() });
-            Err(())
-        }
-    }
-
-    /// Returns the path where dataflow results should be written, or `None`
-    /// `borrowck_graphviz_postflow` was not specified.
-    ///
-    /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
-    ///
-    /// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
-    fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
-        let mut ret = self.basename_and_suffix.as_ref().cloned()?;
-        let suffix = ret.file_name().unwrap(); // Checked when parsing attrs
-
-        let mut file_name: OsString = analysis_name.into();
-        file_name.push("_");
-        file_name.push(suffix);
-        ret.set_file_name(file_name);
-
-        Some(ret)
-    }
-}
diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs
index b9e194b00c54e..d69a8019c8d0e 100644
--- a/compiler/rustc_mir_dataflow/src/impls/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs
@@ -1,7 +1,3 @@
-//! Dataflow analyses are built upon some interpretation of the
-//! bitvectors attached to each basic block, represented via a
-//! zero-sized structure.
-
 mod borrowed_locals;
 mod initialized;
 mod liveness;
diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs
index fec149c8c4324..062d56a79a0b4 100644
--- a/compiler/rustc_passes/src/debugger_visualizer.rs
+++ b/compiler/rustc_passes/src/debugger_visualizer.rs
@@ -1,7 +1,6 @@
 //! Detecting usage of the `#[debugger_visualizer]` attribute.
 
 use rustc_ast::Attribute;
-use rustc_data_structures::sync::Lrc;
 use rustc_expand::base::resolve_path;
 use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType};
 use rustc_middle::query::{LocalCrate, Providers};
@@ -49,10 +48,10 @@ impl DebuggerVisualizerCollector<'_> {
                 }
             };
 
-            match std::fs::read(&file) {
-                Ok(contents) => {
+            match self.sess.source_map().load_binary_file(&file) {
+                Ok((source, _)) => {
                     self.visualizers.push(DebuggerVisualizerFile::new(
-                        Lrc::from(contents),
+                        source,
                         visualizer_type,
                         file,
                     ));
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index 3b17fa6b03285..d216ae7291337 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -18,6 +18,7 @@ pub enum CopyImplementationError<'tcx> {
     InfringingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
     NotAnAdt,
     HasDestructor,
+    HasUnsafeFields,
 }
 
 pub enum ConstParamTyImplementationError<'tcx> {
@@ -39,11 +40,16 @@ pub enum InfringingFieldsReason<'tcx> {
 ///
 /// If it's not an ADT, int ty, `bool`, float ty, `char`, raw pointer, `!`,
 /// a reference or an array returns `Err(NotAnAdt)`.
+///
+/// If the impl is `Safe`, `self_type` must not have unsafe fields. When used to
+/// generate suggestions in lints, `Safe` should be supplied so as to not
+/// suggest implementing `Copy` for types with unsafe fields.
 pub fn type_allowed_to_implement_copy<'tcx>(
     tcx: TyCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     self_type: Ty<'tcx>,
     parent_cause: ObligationCause<'tcx>,
+    impl_safety: hir::Safety,
 ) -> Result<(), CopyImplementationError<'tcx>> {
     let (adt, args) = match self_type.kind() {
         // These types used to have a builtin impl.
@@ -78,6 +84,10 @@ pub fn type_allowed_to_implement_copy<'tcx>(
         return Err(CopyImplementationError::HasDestructor);
     }
 
+    if impl_safety == hir::Safety::Safe && self_type.has_unsafe_fields() {
+        return Err(CopyImplementationError::HasUnsafeFields);
+    }
+
     Ok(())
 }
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 3e2c8467d3220..5e27fd4378976 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -795,8 +795,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 | ty::Never
                 | ty::Tuple(_)
                 | ty::CoroutineWitness(..) => {
-                    use rustc_type_ir::inherent::*;
-
                     // Only consider auto impls of unsafe traits when there are
                     // no unsafe fields.
                     if self.tcx().trait_is_unsafe(def_id) && self_ty.has_unsafe_fields() {
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 4419b11ac1971..93f8091299fa4 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -2261,7 +2261,7 @@ pub fn stream_cargo(
             Ok(msg) => {
                 if builder.config.json_output {
                     // Forward JSON to stdout.
-                    eprintln!("{line}");
+                    println!("{line}");
                 }
                 cb(msg)
             }
diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs
index 0ce86eadbce67..b6862c2d5c4a9 100644
--- a/src/bootstrap/src/core/build_steps/install.rs
+++ b/src/bootstrap/src/core/build_steps/install.rs
@@ -21,9 +21,9 @@ const SHELL: &str = "sh";
 
 /// We have to run a few shell scripts, which choke quite a bit on both `\`
 /// characters and on `C:\` paths, so normalize both of them away.
-fn sanitize_sh(path: &Path) -> String {
+fn sanitize_sh(path: &Path, is_cygwin: bool) -> String {
     let path = path.to_str().unwrap().replace('\\', "/");
-    return change_drive(unc_to_lfs(&path)).unwrap_or(path);
+    return if is_cygwin { path } else { change_drive(unc_to_lfs(&path)).unwrap_or(path) };
 
     fn unc_to_lfs(s: &str) -> &str {
         s.strip_prefix("//?/").unwrap_or(s)
@@ -71,6 +71,7 @@ fn install_sh(
     let prefix = default_path(&builder.config.prefix, "/usr/local");
     let sysconfdir = prefix.join(default_path(&builder.config.sysconfdir, "/etc"));
     let destdir_env = env::var_os("DESTDIR").map(PathBuf::from);
+    let is_cygwin = builder.config.build.is_cygwin();
 
     // Sanity checks on the write access of user.
     //
@@ -103,14 +104,14 @@ fn install_sh(
 
     let mut cmd = command(SHELL);
     cmd.current_dir(&empty_dir)
-        .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh")))
-        .arg(format!("--prefix={}", prepare_dir(&destdir_env, prefix)))
-        .arg(format!("--sysconfdir={}", prepare_dir(&destdir_env, sysconfdir)))
-        .arg(format!("--datadir={}", prepare_dir(&destdir_env, datadir)))
-        .arg(format!("--docdir={}", prepare_dir(&destdir_env, docdir)))
-        .arg(format!("--bindir={}", prepare_dir(&destdir_env, bindir)))
-        .arg(format!("--libdir={}", prepare_dir(&destdir_env, libdir)))
-        .arg(format!("--mandir={}", prepare_dir(&destdir_env, mandir)))
+        .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh"), is_cygwin))
+        .arg(format!("--prefix={}", prepare_dir(&destdir_env, prefix, is_cygwin)))
+        .arg(format!("--sysconfdir={}", prepare_dir(&destdir_env, sysconfdir, is_cygwin)))
+        .arg(format!("--datadir={}", prepare_dir(&destdir_env, datadir, is_cygwin)))
+        .arg(format!("--docdir={}", prepare_dir(&destdir_env, docdir, is_cygwin)))
+        .arg(format!("--bindir={}", prepare_dir(&destdir_env, bindir, is_cygwin)))
+        .arg(format!("--libdir={}", prepare_dir(&destdir_env, libdir, is_cygwin)))
+        .arg(format!("--mandir={}", prepare_dir(&destdir_env, mandir, is_cygwin)))
         .arg("--disable-ldconfig");
     cmd.run(builder);
     t!(fs::remove_dir_all(&empty_dir));
@@ -120,7 +121,7 @@ fn default_path(config: &Option<PathBuf>, default: &str) -> PathBuf {
     config.as_ref().cloned().unwrap_or_else(|| PathBuf::from(default))
 }
 
-fn prepare_dir(destdir_env: &Option<PathBuf>, mut path: PathBuf) -> String {
+fn prepare_dir(destdir_env: &Option<PathBuf>, mut path: PathBuf, is_cygwin: bool) -> String {
     // The DESTDIR environment variable is a standard way to install software in a subdirectory
     // while keeping the original directory structure, even if the prefix or other directories
     // contain absolute paths.
@@ -146,7 +147,7 @@ fn prepare_dir(destdir_env: &Option<PathBuf>, mut path: PathBuf) -> String {
         assert!(path.is_absolute(), "could not make the path relative");
     }
 
-    sanitize_sh(&path)
+    sanitize_sh(&path, is_cygwin)
 }
 
 macro_rules! install {
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index b8f9be26bf23c..002b990bb5280 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -565,6 +565,12 @@ impl TargetSelection {
         self.ends_with("windows-gnu")
     }
 
+    pub fn is_cygwin(&self) -> bool {
+        self.is_windows() &&
+        // ref. https://cygwin.com/pipermail/cygwin/2022-February/250802.html
+        env::var("OSTYPE").is_ok_and(|v| v.to_lowercase().contains("cygwin"))
+    }
+
     /// Path to the file defining the custom target, if any.
     pub fn filepath(&self) -> Option<&Path> {
         self.file.as_ref().map(Path::new)
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index cd90d2f90f77b..6f3f371a68d6b 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -200,6 +200,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                                 cx.param_env,
                                 ty,
                                 traits::ObligationCause::dummy_with_span(span),
+                                rustc_hir::Safety::Safe,
                             )
                             .is_ok()
                             {
diff --git a/src/tools/jsondocck/src/cache.rs b/src/tools/jsondocck/src/cache.rs
index 9f95f9fb408e7..47512039740b2 100644
--- a/src/tools/jsondocck/src/cache.rs
+++ b/src/tools/jsondocck/src/cache.rs
@@ -28,7 +28,8 @@ impl Cache {
         }
     }
 
-    pub fn value(&self) -> &Value {
-        &self.value
+    // FIXME: Make this failible, so jsonpath syntax error has line number.
+    pub fn select(&self, path: &str) -> Vec<&Value> {
+        jsonpath_lib::select(&self.value, path).unwrap()
     }
 }
diff --git a/src/tools/jsondocck/src/error.rs b/src/tools/jsondocck/src/error.rs
index c4cd79a64fda1..0a3e085b405ba 100644
--- a/src/tools/jsondocck/src/error.rs
+++ b/src/tools/jsondocck/src/error.rs
@@ -1,29 +1,7 @@
-use std::error::Error;
-use std::fmt;
-
 use crate::Command;
 
 #[derive(Debug)]
-pub enum CkError {
-    /// A check failed. File didn't exist or failed to match the command
-    FailedCheck(String, Command),
-    /// An error triggered by some other error
-    Induced(Box<dyn Error>),
-}
-
-impl fmt::Display for CkError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            CkError::FailedCheck(msg, cmd) => {
-                write!(f, "Failed check: {} on line {}", msg, cmd.lineno)
-            }
-            CkError::Induced(err) => write!(f, "Check failed: {}", err),
-        }
-    }
-}
-
-impl<T: Error + 'static> From<T> for CkError {
-    fn from(err: T) -> CkError {
-        CkError::Induced(Box::new(err))
-    }
+pub struct CkError {
+    pub message: String,
+    pub command: Command,
 }
diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs
index c08bbbc769a0e..b6a1d7dfa7a3a 100644
--- a/src/tools/jsondocck/src/main.rs
+++ b/src/tools/jsondocck/src/main.rs
@@ -1,8 +1,8 @@
 use std::borrow::Cow;
+use std::process::ExitCode;
 use std::sync::OnceLock;
-use std::{env, fmt, fs};
+use std::{env, fs};
 
-use jsonpath_lib::select;
 use regex::{Regex, RegexBuilder};
 use serde_json::Value;
 
@@ -14,90 +14,134 @@ use cache::Cache;
 use config::parse_config;
 use error::CkError;
 
-fn main() -> Result<(), String> {
+fn main() -> ExitCode {
     let config = parse_config(env::args().collect());
 
     let mut failed = Vec::new();
     let mut cache = Cache::new(&config);
-    let commands = get_commands(&config.template)
-        .map_err(|_| format!("Jsondocck failed for {}", &config.template))?;
+    let Ok(commands) = get_commands(&config.template) else {
+        eprintln!("Jsondocck failed for {}", &config.template);
+        return ExitCode::FAILURE;
+    };
 
     for command in commands {
-        if let Err(e) = check_command(command, &mut cache) {
-            failed.push(e);
+        if let Err(message) = check_command(&command, &mut cache) {
+            failed.push(CkError { command, message });
         }
     }
 
     if failed.is_empty() {
-        Ok(())
+        ExitCode::SUCCESS
     } else {
         for i in failed {
-            eprintln!("{}", i);
+            eprintln!("{}:{}, command failed", config.template, i.command.lineno);
+            eprintln!("{}", i.message)
         }
-        Err(format!("Jsondocck failed for {}", &config.template))
+        ExitCode::FAILURE
     }
 }
 
 #[derive(Debug)]
 pub struct Command {
-    negated: bool,
     kind: CommandKind,
-    args: Vec<String>,
+    path: String,
     lineno: usize,
 }
 
 #[derive(Debug)]
-pub enum CommandKind {
-    Has,
-    Count,
-    Is,
-    IsMany,
-    Set,
+enum CommandKind {
+    /// `//@ has <path>`
+    ///
+    /// Checks the path exists.
+    HasPath,
+
+    /// `//@ has <path> <value>`
+    ///
+    /// Check one thing at the path  is equal to the value.
+    HasValue { value: String },
+
+    /// `//@ !has <path>`
+    ///
+    /// Checks the path doesn't exist.
+    HasNotPath,
+
+    /// `//@ is <path> <value>`
+    ///
+    /// Check the path is the given value.
+    Is { value: String },
+
+    /// `//@ is <path> <value> <value>...`
+    ///
+    /// Check that the path matches to exactly every given value.
+    IsMany { values: Vec<String> },
+
+    /// `//@ !is <path> <value>`
+    ///
+    /// Check the path isn't the given value.
+    IsNot { value: String },
+
+    /// `//@ count <path> <value>`
+    ///
+    /// Check the path has the expected number of matches.
+    CountIs { expected: usize },
+
+    /// `//@ set <name> = <path>`
+    Set { variable: String },
 }
 
 impl CommandKind {
-    fn validate(&self, args: &[String], lineno: usize) -> bool {
-        // FIXME(adotinthevoid): We should "parse, don't validate" here, so we avoid ad-hoc
-        // indexing in check_command.
-        let count = match self {
-            CommandKind::Has => (1..=2).contains(&args.len()),
-            CommandKind::IsMany => args.len() >= 2,
-            CommandKind::Count | CommandKind::Is => 2 == args.len(),
-            CommandKind::Set => 3 == args.len(),
-        };
+    /// Returns both the kind and the path.
+    ///
+    /// Returns `None` if the command isn't from jsondocck (e.g. from compiletest).
+    fn parse<'a>(command_name: &str, negated: bool, args: &'a [String]) -> Option<(Self, &'a str)> {
+        let kind = match (command_name, negated) {
+            ("count", false) => {
+                assert_eq!(args.len(), 2);
+                let expected = args[1].parse().expect("invalid number for `count`");
+                Self::CountIs { expected }
+            }
 
-        if !count {
-            print_err(&format!("Incorrect number of arguments to `{}`", self), lineno);
-            return false;
-        }
+            ("ismany", false) => {
+                // FIXME: Make this >= 3, and migrate len(values)==1 cases to @is
+                assert!(args.len() >= 2, "Not enough args to `ismany`");
+                let values = args[1..].to_owned();
+                Self::IsMany { values }
+            }
 
-        if let CommandKind::Count = self {
-            if args[1].parse::<usize>().is_err() {
-                print_err(
-                    &format!(
-                        "Second argument to `count` must be a valid usize (got `{}`)",
-                        args[1]
-                    ),
-                    lineno,
-                );
-                return false;
+            ("is", false) => {
+                assert_eq!(args.len(), 2);
+                Self::Is { value: args[1].clone() }
+            }
+            ("is", true) => {
+                assert_eq!(args.len(), 2);
+                Self::IsNot { value: args[1].clone() }
             }
-        }
 
-        true
-    }
-}
+            ("set", false) => {
+                assert_eq!(args.len(), 3);
+                assert_eq!(args[1], "=");
+                return Some((Self::Set { variable: args[0].clone() }, &args[2]));
+            }
 
-impl fmt::Display for CommandKind {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let text = match self {
-            CommandKind::Has => "has",
-            CommandKind::IsMany => "ismany",
-            CommandKind::Count => "count",
-            CommandKind::Is => "is",
-            CommandKind::Set => "set",
+            ("has", false) => match args {
+                [_path] => Self::HasPath,
+                [_path, value] => Self::HasValue { value: value.clone() },
+                _ => panic!("`//@ has` must have 2 or 3 arguments, but got {args:?}"),
+            },
+            ("has", true) => {
+                assert_eq!(args.len(), 1, "args={args:?}");
+                Self::HasNotPath
+            }
+
+            (_, false) if KNOWN_DIRECTIVE_NAMES.contains(&command_name) => {
+                return None;
+            }
+            _ => {
+                panic!("Invalid command `//@ {}{command_name}`", if negated { "!" } else { "" })
+            }
         };
-        write!(f, "{}", text)
+
+        Some((kind, &args[0]))
     }
 }
 
@@ -125,8 +169,7 @@ fn print_err(msg: &str, lineno: usize) {
 //        See <https://github.com/rust-lang/rust/issues/125813#issuecomment-2141953780>.
 include!(concat!(env!("CARGO_MANIFEST_DIR"), "/../compiletest/src/directive-list.rs"));
 
-/// Get a list of commands from a file. Does the work of ensuring the commands
-/// are syntactically valid.
+/// Get a list of commands from a file.
 fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
     let mut commands = Vec::new();
     let mut errors = false;
@@ -142,217 +185,102 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
 
         let negated = cap.name("negated").unwrap().as_str() == "!";
 
-        let cmd = match cap.name("cmd").unwrap().as_str() {
-            "has" => CommandKind::Has,
-            "count" => CommandKind::Count,
-            "is" => CommandKind::Is,
-            "ismany" => CommandKind::IsMany,
-            "set" => CommandKind::Set,
-            // FIXME: See the comment above the `include!(...)`.
-            cmd if KNOWN_DIRECTIVE_NAMES.contains(&cmd) => continue,
-            cmd => {
-                print_err(&format!("Unrecognized command name `{cmd}`"), lineno);
-                errors = true;
-                continue;
-            }
-        };
-
-        let args = cap.name("args").map_or(Some(vec![]), |m| shlex::split(m.as_str()));
-
-        let args = match args {
+        let args_str = &cap["args"];
+        let args = match shlex::split(args_str) {
             Some(args) => args,
             None => {
-                print_err(
-                    &format!(
-                        "Invalid arguments to shlex::split: `{}`",
-                        cap.name("args").unwrap().as_str()
-                    ),
-                    lineno,
-                );
+                print_err(&format!("Invalid arguments to shlex::split: `{args_str}`",), lineno);
                 errors = true;
                 continue;
             }
         };
 
-        if !cmd.validate(&args, lineno) {
-            errors = true;
-            continue;
+        if let Some((kind, path)) = CommandKind::parse(&cap["cmd"], negated, &args) {
+            commands.push(Command { kind, lineno, path: path.to_owned() })
         }
-
-        commands.push(Command { negated, kind: cmd, args, lineno })
     }
 
     if !errors { Ok(commands) } else { Err(()) }
 }
 
-/// Performs the actual work of ensuring a command passes. Generally assumes the command
-/// is syntactically valid.
-fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
-    // FIXME: Be more granular about why, (e.g. syntax error, count not equal)
-    let result = match command.kind {
-        CommandKind::Has => {
-            match command.args.len() {
-                // `has <jsonpath>`: Check that `jsonpath` exists.
-                1 => {
-                    let val = cache.value();
-                    let results = select(val, &command.args[0]).unwrap();
-                    !results.is_empty()
-                }
-                // `has <jsonpath> <value>`: Check *any* item matched by `jsonpath` equals `value`.
-                2 => {
-                    let val = cache.value().clone();
-                    let results = select(&val, &command.args[0]).unwrap();
-                    let pat = string_to_value(&command.args[1], cache);
-                    let has = results.contains(&pat.as_ref());
-                    // Give better error for when `has` check fails.
-                    if !command.negated && !has {
-                        return Err(CkError::FailedCheck(
-                            format!(
-                                "{} matched to {:?} but didn't have {:?}",
-                                &command.args[0],
-                                results,
-                                pat.as_ref()
-                            ),
-                            command,
-                        ));
-                    } else {
-                        has
-                    }
-                }
-                _ => unreachable!(),
+/// Performs the actual work of ensuring a command passes.
+fn check_command(command: &Command, cache: &mut Cache) -> Result<(), String> {
+    let matches = cache.select(&command.path);
+    match &command.kind {
+        CommandKind::HasPath => {
+            if matches.is_empty() {
+                return Err("matched to no values".to_owned());
+            }
+        }
+        CommandKind::HasNotPath => {
+            if !matches.is_empty() {
+                return Err(format!("matched to {matches:?}, but wanted no matches"));
+            }
+        }
+        CommandKind::HasValue { value } => {
+            let want_value = string_to_value(value, cache);
+            if !matches.contains(&want_value.as_ref()) {
+                return Err(format!("matched to {matches:?}, which didn't contain {want_value:?}"));
+            }
+        }
+        CommandKind::Is { value } => {
+            let want_value = string_to_value(value, cache);
+            let matched = get_one(&matches)?;
+            if matched != want_value.as_ref() {
+                return Err(format!("matched to {matched:?} but want {want_value:?}"));
+            }
+        }
+        CommandKind::IsNot { value } => {
+            let wantnt_value = string_to_value(value, cache);
+            let matched = get_one(&matches)?;
+            if matched == wantnt_value.as_ref() {
+                return Err(format!("got value {wantnt_value:?}, but want anything else"));
             }
         }
-        // `ismany <path> <jsonpath> <value...>`
-        CommandKind::IsMany => {
-            assert!(!command.negated, "`ismany` may not be negated");
-            let (query, values) = if let [query, values @ ..] = &command.args[..] {
-                (query, values)
-            } else {
-                unreachable!("Checked in CommandKind::validate")
-            };
-            let val = cache.value();
-            let got_values = select(val, &query).unwrap();
 
+        CommandKind::IsMany { values } => {
             // Serde json doesn't implement Ord or Hash for Value, so we must
             // use a Vec here. While in theory that makes setwize equality
             // O(n^2), in practice n will never be large enough to matter.
             let expected_values =
                 values.iter().map(|v| string_to_value(v, cache)).collect::<Vec<_>>();
-            if expected_values.len() != got_values.len() {
-                return Err(CkError::FailedCheck(
-                    format!(
-                        "Expected {} values, but `{}` matched to {} values ({:?})",
-                        expected_values.len(),
-                        query,
-                        got_values.len(),
-                        got_values
-                    ),
-                    command,
+            if expected_values.len() != matches.len() {
+                return Err(format!(
+                    "Expected {} values, but matched to {} values ({:?})",
+                    expected_values.len(),
+                    matches.len(),
+                    matches
                 ));
             };
-            for got_value in got_values {
+            for got_value in matches {
                 if !expected_values.iter().any(|exp| &**exp == got_value) {
-                    return Err(CkError::FailedCheck(
-                        format!("`{}` has match {:?}, which was not expected", query, got_value),
-                        command,
-                    ));
+                    return Err(format!("has match {got_value:?}, which was not expected",));
                 }
             }
-            true
-        }
-        // `count <jsonpath> <count>`: Check that `jsonpath` matches exactly `count` times.
-        CommandKind::Count => {
-            assert_eq!(command.args.len(), 2);
-            let expected: usize = command.args[1].parse().unwrap();
-            let val = cache.value();
-            let results = select(val, &command.args[0]).unwrap();
-            let eq = results.len() == expected;
-            if !command.negated && !eq {
-                return Err(CkError::FailedCheck(
-                    format!(
-                        "`{}` matched to `{:?}` with length {}, but expected length {}",
-                        &command.args[0],
-                        results,
-                        results.len(),
-                        expected
-                    ),
-                    command,
-                ));
-            } else {
-                eq
-            }
         }
-        // `has <jsonpath> <value>`: Check` *exactly one* item matched by `jsonpath`, and it equals `value`.
-        CommandKind::Is => {
-            assert_eq!(command.args.len(), 2);
-            let val = cache.value().clone();
-            let results = select(&val, &command.args[0]).unwrap();
-            let pat = string_to_value(&command.args[1], cache);
-            let is = results.len() == 1 && results[0] == pat.as_ref();
-            if !command.negated && !is {
-                return Err(CkError::FailedCheck(
-                    format!(
-                        "{} matched to {:?}, but expected {:?}",
-                        &command.args[0],
-                        results,
-                        pat.as_ref()
-                    ),
-                    command,
+        CommandKind::CountIs { expected } => {
+            if *expected != matches.len() {
+                return Err(format!(
+                    "matched to `{matches:?}` with length {}, but expected length {expected}",
+                    matches.len(),
                 ));
-            } else {
-                is
             }
         }
-        // `set <name> = <jsonpath>`
-        CommandKind::Set => {
-            assert!(!command.negated, "`set` may not be negated");
-            assert_eq!(command.args.len(), 3);
-            assert_eq!(command.args[1], "=", "Expected an `=`");
-            let val = cache.value().clone();
-            let results = select(&val, &command.args[2]).unwrap();
-            assert_eq!(
-                results.len(),
-                1,
-                "Expected 1 match for `{}` (because of `set`): matched to {:?}",
-                command.args[2],
-                results
-            );
-            match results.len() {
-                0 => false,
-                1 => {
-                    let r = cache.variables.insert(command.args[0].clone(), results[0].clone());
-                    assert!(r.is_none(), "Name collision: {} is duplicated", command.args[0]);
-                    true
-                }
-                _ => {
-                    panic!(
-                        "Got multiple results in `set` for `{}`: {:?}",
-                        &command.args[2], results,
-                    );
-                }
-            }
+        CommandKind::Set { variable } => {
+            let value = get_one(&matches)?;
+            let r = cache.variables.insert(variable.to_owned(), value.clone());
+            assert!(r.is_none(), "name collision: {variable:?} is duplicated");
         }
-    };
+    }
 
-    if result == command.negated {
-        if command.negated {
-            Err(CkError::FailedCheck(
-                format!("`!{} {}` matched when it shouldn't", command.kind, command.args.join(" ")),
-                command,
-            ))
-        } else {
-            // FIXME: In the future, try 'peeling back' each step, and see at what level the match failed
-            Err(CkError::FailedCheck(
-                format!(
-                    "`{} {}` didn't match when it should",
-                    command.kind,
-                    command.args.join(" ")
-                ),
-                command,
-            ))
-        }
-    } else {
-        Ok(())
+    Ok(())
+}
+
+fn get_one<'a>(matches: &[&'a Value]) -> Result<&'a Value, String> {
+    match matches {
+        [] => Err("matched to no values".to_owned()),
+        [matched] => Ok(matched),
+        _ => Err(format!("matched to multiple values {matches:?}, but want exactly 1")),
     }
 }
 
diff --git a/tests/run-make/libs-through-symlinks/Makefile b/tests/run-make/libs-through-symlinks/Makefile
index 592eae663a49a..c6ff566a0e86e 100644
--- a/tests/run-make/libs-through-symlinks/Makefile
+++ b/tests/run-make/libs-through-symlinks/Makefile
@@ -3,10 +3,20 @@ include ../tools.mk
 
 # ignore-windows
 
+# The option -n for the AIX ln command has a different purpose than it does
+# on Linux. On Linux, the -n option is used to treat the destination path as
+# normal file if it is a symbolic link to a directory, which is the default
+# behavior of the AIX ln command.
+ifeq ($(UNAME),AIX)
+LN_FLAGS := -sf
+else
+LN_FLAGS := -nsf
+endif
+
 NAME := $(shell $(RUSTC) --print file-names foo.rs)
 
 all:
 	mkdir -p $(TMPDIR)/outdir
 	$(RUSTC) foo.rs -o $(TMPDIR)/outdir/$(NAME)
-	ln -nsf outdir/$(NAME) $(TMPDIR)
+	ln $(LN_FLAGS) outdir/$(NAME) $(TMPDIR)
 	RUSTC_LOG=rustc_metadata::loader $(RUSTC) bar.rs
diff --git a/tests/ui/async-await/async-closures/call-once-deduction.rs b/tests/ui/async-await/async-closures/call-once-deduction.rs
new file mode 100644
index 0000000000000..41d92bc3d786c
--- /dev/null
+++ b/tests/ui/async-await/async-closures/call-once-deduction.rs
@@ -0,0 +1,14 @@
+//@ edition: 2021
+//@ check-pass
+
+#![feature(async_closure, async_fn_traits, unboxed_closures)]
+
+fn bar<F, O>(_: F)
+where
+    F: AsyncFnOnce<(), CallOnceFuture = O>,
+{
+}
+
+fn main() {
+    bar(async move || {});
+}
diff --git a/tests/ui/runtime/on-broken-pipe/auxiliary/assert-sigpipe-disposition.rs b/tests/ui/runtime/on-broken-pipe/auxiliary/assert-sigpipe-disposition.rs
index 117f6134b4e03..229408fb72474 100644
--- a/tests/ui/runtime/on-broken-pipe/auxiliary/assert-sigpipe-disposition.rs
+++ b/tests/ui/runtime/on-broken-pipe/auxiliary/assert-sigpipe-disposition.rs
@@ -25,7 +25,14 @@ fn start(argc: isize, argv: *const *const u8) -> isize {
     let actual = unsafe {
         let mut actual: libc::sigaction = std::mem::zeroed();
         libc::sigaction(libc::SIGPIPE, std::ptr::null(), &mut actual);
-        actual.sa_sigaction
+        #[cfg(not(target_os = "aix"))]
+        {
+            actual.sa_sigaction
+        }
+        #[cfg(target_os = "aix")]
+        {
+            actual.sa_union.__su_sigaction as libc::sighandler_t
+        }
     };
 
     assert_eq!(actual, expected, "actual and expected SIGPIPE disposition in child differs");
diff --git a/tests/ui/runtime/on-broken-pipe/auxiliary/sigpipe-utils.rs b/tests/ui/runtime/on-broken-pipe/auxiliary/sigpipe-utils.rs
index 3d93d50ca3fbb..d16a2b4d8c8ab 100644
--- a/tests/ui/runtime/on-broken-pipe/auxiliary/sigpipe-utils.rs
+++ b/tests/ui/runtime/on-broken-pipe/auxiliary/sigpipe-utils.rs
@@ -20,7 +20,14 @@ pub fn assert_sigpipe_handler(expected_handler: SignalHandler) {
         let actual = unsafe {
             let mut actual: libc::sigaction = std::mem::zeroed();
             libc::sigaction(libc::SIGPIPE, std::ptr::null(), &mut actual);
-            actual.sa_sigaction
+            #[cfg(not(target_os = "aix"))]
+            {
+                actual.sa_sigaction
+            }
+            #[cfg(target_os = "aix")]
+            {
+                actual.sa_union.__su_sigaction as libc::sighandler_t
+            }
         };
 
         let expected = match expected_handler {
diff --git a/tests/ui/runtime/signal-alternate-stack-cleanup.rs b/tests/ui/runtime/signal-alternate-stack-cleanup.rs
index f2af86be0a5f5..8fce092827318 100644
--- a/tests/ui/runtime/signal-alternate-stack-cleanup.rs
+++ b/tests/ui/runtime/signal-alternate-stack-cleanup.rs
@@ -29,7 +29,14 @@ fn main() {
         // Install signal handler that runs on alternate signal stack.
         let mut action: sigaction = std::mem::zeroed();
         action.sa_flags = (SA_ONSTACK | SA_SIGINFO) as _;
-        action.sa_sigaction = signal_handler as sighandler_t;
+        #[cfg(not(target_os = "aix"))]
+        {
+            action.sa_sigaction = signal_handler as sighandler_t;
+        }
+        #[cfg(target_os = "aix")]
+        {
+            action.sa_union.__su_sigaction = signal_handler as sighandler_t;
+        }
         sigaction(SIGWINCH, &action, std::ptr::null_mut());
 
         // Send SIGWINCH on exit.
diff --git a/tests/ui/unsafe-fields/copy-trait.rs b/tests/ui/unsafe-fields/copy-trait.rs
new file mode 100644
index 0000000000000..fb09ed02e3ffe
--- /dev/null
+++ b/tests/ui/unsafe-fields/copy-trait.rs
@@ -0,0 +1,41 @@
+//@ compile-flags: --crate-type=lib
+
+#![feature(unsafe_fields)]
+#![allow(incomplete_features)]
+#![deny(missing_copy_implementations)]
+
+mod good_safe_impl {
+    enum SafeEnum {
+        Safe(u8),
+    }
+
+    impl Copy for SafeEnum {}
+}
+
+mod bad_safe_impl {
+    enum UnsafeEnum {
+        Safe(u8),
+        Unsafe { unsafe field: u8 },
+    }
+
+    impl Copy for UnsafeEnum {}
+    //~^ ERROR the trait `Copy` requires an `unsafe impl` declaration
+}
+
+mod good_unsafe_impl {
+    enum UnsafeEnum {
+        Safe(u8),
+        Unsafe { unsafe field: u8 },
+    }
+
+    unsafe impl Copy for UnsafeEnum {}
+}
+
+mod bad_unsafe_impl {
+    enum SafeEnum {
+        Safe(u8),
+    }
+
+    unsafe impl Copy for SafeEnum {}
+    //~^ ERROR implementing the trait `Copy` is not unsafe
+}
diff --git a/tests/ui/unsafe-fields/copy-trait.stderr b/tests/ui/unsafe-fields/copy-trait.stderr
new file mode 100644
index 0000000000000..5952f8c89c146
--- /dev/null
+++ b/tests/ui/unsafe-fields/copy-trait.stderr
@@ -0,0 +1,28 @@
+error[E0200]: the trait `Copy` requires an `unsafe impl` declaration
+  --> $DIR/copy-trait.rs:21:5
+   |
+LL |     impl Copy for UnsafeEnum {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: the trait `Copy` cannot be safely implemented for `bad_safe_impl::UnsafeEnum` because it has unsafe fields. Review the invariants of those fields before adding an `unsafe impl`
+help: add `unsafe` to this trait implementation
+   |
+LL |     unsafe impl Copy for UnsafeEnum {}
+   |     ++++++
+
+error[E0199]: implementing the trait `Copy` is not unsafe
+  --> $DIR/copy-trait.rs:39:5
+   |
+LL |     unsafe impl Copy for SafeEnum {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove `unsafe` from this trait implementation
+   |
+LL -     unsafe impl Copy for SafeEnum {}
+LL +     impl Copy for SafeEnum {}
+   |
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0199, E0200.
+For more information about an error, try `rustc --explain E0199`.
diff --git a/triagebot.toml b/triagebot.toml
index 7bbeaacc34070..665108cccd38a 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -1011,9 +1011,11 @@ compiler = [
     "@lcnr",
     "@Nadrieril",
     "@nnethercote",
+    "@Noratrieb",
     "@oli-obk",
     "@petrochenkov",
     "@pnkfelix",
+    "@SparrowLii",
     "@wesleywiser",
 ]
 libs = [