about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast_lowering/src/path.rs19
-rw-r--r--compiler/rustc_ast_passes/messages.ftl2
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs46
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs7
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0667.md6
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs154
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs9
-rw-r--r--compiler/rustc_lint/src/builtin.rs17
-rw-r--r--compiler/rustc_lint/src/context.rs20
-rw-r--r--compiler/rustc_lint/src/early.rs18
-rw-r--r--compiler/rustc_lint/src/late.rs17
-rw-r--r--compiler/rustc_lint/src/lib.rs12
-rw-r--r--src/ci/docker/host-x86_64/dist-various-2/Dockerfile6
-rw-r--r--src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile6
-rwxr-xr-xsrc/ci/docker/scripts/freebsd-toolchain.sh8
-rw-r--r--src/doc/rustc/src/platform-support.md18
-rw-r--r--src/tools/miri/.github/workflows/ci.yml44
-rwxr-xr-xsrc/tools/miri/ci/ci.sh2
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs6
-rw-r--r--src/tools/miri/src/intrinsics/mod.rs30
-rw-r--r--src/tools/miri/src/intrinsics/simd.rs11
-rw-r--r--src/tools/miri/src/lib.rs2
-rw-r--r--src/tools/miri/src/shims/foreign_items.rs9
-rw-r--r--src/tools/miri/src/shims/io_error.rs10
-rw-r--r--src/tools/miri/src/shims/time.rs12
-rw-r--r--src/tools/miri/src/shims/unix/android/foreign_items.rs4
-rw-r--r--src/tools/miri/src/shims/unix/android/mod.rs1
-rw-r--r--src/tools/miri/src/shims/unix/android/thread.rs57
-rw-r--r--src/tools/miri/src/shims/unix/fd.rs37
-rw-r--r--src/tools/miri/src/shims/unix/foreign_items.rs25
-rw-r--r--src/tools/miri/src/shims/unix/freebsd/foreign_items.rs1
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs221
-rw-r--r--src/tools/miri/src/shims/unix/linux/epoll.rs42
-rw-r--r--src/tools/miri/src/shims/unix/linux/foreign_items.rs1
-rw-r--r--src/tools/miri/src/shims/unix/linux/mem.rs4
-rw-r--r--src/tools/miri/src/shims/unix/linux/sync.rs17
-rw-r--r--src/tools/miri/src/shims/unix/macos/foreign_items.rs1
-rw-r--r--src/tools/miri/src/shims/unix/mem.rs16
-rw-r--r--src/tools/miri/src/shims/unix/solarish/foreign_items.rs1
-rw-r--r--src/tools/miri/src/shims/unix/thread.rs12
-rw-r--r--src/tools/miri/src/shims/windows/sync.rs2
-rw-r--r--src/tools/miri/test_dependencies/Cargo.lock6
-rw-r--r--src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.rs10
-rw-r--r--src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr21
-rw-r--r--src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs2
-rw-r--r--src/tools/miri/tests/fail/provenance/ptr_invalid.rs1
-rw-r--r--src/tools/miri/tests/fail/tail_calls/dangling-local-var.rs16
-rw-r--r--src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr30
-rw-r--r--src/tools/miri/tests/pass-dep/libc/prctl-threadname.rs74
-rw-r--r--src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs18
-rw-r--r--src/tools/miri/tests/pass/dyn-upcast.rs3
-rw-r--r--src/tools/miri/tests/pass/float.rs160
-rw-r--r--tests/crashes/126725.rs20
-rw-r--r--tests/ui/impl-trait/impl_trait_projections.rs11
-rw-r--r--tests/ui/impl-trait/impl_trait_projections.stderr70
-rw-r--r--tests/ui/impl-trait/in-trait/bad-projection-from-opaque.rs22
-rw-r--r--tests/ui/impl-trait/in-trait/bad-projection-from-opaque.stderr11
-rw-r--r--tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.rs2
-rw-r--r--tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.stderr6
60 files changed, 802 insertions, 616 deletions
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index 258655de486..6a0f03c8b31 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -34,7 +34,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         modifiers: Option<ast::TraitBoundModifiers>,
     ) -> hir::QPath<'hir> {
         let qself_position = qself.as_ref().map(|q| q.position);
-        let qself = qself.as_ref().map(|q| self.lower_ty(&q.ty, itctx));
+        let qself = qself
+            .as_ref()
+            // Reject cases like `<impl Trait>::Assoc` and `<impl Trait as Trait>::Assoc`.
+            .map(|q| self.lower_ty(&q.ty, ImplTraitContext::Disallowed(ImplTraitPosition::Path)));
 
         let partial_res =
             self.resolver.get_partial_res(id).unwrap_or_else(|| PartialRes::new(Res::Err));
@@ -75,6 +78,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             None
         };
 
+        // Only permit `impl Trait` in the final segment. E.g., we permit `Option<impl Trait>`,
+        // `option::Option<T>::Xyz<impl Trait>` and reject `option::Option<impl Trait>::Xyz`.
+        let itctx = |i| {
+            if i + 1 == p.segments.len() {
+                itctx
+            } else {
+                ImplTraitContext::Disallowed(ImplTraitPosition::Path)
+            }
+        };
+
         let path_span_lo = p.span.shrink_to_lo();
         let proj_start = p.segments.len() - unresolved_segments;
         let path = self.arena.alloc(hir::Path {
@@ -121,7 +134,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         segment,
                         param_mode,
                         generic_args_mode,
-                        itctx,
+                        itctx(i),
                         bound_modifier_allowed_features.clone(),
                     )
                 },
@@ -185,7 +198,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 segment,
                 param_mode,
                 generic_args_mode,
-                itctx,
+                itctx(i),
                 None,
             ));
             let qpath = hir::QPath::TypeRelative(ty, hir_segment);
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index 92acaaa5f36..d81fd53938b 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -146,8 +146,6 @@ ast_passes_generic_before_constraints = generic arguments must come before the f
 
 ast_passes_generic_default_trailing = generic parameters with a default must be trailing
 
-ast_passes_impl_trait_path = `impl Trait` is not allowed in path parameters
-
 ast_passes_incompatible_features = `{$f1}` and `{$f2}` are incompatible, using them at the same time is not allowed
     .help = remove one of these features
 
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 1d149e91b85..0a4f86d4822 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -80,10 +80,6 @@ struct AstValidator<'a> {
 
     disallow_tilde_const: Option<TildeConstReason>,
 
-    /// Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
-    /// or `Foo::Bar<impl Trait>`
-    is_impl_trait_banned: bool,
-
     /// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe.
     extern_mod_safety: Option<Safety>,
 
@@ -123,12 +119,6 @@ impl<'a> AstValidator<'a> {
         self.extern_mod_safety = old;
     }
 
-    fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) {
-        let old = mem::replace(&mut self.is_impl_trait_banned, true);
-        f(self);
-        self.is_impl_trait_banned = old;
-    }
-
     fn with_tilde_const(
         &mut self,
         disallowed: Option<TildeConstReason>,
@@ -213,37 +203,6 @@ impl<'a> AstValidator<'a> {
                 .with_tilde_const(Some(TildeConstReason::TraitObject), |this| {
                     visit::walk_ty(this, t)
                 }),
-            TyKind::Path(qself, path) => {
-                // We allow these:
-                //  - `Option<impl Trait>`
-                //  - `option::Option<impl Trait>`
-                //  - `option::Option<T>::Foo<impl Trait>`
-                //
-                // But not these:
-                //  - `<impl Trait>::Foo`
-                //  - `option::Option<impl Trait>::Foo`.
-                //
-                // To implement this, we disallow `impl Trait` from `qself`
-                // (for cases like `<impl Trait>::Foo>`)
-                // but we allow `impl Trait` in `GenericArgs`
-                // iff there are no more PathSegments.
-                if let Some(qself) = qself {
-                    // `impl Trait` in `qself` is always illegal
-                    self.with_banned_impl_trait(|this| this.visit_ty(&qself.ty));
-                }
-
-                // Note that there should be a call to visit_path here,
-                // so if any logic is added to process `Path`s a call to it should be
-                // added both in visit_path and here. This code mirrors visit::walk_path.
-                for (i, segment) in path.segments.iter().enumerate() {
-                    // Allow `impl Trait` iff we're on the final path segment
-                    if i == path.segments.len() - 1 {
-                        self.visit_path_segment(segment);
-                    } else {
-                        self.with_banned_impl_trait(|this| this.visit_path_segment(segment));
-                    }
-                }
-            }
             _ => visit::walk_ty(self, t),
         }
     }
