about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-08-19 20:42:45 +0000
committerbors <bors@rust-lang.org>2024-08-19 20:42:45 +0000
commit636d7ff91b9847d6d43c7bbe023568828f6e3246 (patch)
tree1465b175c7f5fa0e6ce1c772b4ebc308302a46ed
parent5601d14249818d952da612fec481b7af3ed03a39 (diff)
parent77303568c049c5cb53d2cb8e67c8f34c819d3f5e (diff)
downloadrust-636d7ff91b9847d6d43c7bbe023568828f6e3246.tar.gz
rust-636d7ff91b9847d6d43c7bbe023568828f6e3246.zip
Auto merge of #129275 - matthiaskrgr:rollup-qv64hg6, r=matthiaskrgr
Rollup of 6 pull requests

Successful merges:

 - #129194 (Fix bootstrap test `detect_src_and_out` on Windows)
 - #129217 (safe transmute: check lifetimes)
 - #129223 ( Fix wrong argument for `get_fn_decl`)
 - #129235 (Check that `#[may_dangle]` is properly applied)
 - #129245 (Fix a typo in `rustc_hir` doc comment)
 - #129271 (Prevent double panic in query system, improve diagnostics)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_hir/src/hir.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs2
-rw-r--r--compiler/rustc_passes/messages.ftl7
-rw-r--r--compiler/rustc_passes/src/check_attr.rs25
-rw-r--r--compiler/rustc_passes/src/errors.rs7
-rw-r--r--compiler/rustc_query_impl/src/plumbing.rs10
-rw-r--r--compiler/rustc_query_system/src/query/plumbing.rs11
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs176
-rw-r--r--compiler/rustc_transmute/src/layout/mod.rs13
-rw-r--r--compiler/rustc_transmute/src/layout/tree.rs141
-rw-r--r--compiler/rustc_transmute/src/lib.rs2
-rw-r--r--compiler/rustc_transmute/src/maybe_transmutable/mod.rs7
-rw-r--r--src/bootstrap/src/core/config/tests.rs4
-rw-r--r--tests/ui/async-await/async-fn/recurse-ice-129215.rs9
-rw-r--r--tests/ui/async-await/async-fn/recurse-ice-129215.stderr34
-rw-r--r--tests/ui/attributes/may_dangle.rs53
-rw-r--r--tests/ui/attributes/may_dangle.stderr50
-rw-r--r--tests/ui/closures/add_semicolon_non_block_closure.rs1
-rw-r--r--tests/ui/closures/add_semicolon_non_block_closure.stderr6
-rw-r--r--tests/ui/lint/must_not_suspend/other_items.stderr4
-rw-r--r--tests/ui/lint/must_not_suspend/return.stderr4
-rw-r--r--tests/ui/transmutability/references/accept_assume_lifetime_extension.rs91
-rw-r--r--tests/ui/transmutability/references/accept_unexercised_lifetime_extension.rs68
-rw-r--r--tests/ui/transmutability/references/reject_lifetime_extension.rs91
-rw-r--r--tests/ui/transmutability/references/reject_lifetime_extension.stderr78
25 files changed, 746 insertions, 150 deletions
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 6599e703461..6c7125b75db 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1395,7 +1395,7 @@ pub struct LetExpr<'hir> {
     pub pat: &'hir Pat<'hir>,
     pub ty: Option<&'hir Ty<'hir>>,
     pub init: &'hir Expr<'hir>,
-    /// `Recovered::Yes` when this let expressions is not in a syntanctically valid location.
+    /// `Recovered::Yes` when this let expressions is not in a syntactically valid location.
     /// Used to prevent building MIR in such situations.
     pub recovered: ast::Recovered,
 }
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index d53df251a15..9b34c59f1f1 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -1859,7 +1859,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         };
 
         // If this is due to an explicit `return`, suggest adding a return type.
