about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs2
-rw-r--r--compiler/rustc_const_eval/src/const_eval/fn_queries.rs13
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs2
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs2
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs18
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs2
-rw-r--r--compiler/rustc_middle/src/query/mod.rs6
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/util.rs2
-rw-r--r--compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs3
-rw-r--r--compiler/rustc_typeck/src/check/wfcheck.rs21
-rw-r--r--library/alloc/src/collections/btree/map.rs10
-rw-r--r--library/alloc/src/collections/btree/set.rs2
-rw-r--r--library/std/src/alloc.rs8
-rw-r--r--src/etc/htmldocck.py4
-rw-r--r--src/librustdoc/html/format.rs44
-rw-r--r--src/test/rustdoc/tuples.link1_i32.html1
-rw-r--r--src/test/rustdoc/tuples.link1_t.html1
-rw-r--r--src/test/rustdoc/tuples.link2_i32.html1
-rw-r--r--src/test/rustdoc/tuples.link2_t.html1
-rw-r--r--src/test/rustdoc/tuples.link2_tu.html1
-rw-r--r--src/test/rustdoc/tuples.link_unit.html1
-rw-r--r--src/test/rustdoc/tuples.rs12
25 files changed, 98 insertions, 73 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index ee097b5f051..8ac5f094cf6 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -1120,8 +1120,6 @@ impl<'a> Linker for EmLinker<'a> {
             OptLevel::Size => "-Os",
             OptLevel::SizeMin => "-Oz",
         });