@@ -737,10 +696,6 @@ impl<'a> AstValidator<'a> {
                 }
             }
             TyKind::ImplTrait(_, bounds) => {
-                if self.is_impl_trait_banned {
-                    self.dcx().emit_err(errors::ImplTraitPath { span: ty.span });
-                }
-
                 if let Some(outer_impl_trait_sp) = self.outer_impl_trait {
                     self.dcx().emit_err(errors::NestedImplTrait {
                         span: ty.span,
@@ -1729,7 +1684,6 @@ pub fn check_crate(
         has_proc_macro_decls: false,
         outer_impl_trait: None,
         disallow_tilde_const: Some(TildeConstReason::Item),
-        is_impl_trait_banned: false,
         extern_mod_safety: None,
         lint_buffer: lints,
     };
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 4ca1acde1e2..8c3ac9864ed 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -419,13 +419,6 @@ pub(crate) struct TraitObjectBound {
 }
 
 #[derive(Diagnostic)]
-#[diag(ast_passes_impl_trait_path, code = E0667)]
-pub(crate) struct ImplTraitPath {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(ast_passes_nested_impl_trait, code = E0666)]
 pub(crate) struct NestedImplTrait {
     #[primary_span]
diff --git a/compiler/rustc_error_codes/src/error_codes/E0667.md b/compiler/rustc_error_codes/src/error_codes/E0667.md
index 0709a24c433..339bc068610 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0667.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0667.md
@@ -1,8 +1,10 @@
+#### Note: this error code is no longer emitted by the compiler.
+
 `impl Trait` is not allowed in path parameters.
 
 Erroneous code example:
 
-```compile_fail,E0667
+```ignore (removed error code)
 fn some_fn(mut x: impl Iterator) -> <impl Iterator>::Item { // error!
     x.next().unwrap()
 }
@@ -11,7 +13,7 @@ fn some_fn(mut x: impl Iterator) -> <impl Iterator>::Item { // error!
 You cannot use `impl Trait` in path parameters. If you want something
 equivalent, you can do this instead:
 
-```
+```ignore (removed error code)
 fn some_fn<T: Iterator>(mut x: T) -> T::Item { // ok!
     x.next().unwrap()
 }
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 95e07244a6b..f7daef3e80c 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -33,18 +33,12 @@ use crate::errors;
 
 #[extension(trait RegionExt)]
 impl ResolvedArg {
-    fn early(param: &GenericParam<'_>) -> (LocalDefId, ResolvedArg) {
-        debug!("ResolvedArg::early: def_id={:?}", param.def_id);
-        (param.def_id, ResolvedArg::EarlyBound(param.def_id))
+    fn early(param: &GenericParam<'_>) -> ResolvedArg {
+        ResolvedArg::EarlyBound(param.def_id)
     }
 
-    fn late(idx: u32, param: &GenericParam<'_>) -> (LocalDefId, ResolvedArg) {
-        let depth = ty::INNERMOST;
-        debug!(
-            "ResolvedArg::late: idx={:?}, param={:?} depth={:?} def_id={:?}",
-            idx, param, depth, param.def_id,
-        );
-        (param.def_id, ResolvedArg::LateBound(depth, idx, param.def_id))
+    fn late(idx: u32, param: &GenericParam<'_>) -> ResolvedArg {
+        ResolvedArg::LateBound(ty::INNERMOST, idx, param.def_id)
     }
 
     fn id(&self) -> Option<LocalDefId> {
@@ -282,24 +276,33 @@ fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBou
 
 fn late_arg_as_bound_arg<'tcx>(
     tcx: TyCtxt<'tcx>,
-    arg: &ResolvedArg,
     param: &GenericParam<'tcx>,
 ) -> ty::BoundVariableKind {
-    match arg {
-        ResolvedArg::LateBound(_, _, def_id) => {
-            let def_id = def_id.to_def_id();
-            let name = tcx.item_name(def_id);
-            match param.kind {
-                GenericParamKind::Lifetime { .. } => {
-                    ty::BoundVariableKind::Region(ty::BrNamed(def_id, name))
-                }
-                GenericParamKind::Type { .. } => {
-                    ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id, name))
-                }
-                GenericParamKind::Const { .. } => ty::BoundVariableKind::Const,
-            }
+    let def_id = param.def_id.to_def_id();
+    let name = tcx.item_name(def_id);
+    match param.kind {
+        GenericParamKind::Lifetime { .. } => {
+            ty::BoundVariableKind::Region(ty::BrNamed(def_id, name))
         }
-        _ => bug!("{:?} is not a late argument", arg),
+        GenericParamKind::Type { .. } => {
+            ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id, name))
+        }
+        GenericParamKind::Const { .. } => ty::BoundVariableKind::Const,
+    }
+}
+
+/// Turn a [`ty::GenericParamDef`] into a bound arg. Generally, this should only
+/// be used when turning early-bound vars into late-bound vars when lowering
+/// return type notation.
+fn generic_param_def_as_bound_arg(param: &ty::GenericParamDef) -> ty::BoundVariableKind {
+    match param.kind {
+        ty::GenericParamDefKind::Lifetime => {
+            ty::BoundVariableKind::Region(ty::BoundRegionKind::BrNamed(param.def_id, param.name))
+        }
+        ty::GenericParamDefKind::Type { .. } => {
+            ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(param.def_id, param.name))
+        }
+        ty::GenericParamDefKind::Const { .. } => ty::BoundVariableKind::Const,
     }
 }
 
@@ -360,10 +363,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
         let mut bound_vars: FxIndexMap<LocalDefId, ResolvedArg> = FxIndexMap::default();
         let binders_iter =
             trait_ref.bound_generic_params.iter().enumerate().map(|(late_bound_idx, param)| {
-                let pair = ResolvedArg::late(initial_bound_vars + late_bound_idx as u32, param);
-                let r = late_arg_as_bound_arg(self.tcx, &pair.1, param);
-                bound_vars.insert(pair.0, pair.1);
-                r
+                let arg = ResolvedArg::late(initial_bound_vars + late_bound_idx as u32, param);
+                bound_vars.insert(param.def_id, arg);
+                late_arg_as_bound_arg(self.tcx, param)
             });
         binders.extend(binders_iter);
 
@@ -458,9 +460,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     .iter()
                     .enumerate()
                     .map(|(late_bound_idx, param)| {
-                        let pair = ResolvedArg::late(late_bound_idx as u32, param);
-                        let r = late_arg_as_bound_arg(self.tcx, &pair.1, param);
-                        (pair, r)
+                        (
+                            (param.def_id, ResolvedArg::late(late_bound_idx as u32, param)),
+                            late_arg_as_bound_arg(self.tcx, param),
+                        )
                     })
                     .unzip();
 
@@ -492,8 +495,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
         let mut bound_vars = FxIndexMap::default();
         debug!(?opaque.generics.params);
         for param in opaque.generics.params {
-            let (def_id, reg) = ResolvedArg::early(param);
-            bound_vars.insert(def_id, reg);
+            let arg = ResolvedArg::early(param);
+            bound_vars.insert(param.def_id, arg);
         }
 
         let hir_id = self.tcx.local_def_id_to_hir_id(opaque.def_id);
@@ -618,9 +621,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     .iter()
                     .enumerate()
                     .map(|(late_bound_idx, param)| {
-                        let pair = ResolvedArg::late(late_bound_idx as u32, param);
-                        let r = late_arg_as_bound_arg(self.tcx, &pair.1, param);
-                        (pair, r)
+                        (
+                            (param.def_id, ResolvedArg::late(late_bound_idx as u32, param)),
+                            late_arg_as_bound_arg(self.tcx, param),
+                        )
                     })
                     .unzip();
 
@@ -870,9 +874,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                         .iter()
                         .enumerate()
                         .map(|(late_bound_idx, param)| {
-                            let pair = ResolvedArg::late(late_bound_idx as u32, param);
-                            let r = late_arg_as_bound_arg(self.tcx, &pair.1, param);
-                            (pair, r)
+                            (
+                                (param.def_id, ResolvedArg::late(late_bound_idx as u32, param)),
+                                late_arg_as_bound_arg(self.tcx, param),
+                            )
                         })
                         .unzip();
 
@@ -1052,19 +1057,21 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
         let bound_vars: FxIndexMap<LocalDefId, ResolvedArg> = generics
             .params
             .iter()
-            .map(|param| match param.kind {
-                GenericParamKind::Lifetime { .. } => {
-                    if self.tcx.is_late_bound(param.hir_id) {
-                        let late_bound_idx = named_late_bound_vars;
-                        named_late_bound_vars += 1;
-                        ResolvedArg::late(late_bound_idx, param)
-                    } else {
+            .map(|param| {
+                (param.def_id, match param.kind {
+                    GenericParamKind::Lifetime { .. } => {
+                        if self.tcx.is_late_bound(param.hir_id) {
+                            let late_bound_idx = named_late_bound_vars;
+                            named_late_bound_vars += 1;
+                            ResolvedArg::late(late_bound_idx, param)
+                        } else {
+                            ResolvedArg::early(param)
+                        }
+                    }
+                    GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
                         ResolvedArg::early(param)
                     }
-                }
-                GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
-                    ResolvedArg::early(param)
-                }
+                })
             })
             .collect();
 
@@ -1075,11 +1082,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                 matches!(param.kind, GenericParamKind::Lifetime { .. })
                     && self.tcx.is_late_bound(param.hir_id)
             })
-            .enumerate()
-            .map(|(late_bound_idx, param)| {
-                let pair = ResolvedArg::late(late_bound_idx as u32, param);
-                late_arg_as_bound_arg(self.tcx, &pair.1, param)
-            })
+            .map(|param| late_arg_as_bound_arg(self.tcx, param))
             .collect();
         self.record_late_bound_vars(hir_id, binders);
         let scope = Scope::Binder {
@@ -1096,7 +1099,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
     where
         F: for<'b, 'c> FnOnce(&'b mut BoundVarContext<'c, 'tcx>),
     {
-        let bound_vars = generics.params.iter().map(ResolvedArg::early).collect();
+        let bound_vars =
+            generics.params.iter().map(|param| (param.def_id, ResolvedArg::early(param))).collect();
         self.record_late_bound_vars(hir_id, vec![]);
         let scope = Scope::Binder {
             hir_id,
@@ -1639,17 +1643,13 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                         constraint.ident,
                         ty::AssocKind::Fn,
                     ) {
-                    bound_vars.extend(self.tcx.generics_of(assoc_fn.def_id).own_params.iter().map(
-                        |param| match param.kind {
-                            ty::GenericParamDefKind::Lifetime => ty::BoundVariableKind::Region(
-                                ty::BoundRegionKind::BrNamed(param.def_id, param.name),
-                            ),
-                            ty::GenericParamDefKind::Type { .. } => ty::BoundVariableKind::Ty(
-                                ty::BoundTyKind::Param(param.def_id, param.name),
-                            ),
-                            ty::GenericParamDefKind::Const { .. } => ty::BoundVariableKind::Const,
-                        },
-                    ));
+                    bound_vars.extend(
+                        self.tcx
+                            .generics_of(assoc_fn.def_id)
+                            .own_params
+                            .iter()
+                            .map(|param| generic_param_def_as_bound_arg(param)),
+                    );
                     bound_vars.extend(
                         self.tcx.fn_sig(assoc_fn.def_id).instantiate_identity().bound_vars(),
                     );
@@ -1968,17 +1968,13 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
         // Append the early-bound vars on the function, and then the late-bound ones.
         // We actually turn type parameters into higher-ranked types here, but we
         // deny them later in HIR lowering.
-        bound_vars.extend(self.tcx.generics_of(item_def_id).own_params.iter().map(|param| {
-            match param.kind {
-                ty::GenericParamDefKind::Lifetime => ty::BoundVariableKind::Region(
-                    ty::BoundRegionKind::BrNamed(param.def_id, param.name),
-                ),
-                ty::GenericParamDefKind::Type { .. } => {
-                    ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(param.def_id, param.name))
-                }
-                ty::GenericParamDefKind::Const { .. } => ty::BoundVariableKind::Const,
-            }
-        }));
+        bound_vars.extend(
+            self.tcx
+                .generics_of(item_def_id)
+                .own_params
+                .iter()
+                .map(|param| generic_param_def_as_bound_arg(param)),
+        );
         bound_vars.extend(self.tcx.fn_sig(item_def_id).instantiate_identity().bound_vars());
 
         // SUBTLE: Stash the old bound vars onto the *item segment* before appending
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 863c077a9e0..cfd1241ad69 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -1203,15 +1203,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     err.emit()
                 } else if let Err(reported) = qself_ty.error_reported() {
                     reported
-                } else if let ty::Alias(ty::Opaque, alias_ty) = qself_ty.kind() {
-                    // `<impl Trait as OtherTrait>::Assoc` makes no sense.
-                    struct_span_code_err!(
-                        self.dcx(),
-                        tcx.def_span(alias_ty.def_id),
-                        E0667,
-                        "`impl Trait` is not allowed in path parameters"
-                    )
-                    .emit() // Already reported in an earlier stage.
                 } else {
                     self.maybe_report_similar_assoc_fn(span, qself_ty, qself)?;
 
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 3ee4980a948..dbb8c667532 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -4,21 +4,14 @@
 //! AST visitor. Also see `rustc_session::lint::builtin`, which contains the
 //! definitions of lints that are emitted directly inside the main compiler.
 //!
-//! To add a new lint to rustc, declare it here using `declare_lint!()`.
+//! To add a new lint to rustc, declare it here using [`declare_lint!`].
 //! Then add code to emit the new lint in the appropriate circumstances.
-//! You can do that in an existing `LintPass` if it makes sense, or in a
-//! new `LintPass`, or using `Session::add_lint` elsewhere in the
-//! compiler. Only do the latter if the check can't be written cleanly as a
-//! `LintPass` (also, note that such lints will need to be defined in
-//! `rustc_session::lint::builtin`, not here).
 //!
-//! If you define a new `EarlyLintPass`, you will also need to add it to the
-//! `add_early_builtin!` or `add_early_builtin_with_new!` invocation in
-//! `lib.rs`. Use the former for unit-like structs and the latter for structs
-//! with a `pub fn new()`.
+//! If you define a new [`EarlyLintPass`], you will also need to add it to the
+//! [`crate::early_lint_methods!`] invocation in `lib.rs`.
 //!
-//! If you define a new `LateLintPass`, you will also need to add it to the
-//! `late_lint_methods!` invocation in `lib.rs`.
+//! If you define a new [`LateLintPass`], you will also need to add it to the
+//! [`crate::late_lint_methods!`] invocation in `lib.rs`.
 
 use std::fmt::Write;
 
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 39f90a8e9ed..4af1fd1baf9 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -1,18 +1,7 @@
-//! Implementation of lint checking.
+//! Basic types for managing and implementing lints.
 //!
-//! The lint checking is mostly consolidated into one pass which runs
-//! after all other analyses. Throughout compilation, lint warnings
-//! can be added via the `add_lint` method on the Session structure. This
-//! requires a span and an ID of the node that the lint is being added to. The
-//! lint isn't actually emitted at that time because it is unknown what the
-//! actual lint level at that location is.
-//!
-//! To actually emit lint warnings/errors, a separate pass is used.
-//! A context keeps track of the current state of all lint levels.
-//! Upon entering a node of the ast which can modify the lint settings, the
-//! previous lint state is pushed onto a stack and the ast is then recursed
-//! upon. As the ast is traversed, this keeps track of the current lint level
-//! for all lint attributes.
+//! See <https://rustc-dev-guide.rust-lang.org/diagnostics.html> for an
+//! overview of how lints are implemented.
 
 use std::cell::Cell;
 use std::{iter, slice};
@@ -52,9 +41,6 @@ type LateLintPassFactory =
     dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync;
 
 /// Information about the registered lints.
-///
-/// This is basically the subset of `Context` that we can
-/// build early in the compile pipeline.
 pub struct LintStore {
     /// Registered lints.
     lints: Vec<&'static Lint>,
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index e2247d3a1f6..acccff77a10 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -1,18 +1,8 @@
-//! Implementation of lint checking.
+//! Implementation of the early lint pass.
 //!
-//! The lint checking is mostly consolidated into one pass which runs
-//! after all other analyses. Throughout compilation, lint warnings
-//! can be added via the `add_lint` method on the Session structure. This
-//! requires a span and an ID of the node that the lint is being added to. The
-//! lint isn't actually emitted at that time because it is unknown what the
-//! actual lint level at that location is.
-//!
-//! To actually emit lint warnings/errors, a separate pass is used.
-//! A context keeps track of the current state of all lint levels.
-//! Upon entering a node of the ast which can modify the lint settings, the
-//! previous lint state is pushed onto a stack and the ast is then recursed
-//! upon. As the ast is traversed, this keeps track of the current lint level
-//! for all lint attributes.
+//! The early lint pass works on AST nodes after macro expansion and name
+//! resolution, just before AST lowering. These lints are for purely
+//! syntactical lints.
 
 use rustc_ast::ptr::P;
 use rustc_ast::visit::{self as ast_visit, Visitor, walk_list};
diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs
index 9b1877599ba..9d35ce19b57 100644
--- a/compiler/rustc_lint/src/late.rs
+++ b/compiler/rustc_lint/src/late.rs
@@ -1,18 +1,7 @@
-//! Implementation of lint checking.
+//! Implementation of the late lint pass.
 //!
-//! The lint checking is mostly consolidated into one pass which runs
-//! after all other analyses. Throughout compilation, lint warnings
-//! can be added via the `add_lint` method on the Session structure. This
-//! requires a span and an ID of the node that the lint is being added to. The
-//! lint isn't actually emitted at that time because it is unknown what the
-//! actual lint level at that location is.
-//!
-//! To actually emit lint warnings/errors, a separate pass is used.
-//! A context keeps track of the current state of all lint levels.
-//! Upon entering a node of the ast which can modify the lint settings, the
-//! previous lint state is pushed onto a stack and the ast is then recursed
-//! upon. As the ast is traversed, this keeps track of the current lint level
-//! for all lint attributes.
+//! The late lint pass Works on HIR nodes, towards the end of analysis (after
+//! borrow checking, etc.). These lints have full type information available.
 
 use std::any::Any;
 use std::cell::Cell;
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 5389860e23b..d950b1ff5ec 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -6,20 +6,14 @@
 //! other phases of the compiler, which are generally required to hold in order
 //! to compile the program at all.
 //!
-//! Most lints can be written as [LintPass] instances. These run after
+//! Most lints can be written as [`LintPass`] instances. These run after
 //! all other analyses. The `LintPass`es built into rustc are defined
 //! within [rustc_session::lint::builtin],
 //! which has further comments on how to add such a lint.
 //! rustc can also load external lint plugins, as is done for Clippy.
 //!
-//! Some of rustc's lints are defined elsewhere in the compiler and work by
-//! calling `add_lint()` on the overall `Session` object. This works when
-//! it happens before the main lint pass, which emits the lints stored by
-//! `add_lint()`. To emit lints after the main lint pass (from codegen, for
-//! example) requires more effort. See `emit_lint` and `GatherNodeLevels`
-//! in `context.rs`.
-//!
-//! Some code also exists in [rustc_session::lint], [rustc_middle::lint].
+//! See <https://rustc-dev-guide.rust-lang.org/diagnostics.html> for an
+//! overview of how lints are implemented.
 //!
 //! ## Note
 //!
diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
index 8aabfaafbab..ece5f174d06 100644
--- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
@@ -56,9 +56,9 @@ ENV \
     CFLAGS_x86_64_fortanix_unknown_sgx="-D__ELF__ -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \
     CXX_x86_64_fortanix_unknown_sgx=clang++-11 \
     CXXFLAGS_x86_64_fortanix_unknown_sgx="-D__ELF__ -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \
-    AR_i686_unknown_freebsd=i686-unknown-freebsd13-ar \
-    CC_i686_unknown_freebsd=i686-unknown-freebsd13-clang \
-    CXX_i686_unknown_freebsd=i686-unknown-freebsd13-clang++ \
+    AR_i686_unknown_freebsd=i686-unknown-freebsd12-ar \
+    CC_i686_unknown_freebsd=i686-unknown-freebsd12-clang \
+    CXX_i686_unknown_freebsd=i686-unknown-freebsd12-clang++ \
     CC_aarch64_unknown_uefi=clang-11 \
     CXX_aarch64_unknown_uefi=clang++-11 \
     CC_i686_unknown_uefi=clang-11 \
diff --git a/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile
index fd0f5da8c49..f42e6f770eb 100644
--- a/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-x86_64-freebsd/Dockerfile
@@ -29,9 +29,9 @@ COPY scripts/cmake.sh /scripts/
 RUN /scripts/cmake.sh
 
 ENV \
-    AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-ar \
-    CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-clang \
-    CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd13-clang++
+    AR_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-ar \
+    CC_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-clang \
+    CXX_x86_64_unknown_freebsd=x86_64-unknown-freebsd12-clang++
 
 ENV HOSTS=x86_64-unknown-freebsd
 
diff --git a/src/ci/docker/scripts/freebsd-toolchain.sh b/src/ci/docker/scripts/freebsd-toolchain.sh
index 4826b81d56c..0d02636db91 100755
--- a/src/ci/docker/scripts/freebsd-toolchain.sh
+++ b/src/ci/docker/scripts/freebsd-toolchain.sh
@@ -5,8 +5,8 @@ set -eux
 
 arch=$1
 binutils_version=2.40
-freebsd_version=13.2
-triple=$arch-unknown-freebsd13
+freebsd_version=12.3
+triple=$arch-unknown-freebsd12
 sysroot=/usr/local/$triple
 
 hide_output() {
@@ -59,7 +59,7 @@ done
 
 # Originally downloaded from:
 # URL=https://download.freebsd.org/ftp/releases/${freebsd_arch}/${freebsd_version}-RELEASE/base.txz
-URL=https://ci-mirrors.rust-lang.org/rustc/2024-02-18-freebsd-${freebsd_version}-${freebsd_arch}-base.txz
+URL=https://ci-mirrors.rust-lang.org/rustc/2022-05-06-freebsd-${freebsd_version}-${freebsd_arch}-base.txz
 curl "$URL" | tar xJf - -C "$sysroot" --wildcards "${files_to_extract[@]}"
 
 # Clang can do cross-builds out of the box, if we give it the right
@@ -68,7 +68,7 @@ curl "$URL" | tar xJf - -C "$sysroot" --wildcards "${files_to_extract[@]}"
 # there might be other problems.)
 #
 # The --target option is last because the cross-build of LLVM uses
-# --target without an OS version ("-freebsd" vs. "-freebsd13").  This
+# --target without an OS version ("-freebsd" vs. "-freebsd12").  This
 # makes Clang default to libstdc++ (which no longer exists), and also
 # controls other features, like GNU-style symbol table hashing and
 # anything predicated on the version number in the __FreeBSD__
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index f7b8a8fc6e6..04bb40d750c 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -100,7 +100,7 @@ target | notes
 [`riscv64gc-unknown-linux-gnu`](platform-support/riscv64gc-unknown-linux-gnu.md) | RISC-V Linux (kernel 4.20, glibc 2.29)
 [`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20, musl 1.2.3)
 `s390x-unknown-linux-gnu` | S390x Linux (kernel 3.2, glibc 2.17)
-`x86_64-unknown-freebsd` | 64-bit FreeBSD (version 13.2)
+`x86_64-unknown-freebsd` | 64-bit FreeBSD
 `x86_64-unknown-illumos` | illumos
 `x86_64-unknown-linux-musl` | 64-bit Linux with musl 1.2.3
 [`x86_64-unknown-netbsd`](platform-support/netbsd.md) | NetBSD/amd64
@@ -166,7 +166,7 @@ target | std | notes
 `i586-unknown-linux-musl` | ✓ | 32-bit Linux w/o SSE, musl 1.2.3 [^x86_32-floats-x87]
 [`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android [^x86_32-floats-return-ABI]
 [`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+), LLVM ABI [^x86_32-floats-return-ABI]
-`i686-unknown-freebsd` | ✓ | 32-bit FreeBSD (version 13.2) [^x86_32-floats-return-ABI]
+`i686-unknown-freebsd` | ✓ | 32-bit FreeBSD [^x86_32-floats-return-ABI]
 `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI]
 [`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI
 [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI)
@@ -258,7 +258,7 @@ target | std | host | notes
 [`aarch64-unknown-teeos`](platform-support/aarch64-unknown-teeos.md) | ? |  | ARM64 TEEOS |
 [`aarch64-unknown-nto-qnx700`](platform-support/nto-qnx.md) | ? |  | ARM64 QNX Neutrino 7.0 RTOS |
 [`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ |  | ARM64 QNX Neutrino 7.1 RTOS |
-`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD (version 13.2)
+`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD
 [`aarch64-unknown-hermit`](platform-support/hermit.md) | ✓ |  | ARM64 Hermit
 `aarch64-unknown-illumos` | ✓ | ✓ | ARM64 illumos
 `aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI)
@@ -277,14 +277,14 @@ target | std | host | notes
 `armv4t-unknown-linux-gnueabi` | ? |  | Armv4T Linux
 [`armv5te-none-eabi`](platform-support/armv5te-none-eabi.md) | * |  | Bare Armv5TE
 `armv5te-unknown-linux-uclibceabi` | ? |  | Armv5TE Linux with uClibc
-`armv6-unknown-freebsd` | ✓ | ✓ | Armv6 FreeBSD (version 13.2)
+`armv6-unknown-freebsd` | ✓ | ✓ | Armv6 FreeBSD
 [`armv6-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv6 NetBSD w/hard-float
 [`armv6k-nintendo-3ds`](platform-support/armv6k-nintendo-3ds.md) | ? |  | Armv6k Nintendo 3DS, Horizon (Requires devkitARM toolchain)
 [`armv7-rtems-eabihf`](platform-support/armv7-rtems-eabihf.md) | ? |  | RTEMS OS for ARM BSPs
 [`armv7-sony-vita-newlibeabihf`](platform-support/armv7-sony-vita-newlibeabihf.md) | ✓ |  | Armv7-A Cortex-A9 Sony PlayStation Vita (requires VITASDK toolchain)
 [`armv7-unknown-linux-uclibceabi`](platform-support/armv7-unknown-linux-uclibceabi.md) | ✓ | ✓ | Armv7-A Linux with uClibc, softfloat
 [`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | Armv7-A Linux with uClibc, hardfloat
-`armv7-unknown-freebsd` | ✓ | ✓ | Armv7-A FreeBSD (version 13.2)
+`armv7-unknown-freebsd` | ✓ | ✓ | Armv7-A FreeBSD
 [`armv7-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv7-A NetBSD w/hard-float
 [`armv7-unknown-trusty`](platform-support/trusty.md) | ? |  |
 [`armv7-wrs-vxworks-eabihf`](platform-support/vxworks.md) | ✓ |  | Armv7-A for VxWorks
@@ -343,9 +343,9 @@ target | std | host | notes
 [`powerpc-unknown-openbsd`](platform-support/powerpc-unknown-openbsd.md) | * |  |
 [`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ✓ |  |
 [`powerpc-wrs-vxworks`](platform-support/vxworks.md) | ✓ |  |
-`powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2, version 13.2)
-`powerpc64le-unknown-freebsd` |   |   | PPC64LE FreeBSD (version 13.2)
-`powerpc-unknown-freebsd` |   |   | PowerPC FreeBSD (version 13.2)
+`powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2)
+`powerpc64le-unknown-freebsd` |   |   | PPC64LE FreeBSD
+`powerpc-unknown-freebsd` |   |   | PowerPC FreeBSD
 `powerpc64-unknown-linux-musl` | ? |  | 64-bit PowerPC Linux with musl 1.2.3
 [`powerpc64-wrs-vxworks`](platform-support/vxworks.md) | ✓ |  |
 `powerpc64le-unknown-linux-musl` | ? |  | 64-bit PowerPC Linux with musl 1.2.3, Little Endian
@@ -361,7 +361,7 @@ target | std | host | notes
 [`riscv32imafc-esp-espidf`](platform-support/esp-idf.md) | ✓ |  | RISC-V ESP-IDF
 [`riscv32-wrs-vxworks`](platform-support/vxworks.md) | ✓ |  |
 [`riscv64gc-unknown-hermit`](platform-support/hermit.md) | ✓ |   | RISC-V Hermit
-`riscv64gc-unknown-freebsd` |   |   | RISC-V FreeBSD (version 13.2)
+`riscv64gc-unknown-freebsd` |   |   | RISC-V FreeBSD
 `riscv64gc-unknown-fuchsia` |   |   | RISC-V Fuchsia
 [`riscv64gc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | RISC-V NetBSD
 [`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64
diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml
index 8b0916f5111..2e491319822 100644
--- a/src/tools/miri/.github/workflows/ci.yml
+++ b/src/tools/miri/.github/workflows/ci.yml
@@ -1,11 +1,7 @@
 name: CI
 
 on:
-  push:
-    # Run in PRs and for bors, but not on master.
-    branches:
-      - 'auto'
-      - 'try'
+  merge_group:
   pull_request:
     branches:
       - 'master'
@@ -38,7 +34,7 @@ jobs:
       # The `style` job only runs on Linux; this makes sure the Windows-host-specific
       # code is also covered by clippy.
       - name: Check clippy
-        if: matrix.os == 'windows-latest'
+        if: ${{ matrix.os == 'windows-latest' }}
         run: ./miri clippy -- -D warnings
 
       - name: Test Miri
@@ -62,27 +58,25 @@ jobs:
       - name: rustdoc
         run: RUSTDOCFLAGS="-Dwarnings" ./miri doc --document-private-items
 
-  # These jobs doesn't actually test anything, but they're only used to tell
-  # bors the build completed, as there is no practical way to detect when a
-  # workflow is successful listening to webhooks only.
-  #
+  # Summary job for the merge queue.
   # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
-  end-success:
-    name: bors build finished
-    runs-on: ubuntu-latest
+  # And they should be added below in `cron-fail-notify` as well.
+  conclusion:
     needs: [build, style]
-    if: github.event.pusher.name == 'bors' && success()
-    steps:
-      - name: mark the job as a success
-        run: exit 0
-  end-failure:
-    name: bors build finished
+    # We need to ensure this job does *not* get skipped if its dependencies fail,
+    # because a skipped job is considered a success by GitHub. So we have to
+    # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
+    # when the workflow is canceled manually.
+    if: ${{ !cancelled() }}
     runs-on: ubuntu-latest
-    needs: [build, style]
-    if: github.event.pusher.name == 'bors' && (failure() || cancelled())
     steps:
-      - name: mark the job as a failure
-        run: exit 1
+      # Manually check the status of all dependencies. `if: failure()` does not work.
+      - name: Conclusion
+        run: |
+            # Print the dependent jobs to see them in the CI log
+            jq -C <<< '${{ toJson(needs) }}'
+            # Check if all jobs that we depend on (in the needs array) were successful.
+            jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
 
   cron-fail-notify:
     name: cronjob failure notification
@@ -93,7 +87,7 @@ jobs:
         # ... and create a PR.
         pull-requests: write
     needs: [build, style]
-    if: github.event_name == 'schedule' && failure()
+    if: ${{ github.event_name == 'schedule' && failure() }}
     steps:
       # Send a Zulip notification
       - name: Install zulip-send
@@ -145,7 +139,7 @@ jobs:
           git push -u origin $BRANCH
       - name: Create Pull Request
         run: |
-          PR=$(gh pr create -B master --title 'Automatic Rustup' --body '')
+          PR=$(gh pr create -B master --title 'Automatic Rustup' --body 'Please close and re-open this PR to trigger CI, then enable auto-merge.')
           ~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \
             --stream miri --subject "Miri Build Failure ($(date -u +%Y-%m))" \
             --message "A PR doing a rustc-pull [has been automatically created]($PR) for your convenience."
diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh
index ad1b2f4d0c3..4e7cbc50ca0 100755
--- a/src/tools/miri/ci/ci.sh
+++ b/src/tools/miri/ci/ci.sh
@@ -154,7 +154,7 @@ case $HOST_TARGET in
     TEST_TARGET=i686-unknown-freebsd   run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe
     TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe
     TEST_TARGET=x86_64-pc-solaris      run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe
-    TEST_TARGET=aarch64-linux-android  run_tests_minimal $BASIC $UNIX time hashmap pthread --skip threadname
+    TEST_TARGET=aarch64-linux-android  run_tests_minimal $BASIC $UNIX time hashmap threadname pthread
     TEST_TARGET=wasm32-wasip2          run_tests_minimal $BASIC wasm
     TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std
     TEST_TARGET=thumbv7em-none-eabihf  run_tests_minimal no_std
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 8b9e7efdff9..133edd3191d 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-17a19e684cdf3ca088af8b4da6a6209d128913f4
+814df6e50eaf89b90793e7d9618bb60f1f18377a
diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
index 1684abeec6b..5624c4c479e 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
@@ -424,7 +424,11 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> {
     }
 
     #[inline(never)] // This is only called on fatal code paths
-    pub(super) fn protector_error(&self, item: &Item, kind: ProtectorKind) -> InterpErrorKind<'tcx> {
+    pub(super) fn protector_error(
+        &self,
+        item: &Item,
+        kind: ProtectorKind,
+    ) -> InterpErrorKind<'tcx> {
         let protected = match kind {
             ProtectorKind::WeakProtector => "weakly protected",
             ProtectorKind::StrongProtector => "strongly protected",
diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs
index 09ec2cb46b0..776d2561b43 100644
--- a/src/tools/miri/src/intrinsics/mod.rs
+++ b/src/tools/miri/src/intrinsics/mod.rs
@@ -145,6 +145,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_scalar(Scalar::from_bool(branch), dest)?;
             }
 
+            "floorf16" | "ceilf16" | "truncf16" | "roundf16" | "rintf16" => {
+                let [f] = check_arg_count(args)?;
+                let f = this.read_scalar(f)?.to_f16()?;
+                let mode = match intrinsic_name {
+                    "floorf16" => Round::TowardNegative,
+                    "ceilf16" => Round::TowardPositive,
+                    "truncf16" => Round::TowardZero,
+                    "roundf16" => Round::NearestTiesToAway,
+                    "rintf16" => Round::NearestTiesToEven,
+                    _ => bug!(),
+                };
+                let res = f.round_to_integral(mode).value;
+                let res = this.adjust_nan(res, &[f]);
+                this.write_scalar(res, dest)?;
+            }
             "floorf32" | "ceilf32" | "truncf32" | "roundf32" | "rintf32" => {
                 let [f] = check_arg_count(args)?;
                 let f = this.read_scalar(f)?.to_f32()?;
@@ -175,6 +190,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let res = this.adjust_nan(res, &[f]);
                 this.write_scalar(res, dest)?;
             }
+            "floorf128" | "ceilf128" | "truncf128" | "roundf128" | "rintf128" => {
+                let [f] = check_arg_count(args)?;
+                let f = this.read_scalar(f)?.to_f128()?;
+                let mode = match intrinsic_name {
+                    "floorf128" => Round::TowardNegative,
+                    "ceilf128" => Round::TowardPositive,
+                    "truncf128" => Round::TowardZero,
+                    "roundf128" => Round::NearestTiesToAway,
+                    "rintf128" => Round::NearestTiesToEven,
+                    _ => bug!(),
+                };
+                let res = f.round_to_integral(mode).value;
+                let res = this.adjust_nan(res, &[f]);
+                this.write_scalar(res, dest)?;
+            }
 
             #[rustfmt::skip]
             | "sinf32"
diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs
index 0799b93dbb0..f15b83a054f 100644
--- a/src/tools/miri/src/intrinsics/simd.rs
+++ b/src/tools/miri/src/intrinsics/simd.rs
@@ -1,7 +1,8 @@
 use either::Either;
 use rustc_apfloat::{Float, Round};
+use rustc_middle::ty::FloatTy;
 use rustc_middle::ty::layout::LayoutOf;
-use rustc_middle::{mir, ty, ty::FloatTy};
+use rustc_middle::{mir, ty};
 use rustc_span::{Symbol, sym};
 use rustc_target::abi::{Endian, HasDataLayout};
 
@@ -630,12 +631,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let (right, right_len) = this.project_to_simd(right)?;
                 let (dest, dest_len) = this.project_to_simd(dest)?;
 
-                let index = generic_args[2]
-                    .expect_const()
-                    .try_to_valtree()
-                    .unwrap()
-                    .0
-                    .unwrap_branch();
+                let index =
+                    generic_args[2].expect_const().try_to_valtree().unwrap().0.unwrap_branch();
                 let index_len = index.len();
 
                 assert_eq!(left_len, right_len);
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 660f2e493bc..938d1ca319e 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -147,7 +147,7 @@ pub use crate::range_map::RangeMap;
 pub use crate::shims::EmulateItemResult;
 pub use crate::shims::env::{EnvVars, EvalContextExt as _};
 pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _};
-pub use crate::shims::io_error::{EvalContextExt as _, LibcError};
+pub use crate::shims::io_error::{EvalContextExt as _, IoError, LibcError};
 pub use crate::shims::os_str::EvalContextExt as _;
 pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _};
 pub use crate::shims::time::EvalContextExt as _;
diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs
index f6f91e58969..12f8facfd02 100644
--- a/src/tools/miri/src/shims/foreign_items.rs
+++ b/src/tools/miri/src/shims/foreign_items.rs
@@ -447,8 +447,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 } else {
                     // If this does not fit in an isize, return null and, on Unix, set errno.
                     if this.target_os_is_unix() {
-                        let einval = this.eval_libc("ENOMEM");
-                        this.set_last_error(einval)?;
+                        this.set_last_error(LibcError("ENOMEM"))?;
                     }
                     this.write_null(dest)?;
                 }
@@ -464,8 +463,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 } else {
                     // On size overflow, return null and, on Unix, set errno.
                     if this.target_os_is_unix() {
-                        let einval = this.eval_libc("ENOMEM");
-                        this.set_last_error(einval)?;
+                        this.set_last_error(LibcError("ENOMEM"))?;
                     }
                     this.write_null(dest)?;
                 }
@@ -486,8 +484,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 } else {
                     // If this does not fit in an isize, return null and, on Unix, set errno.
                     if this.target_os_is_unix() {
-                        let einval = this.eval_libc("ENOMEM");
-                        this.set_last_error(einval)?;
+                        this.set_last_error(LibcError("ENOMEM"))?;
                     }
                     this.write_null(dest)?;
                 }
diff --git a/src/tools/miri/src/shims/io_error.rs b/src/tools/miri/src/shims/io_error.rs
index 38aa181cb4f..04491f0542b 100644
--- a/src/tools/miri/src/shims/io_error.rs
+++ b/src/tools/miri/src/shims/io_error.rs
@@ -141,6 +141,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         interp_ok(Scalar::from_i32(-1))
     }
 
+    /// Sets the last OS error and return `-1` as a `i64`-typed Scalar
+    fn set_last_error_and_return_i64(
+        &mut self,
+        err: impl Into<IoError>,
+    ) -> InterpResult<'tcx, Scalar> {
+        let this = self.eval_context_mut();
+        this.set_last_error(err)?;
+        interp_ok(Scalar::from_i64(-1))
+    }
+
     /// Gets the last error variable.
     fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs
index 12c7679608d..6436823b0fd 100644
--- a/src/tools/miri/src/shims/time.rs
+++ b/src/tools/miri/src/shims/time.rs
@@ -81,9 +81,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         } else if relative_clocks.contains(&clk_id) {
             this.machine.clock.now().duration_since(this.machine.clock.epoch())
         } else {
-            let einval = this.eval_libc("EINVAL");
-            this.set_last_error(einval)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         };
 
         let tv_sec = duration.as_secs();
@@ -109,9 +107,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Using tz is obsolete and should always be null
         let tz = this.read_pointer(tz_op)?;
         if !this.ptr_is_null(tz)? {
-            let einval = this.eval_libc("EINVAL");
-            this.set_last_error(einval)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         }
 
         let duration = system_time_to_duration(&SystemTime::now())?;
@@ -323,9 +319,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let duration = match this.read_timespec(&req)? {
             Some(duration) => duration,
             None => {
-                let einval = this.eval_libc("EINVAL");
-                this.set_last_error(einval)?;
-                return interp_ok(Scalar::from_i32(-1));
+                return this.set_last_error_and_return_i32(LibcError("EINVAL"));
             }
         };
 
diff --git a/src/tools/miri/src/shims/unix/android/foreign_items.rs b/src/tools/miri/src/shims/unix/android/foreign_items.rs
index 583a1f65009..b6f04951fc7 100644
--- a/src/tools/miri/src/shims/unix/android/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs
@@ -1,6 +1,7 @@
 use rustc_span::Symbol;
 use rustc_target::spec::abi::Abi;
 
+use crate::shims::unix::android::thread::prctl;
 use crate::*;
 
 pub fn is_dyn_sym(_name: &str) -> bool {
@@ -25,6 +26,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
             }
 
+            // Threading
+            "prctl" => prctl(this, link_name, abi, args, dest)?,
+
             _ => return interp_ok(EmulateItemResult::NotSupported),
         }
         interp_ok(EmulateItemResult::NeedsReturn)
diff --git a/src/tools/miri/src/shims/unix/android/mod.rs b/src/tools/miri/src/shims/unix/android/mod.rs
index 09c6507b24f..1f2a74bac59 100644
--- a/src/tools/miri/src/shims/unix/android/mod.rs
+++ b/src/tools/miri/src/shims/unix/android/mod.rs
@@ -1 +1,2 @@
 pub mod foreign_items;
+pub mod thread;
diff --git a/src/tools/miri/src/shims/unix/android/thread.rs b/src/tools/miri/src/shims/unix/android/thread.rs
new file mode 100644
index 00000000000..6f5f0f74a22
--- /dev/null
+++ b/src/tools/miri/src/shims/unix/android/thread.rs
@@ -0,0 +1,57 @@
+use rustc_span::Symbol;
+use rustc_target::abi::Size;
+use rustc_target::spec::abi::Abi;
+
+use crate::helpers::check_min_arg_count;
+use crate::shims::unix::thread::EvalContextExt as _;
+use crate::*;
+
+const TASK_COMM_LEN: usize = 16;
+
+pub fn prctl<'tcx>(
+    this: &mut MiriInterpCx<'tcx>,
+    link_name: Symbol,
+    abi: Abi,
+    args: &[OpTy<'tcx>],
+    dest: &MPlaceTy<'tcx>,
+) -> InterpResult<'tcx> {
+    // We do not use `check_shim` here because `prctl` is variadic. The argument
+    // count is checked bellow.
+    this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?;
+
+    // FIXME: Use constants once https://github.com/rust-lang/libc/pull/3941 backported to the 0.2 branch.
+    let pr_set_name = 15;
+    let pr_get_name = 16;
+
+    let [op] = check_min_arg_count("prctl", args)?;
+    let res = match this.read_scalar(op)?.to_i32()? {
+        op if op == pr_set_name => {
+            let [_, name] = check_min_arg_count("prctl(PR_SET_NAME, ...)", args)?;
+            let name = this.read_scalar(name)?;
+            let thread = this.pthread_self()?;
+            // The Linux kernel silently truncates long names.
+            // https://www.man7.org/linux/man-pages/man2/PR_SET_NAME.2const.html
+            let res =
+                this.pthread_setname_np(thread, name, TASK_COMM_LEN, /* truncate */ true)?;
+            assert!(res);
+            Scalar::from_u32(0)
+        }
+        op if op == pr_get_name => {
+            let [_, name] = check_min_arg_count("prctl(PR_GET_NAME, ...)", args)?;
+            let name = this.read_scalar(name)?;
+            let thread = this.pthread_self()?;
+            let len = Scalar::from_target_usize(TASK_COMM_LEN as u64, this);
+            this.check_ptr_access(
+                name.to_pointer(this)?,
+                Size::from_bytes(TASK_COMM_LEN),
+                CheckInAllocMsg::MemoryAccessTest,
+            )?;
+            let res = this.pthread_getname_np(thread, name, len, /* truncate*/ false)?;
+            assert!(res);
+            Scalar::from_u32(0)
+        }
+        op => throw_unsup_format!("Miri does not support `prctl` syscall with op={}", op),
+    };
+    this.write_scalar(res, dest)?;
+    interp_ok(())
+}
diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs
index e3914640037..f3db56695fc 100644
--- a/src/tools/miri/src/shims/unix/fd.rs
+++ b/src/tools/miri/src/shims/unix/fd.rs
@@ -423,7 +423,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
 
         let Some(fd) = this.machine.fds.get(old_fd_num) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
         interp_ok(Scalar::from_i32(this.machine.fds.insert(fd)))
     }
@@ -432,7 +432,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
 
         let Some(fd) = this.machine.fds.get(old_fd_num) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
         if new_fd_num != old_fd_num {
             // Close new_fd if it is previously opened.
@@ -448,7 +448,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn flock(&mut self, fd_num: i32, op: i32) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
         let Some(fd) = this.machine.fds.get(fd_num) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
 
         // We need to check that there aren't unsupported options in `op`.
@@ -498,11 +498,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
                 // always sets this flag when opening a file. However we still need to check that the
                 // file itself is open.
-                interp_ok(Scalar::from_i32(if this.machine.fds.is_fd_num(fd_num) {
-                    this.eval_libc_i32("FD_CLOEXEC")
+                if !this.machine.fds.is_fd_num(fd_num) {
+                    this.set_last_error_and_return_i32(LibcError("EBADF"))
                 } else {
-                    this.fd_not_found()?
-                }))
+                    interp_ok(this.eval_libc("FD_CLOEXEC"))
+                }
             }
             cmd if cmd == f_dupfd || cmd == f_dupfd_cloexec => {
                 // Note that we always assume the FD_CLOEXEC flag is set for every open file, in part
@@ -521,7 +521,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 if let Some(fd) = this.machine.fds.get(fd_num) {
                     interp_ok(Scalar::from_i32(this.machine.fds.insert_with_min_num(fd, start)))
                 } else {
-                    interp_ok(Scalar::from_i32(this.fd_not_found()?))
+                    this.set_last_error_and_return_i32(LibcError("EBADF"))
                 }
             }
             cmd if this.tcx.sess.target.os == "macos"
@@ -547,7 +547,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let fd_num = this.read_scalar(fd_op)?.to_i32()?;
 
         let Some(fd) = this.machine.fds.remove(fd_num) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
         let result = fd.close(this.machine.communicate(), this)?;
         // return `0` if close is successful
@@ -555,17 +555,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?))
     }
 
-    /// Function used when a file descriptor does not exist. It returns `Ok(-1)`and sets
-    /// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
-    /// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
-    /// types (like `read`, that returns an `i64`).
-    fn fd_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
-        let this = self.eval_context_mut();
-        let ebadf = this.eval_libc("EBADF");
-        this.set_last_error(ebadf)?;
-        interp_ok((-1).into())
-    }
-
     /// Read data from `fd` into buffer specified by `buf` and `count`.
     ///
     /// If `offset` is `None`, reads data from current cursor position associated with `fd`
@@ -599,9 +588,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // We temporarily dup the FD to be able to retain mutable access to `this`.
         let Some(fd) = this.machine.fds.get(fd_num) else {
             trace!("read: FD not found");
-            let res: i32 = this.fd_not_found()?;
-            this.write_int(res, dest)?;
-            return interp_ok(());
+            return this.set_last_error_and_return(LibcError("EBADF"), dest);
         };
 
         trace!("read: FD mapped to {fd:?}");
@@ -646,9 +633,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // We temporarily dup the FD to be able to retain mutable access to `this`.
         let Some(fd) = this.machine.fds.get(fd_num) else {
-            let res: i32 = this.fd_not_found()?;
-            this.write_int(res, dest)?;
-            return interp_ok(());
+            return this.set_last_error_and_return(LibcError("EBADF"), dest);
         };
 
         match offset {
diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs
index 7ba98981920..355c93c444c 100644
--- a/src/tools/miri/src/shims/unix/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/foreign_items.rs
@@ -362,8 +362,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=reallocarray
                 match this.compute_size_in_bytes(Size::from_bytes(size), nmemb) {
                     None => {
-                        let enmem = this.eval_libc("ENOMEM");
-                        this.set_last_error(enmem)?;
+                        this.set_last_error(LibcError("ENOMEM"))?;
                         this.write_null(dest)?;
                     }
                     Some(len) => {
@@ -653,13 +652,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let chunk_size = CpuAffinityMask::chunk_size(this);
 
                 if this.ptr_is_null(mask)? {
-                    let efault = this.eval_libc("EFAULT");
-                    this.set_last_error(efault)?;
-                    this.write_int(-1, dest)?;
+                    this.set_last_error_and_return(LibcError("EFAULT"), dest)?;
                 } else if cpusetsize == 0 || cpusetsize.checked_rem(chunk_size).unwrap() != 0 {
                     // we only copy whole chunks of size_of::<c_ulong>()
-                    this.set_last_error(LibcError("EINVAL"))?;
-                    this.write_int(-1, dest)?;
+                    this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
                 } else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&thread_id) {
                     let cpuset = cpuset.clone();
                     // we only copy whole chunks of size_of::<c_ulong>()
@@ -668,9 +664,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     this.write_null(dest)?;
                 } else {
                     // The thread whose ID is pid could not be found
-                    let esrch = this.eval_libc("ESRCH");
-                    this.set_last_error(esrch)?;
-                    this.write_int(-1, dest)?;
+                    this.set_last_error_and_return(LibcError("ESRCH"), dest)?;
                 }
             }
             "sched_setaffinity" => {
@@ -695,9 +689,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 };
 
                 if this.ptr_is_null(mask)? {
-                    let efault = this.eval_libc("EFAULT");
-                    this.set_last_error(efault)?;
-                    this.write_int(-1, dest)?;
+                    this.set_last_error_and_return(LibcError("EFAULT"), dest)?;
                 } else {
                     // NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES`.
                     // Any unspecified bytes are treated as zero here (none of the CPUs are configured).
@@ -713,8 +705,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                         }
                         None => {
                             // The intersection between the mask and the available CPUs was empty.
-                            this.set_last_error(LibcError("EINVAL"))?;
-                            this.write_int(-1, dest)?;
+                            this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
                         }
                     }
                 }
@@ -770,9 +761,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // macOS: https://keith.github.io/xcode-man-pages/getentropy.2.html
                 // Solaris/Illumos: https://illumos.org/man/3C/getentropy
                 if bufsize > 256 {
-                    let err = this.eval_libc("EIO");
-                    this.set_last_error(err)?;
-                    this.write_int(-1, dest)?;
+                    this.set_last_error_and_return(LibcError("EIO"), dest)?;
                 } else {
                     this.gen_random(buf, bufsize)?;
                     this.write_null(dest)?;
diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
index 5204e57705a..71953aca989 100644
--- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
@@ -29,6 +29,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     this.read_scalar(thread)?,
                     this.read_scalar(name)?,
                     max_len,
+                    /* truncate */ false,
                 )?;
             }
             "pthread_get_name_np" => {
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index 4b3ae8e0520..f7436d7f089 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -149,6 +149,7 @@ impl FileDescription for FileHandle {
             // to handle possible errors correctly.
             let result = self.file.sync_all();
             // Now we actually close the file and return the result.
+            drop(*self);
             interp_ok(result)
         } else {
             // We drop the file, this closes it but ignores any errors
@@ -157,6 +158,7 @@ impl FileDescription for FileHandle {
             // `/dev/urandom` which are read-only. Check
             // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439
             // for a deeper discussion.
+            drop(*self);
             interp_ok(Ok(()))
         }
     }
@@ -229,6 +231,8 @@ impl FileDescription for FileHandle {
                 TRUE => Ok(()),
                 FALSE => {
                     let mut err = io::Error::last_os_error();
+                    // This only runs on Windows hosts so we can use `raw_os_error`.
+                    // We have to be careful not to forward that error code to target code.
                     let code: u32 = err.raw_os_error().unwrap().try_into().unwrap();
                     if matches!(code, ERROR_IO_PENDING | ERROR_LOCK_VIOLATION) {
                         if lock_nb {
@@ -337,15 +341,10 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     _ => interp_ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()),
                 }
             }
-            Err(e) =>
-                match e.raw_os_error() {
-                    Some(error) => interp_ok(error),
-                    None =>
-                        throw_unsup_format!(
-                            "the error {} couldn't be converted to a return value",
-                            e
-                        ),
-                },
+            Err(_) => {
+                // Fallback on error
+                interp_ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into())
+            }
         }
     }
 }
@@ -528,8 +527,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             let o_tmpfile = this.eval_libc_i32("O_TMPFILE");
             if flag & o_tmpfile == o_tmpfile {
                 // if the flag contains `O_TMPFILE` then we return a graceful error
-                this.set_last_error(LibcError("EOPNOTSUPP"))?;
-                return interp_ok(Scalar::from_i32(-1));
+                return this.set_last_error_and_return_i32(LibcError("EOPNOTSUPP"));
             }
         }
 
@@ -548,9 +546,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // O_NOFOLLOW only fails when the trailing component is a symlink;
                 // the entire rest of the path can still contain symlinks.
                 if path.is_symlink() {
-                    let eloop = this.eval_libc("ELOOP");
-                    this.set_last_error(eloop)?;
-                    return interp_ok(Scalar::from_i32(-1));
+                    return this.set_last_error_and_return_i32(LibcError("ELOOP"));
                 }
             }
             mirror |= o_nofollow;
@@ -565,8 +561,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`open`", reject_with)?;
-            this.set_last_error(ErrorKind::PermissionDenied)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
         }
 
         let fd = options
@@ -584,8 +579,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let seek_from = if whence == this.eval_libc_i32("SEEK_SET") {
             if offset < 0 {
                 // Negative offsets return `EINVAL`.
-                this.set_last_error(LibcError("EINVAL"))?;
-                return interp_ok(Scalar::from_i64(-1));
+                return this.set_last_error_and_return_i64(LibcError("EINVAL"));
             } else {
                 SeekFrom::Start(u64::try_from(offset).unwrap())
             }
@@ -594,14 +588,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         } else if whence == this.eval_libc_i32("SEEK_END") {
             SeekFrom::End(i64::try_from(offset).unwrap())
         } else {
-            this.set_last_error(LibcError("EINVAL"))?;
-            return interp_ok(Scalar::from_i64(-1));
+            return this.set_last_error_and_return_i64(LibcError("EINVAL"));
         };
 
         let communicate = this.machine.communicate();
 
         let Some(fd) = this.machine.fds.get(fd_num) else {
-            return interp_ok(Scalar::from_i64(this.fd_not_found()?));
+            return this.set_last_error_and_return_i64(LibcError("EBADF"));
         };
         let result = fd.seek(communicate, seek_from)?.map(|offset| i64::try_from(offset).unwrap());
         drop(fd);
@@ -618,8 +611,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`unlink`", reject_with)?;
-            this.set_last_error(ErrorKind::PermissionDenied)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
         }
 
         let result = remove_file(path).map(|_| 0);
@@ -649,8 +641,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`symlink`", reject_with)?;
-            this.set_last_error(ErrorKind::PermissionDenied)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
         }
 
         let result = create_link(&target, &linkpath).map(|_| 0);
@@ -674,15 +665,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`stat`", reject_with)?;
-            let eacc = this.eval_libc("EACCES");
-            this.set_last_error(eacc)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EACCES"));
         }
 
         // `stat` always follows symlinks.
         let metadata = match FileMetadata::from_path(this, &path, true)? {
-            Some(metadata) => metadata,
-            None => return interp_ok(Scalar::from_i32(-1)), // `FileMetadata` has set errno
+            Ok(metadata) => metadata,
+            Err(err) => return this.set_last_error_and_return_i32(err),
         };
 
         interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?))
