diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index f6937dc145d..b79d7441aca 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -82,12 +82,6 @@ const_eval_double_storage_live =
 const_eval_dyn_call_not_a_method =
     `dyn` call trying to call something that is not a method
 
-const_eval_dyn_call_vtable_mismatch =
-    `dyn` call on a pointer whose vtable does not match its type
-
-const_eval_dyn_star_call_vtable_mismatch =
-    `dyn*` call on a pointer whose vtable does not match its type
-
 const_eval_error = {$error_kind ->
     [static] could not evaluate static initializer
     [const] evaluation of constant value failed
@@ -192,6 +186,8 @@ const_eval_invalid_uninit_bytes_unknown =
 const_eval_invalid_vtable_pointer =
     using {$pointer} as vtable pointer but it does not point to a vtable
 
+const_eval_invalid_vtable_trait =
+    using vtable for trait `{$vtable_trait}` but trait `{$expected_trait}` was expected
 
 const_eval_live_drop =
     destructor of `{$dropped_ty}` cannot be evaluated at compile-time
@@ -401,9 +397,6 @@ const_eval_unterminated_c_string =
 const_eval_unwind_past_top =
     unwinding past the topmost frame of the stack
 
-const_eval_upcast_mismatch =
-    upcast on a pointer whose vtable does not match its type
-
 ## The `front_matter`s here refer to either `const_eval_front_matter_invalid_value` or `const_eval_front_matter_invalid_value_with_path`.
 ## (We'd love to sort this differently to make that more clear but tidy won't let us...)
 const_eval_validation_box_to_static = {$front_matter}: encountered a box pointing to a static variable in a constant
@@ -450,6 +443,7 @@ const_eval_validation_invalid_fn_ptr = {$front_matter}: encountered {$value}, bu
 const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object
 const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object
 const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer
+const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wide pointer vtable: expected `{$ref_trait}`, but encountered `{$vtable_trait}`
 const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory
 const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!`
 const_eval_validation_null_box = {$front_matter}: encountered a null box
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index a60cedd6500..90d4f1168e4 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -498,6 +498,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
             InvalidTag(_) => const_eval_invalid_tag,
             InvalidFunctionPointer(_) => const_eval_invalid_function_pointer,
             InvalidVTablePointer(_) => const_eval_invalid_vtable_pointer,
+            InvalidVTableTrait { .. } => const_eval_invalid_vtable_trait,
             InvalidStr(_) => const_eval_invalid_str,
             InvalidUninitBytes(None) => const_eval_invalid_uninit_bytes_unknown,
             InvalidUninitBytes(Some(_)) => const_eval_invalid_uninit_bytes,
@@ -537,6 +538,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
             | DeadLocal
             | UninhabitedEnumVariantWritten(_)
             | UninhabitedEnumVariantRead(_) => {}
+
             BoundsCheckFailed { len, index } => {
                 diag.arg("len", len);
                 diag.arg("index", index);
@@ -544,6 +546,13 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
             UnterminatedCString(ptr) | InvalidFunctionPointer(ptr) | InvalidVTablePointer(ptr) => {
                 diag.arg("pointer", ptr);
             }
+            InvalidVTableTrait { expected_trait, vtable_trait } => {
+                diag.arg("expected_trait", expected_trait.to_string());
+                diag.arg(
+                    "vtable_trait",
+                    vtable_trait.map(|t| t.to_string()).unwrap_or_else(|| format!("<trivial>")),
+                );
+            }
             PointerUseAfterFree(alloc_id, msg) => {
                 diag.arg("alloc_id", alloc_id)
                     .arg("bad_pointer_message", bad_pointer_message(msg, dcx));
@@ -634,6 +643,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
             UninhabitedEnumVariant => const_eval_validation_uninhabited_enum_variant,
             Uninit { .. } => const_eval_validation_uninit,
             InvalidVTablePtr { .. } => const_eval_validation_invalid_vtable_ptr,
+            InvalidMetaWrongTrait { .. } => const_eval_validation_invalid_vtable_trait,
             InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Box } => {
                 const_eval_validation_invalid_box_slice_meta
             }
@@ -773,6 +783,13 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
             DanglingPtrNoProvenance { pointer, .. } => {
                 err.arg("pointer", pointer);
             }
+            InvalidMetaWrongTrait { expected_trait: ref_trait, vtable_trait } => {
+                err.arg("ref_trait", ref_trait.to_string());
+                err.arg(
+                    "vtable_trait",
+                    vtable_trait.map(|t| t.to_string()).unwrap_or_else(|| format!("<trivial>")),
+                );
+            }
             NullPtr { .. }
             | PtrToStatic { .. }
             | ConstRefToMutable
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index 9447d18fe8c..76e59ea9055 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -393,6 +393,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let val = self.read_immediate(src)?;
                 if data_a.principal() == data_b.principal() {
                     // A NOP cast that doesn't actually change anything, should be allowed even with mismatching vtables.
+                    // (But currently mismatching vtables violate the validity invariant so UB is triggered anyway.)
                     return self.write_immediate(*val, dest);
                 }
                 let (old_data, old_vptr) = val.to_scalar_pair();
@@ -400,7 +401,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let old_vptr = old_vptr.to_pointer(self)?;
                 let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?;
                 if old_trait != data_a.principal() {
-                    throw_ub_custom!(fluent::const_eval_upcast_mismatch);
+                    throw_ub!(InvalidVTableTrait {
+                        expected_trait: data_a,
+                        vtable_trait: old_trait,
+                    });
                 }
                 let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?;
                 self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 8364a5a8d18..e5241f1ba19 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -1020,16 +1020,20 @@ where
     pub(super) fn unpack_dyn_trait(
         &self,
         mplace: &MPlaceTy<'tcx, M::Provenance>,
+        expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
     ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, Pointer<Option<M::Provenance>>)> {
         assert!(
             matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)),
             "`unpack_dyn_trait` only makes sense on `dyn*` types"
         );
         let vtable = mplace.meta().unwrap_meta().to_pointer(self)?;
-        let (ty, _) = self.get_ptr_vtable(vtable)?;
-        let layout = self.layout_of(ty)?;
+        let (ty, vtable_trait) = self.get_ptr_vtable(vtable)?;
+        if expected_trait.principal() != vtable_trait {
+            throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait });
+        }
         // This is a kind of transmute, from a place with unsized type and metadata to
         // a place with sized type and no metadata.
+        let layout = self.layout_of(ty)?;
         let mplace =
             MPlaceTy { mplace: MemPlace { meta: MemPlaceMeta::None, ..mplace.mplace }, layout };
         Ok((mplace, vtable))
@@ -1040,6 +1044,7 @@ where
     pub(super) fn unpack_dyn_star<P: Projectable<'tcx, M::Provenance>>(
         &self,
         val: &P,
+        expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
     ) -> InterpResult<'tcx, (P, Pointer<Option<M::Provenance>>)> {
         assert!(
             matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)),
@@ -1048,10 +1053,12 @@ where
         let data = self.project_field(val, 0)?;
         let vtable = self.project_field(val, 1)?;
         let vtable = self.read_pointer(&vtable.to_op(self)?)?;
-        let (ty, _) = self.get_ptr_vtable(vtable)?;
+        let (ty, vtable_trait) = self.get_ptr_vtable(vtable)?;
+        if expected_trait.principal() != vtable_trait {
+            throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait });
+        }
+        // `data` is already the right thing but has the wrong type. So we transmute it.
         let layout = self.layout_of(ty)?;
-        // `data` is already the right thing but has the wrong type. So we transmute it, by
-        // projecting with offset 0.
         let data = data.transmute(layout, self)?;
         Ok((data, vtable))
     }
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index 908c4da71f1..9c31532a9ce 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -803,11 +803,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let (vptr, dyn_ty, adjusted_receiver) = if let ty::Dynamic(data, _, ty::DynStar) =
                     receiver_place.layout.ty.kind()
                 {
-                    let (recv, vptr) = self.unpack_dyn_star(&receiver_place)?;
-                    let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
-                    if dyn_trait != data.principal() {
-                        throw_ub_custom!(fluent::const_eval_dyn_star_call_vtable_mismatch);
-                    }
+                    let (recv, vptr) = self.unpack_dyn_star(&receiver_place, data)?;
+                    let (dyn_ty, _dyn_trait) = self.get_ptr_vtable(vptr)?;
 
                     (vptr, dyn_ty, recv.ptr())
                 } else {
@@ -829,7 +826,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     let vptr = receiver_place.meta().unwrap_meta().to_pointer(self)?;
                     let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
                     if dyn_trait != data.principal() {
-                        throw_ub_custom!(fluent::const_eval_dyn_call_vtable_mismatch);
+                        throw_ub!(InvalidVTableTrait {
+                            expected_trait: data,
+                            vtable_trait: dyn_trait,
+                        });
                     }
 
                     // It might be surprising that we use a pointer as the receiver even if this
@@ -939,13 +939,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let place = self.force_allocation(place)?;
 
         let place = match place.layout.ty.kind() {
-            ty::Dynamic(_, _, ty::Dyn) => {
+            ty::Dynamic(data, _, ty::Dyn) => {
                 // Dropping a trait object. Need to find actual drop fn.
-                self.unpack_dyn_trait(&place)?.0
+                self.unpack_dyn_trait(&place, data)?.0
             }
-            ty::Dynamic(_, _, ty::DynStar) => {
+            ty::Dynamic(data, _, ty::DynStar) => {
                 // Dropping a `dyn*`. Need to find actual drop fn.
-                self.unpack_dyn_star(&place)?.0
+                self.unpack_dyn_star(&place, data)?.0
             }
             _ => {
                 debug_assert_eq!(
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index b8a1733e45a..14566719ccd 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -339,16 +339,22 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
     ) -> InterpResult<'tcx> {
         let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env);
         match tail.kind() {
-            ty::Dynamic(_, _, ty::Dyn) => {
+            ty::Dynamic(data, _, ty::Dyn) => {
                 let vtable = meta.unwrap_meta().to_pointer(self.ecx)?;
                 // Make sure it is a genuine vtable pointer.
-                let (_ty, _trait) = try_validation!(
+                let (_dyn_ty, dyn_trait) = try_validation!(
                     self.ecx.get_ptr_vtable(vtable),
                     self.path,
                     Ub(DanglingIntPointer(..) | InvalidVTablePointer(..)) =>
                         InvalidVTablePtr { value: format!("{vtable}") }
                 );
-                // FIXME: check if the type/trait match what ty::Dynamic says?
+                // Make sure it is for the right trait.
+                if dyn_trait != data.principal() {
+                    throw_validation_failure!(
+                        self.path,
+                        InvalidMetaWrongTrait { expected_trait: data, vtable_trait: dyn_trait }
+                    );
+                }
             }
             ty::Slice(..) | ty::Str => {
                 let _len = meta.unwrap_meta().to_target_usize(self.ecx)?;
@@ -933,7 +939,16 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                 }
             }
             _ => {
-                self.walk_value(op)?; // default handler
+                // default handler
+                try_validation!(
+                    self.walk_value(op),
+                    self.path,
+                    // It's not great to catch errors here, since we can't give a very good path,
+                    // but it's better than ICEing.
+                    Ub(InvalidVTableTrait { expected_trait, vtable_trait }) => {
+                        InvalidMetaWrongTrait { expected_trait, vtable_trait: *vtable_trait }
+                    },
+                );
             }
         }
 
diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs
index 0e824f3f592..84557b8e2d6 100644
--- a/compiler/rustc_const_eval/src/interpret/visitor.rs
+++ b/compiler/rustc_const_eval/src/interpret/visitor.rs
@@ -88,22 +88,22 @@ pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
         // Special treatment for special types, where the (static) layout is not sufficient.
         match *ty.kind() {
             // If it is a trait object, switch to the real type that was used to create it.
-            ty::Dynamic(_, _, ty::Dyn) => {
+            ty::Dynamic(data, _, ty::Dyn) => {
                 // Dyn types. This is unsized, and the actual dynamic type of the data is given by the
                 // vtable stored in the place metadata.
                 // unsized values are never immediate, so we can assert_mem_place
                 let op = v.to_op(self.ecx())?;
                 let dest = op.assert_mem_place();
-                let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.0;
+                let inner_mplace = self.ecx().unpack_dyn_trait(&dest, data)?.0;
                 trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout);
                 // recurse with the inner type
                 return self.visit_field(v, 0, &inner_mplace.into());
             }
-            ty::Dynamic(_, _, ty::DynStar) => {
+            ty::Dynamic(data, _, ty::DynStar) => {
                 // DynStar types. Very different from a dyn type (but strangely part of the
                 // same variant in `TyKind`): These are pairs where the 2nd component is the
                 // vtable, and the first component is the data (which must be ptr-sized).
-                let data = self.ecx().unpack_dyn_star(v)?.0;
+                let data = self.ecx().unpack_dyn_star(v, data)?.0;
                 return self.visit_field(v, 0, &data);
             }
             // Slices do not need special handling here: they have `Array` field
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 8b974a461d4..0c89e186c47 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -1769,13 +1769,13 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
 }
 
 declare_lint! {
-    /// The `keyword_idents` lint detects edition keywords being used as an
+    /// The `keyword_idents_2018` lint detects edition keywords being used as an
     /// identifier.
     ///
     /// ### Example
     ///
     /// ```rust,edition2015,compile_fail
-    /// #![deny(keyword_idents)]
+    /// #![deny(keyword_idents_2018)]
     /// // edition 2015
     /// fn dyn() {}
     /// ```
@@ -1804,7 +1804,7 @@ declare_lint! {
     /// [editions]: https://doc.rust-lang.org/edition-guide/
     /// [raw identifier]: https://doc.rust-lang.org/reference/identifiers.html
     /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
-    pub KEYWORD_IDENTS,
+    pub KEYWORD_IDENTS_2018,
     Allow,
     "detects edition keywords being used as an identifier",
     @future_incompatible = FutureIncompatibleInfo {
@@ -1813,9 +1813,54 @@ declare_lint! {
     };
 }
 
+declare_lint! {
+    /// The `keyword_idents_2024` lint detects edition keywords being used as an
+    /// identifier.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,edition2015,compile_fail
+    /// #![deny(keyword_idents_2024)]
+    /// // edition 2015
+    /// fn gen() {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Rust [editions] allow the language to evolve without breaking
+    /// backwards compatibility. This lint catches code that uses new keywords
+    /// that are added to the language that are used as identifiers (such as a
+    /// variable name, function name, etc.). If you switch the compiler to a
+    /// new edition without updating the code, then it will fail to compile if
+    /// you are using a new keyword as an identifier.
+    ///
+    /// You can manually change the identifiers to a non-keyword, or use a
+    /// [raw identifier], for example `r#gen`, to transition to a new edition.
+    ///
+    /// This lint solves the problem automatically. It is "allow" by default
+    /// because the code is perfectly valid in older editions. The [`cargo
+    /// fix`] tool with the `--edition` flag will switch this lint to "warn"
+    /// and automatically apply the suggested fix from the compiler (which is
+    /// to use a raw identifier). This provides a completely automated way to
+    /// update old code for a new edition.
+    ///
+    /// [editions]: https://doc.rust-lang.org/edition-guide/
+    /// [raw identifier]: https://doc.rust-lang.org/reference/identifiers.html
+    /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
+    pub KEYWORD_IDENTS_2024,
+    Allow,
+    "detects edition keywords being used as an identifier",
+    @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
+        reference: "issue #49716 <https://github.com/rust-lang/rust/issues/49716>",
+    };
+}
+
 declare_lint_pass!(
     /// Check for uses of edition keywords used as an identifier.
-    KeywordIdents => [KEYWORD_IDENTS]
+    KeywordIdents => [KEYWORD_IDENTS_2018, KEYWORD_IDENTS_2024]
 );
 
 struct UnderMacro(bool);
@@ -1841,42 +1886,39 @@ impl KeywordIdents {
         UnderMacro(under_macro): UnderMacro,
         ident: Ident,
     ) {
-        let next_edition = match cx.sess().edition() {
-            Edition::Edition2015 => {
-                match ident.name {
-                    kw::Async | kw::Await | kw::Try => Edition::Edition2018,
+        let (lint, edition) = match ident.name {
+            kw::Async | kw::Await | kw::Try => (KEYWORD_IDENTS_2018, Edition::Edition2018),
 
-                    // rust-lang/rust#56327: Conservatively do not
-                    // attempt to report occurrences of `dyn` within
-                    // macro definitions or invocations, because `dyn`
-                    // can legitimately occur as a contextual keyword
-                    // in 2015 code denoting its 2018 meaning, and we
-                    // do not want rustfix to inject bugs into working
-                    // code by rewriting such occurrences.
-                    //
-                    // But if we see `dyn` outside of a macro, we know
-                    // its precise role in the parsed AST and thus are
-                    // assured this is truly an attempt to use it as
-                    // an identifier.
-                    kw::Dyn if !under_macro => Edition::Edition2018,
+            // rust-lang/rust#56327: Conservatively do not
+            // attempt to report occurrences of `dyn` within
+            // macro definitions or invocations, because `dyn`
+            // can legitimately occur as a contextual keyword
+            // in 2015 code denoting its 2018 meaning, and we
+            // do not want rustfix to inject bugs into working
+            // code by rewriting such occurrences.
+            //
+            // But if we see `dyn` outside of a macro, we know
+            // its precise role in the parsed AST and thus are
+            // assured this is truly an attempt to use it as
+            // an identifier.
+            kw::Dyn if !under_macro => (KEYWORD_IDENTS_2018, Edition::Edition2018),
 
-                    _ => return,
-                }
-            }
+            kw::Gen => (KEYWORD_IDENTS_2024, Edition::Edition2024),
 
-            // There are no new keywords yet for the 2018 edition and beyond.
             _ => return,
         };
 
         // Don't lint `r#foo`.
-        if cx.sess().psess.raw_identifier_spans.contains(ident.span) {
+        if ident.span.edition() >= edition
+            || cx.sess().psess.raw_identifier_spans.contains(ident.span)
+        {
             return;
         }
 
         cx.emit_span_lint(
-            KEYWORD_IDENTS,
+            lint,
             ident.span,
-            BuiltinKeywordIdents { kw: ident, next: next_edition, suggestion: ident.span },
+            BuiltinKeywordIdents { kw: ident, next: edition, suggestion: ident.span },
         );
     }
 }
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 086c3834410..e00d8c1e6bd 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -312,6 +312,8 @@ fn register_builtins(store: &mut LintStore) {
                                        // MACRO_USE_EXTERN_CRATE
     );
 
+    add_lint_group!("keyword_idents", KEYWORD_IDENTS_2018, KEYWORD_IDENTS_2024);
+
     add_lint_group!(
         "refining_impl_trait",
         REFINING_IMPL_TRAIT_REACHABLE,
@@ -324,7 +326,7 @@ fn register_builtins(store: &mut LintStore) {
     store.register_renamed("bare_trait_object", "bare_trait_objects");
     store.register_renamed("unstable_name_collision", "unstable_name_collisions");
     store.register_renamed("unused_doc_comment", "unused_doc_comments");
-    store.register_renamed("async_idents", "keyword_idents");
+    store.register_renamed("async_idents", "keyword_idents_2018");
     store.register_renamed("exceeding_bitshifts", "arithmetic_overflow");
     store.register_renamed("redundant_semicolon", "redundant_semicolons");
     store.register_renamed("overlapping_patterns", "overlapping_range_endpoints");
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 65ce1cd8f50..a3d16d4f097 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -2,7 +2,7 @@ use super::{AllocId, AllocRange, ConstAllocation, Pointer, Scalar};
 
 use crate::error;
 use crate::mir::{ConstAlloc, ConstValue};
-use crate::ty::{layout, tls, Ty, TyCtxt, ValTree};
+use crate::ty::{self, layout, tls, Ty, TyCtxt, ValTree};
 
 use rustc_ast_ir::Mutability;
 use rustc_data_structures::sync::Lock;
@@ -344,6 +344,11 @@ pub enum UndefinedBehaviorInfo<'tcx> {
     InvalidFunctionPointer(Pointer<AllocId>),
     /// Using a pointer-not-to-a-vtable as vtable pointer.
     InvalidVTablePointer(Pointer<AllocId>),
+    /// Using a vtable for the wrong trait.
+    InvalidVTableTrait {
+        expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+        vtable_trait: Option<ty::PolyExistentialTraitRef<'tcx>>,
+    },
     /// Using a string that is not valid UTF-8,
     InvalidStr(std::str::Utf8Error),
     /// Using uninitialized data where it is not allowed.
@@ -414,34 +419,86 @@ impl From<PointerKind> for ExpectedKind {
 
 #[derive(Debug)]
 pub enum ValidationErrorKind<'tcx> {
-    PointerAsInt { expected: ExpectedKind },
+    PointerAsInt {
+        expected: ExpectedKind,
+    },
     PartialPointer,
-    PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> },
-    PtrToStatic { ptr_kind: PointerKind },
+    PtrToUninhabited {
+        ptr_kind: PointerKind,
+        ty: Ty<'tcx>,
+    },
+    PtrToStatic {
+        ptr_kind: PointerKind,
+    },
     ConstRefToMutable,
     ConstRefToExtern,
     MutableRefToImmutable,
     UnsafeCellInImmutable,
     NullFnPtr,
     NeverVal,
-    NullablePtrOutOfRange { range: WrappingRange, max_value: u128 },
-    PtrOutOfRange { range: WrappingRange, max_value: u128 },
-    OutOfRange { value: String, range: WrappingRange, max_value: u128 },
-    UninhabitedVal { ty: Ty<'tcx> },
-    InvalidEnumTag { value: String },
+    NullablePtrOutOfRange {
+        range: WrappingRange,
+        max_value: u128,
+    },
+    PtrOutOfRange {
+        range: WrappingRange,
+        max_value: u128,
+    },
+    OutOfRange {
+        value: String,
+        range: WrappingRange,
+        max_value: u128,
+    },
+    UninhabitedVal {
+        ty: Ty<'tcx>,
+    },
+    InvalidEnumTag {
+        value: String,
+    },
     UninhabitedEnumVariant,
-    Uninit { expected: ExpectedKind },
-    InvalidVTablePtr { value: String },
-    InvalidMetaSliceTooLarge { ptr_kind: PointerKind },
-    InvalidMetaTooLarge { ptr_kind: PointerKind },
-    UnalignedPtr { ptr_kind: PointerKind, required_bytes: u64, found_bytes: u64 },
-    NullPtr { ptr_kind: PointerKind },
-    DanglingPtrNoProvenance { ptr_kind: PointerKind, pointer: String },
-    DanglingPtrOutOfBounds { ptr_kind: PointerKind },
-    DanglingPtrUseAfterFree { ptr_kind: PointerKind },
-    InvalidBool { value: String },
-    InvalidChar { value: String },
-    InvalidFnPtr { value: String },
+    Uninit {
+        expected: ExpectedKind,
+    },
+    InvalidVTablePtr {
+        value: String,
+    },
+    InvalidMetaWrongTrait {
+        expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+        vtable_trait: Option<ty::PolyExistentialTraitRef<'tcx>>,
+    },
+    InvalidMetaSliceTooLarge {
+        ptr_kind: PointerKind,
+    },
+    InvalidMetaTooLarge {
+        ptr_kind: PointerKind,
+    },
+    UnalignedPtr {
+        ptr_kind: PointerKind,
+        required_bytes: u64,
+        found_bytes: u64,
+    },
+    NullPtr {
+        ptr_kind: PointerKind,
+    },
+    DanglingPtrNoProvenance {
+        ptr_kind: PointerKind,
+        pointer: String,
+    },
+    DanglingPtrOutOfBounds {
+        ptr_kind: PointerKind,
+    },
+    DanglingPtrUseAfterFree {
+        ptr_kind: PointerKind,
+    },
+    InvalidBool {
+        value: String,
+    },
+    InvalidChar {
+        value: String,
+    },
+    InvalidFnPtr {
+        value: String,
+    },
 }
 
 /// Error information for when the program did something that might (or might not) be correct
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 6381bd190ac..897d6f5662f 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -338,6 +338,10 @@ impl<'tcx> SizeSkeleton<'tcx> {
                         debug_assert!(tail.has_non_region_param());
                         Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) })
                     }
+                    ty::Error(guar) => {
+                        // Fixes ICE #124031
+                        return Err(tcx.arena.alloc(LayoutError::ReferencesError(*guar)));
+                    }
                     _ => bug!(
                         "SizeSkeleton::compute({ty}): layout errored ({err:?}), yet \
                               tail `{tail}` is not a type parameter or a projection",
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index e2436759c22..32a0df2f619 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -230,6 +230,10 @@ parse_expected_struct_field = expected one of `,`, `:`, or `{"}"}`, found `{$tok
 
 parse_expected_trait_in_trait_impl_found_type = expected a trait, found type
 
+parse_expr_rarrow_call = `->` used for field access or method call
+    .suggestion = try using `.` instead
+    .help = the `.` operator will dereference the value if needed
+
 parse_extern_crate_name_with_dashes = crate name using dashes are not valid in `extern crate` statements
     .label = dash-separated idents are not valid
     .suggestion = if the original crate name uses dashes you need to use underscores in the code
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index eae2d904c35..ca8de4240f4 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -2988,3 +2988,12 @@ pub(crate) struct AsyncImpl {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(parse_expr_rarrow_call)]
+#[help]
+pub(crate) struct ExprRArrowCall {
+    #[primary_span]
+    #[suggestion(style = "short", applicability = "machine-applicable", code = ".")]
+    pub span: Span,
+}
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 00947a4c585..8a454610718 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -979,6 +979,12 @@ impl<'a> Parser<'a> {
                 // we are using noexpect here because we don't expect a `.` directly after a `return`
                 // which could be suggested otherwise
                 self.eat_noexpect(&token::Dot)
+            } else if self.token.kind == TokenKind::RArrow && self.may_recover() {
+                // Recovery for `expr->suffix`.
+                self.bump();
+                let span = self.prev_token.span;
+                self.dcx().emit_err(errors::ExprRArrowCall { span });
+                true
             } else {
                 self.eat(&token::Dot)
             };
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index f9e7ed9dcbb..053de2c673b 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -784,7 +784,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
 
                     match (evaluate(c1), evaluate(c2)) {
                         (Ok(c1), Ok(c2)) => {
-                            match selcx.infcx.at(&obligation.cause, obligation.param_env).eq(DefineOpaqueTypes::No,c1, c2)
+                            match selcx.infcx.at(&obligation.cause, obligation.param_env).eq(DefineOpaqueTypes::Yes,c1, c2)
                             {
                                 Ok(_) => (),
                                 Err(_) => return false,
diff --git a/library/std/src/sync/reentrant_lock/tests.rs b/library/std/src/sync/reentrant_lock/tests.rs
index d4c1d440c61..aeef0289d28 100644
--- a/library/std/src/sync/reentrant_lock/tests.rs
+++ b/library/std/src/sync/reentrant_lock/tests.rs
@@ -1,4 +1,4 @@
-use super::{ReentrantLock, ReentrantLockGuard};
+use super::ReentrantLock;
 use crate::cell::RefCell;
 use crate::sync::Arc;
 use crate::thread;
@@ -51,10 +51,3 @@ fn trylock_works() {
     .unwrap();
     let _lock3 = l.try_lock();
 }
-
-pub struct Answer<'a>(pub ReentrantLockGuard<'a, RefCell<u32>>);
-impl Drop for Answer<'_> {
-    fn drop(&mut self) {
-        *self.0.borrow_mut() = 42;
-    }
-}
diff --git a/src/tools/lint-docs/src/groups.rs b/src/tools/lint-docs/src/groups.rs
index 0c39f2fa601..73f5738469e 100644
--- a/src/tools/lint-docs/src/groups.rs
+++ b/src/tools/lint-docs/src/groups.rs
@@ -20,6 +20,10 @@ static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[
         "refining-impl-trait",
         "Detects refinement of `impl Trait` return types by trait implementations",
     ),
+    (
+        "keyword-idents",
+        "Lints that detect identifiers which will be come keywords in later editions",
+    ),
 ];
 
 type LintGroups = BTreeMap<String, BTreeSet<String>>;
diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs
index aca780e33f0..22ab576b077 100644
--- a/src/tools/lint-docs/src/lib.rs
+++ b/src/tools/lint-docs/src/lib.rs
@@ -25,6 +25,7 @@ static RENAMES: &[(Level, &[(&str, &str)])] = &[
             ("elided-lifetime-in-path", "elided-lifetimes-in-paths"),
             ("async-idents", "keyword-idents"),
             ("disjoint-capture-migration", "rust-2021-incompatible-closure-captures"),
+            ("keyword-idents", "keyword-idents-2018"),
             ("or-patterns-back-compat", "rust-2021-incompatible-or-patterns"),
         ],
     ),
diff --git a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs
index 13f913454dc..f71df9a1c90 100644
--- a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs
+++ b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs
@@ -1,3 +1,6 @@
+// Validation stops this too early.
+//@compile-flags: -Zmiri-disable-validation
+
 trait T1 {
     #[allow(dead_code)]
     fn method1(self: Box<Self>);
@@ -13,5 +16,5 @@ impl T1 for i32 {
 fn main() {
     let r = Box::new(0) as Box<dyn T1>;
     let r2: Box<dyn T2> = unsafe { std::mem::transmute(r) };
-    r2.method2(); //~ERROR: call on a pointer whose vtable does not match its type
+    r2.method2(); //~ERROR: using vtable for trait `T1` but trait `T2` was expected
 }
diff --git a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr
index 365186bcc4b..019a55bcdcb 100644
--- a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr
+++ b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: `dyn` call on a pointer whose vtable does not match its type
+error: Undefined Behavior: using vtable for trait `T1` but trait `T2` was expected
   --> $DIR/dyn-call-trait-mismatch.rs:LL:CC
    |
 LL |     r2.method2();
-   |     ^^^^^^^^^^^^ `dyn` call on a pointer whose vtable does not match its type
+   |     ^^^^^^^^^^^^ using vtable for trait `T1` but trait `T2` was expected
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
diff --git a/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.rs b/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.rs
new file mode 100644
index 00000000000..dff5a21c4c7
--- /dev/null
+++ b/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.rs
@@ -0,0 +1,15 @@
+// This upcast is currently forbidden because it involves an invalid value.
+// However, if in the future we relax the validity requirements for raw pointer vtables,
+// we could consider allowing this again -- the cast itself isn't doing anything wrong,
+// only the transmutes needed to set up the testcase are wrong.
+
+use std::fmt;
+
+fn main() {
+    // vtable_mismatch_nop_cast
+    let ptr: &dyn fmt::Display = &0;
+    let ptr: *const (dyn fmt::Debug + Send + Sync) = unsafe { std::mem::transmute(ptr) }; //~ERROR: wrong trait
+    // Even though the vtable is for the wrong trait, this cast doesn't actually change the needed
+    // vtable so it should still be allowed -- if we ever allow the line above.
+    let _ptr2 = ptr as *const dyn fmt::Debug;
+}
diff --git a/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.stderr b/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.stderr
new file mode 100644
index 00000000000..4165d5ea15d
--- /dev/null
+++ b/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug + std::marker::Send + std::marker::Sync`, but encountered `std::fmt::Display`
+  --> $DIR/dyn-upcast-nop-wrong-trait.rs:LL:CC
+   |
+LL |     let ptr: *const (dyn fmt::Debug + Send + Sync) = unsafe { std::mem::transmute(ptr) };
+   |                                                               ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug + std::marker::Send + std::marker::Sync`, but encountered `std::fmt::Display`
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/dyn-upcast-nop-wrong-trait.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs
index 982f33b0a31..1d6b6777032 100644
--- a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs
+++ b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs
@@ -1,3 +1,6 @@
+// Validation stops this too early.
+//@compile-flags: -Zmiri-disable-validation
+
 #![feature(trait_upcasting)]
 #![allow(incomplete_features)]
 
@@ -57,7 +60,7 @@ impl Baz for i32 {
 
 fn main() {
     let baz: &dyn Baz = &1;
-    let baz_fake: &dyn Bar = unsafe { std::mem::transmute(baz) };
-    let _err = baz_fake as &dyn Foo;
-    //~^ERROR: upcast on a pointer whose vtable does not match its type
+    let baz_fake: *const dyn Bar = unsafe { std::mem::transmute(baz) };
+    let _err = baz_fake as *const dyn Foo;
+    //~^ERROR: using vtable for trait `Baz` but trait `Bar` was expected
 }
diff --git a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr
index 8bac908b864..6a2415cf57e 100644
--- a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr
+++ b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: upcast on a pointer whose vtable does not match its type
+error: Undefined Behavior: using vtable for trait `Baz` but trait `Bar` was expected
   --> $DIR/dyn-upcast-trait-mismatch.rs:LL:CC
    |
-LL |     let _err = baz_fake as &dyn Foo;
-   |                ^^^^^^^^ upcast on a pointer whose vtable does not match its type
+LL |     let _err = baz_fake as *const dyn Foo;
+   |                ^^^^^^^^ using vtable for trait `Baz` but trait `Bar` was expected
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
diff --git a/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.rs b/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.rs
new file mode 100644
index 00000000000..9b1cefc4b1d
--- /dev/null
+++ b/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.rs
@@ -0,0 +1,12 @@
+use std::mem;
+
+// Make sure we notice the mismatch also if the difference is "only" in the generic
+// parameters of the trait.
+
+trait Trait<T> {}
+impl<T> Trait<T> for T {}
+
+fn main() {
+    let x: &dyn Trait<i32> = &0;
+    let _y: *const dyn Trait<u32> = unsafe { mem::transmute(x) }; //~ERROR: wrong trait
+}
diff --git a/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.stderr b/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.stderr
new file mode 100644
index 00000000000..1219f9f88cf
--- /dev/null
+++ b/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `Trait<u32>`, but encountered `Trait<i32>`
+  --> $DIR/wrong-dyn-trait-generic.rs:LL:CC
+   |
+LL |     let _y: *const dyn Trait<u32> = unsafe { mem::transmute(x) };
+   |                                              ^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `Trait<u32>`, but encountered `Trait<i32>`
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/wrong-dyn-trait-generic.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/validity/wrong-dyn-trait.rs b/src/tools/miri/tests/fail/validity/wrong-dyn-trait.rs
new file mode 100644
index 00000000000..d6049196f26
--- /dev/null
+++ b/src/tools/miri/tests/fail/validity/wrong-dyn-trait.rs
@@ -0,0 +1,6 @@
+use std::{fmt, mem};
+
+fn main() {
+    let x: &dyn Send = &0;
+    let _y: *const dyn fmt::Debug = unsafe { mem::transmute(x) }; //~ERROR: wrong trait
+}
diff --git a/src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr b/src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr
new file mode 100644
index 00000000000..e3503323b31
--- /dev/null
+++ b/src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `<trivial>`
+  --> $DIR/wrong-dyn-trait.rs:LL:CC
+   |
+LL |     let _y: *const dyn fmt::Debug = unsafe { mem::transmute(x) };
+   |                                              ^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `<trivial>`
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/wrong-dyn-trait.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs b/src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs
index 2491bda0917..adbf5df62cc 100644
--- a/src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs
+++ b/src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs
@@ -25,7 +25,7 @@ impl Foo<u32> for u32 {
 impl Bar for () {}
 
 unsafe fn round_trip_and_call<'a>(t: *const (dyn Foo<u32> + 'a)) -> u32 {
-    let foo_e: *const dyn Foo<u16> = t as *const _;
+    let foo_e: *const dyn Foo<u32> = t as *const _;
     let r_1 = foo_e as *mut dyn Foo<u32>;
 
     (&*r_1).foo(0)
diff --git a/src/tools/miri/tests/pass/dyn-upcast.rs b/src/tools/miri/tests/pass/dyn-upcast.rs
index 529b9c471d4..ddc4bdcf082 100644
--- a/src/tools/miri/tests/pass/dyn-upcast.rs
+++ b/src/tools/miri/tests/pass/dyn-upcast.rs
@@ -1,24 +1,26 @@
 #![feature(trait_upcasting)]
 #![allow(incomplete_features)]
 
+use std::fmt;
+
 fn main() {
     basic();
     diamond();
     struct_();
     replace_vptr();
-    vtable_mismatch_nop_cast();
+    vtable_nop_cast();
 }
 
-fn vtable_mismatch_nop_cast() {
-    let ptr: &dyn std::fmt::Display = &0;
-    // Even though the vtable is for the wrong trait, this cast doesn't actually change the needed
-    // vtable so it should still be allowed.
-    let ptr: *const (dyn std::fmt::Debug + Send + Sync) = unsafe { std::mem::transmute(ptr) };
-    let _ptr2 = ptr as *const dyn std::fmt::Debug;
+fn vtable_nop_cast() {
+    let ptr: &dyn fmt::Debug = &0;
+    // We transmute things around, but the principal trait does not change, so this is allowed.
+    let ptr: *const (dyn fmt::Debug + Send + Sync) = unsafe { std::mem::transmute(ptr) };
+    // This cast is a NOP and should be allowed.
+    let _ptr2 = ptr as *const dyn fmt::Debug;
 }
 
 fn basic() {
-    trait Foo: PartialEq<i32> + std::fmt::Debug + Send + Sync {
+    trait Foo: PartialEq<i32> + fmt::Debug + Send + Sync {
         fn a(&self) -> i32 {
             10
         }
@@ -67,7 +69,7 @@ fn basic() {
     }
 
     let baz: &dyn Baz = &1;
-    let _: &dyn std::fmt::Debug = baz;
+    let _: &dyn fmt::Debug = baz;
     assert_eq!(*baz, 1);
     assert_eq!(baz.a(), 100);
     assert_eq!(baz.b(), 200);
@@ -77,7 +79,7 @@ fn basic() {
     assert_eq!(baz.w(), 21);
 
     let bar: &dyn Bar = baz;
-    let _: &dyn std::fmt::Debug = bar;
+    let _: &dyn fmt::Debug = bar;
     assert_eq!(*bar, 1);
     assert_eq!(bar.a(), 100);
     assert_eq!(bar.b(), 200);
@@ -86,14 +88,14 @@ fn basic() {
     assert_eq!(bar.w(), 21);
 
     let foo: &dyn Foo = baz;
-    let _: &dyn std::fmt::Debug = foo;
+    let _: &dyn fmt::Debug = foo;
     assert_eq!(*foo, 1);
     assert_eq!(foo.a(), 100);
     assert_eq!(foo.z(), 11);
     assert_eq!(foo.y(), 12);
 
     let foo: &dyn Foo = bar;
-    let _: &dyn std::fmt::Debug = foo;
+    let _: &dyn fmt::Debug = foo;
     assert_eq!(*foo, 1);
     assert_eq!(foo.a(), 100);
     assert_eq!(foo.z(), 11);
@@ -101,7 +103,7 @@ fn basic() {
 }
 
 fn diamond() {
-    trait Foo: PartialEq<i32> + std::fmt::Debug + Send + Sync {
+    trait Foo: PartialEq<i32> + fmt::Debug + Send + Sync {
         fn a(&self) -> i32 {
             10
         }
@@ -166,7 +168,7 @@ fn diamond() {
     }
 
     let baz: &dyn Baz = &1;
-    let _: &dyn std::fmt::Debug = baz;
+    let _: &dyn fmt::Debug = baz;
     assert_eq!(*baz, 1);
     assert_eq!(baz.a(), 100);
     assert_eq!(baz.b(), 200);
@@ -178,7 +180,7 @@ fn diamond() {
     assert_eq!(baz.v(), 31);
 
     let bar1: &dyn Bar1 = baz;
-    let _: &dyn std::fmt::Debug = bar1;
+    let _: &dyn fmt::Debug = bar1;
     assert_eq!(*bar1, 1);
     assert_eq!(bar1.a(), 100);
     assert_eq!(bar1.b(), 200);
@@ -187,7 +189,7 @@ fn diamond() {
     assert_eq!(bar1.w(), 21);
 
     let bar2: &dyn Bar2 = baz;
-    let _: &dyn std::fmt::Debug = bar2;
+    let _: &dyn fmt::Debug = bar2;
     assert_eq!(*bar2, 1);
     assert_eq!(bar2.a(), 100);
     assert_eq!(bar2.c(), 300);
@@ -196,17 +198,17 @@ fn diamond() {
     assert_eq!(bar2.v(), 31);
 
     let foo: &dyn Foo = baz;
-    let _: &dyn std::fmt::Debug = foo;
+    let _: &dyn fmt::Debug = foo;
     assert_eq!(*foo, 1);
     assert_eq!(foo.a(), 100);
 
     let foo: &dyn Foo = bar1;
-    let _: &dyn std::fmt::Debug = foo;
+    let _: &dyn fmt::Debug = foo;
     assert_eq!(*foo, 1);
     assert_eq!(foo.a(), 100);
 
     let foo: &dyn Foo = bar2;
-    let _: &dyn std::fmt::Debug = foo;
+    let _: &dyn fmt::Debug = foo;
     assert_eq!(*foo, 1);
     assert_eq!(foo.a(), 100);
 }
@@ -215,7 +217,7 @@ fn struct_() {
     use std::rc::Rc;
     use std::sync::Arc;
 
-    trait Foo: PartialEq<i32> + std::fmt::Debug + Send + Sync {
+    trait Foo: PartialEq<i32> + fmt::Debug + Send + Sync {
         fn a(&self) -> i32 {
             10
         }
diff --git a/tests/rustdoc-gui/notable-trait.goml b/tests/rustdoc-gui/notable-trait.goml
index 34fafe9a141..6ee810c5768 100644
--- a/tests/rustdoc-gui/notable-trait.goml
+++ b/tests/rustdoc-gui/notable-trait.goml
@@ -2,47 +2,70 @@
 include: "utils.goml"
 go-to: "file://" + |DOC_PATH| + "/test_docs/struct.NotableStructWithLongName.html"
 show-text: true
+
+define-function: (
+    "check-notable-tooltip-position",
+    [x, i_x],
+    block {
+        // Checking they have the same y position.
+        compare-elements-position: (
+            "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
+            "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
+            ["y"],
+        )
+        // Checking they don't have the same x position.
+        compare-elements-position-false: (
+            "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
+            "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
+            ["x"],
+        )
+        // The `i` should be *after* the type.
+        assert-position: (
+            "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
+            {"x": |x|},
+        )
+        assert-position: (
+            "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
+            {"x": |i_x|},
+        )
+    },
+)
+
+define-function: (
+    "check-notable-tooltip-position-complete",
+    [x, i_x, popover_x],
+    block {
+        call-function: ("check-notable-tooltip-position", {"x": |x|, "i_x": |i_x|})
+        assert-count: ("//*[@class='tooltip popover']", 0)
+        click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
+        assert-count: ("//*[@class='tooltip popover']", 1)
+        compare-elements-position-near: (
+            "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
+            "//*[@class='tooltip popover']",
+            {"y": 30}
+        )
+        compare-elements-position-false: (
+            "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
+            "//*[@class='tooltip popover']",
+            ["x"]
+        )
+        assert-position: (
+            "//*[@class='tooltip popover']",
+            {"x": |popover_x|}
+        )
+        click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
+        move-cursor-to: "//h1"
+        assert-count: ("//*[@class='tooltip popover']", 0)
+    },
+)
+
 // We start with a wide screen.
 set-window-size: (1100, 600)
-// Checking they have the same y position.
-compare-elements-position: (
-    "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    ["y"],
-)
-// Checking they don't have the same x position.
-compare-elements-position-false: (
-    "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    ["x"],
-)
-// The `i` should be *after* the type.
-assert-position: (
-    "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    {"x": 677},
-)
-assert-position: (
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    {"x": 955},
-)
-// The tooltip should be below the `i`
-// Also, clicking the tooltip should bring its text into the DOM
-assert-count: ("//*[@class='tooltip popover']", 0)
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
-assert-count: ("//*[@class='tooltip popover']", 1)
-compare-elements-position-near: (
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    "//*[@class='tooltip popover']",
-    {"y": 30}
-)
-compare-elements-position-false: (
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    "//*[@class='tooltip popover']",
-    ["x"]
-)
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
-move-cursor-to: "//h1"
-assert-count: ("//*[@class='tooltip popover']", 0)
+call-function: ("check-notable-tooltip-position-complete", {
+    "x": 677,
+    "i_x": 955,
+    "popover_x": 463,
+})
 
 // Now only the `i` should be on the next line.
 set-window-size: (1055, 600)
@@ -54,71 +77,18 @@ compare-elements-position-false: (
 
 // Now both the `i` and the struct name should be on the next line.
 set-window-size: (980, 600)
-// Checking they have the same y position.
-compare-elements-position: (
-    "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    ["y"],
-)
-// Checking they don't have the same x position.
-compare-elements-position-false: (
-    "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    ["x"],
-)
-// The `i` should be *after* the type.
-assert-position: (
-    "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    {"x": 245},
-)
-assert-position: (
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    {"x": 523},
-)
+call-function: ("check-notable-tooltip-position", {
+    "x": 245,
+    "i_x": 523,
+})
 
 // Checking on mobile now.
 set-window-size: (650, 600)
-// Checking they have the same y position.
-compare-elements-position: (
-    "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    ["y"],
-)
-// Checking they don't have the same x position.
-compare-elements-position-false: (
-    "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    ["x"],
-)
-// The `i` should be *after* the type.
-assert-position: (
-    "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
-    {"x": 15},
-)
-assert-position: (
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    {"x": 293},
-)
-// The tooltip should STILL be below `i`
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
-assert-count: ("//*[@class='tooltip popover']", 1)
-compare-elements-position-near: (
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    "//*[@class='tooltip popover']",
-    {"y": 30}
-)
-compare-elements-position-false: (
-    "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']",
-    "//*[@class='tooltip popover']",
-    ["x"]
-)
-assert-position: (
-    "//*[@class='tooltip popover']",
-    {"x": 0}
-)
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
-move-cursor-to: "//h1"
-assert-count: ("//*[@class='tooltip popover']", 0)
+call-function: ("check-notable-tooltip-position-complete", {
+    "x": 15,
+    "i_x": 293,
+    "popover_x": 0,
+})
 
 // Now check the colors.
 define-function: (
@@ -236,31 +206,31 @@ press-key: "Tab"
 assert-count: ("//*[@class='tooltip popover']", 0)
 assert: "#method\.create_an_iterator_from_read .tooltip:focus"
 
+define-function: (
+    "setup-popup",
+    [],
+    block {
+        store-window-property: {"scrollY": scroll}
+        click: "#method\.create_an_iterator_from_read .fn"
+        // We ensure that the scroll position changed.
+        assert-window-property-false: {"scrollY": |scroll|}
+        // Store the new position.
+        store-window-property: {"scrollY": scroll}
+        click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
+        wait-for: "//*[@class='tooltip popover']"
+        click: "#settings-menu a"
+    }
+)
+
 // Now we check that the focus isn't given back to the wrong item when opening
 // another popover.
-store-window-property: {"scrollY": scroll}
-click: "#method\.create_an_iterator_from_read .fn"
-// We ensure that the scroll position changed.
-assert-window-property-false: {"scrollY": |scroll|}
-// Store the new position.
-store-window-property: {"scrollY": scroll}
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
-wait-for: "//*[@class='tooltip popover']"
-click: "#settings-menu a"
+call-function: ("setup-popup", {})
 click: ".search-input"
 // We ensure we didn't come back to the previous focused item.
 assert-window-property-false: {"scrollY": |scroll|}
 
 // Same but with Escape handling.
-store-window-property: {"scrollY": scroll}
-click: "#method\.create_an_iterator_from_read .fn"
-// We ensure that the scroll position changed.
-assert-window-property-false: {"scrollY": |scroll|}
-// Store the new position.
-store-window-property: {"scrollY": scroll}
-click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
-wait-for: "//*[@class='tooltip popover']"
-click: "#settings-menu a"
+call-function: ("setup-popup", {})
 press-key: "Escape"
 // We ensure we didn't come back to the previous focused item.
 assert-window-property-false: {"scrollY": |scroll|}
diff --git a/tests/ui/async-await/await-keyword/2015-edition-error-various-positions.stderr b/tests/ui/async-await/await-keyword/2015-edition-error-various-positions.stderr
index d99967eb23c..8cea73f8651 100644
--- a/tests/ui/async-await/await-keyword/2015-edition-error-various-positions.stderr
+++ b/tests/ui/async-await/await-keyword/2015-edition-error-various-positions.stderr
@@ -11,6 +11,7 @@ note: the lint level is defined here
    |
 LL | #![deny(keyword_idents)]
    |         ^^^^^^^^^^^^^^
+   = note: `#[deny(keyword_idents_2018)]` implied by `#[deny(keyword_idents)]`
 
 error: `await` is a keyword in the 2018 edition
   --> $DIR/2015-edition-error-various-positions.rs:7:20
diff --git a/tests/ui/async-await/await-keyword/2015-edition-warning.stderr b/tests/ui/async-await/await-keyword/2015-edition-warning.stderr
index bf5c4d8d6aa..70b7fa52a19 100644
--- a/tests/ui/async-await/await-keyword/2015-edition-warning.stderr
+++ b/tests/ui/async-await/await-keyword/2015-edition-warning.stderr
@@ -11,6 +11,7 @@ note: the lint level is defined here
    |
 LL | #![deny(keyword_idents)]
    |         ^^^^^^^^^^^^^^
+   = note: `#[deny(keyword_idents_2018)]` implied by `#[deny(keyword_idents)]`
 
 error: `await` is a keyword in the 2018 edition
   --> $DIR/2015-edition-warning.rs:10:20
diff --git a/tests/ui/cast/cast-rfc0401-vtable-kinds.rs b/tests/ui/cast/cast-rfc0401-vtable-kinds.rs
index 410e15d024f..0d8f92f013f 100644
--- a/tests/ui/cast/cast-rfc0401-vtable-kinds.rs
+++ b/tests/ui/cast/cast-rfc0401-vtable-kinds.rs
@@ -17,7 +17,7 @@ impl Foo<u32> for u32 { fn foo(&self, _: u32) -> u32 { self+43 } }
 impl Bar for () {}
 
 unsafe fn round_trip_and_call<'a>(t: *const (dyn Foo<u32>+'a)) -> u32 {
-    let foo_e : *const dyn Foo<u16> = t as *const _;
+    let foo_e : *const dyn Foo<u32> = t as *const _;
     let r_1 = foo_e as *mut dyn Foo<u32>;
 
     (&*r_1).foo(0)
diff --git a/tests/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.stderr b/tests/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.stderr
index 89aded9134f..0d53fb024ac 100644
--- a/tests/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.stderr
+++ b/tests/ui/dyn-keyword/dyn-2015-edition-keyword-ident-lint.stderr
@@ -11,6 +11,7 @@ note: the lint level is defined here
    |
 LL | #![deny(keyword_idents)]
    |         ^^^^^^^^^^^^^^
+   = note: `#[deny(keyword_idents_2018)]` implied by `#[deny(keyword_idents)]`
 
 error: `dyn` is a keyword in the 2018 edition
   --> $DIR/dyn-2015-edition-keyword-ident-lint.rs:17:20
diff --git a/tests/crashes/124031.rs b/tests/ui/layout/ice-type-error-in-tail-124031.rs
similarity index 54%
rename from tests/crashes/124031.rs
rename to tests/ui/layout/ice-type-error-in-tail-124031.rs
index bdc66fbafe4..0a2be117403 100644
--- a/tests/crashes/124031.rs
+++ b/tests/ui/layout/ice-type-error-in-tail-124031.rs
@@ -1,10 +1,13 @@
-//@ known-bug: #124031
+// Regression test for issue #124031
+// Checks that we don't ICE when the tail
+// of an ADT has a type error
 
 trait Trait {
     type RefTarget;
 }
 
 impl Trait for () {}
+//~^ ERROR not all trait items implemented, missing: `RefTarget`
 
 struct Other {
     data: <() as Trait>::RefTarget,
diff --git a/tests/ui/layout/ice-type-error-in-tail-124031.stderr b/tests/ui/layout/ice-type-error-in-tail-124031.stderr
new file mode 100644
index 00000000000..57dc83f92df
--- /dev/null
+++ b/tests/ui/layout/ice-type-error-in-tail-124031.stderr
@@ -0,0 +1,12 @@
+error[E0046]: not all trait items implemented, missing: `RefTarget`
+  --> $DIR/ice-type-error-in-tail-124031.rs:9:1
+   |
+LL |     type RefTarget;
+   |     -------------- `RefTarget` from trait
+...
+LL | impl Trait for () {}
+   | ^^^^^^^^^^^^^^^^^ missing `RefTarget` in implementation
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0046`.
diff --git a/tests/ui/lint/lint-pre-expansion-extern-module.stderr b/tests/ui/lint/lint-pre-expansion-extern-module.stderr
index 8a6e1531d5f..32c76da98b5 100644
--- a/tests/ui/lint/lint-pre-expansion-extern-module.stderr
+++ b/tests/ui/lint/lint-pre-expansion-extern-module.stderr
@@ -6,8 +6,8 @@ LL | pub fn try() {}
    |
    = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
    = note: for more information, see issue #49716 <https://github.com/rust-lang/rust/issues/49716>
-   = note: `-W keyword-idents` implied by `-W rust-2018-compatibility`
-   = help: to override `-W rust-2018-compatibility` add `#[allow(keyword_idents)]`
+   = note: `-W keyword-idents-2018` implied by `-W rust-2018-compatibility`
+   = help: to override `-W rust-2018-compatibility` add `#[allow(keyword_idents_2018)]`
 
 warning: 1 warning emitted
 
diff --git a/tests/ui/parser/expr-rarrow-call.fixed b/tests/ui/parser/expr-rarrow-call.fixed
new file mode 100644
index 00000000000..9a05e20092d
--- /dev/null
+++ b/tests/ui/parser/expr-rarrow-call.fixed
@@ -0,0 +1,33 @@
+//@ run-rustfix
+#![allow(
+    dead_code,
+    unused_must_use
+)]
+
+struct Named {
+    foo: usize
+}
+
+struct Unnamed(usize);
+
+fn named_struct_field_access(named: &Named) {
+    named.foo; //~ ERROR `->` used for field access or method call
+}
+
+fn unnamed_struct_field_access(unnamed: &Unnamed) {
+    unnamed.0; //~ ERROR `->` used for field access or method call
+}
+
+fn tuple_field_access(t: &(u8, u8)) {
+    t.0; //~ ERROR `->` used for field access or method call
+    t.1; //~ ERROR `->` used for field access or method call
+}
+
+#[derive(Clone)]
+struct Foo;
+
+fn method_call(foo: &Foo) {
+    foo.clone(); //~ ERROR `->` used for field access or method call
+}
+
+fn main() {}
diff --git a/tests/ui/parser/expr-rarrow-call.rs b/tests/ui/parser/expr-rarrow-call.rs
new file mode 100644
index 00000000000..760b0f6f345
--- /dev/null
+++ b/tests/ui/parser/expr-rarrow-call.rs
@@ -0,0 +1,33 @@
+//@ run-rustfix
+#![allow(
+    dead_code,
+    unused_must_use
+)]
+
+struct Named {
+    foo: usize
+}
+
+struct Unnamed(usize);
+
+fn named_struct_field_access(named: &Named) {
+    named->foo; //~ ERROR `->` used for field access or method call
+}
+
+fn unnamed_struct_field_access(unnamed: &Unnamed) {
+    unnamed->0; //~ ERROR `->` used for field access or method call
+}
+
+fn tuple_field_access(t: &(u8, u8)) {
+    t->0; //~ ERROR `->` used for field access or method call
+    t->1; //~ ERROR `->` used for field access or method call
+}
+
+#[derive(Clone)]
+struct Foo;
+
+fn method_call(foo: &Foo) {
+    foo->clone(); //~ ERROR `->` used for field access or method call
+}
+
+fn main() {}
diff --git a/tests/ui/parser/expr-rarrow-call.stderr b/tests/ui/parser/expr-rarrow-call.stderr
new file mode 100644
index 00000000000..90082f98cb5
--- /dev/null
+++ b/tests/ui/parser/expr-rarrow-call.stderr
@@ -0,0 +1,42 @@
+error: `->` used for field access or method call
+  --> $DIR/expr-rarrow-call.rs:14:10
+   |
+LL |     named->foo;
+   |          ^^ help: try using `.` instead
+   |
+   = help: the `.` operator will dereference the value if needed
+
+error: `->` used for field access or method call
+  --> $DIR/expr-rarrow-call.rs:18:12
+   |
+LL |     unnamed->0;
+   |            ^^ help: try using `.` instead
+   |
+   = help: the `.` operator will dereference the value if needed
+
+error: `->` used for field access or method call
+  --> $DIR/expr-rarrow-call.rs:22:6
+   |
+LL |     t->0;
+   |      ^^ help: try using `.` instead
+   |
+   = help: the `.` operator will dereference the value if needed
+
+error: `->` used for field access or method call
+  --> $DIR/expr-rarrow-call.rs:23:6
+   |
+LL |     t->1;
+   |      ^^ help: try using `.` instead
+   |
+   = help: the `.` operator will dereference the value if needed
+
+error: `->` used for field access or method call
+  --> $DIR/expr-rarrow-call.rs:30:8
+   |
+LL |     foo->clone();
+   |        ^^ help: try using `.` instead
+   |
+   = help: the `.` operator will dereference the value if needed
+
+error: aborting due to 5 previous errors
+
diff --git a/tests/ui/parser/issues/issue-118530-ice.rs b/tests/ui/parser/issues/issue-118530-ice.rs
index e758e5af4d9..cf14eebec2b 100644
--- a/tests/ui/parser/issues/issue-118530-ice.rs
+++ b/tests/ui/parser/issues/issue-118530-ice.rs
@@ -3,8 +3,9 @@ fn bar() -> String {
     [1, 2, 3].iter() //~ ERROR expected `;`, found `#`
     #[feature]
     attr::fn bar() -> String { //~ ERROR expected identifier, found keyword `fn`
-    //~^ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `->`
+    //~^ ERROR expected one of `(`, `.`, `::`, `;`, `?`, `}`, or an operator, found `{`
     //~| ERROR expected `;`, found `bar`
+    //~| ERROR `->` used for field access or method call
     #[attr]
     [1, 2, 3].iter().map().collect::<String>()
     #[attr]
diff --git a/tests/ui/parser/issues/issue-118530-ice.stderr b/tests/ui/parser/issues/issue-118530-ice.stderr
index ef573fb7ba3..75c6a40c744 100644
--- a/tests/ui/parser/issues/issue-118530-ice.stderr
+++ b/tests/ui/parser/issues/issue-118530-ice.stderr
@@ -33,11 +33,19 @@ LL |     attr::fn bar() -> String {
    |             |
    |             help: add `;` here
 
-error: expected one of `.`, `;`, `?`, `}`, or an operator, found `->`
+error: `->` used for field access or method call
   --> $DIR/issue-118530-ice.rs:5:20
    |
 LL |     attr::fn bar() -> String {
-   |                    ^^ expected one of `.`, `;`, `?`, `}`, or an operator
+   |                    ^^ help: try using `.` instead
+   |
+   = help: the `.` operator will dereference the value if needed
 
-error: aborting due to 4 previous errors
+error: expected one of `(`, `.`, `::`, `;`, `?`, `}`, or an operator, found `{`
+  --> $DIR/issue-118530-ice.rs:5:30
+   |
+LL |     attr::fn bar() -> String {
+   |                              ^ expected one of 7 possible tokens
+
+error: aborting due to 5 previous errors
 
diff --git a/tests/ui/rust-2018/async-ident-allowed.stderr b/tests/ui/rust-2018/async-ident-allowed.stderr
index b413c0fd9ba..378c81d3c77 100644
--- a/tests/ui/rust-2018/async-ident-allowed.stderr
+++ b/tests/ui/rust-2018/async-ident-allowed.stderr
@@ -11,7 +11,7 @@ note: the lint level is defined here
    |
 LL | #![deny(rust_2018_compatibility)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^
-   = note: `#[deny(keyword_idents)]` implied by `#[deny(rust_2018_compatibility)]`
+   = note: `#[deny(keyword_idents_2018)]` implied by `#[deny(rust_2018_compatibility)]`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/rust-2018/async-ident.stderr b/tests/ui/rust-2018/async-ident.stderr
index d15250c54ec..5b8d8184f4f 100644
--- a/tests/ui/rust-2018/async-ident.stderr
+++ b/tests/ui/rust-2018/async-ident.stderr
@@ -11,6 +11,7 @@ note: the lint level is defined here
    |
 LL | #![deny(keyword_idents)]
    |         ^^^^^^^^^^^^^^
+   = note: `#[deny(keyword_idents_2018)]` implied by `#[deny(keyword_idents)]`
 
 error: `async` is a keyword in the 2018 edition
   --> $DIR/async-ident.rs:12:7
diff --git a/tests/ui/rust-2018/dyn-keyword.stderr b/tests/ui/rust-2018/dyn-keyword.stderr
index 6f9a5ddb14f..f8245bc88f5 100644
--- a/tests/ui/rust-2018/dyn-keyword.stderr
+++ b/tests/ui/rust-2018/dyn-keyword.stderr
@@ -11,6 +11,7 @@ note: the lint level is defined here
    |
 LL | #![deny(keyword_idents)]
    |         ^^^^^^^^^^^^^^
+   = note: `#[deny(keyword_idents_2018)]` implied by `#[deny(keyword_idents)]`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/rust-2018/try-ident.stderr b/tests/ui/rust-2018/try-ident.stderr
index 74015ac9da4..eaf4c235697 100644
--- a/tests/ui/rust-2018/try-ident.stderr
+++ b/tests/ui/rust-2018/try-ident.stderr
@@ -11,7 +11,7 @@ note: the lint level is defined here
    |
 LL | #![warn(rust_2018_compatibility)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^
-   = note: `#[warn(keyword_idents)]` implied by `#[warn(rust_2018_compatibility)]`
+   = note: `#[warn(keyword_idents_2018)]` implied by `#[warn(rust_2018_compatibility)]`
 
 warning: `try` is a keyword in the 2018 edition
   --> $DIR/try-ident.rs:12:4
diff --git a/tests/ui/rust-2018/try-macro.stderr b/tests/ui/rust-2018/try-macro.stderr
index 760378f0955..095c755539d 100644
--- a/tests/ui/rust-2018/try-macro.stderr
+++ b/tests/ui/rust-2018/try-macro.stderr
@@ -11,7 +11,7 @@ note: the lint level is defined here
    |
 LL | #![warn(rust_2018_compatibility)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^
-   = note: `#[warn(keyword_idents)]` implied by `#[warn(rust_2018_compatibility)]`
+   = note: `#[warn(keyword_idents_2018)]` implied by `#[warn(rust_2018_compatibility)]`
 
 warning: 1 warning emitted
 
diff --git a/tests/ui/rust-2024/gen-kw.e2015.stderr b/tests/ui/rust-2024/gen-kw.e2015.stderr
new file mode 100644
index 00000000000..b12363184b7
--- /dev/null
+++ b/tests/ui/rust-2024/gen-kw.e2015.stderr
@@ -0,0 +1,26 @@
+error: `gen` is a keyword in the 2024 edition
+  --> $DIR/gen-kw.rs:6:4
+   |
+LL | fn gen() {}
+   |    ^^^ help: you can use a raw identifier to stay compatible: `r#gen`
+   |
+   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024!
+   = note: for more information, see issue #49716 <https://github.com/rust-lang/rust/issues/49716>
+note: the lint level is defined here
+  --> $DIR/gen-kw.rs:4:9
+   |
+LL | #![deny(rust_2024_compatibility)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `#[deny(keyword_idents_2024)]` implied by `#[deny(rust_2024_compatibility)]`
+
+error: `gen` is a keyword in the 2024 edition
+  --> $DIR/gen-kw.rs:12:9
+   |
+LL |     let gen = r#gen;
+   |         ^^^ help: you can use a raw identifier to stay compatible: `r#gen`
+   |
+   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024!
+   = note: for more information, see issue #49716 <https://github.com/rust-lang/rust/issues/49716>
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/rust-2024/gen-kw.e2018.stderr b/tests/ui/rust-2024/gen-kw.e2018.stderr
new file mode 100644
index 00000000000..e10fc4c4512
--- /dev/null
+++ b/tests/ui/rust-2024/gen-kw.e2018.stderr
@@ -0,0 +1,26 @@
+error: `gen` is a keyword in the 2024 edition
+  --> $DIR/gen-kw.rs:6:4
+   |
+LL | fn gen() {}
+   |    ^^^ help: you can use a raw identifier to stay compatible: `r#gen`
+   |
+   = warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2024!
+   = note: for more information, see issue #49716 <https://github.com/rust-lang/rust/issues/49716>
+note: the lint level is defined here
+  --> $DIR/gen-kw.rs:4:9
+   |
+LL | #![deny(rust_2024_compatibility)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `#[deny(keyword_idents_2024)]` implied by `#[deny(rust_2024_compatibility)]`
+
+error: `gen` is a keyword in the 2024 edition
+  --> $DIR/gen-kw.rs:12:9
+   |
+LL |     let gen = r#gen;
+   |         ^^^ help: you can use a raw identifier to stay compatible: `r#gen`
+   |
+   = warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2024!
+   = note: for more information, see issue #49716 <https://github.com/rust-lang/rust/issues/49716>
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/rust-2024/gen-kw.rs b/tests/ui/rust-2024/gen-kw.rs
new file mode 100644
index 00000000000..3d2a3f95165
--- /dev/null
+++ b/tests/ui/rust-2024/gen-kw.rs
@@ -0,0 +1,16 @@
+//@ revisions: e2015 e2018
+//@[e2018] edition: 2018
+
+#![deny(rust_2024_compatibility)]
+
+fn gen() {}
+//~^ ERROR `gen` is a keyword in the 2024 edition
+//[e2015]~| WARNING this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024!
+//[e2018]~| WARNING this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2024!
+
+fn main() {
+    let gen = r#gen;
+    //~^ ERROR `gen` is a keyword in the 2024 edition
+    //[e2015]~| WARNING this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024!
+    //[e2018]~| WARNING this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2024!
+}