-        if let Some((fn_id, fn_decl, can_suggest)) = fcx.get_fn_decl(parent_id)
+        if let Some((fn_id, fn_decl, can_suggest)) = fcx.get_fn_decl(block_or_return_id)
             && !due_to_block
         {
             fcx.suggest_missing_return_type(&mut err, fn_decl, expected, found, can_suggest, fn_id);
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 1ea4ca375f1..dfc726efeb9 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -444,6 +444,9 @@ passes_macro_export_on_decl_macro =
 passes_macro_use =
     `#[{$name}]` only has an effect on `extern crate` and modules
 
+passes_may_dangle =
+    `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
+
 passes_maybe_string_interpolation = you might have meant to use string interpolation in this string literal
 passes_missing_const_err =
     attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const`
@@ -475,8 +478,8 @@ passes_multiple_start_functions =
     .previous = previous `#[start]` function here
 
 passes_must_not_suspend =
-    `must_not_suspend` attribute should be applied to a struct, enum, or trait
-    .label = is not a struct, enum, or trait
+    `must_not_suspend` attribute should be applied to a struct, enum, union, or trait
+    .label = is not a struct, enum, union, or trait
 
 passes_must_use_async =
     `must_use` attribute on `async` functions applies to the anonymous `Future` returned by the function, not the value within
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index a47add929eb..e3c2999142f 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -189,6 +189,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 [sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target),
                 [sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target),
                 [sym::must_use, ..] => self.check_must_use(hir_id, attr, target),
+                [sym::may_dangle, ..] => self.check_may_dangle(hir_id, attr),
                 [sym::rustc_pass_by_value, ..] => self.check_pass_by_value(attr, span, target),
                 [sym::rustc_allow_incoherent_impl, ..] => {
                     self.check_allow_incoherent_impl(attr, span, target)
@@ -255,7 +256,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     | sym::cfg_attr
                     // need to be fixed
                     | sym::cfi_encoding // FIXME(cfi_encoding)
-                    | sym::may_dangle // FIXME(dropck_eyepatch)
                     | sym::pointee // FIXME(derive_smart_pointer)
                     | sym::omit_gdb_pretty_printer_section // FIXME(omit_gdb_pretty_printer_section)
                     | sym::used // handled elsewhere to restrict to static items
@@ -1363,7 +1363,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
     }
 
-    /// Checks if `#[must_not_suspend]` is applied to a function.
+    /// Checks if `#[must_not_suspend]` is applied to a struct, enum, union, or trait.
     fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) {
         match target {
             Target::Struct | Target::Enum | Target::Union | Target::Trait => {}
@@ -1373,6 +1373,27 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
     }
 
+    /// Checks if `#[may_dangle]` is applied to a lifetime or type generic parameter in `Drop` impl.
+    fn check_may_dangle(&self, hir_id: HirId, attr: &Attribute) {
+        if let hir::Node::GenericParam(param) = self.tcx.hir_node(hir_id)
+            && matches!(
+                param.kind,
+                hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. }
+            )
+            && matches!(param.source, hir::GenericParamSource::Generics)
+            && let parent_hir_id = self.tcx.parent_hir_id(hir_id)
+            && let hir::Node::Item(item) = self.tcx.hir_node(parent_hir_id)
+            && let hir::ItemKind::Impl(impl_) = item.kind
+            && let Some(trait_) = impl_.of_trait
+            && let Some(def_id) = trait_.trait_def_id()
+            && self.tcx.is_lang_item(def_id, hir::LangItem::Drop)
+        {
+            return;
+        }
+
+        self.dcx().emit_err(errors::InvalidMayDangle { attr_span: attr.span });
+    }
+
     /// Checks if `#[cold]` is applied to a non-function.
     fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
         match target {
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 3a043e0e3c1..ee7d097e5d3 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -737,6 +737,13 @@ pub struct NonExportedMacroInvalidAttrs {
     pub attr_span: Span,
 }
 
+#[derive(Diagnostic)]
+#[diag(passes_may_dangle)]
+pub struct InvalidMayDangle {
+    #[primary_span]
+    pub attr_span: Span,
+}
+
 #[derive(LintDiagnostic)]
 #[diag(passes_unused_duplicate)]
 pub struct UnusedDuplicate {
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index b9e700c1938..c064b2bd6c1 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -702,11 +702,17 @@ macro_rules! define_queries {
                     let name = stringify!($name);
                     $crate::plumbing::create_query_frame(tcx, rustc_middle::query::descs::$name, key, kind, name)
                 };
-                tcx.query_system.states.$name.try_collect_active_jobs(
+                let res = tcx.query_system.states.$name.try_collect_active_jobs(
                     tcx,
                     make_query,
                     qmap,
-                ).unwrap();
+                );
+                // this can be called during unwinding, and the function has a `try_`-prefix, so
+                // don't `unwrap()` here, just manually check for `None` and do best-effort error
+                // reporting.
+                if res.is_none() {
+                    tracing::warn!("Failed to collect active jobs for query with name `{}`!", stringify!($name));
+                }
             }
 
             pub fn alloc_self_profile_query_strings<'tcx>(tcx: TyCtxt<'tcx>, string_cache: &mut QueryKeyStringCache) {
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index 8ef680cdb6c..6dbd6e89fe9 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -181,8 +181,15 @@ where
         cache.complete(key, result, dep_node_index);
 
         let job = {
-            let mut lock = state.active.lock_shard_by_value(&key);
-            lock.remove(&key).unwrap().expect_job()
+            let val = {
+                // don't keep the lock during the `unwrap()` of the retrieved value, or we taint the
+                // underlying shard.
+                // since unwinding also wants to look at this map, this can also prevent a double
+                // panic.
+                let mut lock = state.active.lock_shard_by_value(&key);
+                lock.remove(&key)
+            };
+            val.unwrap().expect_job()
         };
 
         job.signal_complete();
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 0d7ceca4301..f19cd19c99a 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -17,8 +17,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, HigherRankedType, InferOk};
 use rustc_infer::traits::ObligationCauseCode;
 use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData};
 use rustc_middle::ty::{
-    self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, TraitPredicate, Ty,
-    TyCtxt, Upcast,
+    self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, Ty, TyCtxt, Upcast,
 };
 use rustc_middle::{bug, span_bug};
 use rustc_span::def_id::DefId;
@@ -292,90 +291,120 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
     ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
-        use rustc_transmute::{Answer, Condition};
-        #[instrument(level = "debug", skip(tcx, obligation, predicate))]
+        use rustc_transmute::{Answer, Assume, Condition};
+
+        /// Generate sub-obligations for reference-to-reference transmutations.
+        fn reference_obligations<'tcx>(
+            tcx: TyCtxt<'tcx>,
+            obligation: &PolyTraitObligation<'tcx>,
+            (src_lifetime, src_ty, src_mut): (ty::Region<'tcx>, Ty<'tcx>, Mutability),
+            (dst_lifetime, dst_ty, dst_mut): (ty::Region<'tcx>, Ty<'tcx>, Mutability),
+            assume: Assume,
+        ) -> Vec<PredicateObligation<'tcx>> {
+            let make_transmute_obl = |src, dst| {
+                let transmute_trait = obligation.predicate.def_id();
+                let assume = obligation.predicate.skip_binder().trait_ref.args.const_at(2);
+                let trait_ref = ty::TraitRef::new(
+                    tcx,
+                    transmute_trait,
+                    [
+                        ty::GenericArg::from(dst),
+                        ty::GenericArg::from(src),
+                        ty::GenericArg::from(assume),
+                    ],
+                );
+                Obligation::with_depth(
+                    tcx,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    obligation.param_env,
+                    obligation.predicate.rebind(trait_ref),
+                )
+            };
+
+            let make_freeze_obl = |ty| {
+                let trait_ref = ty::TraitRef::new(
+                    tcx,
+                    tcx.require_lang_item(LangItem::Freeze, None),
+                    [ty::GenericArg::from(ty)],
+                );
+                Obligation::with_depth(
+                    tcx,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    obligation.param_env,
+                    trait_ref,
+                )
+            };
+
+            let make_outlives_obl = |target, region| {
+                let outlives = ty::OutlivesPredicate(target, region);
+                Obligation::with_depth(
+                    tcx,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    obligation.param_env,
+                    obligation.predicate.rebind(outlives),
+                )
+            };
+
+            // Given a transmutation from `&'a (mut) Src` and `&'dst (mut) Dst`,
+            // it is always the case that `Src` must be transmutable into `Dst`,
+            // and that that `'src` must outlive `'dst`.
+            let mut obls = vec![make_transmute_obl(src_ty, dst_ty)];
+            if !assume.lifetimes {
+                obls.push(make_outlives_obl(src_lifetime, dst_lifetime));
+            }
+
+            // Given a transmutation from `&Src`, both `Src` and `Dst` must be
+            // `Freeze`, otherwise, using the transmuted value could lead to
+            // data races.
+            if src_mut == Mutability::Not {
+                obls.extend([make_freeze_obl(src_ty), make_freeze_obl(dst_ty)])
+            }
+
+            // Given a transmutation into `&'dst mut Dst`, it also must be the
+            // case that `Dst` is transmutable into `Src`. For example,
+            // transmuting bool -> u8 is OK as long as you can't update that u8
+            // to be > 1, because you could later transmute the u8 back to a
+            // bool and get undefined behavior. It also must be the case that
+            // `'dst` lives exactly as long as `'src`.
+            if dst_mut == Mutability::Mut {
+                obls.push(make_transmute_obl(dst_ty, src_ty));
+                if !assume.lifetimes {
+                    obls.push(make_outlives_obl(dst_lifetime, src_lifetime));
+                }
+            }
+
+            obls
+        }
+
+        /// Flatten the `Condition` tree into a conjunction of obligations.
+        #[instrument(level = "debug", skip(tcx, obligation))]
         fn flatten_answer_tree<'tcx>(
             tcx: TyCtxt<'tcx>,
             obligation: &PolyTraitObligation<'tcx>,