@@ -706,14 +695,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`lstat`", reject_with)?;
-            let eacc = this.eval_libc("EACCES");
-            this.set_last_error(eacc)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EACCES"));
         }
 
         let metadata = match FileMetadata::from_path(this, &path, false)? {
-            Some(metadata) => metadata,
-            None => return interp_ok(Scalar::from_i32(-1)), // `FileMetadata` has set errno
+            Ok(metadata) => metadata,
+            Err(err) => return this.set_last_error_and_return_i32(err),
         };
 
         interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?))
@@ -736,12 +723,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`fstat`", reject_with)?;
             // Set error code as "EBADF" (bad fd)
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         }
 
         let metadata = match FileMetadata::from_fd_num(this, fd)? {
-            Some(metadata) => metadata,
-            None => return interp_ok(Scalar::from_i32(-1)),
+            Ok(metadata) => metadata,
+            Err(err) => return this.set_last_error_and_return_i32(err),
         };
         interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?))
     }
@@ -766,9 +753,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // If the statxbuf or pathname pointers are null, the function fails with `EFAULT`.
         if this.ptr_is_null(statxbuf_ptr)? || this.ptr_is_null(pathname_ptr)? {
-            let efault = this.eval_libc("EFAULT");
-            this.set_last_error(efault)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EFAULT"));
         }
 
         let statxbuf = this.deref_pointer_as(statxbuf_op, this.libc_ty_layout("statx"))?;