-        // Unusable until https://github.com/rust-lang/rust/issues/38454 is resolved
-        self.cmd.args(&["--memory-init-file", "0"]);
     }
 
     fn pgo_gen(&mut self) {
diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
index d6f62062d1f..f1674d04f8d 100644
--- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
@@ -17,13 +17,14 @@ pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Symbol> {
 
 pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
     let parent_id = tcx.local_parent(def_id);
-    tcx.def_kind(parent_id) == DefKind::Impl
-        && tcx.impl_constness(parent_id) == hir::Constness::Const
+    tcx.def_kind(parent_id) == DefKind::Impl && tcx.constness(parent_id) == hir::Constness::Const
 }
 
-/// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
-/// said intrinsic has a `rustc_const_{un,}stable` attribute.
-fn impl_constness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Constness {
+/// Checks whether an item is considered to be `const`. If it is a constructor, it is const. If
+/// it is a trait impl/function, return if it has a `const` modifier. If it is an intrinsic,
+/// report whether said intrinsic has a `rustc_const_{un,}stable` attribute. Otherwise, return
+/// `Constness::NotConst`.
+fn constness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Constness {
     let def_id = def_id.expect_local();
     let node = tcx.hir().get_by_def_id(def_id);
 
@@ -77,5 +78,5 @@ fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
 }
 
 pub fn provide(providers: &mut Providers) {
-    *providers = Providers { impl_constness, is_promotable_const_fn, ..*providers };
+    *providers = Providers { constness, is_promotable_const_fn, ..*providers };
 }
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index b8a14e37a09..069fbed36ee 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -753,7 +753,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                                 callee = did;
                             }
 
-                            if let hir::Constness::NotConst = tcx.impl_constness(data.impl_def_id) {
+                            if let hir::Constness::NotConst = tcx.constness(data.impl_def_id) {
                                 self.check_op(ops::FnCallNonConst {
                                     caller,
                                     callee,
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index e3581a7607f..0bea2a10da8 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -214,7 +214,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
     impl_parent => { table }
     impl_polarity => { table_direct }
     impl_defaultness => { table_direct }
-    impl_constness => { table_direct }
+    constness => { table_direct }
     coerce_unsized_info => { table }
     mir_const_qualif => { table }
     rendered_const => { table }
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 463af64a2a4..75286b89068 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1063,7 +1063,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         };
 
         record!(self.tables.kind[def_id] <- EntryKind::Variant(self.lazy(data)));
-        self.tables.impl_constness.set(def_id.index, hir::Constness::Const);
+        self.tables.constness.set(def_id.index, hir::Constness::Const);
         record_array!(self.tables.children[def_id] <- variant.fields.iter().map(|f| {
             assert!(f.did.is_local());
             f.did.index
@@ -1092,7 +1092,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         };
 
         record!(self.tables.kind[def_id] <- EntryKind::Variant(self.lazy(data)));
-        self.tables.impl_constness.set(def_id.index, hir::Constness::Const);
+        self.tables.constness.set(def_id.index, hir::Constness::Const);
         self.encode_item_type(def_id);
         if variant.ctor_kind == CtorKind::Fn {
             record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
@@ -1175,7 +1175,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         };
 
         record!(self.tables.repr_options[def_id] <- adt_def.repr());
-        self.tables.impl_constness.set(def_id.index, hir::Constness::Const);
+        self.tables.constness.set(def_id.index, hir::Constness::Const);
         record!(self.tables.kind[def_id] <- EntryKind::Struct(self.lazy(data)));
         self.encode_item_type(def_id);
         if variant.ctor_kind == CtorKind::Fn {
@@ -1226,7 +1226,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                     }
                 };
                 self.tables.asyncness.set(def_id.index, m_sig.header.asyncness);
-                self.tables.impl_constness.set(def_id.index, hir::Constness::NotConst);
+                self.tables.constness.set(def_id.index, hir::Constness::NotConst);
                 record!(self.tables.kind[def_id] <- EntryKind::AssocFn(self.lazy(AssocFnData {
                     container,
                     has_self: trait_item.fn_has_self_parameter,
@@ -1290,7 +1290,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 } else {
                     hir::Constness::NotConst
                 };
-                self.tables.impl_constness.set(def_id.index, constness);
+                self.tables.constness.set(def_id.index, constness);
                 record!(self.tables.kind[def_id] <- EntryKind::AssocFn(self.lazy(AssocFnData {
                     container,
                     has_self: impl_item.fn_has_self_parameter,
@@ -1413,7 +1413,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             hir::ItemKind::Fn(ref sig, .., body) => {
                 self.tables.asyncness.set(def_id.index, sig.header.asyncness);
                 record_array!(self.tables.fn_arg_names[def_id] <- self.tcx.hir().body_param_names(body));
-                self.tables.impl_constness.set(def_id.index, sig.header.constness);
+                self.tables.constness.set(def_id.index, sig.header.constness);
                 EntryKind::Fn
             }
             hir::ItemKind::Macro(ref macro_def, _) => {
@@ -1437,7 +1437,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             hir::ItemKind::Struct(ref struct_def, _) => {
                 let adt_def = self.tcx.adt_def(def_id);
                 record!(self.tables.repr_options[def_id] <- adt_def.repr());
-                self.tables.impl_constness.set(def_id.index, hir::Constness::Const);
+                self.tables.constness.set(def_id.index, hir::Constness::Const);
 
                 // Encode def_ids for each field and method
                 // for methods, write all the stuff get_trait_method
@@ -1468,7 +1468,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             }
             hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => {
                 self.tables.impl_defaultness.set(def_id.index, *defaultness);
-                self.tables.impl_constness.set(def_id.index, *constness);
+                self.tables.constness.set(def_id.index, *constness);
 
                 let trait_ref = self.tcx.impl_trait_ref(def_id);
                 if let Some(trait_ref) = trait_ref {
@@ -1934,7 +1934,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 } else {
                     hir::Constness::NotConst
                 };
-                self.tables.impl_constness.set(def_id.index, constness);
+                self.tables.constness.set(def_id.index, constness);
                 record!(self.tables.kind[def_id] <- EntryKind::ForeignFn);
             }
             hir::ForeignItemKind::Static(..) => {
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 04f0847f5cc..a58c0e68ee3 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -364,7 +364,7 @@ define_tables! {
     thir_abstract_const: Table<DefIndex, LazyValue<&'static [thir::abstract_const::Node<'static>]>>,
     impl_parent: Table<DefIndex, RawDefId>,
     impl_polarity: Table<DefIndex, ty::ImplPolarity>,
-    impl_constness: Table<DefIndex, hir::Constness>,
+    constness: Table<DefIndex, hir::Constness>,
     is_intrinsic: Table<DefIndex, ()>,
     impl_defaultness: Table<DefIndex, hir::Defaultness>,
     // FIXME(eddyb) perhaps compute this on the fly if cheap enough?
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index e0b4eced075..ca2c03cb614 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -590,9 +590,9 @@ rustc_queries! {
     /// not have the feature gate active).
     ///
     /// **Do not call this function manually.** It is only meant to cache the base data for the
-    /// `is_const_fn` function.
-    query impl_constness(key: DefId) -> hir::Constness {
-        desc { |tcx| "checking if item is const fn: `{}`", tcx.def_path_str(key) }
+    /// `is_const_fn` function. Consider using `is_const_fn` or `is_const_fn_raw` instead.
+    query constness(key: DefId) -> hir::Constness {
+        desc { |tcx| "checking if item is const: `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
     }
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 749feb850b0..6789f02c46a 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -2341,7 +2341,7 @@ impl<'tcx> TyCtxt<'tcx> {
     #[inline]
     pub fn is_const_fn_raw(self, def_id: DefId) -> bool {
         matches!(self.def_kind(def_id), DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..))
-            && self.impl_constness(def_id) == hir::Constness::Const
+            && self.constness(def_id) == hir::Constness::Const
     }
 
     #[inline]
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 33ef0283745..b9c95813121 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -376,7 +376,7 @@ impl<'tcx> TyCtxt<'tcx> {
         let (did, constness) = self.find_map_relevant_impl(drop_trait, ty, |impl_did| {
             if let Some(item_id) = self.associated_item_def_ids(impl_did).first() {
                 if validate(self, impl_did).is_ok() {
-                    return Some((*item_id, self.impl_constness(impl_did)));
+                    return Some((*item_id, self.constness(impl_did)));
                 }
             }
             None
diff --git a/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs b/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs
index a90c7b7bc6f..269bf8b8bcd 100644
--- a/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs
+++ b/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs
@@ -2,10 +2,10 @@ use super::{wasm32_unknown_emscripten, LinkerFlavor, Target};
 
 pub fn target() -> Target {
     let mut target = wasm32_unknown_emscripten::target();
-    target
-        .post_link_args
-        .entry(LinkerFlavor::Em)
-        .or_default()
-        .extend(vec!["-s".into(), "WASM=0".into()]);
+    target.post_link_args.entry(LinkerFlavor::Em).or_default().extend(vec![
+        "-sWASM=0".into(),
+        "--memory-init-file".into(),
+        "0".into(),
+    ]);
     target
 }
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index c8b4303e1e0..cfd50c1afb9 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -970,7 +970,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
                 if let Some(impl_def_id) = relevant_impl {
                     // Check that `impl Drop` is actually const, if there is a custom impl
-                    if self.tcx().impl_constness(impl_def_id) == hir::Constness::Const {
+                    if self.tcx().constness(impl_def_id) == hir::Constness::Const {
                         candidates.vec.push(ConstDestructCandidate(Some(impl_def_id)));
                     }
                 } else {
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index ee2c8da5a00..297fbfa1c32 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -1119,8 +1119,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             if obligation.is_const() {
                 match candidate {
                     // const impl
-                    ImplCandidate(def_id)
-                        if tcx.impl_constness(def_id) == hir::Constness::Const => {}
+                    ImplCandidate(def_id) if tcx.constness(def_id) == hir::Constness::Const => {}
                     // const param
                     ParamCandidate(trait_pred) if trait_pred.is_const_if_const() => {}
                     // auto trait impl
diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs
index ad85ac71fc8..79312153895 100644
--- a/compiler/rustc_typeck/src/check/wfcheck.rs
+++ b/compiler/rustc_typeck/src/check/wfcheck.rs
@@ -490,7 +490,7 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<'tcx>>(
     // The bounds we that we would require from `to_check`
     let mut bounds = FxHashSet::default();
 
-    let (regions, types) = GATSubstCollector::visit(tcx, gat_def_id.to_def_id(), to_check);
+    let (regions, types) = GATSubstCollector::visit(gat_def_id.to_def_id(), to_check);
 
     // If both regions and types are empty, then this GAT isn't in the
     // set of types we are checking, and we shouldn't try to do clause analysis
@@ -664,7 +664,6 @@ fn resolve_regions_with_wf_tys<'tcx>(
 /// the two vectors, `regions` and `types` (depending on their kind). For each
 /// parameter `Pi` also track the index `i`.
 struct GATSubstCollector<'tcx> {
-    tcx: TyCtxt<'tcx>,
     gat: DefId,
     // Which region appears and which parameter index its substituted for
     regions: FxHashSet<(ty::Region<'tcx>, usize)>,
@@ -674,16 +673,11 @@ struct GATSubstCollector<'tcx> {
 
 impl<'tcx> GATSubstCollector<'tcx> {
     fn visit<T: TypeFoldable<'tcx>>(
-        tcx: TyCtxt<'tcx>,
         gat: DefId,
         t: T,
     ) -> (FxHashSet<(ty::Region<'tcx>, usize)>, FxHashSet<(Ty<'tcx>, usize)>) {
-        let mut visitor = GATSubstCollector {
-            tcx,
-            gat,
-            regions: FxHashSet::default(),
-            types: FxHashSet::default(),
-        };
+        let mut visitor =
+            GATSubstCollector { gat, regions: FxHashSet::default(), types: FxHashSet::default() };
         t.visit_with(&mut visitor);
         (visitor.regions, visitor.types)
     }
@@ -692,19 +686,12 @@ impl<'tcx> GATSubstCollector<'tcx> {
 impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> {
     type BreakTy = !;
 
-    fn visit_binder<T: TypeFoldable<'tcx>>(
-        &mut self,
-        t: &ty::Binder<'tcx, T>,
-    ) -> ControlFlow<Self::BreakTy> {
-        self.tcx.liberate_late_bound_regions(self.gat, t.clone()).visit_with(self)
-    }
-
     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         match t.kind() {
             ty::Projection(p) if p.item_def_id == self.gat => {
                 for (idx, subst) in p.substs.iter().enumerate() {
                     match subst.unpack() {
-                        GenericArgKind::Lifetime(lt) => {
+                        GenericArgKind::Lifetime(lt) if !lt.is_late_bound() => {
                             self.regions.insert((lt, idx));
                         }
                         GenericArgKind::Type(t) => {
diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs
index e1124a68750..28068a88060 100644
--- a/library/alloc/src/collections/btree/map.rs
+++ b/library/alloc/src/collections/btree/map.rs
@@ -1644,11 +1644,11 @@ impl<K, V, A: Allocator + Clone> IntoIter<K, V, A> {
         &mut self,
     ) -> Option<Handle<NodeRef<marker::Dying, K, V, marker::LeafOrInternal>, marker::KV>> {
         if self.length == 0 {
-            self.range.deallocating_end(&self.alloc);
+            self.range.deallocating_end(self.alloc.clone());
             None
         } else {
             self.length -= 1;
-            Some(unsafe { self.range.deallocating_next_unchecked(&self.alloc) })
+            Some(unsafe { self.range.deallocating_next_unchecked(self.alloc.clone()) })
         }
     }
 
@@ -1658,11 +1658,11 @@ impl<K, V, A: Allocator + Clone> IntoIter<K, V, A> {
         &mut self,
     ) -> Option<Handle<NodeRef<marker::Dying, K, V, marker::LeafOrInternal>, marker::KV>> {
         if self.length == 0 {
-            self.range.deallocating_end(&self.alloc);
+            self.range.deallocating_end(self.alloc.clone());
             None
         } else {
             self.length -= 1;
-            Some(unsafe { self.range.deallocating_next_back_unchecked(&self.alloc) })
+            Some(unsafe { self.range.deallocating_next_back_unchecked(self.alloc.clone()) })
         }
     }
 }
@@ -1849,7 +1849,7 @@ where
     type Item = (K, V);
 
     fn next(&mut self) -> Option<(K, V)> {
-        self.inner.next(&mut self.pred, &self.alloc)
+        self.inner.next(&mut self.pred, self.alloc.clone())
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs
index bec3b967525..0d3fdc9019e 100644
--- a/library/alloc/src/collections/btree/set.rs
+++ b/library/alloc/src/collections/btree/set.rs
@@ -1320,7 +1320,7 @@ where
     fn next(&mut self) -> Option<T> {
         let pred = &mut self.pred;
         let mut mapped_pred = |k: &T, _v: &mut ()| pred(k);
-        self.inner.next(&mut mapped_pred, &self.alloc).map(|(k, _)| k)
+        self.inner.next(&mut mapped_pred, self.alloc.clone()).map(|(k, _)| k)
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs
index d3879273f5b..d554ec59035 100644
--- a/library/std/src/alloc.rs
+++ b/library/std/src/alloc.rs
@@ -187,7 +187,7 @@ impl System {
             old_size => unsafe {
                 let new_ptr = self.alloc_impl(new_layout, zeroed)?;
                 ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_size);
-                Allocator::deallocate(&self, ptr, old_layout);
+                Allocator::deallocate(self, ptr, old_layout);
                 Ok(new_ptr)
             },
         }
@@ -254,7 +254,7 @@ unsafe impl Allocator for System {
         match new_layout.size() {
             // SAFETY: conditions must be upheld by the caller
             0 => unsafe {
-                Allocator::deallocate(&self, ptr, old_layout);
+                Allocator::deallocate(self, ptr, old_layout);
                 Ok(NonNull::slice_from_raw_parts(new_layout.dangling(), 0))
             },
 
@@ -274,9 +274,9 @@ unsafe impl Allocator for System {
             // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
             // for `dealloc` must be upheld by the caller.
             new_size => unsafe {
-                let new_ptr = Allocator::allocate(&self, new_layout)?;
+                let new_ptr = Allocator::allocate(self, new_layout)?;
                 ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_size);
-                Allocator::deallocate(&self, ptr, old_layout);
+                Allocator::deallocate(self, ptr, old_layout);
                 Ok(new_ptr)
             },
         }
diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py
index f762e389005..70b6af717cd 100644
--- a/src/etc/htmldocck.py
+++ b/src/etc/htmldocck.py
@@ -417,7 +417,7 @@ def check_snapshot(snapshot_name, actual_tree, normalize_to_text):
     snapshot_path = '{}.{}.{}'.format(rust_test_path[:-3], snapshot_name, 'html')
     try:
         with open(snapshot_path, 'r') as snapshot_file:
-            expected_str = snapshot_file.read()
+            expected_str = snapshot_file.read().replace("{{channel}}", channel)
     except FileNotFoundError:
         if bless:
             expected_str = None
@@ -429,8 +429,6 @@ def check_snapshot(snapshot_name, actual_tree, normalize_to_text):
     else:
         actual_str = flatten(actual_tree)
 
-    expected_str = expected_str.replace("{{channel}}", channel)
-
     # Conditions:
     #  1. Is --bless
     #  2. Are actual and expected tree different
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 0c0920ae63e..5baa53d5554 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -23,6 +23,8 @@ use rustc_span::symbol::kw;
 use rustc_span::{sym, Symbol};
 use rustc_target::spec::abi::Abi;
 
+use itertools::Itertools;
+
 use crate::clean::{
     self, types::ExternalLocation, utils::find_nearest_parent_module, ExternalCrate, ItemId,
     PrimitiveType,
@@ -874,20 +876,42 @@ fn fmt_type<'cx>(
             match &typs[..] {
                 &[] => primitive_link(f, PrimitiveType::Unit, "()", cx),
                 &[ref one] => {
-                    primitive_link(f, PrimitiveType::Tuple, "(", cx)?;
-                    // Carry `f.alternate()` into this display w/o branching manually.
-                    fmt::Display::fmt(&one.print(cx), f)?;
-                    primitive_link(f, PrimitiveType::Tuple, ",)", cx)
+                    if let clean::Generic(name) = one {
+                        primitive_link(f, PrimitiveType::Tuple, &format!("({name},)"), cx)
+                    } else {
+                        write!(f, "(")?;
+                        // Carry `f.alternate()` into this display w/o branching manually.
+                        fmt::Display::fmt(&one.print(cx), f)?;
+                        write!(f, ",)")
+                    }
                 }
                 many => {
-                    primitive_link(f, PrimitiveType::Tuple, "(", cx)?;
-                    for (i, item) in many.iter().enumerate() {
-                        if i != 0 {
-                            write!(f, ", ")?;
+                    let generic_names: Vec<Symbol> = many
+                        .iter()
+                        .filter_map(|t| match t {
+                            clean::Generic(name) => Some(*name),
+                            _ => None,
+                        })
+                        .collect();
+                    let is_generic = generic_names.len() == many.len();
+                    if is_generic {
+                        primitive_link(
+                            f,
+                            PrimitiveType::Tuple,
+                            &format!("({})", generic_names.iter().map(|s| s.as_str()).join(", ")),
+                            cx,
+                        )
+                    } else {
+                        write!(f, "(")?;
+                        for (i, item) in many.iter().enumerate() {
+                            if i != 0 {
+                                write!(f, ", ")?;
+                            }
+                            // Carry `f.alternate()` into this display w/o branching manually.
+                            fmt::Display::fmt(&item.print(cx), f)?;
                         }
-                        fmt::Display::fmt(&item.print(cx), f)?;
+                        write!(f, ")")
                     }
-                    primitive_link(f, PrimitiveType::Tuple, ")", cx)
                 }
             }
         }
diff --git a/src/test/rustdoc/tuples.link1_i32.html b/src/test/rustdoc/tuples.link1_i32.html
new file mode 100644
index 00000000000..4efde28ed52
--- /dev/null
+++ b/src/test/rustdoc/tuples.link1_i32.html
@@ -0,0 +1 @@
+<code>pub fn tuple1(x: (<a class="primitive" href="{{channel}}/std/primitive.i32.html">i32</a>,)) -&gt; (<a class="primitive" href="{{channel}}/std/primitive.i32.html">i32</a>,)</code>
\ No newline at end of file
diff --git a/src/test/rustdoc/tuples.link1_t.html b/src/test/rustdoc/tuples.link1_t.html
new file mode 100644
index 00000000000..1cbaec05733
--- /dev/null
+++ b/src/test/rustdoc/tuples.link1_t.html
@@ -0,0 +1 @@
+<code>pub fn tuple1_t&lt;T&gt;(x: <a class="primitive" href="{{channel}}/std/primitive.tuple.html">(T,)</a>) -&gt; <a class="primitive" href="{{channel}}/std/primitive.tuple.html">(T,)</a></code>
\ No newline at end of file
diff --git a/src/test/rustdoc/tuples.link2_i32.html b/src/test/rustdoc/tuples.link2_i32.html
new file mode 100644
index 00000000000..77c8d81b842
--- /dev/null
+++ b/src/test/rustdoc/tuples.link2_i32.html
@@ -0,0 +1 @@
+<code>pub fn tuple2(x: (<a class="primitive" href="{{channel}}/std/primitive.i32.html">i32</a>, <a class="primitive" href="{{channel}}/std/primitive.i32.html">i32</a>)) -&gt; (<a class="primitive" href="{{channel}}/std/primitive.i32.html">i32</a>, <a class="primitive" href="{{channel}}/std/primitive.i32.html">i32</a>)</code>
\ No newline at end of file
diff --git a/src/test/rustdoc/tuples.link2_t.html b/src/test/rustdoc/tuples.link2_t.html
new file mode 100644
index 00000000000..2477aa6be9d
--- /dev/null
+++ b/src/test/rustdoc/tuples.link2_t.html
@@ -0,0 +1 @@
+<code>pub fn tuple2_t&lt;T&gt;(x: <a class="primitive" href="{{channel}}/std/primitive.tuple.html">(T, T)</a>) -&gt; <a class="primitive" href="{{channel}}/std/primitive.tuple.html">(T, T)</a></code>
\ No newline at end of file
diff --git a/src/test/rustdoc/tuples.link2_tu.html b/src/test/rustdoc/tuples.link2_tu.html
new file mode 100644
index 00000000000..b02f8dd8d65
--- /dev/null
+++ b/src/test/rustdoc/tuples.link2_tu.html
@@ -0,0 +1 @@
+<code>pub fn tuple2_tu&lt;T, U&gt;(x: <a class="primitive" href="{{channel}}/std/primitive.tuple.html">(T, U)</a>) -&gt; <a class="primitive" href="{{channel}}/std/primitive.tuple.html">(T, U)</a></code>
\ No newline at end of file
diff --git a/src/test/rustdoc/tuples.link_unit.html b/src/test/rustdoc/tuples.link_unit.html
new file mode 100644
index 00000000000..839990e1587
--- /dev/null
+++ b/src/test/rustdoc/tuples.link_unit.html
@@ -0,0 +1 @@
+<code>pub fn tuple0(x: <a class="primitive" href="{{channel}}/std/primitive.unit.html">()</a>)</code>
\ No newline at end of file
diff --git a/src/test/rustdoc/tuples.rs b/src/test/rustdoc/tuples.rs
index 53654abff2a..62e2f9e7ef2 100644
--- a/src/test/rustdoc/tuples.rs
+++ b/src/test/rustdoc/tuples.rs
@@ -1,8 +1,20 @@
 #![crate_name = "foo"]
 
 // @has foo/fn.tuple0.html //pre 'pub fn tuple0(x: ())'
+// @snapshot link_unit - '//pre[@class="rust fn"]/code'
 pub fn tuple0(x: ()) -> () { x }
 // @has foo/fn.tuple1.html //pre 'pub fn tuple1(x: (i32,)) -> (i32,)'
+// @snapshot link1_i32 - '//pre[@class="rust fn"]/code'
 pub fn tuple1(x: (i32,)) -> (i32,) { x }
 // @has foo/fn.tuple2.html //pre 'pub fn tuple2(x: (i32, i32)) -> (i32, i32)'
+// @snapshot link2_i32 - '//pre[@class="rust fn"]/code'
 pub fn tuple2(x: (i32, i32)) -> (i32, i32) { x }
+// @has foo/fn.tuple1_t.html //pre 'pub fn tuple1_t<T>(x: (T,)) -> (T,)'
+// @snapshot link1_t - '//pre[@class="rust fn"]/code'
+pub fn tuple1_t<T>(x: (T,)) -> (T,) { x }
+// @has foo/fn.tuple2_t.html //pre 'pub fn tuple2_t<T>(x: (T, T)) -> (T, T)'
+// @snapshot link2_t - '//pre[@class="rust fn"]/code'
+pub fn tuple2_t<T>(x: (T, T)) -> (T, T) { x }
+// @has foo/fn.tuple2_tu.html //pre 'pub fn tuple2_tu<T, U>(x: (T, U)) -> (T, U)'
+// @snapshot link2_tu - '//pre[@class="rust fn"]/code'
+pub fn tuple2_tu<T, U>(x: (T, U)) -> (T, U) { x }