about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs20
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs10
-rw-r--r--compiler/rustc_const_eval/src/interpret/cast.rs37
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs9
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs13
-rw-r--r--compiler/rustc_const_eval/src/transform/promote_consts.rs16
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs11
-rw-r--r--compiler/rustc_data_structures/src/stable_hasher.rs4
-rw-r--r--compiler/rustc_error_messages/src/lib.rs72
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs86
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs47
-rw-r--r--compiler/rustc_errors/src/lib.rs3
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs36
-rw-r--r--compiler/rustc_macros/src/diagnostics/fluent.rs20
-rw-r--r--compiler/rustc_macros/src/diagnostics/subdiagnostic.rs2
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs18
-rw-r--r--compiler/rustc_middle/src/mir/tcx.rs17
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_rvalue.rs15
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/liveness.rs23
-rw-r--r--compiler/rustc_mir_transform/src/dead_store_elimination.rs4
-rw-r--r--compiler/rustc_mir_transform/src/deref_separator.rs110
-rw-r--r--compiler/rustc_mir_transform/src/generator.rs4
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs3
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs11
-rw-r--r--compiler/rustc_parse/src/parser/item.rs41
-rw-r--r--compiler/rustc_typeck/src/check/check.rs13
-rw-r--r--compiler/rustc_typeck/src/check/coercion.rs25
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs42
-rw-r--r--compiler/rustc_typeck/src/errors.rs11
30 files changed, 418 insertions, 307 deletions
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 4a2b2942008..d9d31ab2c89 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -2147,6 +2147,18 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         }
                     }
 
+                    CastKind::PointerAddress => {
+                        let ty_from = op.ty(body, tcx);
+                        let cast_ty_from = CastTy::from_ty(ty_from);
+                        let cast_ty_to = CastTy::from_ty(*ty);
+                        match (cast_ty_from, cast_ty_to) {
+                            (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => (),
+                            _ => {
+                                span_mirbug!(self, rvalue, "Invalid cast {:?} -> {:?}", ty_from, ty)
+                            }
+                        }
+                    }
+
                     CastKind::Misc => {
                         let ty_from = op.ty(body, tcx);
                         let cast_ty_from = CastTy::from_ty(ty_from);
@@ -2155,7 +2167,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                             (None, _)
                             | (_, None | Some(CastTy::FnPtr))
                             | (Some(CastTy::Float), Some(CastTy::Ptr(_)))
-                            | (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Float)) => {
+                            | (
+                                Some(CastTy::Ptr(_) | CastTy::FnPtr),
+                                Some(CastTy::Float | CastTy::Int(_)),
+                            ) => {
                                 span_mirbug!(self, rvalue, "Invalid cast {:?} -> {:?}", ty_from, ty,)
                             }
                             (
@@ -2163,8 +2178,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                                 Some(CastTy::Int(_) | CastTy::Float | CastTy::Ptr(_)),
                             )
                             | (Some(CastTy::Float), Some(CastTy::Int(_) | CastTy::Float))
-                            | (Some(CastTy::Ptr(_)), Some(CastTy::Int(_) | CastTy::Ptr(_)))
-                            | (Some(CastTy::FnPtr), Some(CastTy::Int(_) | CastTy::Ptr(_))) => (),
+                            | (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Ptr(_))) => (),
                         }
                     }
                 }
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 3fe112d794b..7c59ce354c0 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -607,7 +607,7 @@ fn codegen_stmt<'tcx>(
                     let operand = codegen_operand(fx, operand);
                     lval.write_cvalue(fx, operand.cast_pointer_to(to_layout));
                 }