@@ -801,16 +786,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             let ecode = if path.is_absolute() || dirfd == this.eval_libc_i32("AT_FDCWD") {
                 // since `path` is provided, either absolute or
                 // relative to CWD, `EACCES` is the most relevant.
-                this.eval_libc("EACCES")
+                LibcError("EACCES")
             } else {
                 // `dirfd` is set to target file, and `path` is empty
                 // (or we would have hit the `throw_unsup_format`
                 // above). `EACCES` would violate the spec.
                 assert!(empty_path_flag);
-                this.eval_libc("EBADF")
+                LibcError("EBADF")
             };
-            this.set_last_error(ecode)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(ecode);
         }
 
         // the `_mask_op` parameter specifies the file information that the caller requested.
@@ -831,8 +815,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             FileMetadata::from_path(this, &path, follow_symlink)?
         };
         let metadata = match metadata {
-            Some(metadata) => metadata,
-            None => return interp_ok(Scalar::from_i32(-1)),
+            Ok(metadata) => metadata,
+            Err(err) => return this.set_last_error_and_return_i32(err),
         };
 
         // The `mode` field specifies the type of the file and the permissions over the file for
@@ -939,9 +923,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let newpath_ptr = this.read_pointer(newpath_op)?;
 
         if this.ptr_is_null(oldpath_ptr)? || this.ptr_is_null(newpath_ptr)? {
-            let efault = this.eval_libc("EFAULT");
-            this.set_last_error(efault)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EFAULT"));
         }
 
         let oldpath = this.read_path_from_c_str(oldpath_ptr)?;
