about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_data_structures/src/unord.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs23
-rw-r--r--compiler/rustc_incremental/src/persist/dirty_clean.rs10
-rw-r--r--compiler/rustc_incremental/src/persist/fs.rs11
-rw-r--r--compiler/rustc_resolve/src/ident.rs89
-rw-r--r--compiler/rustc_target/src/spec/aarch64_apple_tvos.rs4
-rw-r--r--compiler/rustc_target/src/spec/apple/tests.rs5
-rw-r--r--compiler/rustc_target/src/spec/apple_base.rs18
-rw-r--r--compiler/rustc_target/src/spec/x86_64_apple_tvos.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs42
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs41
-rw-r--r--compiler/rustc_ty_utils/src/opaque_types.rs80
-rw-r--r--library/core/src/ffi/mod.rs23
-rw-r--r--library/std/build.rs2
-rw-r--r--library/std/src/fs/tests.rs28
-rw-r--r--library/std/src/os/ios/fs.rs2
-rw-r--r--library/std/src/os/mod.rs3
-rw-r--r--library/std/src/os/unix/mod.rs3
-rw-r--r--library/std/src/os/unix/net/stream.rs3
-rw-r--r--library/std/src/os/unix/ucred.rs4
-rw-r--r--library/std/src/os/unix/ucred/tests.rs9
-rw-r--r--library/std/src/personality/gcc.rs2
-rw-r--r--library/std/src/sys/unix/args.rs4
-rw-r--r--library/std/src/sys/unix/env.rs11
-rw-r--r--library/std/src/sys/unix/fd.rs6
-rw-r--r--library/std/src/sys/unix/fs.rs38
-rw-r--r--library/std/src/sys/unix/locks/pthread_condvar.rs3
-rw-r--r--library/std/src/sys/unix/mod.rs3
-rw-r--r--library/std/src/sys/unix/os.rs12
-rw-r--r--library/std/src/sys/unix/process/process_unix.rs51
-rw-r--r--library/std/src/sys/unix/rand.rs3
-rw-r--r--library/std/src/sys/unix/thread.rs11
-rw-r--r--library/std/src/sys/unix/thread_parking/pthread.rs3
-rw-r--r--library/std/src/sys/unix/time.rs6
-rw-r--r--library/std/src/sys/unix/weak.rs4
-rw-r--r--library/std/src/sys_common/net.rs2
-rw-r--r--library/std/src/thread/tests.rs1
-rw-r--r--library/unwind/src/libunwind.rs2
-rw-r--r--src/doc/rustc/src/SUMMARY.md1
-rw-r--r--src/doc/rustc/src/platform-support.md4
-rw-r--r--src/doc/rustc/src/platform-support/apple-tvos.md85
-rw-r--r--src/librustdoc/html/render/mod.rs8
-rw-r--r--src/librustdoc/html/templates/item_info.html2
-rw-r--r--src/librustdoc/html/templates/item_union.html11
-rw-r--r--src/librustdoc/html/templates/print_item.html4
-rw-r--r--tests/rustdoc-gui/fields.goml18
-rw-r--r--tests/rustdoc-gui/src/test_docs/lib.rs21
-rw-r--r--tests/rustdoc/generic-associated-types/gats.rs4
-rw-r--r--tests/rustdoc/union-fields-html.rs11
-rw-r--r--tests/ui/impl-trait/in-trait/suggest-missing-item.fixed5
-rw-r--r--tests/ui/impl-trait/in-trait/suggest-missing-item.rs2
-rw-r--r--tests/ui/impl-trait/in-trait/suggest-missing-item.stderr9
-rw-r--r--tests/ui/type-alias-impl-trait/impl-trait-in-type-alias-with-bad-substs.rs28
-rw-r--r--tests/ui/type-alias-impl-trait/impl-trait-in-type-alias-with-bad-substs.stderr20
-rw-r--r--tests/ui/type-alias-impl-trait/not-matching-trait-refs-isnt-defining.rs33
-rw-r--r--tests/ui/type-alias-impl-trait/not-matching-trait-refs-isnt-defining.stderr22
57 files changed, 650 insertions, 223 deletions
diff --git a/compiler/rustc_data_structures/src/unord.rs b/compiler/rustc_data_structures/src/unord.rs
index e18c7b415f6..2b21815b687 100644
--- a/compiler/rustc_data_structures/src/unord.rs
+++ b/compiler/rustc_data_structures/src/unord.rs
@@ -107,6 +107,10 @@ impl<T, I: Iterator<Item = T>> UnordItems<T, I> {
     {
         UnordItems(self.0.flat_map(f))
     }
+
+    pub fn collect<C: From<UnordItems<T, I>>>(self) -> C {
+        self.into()
+    }
 }
 
 impl<T> UnordItems<T, std::iter::Empty<T>> {
@@ -161,10 +165,6 @@ impl<T: Ord, I: Iterator<Item = T>> UnordItems<T, I> {
         items.sort_by_cached_key(|x| x.to_stable_hash_key(hcx));
         items
     }
-
-    pub fn collect<C: From<UnordItems<T, I>>>(self) -> C {
-        self.into()
-    }
 }
 
 /// This is a set collection type that tries very hard to not expose
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 4247869ce76..1dfe053b215 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -470,19 +470,16 @@ fn suggestion_signature<'tcx>(
     );
 
     match assoc.kind {
-        ty::AssocKind::Fn => {
-            // We skip the binder here because the binder would deanonymize all
-            // late-bound regions, and we don't want method signatures to show up
-            // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
-            // regions just fine, showing `fn(&MyType)`.
-            fn_sig_suggestion(
-                tcx,
-                tcx.fn_sig(assoc.def_id).subst(tcx, substs).skip_binder(),
-                assoc.ident(tcx),
-                tcx.predicates_of(assoc.def_id).instantiate_own(tcx, substs),
-                assoc,
-            )
-        }
+        ty::AssocKind::Fn => fn_sig_suggestion(
+            tcx,
+            tcx.liberate_late_bound_regions(
+                assoc.def_id,
+                tcx.fn_sig(assoc.def_id).subst(tcx, substs),
+            ),
+            assoc.ident(tcx),
+            tcx.predicates_of(assoc.def_id).instantiate_own(tcx, substs),
+            assoc,
+        ),
         ty::AssocKind::Type => {
             let (generics, where_clauses) = bounds_from_generic_predicates(
                 tcx,
diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs
index cbe77e7b16d..f9cd01fd80d 100644
--- a/compiler/rustc_incremental/src/persist/dirty_clean.rs
+++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs
@@ -198,7 +198,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
         let (name, mut auto) = self.auto_labels(item_id, attr);
         let except = self.except(attr);
         let loaded_from_disk = self.loaded_from_disk(attr);
-        for e in except.items().map(|x| x.as_str()).into_sorted_stable_ord() {
+        for e in except.items().into_sorted_stable_ord() {
             if !auto.remove(e) {
                 self.tcx.sess.emit_fatal(errors::AssertionAuto { span: attr.span, name, e });
             }
@@ -377,17 +377,15 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
                 continue;
             };
             self.checked_attrs.insert(attr.id);
-            for label in assertion.clean.items().map(|x| x.as_str()).into_sorted_stable_ord() {
+            for label in assertion.clean.items().into_sorted_stable_ord() {
                 let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap();
                 self.assert_clean(item_span, dep_node);
             }
-            for label in assertion.dirty.items().map(|x| x.as_str()).into_sorted_stable_ord() {
+            for label in assertion.dirty.items().into_sorted_stable_ord() {
                 let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap();
                 self.assert_dirty(item_span, dep_node);
             }
-            for label in
-                assertion.loaded_from_disk.items().map(|x| x.as_str()).into_sorted_stable_ord()
-            {
+            for label in assertion.loaded_from_disk.items().into_sorted_stable_ord() {
                 let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap();
                 self.assert_loaded_from_disk(item_span, dep_node);
             }
diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs
index 243057b99bc..7708deecec7 100644
--- a/compiler/rustc_incremental/src/persist/fs.rs
+++ b/compiler/rustc_incremental/src/persist/fs.rs
@@ -855,12 +855,11 @@ fn all_except_most_recent(
     let most_recent = deletion_candidates.items().map(|(&(timestamp, _), _)| timestamp).max();
 
     if let Some(most_recent) = most_recent {
-        UnordMap::from(
-            deletion_candidates
-                .into_items()
-                .filter(|&((timestamp, _), _)| timestamp != most_recent)
-                .map(|((_, path), lock)| (path, lock)),
-        )
+        deletion_candidates
+            .into_items()
+            .filter(|&((timestamp, _), _)| timestamp != most_recent)
+            .map(|((_, path), lock)| (path, lock))
+            .collect()
     } else {
         UnordMap::default()
     }
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index ec0a8535e71..e29a1626aed 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -1459,60 +1459,47 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 });
             }
 
-            enum FindBindingResult<'a> {
-                Binding(Result<&'a NameBinding<'a>, Determinacy>),
-                Res(Res),
-            }
-            let find_binding_in_ns = |this: &mut Self, ns| {
-                let binding = if let Some(module) = module {
-                    this.resolve_ident_in_module(
-                        module,
-                        ident,
-                        ns,
-                        parent_scope,
-                        finalize,
-                        ignore_binding,
-                    )
-                } else if let Some(ribs) = ribs
-                    && let Some(TypeNS | ValueNS) = opt_ns
-                {
-                    match this.resolve_ident_in_lexical_scope(
-                        ident,
-                        ns,
-                        parent_scope,
-                        finalize,
-                        &ribs[ns],
-                        ignore_binding,
-                    ) {
-                        // we found a locally-imported or available item/module
-                        Some(LexicalScopeBinding::Item(binding)) => Ok(binding),
-                        // we found a local variable or type param
-                        Some(LexicalScopeBinding::Res(res)) => return FindBindingResult::Res(res),
-                        _ => Err(Determinacy::determined(finalize.is_some())),
+            let binding = if let Some(module) = module {
+                self.resolve_ident_in_module(
+                    module,
+                    ident,
+                    ns,
+                    parent_scope,
+                    finalize,
+                    ignore_binding,
+                )
+            } else if let Some(ribs) = ribs && let Some(TypeNS | ValueNS) = opt_ns {
+                match self.resolve_ident_in_lexical_scope(
+                    ident,
+                    ns,
+                    parent_scope,
+                    finalize,
+                    &ribs[ns],
+                    ignore_binding,
+                ) {
+                    // we found a locally-imported or available item/module
+                    Some(LexicalScopeBinding::Item(binding)) => Ok(binding),
+                    // we found a local variable or type param
+                    Some(LexicalScopeBinding::Res(res)) => {
+                        record_segment_res(self, res);
+                        return PathResult::NonModule(PartialRes::with_unresolved_segments(
+                            res,
+                            path.len() - 1,
+                        ));
                     }
-                } else {
-                    let scopes = ScopeSet::All(ns, opt_ns.is_none());
-                    this.early_resolve_ident_in_lexical_scope(
-                        ident,
-                        scopes,
-                        parent_scope,
-                        finalize,
-                        finalize.is_some(),
-                        ignore_binding,
-                    )
-                };
-                FindBindingResult::Binding(binding)
-            };
-            let binding = match find_binding_in_ns(self, ns) {
-                FindBindingResult::Res(res) => {
-                    record_segment_res(self, res);
-                    return PathResult::NonModule(PartialRes::with_unresolved_segments(
-                        res,
-                        path.len() - 1,
-                    ));
+                    _ => Err(Determinacy::determined(finalize.is_some())),
                 }
-                FindBindingResult::Binding(binding) => binding,
+            } else {
+                self.early_resolve_ident_in_lexical_scope(
+                    ident,
+                    ScopeSet::All(ns, opt_ns.is_none()),
+                    parent_scope,
+                    finalize,
+                    finalize.is_some(),
+                    ignore_binding,
+                )
             };
+
             match binding {
                 Ok(binding) => {
                     if segment_idx == 1 {
diff --git a/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs b/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs
index bb7c39ff26b..f7cdfa71c4b 100644
--- a/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs
+++ b/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs
@@ -1,10 +1,10 @@
-use super::apple_base::{opts, Arch};
+use super::apple_base::{opts, tvos_llvm_target, Arch};
 use crate::spec::{FramePointer, Target, TargetOptions};
 
 pub fn target() -> Target {
     let arch = Arch::Arm64;
     Target {
-        llvm_target: "arm64-apple-tvos".into(),
+        llvm_target: tvos_llvm_target(arch).into(),
         pointer_width: 64,
         data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".into(),
         arch: arch.target_arch(),
diff --git a/compiler/rustc_target/src/spec/apple/tests.rs b/compiler/rustc_target/src/spec/apple/tests.rs
index 3c90a5e7e93..3b23ddadcc4 100644
--- a/compiler/rustc_target/src/spec/apple/tests.rs
+++ b/compiler/rustc_target/src/spec/apple/tests.rs
@@ -30,6 +30,9 @@ fn macos_link_environment_unmodified() {
     for target in all_macos_targets {
         // macOS targets should only remove information for cross-compiling, but never
         // for the host.
-        assert_eq!(target.link_env_remove, crate::spec::cvs!["IPHONEOS_DEPLOYMENT_TARGET"]);
+        assert_eq!(
+            target.link_env_remove,
+            crate::spec::cvs!["IPHONEOS_DEPLOYMENT_TARGET", "TVOS_DEPLOYMENT_TARGET"],
+        );
     }
 }
diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs
index ff224631828..8a8d1ab95e8 100644
--- a/compiler/rustc_target/src/spec/apple_base.rs
+++ b/compiler/rustc_target/src/spec/apple_base.rs
@@ -240,7 +240,12 @@ fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow<str>]>
         // Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which
         // may occur when we're linking a custom build script while targeting iOS for example.
         if let Ok(sdkroot) = env::var("SDKROOT") {
-            if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("iPhoneSimulator.platform")
+            if sdkroot.contains("iPhoneOS.platform")
+                || sdkroot.contains("iPhoneSimulator.platform")
+                || sdkroot.contains("AppleTVOS.platform")
+                || sdkroot.contains("AppleTVSimulator.platform")
+                || sdkroot.contains("WatchOS.platform")
+                || sdkroot.contains("WatchSimulator.platform")
             {
                 env_remove.push("SDKROOT".into())
             }
@@ -249,6 +254,7 @@ fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow<str>]>
         // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
         // although this is apparently ignored when using the linker at "/usr/bin/ld".
         env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".into());
+        env_remove.push("TVOS_DEPLOYMENT_TARGET".into());
         env_remove.into()
     } else {
         // Otherwise if cross-compiling for a different OS/SDK, remove any part
@@ -299,6 +305,16 @@ fn tvos_lld_platform_version() -> String {
     format!("{major}.{minor}")
 }
 
+pub fn tvos_llvm_target(arch: Arch) -> String {
+    let (major, minor) = tvos_deployment_target();
+    format!("{}-apple-tvos{}.{}.0", arch.target_name(), major, minor)
+}
+
+pub fn tvos_sim_llvm_target(arch: Arch) -> String {
+    let (major, minor) = tvos_deployment_target();
+    format!("{}-apple-tvos{}.{}.0-simulator", arch.target_name(), major, minor)
+}
+
 fn watchos_deployment_target() -> (u32, u32) {
     // If you are looking for the default deployment target, prefer `rustc --print deployment-target`.
     from_set_deployment_target("WATCHOS_DEPLOYMENT_TARGET").unwrap_or((5, 0))
diff --git a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs
index 76de7d20c4c..2ec4d9569e3 100644
--- a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs
+++ b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs
@@ -1,12 +1,13 @@
-use super::apple_base::{opts, Arch};
+use super::apple_base::{opts, tvos_sim_llvm_target, Arch};
 use crate::spec::{StackProbeType, Target, TargetOptions};
 
 pub fn target() -> Target {
     let arch = Arch::X86_64_sim;
     Target {
-        llvm_target: "x86_64-apple-tvos".into(),
+        llvm_target: tvos_sim_llvm_target(arch).into(),
         pointer_width: 64,
-        data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".into(),
+        data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+            .into(),
         arch: arch.target_arch(),
         options: TargetOptions {
             max_atomic_width: Some(128),
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 15081d65682..a5481714e3e 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -65,12 +65,12 @@ pub use self::specialize::{
 pub use self::structural_match::search_for_structural_match_violation;
 pub use self::structural_normalize::StructurallyNormalizeExt;
 pub use self::util::elaborate;
-pub use self::util::{expand_trait_aliases, TraitAliasExpander};
-pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
 pub use self::util::{
-    supertrait_def_ids, supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item,
-    SupertraitDefIds,
+    check_substs_compatible, supertrait_def_ids, supertraits, transitive_bounds,
+    transitive_bounds_that_define_assoc_item, SupertraitDefIds,
 };
+pub use self::util::{expand_trait_aliases, TraitAliasExpander};
+pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
 
 pub use self::chalk_fulfill::FulfillmentContext as ChalkFulfillmentContext;
 
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index de76587c7b9..81b1ecc0d88 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1,5 +1,6 @@
 //! Code for projecting associated types out of trait references.
 
+use super::check_substs_compatible;
 use super::specialization_graph;
 use super::translate_substs;
 use super::util;
@@ -2378,47 +2379,6 @@ fn confirm_impl_candidate<'cx, 'tcx>(
     }
 }
 
-// Verify that the trait item and its implementation have compatible substs lists
-fn check_substs_compatible<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    assoc_item: ty::AssocItem,
-    substs: ty::SubstsRef<'tcx>,
-) -> bool {
-    fn check_substs_compatible_inner<'tcx>(
-        tcx: TyCtxt<'tcx>,
-        generics: &'tcx ty::Generics,
-        args: &'tcx [ty::GenericArg<'tcx>],
-    ) -> bool {
-        if generics.count() != args.len() {
-            return false;
-        }
-
-        let (parent_args, own_args) = args.split_at(generics.parent_count);
-
-        if let Some(parent) = generics.parent
-            && let parent_generics = tcx.generics_of(parent)
-            && !check_substs_compatible_inner(tcx, parent_generics, parent_args) {
-            return false;
-        }
-
-        for (param, arg) in std::iter::zip(&generics.params, own_args) {
-            match (&param.kind, arg.unpack()) {
-                (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_))
-                | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_))
-                | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {}
-                _ => return false,
-            }
-        }
-
-        true
-    }
-
-    let generics = tcx.generics_of(assoc_item.def_id);
-    // Chop off any additional substs (RPITIT) substs
-    let substs = &substs[0..generics.count().min(substs.len())];
-    check_substs_compatible_inner(tcx, generics, substs)
-}
-
 fn confirm_impl_trait_in_trait_candidate<'tcx>(
     selcx: &mut SelectionContext<'_, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index 906c357e8ca..05a7f3e3b02 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -302,3 +302,44 @@ pub enum TupleArgumentsFlag {
     Yes,
     No,
 }
+
+// Verify that the trait item and its implementation have compatible substs lists
+pub fn check_substs_compatible<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    assoc_item: ty::AssocItem,
+    substs: ty::SubstsRef<'tcx>,
+) -> bool {
+    fn check_substs_compatible_inner<'tcx>(
+        tcx: TyCtxt<'tcx>,
+        generics: &'tcx ty::Generics,
+        args: &'tcx [ty::GenericArg<'tcx>],
+    ) -> bool {
+        if generics.count() != args.len() {
+            return false;
+        }
+
+        let (parent_args, own_args) = args.split_at(generics.parent_count);
+
+        if let Some(parent) = generics.parent
+            && let parent_generics = tcx.generics_of(parent)
+            && !check_substs_compatible_inner(tcx, parent_generics, parent_args) {
+            return false;
+        }
+
+        for (param, arg) in std::iter::zip(&generics.params, own_args) {
+            match (&param.kind, arg.unpack()) {
+                (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_))
+                | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_))
+                | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {}
+                _ => return false,
+            }
+        }
+
+        true
+    }
+
+    let generics = tcx.generics_of(assoc_item.def_id);
+    // Chop off any additional substs (RPITIT) substs
+    let substs = &substs[0..generics.count().min(substs.len())];
+    check_substs_compatible_inner(tcx, generics, substs)
+}
diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs
index 4e91dd380e8..34c7b9f4451 100644
--- a/compiler/rustc_ty_utils/src/opaque_types.rs
+++ b/compiler/rustc_ty_utils/src/opaque_types.rs
@@ -6,7 +6,7 @@ use rustc_middle::ty::util::{CheckRegions, NotUniqueParam};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
 use rustc_span::Span;
-use rustc_type_ir::AliasKind;
+use rustc_trait_selection::traits::check_substs_compatible;
 use std::ops::ControlFlow;
 
 use crate::errors::{DuplicateArg, NotParam};
@@ -36,6 +36,15 @@ impl<'tcx> OpaqueTypeCollector<'tcx> {
         self.tcx.def_span(self.item)
     }
 
+    fn parent_trait_ref(&self) -> Option<ty::TraitRef<'tcx>> {
+        let parent = self.parent()?;
+        if matches!(self.tcx.def_kind(parent), DefKind::Impl { .. }) {
+            Some(self.tcx.impl_trait_ref(parent)?.subst_identity())
+        } else {
+            None
+        }
+    }
+
     fn parent(&self) -> Option<LocalDefId> {
         match self.tcx.def_kind(self.item) {
             DefKind::Fn => None,
@@ -56,7 +65,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
     #[instrument(skip(self), ret, level = "trace")]
     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<ErrorGuaranteed> {
         match t.kind() {
-            ty::Alias(AliasKind::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
+            ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
                 if !self.seen.insert(alias_ty.def_id.expect_local()) {
                     return ControlFlow::Continue(());
                 }
@@ -98,37 +107,48 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
                     }
                 }
             }
-            ty::Alias(AliasKind::Projection, alias_ty) => {
-                if let Some(parent) = self.parent() {
-                    trace!(?alias_ty);
-                    let (trait_ref, own_substs) = alias_ty.trait_ref_and_own_substs(self.tcx);
-
-                    trace!(?trait_ref, ?own_substs);
-                    // This avoids having to do normalization of `Self::AssocTy` by only
-                    // supporting the case of a method defining opaque types from assoc types
-                    // in the same impl block.
-                    if trait_ref.self_ty() == self.tcx.type_of(parent).subst_identity() {
-                        for assoc in self.tcx.associated_items(parent).in_definition_order() {
+            ty::Alias(ty::Projection, alias_ty) => {
+                // This avoids having to do normalization of `Self::AssocTy` by only
+                // supporting the case of a method defining opaque types from assoc types
+                // in the same impl block.
+                if let Some(parent_trait_ref) = self.parent_trait_ref() {
+                    // If the trait ref of the associated item and the impl differs,
+                    // then we can't use the impl's identity substitutions below, so
+                    // just skip.
+                    if alias_ty.trait_ref(self.tcx) == parent_trait_ref {
+                        let parent = self.parent().expect("we should have a parent here");
+
+                        for &assoc in self.tcx.associated_items(parent).in_definition_order() {
                             trace!(?assoc);
-                            if assoc.trait_item_def_id == Some(alias_ty.def_id) {
-                                // We reconstruct the generic args of the associated type within the impl
-                                // from the impl's generics and the generic args passed to the type via the
-                                // projection.
-                                let substs = ty::InternalSubsts::identity_for_item(
-                                    self.tcx,
-                                    parent.to_def_id(),
+                            if assoc.trait_item_def_id != Some(alias_ty.def_id) {
+                                continue;
+                            }
+
+                            // If the type is further specializable, then the type_of
+                            // is not actually correct below.
+                            if !assoc.defaultness(self.tcx).is_final() {
+                                continue;
+                            }
+
+                            let impl_substs = alias_ty.substs.rebase_onto(
+                                self.tcx,
+                                parent_trait_ref.def_id,
+                                ty::InternalSubsts::identity_for_item(self.tcx, parent),
+                            );
+
+                            if !check_substs_compatible(self.tcx, assoc, impl_substs) {
+                                self.tcx.sess.delay_span_bug(
+                                    self.tcx.def_span(assoc.def_id),
+                                    "item had incorrect substs",
                                 );
-                                trace!(?substs);
-                                let substs: Vec<_> =
-                                    substs.iter().chain(own_substs.iter().copied()).collect();
-                                trace!(?substs);
-                                // Find opaque types in this associated type.
-                                return self
-                                    .tcx
-                                    .type_of(assoc.def_id)
-                                    .subst(self.tcx, &substs)
-                                    .visit_with(self);
+                                return ControlFlow::Continue(());
                             }
+
+                            return self
+                                .tcx
+                                .type_of(assoc.def_id)
+                                .subst(self.tcx, impl_substs)
+                                .visit_with(self);
                         }
                     }
                 }
diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs
index 86ea154a886..7437722fd06 100644
--- a/library/core/src/ffi/mod.rs
+++ b/library/core/src/ffi/mod.rs
@@ -238,7 +238,7 @@ impl fmt::Debug for c_void {
         not(target_arch = "s390x"),
         not(target_arch = "x86_64")
     ),
-    all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")),
+    all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios", target_os = "tvos")),
     target_family = "wasm",
     target_arch = "asmjs",
     target_os = "uefi",
@@ -267,7 +267,7 @@ pub struct VaListImpl<'f> {
         not(target_arch = "s390x"),
         not(target_arch = "x86_64")
     ),
-    all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")),
+    all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios", target_os = "tvos")),
     target_family = "wasm",
     target_arch = "asmjs",
     target_os = "uefi",
@@ -292,7 +292,7 @@ impl<'f> fmt::Debug for VaListImpl<'f> {
 /// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
 #[cfg(all(
     target_arch = "aarch64",
-    not(any(target_os = "macos", target_os = "ios")),
+    not(any(target_os = "macos", target_os = "ios", target_os = "tvos")),
     not(target_os = "uefi"),
     not(windows),
 ))]
@@ -389,7 +389,10 @@ pub struct VaList<'a, 'f: 'a> {
             not(target_arch = "s390x"),
             not(target_arch = "x86_64")
         ),
-        all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")),
+        all(
+            target_arch = "aarch64",
+            any(target_os = "macos", target_os = "ios", target_os = "tvos")
+        ),
         target_family = "wasm",
         target_arch = "asmjs",
         target_os = "uefi",
@@ -404,7 +407,10 @@ pub struct VaList<'a, 'f: 'a> {
             target_arch = "s390x",
             target_arch = "x86_64"
         ),
-        any(not(target_arch = "aarch64"), not(any(target_os = "macos", target_os = "ios"))),
+        any(
+            not(target_arch = "aarch64"),
+            not(any(target_os = "macos", target_os = "ios", target_os = "tvos"))
+        ),
         not(target_family = "wasm"),
         not(target_arch = "asmjs"),
         not(target_os = "uefi"),
@@ -422,7 +428,7 @@ pub struct VaList<'a, 'f: 'a> {
         not(target_arch = "s390x"),
         not(target_arch = "x86_64")
     ),
-    all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")),
+    all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios", target_os = "tvos")),
     target_family = "wasm",
     target_arch = "asmjs",
     target_os = "uefi",
@@ -449,7 +455,10 @@ impl<'f> VaListImpl<'f> {
         target_arch = "s390x",
         target_arch = "x86_64"
     ),
-    any(not(target_arch = "aarch64"), not(any(target_os = "macos", target_os = "ios"))),
+    any(
+        not(target_arch = "aarch64"),
+        not(any(target_os = "macos", target_os = "ios", target_os = "tvos"))
+    ),
     not(target_family = "wasm"),
     not(target_arch = "asmjs"),
     not(target_os = "uefi"),
diff --git a/library/std/build.rs b/library/std/build.rs
index d0b37940936..ddf6e84d8d0 100644
--- a/library/std/build.rs
+++ b/library/std/build.rs
@@ -18,6 +18,7 @@ fn main() {
         || target.contains("illumos")
         || target.contains("apple-darwin")
         || target.contains("apple-ios")
+        || target.contains("apple-tvos")
         || target.contains("apple-watchos")
         || target.contains("uwp")
         || target.contains("windows")
@@ -48,7 +49,6 @@ fn main() {
         // - mipsel-sony-psp
         // - nvptx64-nvidia-cuda
         // - arch=avr
-        // - tvos (aarch64-apple-tvos, x86_64-apple-tvos)
         // - uefi (x86_64-unknown-uefi, i686-unknown-uefi)
         // - JSON targets
         // - Any new targets that have not been explicitly added above.
diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs
index e2480bcbbc7..9ff01b9c35d 100644
--- a/library/std/src/fs/tests.rs
+++ b/library/std/src/fs/tests.rs
@@ -1640,6 +1640,10 @@ fn test_file_times() {
     use crate::os::ios::fs::FileTimesExt;
     #[cfg(target_os = "macos")]
     use crate::os::macos::fs::FileTimesExt;
+    #[cfg(target_os = "tvos")]
+    use crate::os::tvos::fs::FileTimesExt;
+    #[cfg(target_os = "tvos")]
+    use crate::os::tvos::fs::FileTimesExt;
     #[cfg(target_os = "watchos")]
     use crate::os::watchos::fs::FileTimesExt;
     #[cfg(windows)]
@@ -1651,9 +1655,21 @@ fn test_file_times() {
     let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345);
     let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321);
     times = times.set_accessed(accessed).set_modified(modified);
-    #[cfg(any(windows, target_os = "macos", target_os = "ios", target_os = "watchos"))]
+    #[cfg(any(
+        windows,
+        target_os = "macos",
+        target_os = "ios",
+        target_os = "watchos",
+        target_os = "tvos",
+    ))]
     let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123);
-    #[cfg(any(windows, target_os = "macos", target_os = "ios", target_os = "watchos"))]
+    #[cfg(any(
+        windows,
+        target_os = "macos",
+        target_os = "ios",
+        target_os = "watchos",
+        target_os = "tvos",
+    ))]
     {
         times = times.set_created(created);
     }