-            predicate: TraitPredicate<'tcx>,
             cond: Condition<rustc_transmute::layout::rustc::Ref<'tcx>>,
+            assume: Assume,
         ) -> Vec<PredicateObligation<'tcx>> {
             match cond {
                 // FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll`
                 // Not possible until the trait solver supports disjunctions of obligations
                 Condition::IfAll(conds) | Condition::IfAny(conds) => conds
                     .into_iter()
-                    .flat_map(|cond| flatten_answer_tree(tcx, obligation, predicate, cond))
+                    .flat_map(|cond| flatten_answer_tree(tcx, obligation, cond, assume))
                     .collect(),
-                Condition::IfTransmutable { src, dst } => {
-                    let transmute_trait = obligation.predicate.def_id();
-                    let assume_const = predicate.trait_ref.args.const_at(2);
-                    let make_transmute_obl = |from_ty, to_ty| {
-                        let trait_ref = ty::TraitRef::new(
-                            tcx,
-                            transmute_trait,
-                            [
-                                ty::GenericArg::from(to_ty),
-                                ty::GenericArg::from(from_ty),
-                                ty::GenericArg::from(assume_const),
-                            ],
-                        );
-                        Obligation::with_depth(
-                            tcx,
-                            obligation.cause.clone(),
-                            obligation.recursion_depth + 1,
-                            obligation.param_env,
-                            trait_ref,
-                        )
-                    };
-
-                    let make_freeze_obl = |ty| {
-                        let trait_ref = ty::TraitRef::new(
-                            tcx,
-                            tcx.require_lang_item(LangItem::Freeze, None),
-                            [ty::GenericArg::from(ty)],
-                        );
-                        Obligation::with_depth(
-                            tcx,
-                            obligation.cause.clone(),
-                            obligation.recursion_depth + 1,
-                            obligation.param_env,
-                            trait_ref,
-                        )
-                    };
-
-                    let mut obls = vec![];
-
-                    // If the source is a shared reference, it must be `Freeze`;
-                    // otherwise, transmuting could lead to data races.
-                    if src.mutability == Mutability::Not {
-                        obls.extend([make_freeze_obl(src.ty), make_freeze_obl(dst.ty)])
-                    }
-
-                    // If Dst is mutable, check bidirectionally.
-                    // For example, transmuting bool -> u8 is OK as long as you can't update that u8
-                    // to be > 1, because you could later transmute the u8 back to a bool and get UB.
-                    match dst.mutability {
-                        Mutability::Not => obls.push(make_transmute_obl(src.ty, dst.ty)),
-                        Mutability::Mut => obls.extend([
-                            make_transmute_obl(src.ty, dst.ty),
-                            make_transmute_obl(dst.ty, src.ty),
-                        ]),
-                    }
-
-                    obls
-                }
+                Condition::IfTransmutable { src, dst } => reference_obligations(
+                    tcx,
+                    obligation,
+                    (src.lifetime, src.ty, src.mutability),
+                    (dst.lifetime, dst.ty, dst.mutability),
+                    assume,
+                ),
             }
         }
 
-        // We erase regions here because transmutability calls layout queries,
-        // which does not handle inference regions and doesn't particularly
-        // care about other regions. Erasing late-bound regions is equivalent
-        // to instantiating the binder with placeholders then erasing those
-        // placeholder regions.
-        let predicate = self
-            .tcx()
-            .erase_regions(self.tcx().instantiate_bound_regions_with_erased(obligation.predicate));
+        let predicate = obligation.predicate.skip_binder();
 
         let Some(assume) = rustc_transmute::Assume::from_const(
             self.infcx.tcx,
@@ -387,6 +416,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         let dst = predicate.trait_ref.args.type_at(0);
         let src = predicate.trait_ref.args.type_at(1);
+
         debug!(?src, ?dst);
         let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx);
         let maybe_transmutable = transmute_env.is_transmutable(
@@ -397,7 +427,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         let fully_flattened = match maybe_transmutable {
             Answer::No(_) => Err(Unimplemented)?,
-            Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, predicate, cond),
+            Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, cond, assume),
             Answer::Yes => vec![],
         };
 
diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs
index bbf155581f9..1cf9e0b9b70 100644
--- a/compiler/rustc_transmute/src/layout/mod.rs
+++ b/compiler/rustc_transmute/src/layout/mod.rs
@@ -63,7 +63,9 @@ pub mod rustc {
     use std::fmt::{self, Write};
 
     use rustc_middle::mir::Mutability;
-    use rustc_middle::ty::{self, Ty};
+    use rustc_middle::ty::layout::{LayoutCx, LayoutError};
+    use rustc_middle::ty::{self, Ty, TyCtxt};
+    use rustc_target::abi::Layout;
 
     /// A reference in the layout.
     #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
@@ -120,4 +122,13 @@ pub mod rustc {
             self != &Self::Primitive
         }
     }
+
+    pub(crate) fn layout_of<'tcx>(
+        cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
+        ty: Ty<'tcx>,
+    ) -> Result<Layout<'tcx>, &'tcx LayoutError<'tcx>> {
+        use rustc_middle::ty::layout::LayoutOf;
+        let ty = cx.tcx.erase_regions(ty);
+        cx.layout_of(ty).map(|tl| tl.layout)
+    }
 }
diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs
index 5c25f913ffe..7c73f74e629 100644
--- a/compiler/rustc_transmute/src/layout/tree.rs
+++ b/compiler/rustc_transmute/src/layout/tree.rs
@@ -171,10 +171,12 @@ where
 
 #[cfg(feature = "rustc")]
 pub(crate) mod rustc {
-    use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, LayoutError, LayoutOf};
+    use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, LayoutError};
     use rustc_middle::ty::{self, AdtDef, AdtKind, List, ScalarInt, Ty, TyCtxt, TypeVisitableExt};
     use rustc_span::ErrorGuaranteed;
-    use rustc_target::abi::{FieldsShape, Size, TyAndLayout, Variants};
+    use rustc_target::abi::{
+        FieldIdx, FieldsShape, Layout, Size, TyAndLayout, VariantIdx, Variants,
+    };
 
     use super::Tree;
     use crate::layout::rustc::{Def, Ref};
@@ -202,20 +204,18 @@ pub(crate) mod rustc {
     }
 
     impl<'tcx> Tree<Def<'tcx>, Ref<'tcx>> {