@@ -950,8 +932,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`rename`", reject_with)?;
-            this.set_last_error(ErrorKind::PermissionDenied)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
         }
 
         let result = rename(oldpath, newpath).map(|_| 0);
@@ -974,8 +955,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`mkdir`", reject_with)?;
-            this.set_last_error(ErrorKind::PermissionDenied)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
         }
 
         #[cfg_attr(not(unix), allow(unused_mut))]
@@ -1002,8 +982,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`rmdir`", reject_with)?;
-            this.set_last_error(ErrorKind::PermissionDenied)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(ErrorKind::PermissionDenied);
         }
 
         let result = remove_dir(path).map(|_| 0i32);
@@ -1019,8 +998,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`opendir`", reject_with)?;
-            let eacc = this.eval_libc("EACCES");
-            this.set_last_error(eacc)?;
+            this.set_last_error(LibcError("EACCES"))?;
             return interp_ok(Scalar::null_ptr(this));
         }
 
@@ -1052,8 +1030,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`readdir`", reject_with)?;
-            let eacc = this.eval_libc("EBADF");
-            this.set_last_error(eacc)?;
+            this.set_last_error(LibcError("EBADF"))?;
             return interp_ok(Scalar::null_ptr(this));
         }
 
@@ -1152,14 +1129,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`readdir_r`", reject_with)?;
-            // Set error code as "EBADF" (bad fd)
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            // Return error code, do *not* set `errno`.
+            return interp_ok(this.eval_libc("EBADF"));
         }
 
         let open_dir = this.machine.dirs.streams.get_mut(&dirp).ok_or_else(|| {
             err_unsup_format!("the DIR pointer passed to readdir_r did not come from opendir")
         })?;
-        interp_ok(Scalar::from_i32(match open_dir.read_dir.next() {
+        interp_ok(match open_dir.read_dir.next() {
             Some(Ok(dir_entry)) => {
                 // Write into entry, write pointer to result, return 0 on success.
                 // The name is written with write_os_str_to_c_str, while the rest of the
@@ -1237,25 +1214,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let result_place = this.deref_pointer(result_op)?;
                 this.write_scalar(this.read_scalar(entry_op)?, &result_place)?;
 
-                0
+                Scalar::from_i32(0)
             }
             None => {
                 // end of stream: return 0, assign *result=NULL
                 this.write_null(&this.deref_pointer(result_op)?)?;
-                0
+                Scalar::from_i32(0)
             }
-            Some(Err(e)) =>
-                match e.raw_os_error() {
-                    // return positive error number on error
-                    Some(error) => error,
-                    None => {
-                        throw_unsup_format!(
-                            "the error {} couldn't be converted to a return value",
-                            e
-                        )
-                    }
-                },
-        }))
+            Some(Err(e)) => {
+                // return positive error number on error (do *not* set last error)
+                this.io_error_to_errnum(e)?
+            }
+        })
     }
 
     fn closedir(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
@@ -1264,20 +1234,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let dirp = this.read_target_usize(dirp_op)?;
 
         // Reject if isolation is enabled.
-        interp_ok(Scalar::from_i32(
-            if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
-                this.reject_in_isolation("`closedir`", reject_with)?;
-                this.fd_not_found()?
-            } else if let Some(open_dir) = this.machine.dirs.streams.remove(&dirp) {
-                if let Some(entry) = open_dir.entry {
-                    this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?;
-                }
-                drop(open_dir);
-                0
-            } else {
-                this.fd_not_found()?
-            },
-        ))
+        if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
+            this.reject_in_isolation("`closedir`", reject_with)?;
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
+        }
+
+        let Some(mut open_dir) = this.machine.dirs.streams.remove(&dirp) else {
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
+        };
+        if let Some(entry) = open_dir.entry.take() {
+            this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?;
+        }
+        // We drop the `open_dir`, which will close the host dir handle.
+        drop(open_dir);
+
+        interp_ok(Scalar::from_i32(0))
     }
 
     fn ftruncate64(&mut self, fd_num: i32, length: i128) -> InterpResult<'tcx, Scalar> {
@@ -1287,11 +1258,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`ftruncate64`", reject_with)?;
             // Set error code as "EBADF" (bad fd)
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         }
 
         let Some(fd) = this.machine.fds.get(fd_num) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
 
         // FIXME: Support ftruncate64 for all FDs
@@ -1307,14 +1278,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 interp_ok(Scalar::from_i32(result))
             } else {
                 drop(fd);
-                this.set_last_error(LibcError("EINVAL"))?;
-                interp_ok(Scalar::from_i32(-1))
+                this.set_last_error_and_return_i32(LibcError("EINVAL"))
             }
         } else {
             drop(fd);
             // The file is not writable
-            this.set_last_error(LibcError("EINVAL"))?;
-            interp_ok(Scalar::from_i32(-1))
+            this.set_last_error_and_return_i32(LibcError("EINVAL"))
         }
     }
 
@@ -1332,7 +1301,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`fsync`", reject_with)?;
             // Set error code as "EBADF" (bad fd)
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         }
 
         self.ffullsync_fd(fd)
@@ -1341,7 +1310,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn ffullsync_fd(&mut self, fd_num: i32) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
         let Some(fd) = this.machine.fds.get(fd_num) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
         // Only regular files support synchronization.
         let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| {
@@ -1361,11 +1330,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`fdatasync`", reject_with)?;
             // Set error code as "EBADF" (bad fd)
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         }
 
         let Some(fd) = this.machine.fds.get(fd) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
         // Only regular files support synchronization.
         let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| {
@@ -1391,26 +1360,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let flags = this.read_scalar(flags_op)?.to_i32()?;
 
         if offset < 0 || nbytes < 0 {
-            this.set_last_error(LibcError("EINVAL"))?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         }
         let allowed_flags = this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_BEFORE")
             | this.eval_libc_i32("SYNC_FILE_RANGE_WRITE")
             | this.eval_libc_i32("SYNC_FILE_RANGE_WAIT_AFTER");
         if flags & allowed_flags != flags {
-            this.set_last_error(LibcError("EINVAL"))?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         }
 
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`sync_file_range`", reject_with)?;
             // Set error code as "EBADF" (bad fd)
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         }
 
         let Some(fd) = this.machine.fds.get(fd) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
         // Only regular files support synchronization.
         let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| {
@@ -1436,8 +1403,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`readlink`", reject_with)?;
-            let eacc = this.eval_libc("EACCES");
-            this.set_last_error(eacc)?;
+            this.set_last_error(LibcError("EACCES"))?;
             return interp_ok(-1);
         }
 
@@ -1475,11 +1441,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             if fd.is_tty(this.machine.communicate()) {
                 return interp_ok(Scalar::from_i32(1));
             } else {
-                this.eval_libc("ENOTTY")
+                LibcError("ENOTTY")
             }
         } else {
             // FD does not exist
-            this.eval_libc("EBADF")
+            LibcError("EBADF")
         };
         this.set_last_error(error)?;
         interp_ok(Scalar::from_i32(0))
@@ -1499,8 +1465,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`realpath`", reject_with)?;
-            let eacc = this.eval_libc("EACCES");
-            this.set_last_error(eacc)?;
+            this.set_last_error(LibcError("EACCES"))?;
             return interp_ok(Scalar::from_target_usize(0, this));
         }
 
@@ -1530,8 +1495,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                         // Note that we do not explicitly handle `FILENAME_MAX`
                         // (different from `PATH_MAX` above) as it is Linux-specific and
                         // seems like a bit of a mess anyway: <https://eklitzke.org/path-max-is-tricky>.
-                        let enametoolong = this.eval_libc("ENAMETOOLONG");
-                        this.set_last_error(enametoolong)?;
+                        this.set_last_error(LibcError("ENAMETOOLONG"))?;
                         return interp_ok(Scalar::from_target_usize(0, this));
                     }
                     processed_ptr
@@ -1574,9 +1538,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Reject if isolation is enabled.
         if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
             this.reject_in_isolation("`mkstemp`", reject_with)?;
-            let eacc = this.eval_libc("EACCES");
-            this.set_last_error(eacc)?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EACCES"));
         }
 
         // Get the bytes of the suffix we expect in _target_ encoding.
@@ -1592,8 +1554,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // If we don't find the suffix, it is an error.
         if last_six_char_bytes != suffix_bytes {
-            this.set_last_error(LibcError("EINVAL"))?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         }
 
         // At this point we know we have 6 ASCII 'X' characters as a suffix.
@@ -1658,17 +1619,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                         _ => {
                             // "On error, -1 is returned, and errno is set to
                             // indicate the error"
-                            this.set_last_error(e)?;
-                            return interp_ok(Scalar::from_i32(-1));
+                            return this.set_last_error_and_return_i32(e);
                         }
                     },
             }
         }
 
         // We ran out of attempts to create the file, return an error.
-        let eexist = this.eval_libc("EEXIST");
-        this.set_last_error(eexist)?;
-        interp_ok(Scalar::from_i32(-1))
+        this.set_last_error_and_return_i32(LibcError("EEXIST"))
     }
 }
 
@@ -1702,7 +1660,7 @@ impl FileMetadata {
         ecx: &mut MiriInterpCx<'tcx>,
         path: &Path,
         follow_symlink: bool,
-    ) -> InterpResult<'tcx, Option<FileMetadata>> {
+    ) -> InterpResult<'tcx, Result<FileMetadata, IoError>> {
         let metadata =
             if follow_symlink { std::fs::metadata(path) } else { std::fs::symlink_metadata(path) };
 
@@ -1712,9 +1670,9 @@ impl FileMetadata {
     fn from_fd_num<'tcx>(
         ecx: &mut MiriInterpCx<'tcx>,
         fd_num: i32,
-    ) -> InterpResult<'tcx, Option<FileMetadata>> {
+    ) -> InterpResult<'tcx, Result<FileMetadata, IoError>> {
         let Some(fd) = ecx.machine.fds.get(fd_num) else {
-            return ecx.fd_not_found().map(|_: i32| None);
+            return interp_ok(Err(LibcError("EBADF")));
         };
 
         let file = &fd