@@ -1678,7 +1694,13 @@ fn test_file_times() {
     let metadata = file.metadata().unwrap();
     assert_eq!(metadata.accessed().unwrap(), accessed);
     assert_eq!(metadata.modified().unwrap(), modified);
-    #[cfg(any(windows, target_os = "macos", target_os = "ios", target_os = "watchos"))]
+    #[cfg(any(
+        windows,
+        target_os = "macos",
+        target_os = "ios",
+        target_os = "watchos",
+        target_os = "tvos",
+    ))]
     {
         assert_eq!(metadata.created().unwrap(), created);
     }
diff --git a/library/std/src/os/ios/fs.rs b/library/std/src/os/ios/fs.rs
index 6d4d54b7c78..b319527a541 100644
--- a/library/std/src/os/ios/fs.rs
+++ b/library/std/src/os/ios/fs.rs
@@ -6,7 +6,7 @@ use crate::sys_common::{AsInner, AsInnerMut, IntoInner};
 use crate::time::SystemTime;
 
 #[allow(deprecated)]
-use crate::os::ios::raw;
+use super::raw;
 
 /// OS-specific extensions to [`fs::Metadata`].
 ///
diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs
index 5b54cc5f2e4..634c3cc4a15 100644
--- a/library/std/src/os/mod.rs
+++ b/library/std/src/os/mod.rs
@@ -137,6 +137,9 @@ pub mod redox;
 pub mod solaris;
 #[cfg(target_os = "solid_asp3")]
 pub mod solid;