-        pub fn from_ty(
-            ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
-            cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
-        ) -> Result<Self, Err> {
+        pub fn from_ty(ty: Ty<'tcx>, cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result<Self, Err> {
             use rustc_target::abi::HasDataLayout;
+            let layout = ty_layout(cx, ty);
 
-            if let Err(e) = ty_and_layout.ty.error_reported() {
+            if let Err(e) = ty.error_reported() {
                 return Err(Err::TypeError(e));
             }
 
             let target = cx.tcx.data_layout();
             let pointer_size = target.pointer_size;
 
-            match ty_and_layout.ty.kind() {
+            match ty.kind() {
                 ty::Bool => Ok(Self::bool()),
 
                 ty::Float(nty) => {
@@ -233,32 +233,30 @@ pub(crate) mod rustc {
                     Ok(Self::number(width as _))
                 }
 
-                ty::Tuple(members) => Self::from_tuple(ty_and_layout, members, cx),
+                ty::Tuple(members) => Self::from_tuple((ty, layout), members, cx),
 
                 ty::Array(inner_ty, len) => {
-                    let FieldsShape::Array { stride, count } = &ty_and_layout.fields else {
+                    let FieldsShape::Array { stride, count } = &layout.fields else {
                         return Err(Err::NotYetSupported);
                     };
-                    let inner_ty_and_layout = cx.layout_of(*inner_ty)?;
-                    assert_eq!(*stride, inner_ty_and_layout.size);
-                    let elt = Tree::from_ty(inner_ty_and_layout, cx)?;
+                    let inner_layout = ty_layout(cx, *inner_ty);
+                    assert_eq!(*stride, inner_layout.size);
+                    let elt = Tree::from_ty(*inner_ty, cx)?;
                     Ok(std::iter::repeat(elt)
                         .take(*count as usize)
                         .fold(Tree::unit(), |tree, elt| tree.then(elt)))
                 }
 
-                ty::Adt(adt_def, _args_ref) if !ty_and_layout.ty.is_box() => {
-                    match adt_def.adt_kind() {
-                        AdtKind::Struct => Self::from_struct(ty_and_layout, *adt_def, cx),
-                        AdtKind::Enum => Self::from_enum(ty_and_layout, *adt_def, cx),
-                        AdtKind::Union => Self::from_union(ty_and_layout, *adt_def, cx),
-                    }
-                }
+                ty::Adt(adt_def, _args_ref) if !ty.is_box() => match adt_def.adt_kind() {
+                    AdtKind::Struct => Self::from_struct((ty, layout), *adt_def, cx),
+                    AdtKind::Enum => Self::from_enum((ty, layout), *adt_def, cx),
+                    AdtKind::Union => Self::from_union((ty, layout), *adt_def, cx),
+                },
 
                 ty::Ref(lifetime, ty, mutability) => {
-                    let ty_and_layout = cx.layout_of(*ty)?;
-                    let align = ty_and_layout.align.abi.bytes_usize();
-                    let size = ty_and_layout.size.bytes_usize();
+                    let layout = ty_layout(cx, *ty);
+                    let align = layout.align.abi.bytes_usize();
+                    let size = layout.size.bytes_usize();
                     Ok(Tree::Ref(Ref {
                         lifetime: *lifetime,
                         ty: *ty,
@@ -274,21 +272,20 @@ pub(crate) mod rustc {
 
         /// Constructs a `Tree` from a tuple.
         fn from_tuple(
-            ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
+            (ty, layout): (Ty<'tcx>, Layout<'tcx>),
             members: &'tcx List<Ty<'tcx>>,
             cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
         ) -> Result<Self, Err> {
-            match &ty_and_layout.fields {
+            match &layout.fields {
                 FieldsShape::Primitive => {
                     assert_eq!(members.len(), 1);
                     let inner_ty = members[0];
-                    let inner_ty_and_layout = cx.layout_of(inner_ty)?;
-                    assert_eq!(ty_and_layout.layout, inner_ty_and_layout.layout);
-                    Self::from_ty(inner_ty_and_layout, cx)
+                    let inner_layout = ty_layout(cx, inner_ty);
+                    Self::from_ty(inner_ty, cx)
                 }
                 FieldsShape::Arbitrary { offsets, .. } => {
                     assert_eq!(offsets.len(), members.len());
-                    Self::from_variant(Def::Primitive, None, ty_and_layout, ty_and_layout.size, cx)
+                    Self::from_variant(Def::Primitive, None, (ty, layout), layout.size, cx)
                 }
                 FieldsShape::Array { .. } | FieldsShape::Union(_) => Err(Err::NotYetSupported),
             }
@@ -300,13 +297,13 @@ pub(crate) mod rustc {
         ///
         /// Panics if `def` is not a struct definition.
         fn from_struct(
-            ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
+            (ty, layout): (Ty<'tcx>, Layout<'tcx>),
             def: AdtDef<'tcx>,
             cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
         ) -> Result<Self, Err> {
             assert!(def.is_struct());
             let def = Def::Adt(def);
-            Self::from_variant(def, None, ty_and_layout, ty_and_layout.size, cx)
+            Self::from_variant(def, None, (ty, layout), layout.size, cx)
         }
 
         /// Constructs a `Tree` from an enum.
@@ -315,19 +312,18 @@ pub(crate) mod rustc {
         ///
         /// Panics if `def` is not an enum definition.
         fn from_enum(
-            ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
+            (ty, layout): (Ty<'tcx>, Layout<'tcx>),
             def: AdtDef<'tcx>,
             cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
         ) -> Result<Self, Err> {
             assert!(def.is_enum());
-            let layout = ty_and_layout.layout;
 
             // Computes the variant of a given index.
             let layout_of_variant = |index| {
-                let tag = cx.tcx.tag_for_variant((ty_and_layout.ty, index));
+                let tag = cx.tcx.tag_for_variant((cx.tcx.erase_regions(ty), index));
                 let variant_def = Def::Variant(def.variant(index));
-                let variant_ty_and_layout = ty_and_layout.for_variant(&cx, index);
-                Self::from_variant(variant_def, tag, variant_ty_and_layout, layout.size, cx)
+                let variant_layout = ty_variant(cx, (ty, layout), index);
+                Self::from_variant(variant_def, tag, (ty, variant_layout), layout.size, cx)
             };
 
             // We consider three kinds of enums, each demanding a different
@@ -385,21 +381,20 @@ pub(crate) mod rustc {
         fn from_variant(
             def: Def<'tcx>,
             tag: Option<ScalarInt>,
-            ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
+            (ty, layout): (Ty<'tcx>, Layout<'tcx>),
             total_size: Size,
             cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
         ) -> Result<Self, Err> {
             // This constructor does not support non-`FieldsShape::Arbitrary`
             // layouts.
-            let FieldsShape::Arbitrary { offsets, memory_index } = ty_and_layout.layout.fields()
-            else {
+            let FieldsShape::Arbitrary { offsets, memory_index } = layout.fields() else {
                 return Err(Err::NotYetSupported);
             };
 
             // When this function is invoked with enum variants,
             // `ty_and_layout.size` does not encompass the entire size of the
             // enum. We rely on `total_size` for this.
-            assert!(ty_and_layout.size <= total_size);
+            assert!(layout.size <= total_size);
 
             let mut size = Size::ZERO;
             let mut struct_tree = Self::def(def);
@@ -412,17 +407,18 @@ pub(crate) mod rustc {
 
             // Append the fields, in memory order, to the layout.
             let inverse_memory_index = memory_index.invert_bijective_mapping();
-            for (memory_idx, field_idx) in inverse_memory_index.iter_enumerated() {
+            for (memory_idx, &field_idx) in inverse_memory_index.iter_enumerated() {
                 // Add interfield padding.
-                let padding_needed = offsets[*field_idx] - size;
+                let padding_needed = offsets[field_idx] - size;
                 let padding = Self::padding(padding_needed.bytes_usize());
 
-                let field_ty_and_layout = ty_and_layout.field(&cx, field_idx.as_usize());
-                let field_tree = Self::from_ty(field_ty_and_layout, cx)?;
+                let field_ty = ty_field(cx, (ty, layout), field_idx);
+                let field_layout = ty_layout(cx, field_ty);
+                let field_tree = Self::from_ty(field_ty, cx)?;
 
                 struct_tree = struct_tree.then(padding).then(field_tree);
 
-                size += padding_needed + field_ty_and_layout.size;
+                size += padding_needed + field_layout.size;
             }
 
             // Add trailing padding.
@@ -457,28 +453,27 @@ pub(crate) mod rustc {
         ///
         /// Panics if `def` is not a union definition.
         fn from_union(
-            ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
+            (ty, layout): (Ty<'tcx>, Layout<'tcx>),
             def: AdtDef<'tcx>,
             cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
         ) -> Result<Self, Err> {
             assert!(def.is_union());
 
-            let union_layout = ty_and_layout.layout;
-
             // This constructor does not support non-`FieldsShape::Union`
             // layouts. Fields of this shape are all placed at offset 0.
-            let FieldsShape::Union(fields) = union_layout.fields() else {
+            let FieldsShape::Union(fields) = layout.fields() else {
                 return Err(Err::NotYetSupported);
             };
 
             let fields = &def.non_enum_variant().fields;
             let fields = fields.iter_enumerated().try_fold(
                 Self::uninhabited(),
-                |fields, (idx, ref field_def)| {
+                |fields, (idx, field_def)| {
                     let field_def = Def::Field(field_def);
-                    let field_ty_and_layout = ty_and_layout.field(&cx, idx.as_usize());
-                    let field = Self::from_ty(field_ty_and_layout, cx)?;
-                    let trailing_padding_needed = union_layout.size - field_ty_and_layout.size;
+                    let field_ty = ty_field(cx, (ty, layout), idx);
+                    let field_layout = ty_layout(cx, field_ty);
+                    let field = Self::from_ty(field_ty, cx)?;
+                    let trailing_padding_needed = layout.size - field_layout.size;
                     let trailing_padding = Self::padding(trailing_padding_needed.bytes_usize());
                     let field_and_padding = field.then(trailing_padding);
                     Result::<Self, Err>::Ok(fields.or(field_and_padding))
@@ -488,4 +483,44 @@ pub(crate) mod rustc {
             Ok(Self::def(Def::Adt(def)).then(fields))
         }
     }
+
+    pub(crate) fn ty_layout<'tcx>(cx: LayoutCx<'tcx, TyCtxt<'tcx>>, ty: Ty<'tcx>) -> Layout<'tcx> {
+        crate::layout::rustc::layout_of(cx, ty).unwrap()
+    }
+
+    fn ty_field<'tcx>(
+        cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
+        (ty, layout): (Ty<'tcx>, Layout<'tcx>),
+        i: FieldIdx,
+    ) -> Ty<'tcx> {
+        match ty.kind() {
+            ty::Adt(def, args) => {
+                match layout.variants {
+                    Variants::Single { index } => {
+                        let field = &def.variant(index).fields[i];
+                        field.ty(cx.tcx, args)
+                    }
+                    // Discriminant field for enums (where applicable).
+                    Variants::Multiple { tag, .. } => {
+                        assert_eq!(i.as_usize(), 0);
+                        ty::layout::PrimitiveExt::to_ty(&tag.primitive(), cx.tcx)
+                    }
+                }
+            }
+            ty::Tuple(fields) => fields[i.as_usize()],
+            kind @ _ => unimplemented!(
+                "only a subset of `Ty::ty_and_layout_field`'s functionality is implemented. implementation needed for {:?}",
+                kind
+            ),
+        }
+    }
+
+    fn ty_variant<'tcx>(
+        cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
+        (ty, layout): (Ty<'tcx>, Layout<'tcx>),
+        i: VariantIdx,
+    ) -> Layout<'tcx> {
+        let ty = cx.tcx.erase_regions(ty);
+        TyAndLayout { ty, layout }.for_variant(&cx, i).layout
+    }
 }
diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs
index 31664ee6c4f..bdc98bcea5e 100644
--- a/compiler/rustc_transmute/src/lib.rs
+++ b/compiler/rustc_transmute/src/lib.rs
@@ -9,7 +9,7 @@ pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set}
 pub mod layout;
 mod maybe_transmutable;
 
-#[derive(Default)]
+#[derive(Copy, Clone, Debug, Default)]
 pub struct Assume {
     pub alignment: bool,
     pub lifetimes: bool,
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
index 7c66a827db9..1f3c4e3c817 100644
--- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
+++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
@@ -30,7 +30,7 @@ where
 // FIXME: Nix this cfg, so we can write unit tests independently of rustc
 #[cfg(feature = "rustc")]
 mod rustc {
-    use rustc_middle::ty::layout::{LayoutCx, LayoutOf};
+    use rustc_middle::ty::layout::LayoutCx;
     use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
 
     use super::*;
@@ -45,10 +45,9 @@ mod rustc {
 
             let layout_cx = LayoutCx { tcx: context, param_env: ParamEnv::reveal_all() };
             let layout_of = |ty| {
-                layout_cx
-                    .layout_of(ty)
+                crate::layout::rustc::layout_of(layout_cx, ty)
                     .map_err(|_| Err::NotYetSupported)
-                    .and_then(|tl| Tree::from_ty(tl, layout_cx))
+                    .and_then(|_| Tree::from_ty(ty, layout_cx))
             };
 
             // Convert `src` and `dst` from their rustc representations, to `Tree`-based
diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs
index 378d069672f..219c5a6ec91 100644
--- a/src/bootstrap/src/core/config/tests.rs
+++ b/src/bootstrap/src/core/config/tests.rs
@@ -96,8 +96,8 @@ fn detect_src_and_out() {
     test(parse(""), None);
 
     {
-        let build_dir = if cfg!(windows) { Some("C:\\tmp") } else { Some("/tmp") };
-        test(parse("build.build-dir = \"/tmp\""), build_dir);
+        let build_dir = if cfg!(windows) { "C:\\tmp" } else { "/tmp" };
+        test(parse(&format!("build.build-dir = '{build_dir}'")), Some(build_dir));
     }
 }
 
diff --git a/tests/ui/async-await/async-fn/recurse-ice-129215.rs b/tests/ui/async-await/async-fn/recurse-ice-129215.rs
new file mode 100644
index 00000000000..06a2d7be9ef
--- /dev/null
+++ b/tests/ui/async-await/async-fn/recurse-ice-129215.rs
@@ -0,0 +1,9 @@
+//@ edition: 2021
+
+async fn a() {
+    //~^ ERROR `()` is not a future
+    //~| ERROR mismatched types
+    a() //~ ERROR `()` is not a future
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/async-fn/recurse-ice-129215.stderr b/tests/ui/async-await/async-fn/recurse-ice-129215.stderr
new file mode 100644
index 00000000000..98c7be2a5a3
--- /dev/null
+++ b/tests/ui/async-await/async-fn/recurse-ice-129215.stderr
@@ -0,0 +1,34 @@
+error[E0277]: `()` is not a future
+  --> $DIR/recurse-ice-129215.rs:6:5
+   |
+LL |     a()
+   |     ^^^ `()` is not a future
+   |
+   = help: the trait `Future` is not implemented for `()`
+
+error[E0277]: `()` is not a future
+  --> $DIR/recurse-ice-129215.rs:3:1
+   |
+LL | async fn a() {
+   | ^^^^^^^^^^^^ `()` is not a future
+   |
+   = help: the trait `Future` is not implemented for `()`
+
+error[E0308]: mismatched types
+  --> $DIR/recurse-ice-129215.rs:3:14
+   |
+LL |   async fn a() {
+   |  ______________^
+LL | |
+LL | |
+LL | |     a()
+LL | | }
+   | |_^ expected `()`, found `async` fn body
+   |
+   = note:    expected unit type `()`
+           found `async` fn body `{async fn body of a()}`
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0277, E0308.
+For more information about an error, try `rustc --explain E0277`.
diff --git a/tests/ui/attributes/may_dangle.rs b/tests/ui/attributes/may_dangle.rs
new file mode 100644
index 00000000000..209ba0e88ad
--- /dev/null
+++ b/tests/ui/attributes/may_dangle.rs
@@ -0,0 +1,53 @@
+#![feature(dropck_eyepatch)]
+
+struct Implee1<'a, T, const N: usize>(&'a T);
+struct Implee2<'a, T, const N: usize>(&'a T);
+struct Implee3<'a, T, const N: usize>(&'a T);
+trait NotDrop {}
+
+unsafe impl<#[may_dangle] 'a, T, const N: usize> NotDrop for Implee1<'a, T, N> {}
+//~^ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl
+
+unsafe impl<'a, #[may_dangle] T, const N: usize> NotDrop for Implee2<'a, T, N> {}
+//~^ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl
+
+unsafe impl<'a, T, #[may_dangle] const N: usize> Drop for Implee1<'a, T, N> {
+    //~^ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl
+    fn drop(&mut self) {}
+}
+
+// Ok, lifetime param in a `Drop` impl.
+unsafe impl<#[may_dangle] 'a, T, const N: usize> Drop for Implee2<'a, T, N> {
+    fn drop(&mut self) {}
+}
+
+// Ok, type param in a `Drop` impl.
+unsafe impl<'a, #[may_dangle] T, const N: usize> Drop for Implee3<'a, T, N> {
+    fn drop(&mut self) {}
+}
+
+// Check that this check is not textual.
+mod fake {
+    trait Drop {
+        fn drop(&mut self);
+    }
+    struct Implee<T>(T);
+
+    unsafe impl<#[may_dangle] T> Drop for Implee<T> {
+        //~^ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl
+        fn drop(&mut self) {}
+    }
+}
+
+#[may_dangle] //~ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl
+struct Dangling;
+
+#[may_dangle] //~ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl
+impl NotDrop for () {
+}
+
+#[may_dangle] //~ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl
+fn main() {
+    #[may_dangle] //~ ERROR must be applied to a lifetime or type generic parameter in `Drop` impl
+    let () = ();
+}
diff --git a/tests/ui/attributes/may_dangle.stderr b/tests/ui/attributes/may_dangle.stderr
new file mode 100644
index 00000000000..dc24f847f71
--- /dev/null
+++ b/tests/ui/attributes/may_dangle.stderr
@@ -0,0 +1,50 @@
+error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
+  --> $DIR/may_dangle.rs:8:13
+   |
+LL | unsafe impl<#[may_dangle] 'a, T, const N: usize> NotDrop for Implee1<'a, T, N> {}
+   |             ^^^^^^^^^^^^^
+
+error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
+  --> $DIR/may_dangle.rs:11:17
+   |
+LL | unsafe impl<'a, #[may_dangle] T, const N: usize> NotDrop for Implee2<'a, T, N> {}
+   |                 ^^^^^^^^^^^^^
+
+error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
+  --> $DIR/may_dangle.rs:14:20
+   |
+LL | unsafe impl<'a, T, #[may_dangle] const N: usize> Drop for Implee1<'a, T, N> {
+   |                    ^^^^^^^^^^^^^
+
+error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
+  --> $DIR/may_dangle.rs:42:1
+   |
+LL | #[may_dangle]
+   | ^^^^^^^^^^^^^
+
+error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
+  --> $DIR/may_dangle.rs:45:1
+   |
+LL | #[may_dangle]
+   | ^^^^^^^^^^^^^
+
+error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
+  --> $DIR/may_dangle.rs:49:1
+   |
+LL | #[may_dangle]
+   | ^^^^^^^^^^^^^
+
+error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
+  --> $DIR/may_dangle.rs:51:5
+   |
+LL |     #[may_dangle]
+   |     ^^^^^^^^^^^^^
+
+error: `#[may_dangle]` must be applied to a lifetime or type generic parameter in `Drop` impl
+  --> $DIR/may_dangle.rs:36:17
+   |
+LL |     unsafe impl<#[may_dangle] T> Drop for Implee<T> {
+   |                 ^^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
+
diff --git a/tests/ui/closures/add_semicolon_non_block_closure.rs b/tests/ui/closures/add_semicolon_non_block_closure.rs
index 3ae91be60c5..62c5e343cd3 100644
--- a/tests/ui/closures/add_semicolon_non_block_closure.rs
+++ b/tests/ui/closures/add_semicolon_non_block_closure.rs
@@ -8,4 +8,5 @@ fn main() {
     foo(|| bar())
     //~^ ERROR mismatched types [E0308]
     //~| HELP consider using a semicolon here
+    //~| HELP try adding a return type
 }
diff --git a/tests/ui/closures/add_semicolon_non_block_closure.stderr b/tests/ui/closures/add_semicolon_non_block_closure.stderr
index d095e59c7eb..7883db8f98e 100644
--- a/tests/ui/closures/add_semicolon_non_block_closure.stderr
+++ b/tests/ui/closures/add_semicolon_non_block_closure.stderr
@@ -1,8 +1,6 @@
 error[E0308]: mismatched types
   --> $DIR/add_semicolon_non_block_closure.rs:8:12
    |
-LL | fn main() {
-   |          - expected `()` because of default return type
 LL |     foo(|| bar())
    |            ^^^^^ expected `()`, found `i32`
    |
@@ -10,6 +8,10 @@ help: consider using a semicolon here
    |
 LL |     foo(|| { bar(); })
    |            +      +++
+help: try adding a return type
+   |
+LL |     foo(|| -> i32 bar())
+   |            ++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/lint/must_not_suspend/other_items.stderr b/tests/ui/lint/must_not_suspend/other_items.stderr
index e6c36b78951..dff5210b7e4 100644
--- a/tests/ui/lint/must_not_suspend/other_items.stderr
+++ b/tests/ui/lint/must_not_suspend/other_items.stderr
@@ -1,10 +1,10 @@
-error: `must_not_suspend` attribute should be applied to a struct, enum, or trait
+error: `must_not_suspend` attribute should be applied to a struct, enum, union, or trait
   --> $DIR/other_items.rs:5:1
    |
 LL | #[must_not_suspend]
    | ^^^^^^^^^^^^^^^^^^^
 LL | mod inner {}
-   | ------------ is not a struct, enum, or trait
+   | ------------ is not a struct, enum, union, or trait
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/lint/must_not_suspend/return.stderr b/tests/ui/lint/must_not_suspend/return.stderr
index 5a73064c787..440f8165686 100644
--- a/tests/ui/lint/must_not_suspend/return.stderr
+++ b/tests/ui/lint/must_not_suspend/return.stderr
@@ -1,4 +1,4 @@
-error: `must_not_suspend` attribute should be applied to a struct, enum, or trait
+error: `must_not_suspend` attribute should be applied to a struct, enum, union, or trait
   --> $DIR/return.rs:5:1
    |
 LL |   #[must_not_suspend]
@@ -6,7 +6,7 @@ LL |   #[must_not_suspend]
 LL | / fn foo() -> i32 {
 LL | |     0
 LL | | }
-   | |_- is not a struct, enum, or trait
+   | |_- is not a struct, enum, union, or trait
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/transmutability/references/accept_assume_lifetime_extension.rs b/tests/ui/transmutability/references/accept_assume_lifetime_extension.rs
new file mode 100644
index 00000000000..3bdd7256791
--- /dev/null
+++ b/tests/ui/transmutability/references/accept_assume_lifetime_extension.rs
@@ -0,0 +1,91 @@
+//@ check-pass
+
+//! Accept lifetime extensions with `Assume::LIFETIMES`.
+
+#![feature(transmutability, core_intrinsics)]
+
+use std::mem::{Assume, BikeshedIntrinsicFrom};
+
+unsafe fn transmute<Src, Dst>(src: Src) -> Dst
+where
+    Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY.and(Assume::LIFETIMES) }>,
+{
+    core::intrinsics::transmute_unchecked(src)
+}
+
+mod bare {
+    use super::*;
+
+    fn extend_bare<'a>(src: &'a u8) -> &'static u8 {
+        unsafe { transmute(src) }
+    }
+}
+
+mod nested {
+    use super::*;
+
+    fn extend_nested<'a>(src: &'a &'a u8) -> &'a &'static u8 {
+        unsafe { transmute(src) }
+    }
+}
+
+mod tuple {
+    use super::*;
+
+    fn extend_unit<'a>(src: (&'a u8,)) -> (&'static u8,) {
+        unsafe { transmute(src) }
+    }
+
+    fn extend_pair<'a>(src: (&'a u8, u8)) -> (&'static u8, u8) {
+        unsafe { transmute(src) }
+    }
+}
+
+mod r#struct {
+    use super::*;
+
+    struct Struct<'a>(&'a u8);
+
+    fn extend_struct<'a>(src: Struct<'a>) -> Struct<'static> {
+        unsafe { transmute(src) }
+    }
+}
+
+mod r#enum {
+    use super::*;
+
+    enum Single<'a> {
+        A(&'a u8),
+    }
+
+    fn extend_single<'a>(src: Single<'a>) -> Single<'static> {
+        unsafe { transmute(src) }
+    }
+
+    enum Multi<'a> {
+        A(&'a u8),
+        B,
+        C,
+    }
+
+    fn extend_multi<'a>(src: Multi<'a>) -> Multi<'static> {
+        unsafe { transmute(src) }
+    }
+}
+
+mod hrtb {
+    use super::*;
+
+    fn call_extend_hrtb<'a>(src: &'a u8) -> &'static u8 {
+        unsafe { extend_hrtb(src) }
+    }
+
+    unsafe fn extend_hrtb<'a>(src: &'a u8) -> &'static u8
+    where
+        for<'b> &'b u8: BikeshedIntrinsicFrom<&'a u8, { Assume::LIFETIMES }>,
+    {
+        core::intrinsics::transmute_unchecked(src)
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/transmutability/references/accept_unexercised_lifetime_extension.rs b/tests/ui/transmutability/references/accept_unexercised_lifetime_extension.rs
new file mode 100644
index 00000000000..559ee23a446
--- /dev/null
+++ b/tests/ui/transmutability/references/accept_unexercised_lifetime_extension.rs
@@ -0,0 +1,68 @@
+//@ check-pass
+
+//! Accept lifetime extensions of un-exercised lifetimes.
+
+#![feature(transmutability, core_intrinsics)]
+
+use std::mem::{Assume, BikeshedIntrinsicFrom};
+
+unsafe fn transmute<Src, Dst>(src: Src) -> Dst
+where
+    Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>,
+{
+    core::intrinsics::transmute_unchecked(src)
+}
+
+enum Void {}
+
+mod phantom {
+    use super::*;
+    use std::marker::PhantomData;
+
+    fn extend_bare<'a>(src: PhantomData<&'a u8>) -> PhantomData<&'static u8> {
+        unsafe { transmute(src) }
+    }
+}
+
+
+mod tuple {
+    use super::*;
+
+    fn extend_pair<'a>(src: (&'a u8, Void)) -> (&'static u8, Void) {
+        unsafe { transmute(src) }
+    }
+}
+
+mod r#struct {
+    use super::*;
+
+    struct Struct<'a>(&'a u8, Void);
+
+    fn extend_struct<'a>(src: Struct<'a>) -> Struct<'static> {
+        unsafe { transmute(src) }
+    }
+}
+
+mod r#enum {
+    use super::*;
+
+    enum Single<'a> {
+        A(&'a u8, Void),
+    }
+
+    fn extend_single<'a>(src: Single<'a>) -> Single<'static> {
+        unsafe { transmute(src) }
+    }
+
+    enum Multi<'a> {
+        A(&'a u8, Void),
+        B,
+        C,
+    }
+
+    fn extend_multi<'a>(src: Multi<'a>) -> Multi<'static> {
+        unsafe { transmute(src) }
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/transmutability/references/reject_lifetime_extension.rs b/tests/ui/transmutability/references/reject_lifetime_extension.rs
new file mode 100644
index 00000000000..79bb4e1e556
--- /dev/null
+++ b/tests/ui/transmutability/references/reject_lifetime_extension.rs
@@ -0,0 +1,91 @@
+//@ check-fail
+
+//! Reject lifetime extensions.
+
+#![feature(transmutability, core_intrinsics)]
+
+use std::mem::{Assume, BikeshedIntrinsicFrom};
+
+unsafe fn transmute<Src, Dst>(src: Src) -> Dst
+where
+    Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>,
+{
+    core::intrinsics::transmute_unchecked(src)
+}
+
+mod bare {
+    use super::*;
+
+    fn extend_bare<'a>(src: &'a u8) -> &'static u8 {
+        unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
+    }
+}
+
+mod nested {
+    use super::*;
+
+    fn extend_nested<'a>(src: &'a &'a u8) -> &'a &'static u8 {
+        unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
+    }
+}
+
+mod tuple {
+    use super::*;
+
+    fn extend_unit<'a>(src: (&'a u8,)) -> (&'static u8,) {
+        unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
+    }
+
+    fn extend_pair<'a>(src: (&'a u8, u8)) -> (&'static u8, u8) {
+        unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
+    }
+}
+
+mod r#struct {
+    use super::*;
+
+    struct Struct<'a>(&'a u8);
+
+    fn extend_struct<'a>(src: Struct<'a>) -> Struct<'static> {
+        unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
+    }
+}
+
+mod r#enum {
+    use super::*;
+
+    enum Single<'a> {
+        A(&'a u8),
+    }
+
+    fn extend_single<'a>(src: Single<'a>) -> Single<'static> {
+        unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
+    }
+
+    enum Multi<'a> {
+        A(&'a u8),
+        B,
+        C,
+    }
+
+    fn extend_multi<'a>(src: Multi<'a>) -> Multi<'static> {
+        unsafe { transmute(src) } //~ ERROR lifetime may not live long enough
+    }
+}
+
+mod hrtb {
+    use super::*;
+
+    fn call_extend_hrtb<'a>(src: &'a u8) -> &'static u8 {
+        unsafe { extend_hrtb(src) } //~ ERROR borrowed data escapes outside of function
+    }
+
+    unsafe fn extend_hrtb<'a>(src: &'a u8) -> &'static u8
+    where
+        for<'b> &'b u8: BikeshedIntrinsicFrom<&'a u8>,
+    {
+        core::intrinsics::transmute_unchecked(src)
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/transmutability/references/reject_lifetime_extension.stderr b/tests/ui/transmutability/references/reject_lifetime_extension.stderr
new file mode 100644
index 00000000000..df1b81f26d2
--- /dev/null
+++ b/tests/ui/transmutability/references/reject_lifetime_extension.stderr
@@ -0,0 +1,78 @@
+error: lifetime may not live long enough
+  --> $DIR/reject_lifetime_extension.rs:20:18
+   |
+LL |     fn extend_bare<'a>(src: &'a u8) -> &'static u8 {
+   |                    -- lifetime `'a` defined here
+LL |         unsafe { transmute(src) }
+   |                  ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/reject_lifetime_extension.rs:28:18
+   |
+LL |     fn extend_nested<'a>(src: &'a &'a u8) -> &'a &'static u8 {
+   |                      -- lifetime `'a` defined here
+LL |         unsafe { transmute(src) }
+   |                  ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/reject_lifetime_extension.rs:36:18
+   |
+LL |     fn extend_unit<'a>(src: (&'a u8,)) -> (&'static u8,) {
+   |                    -- lifetime `'a` defined here
+LL |         unsafe { transmute(src) }
+   |                  ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/reject_lifetime_extension.rs:40:18
+   |
+LL |     fn extend_pair<'a>(src: (&'a u8, u8)) -> (&'static u8, u8) {
+   |                    -- lifetime `'a` defined here
+LL |         unsafe { transmute(src) }
+   |                  ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/reject_lifetime_extension.rs:50:18
+   |
+LL |     fn extend_struct<'a>(src: Struct<'a>) -> Struct<'static> {
+   |                      -- lifetime `'a` defined here
+LL |         unsafe { transmute(src) }
+   |                  ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/reject_lifetime_extension.rs:62:18
+   |
+LL |     fn extend_single<'a>(src: Single<'a>) -> Single<'static> {
+   |                      -- lifetime `'a` defined here
+LL |         unsafe { transmute(src) }
+   |                  ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/reject_lifetime_extension.rs:72:18
+   |
+LL |     fn extend_multi<'a>(src: Multi<'a>) -> Multi<'static> {
+   |                     -- lifetime `'a` defined here
+LL |         unsafe { transmute(src) }
+   |                  ^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
+
+error[E0521]: borrowed data escapes outside of function
+  --> $DIR/reject_lifetime_extension.rs:80:18
+   |
+LL |     fn call_extend_hrtb<'a>(src: &'a u8) -> &'static u8 {
+   |                         --  --- `src` is a reference that is only valid in the function body
+   |                         |
+   |                         lifetime `'a` defined here
+LL |         unsafe { extend_hrtb(src) }
+   |                  ^^^^^^^^^^^^^^^^
+   |                  |
+   |                  `src` escapes the function body here
+   |                  argument requires that `'a` must outlive `'static`
+   |
+note: due to current limitations in the borrow checker, this implies a `'static` lifetime
+  --> $DIR/reject_lifetime_extension.rs:85:25
+   |
+LL |         for<'b> &'b u8: BikeshedIntrinsicFrom<&'a u8>,
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0521`.