@@ -1734,12 +1692,11 @@ impl FileMetadata {
     fn from_meta<'tcx>(
         ecx: &mut MiriInterpCx<'tcx>,
         metadata: Result<std::fs::Metadata, std::io::Error>,
-    ) -> InterpResult<'tcx, Option<FileMetadata>> {
+    ) -> InterpResult<'tcx, Result<FileMetadata, IoError>> {
         let metadata = match metadata {
             Ok(metadata) => metadata,
             Err(e) => {
-                ecx.set_last_error(e)?;
-                return interp_ok(None);
+                return interp_ok(Err(e.into()));
             }
         };
 
@@ -1762,6 +1719,6 @@ impl FileMetadata {
         let modified = extract_sec_and_nsec(metadata.modified())?;
 
         // FIXME: Provide more fields using platform specific methods.
-        interp_ok(Some(FileMetadata { mode, size, created, accessed, modified }))
+        interp_ok(Ok(FileMetadata { mode, size, created, accessed, modified }))
     }
 }
diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs
index cafc7161d26..de108665e9f 100644
--- a/src/tools/miri/src/shims/unix/linux/epoll.rs
+++ b/src/tools/miri/src/shims/unix/linux/epoll.rs
@@ -256,23 +256,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let epollhup = this.eval_libc_u32("EPOLLHUP");
         let epollerr = this.eval_libc_u32("EPOLLERR");
 
-        // Fail on unsupported operations.
-        if op & epoll_ctl_add != epoll_ctl_add
-            && op & epoll_ctl_mod != epoll_ctl_mod
-            && op & epoll_ctl_del != epoll_ctl_del
-        {
-            throw_unsup_format!("epoll_ctl: encountered unknown unsupported operation {:#x}", op);
-        }
-
         // Throw EINVAL if epfd and fd have the same value.
         if epfd_value == fd {
-            this.set_last_error(LibcError("EINVAL"))?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         }
 
         // Check if epfd is a valid epoll file descriptor.
         let Some(epfd) = this.machine.fds.get(epfd_value) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
         let epoll_file_description = epfd
             .downcast::<Epoll>()
@@ -282,7 +273,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let ready_list = &epoll_file_description.ready_list;
 
         let Some(fd_ref) = this.machine.fds.get(fd) else {
-            return interp_ok(Scalar::from_i32(this.fd_not_found()?));
+            return this.set_last_error_and_return_i32(LibcError("EBADF"));
         };
         let id = fd_ref.get_id();
 
@@ -332,15 +323,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Check the existence of fd in the interest list.
             if op == epoll_ctl_add {
                 if interest_list.contains_key(&epoll_key) {
-                    let eexist = this.eval_libc("EEXIST");
-                    this.set_last_error(eexist)?;
-                    return interp_ok(Scalar::from_i32(-1));
+                    return this.set_last_error_and_return_i32(LibcError("EEXIST"));
                 }
             } else {
                 if !interest_list.contains_key(&epoll_key) {
-                    let enoent = this.eval_libc("ENOENT");
-                    this.set_last_error(enoent)?;
-                    return interp_ok(Scalar::from_i32(-1));
+                    return this.set_last_error_and_return_i32(LibcError("ENOENT"));
                 }
             }
 
@@ -368,15 +355,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Notification will be returned for current epfd if there is event in the file
             // descriptor we registered.
             check_and_update_one_event_interest(&fd_ref, interest, id, this)?;
-            return interp_ok(Scalar::from_i32(0));
+            interp_ok(Scalar::from_i32(0))
         } else if op == epoll_ctl_del {
             let epoll_key = (id, fd);
 
             // Remove epoll_event_interest from interest_list.
             let Some(epoll_interest) = interest_list.remove(&epoll_key) else {
-                let enoent = this.eval_libc("ENOENT");
-                this.set_last_error(enoent)?;
-                return interp_ok(Scalar::from_i32(-1));
+                return this.set_last_error_and_return_i32(LibcError("ENOENT"));
             };
             // All related Weak<EpollEventInterest> will fail to upgrade after the drop.
             drop(epoll_interest);
@@ -394,9 +379,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 .unwrap()
                 .retain(|event| event.upgrade().is_some());
 
-            return interp_ok(Scalar::from_i32(0));
+            interp_ok(Scalar::from_i32(0))
+        } else {
+            throw_unsup_format!("unsupported epoll_ctl operation: {op}");
         }
-        interp_ok(Scalar::from_i32(-1))
     }
 
     /// The `epoll_wait()` system call waits for events on the `Epoll`
@@ -447,9 +433,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let timeout = this.read_scalar(timeout)?.to_i32()?;
 
         if epfd_value <= 0 || maxevents <= 0 {
-            this.set_last_error(LibcError("EINVAL"))?;
-            this.write_int(-1, dest)?;
-            return interp_ok(());
+            return this.set_last_error_and_return(LibcError("EINVAL"), dest);
         }
 
         // This needs to come after the maxevents value check, or else maxevents.try_into().unwrap()
@@ -460,9 +444,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         )?;
 
         let Some(epfd) = this.machine.fds.get(epfd_value) else {
-            let result_value: i32 = this.fd_not_found()?;
-            this.write_int(result_value, dest)?;
-            return interp_ok(());
+            return this.set_last_error_and_return(LibcError("EBADF"), dest);
         };
         // Create a weak ref of epfd and pass it to callback so we will make sure that epfd
         // is not close after the thread unblocks.
diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
index e73bde1ddb6..6616a9845c0 100644
--- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
@@ -84,6 +84,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     this.read_scalar(thread)?,
                     this.read_scalar(name)?,
                     TASK_COMM_LEN,
+                    /* truncate */ false,
                 )?;
                 let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
                 this.write_scalar(res, dest)?;
diff --git a/src/tools/miri/src/shims/unix/linux/mem.rs b/src/tools/miri/src/shims/unix/linux/mem.rs
index 4f2e17d50c8..d5f9669b360 100644
--- a/src/tools/miri/src/shims/unix/linux/mem.rs
+++ b/src/tools/miri/src/shims/unix/linux/mem.rs
@@ -24,7 +24,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // old_address must be a multiple of the page size
         #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
         if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 {
-            this.set_last_error(this.eval_libc("EINVAL"))?;
+            this.set_last_error(LibcError("EINVAL"))?;
             return interp_ok(this.eval_libc("MAP_FAILED"));
         }
 
@@ -38,7 +38,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         if flags & this.eval_libc_i32("MREMAP_MAYMOVE") == 0 {
             // We only support MREMAP_MAYMOVE, so not passing the flag is just a failure
-            this.set_last_error(this.eval_libc("EINVAL"))?;
+            this.set_last_error(LibcError("EINVAL"))?;
             return interp_ok(this.eval_libc("MAP_FAILED"));
         }
 
diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs
index 941011bfac6..c258be78f76 100644
--- a/src/tools/miri/src/shims/unix/linux/sync.rs
+++ b/src/tools/miri/src/shims/unix/linux/sync.rs
@@ -63,8 +63,7 @@ pub fn futex<'tcx>(
             };
 
             if bitset == 0 {
-                this.set_last_error(LibcError("EINVAL"))?;
-                this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
+                this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
                 return interp_ok(());
             }
 
@@ -75,9 +74,7 @@ pub fn futex<'tcx>(
                 let duration = match this.read_timespec(&timeout)? {
                     Some(duration) => duration,
                     None => {
-                        this.set_last_error(LibcError("EINVAL"))?;
-                        this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
-                        return interp_ok(());
+                        return this.set_last_error_and_return(LibcError("EINVAL"), dest);
                     }
                 };
                 let timeout_clock = if op & futex_realtime == futex_realtime {
@@ -153,14 +150,12 @@ pub fn futex<'tcx>(
                     Scalar::from_target_isize(0, this), // retval_succ
                     Scalar::from_target_isize(-1, this), // retval_timeout
                     dest.clone(),
-                    this.eval_libc("ETIMEDOUT"),
+                    this.eval_libc("ETIMEDOUT"), // errno_timeout
                 );
             } else {
                 // The futex value doesn't match the expected value, so we return failure
                 // right away without sleeping: -1 and errno set to EAGAIN.
-                let eagain = this.eval_libc("EAGAIN");
-                this.set_last_error(eagain)?;
-                this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
+                return this.set_last_error_and_return(LibcError("EAGAIN"), dest);
             }
         }
         // FUTEX_WAKE: (int *addr, int op = FUTEX_WAKE, int val)
@@ -180,9 +175,7 @@ pub fn futex<'tcx>(
                 u32::MAX
             };
             if bitset == 0 {
-                this.set_last_error(LibcError("EINVAL"))?;
-                this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
-                return interp_ok(());
+                return this.set_last_error_and_return(LibcError("EINVAL"), dest);
             }
             // Together with the SeqCst fence in futex_wait, this makes sure that futex_wait
             // will see the latest value on addr which could be changed by our caller
diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
index b199992245c..cd07bc9e013 100644
--- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
@@ -181,6 +181,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     thread,
                     this.read_scalar(name)?,
                     this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?.try_into().unwrap(),
+                    /* truncate */ false,
                 )? {
                     Scalar::from_u32(0)
                 } else {
diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs
index 9273748ef3b..9371edfc83d 100644
--- a/src/tools/miri/src/shims/unix/mem.rs
+++ b/src/tools/miri/src/shims/unix/mem.rs
@@ -57,11 +57,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // First, we do some basic argument validation as required by mmap
         if (flags & (map_private | map_shared)).count_ones() != 1 {
-            this.set_last_error(this.eval_libc("EINVAL"))?;
+            this.set_last_error(LibcError("EINVAL"))?;
             return interp_ok(this.eval_libc("MAP_FAILED"));
         }
         if length == 0 {
-            this.set_last_error(this.eval_libc("EINVAL"))?;
+            this.set_last_error(LibcError("EINVAL"))?;
             return interp_ok(this.eval_libc("MAP_FAILED"));
         }
 
@@ -103,11 +103,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         let align = this.machine.page_align();
         let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else {
-            this.set_last_error(this.eval_libc("EINVAL"))?;
+            this.set_last_error(LibcError("EINVAL"))?;
             return interp_ok(this.eval_libc("MAP_FAILED"));
         };
         if map_length > this.target_usize_max() {
-            this.set_last_error(this.eval_libc("EINVAL"))?;
+            this.set_last_error(LibcError("EINVAL"))?;
             return interp_ok(this.eval_libc("MAP_FAILED"));
         }
 
@@ -134,16 +134,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // as a dealloc.
         #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
         if addr.addr().bytes() % this.machine.page_size != 0 {
-            this.set_last_error(this.eval_libc("EINVAL"))?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         }
 
         let Some(length) = length.checked_next_multiple_of(this.machine.page_size) else {
-            this.set_last_error(this.eval_libc("EINVAL"))?;
-            return interp_ok(Scalar::from_i32(-1));
+            return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         };
         if length > this.target_usize_max() {
-            this.set_last_error(this.eval_libc("EINVAL"))?;
+            this.set_last_error(LibcError("EINVAL"))?;
             return interp_ok(this.eval_libc("MAP_FAILED"));
         }
 
diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
index 7f3d0f07bdc..c9c1b01b8b1 100644
--- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
@@ -30,6 +30,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     this.read_scalar(thread)?,
                     this.read_scalar(name)?,
                     max_len,
+                    /* truncate */ false,
                 )?;
                 let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
                 this.write_scalar(res, dest)?;
diff --git a/src/tools/miri/src/shims/unix/thread.rs b/src/tools/miri/src/shims/unix/thread.rs
index 7f97afc8e4b..51256d800a4 100644
--- a/src/tools/miri/src/shims/unix/thread.rs
+++ b/src/tools/miri/src/shims/unix/thread.rs
@@ -64,23 +64,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     /// Set the name of the specified thread. If the name including the null terminator
-    /// is longer than `name_max_len`, then `false` is returned.
+    /// is longer or equals to `name_max_len`, then if `truncate` is set the truncated name
+    /// is used as the thread name, otherwise `false` is returned.
     fn pthread_setname_np(
         &mut self,
         thread: Scalar,
         name: Scalar,
         name_max_len: usize,
+        truncate: bool,
     ) -> InterpResult<'tcx, bool> {
         let this = self.eval_context_mut();
 
         let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
         let thread = ThreadId::try_from(thread).unwrap();
         let name = name.to_pointer(this)?;
-        let name = this.read_c_str(name)?.to_owned();
+        let mut name = this.read_c_str(name)?.to_owned();
 
         // Comparing with `>=` to account for null terminator.
         if name.len() >= name_max_len {
-            return interp_ok(false);
+            if truncate {
+                name.truncate(name_max_len.saturating_sub(1));
+            } else {
+                return interp_ok(false);
+            }
         }
 
         this.set_thread_name(thread, name);
diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs
index f8861085fe5..f7566a8112d 100644
--- a/src/tools/miri/src/shims/windows/sync.rs
+++ b/src/tools/miri/src/shims/windows/sync.rs
@@ -202,7 +202,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 Scalar::from_i32(1), // retval_succ
                 Scalar::from_i32(0), // retval_timeout
                 dest.clone(),
-                this.eval_windows("c", "ERROR_TIMEOUT"),
+                this.eval_windows("c", "ERROR_TIMEOUT"), // errno_timeout
             );
         }
 
diff --git a/src/tools/miri/test_dependencies/Cargo.lock b/src/tools/miri/test_dependencies/Cargo.lock
index 64bfc84ef20..0a5e9f62dd9 100644
--- a/src/tools/miri/test_dependencies/Cargo.lock
+++ b/src/tools/miri/test_dependencies/Cargo.lock
@@ -1,6 +1,6 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-version = 3
+version = 4
 
 [[package]]
 name = "addr2line"
@@ -128,9 +128,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.158"
+version = "0.2.161"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
+checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
 
 [[package]]
 name = "linux-raw-sys"
diff --git a/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.rs b/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.rs
new file mode 100644
index 00000000000..4b731866aca
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.rs
@@ -0,0 +1,10 @@
+//! Ensure we report UB when the buffer is smaller than 16 bytes (even if the thread
+//! name would fit in the smaller buffer).
+//@only-target: android  # Miri supports prctl for Android only
+
+fn main() {
+    let mut buf = vec![0u8; 15];
+    unsafe {
+        libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr().cast::<libc::c_char>()); //~ ERROR: memory access failed: expected a pointer to 16 bytes of memory, but got alloc952 which is only 15 bytes from the end of the allocation
+    }
+}
diff --git a/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr b/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr
new file mode 100644
index 00000000000..275a38e593c
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr
@@ -0,0 +1,21 @@
+error: Undefined Behavior: memory access failed: expected a pointer to 16 bytes of memory, but got ALLOC which is only 15 bytes from the end of the allocation
+  --> tests/fail-dep/libc/prctl-threadname.rs:LL:CC
+   |
+LL |         libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr().cast::<libc::c_char>());
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: expected a pointer to 16 bytes of memory, but got ALLOC which is only 15 bytes from the end of the allocation
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+help: ALLOC was allocated here:
+  --> tests/fail-dep/libc/prctl-threadname.rs:LL:CC
+   |
+LL |     let mut buf = vec![0u8; 15];
+   |                   ^^^^^^^^^^^^^
+   = note: BACKTRACE (of the first span):
+   = note: inside `main` at tests/fail-dep/libc/prctl-threadname.rs:LL:CC
+   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs b/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs
index f4c009456d2..55491da9f60 100644
--- a/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs
+++ b/src/tools/miri/tests/fail-dep/libc/socketpair-data-race.rs
@@ -1,7 +1,7 @@
 //! This is a regression test for <https://github.com/rust-lang/miri/issues/3947>: we had some
 //! faulty logic around `release_clock` that led to this code not reporting a data race.
 //@ignore-target: windows # no libc socketpair on Windows