+#[cfg(target_os = "tvos")]
+#[path = "ios/mod.rs"]
+pub(crate) mod tvos;
 #[cfg(target_os = "vita")]
 pub mod vita;
 #[cfg(target_os = "vxworks")]
diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs
index 6fe1111188a..401ec1e7a01 100644
--- a/library/std/src/os/unix/mod.rs
+++ b/library/std/src/os/unix/mod.rs
@@ -73,6 +73,8 @@ mod platform {
     pub use crate::os::redox::*;
     #[cfg(target_os = "solaris")]
     pub use crate::os::solaris::*;
+    #[cfg(target_os = "tvos")]
+    pub use crate::os::tvos::*;
     #[cfg(target_os = "vita")]
     pub use crate::os::vita::*;
     #[cfg(target_os = "vxworks")]
@@ -96,6 +98,7 @@ pub mod thread;
     target_os = "dragonfly",
     target_os = "freebsd",
     target_os = "ios",
+    target_os = "tvos",
     target_os = "watchos",
     target_os = "macos",
     target_os = "netbsd",
diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs
index bf2a51b5edb..e20170873bb 100644
--- a/library/std/src/os/unix/net/stream.rs
+++ b/library/std/src/os/unix/net/stream.rs
@@ -11,6 +11,7 @@ use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, Owned
     target_os = "dragonfly",
     target_os = "freebsd",
     target_os = "ios",
+    target_os = "tvos",
     target_os = "macos",
     target_os = "watchos",
     target_os = "netbsd",
@@ -30,6 +31,7 @@ use crate::time::Duration;
     target_os = "dragonfly",
     target_os = "freebsd",
     target_os = "ios",
+    target_os = "tvos",
     target_os = "macos",
     target_os = "watchos",
     target_os = "netbsd",
@@ -238,6 +240,7 @@ impl UnixStream {
         target_os = "dragonfly",
         target_os = "freebsd",
         target_os = "ios",
+        target_os = "tvos",
         target_os = "macos",
         target_os = "watchos",
         target_os = "netbsd",
diff --git a/library/std/src/os/unix/ucred.rs b/library/std/src/os/unix/ucred.rs
index 95967eac295..6a0cc2d2c48 100644
--- a/library/std/src/os/unix/ucred.rs
+++ b/library/std/src/os/unix/ucred.rs
@@ -36,7 +36,7 @@ pub use self::impl_linux::peer_cred;
 ))]
 pub use self::impl_bsd::peer_cred;
 