-                Rvalue::Cast(CastKind::Misc, ref operand, to_ty) => {
+                Rvalue::Cast(CastKind::Misc | CastKind::PointerAddress, ref operand, to_ty) => {
                     let operand = codegen_operand(fx, operand);
                     let from_ty = operand.layout().ty;
                     let to_ty = fx.monomorphize(to_ty);
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index fd29c9e281b..bd88aa33372 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -181,6 +181,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 let cast = bx.cx().layout_of(self.monomorphize(mir_cast_ty));
 
                 let val = match *kind {
+                    mir::CastKind::PointerAddress => {
+                        assert!(bx.cx().is_backend_immediate(cast));
+                        let llptr = operand.immediate();
+                        let llcast_ty = bx.cx().immediate_backend_type(cast);
+                        let lladdr = bx.ptrtoint(llptr, llcast_ty);
+                        OperandValue::Immediate(lladdr)
+                    }
                     mir::CastKind::Pointer(PointerCast::ReifyFnPointer) => {
                         match *operand.layout.ty.kind() {
                             ty::FnDef(def_id, substs) => {
@@ -362,9 +369,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                             (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Ptr(_)) => {
                                 bx.pointercast(llval, ll_t_out)
                             }
-                            (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
-                                bx.ptrtoint(llval, ll_t_out)
-                            }
                             (CastTy::Int(_), CastTy::Ptr(_)) => {
                                 let usize_llval = bx.intcast(llval, bx.cx().type_isize(), signed);
                                 bx.inttoptr(usize_llval, ll_t_out)
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index be34a77bdba..af563c1450e 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -1,3 +1,4 @@
+use std::assert_matches::assert_matches;
 use std::convert::TryFrom;
 
 use rustc_apfloat::ieee::{Double, Single};
@@ -30,6 +31,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 self.unsize_into(src, cast_ty, dest)?;
             }
 
+            PointerAddress => {
+                let src = self.read_immediate(src)?;
+                let res = self.pointer_address_cast(&src, cast_ty)?;
+                self.write_immediate(res, dest)?;
+            }
+
             Misc => {
                 let src = self.read_immediate(src)?;
                 let res = self.misc_cast(&src, cast_ty)?;
@@ -174,23 +181,23 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
         // # The remaining source values are scalar and "int-like".
         let scalar = src.to_scalar()?;
+        Ok(self.cast_from_int_like(scalar, src.layout, cast_ty)?.into())
+    }
 
-        // If we are casting from a pointer to something
-        // that is not a pointer, mark the pointer as exposed
-        if src.layout.ty.is_any_ptr() && !cast_ty.is_any_ptr() {
-            let ptr = self.scalar_to_ptr(scalar)?;
-
-            match ptr.into_pointer_or_addr() {
-                Ok(ptr) => {
-                    M::expose_ptr(self, ptr)?;
-                }
-                Err(_) => {
-                    // do nothing, exposing an invalid pointer
-                    // has no meaning
-                }
-            };
-        }
+    pub fn pointer_address_cast(
+        &mut self,
+        src: &ImmTy<'tcx, M::PointerTag>,
+        cast_ty: Ty<'tcx>,
+    ) -> InterpResult<'tcx, Immediate<M::PointerTag>> {
+        assert_matches!(src.layout.ty.kind(), ty::RawPtr(_) | ty::FnPtr(_));
+        assert!(cast_ty.is_integral());
 
+        let scalar = src.to_scalar()?;
+        let ptr = self.scalar_to_ptr(scalar)?;
+        match ptr.into_pointer_or_addr() {
+            Ok(ptr) => M::expose_ptr(self, ptr)?,
+            Err(_) => {} // do nothing, exposing an invalid pointer has no meaning
+        };
         Ok(self.cast_from_int_like(scalar, src.layout, cast_ty)?.into())
     }
 
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index a5c7d4c8e20..10da2f803af 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -185,7 +185,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // No question
                 return true;
             }
-            // Compare layout
+            if caller_abi.layout.size != callee_abi.layout.size
+                || caller_abi.layout.align.abi != callee_abi.layout.align.abi
+            {
+                // This cannot go well...
+                // FIXME: What about unsized types?
+                return false;
+            }
+            // The rest *should* be okay, but we are extra conservative.
             match (caller_abi.layout.abi, callee_abi.layout.abi) {
                 // Different valid ranges are okay (once we enforce validity,
                 // that will take care to make it UB to leave the range, just
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 eea6e2a47a9..4ef33d62a6b 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -8,7 +8,6 @@ use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
 use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
-use rustc_middle::ty::cast::CastTy;
 use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
 use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty, TyCtxt};
 use rustc_middle::ty::{Binder, TraitPredicate, TraitRef, TypeFoldable};
@@ -543,16 +542,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                 // in the type of any local, which also excludes casts).
             }
 
-            Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
-                let operand_ty = operand.ty(self.body, self.tcx);
-                let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
-                let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
-
-                if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) {
-                    self.check_op(ops::RawPtrToIntCast);
-                }
+            Rvalue::Cast(CastKind::PointerAddress, _, _) => {
+                self.check_op(ops::RawPtrToIntCast);
             }
 
+            Rvalue::Cast(CastKind::Misc, _, _) => {}
+
             Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) => {}
             Rvalue::ShallowInitBox(_, _) => {}
 
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index fc6b8a1a723..ea23bd14d25 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -16,7 +16,6 @@ use rustc_hir as hir;
 use rustc_middle::mir::traversal::ReversePostorderIter;
 use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
-use rustc_middle::ty::cast::CastTy;
 use rustc_middle::ty::subst::InternalSubsts;
 use rustc_middle::ty::{self, List, TyCtxt, TypeFoldable};
 use rustc_span::Span;
@@ -502,18 +501,11 @@ impl<'tcx> Validator<'_, 'tcx> {
 
             Rvalue::ThreadLocalRef(_) => return Err(Unpromotable),
 
-            Rvalue::Cast(kind, operand, cast_ty) => {
-                if matches!(kind, CastKind::Misc) {
-                    let operand_ty = operand.ty(self.body, self.tcx);
-                    let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
-                    let cast_out = CastTy::from_ty(*cast_ty).expect("bad output type for cast");
-                    if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) {
-                        // ptr-to-int casts are not possible in consts and thus not promotable
-                        return Err(Unpromotable);
-                    }
-                    // int-to-ptr casts are fine, they just use the integer value at pointer type.
-                }
+            // ptr-to-int casts are not possible in consts and thus not promotable
+            Rvalue::Cast(CastKind::PointerAddress, _, _) => return Err(Unpromotable),
 
+            // int-to-ptr casts are fine, they just use the integer value at pointer type.
+            Rvalue::Cast(_, operand, _) => {
                 self.validate_operand(operand)?;
             }
 
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 54c2daf9ac2..665b07c9f89 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -4,6 +4,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_index::bit_set::BitSet;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::mir::interpret::Scalar;
+use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
 use rustc_middle::mir::visit::{PlaceContext, Visitor};
 use rustc_middle::mir::{
     traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, Local, Location, MirPass,
@@ -302,9 +303,17 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
         self.super_projection_elem(local, proj_base, elem, context, location);
     }
 
-    fn visit_place(&mut self, place: &Place<'tcx>, _: PlaceContext, _: Location) {
+    fn visit_place(&mut self, place: &Place<'tcx>, cntxt: PlaceContext, location: Location) {
         // Set off any `bug!`s in the type computation code
         let _ = place.ty(&self.body.local_decls, self.tcx);
+
+        if self.mir_phase >= MirPhase::Derefered
+            && place.projection.len() > 1
+            && cntxt != PlaceContext::NonUse(VarDebugInfo)
+            && place.projection[1..].contains(&ProjectionElem::Deref)
+        {
+            self.fail(location, format!("{:?}, has deref at the wrong place", place));
+        }
     }
 
     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs
index c8bb4fc5e6a..a915a4daa95 100644
--- a/compiler/rustc_data_structures/src/stable_hasher.rs
+++ b/compiler/rustc_data_structures/src/stable_hasher.rs
@@ -632,10 +632,10 @@ fn stable_hash_reduce<HCX, I, C, F>(
     }
 }
 
-/// Controls what data we do or not not hash.
+/// Controls what data we do or do not hash.
 /// Whenever a `HashStable` implementation caches its
 /// result, it needs to include `HashingControls` as part
-/// of the key, to ensure that is does not produce an incorrect
+/// of the key, to ensure that it does not produce an incorrect
 /// result (for example, using a `Fingerprint` produced while
 /// hashing `Span`s when a `Fingerprint` without `Span`s is
 /// being requested)
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index 7faf14a2472..02d076c95ca 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -234,6 +234,48 @@ pub fn fallback_fluent_bundle(
 /// Identifier for the Fluent message/attribute corresponding to a diagnostic message.
 type FluentId = Cow<'static, str>;
 
+/// Abstraction over a message in a subdiagnostic (i.e. label, note, help, etc) to support both
+/// translatable and non-translatable diagnostic messages.
+///
+/// Translatable messages for subdiagnostics are typically attributes attached to a larger Fluent
+/// message so messages of this type must be combined with a `DiagnosticMessage` (using
+/// `DiagnosticMessage::with_subdiagnostic_message`) before rendering. However, subdiagnostics from
+/// the `SessionSubdiagnostic` derive refer to Fluent identifiers directly.
+pub enum SubdiagnosticMessage {
+    /// Non-translatable diagnostic message.
+    // FIXME(davidtwco): can a `Cow<'static, str>` be used here?
+    Str(String),
+    /// Identifier of a Fluent message. Instances of this variant are generated by the
+    /// `SessionSubdiagnostic` derive.
+    FluentIdentifier(FluentId),
+    /// Attribute of a Fluent message. Needs to be combined with a Fluent identifier to produce an
+    /// actual translated message. Instances of this variant are generated by the `fluent_messages`
+    /// macro.
+    ///
+    /// <https://projectfluent.org/fluent/guide/attributes.html>
+    FluentAttr(FluentId),
+}
+
+impl SubdiagnosticMessage {
+    /// Create a `SubdiagnosticMessage` for the provided Fluent attribute.
+    pub fn attr(id: impl Into<FluentId>) -> Self {
+        SubdiagnosticMessage::FluentAttr(id.into())
+    }
+
+    /// Create a `SubdiagnosticMessage` for the provided Fluent identifier.
+    pub fn message(id: impl Into<FluentId>) -> Self {
+        SubdiagnosticMessage::FluentIdentifier(id.into())
+    }
+}
+
+/// `From` impl that enables existing diagnostic calls to functions which now take
+/// `impl Into<SubdiagnosticMessage>` to continue to work as before.
+impl<S: Into<String>> From<S> for SubdiagnosticMessage {
+    fn from(s: S) -> Self {
+        SubdiagnosticMessage::Str(s.into())
+    }
+}
+
 /// Abstraction over a message in a diagnostic to support both translatable and non-translatable
 /// diagnostic messages.
 ///
@@ -252,6 +294,29 @@ pub enum DiagnosticMessage {
 }
 
 impl DiagnosticMessage {
+    /// Given a `SubdiagnosticMessage` which may contain a Fluent attribute, create a new
+    /// `DiagnosticMessage` that combines that attribute with the Fluent identifier of `self`.
+    ///
+    /// - If the `SubdiagnosticMessage` is non-translatable then return the message as a
+    /// `DiagnosticMessage`.
+    /// - If `self` is non-translatable then return `self`'s message.
+    pub fn with_subdiagnostic_message(&self, sub: SubdiagnosticMessage) -> Self {
+        let attr = match sub {
+            SubdiagnosticMessage::Str(s) => return DiagnosticMessage::Str(s.clone()),
+            SubdiagnosticMessage::FluentIdentifier(id) => {
+                return DiagnosticMessage::FluentIdentifier(id, None);
+            }
+            SubdiagnosticMessage::FluentAttr(attr) => attr,
+        };
+
+        match self {
+            DiagnosticMessage::Str(s) => DiagnosticMessage::Str(s.clone()),
+            DiagnosticMessage::FluentIdentifier(id, _) => {
+                DiagnosticMessage::FluentIdentifier(id.clone(), Some(attr))
+            }
+        }
+    }
+
     /// Returns the `String` contained within the `DiagnosticMessage::Str` variant, assuming that
     /// this diagnostic message is of the legacy, non-translatable variety. Panics if this
     /// assumption does not hold.
@@ -266,14 +331,9 @@ impl DiagnosticMessage {
     }
 
     /// Create a `DiagnosticMessage` for the provided Fluent identifier.
-    pub fn fluent(id: impl Into<FluentId>) -> Self {
+    pub fn new(id: impl Into<FluentId>) -> Self {
         DiagnosticMessage::FluentIdentifier(id.into(), None)
     }
-
-    /// Create a `DiagnosticMessage` for the provided Fluent identifier and attribute.
-    pub fn fluent_attr(id: impl Into<FluentId>, attr: impl Into<FluentId>) -> Self {
-        DiagnosticMessage::FluentIdentifier(id.into(), Some(attr.into()))
-    }
 }
 
 /// `From` impl that enables existing diagnostic calls to functions which now take
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index f130b5aa9a6..643f3c12134 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -1,7 +1,7 @@
 use crate::snippet::Style;
 use crate::{
-    CodeSuggestion, DiagnosticMessage, Level, MultiSpan, Substitution, SubstitutionPart,
-    SuggestionStyle,
+    CodeSuggestion, DiagnosticMessage, Level, MultiSpan, SubdiagnosticMessage, Substitution,
+    SubstitutionPart, SuggestionStyle,
 };
 use rustc_data_structures::stable_map::FxHashMap;
 use rustc_error_messages::FluentValue;
@@ -283,8 +283,8 @@ impl Diagnostic {
     ///
     /// This span is *not* considered a ["primary span"][`MultiSpan`]; only
     /// the `Span` supplied when creating the diagnostic is primary.
-    pub fn span_label(&mut self, span: Span, label: impl Into<DiagnosticMessage>) -> &mut Self {
-        self.span.push_span_label(span, label.into());
+    pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagnosticMessage>) -> &mut Self {
+        self.span.push_span_label(span, self.subdiagnostic_message_to_diagnostic_message(label));
         self
     }
 
@@ -401,12 +401,12 @@ impl Diagnostic {
     }
 
     /// Add a note attached to this diagnostic.
-    pub fn note(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
+    pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
         self.sub(Level::Note, msg, MultiSpan::new(), None);
         self
     }
 
-    pub fn highlighted_note<M: Into<DiagnosticMessage>>(
+    pub fn highlighted_note<M: Into<SubdiagnosticMessage>>(
         &mut self,
         msg: Vec<(M, Style)>,
     ) -> &mut Self {
@@ -416,7 +416,7 @@ impl Diagnostic {
 
     /// Prints the span with a note above it.
     /// This is like [`Diagnostic::note()`], but it gets its own span.
-    pub fn note_once(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
+    pub fn note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
         self.sub(Level::OnceNote, msg, MultiSpan::new(), None);
         self
     }
@@ -426,7 +426,7 @@ impl Diagnostic {
     pub fn span_note<S: Into<MultiSpan>>(
         &mut self,
         sp: S,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self {
         self.sub(Level::Note, msg, sp.into(), None);
         self
@@ -437,14 +437,14 @@ impl Diagnostic {
     pub fn span_note_once<S: Into<MultiSpan>>(
         &mut self,
         sp: S,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self {
         self.sub(Level::OnceNote, msg, sp.into(), None);
         self
     }
 
     /// Add a warning attached to this diagnostic.
-    pub fn warn(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
+    pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
         self.sub(Level::Warning, msg, MultiSpan::new(), None);
         self
     }
@@ -454,14 +454,14 @@ impl Diagnostic {
     pub fn span_warn<S: Into<MultiSpan>>(
         &mut self,
         sp: S,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self {
         self.sub(Level::Warning, msg, sp.into(), None);
         self
     }
 
     /// Add a help message attached to this diagnostic.
-    pub fn help(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
+    pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
         self.sub(Level::Help, msg, MultiSpan::new(), None);
         self
     }
@@ -477,7 +477,7 @@ impl Diagnostic {
     pub fn span_help<S: Into<MultiSpan>>(
         &mut self,
         sp: S,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self {
         self.sub(Level::Help, msg, sp.into(), None);
         self
@@ -514,7 +514,7 @@ impl Diagnostic {
     /// In other words, multiple changes need to be applied as part of this suggestion.
     pub fn multipart_suggestion(
         &mut self,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: Vec<(Span, String)>,
         applicability: Applicability,
     ) -> &mut Self {
@@ -530,7 +530,7 @@ impl Diagnostic {
     /// In other words, multiple changes need to be applied as part of this suggestion.
     pub fn multipart_suggestion_verbose(
         &mut self,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: Vec<(Span, String)>,
         applicability: Applicability,
     ) -> &mut Self {
@@ -544,7 +544,7 @@ impl Diagnostic {
     /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`].
     pub fn multipart_suggestion_with_style(
         &mut self,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: Vec<(Span, String)>,
         applicability: Applicability,
         style: SuggestionStyle,
@@ -557,7 +557,7 @@ impl Diagnostic {
                     .map(|(span, snippet)| SubstitutionPart { snippet, span })
                     .collect(),
             }],
-            msg: msg.into(),
+            msg: self.subdiagnostic_message_to_diagnostic_message(msg),
             style,
             applicability,
         });
@@ -572,7 +572,7 @@ impl Diagnostic {
     /// improve understandability.
     pub fn tool_only_multipart_suggestion(
         &mut self,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: Vec<(Span, String)>,
         applicability: Applicability,
     ) -> &mut Self {
@@ -584,7 +584,7 @@ impl Diagnostic {
                     .map(|(span, snippet)| SubstitutionPart { snippet, span })
                     .collect(),
             }],
-            msg: msg.into(),
+            msg: self.subdiagnostic_message_to_diagnostic_message(msg),
             style: SuggestionStyle::CompletelyHidden,
             applicability,
         });
@@ -611,7 +611,7 @@ impl Diagnostic {
     pub fn span_suggestion(
         &mut self,
         sp: Span,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self {
@@ -629,7 +629,7 @@ impl Diagnostic {
     pub fn span_suggestion_with_style(
         &mut self,
         sp: Span,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: impl ToString,
         applicability: Applicability,
         style: SuggestionStyle,
@@ -638,7 +638,7 @@ impl Diagnostic {
             substitutions: vec![Substitution {
                 parts: vec![SubstitutionPart { snippet: suggestion.to_string(), span: sp }],
             }],
-            msg: msg.into(),
+            msg: self.subdiagnostic_message_to_diagnostic_message(msg),
             style,
             applicability,
         });
@@ -649,7 +649,7 @@ impl Diagnostic {
     pub fn span_suggestion_verbose(
         &mut self,
         sp: Span,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self {
@@ -668,7 +668,7 @@ impl Diagnostic {
     pub fn span_suggestions(
         &mut self,
         sp: Span,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestions: impl Iterator<Item = String>,
         applicability: Applicability,
     ) -> &mut Self {
@@ -680,7 +680,7 @@ impl Diagnostic {
             .collect();
         self.push_suggestion(CodeSuggestion {
             substitutions,
-            msg: msg.into(),
+            msg: self.subdiagnostic_message_to_diagnostic_message(msg),
             style: SuggestionStyle::ShowCode,
             applicability,
         });
@@ -691,7 +691,7 @@ impl Diagnostic {
     /// See also [`Diagnostic::span_suggestion()`].
     pub fn multipart_suggestions(
         &mut self,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestions: impl Iterator<Item = Vec<(Span, String)>>,
         applicability: Applicability,
     ) -> &mut Self {
@@ -704,7 +704,7 @@ impl Diagnostic {
                         .collect(),
                 })
                 .collect(),
-            msg: msg.into(),
+            msg: self.subdiagnostic_message_to_diagnostic_message(msg),
             style: SuggestionStyle::ShowCode,
             applicability,
         });
@@ -717,7 +717,7 @@ impl Diagnostic {
     pub fn span_suggestion_short(
         &mut self,
         sp: Span,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self {
@@ -740,7 +740,7 @@ impl Diagnostic {
     pub fn span_suggestion_hidden(
         &mut self,
         sp: Span,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self {
@@ -761,7 +761,7 @@ impl Diagnostic {
     pub fn tool_only_span_suggestion(
         &mut self,
         sp: Span,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self {
@@ -831,6 +831,18 @@ impl Diagnostic {
         &self.message
     }
 
+    /// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by
+    /// combining it with the primary message of the diagnostic (if translatable, otherwise it just
+    /// passes the user's string along).
+    fn subdiagnostic_message_to_diagnostic_message(
+        &self,
+        attr: impl Into<SubdiagnosticMessage>,
+    ) -> DiagnosticMessage {
+        let msg =
+            self.message.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages");
+        msg.with_subdiagnostic_message(attr.into())
+    }
+
     /// Convenience function for internal use, clients should use one of the
     /// public methods above.
     ///
@@ -838,13 +850,16 @@ impl Diagnostic {
     pub fn sub(
         &mut self,
         level: Level,
-        message: impl Into<DiagnosticMessage>,
+        message: impl Into<SubdiagnosticMessage>,
         span: MultiSpan,
         render_span: Option<MultiSpan>,
     ) {
         let sub = SubDiagnostic {
             level,
-            message: vec![(message.into(), Style::NoStyle)],
+            message: vec![(
+                self.subdiagnostic_message_to_diagnostic_message(message),
+                Style::NoStyle,
+            )],
             span,
             render_span,
         };
@@ -853,14 +868,17 @@ impl Diagnostic {
 
     /// Convenience function for internal use, clients should use one of the
     /// public methods above.
-    fn sub_with_highlights<M: Into<DiagnosticMessage>>(
+    fn sub_with_highlights<M: Into<SubdiagnosticMessage>>(
         &mut self,
         level: Level,
         mut message: Vec<(M, Style)>,
         span: MultiSpan,
         render_span: Option<MultiSpan>,
     ) {
-        let message = message.drain(..).map(|m| (m.0.into(), m.1)).collect();
+        let message = message
+            .drain(..)
+            .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1))
+            .collect();
         let sub = SubDiagnostic { level, message, span, render_span };
         self.children.push(sub);
     }
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index 6ef2c832c65..9e0a99849a3 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -1,5 +1,8 @@
 use crate::diagnostic::IntoDiagnosticArg;
-use crate::{Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed};
+use crate::{
+    Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed,
+    SubdiagnosticMessage,
+};
 use crate::{Handler, Level, MultiSpan, StashKey};
 use rustc_lint_defs::Applicability;
 
@@ -395,7 +398,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     /// the diagnostic was constructed. However, the label span is *not* considered a
     /// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is
     /// primary.
-    pub fn span_label(&mut self, span: Span, label: impl Into<DiagnosticMessage>) -> &mut Self);
+    pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagnosticMessage>) -> &mut Self);
 
     forward!(
     /// Labels all the given spans with the provided label.
@@ -430,25 +433,29 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
         found: DiagnosticStyledString,
     ) -> &mut Self);
 
-    forward!(pub fn note(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self);
-    forward!(pub fn note_once(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self);
+    forward!(pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
+    forward!(pub fn note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
     forward!(pub fn span_note(
         &mut self,
         sp: impl Into<MultiSpan>,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self);
     forward!(pub fn span_note_once(
         &mut self,
         sp: impl Into<MultiSpan>,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self);
-    forward!(pub fn warn(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self);
-    forward!(pub fn span_warn(&mut self, sp: impl Into<MultiSpan>, msg: &str) -> &mut Self);
-    forward!(pub fn help(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self);
+    forward!(pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
+    forward!(pub fn span_warn(
+        &mut self,
+        sp: impl Into<MultiSpan>,
+        msg: impl Into<SubdiagnosticMessage>,
+    ) -> &mut Self);
+    forward!(pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
     forward!(pub fn span_help(
         &mut self,
         sp: impl Into<MultiSpan>,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self);
     forward!(pub fn help_use_latest_edition(&mut self,) -> &mut Self);
     forward!(pub fn set_is_lint(&mut self,) -> &mut Self);
@@ -457,67 +464,67 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
 
     forward!(pub fn multipart_suggestion(
         &mut self,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: Vec<(Span, String)>,
         applicability: Applicability,
     ) -> &mut Self);
     forward!(pub fn multipart_suggestion_verbose(
         &mut self,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: Vec<(Span, String)>,
         applicability: Applicability,
     ) -> &mut Self);
     forward!(pub fn tool_only_multipart_suggestion(
         &mut self,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: Vec<(Span, String)>,
         applicability: Applicability,
     ) -> &mut Self);
     forward!(pub fn span_suggestion(
         &mut self,
         sp: Span,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self);
     forward!(pub fn span_suggestions(
         &mut self,
         sp: Span,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestions: impl Iterator<Item = String>,
         applicability: Applicability,
     ) -> &mut Self);
     forward!(pub fn multipart_suggestions(
         &mut self,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestions: impl Iterator<Item = Vec<(Span, String)>>,
         applicability: Applicability,
     ) -> &mut Self);
     forward!(pub fn span_suggestion_short(
         &mut self,
         sp: Span,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self);
     forward!(pub fn span_suggestion_verbose(
         &mut self,
         sp: Span,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self);
     forward!(pub fn span_suggestion_hidden(
         &mut self,
         sp: Span,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self);
     forward!(pub fn tool_only_span_suggestion(
         &mut self,
         sp: Span,
-        msg: impl Into<DiagnosticMessage>,
+        msg: impl Into<SubdiagnosticMessage>,
         suggestion: impl ToString,
         applicability: Applicability,
     ) -> &mut Self);
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 5b9b65da343..fb02f1d68eb 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -32,7 +32,8 @@ use rustc_data_structures::sync::{self, Lock, Lrc};
 use rustc_data_structures::AtomicRef;
 pub use rustc_error_messages::{
     fallback_fluent_bundle, fluent, fluent_bundle, DiagnosticMessage, FluentBundle,
-    LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, DEFAULT_LOCALE_RESOURCES,
+    LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagnosticMessage,
+    DEFAULT_LOCALE_RESOURCES,
 };
 pub use rustc_lint_defs::{pluralize, Applicability};
 use rustc_span::source_map::SourceMap;
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
index d7daee64d79..95ee0d4a060 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
@@ -126,14 +126,14 @@ impl<'a> SessionDiagnosticDerive<'a> {
                     (Some((SessionDiagnosticKind::Error, _)), Some((slug, _))) => {
                         quote! {
                             let mut #diag = #sess.struct_err(
-                                rustc_errors::DiagnosticMessage::fluent(#slug),
+                                rustc_errors::DiagnosticMessage::new(#slug),
                             );
                         }
                     }
                     (Some((SessionDiagnosticKind::Warn, _)), Some((slug, _))) => {
                         quote! {
                             let mut #diag = #sess.struct_warn(
-                                rustc_errors::DiagnosticMessage::fluent(#slug),
+                                rustc_errors::DiagnosticMessage::new(#slug),
                             );
                         }
                     }
@@ -254,21 +254,6 @@ impl SessionDiagnosticDeriveBuilder {
 
         if matches!(name, "help" | "note") && matches!(meta, Meta::Path(_) | Meta::NameValue(_)) {
             let diag = &self.diag;
-            let slug = match &self.slug {
-                Some((slug, _)) => slug.as_str(),
-                None => throw_span_err!(
-                    span,
-                    &format!(
-                        "`#[{}{}]` must come after `#[error(..)]` or `#[warn(..)]`",
-                        name,
-                        match meta {
-                            Meta::Path(_) => "",
-                            Meta::NameValue(_) => " = ...",
-                            _ => unreachable!(),
-                        }
-                    )
-                ),
-            };
             let id = match meta {
                 Meta::Path(..) => quote! { #name },
                 Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
@@ -279,7 +264,7 @@ impl SessionDiagnosticDeriveBuilder {
             let fn_name = proc_macro2::Ident::new(name, attr.span());
 
             return Ok(quote! {
-                #diag.#fn_name(rustc_errors::DiagnosticMessage::fluent_attr(#slug, #id));
+                #diag.#fn_name(rustc_errors::SubdiagnosticMessage::attr(#id));
             });
         }
 
@@ -525,13 +510,8 @@ impl SessionDiagnosticDeriveBuilder {
 
                 let method = format_ident!("span_{}", name);
 
-                let slug = self
-                    .slug
-                    .as_ref()
-                    .map(|(slug, _)| slug.as_str())
-                    .unwrap_or_else(|| "missing-slug");
                 let msg = msg.as_deref().unwrap_or("suggestion");
-                let msg = quote! { rustc_errors::DiagnosticMessage::fluent_attr(#slug, #msg) };
+                let msg = quote! { rustc_errors::SubdiagnosticMessage::attr(#msg) };
                 let code = code.unwrap_or_else(|| quote! { String::new() });
 
                 Ok(quote! { #diag.#method(#span_field, #msg, #code, #applicability); })
@@ -549,14 +529,11 @@ impl SessionDiagnosticDeriveBuilder {
         fluent_attr_identifier: &str,
     ) -> TokenStream {
         let diag = &self.diag;
-
-        let slug =
-            self.slug.as_ref().map(|(slug, _)| slug.as_str()).unwrap_or_else(|| "missing-slug");
         let fn_name = format_ident!("span_{}", kind);
         quote! {
             #diag.#fn_name(
                 #field_binding,
-                rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier)
+                rustc_errors::SubdiagnosticMessage::attr(#fluent_attr_identifier)
             );
         }
     }
@@ -565,9 +542,8 @@ impl SessionDiagnosticDeriveBuilder {
     /// and `fluent_attr_identifier`.
     fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: &str) -> TokenStream {
         let diag = &self.diag;
-        let slug = self.slug.as_ref().map(|(slug, _)| slug.as_str()).unwrap_or("missing-slug");
         quote! {
-            #diag.#kind(rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier));
+            #diag.#kind(rustc_errors::SubdiagnosticMessage::attr(#fluent_attr_identifier));
         }
     }
 
diff --git a/compiler/rustc_macros/src/diagnostics/fluent.rs b/compiler/rustc_macros/src/diagnostics/fluent.rs
index 8523d7fa9f9..42a9bf477a4 100644
--- a/compiler/rustc_macros/src/diagnostics/fluent.rs
+++ b/compiler/rustc_macros/src/diagnostics/fluent.rs
@@ -11,7 +11,7 @@ use proc_macro::{Diagnostic, Level, Span};
 use proc_macro2::TokenStream;
 use quote::quote;
 use std::{
-    collections::HashMap,
+    collections::{HashMap, HashSet},
     fs::File,
     io::Read,
     path::{Path, PathBuf},
@@ -100,6 +100,10 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
         let ident_span = res.ident.span().unwrap();
         let path_span = res.resource.span().unwrap();
 
+        // Set of Fluent attribute names already output, to avoid duplicate type errors - any given
+        // constant created for a given attribute is the same.
+        let mut previous_attrs = HashSet::new();
+
         let relative_ftl_path = res.resource.value();
         let absolute_ftl_path =
             invocation_relative_path_to_absolute(ident_span, &relative_ftl_path);
@@ -199,13 +203,15 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
                 });
 
                 for Attribute { id: Identifier { name: attr_name }, .. } in attributes {
-                    let attr_snake_name = attr_name.replace("-", "_");
-                    let snake_name = Ident::new(&format!("{snake_name}_{attr_snake_name}"), span);
+                    let snake_name = Ident::new(&attr_name.replace("-", "_"), span);
+                    if !previous_attrs.insert(snake_name.clone()) {
+                        continue;
+                    }
+
                     constants.extend(quote! {
-                        pub const #snake_name: crate::DiagnosticMessage =
-                            crate::DiagnosticMessage::FluentIdentifier(
-                                std::borrow::Cow::Borrowed(#name),
-                                Some(std::borrow::Cow::Borrowed(#attr_name))
+                        pub const #snake_name: crate::SubdiagnosticMessage =
+                            crate::SubdiagnosticMessage::FluentAttr(
+                                std::borrow::Cow::Borrowed(#attr_name)
                             );
                     });
                 }
diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index df01419c82a..9aeb484bfd5 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -397,7 +397,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
 
         let diag = &self.diag;
         let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
-        let message = quote! { rustc_errors::DiagnosticMessage::fluent(#slug) };
+        let message = quote! { rustc_errors::SubdiagnosticMessage::message(#slug) };
         let call = if matches!(kind, SubdiagnosticKind::Suggestion(..)) {
             if let Some(span) = span_field {
                 quote! { #diag.#name(#span, #message, #code, #applicability); }
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 71cea005cf8..1b63c8d67ca 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -172,12 +172,14 @@ pub enum MirPhase {
     /// terminator means that the auto-generated drop glue will be invoked. Also, `Copy` operands
     /// are allowed for non-`Copy` types.
     DropsLowered = 3,
+    /// After this projections may only contain deref projections as the first element.
+    Derefered = 4,
     /// Beginning with this phase, the following variant is disallowed:
     /// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array`
     ///
     /// And the following variant is allowed:
     /// * [`StatementKind::SetDiscriminant`]
-    Deaggregated = 4,
+    Deaggregated = 5,
     /// Before this phase, generators are in the "source code" form, featuring `yield` statements
     /// and such. With this phase change, they are transformed into a proper state machine. Running
     /// optimizations before this change can be potentially dangerous because the source code is to
@@ -191,8 +193,8 @@ pub enum MirPhase {
     /// Beginning with this phase, the following variants are disallowed:
     /// * [`TerminatorKind::Yield`](terminator::TerminatorKind::Yield)
     /// * [`TerminatorKind::GeneratorDrop`](terminator::TerminatorKind::GeneratorDrop)
-    GeneratorsLowered = 5,
-    Optimized = 6,
+    GeneratorsLowered = 6,
+    Optimized = 7,
 }
 
 impl MirPhase {
@@ -2602,9 +2604,19 @@ pub enum Rvalue<'tcx> {
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 static_assert_size!(Rvalue<'_>, 40);
 
+impl<'tcx> Rvalue<'tcx> {
+    #[inline]
+    pub fn is_pointer_int_cast(&self) -> bool {
+        matches!(self, Rvalue::Cast(CastKind::PointerAddress, _, _))
+    }
+}
+
 #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
 pub enum CastKind {
     Misc,
+    /// A pointer to address cast. A cast between a pointer and an integer type,
+    /// or between a function pointer and an integer type.
+    PointerAddress,
     Pointer(PointerCast),
 }
 
diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs
index f1fb484a801..c93b7a95502 100644
--- a/compiler/rustc_middle/src/mir/tcx.rs
+++ b/compiler/rustc_middle/src/mir/tcx.rs
@@ -4,7 +4,6 @@
  */
 
 use crate::mir::*;
-use crate::ty::cast::CastTy;
 use crate::ty::subst::Subst;
 use crate::ty::{self, Ty, TyCtxt};
 use rustc_hir as hir;
@@ -224,22 +223,6 @@ impl<'tcx> Rvalue<'tcx> {
             _ => RvalueInitializationState::Deep,
         }
     }
-
-    pub fn is_pointer_int_cast<D>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> bool
-    where
-        D: HasLocalDecls<'tcx>,
-    {
-        if let Rvalue::Cast(CastKind::Misc, src_op, dest_ty) = self {
-            if let Some(CastTy::Int(_)) = CastTy::from_ty(*dest_ty) {
-                let src_ty = src_op.ty(local_decls, tcx);
-                if let Some(CastTy::FnPtr | CastTy::Ptr(_)) = CastTy::from_ty(src_ty) {
-                    return true;
-                }
-            }
-        }
-
-        false
-    }
 }
 
 impl<'tcx> Operand<'tcx> {
diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
index 0a0c7659b08..2b137046c7f 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
@@ -11,6 +11,7 @@ use rustc_middle::mir::AssertKind;
 use rustc_middle::mir::Place;
 use rustc_middle::mir::*;
 use rustc_middle::thir::*;
+use rustc_middle::ty::cast::CastTy;
 use rustc_middle::ty::{self, Ty, UpvarSubsts};
 use rustc_span::Span;
 
@@ -188,11 +189,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 block.and(Rvalue::Use(Operand::Move(Place::from(result))))
             }
             ExprKind::Cast { source } => {
+                let source = &this.thir[source];
+                let from_ty = CastTy::from_ty(source.ty);
+                let cast_ty = CastTy::from_ty(expr.ty);
+                let cast_kind = match (from_ty, cast_ty) {
+                    (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => {
+                        CastKind::PointerAddress
+                    }
+                    (_, _) => CastKind::Misc,
+                };
                 let source = unpack!(
-                    block =
-                        this.as_operand(block, scope, &this.thir[source], None, NeedsTemporary::No)
+                    block = this.as_operand(block, scope, source, None, NeedsTemporary::No)
                 );
-                block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty))
+                block.and(Rvalue::Cast(cast_kind, source, expr.ty))
             }
             ExprKind::Pointer { cast, source } => {
                 let source = unpack!(
diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
index 4350eb6cdd3..7076fbe1bdb 100644
--- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
@@ -1,7 +1,6 @@
 use rustc_index::bit_set::{BitSet, ChunkedBitSet};
 use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
-use rustc_middle::mir::{self, Local, LocalDecls, Location, Place, StatementKind};
-use rustc_middle::ty::TyCtxt;
+use rustc_middle::mir::{self, Local, Location, Place, StatementKind};
 
 use crate::{Analysis, AnalysisDomain, Backward, CallReturnPlaces, GenKill, GenKillAnalysis};
 
@@ -193,27 +192,21 @@ impl DefUse {
 /// This is basically written for dead store elimination and nothing else.
 ///
 /// All of the caveats of `MaybeLiveLocals` apply.
-pub struct MaybeTransitiveLiveLocals<'a, 'tcx> {
+pub struct MaybeTransitiveLiveLocals<'a> {
     always_live: &'a BitSet<Local>,
-    local_decls: &'a LocalDecls<'tcx>,
-    tcx: TyCtxt<'tcx>,
 }
 
-impl<'a, 'tcx> MaybeTransitiveLiveLocals<'a, 'tcx> {
+impl<'a> MaybeTransitiveLiveLocals<'a> {
     /// The `always_alive` set is the set of locals to which all stores should unconditionally be
     /// considered live.
     ///
     /// This should include at least all locals that are ever borrowed.
-    pub fn new(
-        always_live: &'a BitSet<Local>,
-        local_decls: &'a LocalDecls<'tcx>,
-        tcx: TyCtxt<'tcx>,
-    ) -> Self {
-        MaybeTransitiveLiveLocals { always_live, local_decls, tcx }
+    pub fn new(always_live: &'a BitSet<Local>) -> Self {
+        MaybeTransitiveLiveLocals { always_live }
     }
 }
 
-impl<'a, 'tcx> AnalysisDomain<'tcx> for MaybeTransitiveLiveLocals<'a, 'tcx> {
+impl<'a, 'tcx> AnalysisDomain<'tcx> for MaybeTransitiveLiveLocals<'a> {
     type Domain = ChunkedBitSet<Local>;
     type Direction = Backward;
 
@@ -241,7 +234,7 @@ impl<'a> GenKill<Local> for TransferWrapper<'a> {
     }
 }
 
-impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a, 'tcx> {
+impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
     fn apply_statement_effect(
         &self,
         trans: &mut Self::Domain,
@@ -251,7 +244,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a, 'tcx> {
         // Compute the place that we are storing to, if any
         let destination = match &statement.kind {
             StatementKind::Assign(assign) => {
-                if assign.1.is_pointer_int_cast(self.local_decls, self.tcx) {
+                if assign.1.is_pointer_int_cast() {
                     // Pointer to int casts may be side-effects due to exposing the provenance.
                     // While the model is undecided, we should be conservative. See
                     // <https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html>
diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs
index 84f2ee639e4..8becac34ed7 100644
--- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs
+++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs
@@ -24,7 +24,7 @@ use rustc_mir_dataflow::{impls::MaybeTransitiveLiveLocals, Analysis};
 /// The `borrowed` set must be a `BitSet` of all the locals that are ever borrowed in this body. It
 /// can be generated via the [`get_borrowed_locals`] function.
 pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitSet<Local>) {
-    let mut live = MaybeTransitiveLiveLocals::new(borrowed, &body.local_decls, tcx)
+    let mut live = MaybeTransitiveLiveLocals::new(borrowed)
         .into_engine(tcx, body)
         .iterate_to_fixpoint()
         .into_results_cursor(body);
@@ -34,7 +34,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS
         for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() {
             let loc = Location { block: bb, statement_index };
             if let StatementKind::Assign(assign) = &statement.kind {
-                if assign.1.is_pointer_int_cast(&body.local_decls, tcx) {
+                if assign.1.is_pointer_int_cast() {
                     continue;
                 }
             }
diff --git a/compiler/rustc_mir_transform/src/deref_separator.rs b/compiler/rustc_mir_transform/src/deref_separator.rs
index 57a95a67df7..bfb3ad1be27 100644
--- a/compiler/rustc_mir_transform/src/deref_separator.rs
+++ b/compiler/rustc_mir_transform/src/deref_separator.rs
@@ -1,9 +1,11 @@
 use crate::MirPass;
 use rustc_index::vec::IndexVec;
 use rustc_middle::mir::patch::MirPatch;
+use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
 use rustc_middle::mir::visit::{MutVisitor, PlaceContext};
 use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
+
 pub struct Derefer;
 
 pub struct DerefChecker<'tcx> {
@@ -17,63 +19,68 @@ impl<'tcx> MutVisitor<'tcx> for DerefChecker<'tcx> {
         self.tcx
     }
 
-    fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, loc: Location) {
-        let mut place_local = place.local;
-        let mut last_len = 0;
-        let mut last_deref_idx = 0;
+    fn visit_place(&mut self, place: &mut Place<'tcx>, cntxt: PlaceContext, loc: Location) {
+        if !place.projection.is_empty()
+            && cntxt != PlaceContext::NonUse(VarDebugInfo)
+            && place.projection[1..].contains(&ProjectionElem::Deref)
+        {
+            let mut place_local = place.local;
+            let mut last_len = 0;
+            let mut last_deref_idx = 0;
 
-        let mut prev_temp: Option<Local> = None;
+            let mut prev_temp: Option<Local> = None;
 
-        for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
-            if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() {
-                last_deref_idx = idx;
-            }
-        }
-
-        for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
-            if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() {
-                let ty = p_ref.ty(&self.local_decls, self.tcx).ty;
-                let temp = self.patcher.new_local_with_info(
-                    ty,
-                    self.local_decls[p_ref.local].source_info.span,
-                    Some(Box::new(LocalInfo::DerefTemp)),
-                );
-
-                self.patcher.add_statement(loc, StatementKind::StorageLive(temp));
-
-                // We are adding current p_ref's projections to our
-                // temp value, excluding projections we already covered.
-                let deref_place = Place::from(place_local)
-                    .project_deeper(&p_ref.projection[last_len..], self.tcx);
-
-                self.patcher.add_assign(
-                    loc,
-                    Place::from(temp),
-                    Rvalue::Use(Operand::Move(deref_place)),
-                );
-                place_local = temp;
-                last_len = p_ref.projection.len();
-
-                // Change `Place` only if we are actually at the Place's last deref
-                if idx == last_deref_idx {
-                    let temp_place =
-                        Place::from(temp).project_deeper(&place.projection[idx..], self.tcx);
-                    *place = temp_place;
+            for (idx, elem) in place.projection[0..].iter().enumerate() {
+                if *elem == ProjectionElem::Deref {
+                    last_deref_idx = idx;
                 }
-
-                // We are destroying the previous temp since it's no longer used.
-                if let Some(prev_temp) = prev_temp {
-                    self.patcher.add_statement(loc, StatementKind::StorageDead(prev_temp));
+            }
+            for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
+                if !p_ref.projection.is_empty() && p_elem == ProjectionElem::Deref {
+                    let ty = p_ref.ty(&self.local_decls, self.tcx).ty;
+                    let temp = self.patcher.new_local_with_info(
+                        ty,
+                        self.local_decls[p_ref.local].source_info.span,
+                        Some(Box::new(LocalInfo::DerefTemp)),
+                    );
+
+                    self.patcher.add_statement(loc, StatementKind::StorageLive(temp));
+
+                    // We are adding current p_ref's projections to our
+                    // temp value, excluding projections we already covered.
+                    let deref_place = Place::from(place_local)
+                        .project_deeper(&p_ref.projection[last_len..], self.tcx);
+
+                    self.patcher.add_assign(
+                        loc,
+                        Place::from(temp),
+                        Rvalue::Use(Operand::Move(deref_place)),
+                    );
+                    place_local = temp;
+                    last_len = p_ref.projection.len();
+
+                    // Change `Place` only if we are actually at the Place's last deref
+                    if idx == last_deref_idx {
+                        let temp_place =
+                            Place::from(temp).project_deeper(&place.projection[idx..], self.tcx);
+                        *place = temp_place;
+                    }
+
+                    // We are destroying the previous temp since it's no longer used.
+                    if let Some(prev_temp) = prev_temp {
+                        self.patcher.add_statement(loc, StatementKind::StorageDead(prev_temp));
+                    }
+
+                    prev_temp = Some(temp);
                 }
-
-                prev_temp = Some(temp);
             }
-        }
 
-        // Since we won't be able to reach final temp, we destroy it outside the loop.
-        if let Some(prev_temp) = prev_temp {
-            let last_loc = Location { block: loc.block, statement_index: loc.statement_index + 1 };
-            self.patcher.add_statement(last_loc, StatementKind::StorageDead(prev_temp));
+            // Since we won't be able to reach final temp, we destroy it outside the loop.
+            if let Some(prev_temp) = prev_temp {
+                let last_loc =
+                    Location { block: loc.block, statement_index: loc.statement_index + 1 };
+                self.patcher.add_statement(last_loc, StatementKind::StorageDead(prev_temp));
+            }
         }
     }
 }
@@ -92,5 +99,6 @@ pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
 impl<'tcx> MirPass<'tcx> for Derefer {
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         deref_finder(tcx, body);
+        body.phase = MirPhase::Derefered;
     }
 }
diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs
index 4a3505ca3ff..9eb77f60213 100644
--- a/compiler/rustc_mir_transform/src/generator.rs
+++ b/compiler/rustc_mir_transform/src/generator.rs
@@ -49,6 +49,7 @@
 //! For generators with state 1 (returned) and state 2 (poisoned) it does nothing.
 //! Otherwise it drops all the values in scope at the last suspension point.
 
+use crate::deref_separator::deref_finder;
 use crate::simplify;
 use crate::util::expand_aggregate;
 use crate::MirPass;
@@ -1368,6 +1369,9 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
 
         // Create the Generator::resume function
         create_generator_resume_function(tcx, transform, body, can_return);
+
+        // Run derefer to fix Derefs that are not in the first place
+        deref_finder(tcx, body);
     }
 }
 
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 66fb01bd464..9526c0acc66 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -1,5 +1,5 @@
 //! Inlining pass for MIR functions
-
+use crate::deref_separator::deref_finder;
 use rustc_attr::InlineAttr;
 use rustc_index::bit_set::BitSet;
 use rustc_index::vec::Idx;
@@ -53,6 +53,7 @@ impl<'tcx> MirPass<'tcx> for Inline {
             debug!("running simplify cfg on {:?}", body.source);
             CfgSimplifier::new(body).simplify();
             remove_dead_blocks(tcx, body);
+            deref_finder(tcx, body);
         }
     }
 }
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index c9da58aae5c..12302315e90 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -17,10 +17,10 @@ use rustc_ast::{
 };
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{pluralize, struct_span_err, Diagnostic, EmissionGuarantee, ErrorGuaranteed};
 use rustc_errors::{
-    Applicability, DiagnosticBuilder, DiagnosticMessage, Handler, MultiSpan, PResult,
+    fluent, Applicability, DiagnosticBuilder, DiagnosticMessage, Handler, MultiSpan, PResult,
 };
+use rustc_errors::{pluralize, struct_span_err, Diagnostic, EmissionGuarantee, ErrorGuaranteed};
 use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, Ident};
@@ -658,13 +658,10 @@ impl<'a> Parser<'a> {
                     err.delay_as_bug();
                     self.struct_span_err(
                         expr.span,
-                        DiagnosticMessage::fluent("parser-struct-literal-body-without-path"),
+                        fluent::parser::struct_literal_body_without_path,
                     )
                     .multipart_suggestion(
-                        DiagnosticMessage::fluent_attr(
-                            "parser-struct-literal-body-without-path",
-                            "suggestion",
-                        ),
+                        fluent::parser::suggestion,
                         vec![
                             (expr.span.shrink_to_lo(), "{ SomeStruct ".to_string()),
                             (expr.span.shrink_to_hi(), " }".to_string()),
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index e99347206fe..6720399aacb 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -996,35 +996,24 @@ impl<'a> Parser<'a> {
     fn parse_item_foreign_mod(
         &mut self,
         attrs: &mut Vec<Attribute>,
-        unsafety: Unsafe,
+        mut unsafety: Unsafe,
     ) -> PResult<'a, ItemInfo> {
-        let sp_start = self.prev_token.span;
         let abi = self.parse_abi(); // ABI?
-        match self.parse_item_list(attrs, |p| p.parse_foreign_item(ForceCollect::No)) {
-            Ok(items) => {
-                let module = ast::ForeignMod { unsafety, abi, items };
-                Ok((Ident::empty(), ItemKind::ForeignMod(module)))
-            }
-            Err(mut err) => {
-                let current_qual_sp = self.prev_token.span;
-                let current_qual_sp = current_qual_sp.to(sp_start);
-                if let Ok(current_qual) = self.span_to_snippet(current_qual_sp) {
-                    // FIXME(davidtwco): avoid depending on the error message text
-                    if err.message[0].0.expect_str() == "expected `{`, found keyword `unsafe`" {
-                        let invalid_qual_sp = self.token.uninterpolated_span();
-                        let invalid_qual = self.span_to_snippet(invalid_qual_sp).unwrap();
-
-                        err.span_suggestion(
-                                current_qual_sp.to(invalid_qual_sp),
-                                &format!("`{}` must come before `{}`", invalid_qual, current_qual),
-                                format!("{} {}", invalid_qual, current_qual),
-                                Applicability::MachineApplicable,
-                            ).note("keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`");
-                    }
-                }
-                Err(err)
-            }
+        if unsafety == Unsafe::No
+            && self.token.is_keyword(kw::Unsafe)
+            && self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Brace))
+        {
+            let mut err = self.expect(&token::OpenDelim(Delimiter::Brace)).unwrap_err();
+            err.emit();
+            unsafety = Unsafe::Yes(self.token.span);
+            self.eat_keyword(kw::Unsafe);
         }
+        let module = ast::ForeignMod {
+            unsafety,
+            abi,
+            items: self.parse_item_list(attrs, |p| p.parse_foreign_item(ForceCollect::No))?,
+        };
+        Ok((Ident::empty(), ItemKind::ForeignMod(module)))
     }
 
     /// Parses a foreign item (one in an `extern { ... }` block).
diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs
index 09854336599..7499e5efdee 100644
--- a/compiler/rustc_typeck/src/check/check.rs
+++ b/compiler/rustc_typeck/src/check/check.rs
@@ -105,12 +105,6 @@ pub(super) fn check_fn<'a, 'tcx>(
             DUMMY_SP,
             param_env,
         ));
-    // HACK(oli-obk): we rewrite the declared return type, too, so that we don't end up inferring all
-    // unconstrained RPIT to have `()` as their hidden type. This would happen because further down we
-    // compare the ret_coercion with declared_ret_ty, and anything uninferred would be inferred to the
-    // opaque type itself. That again would cause writeback to assume we have a recursive call site
-    // and do the sadly stabilized fallback to `()`.
-    let declared_ret_ty = ret_ty;
     fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
     fcx.ret_type_span = Some(decl.output.span());
 
@@ -254,7 +248,12 @@ pub(super) fn check_fn<'a, 'tcx>(
             fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::DynReturnFn, span });
         debug!("actual_return_ty replaced with {:?}", actual_return_ty);
     }
-    fcx.demand_suptype(span, declared_ret_ty, actual_return_ty);
+
+    // HACK(oli-obk, compiler-errors): We should be comparing this against
+    // `declared_ret_ty`, but then anything uninferred would be inferred to
+    // the opaque type itself. That again would cause writeback to assume
+    // we have a recursive call site and do the sadly stabilized fallback to `()`.
+    fcx.demand_suptype(span, ret_ty, actual_return_ty);
 
     // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !`
     if let Some(panic_impl_did) = tcx.lang_items().panic_impl()
diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs
index 1af50191cb0..d6a8659d54b 100644
--- a/compiler/rustc_typeck/src/check/coercion.rs
+++ b/compiler/rustc_typeck/src/check/coercion.rs
@@ -1500,7 +1500,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                             coercion_error.clone(),
                             fcx,
                             parent_id,
-                            expression.map(|expr| (expr, blk_id)),
+                            expression,
+                            Some(blk_id),
                         );
                         if !fcx.tcx.features().unsized_locals {
                             unsized_return = self.is_return_ty_unsized(fcx, blk_id);
@@ -1514,6 +1515,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                             coercion_error.clone(),
                             fcx,
                             id,
+                            expression,
                             None,
                         );
                         if !fcx.tcx.features().unsized_locals {
@@ -1564,21 +1566,28 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         ty_err: TypeError<'tcx>,
         fcx: &FnCtxt<'a, 'tcx>,
         id: hir::HirId,
-        expression: Option<(&'tcx hir::Expr<'tcx>, hir::HirId)>,
+        expression: Option<&'tcx hir::Expr<'tcx>>,
+        blk_id: Option<hir::HirId>,
     ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
         let mut err = fcx.report_mismatched_types(cause, expected, found, ty_err);
 
         let mut pointing_at_return_type = false;
         let mut fn_output = None;
 
+        let parent_id = fcx.tcx.hir().get_parent_node(id);
+        let parent = fcx.tcx.hir().get(parent_id);
+        if let Some(expr) = expression
+            && let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(_, _, body_id, ..), .. }) = parent
+            && !matches!(fcx.tcx.hir().get(body_id.hir_id), hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Block(..), .. }))
+        {
+            fcx.suggest_missing_semicolon(&mut err, expr, expected, true);
+        }
         // Verify that this is a tail expression of a function, otherwise the
         // label pointing out the cause for the type coercion will be wrong
         // as prior return coercions would not be relevant (#57664).
-        let parent_id = fcx.tcx.hir().get_parent_node(id);
-        let fn_decl = if let Some((expr, blk_id)) = expression {
+        let fn_decl = if let (Some(expr), Some(blk_id)) = (expression, blk_id) {
             pointing_at_return_type =
                 fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id);
-            let parent = fcx.tcx.hir().get(parent_id);
             if let (Some(cond_expr), true, false) = (
                 fcx.tcx.hir().get_if_cause(expr.hir_id),
                 expected.is_unit(),
@@ -1607,7 +1616,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         };
 
         if let Some((fn_decl, can_suggest)) = fn_decl {
-            if expression.is_none() {
+            if blk_id.is_none() {
                 pointing_at_return_type |= fcx.suggest_missing_return_type(
                     &mut err,
                     &fn_decl,
@@ -1625,8 +1634,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         let parent_id = fcx.tcx.hir().get_parent_item(id);
         let parent_item = fcx.tcx.hir().get_by_def_id(parent_id);
 
-        if let (Some((expr, _)), Some((fn_decl, _, _))) =
-            (expression, fcx.get_node_fn_decl(parent_item))
+        if let (Some(expr), Some(_), Some((fn_decl, _, _))) =
+            (expression, blk_id, fcx.get_node_fn_decl(parent_item))
         {
             fcx.suggest_missing_break_or_return_expr(
                 &mut err,
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
index bd58a675448..76add2fb9c2 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -46,12 +46,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         blk_id: hir::HirId,
     ) -> bool {
         let expr = expr.peel_drop_temps();
-        // If the expression is from an external macro, then do not suggest
-        // adding a semicolon, because there's nowhere to put it.
-        // See issue #81943.
-        if expr.can_have_side_effects() && !in_external_macro(self.tcx.sess, expr.span) {
-            self.suggest_missing_semicolon(err, expr, expected);
-        }
+        self.suggest_missing_semicolon(err, expr, expected, false);
         let mut pointing_at_return_type = false;
         if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
             let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap();
@@ -473,11 +468,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// This routine checks if the return expression in a block would make sense on its own as a
     /// statement and the return type has been left as default or has been specified as `()`. If so,
     /// it suggests adding a semicolon.
-    fn suggest_missing_semicolon(
+    ///
+    /// If the expression is the expression of a closure without block (`|| expr`), a
+    /// block is needed to be added too (`|| { expr; }`). This is denoted by `needs_block`.
+    pub fn suggest_missing_semicolon(
         &self,
         err: &mut Diagnostic,
         expression: &'tcx hir::Expr<'tcx>,
         expected: Ty<'tcx>,
+        needs_block: bool,
     ) {
         if expected.is_unit() {
             // `BlockTailExpression` only relevant if the tail expr would be
@@ -489,14 +488,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 | ExprKind::If(..)
                 | ExprKind::Match(..)
                 | ExprKind::Block(..)
-                    if expression.can_have_side_effects() =>
+                    if expression.can_have_side_effects()
+                        // If the expression is from an external macro, then do not suggest
+                        // adding a semicolon, because there's nowhere to put it.
+                        // See issue #81943.
+                        && !in_external_macro(self.tcx.sess, expression.span) =>
                 {
-                    err.span_suggestion(
-                        expression.span.shrink_to_hi(),
-                        "consider using a semicolon here",
-                        ";".to_string(),
-                        Applicability::MachineApplicable,
-                    );
+                    if needs_block {
+                        err.multipart_suggestion(
+                            "consider using a semicolon here",
+                            vec![
+                                (expression.span.shrink_to_lo(), "{ ".to_owned()),
+                                (expression.span.shrink_to_hi(), "; }".to_owned()),
+                            ],
+                            Applicability::MachineApplicable,
+                        );
+                    } else {
+                        err.span_suggestion(
+                            expression.span.shrink_to_hi(),
+                            "consider using a semicolon here",
+                            ";".to_string(),
+                            Applicability::MachineApplicable,
+                        );
+                    }
                 }
                 _ => (),
             }
diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs
index d9c9f2920b0..a7f736fed14 100644
--- a/compiler/rustc_typeck/src/errors.rs
+++ b/compiler/rustc_typeck/src/errors.rs
@@ -277,7 +277,7 @@ impl<'a> SessionDiagnostic<'a> for MissingTypeParams {
                 .join(", "),
         );
 
-        err.span_label(self.def_span, rustc_errors::fluent::typeck::missing_type_params_label);
+        err.span_label(self.def_span, rustc_errors::fluent::typeck::label);
 
         let mut suggested = false;
         if let (Ok(snippet), true) = (
@@ -295,7 +295,7 @@ impl<'a> SessionDiagnostic<'a> for MissingTypeParams {
                 // least we can clue them to the correct syntax `Iterator<Type>`.
                 err.span_suggestion(
                     self.span,
-                    rustc_errors::fluent::typeck::missing_type_params_suggestion,
+                    rustc_errors::fluent::typeck::suggestion,
                     format!("{}<{}>", snippet, self.missing_type_params.join(", ")),
                     Applicability::HasPlaceholders,
                 );
@@ -303,13 +303,10 @@ impl<'a> SessionDiagnostic<'a> for MissingTypeParams {
             }
         }
         if !suggested {
-            err.span_label(
-                self.span,
-                rustc_errors::fluent::typeck::missing_type_params_no_suggestion_label,
-            );
+            err.span_label(self.span, rustc_errors::fluent::typeck::no_suggestion_label);
         }
 
-        err.note(rustc_errors::fluent::typeck::missing_type_params_note);
+        err.note(rustc_errors::fluent::typeck::note);
         err
     }
 }