-//@compile-flags: -Zmiri-preemption-rate=0
+//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-rate=0
 use std::thread;
 
 fn main() {
diff --git a/src/tools/miri/tests/fail/provenance/ptr_invalid.rs b/src/tools/miri/tests/fail/provenance/ptr_invalid.rs
index d4479f32e56..c91f4ec158f 100644
--- a/src/tools/miri/tests/fail/provenance/ptr_invalid.rs
+++ b/src/tools/miri/tests/fail/provenance/ptr_invalid.rs
@@ -1,4 +1,3 @@
-
 // Ensure that a `ptr::without_provenance` ptr is truly invalid.
 fn main() {
     let x = 42;
diff --git a/src/tools/miri/tests/fail/tail_calls/dangling-local-var.rs b/src/tools/miri/tests/fail/tail_calls/dangling-local-var.rs
new file mode 100644
index 00000000000..b69f7ee9dff
--- /dev/null
+++ b/src/tools/miri/tests/fail/tail_calls/dangling-local-var.rs
@@ -0,0 +1,16 @@
+#![feature(explicit_tail_calls)]
+#![allow(incomplete_features)]
+
+fn g(x: *const i32) {
+    let _val = unsafe { *x }; //~ERROR: has been freed, so this pointer is dangling
+}
+
+fn f(_x: *const i32) {
+    let local = 0;
+    let ptr = &local as *const i32;
+    become g(ptr)
+}
+
+fn main() {
+    f(std::ptr::null());
+}
diff --git a/src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr b/src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr
new file mode 100644
index 00000000000..6acd69ab3f8
--- /dev/null
+++ b/src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr
@@ -0,0 +1,30 @@
+error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
+  --> tests/fail/tail_calls/dangling-local-var.rs:LL:CC
+   |
+LL |     let _val = unsafe { *x };
+   |                         ^^ memory access failed: ALLOC has been freed, so this pointer is dangling
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+help: ALLOC was allocated here:
+  --> tests/fail/tail_calls/dangling-local-var.rs:LL:CC
+   |
+LL |     let local = 0;
+   |         ^^^^^
+help: ALLOC was deallocated here:
+  --> tests/fail/tail_calls/dangling-local-var.rs:LL:CC
+   |
+LL | }
+   | ^
+   = note: BACKTRACE (of the first span):
+   = note: inside `g` at tests/fail/tail_calls/dangling-local-var.rs:LL:CC
+note: inside `main`
+  --> tests/fail/tail_calls/dangling-local-var.rs:LL:CC
+   |
+LL |     f(std::ptr::null());
+   |     ^^^^^^^^^^^^^^^^^^^
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/pass-dep/libc/prctl-threadname.rs b/src/tools/miri/tests/pass-dep/libc/prctl-threadname.rs
new file mode 100644
index 00000000000..87ae3753fea
--- /dev/null
+++ b/src/tools/miri/tests/pass-dep/libc/prctl-threadname.rs
@@ -0,0 +1,74 @@
+//@only-target: android  # Miri supports prctl for Android only
+use std::ffi::{CStr, CString};
+use std::thread;
+
+// The Linux kernel all names 16 bytes long including the null terminator.
+const MAX_THREAD_NAME_LEN: usize = 16;
+
+fn main() {
+    // The short name should be shorter than 16 bytes which POSIX promises
+    // for thread names. The length includes a null terminator.
+    let short_name = "test_named".to_owned();
+    let long_name = std::iter::once("test_named_thread_truncation")
+        .chain(std::iter::repeat(" yada").take(100))
+        .collect::<String>();
+
+    fn set_thread_name(name: &CStr) -> i32 {
+        unsafe { libc::prctl(libc::PR_SET_NAME, name.as_ptr().cast::<libc::c_char>()) }
+    }
+
+    fn get_thread_name(name: &mut [u8]) -> i32 {
+        assert!(name.len() >= MAX_THREAD_NAME_LEN);
+        unsafe { libc::prctl(libc::PR_GET_NAME, name.as_mut_ptr().cast::<libc::c_char>()) }
+    }
+
+    // Set name via Rust API, get it via prctl.
+    let long_name2 = long_name.clone();
+    thread::Builder::new()
+        .name(long_name.clone())
+        .spawn(move || {
+            let mut buf = vec![0u8; MAX_THREAD_NAME_LEN];
+            assert_eq!(get_thread_name(&mut buf), 0);
+            let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
+            let truncated_name = &long_name2[..long_name2.len().min(MAX_THREAD_NAME_LEN - 1)];
+            assert_eq!(cstr.to_bytes(), truncated_name.as_bytes());
+        })
+        .unwrap()
+        .join()
+        .unwrap();
+
+    // Set name via prctl and get it again (short name).
+    thread::Builder::new()
+        .spawn(move || {
+            // Set short thread name.
+            let cstr = CString::new(short_name.clone()).unwrap();
+            assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN); // this should fit
+            assert_eq!(set_thread_name(&cstr), 0);
+
+            let mut buf = vec![0u8; MAX_THREAD_NAME_LEN];
+            assert_eq!(get_thread_name(&mut buf), 0);
+            let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
+            assert_eq!(cstr.to_bytes(), short_name.as_bytes());
+        })
+        .unwrap()
+        .join()
+        .unwrap();
+
+    // Set name via prctl and get it again (long name).
+    thread::Builder::new()
+        .spawn(move || {
+            // Set full thread name.
+            let cstr = CString::new(long_name.clone()).unwrap();
+            assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN);
+            // Names are truncated by the Linux kernel.
+            assert_eq!(set_thread_name(&cstr), 0);
+
+            let mut buf = vec![0u8; MAX_THREAD_NAME_LEN];
+            assert_eq!(get_thread_name(&mut buf), 0);
+            let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
+            assert_eq!(cstr.to_bytes(), &long_name.as_bytes()[..(MAX_THREAD_NAME_LEN - 1)]);
+        })
+        .unwrap()
+        .join()
+        .unwrap();
+}
diff --git a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs
index 404ef7cc42d..0e5b501bbcc 100644
--- a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs
+++ b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs
@@ -1,4 +1,5 @@
 //@ignore-target: windows # No pthreads on Windows
+//@ignore-target: android # No pthread_{get,set}_name on Android
 use std::ffi::{CStr, CString};
 use std::thread;
 
@@ -65,6 +66,22 @@ fn main() {
         }
     }
 