-#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
 pub use self::impl_mac::peer_cred;
 
 #[cfg(any(target_os = "linux", target_os = "android"))]
@@ -98,7 +98,7 @@ pub mod impl_bsd {
     }
 }
 
-#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
 pub mod impl_mac {
     use super::UCred;
     use crate::os::unix::io::AsRawFd;
diff --git a/library/std/src/os/unix/ucred/tests.rs b/library/std/src/os/unix/ucred/tests.rs
index e63a2fc248e..dd99ecdd819 100644
--- a/library/std/src/os/unix/ucred/tests.rs
+++ b/library/std/src/os/unix/ucred/tests.rs
@@ -8,6 +8,7 @@ use libc::{getegid, geteuid, getpid};
     target_os = "dragonfly",
     target_os = "freebsd",
     target_os = "ios",
+    target_os = "tvos",
     target_os = "macos",
     target_os = "watchos",
     target_os = "openbsd"
@@ -26,7 +27,13 @@ fn test_socket_pair() {
 }
 
 #[test]
-#[cfg(any(target_os = "linux", target_os = "ios", target_os = "macos", target_os = "watchos"))]
+#[cfg(any(
+    target_os = "linux",
+    target_os = "ios",
+    target_os = "macos",
+    target_os = "watchos",
+    target_os = "tvos",
+))]
 fn test_socket_pair_pids(arg: Type) -> RetType {
     // Create two connected sockets and get their peer credentials.
     let (sock_a, sock_b) = UnixStream::pair().unwrap();
diff --git a/library/std/src/personality/gcc.rs b/library/std/src/personality/gcc.rs
index 82edb11cbd1..6552d96ca69 100644
--- a/library/std/src/personality/gcc.rs
+++ b/library/std/src/personality/gcc.rs
@@ -85,7 +85,7 @@ const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1
 // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c
 
 cfg_if::cfg_if! {
-    if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "watchos"), not(target_os = "netbsd")))] {
+    if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "netbsd")))] {
         // ARM EHABI personality routine.
         // https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf
         //
diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs
index 0efe2570d67..f8fa81e6ef1 100644
--- a/library/std/src/sys/unix/args.rs
+++ b/library/std/src/sys/unix/args.rs
@@ -168,7 +168,7 @@ mod imp {
     }
 }
 
-#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
 mod imp {
     use super::Args;
     use crate::ffi::CStr;
@@ -209,7 +209,7 @@ mod imp {
     // for i in (0..[args count])
     //      res.push([args objectAtIndex:i])
     // res
-    #[cfg(any(target_os = "ios", target_os = "watchos"))]
+    #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))]
     pub fn args() -> Args {
         use crate::ffi::OsString;
         use crate::mem;
diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs
index 8c3ef88d8f8..929e9dae738 100644
--- a/library/std/src/sys/unix/env.rs
+++ b/library/std/src/sys/unix/env.rs
@@ -31,6 +31,17 @@ pub mod os {
     pub const EXE_EXTENSION: &str = "";
 }
 
+#[cfg(target_os = "tvos")]
+pub mod os {
+    pub const FAMILY: &str = "unix";
+    pub const OS: &str = "tvos";
+    pub const DLL_PREFIX: &str = "lib";
+    pub const DLL_SUFFIX: &str = ".dylib";
+    pub const DLL_EXTENSION: &str = "dylib";
+    pub const EXE_SUFFIX: &str = "";
+    pub const EXE_EXTENSION: &str = "";
+}
+
 #[cfg(target_os = "watchos")]
 pub mod os {
     pub const FAMILY: &str = "unix";
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
index 69c93c92003..85e020ae413 100644
--- a/library/std/src/sys/unix/fd.rs
+++ b/library/std/src/sys/unix/fd.rs
@@ -44,6 +44,7 @@ const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
     target_os = "dragonfly",
     target_os = "freebsd",
     target_os = "ios",
+    target_os = "tvos",
     target_os = "macos",
     target_os = "netbsd",
     target_os = "openbsd",
@@ -69,6 +70,7 @@ const fn max_iov() -> usize {
     target_os = "emscripten",
     target_os = "freebsd",
     target_os = "ios",
+    target_os = "tvos",
     target_os = "linux",
     target_os = "macos",
     target_os = "netbsd",
@@ -181,6 +183,7 @@ impl FileDesc {
         target_os = "fuchsia",
         target_os = "illumos",
         target_os = "ios",
+        target_os = "tvos",
         target_os = "linux",
         target_os = "macos",
         target_os = "netbsd",
@@ -222,6 +225,7 @@ impl FileDesc {
     #[cfg(any(
         all(target_os = "android", target_pointer_width = "32"),
         target_os = "ios",
+        target_os = "tvos",
         target_os = "macos",
     ))]
     pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
@@ -320,6 +324,7 @@ impl FileDesc {
         target_os = "fuchsia",
         target_os = "illumos",
         target_os = "ios",
+        target_os = "tvos",
         target_os = "linux",
         target_os = "macos",
         target_os = "netbsd",
@@ -361,6 +366,7 @@ impl FileDesc {
     #[cfg(any(
         all(target_os = "android", target_pointer_width = "32"),
         target_os = "ios",
+        target_os = "tvos",
         target_os = "macos",
     ))]
     pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index d2fb6238387..9cf5cfcc8d5 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -32,6 +32,7 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
     all(target_os = "linux", target_env = "gnu"),
     target_os = "macos",
     target_os = "ios",
+    target_os = "tvos",
     target_os = "watchos",
 ))]
 use crate::sys::weak::syscall;
@@ -43,6 +44,7 @@ use libc::{c_int, mode_t};
 #[cfg(any(
     target_os = "macos",
     target_os = "ios",
+    target_os = "tvos",
     target_os = "watchos",
     target_os = "solaris",
     all(target_os = "linux", target_env = "gnu")
@@ -360,7 +362,7 @@ pub struct FilePermissions {
 pub struct FileTimes {
     accessed: Option<SystemTime>,
     modified: Option<SystemTime>,
-    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))]
     created: Option<SystemTime>,
 }
 
@@ -519,6 +521,7 @@ impl FileAttr {
         target_os = "openbsd",
         target_os = "macos",
         target_os = "ios",
+        target_os = "tvos",
         target_os = "watchos",
     ))]
     pub fn created(&self) -> io::Result<SystemTime> {
@@ -530,6 +533,7 @@ impl FileAttr {
         target_os = "openbsd",
         target_os = "macos",
         target_os = "ios",
+        target_os = "tvos",
         target_os = "watchos",
         target_os = "vita",
     )))]
@@ -611,7 +615,7 @@ impl FileTimes {
         self.modified = Some(t);
     }
 
-    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))]
     pub fn set_created(&mut self, t: SystemTime) {
         self.created = Some(t);
     }
@@ -895,6 +899,7 @@ impl DirEntry {
     #[cfg(any(
         target_os = "macos",
         target_os = "ios",
+        target_os = "tvos",
         target_os = "watchos",
         target_os = "linux",
         target_os = "emscripten",
@@ -928,6 +933,7 @@ impl DirEntry {
     #[cfg(any(
         target_os = "macos",
         target_os = "ios",
+        target_os = "tvos",
         target_os = "watchos",
         target_os = "netbsd",
         target_os = "openbsd",
@@ -946,6 +952,7 @@ impl DirEntry {
     #[cfg(not(any(
         target_os = "macos",
         target_os = "ios",
+        target_os = "tvos",
         target_os = "watchos",
         target_os = "netbsd",
         target_os = "openbsd",
@@ -1107,11 +1114,21 @@ impl File {
         cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?;
         return Ok(());
 
-        #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "ios",
+            target_os = "tvos",
+            target_os = "watchos",
+        ))]
         unsafe fn os_fsync(fd: c_int) -> c_int {
             libc::fcntl(fd, libc::F_FULLFSYNC)
         }
-        #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "watchos")))]
+        #[cfg(not(any(
+            target_os = "macos",
+            target_os = "ios",
+            target_os = "tvos",
+            target_os = "watchos",
+        )))]
         unsafe fn os_fsync(fd: c_int) -> c_int {
             libc::fsync(fd)
         }
@@ -1121,7 +1138,12 @@ impl File {
         cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?;
         return Ok(());
 
-        #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+        #[cfg(any(
+            target_os = "macos",
+            target_os = "ios",
+            target_os = "tvos",
+            target_os = "watchos",
+        ))]
         unsafe fn os_datasync(fd: c_int) -> c_int {
             libc::fcntl(fd, libc::F_FULLFSYNC)
         }
@@ -1140,6 +1162,7 @@ impl File {
             target_os = "android",
             target_os = "freebsd",
             target_os = "ios",
+            target_os = "tvos",
             target_os = "linux",
             target_os = "macos",
             target_os = "netbsd",
@@ -1249,7 +1272,7 @@ impl File {
                     io::ErrorKind::Unsupported,
                     "setting file times not supported",
                 ))
-            } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] {
+            } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] {
                 let mut buf = [mem::MaybeUninit::<libc::timespec>::uninit(); 3];
                 let mut num_times = 0;
                 let mut attrlist: libc::attrlist = unsafe { mem::zeroed() };
@@ -1709,6 +1732,7 @@ fn open_to_and_set_permissions(
     target_os = "android",
     target_os = "macos",
     target_os = "ios",
+    target_os = "tvos",
     target_os = "watchos",
 )))]
 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
@@ -1736,7 +1760,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
     }
 }
 
-#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
     use crate::sync::atomic::{AtomicBool, Ordering};
 
diff --git a/library/std/src/sys/unix/locks/pthread_condvar.rs b/library/std/src/sys/unix/locks/pthread_condvar.rs
index 192fa216dfa..2dc1b0c601e 100644
--- a/library/std/src/sys/unix/locks/pthread_condvar.rs
+++ b/library/std/src/sys/unix/locks/pthread_condvar.rs
@@ -32,6 +32,7 @@ impl LazyInit for AllocatedCondvar {
             if #[cfg(any(
                 target_os = "macos",
                 target_os = "ios",
+                target_os = "tvos",
                 target_os = "watchos",
                 target_os = "l4re",
                 target_os = "android",
@@ -124,6 +125,7 @@ impl Condvar {
     #[cfg(not(any(
         target_os = "macos",
         target_os = "ios",
+        target_os = "tvos",
         target_os = "watchos",
         target_os = "android",
         target_os = "espidf",
@@ -158,6 +160,7 @@ impl Condvar {
     #[cfg(any(
         target_os = "macos",
         target_os = "ios",
+        target_os = "tvos",
         target_os = "watchos",
         target_os = "android",
         target_os = "espidf",
diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs
index 24566d96bcd..1b72e21a832 100644
--- a/library/std/src/sys/unix/mod.rs
+++ b/library/std/src/sys/unix/mod.rs
@@ -87,6 +87,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
             // The poll on Darwin doesn't set POLLNVAL for closed fds.
             target_os = "macos",
             target_os = "ios",
+            target_os = "tvos",
             target_os = "watchos",
             target_os = "redox",
             target_os = "l4re",
@@ -387,7 +388,7 @@ cfg_if::cfg_if! {
     } else if #[cfg(target_os = "macos")] {
         #[link(name = "System")]
         extern "C" {}
-    } else if #[cfg(any(target_os = "ios", target_os = "watchos"))] {
+    } else if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] {
         #[link(name = "System")]
         #[link(name = "objc")]
         #[link(name = "Security", kind = "framework")]
diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs
index 8edfd331304..a68c14758ff 100644
--- a/library/std/src/sys/unix/os.rs
+++ b/library/std/src/sys/unix/os.rs
@@ -63,7 +63,13 @@ extern "C" {
     #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")]
     #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")]
     #[cfg_attr(
-        any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "watchos"),
+        any(
+            target_os = "macos",
+            target_os = "ios",
+            target_os = "tvos",
+            target_os = "freebsd",
+            target_os = "watchos"
+        ),
         link_name = "__error"
     )]
     #[cfg_attr(target_os = "haiku", link_name = "_errnop")]
@@ -375,7 +381,7 @@ pub fn current_exe() -> io::Result<PathBuf> {
     Ok(PathBuf::from(OsString::from_vec(e)))
 }
 
-#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
 pub fn current_exe() -> io::Result<PathBuf> {
     unsafe {
         let mut sz: u32 = 0;
@@ -609,6 +615,7 @@ pub fn home_dir() -> Option<PathBuf> {
     #[cfg(any(
         target_os = "android",
         target_os = "ios",
+        target_os = "tvos",
         target_os = "watchos",
         target_os = "emscripten",
         target_os = "redox",
@@ -623,6 +630,7 @@ pub fn home_dir() -> Option<PathBuf> {
     #[cfg(not(any(
         target_os = "android",
         target_os = "ios",
+        target_os = "tvos",
         target_os = "watchos",
         target_os = "emscripten",
         target_os = "redox",
diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
index 22d9d6141f4..129e7643661 100644
--- a/library/std/src/sys/unix/process/process_unix.rs
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -15,6 +15,8 @@ use crate::sys::weak::raw_syscall;
 
 #[cfg(any(
     target_os = "macos",
+    target_os = "watchos",
+    target_os = "tvos",
     target_os = "freebsd",
     all(target_os = "linux", target_env = "gnu"),
     all(target_os = "linux", target_env = "musl"),
@@ -28,7 +30,12 @@ use libc::RTP_ID as pid_t;
 #[cfg(not(target_os = "vxworks"))]
 use libc::{c_int, pid_t};
 
-#[cfg(not(any(target_os = "vxworks", target_os = "l4re")))]
+#[cfg(not(any(
+    target_os = "vxworks",
+    target_os = "l4re",
+    target_os = "tvos",
+    target_os = "watchos",
+)))]
 use libc::{gid_t, uid_t};
 
 cfg_if::cfg_if! {
@@ -84,7 +91,6 @@ impl Command {
         if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
             return Ok((ret, ours));
         }
-
         let (input, output) = sys::pipe::anon_pipe()?;
 
         // Whatever happens after the fork is almost for sure going to touch or
@@ -166,9 +172,31 @@ impl Command {
         crate::sys_common::process::wait_with_output(proc, pipes)
     }
 
+    // WatchOS and TVOS headers mark the `fork`/`exec*` functions with
+    // `__WATCHOS_PROHIBITED __TVOS_PROHIBITED`, and indicate that the
+    // `posix_spawn*` functions should be used instead. It isn't entirely clear
+    // what `PROHIBITED` means here (e.g. if calls to these functions are
+    // allowed to exist in dead code), but it sounds bad, so we go out of our
+    // way to avoid that all-together.
+    #[cfg(any(target_os = "tvos", target_os = "watchos"))]
+    const ERR_APPLE_TV_WATCH_NO_FORK_EXEC: Error = io::const_io_error!(
+        ErrorKind::Unsupported,
+        "`fork`+`exec`-based process spawning is not supported on this target",
+    );
+
+    #[cfg(any(target_os = "tvos", target_os = "watchos"))]
+    unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
+        return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC);
+    }
+
     // Attempts to fork the process. If successful, returns Ok((0, -1))
     // in the child, and Ok((child_pid, -1)) in the parent.