+    // Set name via Rust API, get it via pthreads.
+    let long_name2 = long_name.clone();
+    thread::Builder::new()
+        .name(long_name.clone())
+        .spawn(move || {
+            let mut buf = vec![0u8; long_name2.len() + 1];
+            assert_eq!(get_thread_name(&mut buf), 0);
+            let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
+            let truncated_name = &long_name2[..long_name2.len().min(MAX_THREAD_NAME_LEN - 1)];
+            assert_eq!(cstr.to_bytes(), truncated_name.as_bytes());
+        })
+        .unwrap()
+        .join()
+        .unwrap();
+
+    // Set name via pthread and get it again (short name).
     thread::Builder::new()
         .spawn(move || {
             // Set short thread name.
@@ -130,6 +147,7 @@ fn main() {
         .join()
         .unwrap();
 
+    // Set name via pthread and get it again (long name).
     thread::Builder::new()
         .spawn(move || {
             // Set full thread name.
diff --git a/src/tools/miri/tests/pass/dyn-upcast.rs b/src/tools/miri/tests/pass/dyn-upcast.rs
index 306e9ab9c67..61410f7c4e0 100644
--- a/src/tools/miri/tests/pass/dyn-upcast.rs
+++ b/src/tools/miri/tests/pass/dyn-upcast.rs
@@ -433,7 +433,8 @@ fn replace_vptr() {
 }
 
 fn drop_principal() {
-    use std::{alloc::Layout, any::Any};
+    use std::alloc::Layout;
+    use std::any::Any;
 
     const fn yeet_principal(x: Box<dyn Any + Send>) -> Box<dyn Send> {
         x
diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs
index 853d3e80517..66843ca584b 100644
--- a/src/tools/miri/tests/pass/float.rs
+++ b/src/tools/miri/tests/pass/float.rs
@@ -157,13 +157,18 @@ fn basic() {
     assert_eq(-{ 5.0_f128 }, -5.0_f128);
 
     // infinities, NaN
-    // FIXME(f16_f128): add when constants and `is_infinite` are available
+    assert!((5.0_f16 / 0.0).is_infinite());
+    assert_ne!({ 5.0_f16 / 0.0 }, { -5.0_f16 / 0.0 });
     assert!((5.0_f32 / 0.0).is_infinite());
     assert_ne!({ 5.0_f32 / 0.0 }, { -5.0_f32 / 0.0 });
     assert!((5.0_f64 / 0.0).is_infinite());
     assert_ne!({ 5.0_f64 / 0.0 }, { 5.0_f64 / -0.0 });
+    assert!((5.0_f128 / 0.0).is_infinite());
+    assert_ne!({ 5.0_f128 / 0.0 }, { 5.0_f128 / -0.0 });
+    assert_ne!(f16::NAN, f16::NAN);
     assert_ne!(f32::NAN, f32::NAN);
     assert_ne!(f64::NAN, f64::NAN);
+    assert_ne!(f128::NAN, f128::NAN);
 
     // negative zero
     let posz = 0.0f16;
@@ -215,9 +220,14 @@ fn basic() {
     assert!((black_box(-1.0f128) % 1.0).is_sign_negative());
     assert!((black_box(-1.0f128) % -1.0).is_sign_negative());
 
-    // FIXME(f16_f128): add when `abs` is available
+    assert_eq!((-1.0f16).abs(), 1.0f16);
+    assert_eq!(34.2f16.abs(), 34.2f16);
     assert_eq!((-1.0f32).abs(), 1.0f32);
+    assert_eq!(34.2f32.abs(), 34.2f32);
+    assert_eq!((-1.0f64).abs(), 1.0f64);
     assert_eq!(34.2f64.abs(), 34.2f64);
+    assert_eq!((-1.0f128).abs(), 1.0f128);
+    assert_eq!(34.2f128.abs(), 34.2f128);
 }
 
 /// Test casts from floats to ints and back
@@ -654,6 +664,14 @@ fn casts() {
 }
 
 fn ops() {
+    // f16 min/max
+    assert_eq((1.0_f16).max(-1.0), 1.0);
+    assert_eq((1.0_f16).min(-1.0), -1.0);
+    assert_eq(f16::NAN.min(9.0), 9.0);
+    assert_eq(f16::NAN.max(-9.0), -9.0);
+    assert_eq((9.0_f16).min(f16::NAN), 9.0);
+    assert_eq((-9.0_f16).max(f16::NAN), -9.0);
+
     // f32 min/max
     assert_eq((1.0 as f32).max(-1.0), 1.0);
     assert_eq((1.0 as f32).min(-1.0), -1.0);
@@ -670,6 +688,21 @@ fn ops() {
     assert_eq((9.0 as f64).min(f64::NAN), 9.0);
     assert_eq((-9.0 as f64).max(f64::NAN), -9.0);
 
+    // f128 min/max
+    assert_eq((1.0_f128).max(-1.0), 1.0);
+    assert_eq((1.0_f128).min(-1.0), -1.0);
+    assert_eq(f128::NAN.min(9.0), 9.0);
+    assert_eq(f128::NAN.max(-9.0), -9.0);
+    assert_eq((9.0_f128).min(f128::NAN), 9.0);
+    assert_eq((-9.0_f128).max(f128::NAN), -9.0);
+
+    // f16 copysign
+    assert_eq(3.5_f16.copysign(0.42), 3.5_f16);
+    assert_eq(3.5_f16.copysign(-0.42), -3.5_f16);
+    assert_eq((-3.5_f16).copysign(0.42), 3.5_f16);
+    assert_eq((-3.5_f16).copysign(-0.42), -3.5_f16);
+    assert!(f16::NAN.copysign(1.0).is_nan());
+
     // f32 copysign
     assert_eq(3.5_f32.copysign(0.42), 3.5_f32);
     assert_eq(3.5_f32.copysign(-0.42), -3.5_f32);
@@ -683,6 +716,13 @@ fn ops() {
     assert_eq((-3.5_f64).copysign(0.42), 3.5_f64);
     assert_eq((-3.5_f64).copysign(-0.42), -3.5_f64);
     assert!(f64::NAN.copysign(1.0).is_nan());
+
+    // f128 copysign
+    assert_eq(3.5_f128.copysign(0.42), 3.5_f128);
+    assert_eq(3.5_f128.copysign(-0.42), -3.5_f128);
+    assert_eq((-3.5_f128).copysign(0.42), 3.5_f128);
+    assert_eq((-3.5_f128).copysign(-0.42), -3.5_f128);
+    assert!(f128::NAN.copysign(1.0).is_nan());
 }
 
 /// Tests taken from rustc test suite.
@@ -807,6 +847,18 @@ fn nan_casts() {
 
 fn rounding() {
     // Test cases taken from the library's tests for this feature
+    // f16
+    assert_eq(2.5f16.round_ties_even(), 2.0f16);
+    assert_eq(1.0f16.round_ties_even(), 1.0f16);
+    assert_eq(1.3f16.round_ties_even(), 1.0f16);
+    assert_eq(1.5f16.round_ties_even(), 2.0f16);
+    assert_eq(1.7f16.round_ties_even(), 2.0f16);
+    assert_eq(0.0f16.round_ties_even(), 0.0f16);
+    assert_eq((-0.0f16).round_ties_even(), -0.0f16);
+    assert_eq((-1.0f16).round_ties_even(), -1.0f16);
+    assert_eq((-1.3f16).round_ties_even(), -1.0f16);
+    assert_eq((-1.5f16).round_ties_even(), -2.0f16);
+    assert_eq((-1.7f16).round_ties_even(), -2.0f16);
     // f32
     assert_eq(2.5f32.round_ties_even(), 2.0f32);
     assert_eq(1.0f32.round_ties_even(), 1.0f32);
@@ -831,23 +883,59 @@ fn rounding() {
     assert_eq((-1.3f64).round_ties_even(), -1.0f64);
     assert_eq((-1.5f64).round_ties_even(), -2.0f64);
     assert_eq((-1.7f64).round_ties_even(), -2.0f64);
-
+    // f128
+    assert_eq(2.5f128.round_ties_even(), 2.0f128);
+    assert_eq(1.0f128.round_ties_even(), 1.0f128);
+    assert_eq(1.3f128.round_ties_even(), 1.0f128);
+    assert_eq(1.5f128.round_ties_even(), 2.0f128);
+    assert_eq(1.7f128.round_ties_even(), 2.0f128);
+    assert_eq(0.0f128.round_ties_even(), 0.0f128);
+    assert_eq((-0.0f128).round_ties_even(), -0.0f128);
+    assert_eq((-1.0f128).round_ties_even(), -1.0f128);
+    assert_eq((-1.3f128).round_ties_even(), -1.0f128);
+    assert_eq((-1.5f128).round_ties_even(), -2.0f128);
+    assert_eq((-1.7f128).round_ties_even(), -2.0f128);
+
+    assert_eq!(3.8f16.floor(), 3.0f16);
+    assert_eq!((-1.1f16).floor(), -2.0f16);
     assert_eq!(3.8f32.floor(), 3.0f32);
+    assert_eq!((-1.1f32).floor(), -2.0f32);
+    assert_eq!(3.8f64.floor(), 3.0f64);
     assert_eq!((-1.1f64).floor(), -2.0f64);
+    assert_eq!(3.8f128.floor(), 3.0f128);
+    assert_eq!((-1.1f128).floor(), -2.0f128);
 
+    assert_eq!(3.8f16.ceil(), 4.0f16);
+    assert_eq!((-2.3f16).ceil(), -2.0f16);
+    assert_eq!(3.8f32.ceil(), 4.0f32);
     assert_eq!((-2.3f32).ceil(), -2.0f32);
     assert_eq!(3.8f64.ceil(), 4.0f64);
+    assert_eq!((-2.3f64).ceil(), -2.0f64);
+    assert_eq!(3.8f128.ceil(), 4.0f128);
+    assert_eq!((-2.3f128).ceil(), -2.0f128);
 
+    assert_eq!(0.1f16.trunc(), 0.0f16);
+    assert_eq!((-0.1f16).trunc(), 0.0f16);
     assert_eq!(0.1f32.trunc(), 0.0f32);
+    assert_eq!((-0.1f32).trunc(), 0.0f32);
+    assert_eq!(0.1f64.trunc(), 0.0f64);
     assert_eq!((-0.1f64).trunc(), 0.0f64);
+    assert_eq!(0.1f128.trunc(), 0.0f128);
+    assert_eq!((-0.1f128).trunc(), 0.0f128);
 
+    assert_eq!(3.3_f16.round(), 3.0);
+    assert_eq!(2.5_f16.round(), 3.0);
     assert_eq!(3.3_f32.round(), 3.0);
     assert_eq!(2.5_f32.round(), 3.0);
     assert_eq!(3.9_f64.round(), 4.0);
     assert_eq!(2.5_f64.round(), 3.0);
+    assert_eq!(3.9_f128.round(), 4.0);
+    assert_eq!(2.5_f128.round(), 3.0);
 }
 
 fn mul_add() {
+    // FIXME(f16_f128): add when supported
+
     assert_eq!(3.0f32.mul_add(2.0f32, 5.0f32), 11.0);
     assert_eq!(0.0f32.mul_add(-2.0, f32::consts::E), f32::consts::E);
     assert_eq!(3.0f64.mul_add(2.0, 5.0), 11.0);
@@ -983,7 +1071,7 @@ fn test_fast() {
     use std::intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast};
 
     #[inline(never)]
-    pub fn test_operations_f64(a: f64, b: f64) {
+    pub fn test_operations_f16(a: f16, b: f16) {
         // make sure they all map to the correct operation
         unsafe {
             assert_eq!(fadd_fast(a, b), a + b);
@@ -1006,10 +1094,38 @@ fn test_fast() {
         }
     }
 
-    test_operations_f64(1., 2.);
-    test_operations_f64(10., 5.);
+    #[inline(never)]
+    pub fn test_operations_f64(a: f64, b: f64) {
+        // make sure they all map to the correct operation
+        unsafe {
+            assert_eq!(fadd_fast(a, b), a + b);
+            assert_eq!(fsub_fast(a, b), a - b);
+            assert_eq!(fmul_fast(a, b), a * b);
+            assert_eq!(fdiv_fast(a, b), a / b);
+            assert_eq!(frem_fast(a, b), a % b);
+        }
+    }
+
+    #[inline(never)]
+    pub fn test_operations_f128(a: f128, b: f128) {
+        // make sure they all map to the correct operation
+        unsafe {
+            assert_eq!(fadd_fast(a, b), a + b);
+            assert_eq!(fsub_fast(a, b), a - b);
+            assert_eq!(fmul_fast(a, b), a * b);
+            assert_eq!(fdiv_fast(a, b), a / b);
+            assert_eq!(frem_fast(a, b), a % b);
+        }
+    }
+
+    test_operations_f16(11., 2.);
+    test_operations_f16(10., 15.);
     test_operations_f32(11., 2.);
     test_operations_f32(10., 15.);
+    test_operations_f64(1., 2.);
+    test_operations_f64(10., 5.);
+    test_operations_f128(1., 2.);
+    test_operations_f128(10., 5.);
 }
 
 fn test_algebraic() {
@@ -1018,7 +1134,7 @@ fn test_algebraic() {
     };
 
     #[inline(never)]
-    pub fn test_operations_f64(a: f64, b: f64) {
+    pub fn test_operations_f16(a: f16, b: f16) {
         // make sure they all map to the correct operation
         assert_eq!(fadd_algebraic(a, b), a + b);
         assert_eq!(fsub_algebraic(a, b), a - b);
@@ -1037,15 +1153,41 @@ fn test_algebraic() {
         assert_eq!(frem_algebraic(a, b), a % b);
     }
 
-    test_operations_f64(1., 2.);
-    test_operations_f64(10., 5.);
+    #[inline(never)]
+    pub fn test_operations_f64(a: f64, b: f64) {
+        // make sure they all map to the correct operation
+        assert_eq!(fadd_algebraic(a, b), a + b);
+        assert_eq!(fsub_algebraic(a, b), a - b);
+        assert_eq!(fmul_algebraic(a, b), a * b);
+        assert_eq!(fdiv_algebraic(a, b), a / b);
+        assert_eq!(frem_algebraic(a, b), a % b);
+    }
+
+    #[inline(never)]
+    pub fn test_operations_f128(a: f128, b: f128) {
+        // make sure they all map to the correct operation
+        assert_eq!(fadd_algebraic(a, b), a + b);
+        assert_eq!(fsub_algebraic(a, b), a - b);
+        assert_eq!(fmul_algebraic(a, b), a * b);
+        assert_eq!(fdiv_algebraic(a, b), a / b);
+        assert_eq!(frem_algebraic(a, b), a % b);
+    }
+
+    test_operations_f16(11., 2.);
+    test_operations_f16(10., 15.);
     test_operations_f32(11., 2.);
     test_operations_f32(10., 15.);
+    test_operations_f64(1., 2.);
+    test_operations_f64(10., 5.);
+    test_operations_f128(1., 2.);
+    test_operations_f128(10., 5.);
 }
 
 fn test_fmuladd() {
     use std::intrinsics::{fmuladdf32, fmuladdf64};
 
+    // FIXME(f16_f128): add when supported
+
     #[inline(never)]
     pub fn test_operations_f32(a: f32, b: f32, c: f32) {
         assert_approx_eq!(unsafe { fmuladdf32(a, b, c) }, a * b + c);
diff --git a/tests/crashes/126725.rs b/tests/crashes/126725.rs
deleted file mode 100644
index d7a7d21ae42..00000000000
--- a/tests/crashes/126725.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-//@ known-bug: rust-lang/rust#126725
-trait Foo {
-    fn foo<'a>(&'a self) -> <&'a impl Sized as Bar>::Output;
-}
-
-trait Bar {
-    type Output;
-}
-
-struct X(i32);
-
-impl<'a> Bar for &'a X {
-    type Output = &'a i32;
-}
-
-impl Foo for X {
-    fn foo<'a>(&'a self) -> <&'a Self as Bar>::Output {
-        &self.0
-    }
-}
diff --git a/tests/ui/impl-trait/impl_trait_projections.rs b/tests/ui/impl-trait/impl_trait_projections.rs
index 365ac85e2f6..2c277aee06d 100644
--- a/tests/ui/impl-trait/impl_trait_projections.rs
+++ b/tests/ui/impl-trait/impl_trait_projections.rs
@@ -10,30 +10,27 @@ fn path_parametrized_type_is_allowed() -> option::Option<impl Debug> {
 }
 
 fn projection_is_disallowed(x: impl Iterator) -> <impl Iterator>::Item {
-//~^ ERROR `impl Trait` is not allowed in path parameters
-//~| ERROR `impl Trait` is not allowed in path parameters
+//~^ ERROR `impl Trait` is not allowed in paths
     x.next().unwrap()
 }
 
 fn projection_with_named_trait_is_disallowed(mut x: impl Iterator)
     -> <impl Iterator as Iterator>::Item
-//~^ ERROR `impl Trait` is not allowed in path parameters
+//~^ ERROR `impl Trait` is not allowed in paths
 {
     x.next().unwrap()
 }
 
 fn projection_with_named_trait_inside_path_is_disallowed()
     -> <::std::ops::Range<impl Debug> as Iterator>::Item
-//~^ ERROR `impl Trait` is not allowed in path parameters
-//~| ERROR `impl Debug: Step` is not satisfied
+//~^ ERROR `impl Trait` is not allowed in paths
 {
-    //~^ ERROR `impl Debug: Step` is not satisfied
     (1i32..100).next().unwrap()
 }
 
 fn projection_from_impl_trait_inside_dyn_trait_is_disallowed()
     -> <dyn Iterator<Item = impl Debug> as Iterator>::Item
-//~^ ERROR `impl Trait` is not allowed in path parameters
+//~^ ERROR `impl Trait` is not allowed in paths
 {
     panic!()
 }
diff --git a/tests/ui/impl-trait/impl_trait_projections.stderr b/tests/ui/impl-trait/impl_trait_projections.stderr
index d62e3ac4183..5e0b80fcd59 100644
--- a/tests/ui/impl-trait/impl_trait_projections.stderr
+++ b/tests/ui/impl-trait/impl_trait_projections.stderr
@@ -1,73 +1,35 @@
-error[E0667]: `impl Trait` is not allowed in path parameters
+error[E0562]: `impl Trait` is not allowed in paths
   --> $DIR/impl_trait_projections.rs:12:51
    |
 LL | fn projection_is_disallowed(x: impl Iterator) -> <impl Iterator>::Item {
    |                                                   ^^^^^^^^^^^^^
+   |
+   = note: `impl Trait` is only allowed in arguments and return types of functions and methods
 
-error[E0667]: `impl Trait` is not allowed in path parameters
-  --> $DIR/impl_trait_projections.rs:19:9
+error[E0562]: `impl Trait` is not allowed in paths
+  --> $DIR/impl_trait_projections.rs:18:9
    |
 LL |     -> <impl Iterator as Iterator>::Item
    |         ^^^^^^^^^^^^^
+   |
+   = note: `impl Trait` is only allowed in arguments and return types of functions and methods
 
-error[E0667]: `impl Trait` is not allowed in path parameters
-  --> $DIR/impl_trait_projections.rs:26:27
+error[E0562]: `impl Trait` is not allowed in paths
+  --> $DIR/impl_trait_projections.rs:25:27
    |
 LL |     -> <::std::ops::Range<impl Debug> as Iterator>::Item
    |                           ^^^^^^^^^^
+   |
+   = note: `impl Trait` is only allowed in arguments and return types of functions and methods
 
-error[E0667]: `impl Trait` is not allowed in path parameters
-  --> $DIR/impl_trait_projections.rs:35:29
+error[E0562]: `impl Trait` is not allowed in paths
+  --> $DIR/impl_trait_projections.rs:32:29
    |
 LL |     -> <dyn Iterator<Item = impl Debug> as Iterator>::Item
    |                             ^^^^^^^^^^
-
-error[E0667]: `impl Trait` is not allowed in path parameters
-  --> $DIR/impl_trait_projections.rs:12:51
-   |
-LL | fn projection_is_disallowed(x: impl Iterator) -> <impl Iterator>::Item {
-   |                                                   ^^^^^^^^^^^^^
-
-error[E0277]: the trait bound `impl Debug: Step` is not satisfied
-  --> $DIR/impl_trait_projections.rs:26:8
-   |
-LL |     -> <::std::ops::Range<impl Debug> as Iterator>::Item
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Step` is not implemented for `impl Debug`, which is required by `std::ops::Range<impl Debug>: Iterator`
-   |
-   = help: the following other types implement trait `Step`:
-             Char
-             Ipv4Addr
-             Ipv6Addr
-             char
-             i128
-             i16
-             i32
-             i64
-           and 8 others
-   = note: required for `std::ops::Range<impl Debug>` to implement `Iterator`
-
-error[E0277]: the trait bound `impl Debug: Step` is not satisfied
-  --> $DIR/impl_trait_projections.rs:29:1
-   |
-LL | / {
-LL | |
-LL | |     (1i32..100).next().unwrap()
-LL | | }
-   | |_^ the trait `Step` is not implemented for `impl Debug`, which is required by `std::ops::Range<impl Debug>: Iterator`
    |
-   = help: the following other types implement trait `Step`:
-             Char
-             Ipv4Addr
-             Ipv6Addr
-             char
-             i128
-             i16
-             i32
-             i64
-           and 8 others
-   = note: required for `std::ops::Range<impl Debug>` to implement `Iterator`
+   = note: `impl Trait` is only allowed in arguments and return types of functions and methods
 
-error: aborting due to 7 previous errors
+error: aborting due to 4 previous errors
 
-Some errors have detailed explanations: E0277, E0667.
-For more information about an error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0562`.
diff --git a/tests/ui/impl-trait/in-trait/bad-projection-from-opaque.rs b/tests/ui/impl-trait/in-trait/bad-projection-from-opaque.rs
new file mode 100644
index 00000000000..c2c22cd1abf
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/bad-projection-from-opaque.rs
@@ -0,0 +1,22 @@
+// issue: rust-lang/rust#126725
+
+trait Foo {
+    fn foo<'a>() -> <&'a impl Sized as Bar>::Output;
+    //~^ ERROR `impl Trait` is not allowed in paths
+}
+
+trait Bar {
+    type Output;
+}
+
+impl<'a> Bar for &'a () {
+    type Output = &'a i32;
+}
+
+impl Foo for () {
+    fn foo<'a>() -> <&'a Self as Bar>::Output {
+        &0
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/impl-trait/in-trait/bad-projection-from-opaque.stderr b/tests/ui/impl-trait/in-trait/bad-projection-from-opaque.stderr
new file mode 100644
index 00000000000..bea7ccd1a18
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/bad-projection-from-opaque.stderr
@@ -0,0 +1,11 @@
+error[E0562]: `impl Trait` is not allowed in paths
+  --> $DIR/bad-projection-from-opaque.rs:4:26
+   |
+LL |     fn foo<'a>() -> <&'a impl Sized as Bar>::Output;
+   |                          ^^^^^^^^^^
+   |
+   = note: `impl Trait` is only allowed in arguments and return types of functions and methods
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0562`.
diff --git a/tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.rs b/tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.rs
index c5ecd1caae1..9466668b1dc 100644
--- a/tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.rs
+++ b/tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.rs
@@ -6,7 +6,7 @@
 pub trait Bar { }
 pub trait Quux<T> { type Assoc; }
 pub fn demo(_: impl Quux<(), Assoc=<() as Quux<impl Bar>>::Assoc>) { }
-//~^ ERROR `impl Trait` is not allowed in path parameters
+//~^ ERROR `impl Trait` is not allowed in paths
 impl<T> Quux<T> for () { type Assoc = u32; }
 
 fn main() { }
diff --git a/tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.stderr b/tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.stderr
index 55f47785f0e..25547dfa66f 100644
--- a/tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.stderr
+++ b/tests/ui/impl-trait/issues/issue-57979-impl-trait-in-path.stderr
@@ -1,9 +1,11 @@
-error[E0667]: `impl Trait` is not allowed in path parameters
+error[E0562]: `impl Trait` is not allowed in paths
   --> $DIR/issue-57979-impl-trait-in-path.rs:8:48
    |
 LL | pub fn demo(_: impl Quux<(), Assoc=<() as Quux<impl Bar>>::Assoc>) { }
    |                                                ^^^^^^^^
+   |
+   = note: `impl Trait` is only allowed in arguments and return types of functions and methods
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0667`.
+For more information about this error, try `rustc --explain E0562`.