-    #[cfg(not(any(target_os = "linux", all(target_os = "nto", target_env = "nto71"))))]
+    #[cfg(not(any(
+        target_os = "linux",
+        target_os = "watchos",
+        target_os = "tvos",
+        all(target_os = "nto", target_env = "nto71"),
+    )))]
     unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
         cvt(libc::fork()).map(|res| (res, -1))
     }
@@ -339,6 +367,7 @@ impl Command {
     // allocation). Instead we just close it manually. This will never
     // have the drop glue anyway because this code never returns (the
     // child will either exec() or invoke libc::exit)
+    #[cfg(not(any(target_os = "tvos", target_os = "watchos")))]
     unsafe fn do_exec(
         &mut self,
         stdio: ChildPipes,
@@ -445,8 +474,19 @@ impl Command {
         Err(io::Error::last_os_error())
     }
 
+    #[cfg(any(target_os = "tvos", target_os = "watchos"))]
+    unsafe fn do_exec(
+        &mut self,
+        _stdio: ChildPipes,
+        _maybe_envp: Option<&CStringArray>,
+    ) -> Result<!, io::Error> {
+        return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC);
+    }
+
     #[cfg(not(any(
         target_os = "macos",
+        target_os = "tvos",
+        target_os = "watchos",
         target_os = "freebsd",
         all(target_os = "linux", target_env = "gnu"),
         all(target_os = "linux", target_env = "musl"),
@@ -464,6 +504,9 @@ impl Command {
     // directly.
     #[cfg(any(
         target_os = "macos",
+        // FIXME: `target_os = "ios"`?
+        target_os = "tvos",
+        target_os = "watchos",
         target_os = "freebsd",
         all(target_os = "linux", target_env = "gnu"),
         all(target_os = "linux", target_env = "musl"),
@@ -550,7 +593,7 @@ impl Command {
         }
         let addchdir = match self.get_cwd() {
             Some(cwd) => {
-                if cfg!(target_os = "macos") {
+                if cfg!(any(target_os = "macos", target_os = "tvos", target_os = "watchos")) {
                     // There is a bug in macOS where a relative executable
                     // path like "../myprogram" will cause `posix_spawn` to
                     // successfully launch the program, but erroneously return
diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs
index d8b63546b9e..d471be33ed5 100644
--- a/library/std/src/sys/unix/rand.rs
+++ b/library/std/src/sys/unix/rand.rs
@@ -14,6 +14,7 @@ pub fn hashmap_random_keys() -> (u64, u64) {
     unix,
     not(target_os = "macos"),
     not(target_os = "ios"),
+    not(target_os = "tvos"),
     not(target_os = "watchos"),
     not(target_os = "openbsd"),
     not(target_os = "freebsd"),
@@ -198,7 +199,7 @@ mod imp {
 // once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is
 // only used on iOS where direct access to `/dev/urandom` is blocked by the
 // sandbox.
-#[cfg(any(target_os = "ios", target_os = "watchos"))]
+#[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))]
 mod imp {
     use crate::io;
     use crate::ptr;
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
index 010015667f7..893f8b8df3f 100644
--- a/library/std/src/sys/unix/thread.rs
+++ b/library/std/src/sys/unix/thread.rs
@@ -150,7 +150,7 @@ impl Thread {
         }
     }
 
-    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
     pub fn set_name(name: &CStr) {
         unsafe {
             let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name);
@@ -283,7 +283,13 @@ impl Drop for Thread {
     }
 }
 
-#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "watchos"))]
+#[cfg(any(
+    target_os = "linux",
+    target_os = "macos",
+    target_os = "ios",
+    target_os = "tvos",
+    target_os = "watchos",
+))]
 fn truncate_cstr<const MAX_WITH_NUL: usize>(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] {
     let mut result = [0; MAX_WITH_NUL];
     for (src, dst) in cstr.to_bytes().iter().zip(&mut result[..MAX_WITH_NUL - 1]) {
@@ -299,6 +305,7 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> {
             target_os = "emscripten",
             target_os = "fuchsia",
             target_os = "ios",
+            target_os = "tvos",
             target_os = "linux",
             target_os = "macos",
             target_os = "solaris",
diff --git a/library/std/src/sys/unix/thread_parking/pthread.rs b/library/std/src/sys/unix/thread_parking/pthread.rs
index 8bf4bae7a3f..ae805d84399 100644
--- a/library/std/src/sys/unix/thread_parking/pthread.rs
+++ b/library/std/src/sys/unix/thread_parking/pthread.rs
@@ -46,6 +46,7 @@ unsafe fn wait_timeout(
     #[cfg(any(
         target_os = "macos",
         target_os = "ios",
+        target_os = "tvos",
         target_os = "watchos",
         target_os = "espidf",
         target_os = "horizon",
@@ -73,6 +74,7 @@ unsafe fn wait_timeout(
     #[cfg(not(any(
         target_os = "macos",
         target_os = "ios",
+        target_os = "tvos",
         target_os = "watchos",
         target_os = "espidf",
         target_os = "horizon",
@@ -120,6 +122,7 @@ impl Parker {
             if #[cfg(any(
                 target_os = "macos",
                 target_os = "ios",
+                target_os = "tvos",
                 target_os = "watchos",
                 target_os = "l4re",
                 target_os = "android",
diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs
index a9fbc7ab108..17b4130c202 100644
--- a/library/std/src/sys/unix/time.rs
+++ b/library/std/src/sys/unix/time.rs
@@ -219,7 +219,8 @@ impl From<__timespec64> for Timespec {
 #[cfg(any(
     all(target_os = "macos", any(not(target_arch = "aarch64"))),
     target_os = "ios",
-    target_os = "watchos"
+    target_os = "watchos",
+    target_os = "tvos"
 ))]
 mod inner {
     use crate::sync::atomic::{AtomicU64, Ordering};
@@ -339,7 +340,8 @@ mod inner {
 #[cfg(not(any(
     all(target_os = "macos", any(not(target_arch = "aarch64"))),
     target_os = "ios",
-    target_os = "watchos"
+    target_os = "watchos",
+    target_os = "tvos"
 )))]
 mod inner {
     use crate::fmt;
diff --git a/library/std/src/sys/unix/weak.rs b/library/std/src/sys/unix/weak.rs
index 62ffee70bec..61088ff16ed 100644
--- a/library/std/src/sys/unix/weak.rs
+++ b/library/std/src/sys/unix/weak.rs
@@ -28,7 +28,7 @@ use crate::ptr;
 use crate::sync::atomic::{self, AtomicPtr, Ordering};
 
 // We can use true weak linkage on ELF targets.
-#[cfg(not(any(target_os = "macos", target_os = "ios")))]
+#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos")))]
 pub(crate) macro weak {
     (fn $name:ident($($t:ty),*) -> $ret:ty) => (
         let ref $name: ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
@@ -43,7 +43,7 @@ pub(crate) macro weak {
 }
 
 // On non-ELF targets, use the dlsym approximation of weak linkage.
-#[cfg(any(target_os = "macos", target_os = "ios"))]
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))]
 pub(crate) use self::dlsym as weak;
 
 pub(crate) struct ExternWeak<F: Copy> {
diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs
index 652c695fc57..2976a9f578e 100644
--- a/library/std/src/sys_common/net.rs
+++ b/library/std/src/sys_common/net.rs
@@ -18,7 +18,7 @@ use crate::ffi::{c_int, c_void};
 cfg_if::cfg_if! {
     if #[cfg(any(
         target_os = "dragonfly", target_os = "freebsd",
-        target_os = "ios", target_os = "macos", target_os = "watchos",
+        target_os = "ios", target_os = "tvos", target_os = "macos", target_os = "watchos",
         target_os = "openbsd", target_os = "netbsd", target_os = "illumos",
         target_os = "solaris", target_os = "haiku", target_os = "l4re", target_os = "nto"))] {
         use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP;
diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs
index b65e2572cc5..5d6b9e94ee9 100644
--- a/library/std/src/thread/tests.rs
+++ b/library/std/src/thread/tests.rs
@@ -42,6 +42,7 @@ fn test_named_thread() {
     all(target_os = "linux", target_env = "gnu"),
     target_os = "macos",
     target_os = "ios",
+    target_os = "tvos",
     target_os = "watchos"
 ))]
 #[test]
diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs
index f6a68073b2f..ec24e137572 100644
--- a/library/unwind/src/libunwind.rs
+++ b/library/unwind/src/libunwind.rs
@@ -117,7 +117,7 @@ extern "C" {
 }
 
 cfg_if::cfg_if! {
-if #[cfg(any(target_os = "ios", target_os = "watchos", target_os = "netbsd", not(target_arch = "arm")))] {
+if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "netbsd", not(target_arch = "arm")))] {
     // Not ARM EHABI
     #[repr(C)]
     #[derive(Copy, Clone, PartialEq)]
diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md
index 2b0431a159b..1e17a90a842 100644
--- a/src/doc/rustc/src/SUMMARY.md
+++ b/src/doc/rustc/src/SUMMARY.md
@@ -16,6 +16,7 @@
     - [Target Tier Policy](target-tier-policy.md)
     - [Template for Target-specific Documentation](platform-support/TEMPLATE.md)
     - [aarch64-apple-ios-sim](platform-support/aarch64-apple-ios-sim.md)
+    - [\*-apple-tvos](platform-support/apple-tvos.md)
     - [\*-apple-watchos\*](platform-support/apple-watchos.md)
     - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md)
     - [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md)
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index f69dcd5983d..62f628f8229 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -214,7 +214,7 @@ host tools.
 target | std | host | notes
 -------|:---:|:----:|-------
 `aarch64-apple-ios-macabi` | ? |  | Apple Catalyst on ARM64
-`aarch64-apple-tvos` | * |  | ARM64 tvOS
+[`aarch64-apple-tvos`](platform-support/apple-tvos.md) | ? |  | ARM64 tvOS
 [`aarch64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ |  | ARM64 Apple WatchOS Simulator
 [`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ |  | ARM64 SOLID with TOPPERS/ASP3
 [`aarch64-nintendo-switch-freestanding`](platform-support/aarch64-nintendo-switch-freestanding.md) | * |  | ARM64 Nintendo Switch, Horizon
@@ -316,7 +316,7 @@ target | std | host | notes
 `thumbv7neon-unknown-linux-musleabihf` | ? |  | Thumb2-mode ARMv7a Linux with NEON, MUSL
 [`wasm64-unknown-unknown`](platform-support/wasm64-unknown-unknown.md) | ? |  | WebAssembly
 `x86_64-apple-ios-macabi` | ✓ |  | Apple Catalyst on x86_64
-`x86_64-apple-tvos` | * | | x86 64-bit tvOS
+[`x86_64-apple-tvos`](platform-support/apple-tvos.md) | ? | | x86 64-bit tvOS
 [`x86_64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | x86 64-bit Apple WatchOS simulator
 [`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ |  | x86 64-bit QNX Neutrino 7.1 RTOS |
 [`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ |
diff --git a/src/doc/rustc/src/platform-support/apple-tvos.md b/src/doc/rustc/src/platform-support/apple-tvos.md
new file mode 100644
index 00000000000..d87fd1959b4
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/apple-tvos.md
@@ -0,0 +1,85 @@
+# `*-apple-tvos`
+- aarch64-apple-tvos
+- x86_64-apple-tvos
+
+**Tier: 3**
+
+Apple tvOS targets:
+- Apple tvOS on aarch64
+- Apple tvOS Simulator on x86_64
+
+## Target maintainers
+
+* [@thomcc](https://github.com/thomcc)
+
+## Requirements
+
+These targets are cross-compiled. You will need appropriate versions of Xcode
+and the SDKs for tvOS (`AppleTVOS.sdk`) and/or the tvOS Simulator
+(`AppleTVSimulator.sdk`) to build a toolchain and target these platforms.
+
+The targets support most (see below) of the standard library including the
+allocator to the best of my knowledge, however they are very new, not yet
+well-tested, and it is possible that there are various bugs.
+
+In theory we support back to tvOS version 7.0, although the actual minimum
+version you can target may be newer than this, for example due to the versions
+of Xcode and your SDKs.
+
+As with the other Apple targets, `rustc` respects the common environment
+variables used by Xcode to configure this, in this case
+`TVOS_DEPLOYMENT_TARGET`.
+
+#### Incompletely supported library functionality
+
+As mentioned, "most" of the standard library is supported, which means that some portions
+are known to be unsupported. The following APIs are currently known to have
+missing or incomplete support:
+
+- `std::process::Command`'s API will return an error if it is configured in a
+  manner which cannot be performed using `posix_spawn` -- this is because the
+  more flexible `fork`/`exec`-based approach is prohibited on these platforms in
+  favor of `posix_spawn{,p}` (which still probably will get you rejected from
+  app stores, so is likely sideloading-only). A concrete set of cases where this
+  will occur is difficult to enumerate (and would quickly become stale), but in
+  some cases it may be worked around by tweaking the manner in which `Command`
+  is invoked.
+
+## Building the target
+
+The targets can be built by enabling them for a `rustc` build in `config.toml`, by adding, for example:
+
+```toml
+[build]
+build-stage = 1
+target = ["aarch64-apple-tvos", "x86_64-apple-tvos"]
+```
+
+It's possible that cargo under `-Zbuild-std` may also be used to target them.
+
+## Building Rust programs
+
+*Note: Building for this target requires the corresponding TVOS SDK, as provided by Xcode.*
+
+Rust programs can be built for these targets
+
+```text
+$ rustc --target aarch64-apple-tvos your-code.rs
+...
+$ rustc --target x86_64-apple-tvos your-code.rs
+```
+
+## Testing
+
+There is no support for running the Rust or standard library testsuite on tvOS
+or the simulators at the moment. Testing has mostly been done manually with
+builds of static libraries called from Xcode or a simulator.
+
+It hopefully will be possible to improve this in the future.
+
+## Cross-compilation toolchains and C code
+
+This target can be cross-compiled from x86_64 or aarch64 macOS hosts.
+
+Other hosts are not supported for cross-compilation, but might work when also
+providing the required Xcode SDK.
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 5bd9389a400..f15c9bacec0 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -421,11 +421,10 @@ fn document<'a, 'cx: 'a>(
     display_fn(move |f| {
         document_item_info(cx, item, parent).render_into(f).unwrap();
         if parent.is_none() {
-            write!(f, "{}", document_full_collapsible(item, cx, heading_offset))?;
+            write!(f, "{}", document_full_collapsible(item, cx, heading_offset))
         } else {
-            write!(f, "{}", document_full(item, cx, heading_offset))?;
+            write!(f, "{}", document_full(item, cx, heading_offset))
         }
-        Ok(())
     })
 }
 
@@ -800,10 +799,11 @@ fn assoc_type(
     if !bounds.is_empty() {
         write!(w, ": {}", print_generic_bounds(bounds, cx))
     }
-    write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline));
+    // Render the default before the where-clause which aligns with the new recommended style. See #89122.
     if let Some(default) = default {
         write!(w, " = {}", default.print(cx))
     }
+    write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline));
 }
 
 fn assoc_method(
diff --git a/src/librustdoc/html/templates/item_info.html b/src/librustdoc/html/templates/item_info.html
index d2ea9bdae9c..9e65ae95e15 100644
--- a/src/librustdoc/html/templates/item_info.html
+++ b/src/librustdoc/html/templates/item_info.html
@@ -1,5 +1,5 @@
 {% if !items.is_empty() %}
-    <span class="item-info"> {# #}
+    <span class="item-info">
         {% for item in items %}
             {{item|safe}} {# #}
         {% endfor %}
diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html
index a01457971c1..f6d2fa34890 100644
--- a/src/librustdoc/html/templates/item_union.html
+++ b/src/librustdoc/html/templates/item_union.html
@@ -4,14 +4,15 @@
 </code></pre>
 {{ self.document() | safe }}
 {% if self.fields_iter().peek().is_some() %}
-    <h2 id="fields" class="fields small-section-header">
-        Fields<a href="#fields" class="anchor">§</a>
+    <h2 id="fields" class="fields small-section-header"> {# #}
+        Fields<a href="#fields" class="anchor">§</a> {# #}
     </h2>
     {% for (field, ty) in self.fields_iter() %}
         {% let name = field.name.expect("union field name") %}
-        <span id="structfield.{{ name }}" class="{{ ItemType::StructField }} small-section-header">
-            <a href="#structfield.{{ name }}" class="anchor field">§</a>
-            <code>{{ name }}: {{ self.print_ty(ty) | safe }}</code>
+        <span id="structfield.{{ name }}" {#+ #}
+            class="{{ ItemType::StructField +}} small-section-header"> {# #}
+            <a href="#structfield.{{ name }}" class="anchor field">§</a> {# #}
+            <code>{{ name }}: {{+ self.print_ty(ty) | safe }}</code> {# #}
         </span>
         {% if let Some(stability_class) = self.stability_field(field) %}
             <span class="stab {{ stability_class }}"></span>
diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html
index edabac9a082..68a295ae095 100644
--- a/src/librustdoc/html/templates/print_item.html
+++ b/src/librustdoc/html/templates/print_item.html
@@ -1,5 +1,5 @@
 <div class="main-heading"> {# #}
-    <h1> {# #}
+    <h1>
         {{typ}}
         {# The breadcrumbs of the item path, like std::string #}
         {% for component in path_components %}
@@ -12,7 +12,7 @@
                 alt="Copy item path"> {# #}
         </button> {# #}
     </h1> {# #}
-    <span class="out-of-band"> {# #}
+    <span class="out-of-band">
         {% if !stability_since_raw.is_empty() %}
         {{ stability_since_raw|safe +}} · {#+ #}
         {% endif %}
diff --git a/tests/rustdoc-gui/fields.goml b/tests/rustdoc-gui/fields.goml
new file mode 100644
index 00000000000..b8139a2edac
--- /dev/null
+++ b/tests/rustdoc-gui/fields.goml
@@ -0,0 +1,18 @@
+// This test checks that fields are displayed as expected (one by line).
+go-to: "file://" + |DOC_PATH| + "/test_docs/fields/struct.Struct.html"
+store-position: ("#structfield\.a", {"y": a_y})
+store-position: ("#structfield\.b", {"y": b_y})
+assert: |a_y| < |b_y|
+
+go-to: "file://" + |DOC_PATH| + "/test_docs/fields/union.Union.html"
+store-position: ("#structfield\.a", {"y": a_y})
+store-position: ("#structfield\.b", {"y": b_y})
+assert: |a_y| < |b_y|
+
+go-to: "file://" + |DOC_PATH| + "/test_docs/fields/enum.Enum.html"
+store-position: ("#variant\.A\.field\.a", {"y": a_y})
+store-position: ("#variant\.A\.field\.b", {"y": b_y})
+assert: |a_y| < |b_y|
+store-position: ("#variant\.B\.field\.a", {"y": a_y})
+store-position: ("#variant\.B\.field\.b", {"y": b_y})
+assert: |a_y| < |b_y|
diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs
index 6ad1e8b4f67..c040fa02dff 100644
--- a/tests/rustdoc-gui/src/test_docs/lib.rs
+++ b/tests/rustdoc-gui/src/test_docs/lib.rs
@@ -486,3 +486,24 @@ pub mod search_results {
     }
 
 }
+
+pub mod fields {
+    pub struct Struct {
+        pub a: u8,
+        pub b: u32,
+    }
+    pub union Union {
+        pub a: u8,
+        pub b: u32,
+    }
+    pub enum Enum {
+        A {
+            a: u8,
+            b: u32,
+        },
+        B {
+            a: u8,
+            b: u32,
+        },
+    }
+}
diff --git a/tests/rustdoc/generic-associated-types/gats.rs b/tests/rustdoc/generic-associated-types/gats.rs
index 7ab82bb5829..605176e5fea 100644
--- a/tests/rustdoc/generic-associated-types/gats.rs
+++ b/tests/rustdoc/generic-associated-types/gats.rs
@@ -23,9 +23,9 @@ impl LendingIterator for () {
 pub struct Infinite<T>(T);
 
 // @has foo/trait.LendingIterator.html
-// @has - '//*[@id="associatedtype.Item-2"]//h4[@class="code-header"]' "type Item<'a> where Self: 'a = &'a T"
+// @has - '//*[@id="associatedtype.Item-2"]//h4[@class="code-header"]' "type Item<'a> = &'a T where Self: 'a"
 impl<T> LendingIterator for Infinite<T> {
-    type Item<'a> where Self: 'a = &'a T;
+    type Item<'a> = &'a T where Self: 'a;
 
     fn next<'a>(&'a self) -> Self::Item<'a> {
         &self.0
diff --git a/tests/rustdoc/union-fields-html.rs b/tests/rustdoc/union-fields-html.rs
new file mode 100644
index 00000000000..1ac01232c3e
--- /dev/null
+++ b/tests/rustdoc/union-fields-html.rs
@@ -0,0 +1,11 @@
+#![crate_name = "foo"]
+
+// @has 'foo/union.Union.html'
+// Checking that there is a whitespace after `:`.
+// @has - '//*[@id="structfield.a"]/code' 'a: u8'
+// @has - '//*[@id="structfield.b"]/code' 'b: u32'
+pub union Union {
+    pub a: u8,
+    /// tadam
+    pub b: u32,
+}
diff --git a/tests/ui/impl-trait/in-trait/suggest-missing-item.fixed b/tests/ui/impl-trait/in-trait/suggest-missing-item.fixed
index 16c4d15ddcd..d9f775a6c84 100644
--- a/tests/ui/impl-trait/in-trait/suggest-missing-item.fixed
+++ b/tests/ui/impl-trait/in-trait/suggest-missing-item.fixed
@@ -9,11 +9,14 @@ trait Trait {
     async fn bar() -> i32;
 
     fn test(&self) -> impl Sized + '_;
+
+    async fn baz(&self) -> &i32;
 }
 
 struct S;
 
-impl Trait for S {fn test(&self) -> impl Sized + '_ { todo!() }
+impl Trait for S {async fn baz(&self) -> &i32 { todo!() }
+fn test(&self) -> impl Sized + '_ { todo!() }
 async fn bar() -> i32 { todo!() }
 async fn foo() { todo!() }
 }
diff --git a/tests/ui/impl-trait/in-trait/suggest-missing-item.rs b/tests/ui/impl-trait/in-trait/suggest-missing-item.rs
index f218e6cb581..26979b5149b 100644
--- a/tests/ui/impl-trait/in-trait/suggest-missing-item.rs
+++ b/tests/ui/impl-trait/in-trait/suggest-missing-item.rs
@@ -9,6 +9,8 @@ trait Trait {
     async fn bar() -> i32;
 
     fn test(&self) -> impl Sized + '_;
+
+    async fn baz(&self) -> &i32;
 }
 
 struct S;
diff --git a/tests/ui/impl-trait/in-trait/suggest-missing-item.stderr b/tests/ui/impl-trait/in-trait/suggest-missing-item.stderr
index d96641fe163..44f98896eb3 100644
--- a/tests/ui/impl-trait/in-trait/suggest-missing-item.stderr
+++ b/tests/ui/impl-trait/in-trait/suggest-missing-item.stderr
@@ -1,5 +1,5 @@
-error[E0046]: not all trait items implemented, missing: `foo`, `bar`, `test`
-  --> $DIR/suggest-missing-item.rs:16:1
+error[E0046]: not all trait items implemented, missing: `foo`, `bar`, `test`, `baz`
+  --> $DIR/suggest-missing-item.rs:18:1
    |
 LL |     async fn foo();
    |     --------------- `foo` from trait
@@ -9,9 +9,12 @@ LL |     async fn bar() -> i32;
 LL |
 LL |     fn test(&self) -> impl Sized + '_;
    |     ---------------------------------- `test` from trait
+LL |
+LL |     async fn baz(&self) -> &i32;
+   |     ---------------------------- `baz` from trait
 ...
 LL | impl Trait for S {}
-   | ^^^^^^^^^^^^^^^^ missing `foo`, `bar`, `test` in implementation
+   | ^^^^^^^^^^^^^^^^ missing `foo`, `bar`, `test`, `baz` in implementation
 
 error: aborting due to previous error
 
diff --git a/tests/ui/type-alias-impl-trait/impl-trait-in-type-alias-with-bad-substs.rs b/tests/ui/type-alias-impl-trait/impl-trait-in-type-alias-with-bad-substs.rs
new file mode 100644
index 00000000000..a3f65146f75
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/impl-trait-in-type-alias-with-bad-substs.rs
@@ -0,0 +1,28 @@
+#![feature(impl_trait_in_assoc_type)]
+
+// We weren't checking that the trait and impl generics line up in the
+// normalization-shortcut code in `OpaqueTypeCollector`.
+
+use std::ops::Deref;
+
+trait Foo {
+    type Bar<'a>;
+
+    type Baz<'a>;
+
+    fn test<'a>() -> Self::Bar<'a>;
+}
+
+impl Foo for () {
+    type Bar<'a> = impl Deref<Target = Self::Baz<'a>>;
+
+    type Baz<T> = impl Sized;
+    //~^ ERROR type `Baz` has 1 type parameter but its trait declaration has 0 type parameters
+    //~| ERROR unconstrained opaque type
+
+    fn test<'a>() -> Self::Bar<'a> {
+        &()
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/impl-trait-in-type-alias-with-bad-substs.stderr b/tests/ui/type-alias-impl-trait/impl-trait-in-type-alias-with-bad-substs.stderr
new file mode 100644
index 00000000000..13f5d8b8ea6
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/impl-trait-in-type-alias-with-bad-substs.stderr
@@ -0,0 +1,20 @@
+error[E0049]: type `Baz` has 1 type parameter but its trait declaration has 0 type parameters
+  --> $DIR/impl-trait-in-type-alias-with-bad-substs.rs:19:14
+   |
+LL |     type Baz<'a>;
+   |              -- expected 0 type parameters
+...
+LL |     type Baz<T> = impl Sized;
+   |              ^ found 1 type parameter
+
+error: unconstrained opaque type
+  --> $DIR/impl-trait-in-type-alias-with-bad-substs.rs:19:19
+   |
+LL |     type Baz<T> = impl Sized;
+   |                   ^^^^^^^^^^
+   |
+   = note: `Baz` must be used in combination with a concrete type within the same impl
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0049`.
diff --git a/tests/ui/type-alias-impl-trait/not-matching-trait-refs-isnt-defining.rs b/tests/ui/type-alias-impl-trait/not-matching-trait-refs-isnt-defining.rs
new file mode 100644
index 00000000000..131f8d999d8
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/not-matching-trait-refs-isnt-defining.rs
@@ -0,0 +1,33 @@
+#![feature(impl_trait_in_assoc_type)]
+
+trait Foo<T> {
+    type Assoc;
+
+    fn test() -> u32;
+}
+
+struct DefinesOpaque;
+impl Foo<DefinesOpaque> for () {
+    type Assoc = impl Sized;
+
+    // This test's return type is `u32`, *not* the opaque that is defined above.
+    // Previously we were only checking that the self type of the assoc matched,
+    // but this doesn't account for other impls with different trait substs.
+    fn test() -> <() as Foo<NoOpaques>>::Assoc {
+        let _: <Self as Foo<DefinesOpaque>>::Assoc = "";
+        //~^ ERROR mismatched types
+
+        1
+    }
+}
+
+struct NoOpaques;
+impl Foo<NoOpaques> for () {
+    type Assoc = u32;
+
+    fn test() -> u32 {
+        1
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/not-matching-trait-refs-isnt-defining.stderr b/tests/ui/type-alias-impl-trait/not-matching-trait-refs-isnt-defining.stderr
new file mode 100644
index 00000000000..d2d00749091
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/not-matching-trait-refs-isnt-defining.stderr
@@ -0,0 +1,22 @@
+error[E0308]: mismatched types
+  --> $DIR/not-matching-trait-refs-isnt-defining.rs:17:54
+   |
+LL |     type Assoc = impl Sized;
+   |                  ---------- the expected opaque type
+...
+LL |         let _: <Self as Foo<DefinesOpaque>>::Assoc = "";
+   |                -----------------------------------   ^^ expected opaque type, found `&str`
+   |                |
+   |                expected due to this
+   |
+   = note: expected opaque type `<() as Foo<DefinesOpaque>>::Assoc`
+                found reference `&'static str`
+note: this item must have the opaque type in its signature in order to be able to register hidden types
+  --> $DIR/not-matching-trait-refs-isnt-defining.rs:16:5
+   |
+LL |     fn test() -> <() as Foo<NoOpaques>>::Assoc {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.