about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_error_messages/locales/en-US/parser.ftl2
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs14
-rw-r--r--compiler/rustc_parse/src/parser/item.rs4
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs613
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs10
-rw-r--r--library/core/src/time.rs98
-rw-r--r--library/std/src/env.rs2
-rw-r--r--library/std/src/os/fd/mod.rs13
-rw-r--r--library/std/src/os/fd/owned.rs6
-rw-r--r--library/std/src/os/fd/raw.rs18
-rw-r--r--library/std/src/os/mod.rs2
-rw-r--r--library/std/src/os/unix/io/fd.rs8
-rw-r--r--library/std/src/os/unix/io/mod.rs11
-rw-r--r--library/std/src/os/unix/io/raw.rs6
-rw-r--r--library/std/src/os/unix/io/tests.rs (renamed from library/std/src/os/unix/io/fd/tests.rs)0
-rw-r--r--library/std/src/os/wasi/io/mod.rs8
-rw-r--r--library/std/src/sys/unix/time.rs34
-rw-r--r--src/etc/natvis/libcore.natvis4
-rw-r--r--src/test/rustdoc-js-std/asrawfd.js6
-rw-r--r--src/test/ui/error-codes/E0585.stderr2
-rw-r--r--src/test/ui/generics/issue-94923.rs49
-rw-r--r--src/test/ui/parser/doc-after-struct-field.rs4
-rw-r--r--src/test/ui/parser/doc-after-struct-field.stderr4
-rw-r--r--src/test/ui/parser/doc-before-extern-rbrace.stderr2
-rw-r--r--src/test/ui/parser/doc-before-fn-rbrace.rs2
-rw-r--r--src/test/ui/parser/doc-before-fn-rbrace.stderr2
-rw-r--r--src/test/ui/parser/doc-before-rbrace.rs2
-rw-r--r--src/test/ui/parser/doc-before-rbrace.stderr2
-rw-r--r--src/test/ui/parser/doc-before-semi.rs2
-rw-r--r--src/test/ui/parser/doc-before-semi.stderr2
-rw-r--r--src/test/ui/parser/doc-before-struct-rbrace-1.rs2
-rw-r--r--src/test/ui/parser/doc-before-struct-rbrace-1.stderr2
-rw-r--r--src/test/ui/parser/doc-before-struct-rbrace-2.rs2
-rw-r--r--src/test/ui/parser/doc-before-struct-rbrace-2.stderr2
-rw-r--r--src/test/ui/parser/doc-inside-trait-item.stderr2
-rw-r--r--src/test/ui/parser/issues/issue-34222-1.stderr2
-rw-r--r--src/test/ui/parser/issues/issue-48636.stderr2
-rw-r--r--src/test/ui/regions/issue-102374.rs20
-rw-r--r--src/test/ui/regions/issue-102374.stderr14
39 files changed, 591 insertions, 389 deletions
diff --git a/compiler/rustc_error_messages/locales/en-US/parser.ftl b/compiler/rustc_error_messages/locales/en-US/parser.ftl
index 6d34cdce340..13c368d1c58 100644
--- a/compiler/rustc_error_messages/locales/en-US/parser.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/parser.ftl
@@ -245,7 +245,7 @@ parser_assignment_else_not_allowed = <assignment> ... else {"{"} ... {"}"} is no
 parser_expected_statement_after_outer_attr = expected statement after outer attribute
 
 parser_doc_comment_does_not_document_anything = found a documentation comment that doesn't document anything
-    .help = doc comments must come before what they document, maybe a comment was intended with `//`?
+    .help = doc comments must come before what they document, if a comment was intended use `//`
     .suggestion = missing comma here
 
 parser_const_let_mutually_exclusive = `const` and `let` are mutually exclusive
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 70efa748846..f69ac076820 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -2173,10 +2173,16 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
 
         let mut region_index = self.region_index;
         let mut next_name = |this: &Self| {
-            let name = name_by_region_index(region_index, &mut available_names, num_available);
-            debug!(?name);
-            region_index += 1;
-            assert!(!this.used_region_names.contains(&name));
+            let mut name;
+
+            loop {
+                name = name_by_region_index(region_index, &mut available_names, num_available);
+                region_index += 1;
+
+                if !this.used_region_names.contains(&name) {
+                    break;
+                }
+            }
 
             name
         };
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index b7454d7bfc1..c8a8e00b1fa 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -760,8 +760,8 @@ impl<'a> Parser<'a> {
                 )
                 .span_label(self.token.span, "this doc comment doesn't document anything")
                 .help(
-                    "doc comments must come before what they document, maybe a \
-                    comment was intended with `//`?",
+                    "doc comments must come before what they document, if a comment was \
+                    intended use `//`",
                 )
                 .emit();
                 self.bump();
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 3c276a9ada9..824cebd7e0a 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -130,6 +130,16 @@ pub(super) enum LifetimeElisionCandidate {
     Missing(MissingLifetime),
 }
 
+/// Only used for diagnostics.
+struct BaseError {
+    msg: String,
+    fallback_label: String,
+    span: Span,
+    span_label: Option<(Span, &'static str)>,
+    could_be_expr: bool,
+    suggestion: Option<(Span, &'static str, String)>,
+}
+
 impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
     fn def_span(&self, def_id: DefId) -> Option<Span> {
         match def_id.krate {
@@ -138,35 +148,18 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
         }
     }
 
-    /// Handles error reporting for `smart_resolve_path_fragment` function.
-    /// Creates base error and amends it with one short label and possibly some longer helps/notes.
-    pub(crate) fn smart_resolve_report_errors(
+    fn make_base_error(
         &mut self,
         path: &[Segment],
         span: Span,
         source: PathSource<'_>,
         res: Option<Res>,
-    ) -> (DiagnosticBuilder<'a, ErrorGuaranteed>, Vec<ImportSuggestion>) {
-        let ident_span = path.last().map_or(span, |ident| ident.ident.span);
-        let ns = source.namespace();
-        let is_expected = &|res| source.is_expected(res);
-        let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));
-
-        debug!(?res, ?source);
-
+    ) -> BaseError {
         // Make the base error.
-        struct BaseError<'a> {
-            msg: String,
-            fallback_label: String,
-            span: Span,
-            span_label: Option<(Span, &'a str)>,
-            could_be_expr: bool,
-            suggestion: Option<(Span, &'a str, String)>,
-        }
         let mut expected = source.descr_expected();
         let path_str = Segment::names_to_string(path);
         let item_str = path.last().unwrap().ident;
-        let base_error = if let Some(res) = res {
+        if let Some(res) = res {
             BaseError {
                 msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
                 fallback_label: format!("not a {expected}"),
@@ -277,8 +270,20 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 could_be_expr: false,
                 suggestion,
             }
-        };
+        }
+    }
 
+    /// Handles error reporting for `smart_resolve_path_fragment` function.
+    /// Creates base error and amends it with one short label and possibly some longer helps/notes.
+    pub(crate) fn smart_resolve_report_errors(
+        &mut self,
+        path: &[Segment],
+        span: Span,
+        source: PathSource<'_>,
+        res: Option<Res>,
+    ) -> (DiagnosticBuilder<'a, ErrorGuaranteed>, Vec<ImportSuggestion>) {
+        debug!(?res, ?source);
+        let base_error = self.make_base_error(path, span, source, res);
         let code = source.error_code(res.is_some());
         let mut err =
             self.r.session.struct_span_err_with_code(base_error.span, &base_error.msg, code);
@@ -289,41 +294,79 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
             err.span_label(span, label);
         }
 
-        if let Some(sugg) = base_error.suggestion {
-            err.span_suggestion_verbose(sugg.0, sugg.1, sugg.2, Applicability::MaybeIncorrect);
+        if let Some(ref sugg) = base_error.suggestion {
+            err.span_suggestion_verbose(sugg.0, sugg.1, &sugg.2, Applicability::MaybeIncorrect);
         }
 
-        if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal {
-            err.multipart_suggestion(
-                "you might have meant to write a `struct` literal",
-                vec![
-                    (span.shrink_to_lo(), "{ SomeStruct ".to_string()),
-                    (span.shrink_to_hi(), "}".to_string()),
-                ],
-                Applicability::HasPlaceholders,
-            );
+        self.suggest_bare_struct_literal(&mut err);
+        self.suggest_pattern_match_with_let(&mut err, source, span);
+
+        self.suggest_self_or_self_ref(&mut err, path, span);
+        self.detect_assoct_type_constraint_meant_as_path(&mut err, &base_error);
+        if self.suggest_self_ty(&mut err, source, path, span)
+            || self.suggest_self_value(&mut err, source, path, span)
+        {
+            return (err, Vec::new());
         }
-        match (source, self.diagnostic_metadata.in_if_condition) {
-            (
-                PathSource::Expr(_),
-                Some(Expr { span: expr_span, kind: ExprKind::Assign(lhs, _, _), .. }),
-            ) => {
-                // Icky heuristic so we don't suggest:
-                // `if (i + 2) = 2` => `if let (i + 2) = 2` (approximately pattern)
-                // `if 2 = i` => `if let 2 = i` (lhs needs to contain error span)
-                if lhs.is_approximately_pattern() && lhs.span.contains(span) {
-                    err.span_suggestion_verbose(
-                        expr_span.shrink_to_lo(),
-                        "you might have meant to use pattern matching",
-                        "let ",
-                        Applicability::MaybeIncorrect,
-                    );
+
+        let (found, candidates) =
+            self.try_lookup_name_relaxed(&mut err, source, path, span, res, &base_error);
+        if found {
+            return (err, candidates);
+        }
+
+        if !self.type_ascription_suggestion(&mut err, base_error.span) {
+            let mut fallback =
+                self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error);
+            fallback |= self.suggest_typo(&mut err, source, path, span, &base_error);
+            if fallback {
+                // Fallback label.
+                err.span_label(base_error.span, &base_error.fallback_label);
+            }
+        }
+        self.err_code_special_cases(&mut err, source, path, span);
+
+        (err, candidates)
+    }
+
+    fn detect_assoct_type_constraint_meant_as_path(
+        &self,
+        err: &mut Diagnostic,
+        base_error: &BaseError,
+    ) {
+        let Some(ty) = self.diagnostic_metadata.current_type_path else { return; };
+        let TyKind::Path(_, path) = &ty.kind else { return; };
+        for segment in &path.segments {
+            let Some(params) = &segment.args else { continue; };
+            let ast::GenericArgs::AngleBracketed(ref params) = params.deref() else { continue; };
+            for param in &params.args {
+                let ast::AngleBracketedArg::Constraint(constraint) = param else { continue; };
+                let ast::AssocConstraintKind::Bound { bounds } = &constraint.kind else {
+                    continue;
+                };
+                for bound in bounds {
+                    let ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifier::None)
+                        = bound else
+                    {
+                        continue;
+                    };
+                    if base_error.span == trait_ref.span {
+                        err.span_suggestion_verbose(
+                            constraint.ident.span.between(trait_ref.span),
+                            "you might have meant to write a path instead of an associated type bound",
+                            "::",
+                            Applicability::MachineApplicable,
+                        );
+                    }
                 }
             }
-            _ => {}
         }
+    }
 
+    fn suggest_self_or_self_ref(&mut self, err: &mut Diagnostic, path: &[Segment], span: Span) {
         let is_assoc_fn = self.self_type_is_available();
+        let Some(path_last_segment) = path.last() else { return };
+        let item_str = path_last_segment.ident;
         // Emit help message for fake-self from other languages (e.g., `this` in Javascript).
         if ["this", "my"].contains(&item_str.as_str()) && is_assoc_fn {
             err.span_suggestion_short(
@@ -358,96 +401,25 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 }
             }
         }
+    }
 
-        self.detect_assoct_type_constraint_meant_as_path(base_error.span, &mut err);
-
-        // Emit special messages for unresolved `Self` and `self`.
-        if is_self_type(path, ns) {
-            err.code(rustc_errors::error_code!(E0411));
-            err.span_label(
-                span,
-                "`Self` is only available in impls, traits, and type definitions".to_string(),
-            );
-            if let Some(item_kind) = self.diagnostic_metadata.current_item {
-                err.span_label(
-                    item_kind.ident.span,
-                    format!(
-                        "`Self` not allowed in {} {}",
-                        item_kind.kind.article(),
-                        item_kind.kind.descr()
-                    ),
-                );
-            }
-            return (err, Vec::new());
-        }
-        if is_self_value(path, ns) {
-            debug!("smart_resolve_path_fragment: E0424, source={:?}", source);
-
-            err.code(rustc_errors::error_code!(E0424));
-            err.span_label(span, match source {
-                PathSource::Pat => "`self` value is a keyword and may not be bound to variables or shadowed",
-                _ => "`self` value is a keyword only available in methods with a `self` parameter",
-            });
-            if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function {
-                // The current function has a `self' parameter, but we were unable to resolve
-                // a reference to `self`. This can only happen if the `self` identifier we
-                // are resolving came from a different hygiene context.
-                if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) {
-                    err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters");
-                } else {
-                    let doesnt = if is_assoc_fn {
-                        let (span, sugg) = fn_kind
-                            .decl()
-                            .inputs
-                            .get(0)
-                            .map(|p| (p.span.shrink_to_lo(), "&self, "))
-                            .unwrap_or_else(|| {
-                                // Try to look for the "(" after the function name, if possible.
-                                // This avoids placing the suggestion into the visibility specifier.
-                                let span = fn_kind
-                                    .ident()
-                                    .map_or(*span, |ident| span.with_lo(ident.span.hi()));
-                                (
-                                    self.r
-                                        .session
-                                        .source_map()
-                                        .span_through_char(span, '(')
-                                        .shrink_to_hi(),
-                                    "&self",
-                                )
-                            });
-                        err.span_suggestion_verbose(
-                            span,
-                            "add a `self` receiver parameter to make the associated `fn` a method",
-                            sugg,
-                            Applicability::MaybeIncorrect,
-                        );
-                        "doesn't"
-                    } else {
-                        "can't"
-                    };
-                    if let Some(ident) = fn_kind.ident() {
-                        err.span_label(
-                            ident.span,
-                            &format!("this function {} have a `self` parameter", doesnt),
-                        );
-                    }
-                }
-            } else if let Some(item_kind) = self.diagnostic_metadata.current_item {
-                err.span_label(
-                    item_kind.ident.span,
-                    format!(
-                        "`self` not allowed in {} {}",
-                        item_kind.kind.article(),
-                        item_kind.kind.descr()
-                    ),
-                );
-            }
-            return (err, Vec::new());
-        }
-
+    fn try_lookup_name_relaxed(
+        &mut self,
+        err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+        source: PathSource<'_>,
+        path: &[Segment],
+        span: Span,
+        res: Option<Res>,
+        base_error: &BaseError,
+    ) -> (bool, Vec<ImportSuggestion>) {
         // Try to lookup name in more relaxed fashion for better error reporting.
         let ident = path.last().unwrap().ident;
+        let is_expected = &|res| source.is_expected(res);
+        let ns = source.namespace();
+        let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));
+        let path_str = Segment::names_to_string(path);
+        let ident_span = path.last().map_or(span, |ident| ident.ident.span);
+
         let mut candidates = self
             .r
             .lookup_import_candidates(ident, ns, &self.parent_scope, is_expected)
@@ -494,7 +466,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                     {
                         // Already reported this issue on the lhs of the type ascription.
                         err.delay_as_bug();
-                        return (err, candidates);
+                        return (true, candidates);
                     }
                 }
 
@@ -522,8 +494,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 );
             }
         }
+
         // Try Levenshtein algorithm.
-        let typo_sugg = self.lookup_typo_candidate(path, ns, is_expected);
+        let typo_sugg = self.lookup_typo_candidate(path, source.namespace(), is_expected);
         if path.len() == 1 && self.self_type_is_available() {
             if let Some(candidate) = self.lookup_assoc_candidate(ident, ns, is_expected) {
                 let self_is_available = self.self_value_is_available(path[0].ident.span);
@@ -560,8 +533,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                         );
                     }
                 }
-                self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span);
-                return (err, candidates);
+                self.r.add_typo_suggestion(err, typo_sugg, ident_span);
+                return (true, candidates);
             }
 
             // If the first argument in call is `self` suggest calling a method.
@@ -579,14 +552,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                     format!("self.{path_str}({args_snippet})"),
                     Applicability::MachineApplicable,
                 );
-                return (err, candidates);
+                return (true, candidates);
             }
         }
 
         // Try context-dependent help if relaxed lookup didn't work.
         if let Some(res) = res {
             if self.smart_resolve_context_dependent_help(
-                &mut err,
+                err,
                 span,
                 source,
                 res,
@@ -594,106 +567,135 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 &base_error.fallback_label,
             ) {
                 // We do this to avoid losing a secondary span when we override the main error span.
-                self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span);
-                return (err, candidates);
+                self.r.add_typo_suggestion(err, typo_sugg, ident_span);
+                return (true, candidates);
             }
         }
+        return (false, candidates);
+    }
 
+    fn suggest_trait_and_bounds(
+        &mut self,
+        err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+        source: PathSource<'_>,
+        res: Option<Res>,
+        span: Span,
+        base_error: &BaseError,
+    ) -> bool {
         let is_macro =
             base_error.span.from_expansion() && base_error.span.desugaring_kind().is_none();
-        if !self.type_ascription_suggestion(&mut err, base_error.span) {
-            let mut fallback = false;
-            if let (
-                PathSource::Trait(AliasPossibility::Maybe),
-                Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)),
-                false,
-            ) = (source, res, is_macro)
-            {
-                if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object {
-                    fallback = true;
-                    let spans: Vec<Span> = bounds
-                        .iter()
-                        .map(|bound| bound.span())
-                        .filter(|&sp| sp != base_error.span)
-                        .collect();
+        let mut fallback = false;
 
-                    let start_span = bounds[0].span();
-                    // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><)
-                    let end_span = bounds.last().unwrap().span();
-                    // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar)
-                    let last_bound_span = spans.last().cloned().unwrap();
-                    let mut multi_span: MultiSpan = spans.clone().into();
-                    for sp in spans {
-                        let msg = if sp == last_bound_span {
-                            format!(
-                                "...because of {these} bound{s}",
-                                these = pluralize!("this", bounds.len() - 1),
-                                s = pluralize!(bounds.len() - 1),
-                            )
-                        } else {
-                            String::new()
-                        };
-                        multi_span.push_span_label(sp, msg);
-                    }
-                    multi_span
-                        .push_span_label(base_error.span, "expected this type to be a trait...");
-                    err.span_help(
-                        multi_span,
-                        "`+` is used to constrain a \"trait object\" type with lifetimes or \
-                         auto-traits; structs and enums can't be bound in that way",
-                    );
-                    if bounds.iter().all(|bound| match bound {
-                        ast::GenericBound::Outlives(_) => true,
-                        ast::GenericBound::Trait(tr, _) => tr.span == base_error.span,
-                    }) {
-                        let mut sugg = vec![];
-                        if base_error.span != start_span {
-                            sugg.push((start_span.until(base_error.span), String::new()));
-                        }
-                        if base_error.span != end_span {
-                            sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new()));
-                        }
+        if let (
+            PathSource::Trait(AliasPossibility::Maybe),
+            Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)),
+            false,
+        ) = (source, res, is_macro)
+        {
+            if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object {
+                fallback = true;
+                let spans: Vec<Span> = bounds
+                    .iter()
+                    .map(|bound| bound.span())
+                    .filter(|&sp| sp != base_error.span)
+                    .collect();
 
-                        err.multipart_suggestion(
-                            "if you meant to use a type and not a trait here, remove the bounds",
-                            sugg,
-                            Applicability::MaybeIncorrect,
-                        );
+                let start_span = bounds[0].span();
+                // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><)
+                let end_span = bounds.last().unwrap().span();
+                // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar)
+                let last_bound_span = spans.last().cloned().unwrap();
+                let mut multi_span: MultiSpan = spans.clone().into();
+                for sp in spans {
+                    let msg = if sp == last_bound_span {
+                        format!(
+                            "...because of {these} bound{s}",
+                            these = pluralize!("this", bounds.len() - 1),
+                            s = pluralize!(bounds.len() - 1),
+                        )
+                    } else {
+                        String::new()
+                    };
+                    multi_span.push_span_label(sp, msg);
+                }
+                multi_span.push_span_label(base_error.span, "expected this type to be a trait...");
+                err.span_help(
+                    multi_span,
+                    "`+` is used to constrain a \"trait object\" type with lifetimes or \
+                        auto-traits; structs and enums can't be bound in that way",
+                );
+                if bounds.iter().all(|bound| match bound {
+                    ast::GenericBound::Outlives(_) => true,
+                    ast::GenericBound::Trait(tr, _) => tr.span == base_error.span,
+                }) {
+                    let mut sugg = vec![];
+                    if base_error.span != start_span {
+                        sugg.push((start_span.until(base_error.span), String::new()));
                     }
+                    if base_error.span != end_span {
+                        sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new()));
+                    }
+
+                    err.multipart_suggestion(
+                        "if you meant to use a type and not a trait here, remove the bounds",
+                        sugg,
+                        Applicability::MaybeIncorrect,
+                    );
                 }
             }
+        }
 
-            fallback |= self.restrict_assoc_type_in_where_clause(span, &mut err);
+        fallback |= self.restrict_assoc_type_in_where_clause(span, err);
+        fallback
+    }
 
-            if !self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span) {
-                fallback = true;
-                match self.diagnostic_metadata.current_let_binding {
-                    Some((pat_sp, Some(ty_sp), None))
-                        if ty_sp.contains(base_error.span) && base_error.could_be_expr =>
-                    {
-                        err.span_suggestion_short(
-                            pat_sp.between(ty_sp),
-                            "use `=` if you meant to assign",
-                            " = ",
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
-                    _ => {}
+    fn suggest_typo(
+        &mut self,
+        err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+        source: PathSource<'_>,
+        path: &[Segment],
+        span: Span,
+        base_error: &BaseError,
+    ) -> bool {
+        let is_expected = &|res| source.is_expected(res);
+        let ident_span = path.last().map_or(span, |ident| ident.ident.span);
+        let typo_sugg = self.lookup_typo_candidate(path, source.namespace(), is_expected);
+        let mut fallback = false;
+        if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) {
+            fallback = true;
+            match self.diagnostic_metadata.current_let_binding {
+                Some((pat_sp, Some(ty_sp), None))
+                    if ty_sp.contains(base_error.span) && base_error.could_be_expr =>
+                {
+                    err.span_suggestion_short(
+                        pat_sp.between(ty_sp),
+                        "use `=` if you meant to assign",
+                        " = ",
+                        Applicability::MaybeIncorrect,
+                    );
                 }
-
-                // If the trait has a single item (which wasn't matched by Levenshtein), suggest it
-                let suggestion = self.get_single_associated_item(&path, &source, is_expected);
-                self.r.add_typo_suggestion(&mut err, suggestion, ident_span);
-            }
-            if fallback {
-                // Fallback label.
-                err.span_label(base_error.span, base_error.fallback_label);
+                _ => {}
             }
+
+            // If the trait has a single item (which wasn't matched by Levenshtein), suggest it
+            let suggestion = self.get_single_associated_item(&path, &source, is_expected);
+            self.r.add_typo_suggestion(err, suggestion, ident_span);
         }
+        fallback
+    }
+
+    fn err_code_special_cases(
+        &mut self,
+        err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+        source: PathSource<'_>,
+        path: &[Segment],
+        span: Span,
+    ) {
         if let Some(err_code) = &err.code {
             if err_code == &rustc_errors::error_code!(E0425) {
                 for label_rib in &self.label_ribs {
                     for (label_ident, node_id) in &label_rib.bindings {
+                        let ident = path.last().unwrap().ident;
                         if format!("'{}", ident) == label_ident.to_string() {
                             err.span_label(label_ident.span, "a label with a similar name exists");
                             if let PathSource::Expr(Some(Expr {
@@ -724,38 +726,116 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 }
             }
         }
+    }
 
-        (err, candidates)
+    /// Emit special messages for unresolved `Self` and `self`.
+    fn suggest_self_ty(
+        &mut self,
+        err: &mut Diagnostic,
+        source: PathSource<'_>,
+        path: &[Segment],
+        span: Span,
+    ) -> bool {
+        if !is_self_type(path, source.namespace()) {
+            return false;
+        }
+        err.code(rustc_errors::error_code!(E0411));
+        err.span_label(
+            span,
+            "`Self` is only available in impls, traits, and type definitions".to_string(),
+        );
+        if let Some(item_kind) = self.diagnostic_metadata.current_item {
+            err.span_label(
+                item_kind.ident.span,
+                format!(
+                    "`Self` not allowed in {} {}",
+                    item_kind.kind.article(),
+                    item_kind.kind.descr()
+                ),
+            );
+        }
+        true
     }
 
-    fn detect_assoct_type_constraint_meant_as_path(&self, base_span: Span, err: &mut Diagnostic) {
-        let Some(ty) = self.diagnostic_metadata.current_type_path else { return; };
-        let TyKind::Path(_, path) = &ty.kind else { return; };
-        for segment in &path.segments {
-            let Some(params) = &segment.args else { continue; };
-            let ast::GenericArgs::AngleBracketed(ref params) = params.deref() else { continue; };
-            for param in &params.args {
-                let ast::AngleBracketedArg::Constraint(constraint) = param else { continue; };
-                let ast::AssocConstraintKind::Bound { bounds } = &constraint.kind else {
-                    continue;
+    fn suggest_self_value(
+        &mut self,
+        err: &mut Diagnostic,
+        source: PathSource<'_>,
+        path: &[Segment],
+        span: Span,
+    ) -> bool {
+        if !is_self_value(path, source.namespace()) {
+            return false;
+        }
+
+        debug!("smart_resolve_path_fragment: E0424, source={:?}", source);
+        err.code(rustc_errors::error_code!(E0424));
+        err.span_label(
+            span,
+            match source {
+                PathSource::Pat => {
+                    "`self` value is a keyword and may not be bound to variables or shadowed"
+                }
+                _ => "`self` value is a keyword only available in methods with a `self` parameter",
+            },
+        );
+        let is_assoc_fn = self.self_type_is_available();
+        if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function {
+            // The current function has a `self' parameter, but we were unable to resolve
+            // a reference to `self`. This can only happen if the `self` identifier we
+            // are resolving came from a different hygiene context.
+            if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) {
+                err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters");
+            } else {
+                let doesnt = if is_assoc_fn {
+                    let (span, sugg) = fn_kind
+                        .decl()
+                        .inputs
+                        .get(0)
+                        .map(|p| (p.span.shrink_to_lo(), "&self, "))
+                        .unwrap_or_else(|| {
+                            // Try to look for the "(" after the function name, if possible.
+                            // This avoids placing the suggestion into the visibility specifier.
+                            let span = fn_kind
+                                .ident()
+                                .map_or(*span, |ident| span.with_lo(ident.span.hi()));
+                            (
+                                self.r
+                                    .session
+                                    .source_map()
+                                    .span_through_char(span, '(')
+                                    .shrink_to_hi(),
+                                "&self",
+                            )
+                        });
+                    err.span_suggestion_verbose(
+                        span,
+                        "add a `self` receiver parameter to make the associated `fn` a method",
+                        sugg,
+                        Applicability::MaybeIncorrect,
+                    );
+                    "doesn't"
+                } else {
+                    "can't"
                 };
-                for bound in bounds {
-                    let ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifier::None)
-                        = bound else
-                    {
-                        continue;
-                    };
-                    if base_span == trait_ref.span {
-                        err.span_suggestion_verbose(
-                            constraint.ident.span.between(trait_ref.span),
-                            "you might have meant to write a path instead of an associated type bound",
-                            "::",
-                            Applicability::MachineApplicable,
-                        );
-                    }
+                if let Some(ident) = fn_kind.ident() {
+                    err.span_label(
+                        ident.span,
+                        &format!("this function {} have a `self` parameter", doesnt),
+                    );
                 }
             }
+        } else if let Some(item_kind) = self.diagnostic_metadata.current_item {
+            err.span_label(
+                item_kind.ident.span,
+                format!(
+                    "`self` not allowed in {} {}",
+                    item_kind.kind.article(),
+                    item_kind.kind.descr()
+                ),
+            );
         }
+        true
     }
 
     fn suggest_swapping_misplaced_self_ty_and_trait(
@@ -787,6 +867,45 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
         }
     }
 
+    fn suggest_bare_struct_literal(&mut self, err: &mut Diagnostic) {
+        if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal {
+            err.multipart_suggestion(
+                "you might have meant to write a `struct` literal",
+                vec![
+                    (span.shrink_to_lo(), "{ SomeStruct ".to_string()),
+                    (span.shrink_to_hi(), "}".to_string()),
+                ],
+                Applicability::HasPlaceholders,
+            );
+        }
+    }
+
+    fn suggest_pattern_match_with_let(
+        &mut self,
+        err: &mut Diagnostic,
+        source: PathSource<'_>,
+        span: Span,
+    ) {
+        if let PathSource::Expr(_) = source &&
+        let Some(Expr {
+                    span: expr_span,
+                    kind: ExprKind::Assign(lhs, _, _),
+                    ..
+                })  = self.diagnostic_metadata.in_if_condition {
+            // Icky heuristic so we don't suggest:
+            // `if (i + 2) = 2` => `if let (i + 2) = 2` (approximately pattern)
+            // `if 2 = i` => `if let 2 = i` (lhs needs to contain error span)
+            if lhs.is_approximately_pattern() && lhs.span.contains(span) {
+                err.span_suggestion_verbose(
+                    expr_span.shrink_to_lo(),
+                    "you might have meant to use pattern matching",
+                    "let ",
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        }
+    }
+
     fn get_single_associated_item(
         &mut self,
         path: &[Segment],
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 5e32a27cdb1..0ed59223337 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -1737,12 +1737,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
             (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => {
                 // See if we can toss out `victim` based on specialization.
-                // This requires us to know *for sure* that the `other` impl applies
-                // i.e., `EvaluatedToOk`.
+                // While this requires us to know *for sure* that the `other` impl applies
+                // we still use modulo regions here.
                 //
-                // FIXME(@lcnr): Using `modulo_regions` here seems kind of scary
-                // to me but is required for `std` to compile, so I didn't change it
-                // for now.
+                // This is fine as specialization currently assumes that specializing
+                // impls have to be always applicable, meaning that the only allowed
+                // region constraints may be constraints also present on the default impl.
                 let tcx = self.tcx();
                 if other.evaluation.must_apply_modulo_regions() {
                     if tcx.specializes((other_def, victim_def)) {
diff --git a/library/core/src/time.rs b/library/core/src/time.rs
index 4f29ecc0fba..2b85d6e2225 100644
--- a/library/core/src/time.rs
+++ b/library/core/src/time.rs
@@ -29,6 +29,20 @@ const NANOS_PER_MICRO: u32 = 1_000;
 const MILLIS_PER_SEC: u64 = 1_000;
 const MICROS_PER_SEC: u64 = 1_000_000;
 
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[repr(transparent)]
+#[rustc_layout_scalar_valid_range_start(0)]
+#[rustc_layout_scalar_valid_range_end(999_999_999)]
+struct Nanoseconds(u32);
+
+impl Default for Nanoseconds {
+    #[inline]
+    fn default() -> Self {
+        // SAFETY: 0 is within the valid range
+        unsafe { Nanoseconds(0) }
+    }
+}
+
 /// A `Duration` type to represent a span of time, typically used for system
 /// timeouts.
 ///
@@ -71,7 +85,7 @@ const MICROS_PER_SEC: u64 = 1_000_000;
 #[cfg_attr(not(test), rustc_diagnostic_item = "Duration")]
 pub struct Duration {
     secs: u64,
-    nanos: u32, // Always 0 <= nanos < NANOS_PER_SEC
+    nanos: Nanoseconds, // Always 0 <= nanos < NANOS_PER_SEC
 }
 
 impl Duration {
@@ -188,7 +202,8 @@ impl Duration {
             None => panic!("overflow in Duration::new"),
         };
         let nanos = nanos % NANOS_PER_SEC;
-        Duration { secs, nanos }
+        // SAFETY: nanos % NANOS_PER_SEC < NANOS_PER_SEC, therefore nanos is within the valid range
+        Duration { secs, nanos: unsafe { Nanoseconds(nanos) } }
     }
 
     /// Creates a new `Duration` from the specified number of whole seconds.
@@ -208,7 +223,7 @@ impl Duration {
     #[inline]
     #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")]
     pub const fn from_secs(secs: u64) -> Duration {
-        Duration { secs, nanos: 0 }
+        Duration::new(secs, 0)
     }
 
     /// Creates a new `Duration` from the specified number of milliseconds.
@@ -228,10 +243,7 @@ impl Duration {
     #[inline]
     #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")]
     pub const fn from_millis(millis: u64) -> Duration {
-        Duration {
-            secs: millis / MILLIS_PER_SEC,
-            nanos: ((millis % MILLIS_PER_SEC) as u32) * NANOS_PER_MILLI,
-        }
+        Duration::new(millis / MILLIS_PER_SEC, ((millis % MILLIS_PER_SEC) as u32) * NANOS_PER_MILLI)
     }
 
     /// Creates a new `Duration` from the specified number of microseconds.
@@ -251,10 +263,7 @@ impl Duration {
     #[inline]
     #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")]
     pub const fn from_micros(micros: u64) -> Duration {
-        Duration {
-            secs: micros / MICROS_PER_SEC,
-            nanos: ((micros % MICROS_PER_SEC) as u32) * NANOS_PER_MICRO,
-        }
+        Duration::new(micros / MICROS_PER_SEC, ((micros % MICROS_PER_SEC) as u32) * NANOS_PER_MICRO)
     }
 
     /// Creates a new `Duration` from the specified number of nanoseconds.
@@ -274,10 +283,7 @@ impl Duration {
     #[inline]
     #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")]
     pub const fn from_nanos(nanos: u64) -> Duration {
-        Duration {
-            secs: nanos / (NANOS_PER_SEC as u64),
-            nanos: (nanos % (NANOS_PER_SEC as u64)) as u32,
-        }
+        Duration::new(nanos / (NANOS_PER_SEC as u64), (nanos % (NANOS_PER_SEC as u64)) as u32)
     }
 
     /// Returns true if this `Duration` spans no time.
@@ -301,7 +307,7 @@ impl Duration {
     #[rustc_const_stable(feature = "duration_zero", since = "1.53.0")]
     #[inline]
     pub const fn is_zero(&self) -> bool {
-        self.secs == 0 && self.nanos == 0
+        self.secs == 0 && self.nanos.0 == 0
     }
 
     /// Returns the number of _whole_ seconds contained by this `Duration`.
@@ -352,7 +358,7 @@ impl Duration {
     #[must_use]
     #[inline]
     pub const fn subsec_millis(&self) -> u32 {
-        self.nanos / NANOS_PER_MILLI
+        self.nanos.0 / NANOS_PER_MILLI
     }
 
     /// Returns the fractional part of this `Duration`, in whole microseconds.
@@ -375,7 +381,7 @@ impl Duration {
     #[must_use]
     #[inline]
     pub const fn subsec_micros(&self) -> u32 {
-        self.nanos / NANOS_PER_MICRO
+        self.nanos.0 / NANOS_PER_MICRO
     }
 
     /// Returns the fractional part of this `Duration`, in nanoseconds.
@@ -398,7 +404,7 @@ impl Duration {
     #[must_use]
     #[inline]
     pub const fn subsec_nanos(&self) -> u32 {
-        self.nanos
+        self.nanos.0
     }
 
     /// Returns the total number of whole milliseconds contained by this `Duration`.
@@ -416,7 +422,7 @@ impl Duration {
     #[must_use]
     #[inline]
     pub const fn as_millis(&self) -> u128 {
-        self.secs as u128 * MILLIS_PER_SEC as u128 + (self.nanos / NANOS_PER_MILLI) as u128
+        self.secs as u128 * MILLIS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MILLI) as u128
     }
 
     /// Returns the total number of whole microseconds contained by this `Duration`.
@@ -434,7 +440,7 @@ impl Duration {
     #[must_use]
     #[inline]
     pub const fn as_micros(&self) -> u128 {
-        self.secs as u128 * MICROS_PER_SEC as u128 + (self.nanos / NANOS_PER_MICRO) as u128
+        self.secs as u128 * MICROS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MICRO) as u128
     }
 
     /// Returns the total number of nanoseconds contained by this `Duration`.
@@ -452,7 +458,7 @@ impl Duration {
     #[must_use]
     #[inline]
     pub const fn as_nanos(&self) -> u128 {
-        self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos as u128
+        self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos.0 as u128
     }
 
     /// Checked `Duration` addition. Computes `self + other`, returning [`None`]
@@ -475,7 +481,7 @@ impl Duration {
     #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")]
     pub const fn checked_add(self, rhs: Duration) -> Option<Duration> {
         if let Some(mut secs) = self.secs.checked_add(rhs.secs) {
-            let mut nanos = self.nanos + rhs.nanos;
+            let mut nanos = self.nanos.0 + rhs.nanos.0;
             if nanos >= NANOS_PER_SEC {
                 nanos -= NANOS_PER_SEC;
                 if let Some(new_secs) = secs.checked_add(1) {
@@ -485,7 +491,7 @@ impl Duration {
                 }
             }
             debug_assert!(nanos < NANOS_PER_SEC);
-            Some(Duration { secs, nanos })
+            Some(Duration::new(secs, nanos))
         } else {
             None
         }
@@ -535,16 +541,16 @@ impl Duration {
     #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")]
     pub const fn checked_sub(self, rhs: Duration) -> Option<Duration> {
         if let Some(mut secs) = self.secs.checked_sub(rhs.secs) {
-            let nanos = if self.nanos >= rhs.nanos {
-                self.nanos - rhs.nanos
+            let nanos = if self.nanos.0 >= rhs.nanos.0 {
+                self.nanos.0 - rhs.nanos.0
             } else if let Some(sub_secs) = secs.checked_sub(1) {
                 secs = sub_secs;
-                self.nanos + NANOS_PER_SEC - rhs.nanos
+                self.nanos.0 + NANOS_PER_SEC - rhs.nanos.0
             } else {
                 return None;
             };
             debug_assert!(nanos < NANOS_PER_SEC);
-            Some(Duration { secs, nanos })
+            Some(Duration::new(secs, nanos))
         } else {
             None
         }
@@ -593,13 +599,13 @@ impl Duration {
     #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")]
     pub const fn checked_mul(self, rhs: u32) -> Option<Duration> {
         // Multiply nanoseconds as u64, because it cannot overflow that way.
-        let total_nanos = self.nanos as u64 * rhs as u64;
+        let total_nanos = self.nanos.0 as u64 * rhs as u64;
         let extra_secs = total_nanos / (NANOS_PER_SEC as u64);
         let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32;
         if let Some(s) = self.secs.checked_mul(rhs as u64) {
             if let Some(secs) = s.checked_add(extra_secs) {
                 debug_assert!(nanos < NANOS_PER_SEC);
-                return Some(Duration { secs, nanos });
+                return Some(Duration::new(secs, nanos));
             }
         }
         None
@@ -653,9 +659,9 @@ impl Duration {
             let secs = self.secs / (rhs as u64);
             let carry = self.secs - secs * (rhs as u64);
             let extra_nanos = carry * (NANOS_PER_SEC as u64) / (rhs as u64);
-            let nanos = self.nanos / rhs + (extra_nanos as u32);
+            let nanos = self.nanos.0 / rhs + (extra_nanos as u32);
             debug_assert!(nanos < NANOS_PER_SEC);
-            Some(Duration { secs, nanos })
+            Some(Duration::new(secs, nanos))
         } else {
             None
         }
@@ -677,7 +683,7 @@ impl Duration {
     #[inline]
     #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")]
     pub const fn as_secs_f64(&self) -> f64 {
-        (self.secs as f64) + (self.nanos as f64) / (NANOS_PER_SEC as f64)
+        (self.secs as f64) + (self.nanos.0 as f64) / (NANOS_PER_SEC as f64)
     }
 
     /// Returns the number of seconds contained by this `Duration` as `f32`.
@@ -696,7 +702,7 @@ impl Duration {
     #[inline]
     #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")]
     pub const fn as_secs_f32(&self) -> f32 {
-        (self.secs as f32) + (self.nanos as f32) / (NANOS_PER_SEC as f32)
+        (self.secs as f32) + (self.nanos.0 as f32) / (NANOS_PER_SEC as f32)
     }
 
     /// Creates a new `Duration` from the specified number of seconds represented
@@ -987,13 +993,13 @@ macro_rules! sum_durations {
         for entry in $iter {
             total_secs =
                 total_secs.checked_add(entry.secs).expect("overflow in iter::sum over durations");
-            total_nanos = match total_nanos.checked_add(entry.nanos as u64) {
+            total_nanos = match total_nanos.checked_add(entry.nanos.0 as u64) {
                 Some(n) => n,
                 None => {
                     total_secs = total_secs
                         .checked_add(total_nanos / NANOS_PER_SEC as u64)
                         .expect("overflow in iter::sum over durations");
-                    (total_nanos % NANOS_PER_SEC as u64) + entry.nanos as u64
+                    (total_nanos % NANOS_PER_SEC as u64) + entry.nanos.0 as u64
                 }
             };
         }
@@ -1001,7 +1007,7 @@ macro_rules! sum_durations {
             .checked_add(total_nanos / NANOS_PER_SEC as u64)
             .expect("overflow in iter::sum over durations");
         total_nanos = total_nanos % NANOS_PER_SEC as u64;
-        Duration { secs: total_secs, nanos: total_nanos as u32 }
+        Duration::new(total_secs, total_nanos as u32)
     }};
 }
 
@@ -1166,27 +1172,27 @@ impl fmt::Debug for Duration {
         let prefix = if f.sign_plus() { "+" } else { "" };
 
         if self.secs > 0 {
-            fmt_decimal(f, self.secs, self.nanos, NANOS_PER_SEC / 10, prefix, "s")
-        } else if self.nanos >= NANOS_PER_MILLI {
+            fmt_decimal(f, self.secs, self.nanos.0, NANOS_PER_SEC / 10, prefix, "s")
+        } else if self.nanos.0 >= NANOS_PER_MILLI {
             fmt_decimal(
                 f,
-                (self.nanos / NANOS_PER_MILLI) as u64,
-                self.nanos % NANOS_PER_MILLI,
+                (self.nanos.0 / NANOS_PER_MILLI) as u64,
+                self.nanos.0 % NANOS_PER_MILLI,
                 NANOS_PER_MILLI / 10,
                 prefix,
                 "ms",
             )
-        } else if self.nanos >= NANOS_PER_MICRO {
+        } else if self.nanos.0 >= NANOS_PER_MICRO {
             fmt_decimal(
                 f,
-                (self.nanos / NANOS_PER_MICRO) as u64,
-                self.nanos % NANOS_PER_MICRO,
+                (self.nanos.0 / NANOS_PER_MICRO) as u64,
+                self.nanos.0 % NANOS_PER_MICRO,
                 NANOS_PER_MICRO / 10,
                 prefix,
                 "µs",
             )
         } else {
-            fmt_decimal(f, self.nanos as u64, 0, 1, prefix, "ns")
+            fmt_decimal(f, self.nanos.0 as u64, 0, 1, prefix, "ns")
         }
     }
 }
@@ -1317,7 +1323,7 @@ macro_rules! try_from_secs {
             return Err(FromFloatSecsError { kind: FromFloatSecsErrorKind::OverflowOrNan });
         };
 
-        Ok(Duration { secs, nanos })
+        Ok(Duration::new(secs, nanos))
     }};
 }
 
diff --git a/library/std/src/env.rs b/library/std/src/env.rs
index 463f714064c..6eb7cbea626 100644
--- a/library/std/src/env.rs
+++ b/library/std/src/env.rs
@@ -603,7 +603,7 @@ pub fn home_dir() -> Option<PathBuf> {
 /// # Platform-specific behavior
 ///
 /// On Unix, returns the value of the `TMPDIR` environment variable if it is
-/// set, otherwise for non-Android it returns `/tmp`. If Android, since there
+/// set, otherwise for non-Android it returns `/tmp`. On Android, since there
 /// is no global temporary folder (it is usually allocated per-app), it returns
 /// `/data/local/tmp`.
 /// On Windows, the behavior is equivalent to that of [`GetTempPath2`][GetTempPath2] /
diff --git a/library/std/src/os/fd/mod.rs b/library/std/src/os/fd/mod.rs
index a456947534a..c6aa7c77dbc 100644
--- a/library/std/src/os/fd/mod.rs
+++ b/library/std/src/os/fd/mod.rs
@@ -1,16 +1,25 @@
 //! Owned and borrowed Unix-like file descriptors.
+//!
+//! This module is supported on Unix platforms and WASI, which both use a
+//! similar file descriptor system for referencing OS resources.
 
 #![stable(feature = "io_safety", since = "1.63.0")]
 #![deny(unsafe_op_in_unsafe_fn)]
 
 // `RawFd`, `AsRawFd`, etc.
-pub mod raw;
+mod raw;
 
 // `OwnedFd`, `AsFd`, etc.
-pub mod owned;
+mod owned;
 
 // Implementations for `AsRawFd` etc. for network types.
 mod net;
 
 #[cfg(test)]
 mod tests;
+
+// Export the types and traits for the public API.
+#[unstable(feature = "os_fd", issue = "98699")]
+pub use owned::*;
+#[unstable(feature = "os_fd", issue = "98699")]
+pub use raw::*;
diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs
index 71e33fb9ed8..9875c389d8a 100644
--- a/library/std/src/os/fd/owned.rs
+++ b/library/std/src/os/fd/owned.rs
@@ -206,10 +206,8 @@ pub trait AsFd {
     /// ```rust,no_run
     /// use std::fs::File;
     /// # use std::io;
-    /// # #[cfg(target_os = "wasi")]
-    /// # use std::os::wasi::io::{AsFd, BorrowedFd};
-    /// # #[cfg(unix)]
-    /// # use std::os::unix::io::{AsFd, BorrowedFd};
+    /// # #[cfg(any(unix, target_os = "wasi"))]
+    /// # use std::os::fd::{AsFd, BorrowedFd};
     ///
     /// let mut f = File::open("foo.txt")?;
     /// # #[cfg(any(unix, target_os = "wasi"))]
diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs
index 1b3d110426f..f92a0506670 100644
--- a/library/std/src/os/fd/raw.rs
+++ b/library/std/src/os/fd/raw.rs
@@ -42,10 +42,8 @@ pub trait AsRawFd {
     /// ```no_run
     /// use std::fs::File;
     /// # use std::io;
-    /// #[cfg(unix)]
-    /// use std::os::unix::io::{AsRawFd, RawFd};
-    /// #[cfg(target_os = "wasi")]
-    /// use std::os::wasi::io::{AsRawFd, RawFd};
+    /// #[cfg(any(unix, target_os = "wasi"))]
+    /// use std::os::fd::{AsRawFd, RawFd};
     ///
     /// let mut f = File::open("foo.txt")?;
     /// // Note that `raw_fd` is only valid as long as `f` exists.
@@ -83,10 +81,8 @@ pub trait FromRawFd {
     /// ```no_run
     /// use std::fs::File;
     /// # use std::io;
-    /// #[cfg(unix)]
-    /// use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd};
-    /// #[cfg(target_os = "wasi")]
-    /// use std::os::wasi::io::{FromRawFd, IntoRawFd, RawFd};
+    /// #[cfg(any(unix, target_os = "wasi"))]
+    /// use std::os::fd::{FromRawFd, IntoRawFd, RawFd};
     ///
     /// let f = File::open("foo.txt")?;
     /// # #[cfg(any(unix, target_os = "wasi"))]
@@ -121,10 +117,8 @@ pub trait IntoRawFd {
     /// ```no_run
     /// use std::fs::File;
     /// # use std::io;
-    /// #[cfg(unix)]
-    /// use std::os::unix::io::{IntoRawFd, RawFd};
-    /// #[cfg(target_os = "wasi")]
-    /// use std::os::wasi::io::{IntoRawFd, RawFd};
+    /// #[cfg(any(unix, target_os = "wasi"))]
+    /// use std::os::fd::{IntoRawFd, RawFd};
     ///
     /// let f = File::open("foo.txt")?;
     /// #[cfg(any(unix, target_os = "wasi"))]
diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs
index 18c64b51007..f62f5af774f 100644
--- a/library/std/src/os/mod.rs
+++ b/library/std/src/os/mod.rs
@@ -147,7 +147,7 @@ pub mod solid;
 pub mod vxworks;
 
 #[cfg(any(unix, target_os = "wasi", doc))]
-mod fd;
+pub mod fd;
 
 #[cfg(any(target_os = "linux", target_os = "android", doc))]
 mod net;
diff --git a/library/std/src/os/unix/io/fd.rs b/library/std/src/os/unix/io/fd.rs
deleted file mode 100644
index d4cb696459b..00000000000
--- a/library/std/src/os/unix/io/fd.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-//! Owned and borrowed file descriptors.
-
-// Tests for this module
-#[cfg(test)]
-mod tests;
-
-#[stable(feature = "io_safety", since = "1.63.0")]
-pub use crate::os::fd::owned::*;
diff --git a/library/std/src/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs
index 3ab5606f889..25b5dbff14f 100644
--- a/library/std/src/os/unix/io/mod.rs
+++ b/library/std/src/os/unix/io/mod.rs
@@ -77,10 +77,9 @@
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
-mod fd;
-mod raw;
-
-#[stable(feature = "io_safety", since = "1.63.0")]
-pub use fd::*;
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use raw::*;
+pub use crate::os::fd::*;
+
+// Tests for this module
+#[cfg(test)]
+mod tests;
diff --git a/library/std/src/os/unix/io/raw.rs b/library/std/src/os/unix/io/raw.rs
deleted file mode 100644
index a4d2ba797d9..00000000000
--- a/library/std/src/os/unix/io/raw.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-//! Unix-specific extensions to general I/O primitives.
-
-#![stable(feature = "rust1", since = "1.0.0")]
-
-#[stable(feature = "rust1", since = "1.0.0")]
-pub use crate::os::fd::raw::*;
diff --git a/library/std/src/os/unix/io/fd/tests.rs b/library/std/src/os/unix/io/tests.rs
index 84d2a7a1a91..84d2a7a1a91 100644
--- a/library/std/src/os/unix/io/fd/tests.rs
+++ b/library/std/src/os/unix/io/tests.rs
diff --git a/library/std/src/os/wasi/io/mod.rs b/library/std/src/os/wasi/io/mod.rs
index 6c884e2eaf4..d528590d75b 100644
--- a/library/std/src/os/wasi/io/mod.rs
+++ b/library/std/src/os/wasi/io/mod.rs
@@ -1,12 +1,6 @@
 //! WASI-specific extensions to general I/O primitives.
 
-#![deny(unsafe_op_in_unsafe_fn)]
 #![unstable(feature = "wasi_ext", issue = "71213")]
 
-mod fd;
-mod raw;
-
-#[unstable(feature = "wasi_ext", issue = "71213")]
-pub use fd::*;
 #[unstable(feature = "wasi_ext", issue = "71213")]
-pub use raw::*;
+pub use crate::os::fd::*;
diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs
index dff973f59d1..cca9c676701 100644
--- a/library/std/src/sys/unix/time.rs
+++ b/library/std/src/sys/unix/time.rs
@@ -7,6 +7,12 @@ const NSEC_PER_SEC: u64 = 1_000_000_000;
 pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
 
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[repr(transparent)]
+#[rustc_layout_scalar_valid_range_start(0)]
+#[rustc_layout_scalar_valid_range_end(999_999_999)]
+struct Nanoseconds(u32);
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct SystemTime {
     pub(in crate::sys::unix) t: Timespec,
 }
@@ -14,7 +20,7 @@ pub struct SystemTime {
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub(in crate::sys::unix) struct Timespec {
     tv_sec: i64,
-    tv_nsec: i64,
+    tv_nsec: Nanoseconds,
 }
 
 impl SystemTime {
@@ -46,18 +52,20 @@ impl fmt::Debug for SystemTime {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct("SystemTime")
             .field("tv_sec", &self.t.tv_sec)
-            .field("tv_nsec", &self.t.tv_nsec)
+            .field("tv_nsec", &self.t.tv_nsec.0)
             .finish()
     }
 }
 
 impl Timespec {
     pub const fn zero() -> Timespec {
-        Timespec { tv_sec: 0, tv_nsec: 0 }
+        Timespec::new(0, 0)
     }
 
-    fn new(tv_sec: i64, tv_nsec: i64) -> Timespec {
-        Timespec { tv_sec, tv_nsec }
+    const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec {
+        assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64);
+        // SAFETY: The assert above checks tv_nsec is within the valid range
+        Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } }
     }
 
     pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
@@ -75,12 +83,12 @@ impl Timespec {
             //
             // Ideally this code could be rearranged such that it more
             // directly expresses the lower-cost behavior we want from it.
-            let (secs, nsec) = if self.tv_nsec >= other.tv_nsec {
-                ((self.tv_sec - other.tv_sec) as u64, (self.tv_nsec - other.tv_nsec) as u32)
+            let (secs, nsec) = if self.tv_nsec.0 >= other.tv_nsec.0 {
+                ((self.tv_sec - other.tv_sec) as u64, self.tv_nsec.0 - other.tv_nsec.0)
             } else {
                 (
                     (self.tv_sec - other.tv_sec - 1) as u64,
-                    self.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.tv_nsec as u32,
+                    self.tv_nsec.0 + (NSEC_PER_SEC as u32) - other.tv_nsec.0,
                 )
             };
 
@@ -102,7 +110,7 @@ impl Timespec {
 
         // Nano calculations can't overflow because nanos are <1B which fit
         // in a u32.
-        let mut nsec = other.subsec_nanos() + self.tv_nsec as u32;
+        let mut nsec = other.subsec_nanos() + self.tv_nsec.0;
         if nsec >= NSEC_PER_SEC as u32 {
             nsec -= NSEC_PER_SEC as u32;
             secs = secs.checked_add(1)?;
@@ -118,7 +126,7 @@ impl Timespec {
             .and_then(|secs| self.tv_sec.checked_sub(secs))?;
 
         // Similar to above, nanos can't overflow.
-        let mut nsec = self.tv_nsec as i32 - other.subsec_nanos() as i32;
+        let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32;
         if nsec < 0 {
             nsec += NSEC_PER_SEC as i32;
             secs = secs.checked_sub(1)?;
@@ -130,7 +138,7 @@ impl Timespec {
     pub fn to_timespec(&self) -> Option<libc::timespec> {
         Some(libc::timespec {
             tv_sec: self.tv_sec.try_into().ok()?,
-            tv_nsec: self.tv_nsec.try_into().ok()?,
+            tv_nsec: self.tv_nsec.0.try_into().ok()?,
         })
     }
 }
@@ -293,7 +301,7 @@ mod inner {
         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             f.debug_struct("Instant")
                 .field("tv_sec", &self.t.tv_sec)
-                .field("tv_nsec", &self.t.tv_nsec)
+                .field("tv_nsec", &self.t.tv_nsec.0)
                 .finish()
         }
     }
@@ -334,7 +342,7 @@ mod inner {
                     let mut t = MaybeUninit::uninit();
                     cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap();
                     let t = unsafe { t.assume_init() };
-                    return Timespec { tv_sec: t.tv_sec, tv_nsec: t.tv_nsec as i64 };
+                    return Timespec::new(t.tv_sec, t.tv_nsec as i64);
                 }
             }
 
diff --git a/src/etc/natvis/libcore.natvis b/src/etc/natvis/libcore.natvis
index a4e8a57e4b1..624d8cc5cc5 100644
--- a/src/etc/natvis/libcore.natvis
+++ b/src/etc/natvis/libcore.natvis
@@ -154,10 +154,10 @@
   </Type>
 
   <Type Name="core::time::Duration">
-    <DisplayString>{secs,d}s {nanos,d}ns</DisplayString>
+    <DisplayString>{secs,d}s {nanos.__0,d}ns</DisplayString>
     <Expand>
       <Item Name="seconds">secs,d</Item>
-      <Item Name="nanoseconds">nanos,d</Item>
+      <Item Name="nanoseconds">nanos.__0,d</Item>
     </Expand>
   </Type>
 </AutoVisualizer>
diff --git a/src/test/rustdoc-js-std/asrawfd.js b/src/test/rustdoc-js-std/asrawfd.js
index fd228a59099..369a34f9c6e 100644
--- a/src/test/rustdoc-js-std/asrawfd.js
+++ b/src/test/rustdoc-js-std/asrawfd.js
@@ -6,9 +6,9 @@ const EXPECTED = {
     'others': [
         // Reproduction test for https://github.com/rust-lang/rust/issues/78724
         // Validate that type alias methods get the correct path.
-        { 'path': 'std::os::unix::io::AsRawFd', 'name': 'as_raw_fd' },
-        { 'path': 'std::os::wasi::io::AsRawFd', 'name': 'as_raw_fd' },
+        { 'path': 'std::os::fd::AsRawFd', 'name': 'as_raw_fd' },
+        { 'path': 'std::os::fd::AsRawFd', 'name': 'as_raw_fd' },
         { 'path': 'std::os::linux::process::PidFd', 'name': 'as_raw_fd' },
-        { 'path': 'std::os::unix::io::RawFd', 'name': 'as_raw_fd' },
+        { 'path': 'std::os::fd::RawFd', 'name': 'as_raw_fd' },
     ],
 };
diff --git a/src/test/ui/error-codes/E0585.stderr b/src/test/ui/error-codes/E0585.stderr
index 7a31c4896ee..53c82fb416b 100644
--- a/src/test/ui/error-codes/E0585.stderr
+++ b/src/test/ui/error-codes/E0585.stderr
@@ -4,7 +4,7 @@ error[E0585]: found a documentation comment that doesn't document anything
 LL |     /// Hello! I'm useless...
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: doc comments must come before what they document, maybe a comment was intended with `//`?
+   = help: doc comments must come before what they document, if a comment was intended use `//`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/generics/issue-94923.rs b/src/test/ui/generics/issue-94923.rs
new file mode 100644
index 00000000000..d337a5dffc9
--- /dev/null
+++ b/src/test/ui/generics/issue-94923.rs
@@ -0,0 +1,49 @@
+// run-pass
+// regression test for issue #94923
+// min-llvm-version: 15.0.0
+// compile-flags: -C opt-level=3
+
+fn f0<T>(mut x: usize) -> usize {
+    for _ in 0..1000 {
+        x *= 123;
+        x %= 99
+    }
+    x + 321 // function composition is not just longer iteration
+}
+
+fn f1<T>(x: usize) -> usize {
+    f0::<(i8, T)>(f0::<(u8, T)>(x))
+}
+
+fn f2<T>(x: usize) -> usize {
+    f1::<(i8, T)>(f1::<(u8, T)>(x))
+}
+
+fn f3<T>(x: usize) -> usize {
+    f2::<(i8, T)>(f2::<(u8, T)>(x))
+}
+
+fn f4<T>(x: usize) -> usize {
+    f3::<(i8, T)>(f3::<(u8, T)>(x))
+}
+
+fn f5<T>(x: usize) -> usize {
+    f4::<(i8, T)>(f4::<(u8, T)>(x))
+}
+
+fn f6<T>(x: usize) -> usize {
+    f5::<(i8, T)>(f5::<(u8, T)>(x))
+}
+
+fn f7<T>(x: usize) -> usize {
+    f6::<(i8, T)>(f6::<(u8, T)>(x))
+}
+
+fn f8<T>(x: usize) -> usize {
+    f7::<(i8, T)>(f7::<(u8, T)>(x))
+}
+
+fn main() {
+    let y = f8::<()>(1);
+    assert_eq!(y, 348);
+}
diff --git a/src/test/ui/parser/doc-after-struct-field.rs b/src/test/ui/parser/doc-after-struct-field.rs
index 5b6f0803603..03faa6733e2 100644
--- a/src/test/ui/parser/doc-after-struct-field.rs
+++ b/src/test/ui/parser/doc-after-struct-field.rs
@@ -1,13 +1,13 @@
 struct X {
     a: u8 /** document a */,
     //~^ ERROR found a documentation comment that doesn't document anything
-    //~| HELP maybe a comment was intended
+    //~| HELP if a comment was intended use `//`
 }
 
 struct Y {
     a: u8 /// document a
     //~^ ERROR found a documentation comment that doesn't document anything
-    //~| HELP maybe a comment was intended
+    //~| HELP if a comment was intended use `//`
 }
 
 fn main() {
diff --git a/src/test/ui/parser/doc-after-struct-field.stderr b/src/test/ui/parser/doc-after-struct-field.stderr
index e3b32a7f035..ae177f1a2d1 100644
--- a/src/test/ui/parser/doc-after-struct-field.stderr
+++ b/src/test/ui/parser/doc-after-struct-field.stderr
@@ -4,7 +4,7 @@ error[E0585]: found a documentation comment that doesn't document anything
 LL |     a: u8 /** document a */,
    |           ^^^^^^^^^^^^^^^^^
    |
-   = help: doc comments must come before what they document, maybe a comment was intended with `//`?
+   = help: doc comments must come before what they document, if a comment was intended use `//`
 
 error[E0585]: found a documentation comment that doesn't document anything
   --> $DIR/doc-after-struct-field.rs:8:11
@@ -12,7 +12,7 @@ error[E0585]: found a documentation comment that doesn't document anything
 LL |     a: u8 /// document a
    |           ^^^^^^^^^^^^^^
    |
-   = help: doc comments must come before what they document, maybe a comment was intended with `//`?
+   = help: doc comments must come before what they document, if a comment was intended use `//`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/parser/doc-before-extern-rbrace.stderr b/src/test/ui/parser/doc-before-extern-rbrace.stderr
index 0edceb268a7..8fa12ec261e 100644
--- a/src/test/ui/parser/doc-before-extern-rbrace.stderr
+++ b/src/test/ui/parser/doc-before-extern-rbrace.stderr
@@ -4,7 +4,7 @@ error[E0584]: found a documentation comment that doesn't document anything
 LL |     /// hi
    |     ^^^^^^ this doc comment doesn't document anything
    |
-   = help: doc comments must come before what they document, maybe a comment was intended with `//`?
+   = help: doc comments must come before what they document, if a comment was intended use `//`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/doc-before-fn-rbrace.rs b/src/test/ui/parser/doc-before-fn-rbrace.rs
index eb355136f1e..c8502164854 100644
--- a/src/test/ui/parser/doc-before-fn-rbrace.rs
+++ b/src/test/ui/parser/doc-before-fn-rbrace.rs
@@ -1,5 +1,5 @@
 fn main() {
     /// document
     //~^ ERROR found a documentation comment that doesn't document anything
-    //~| HELP maybe a comment was intended
+    //~| HELP if a comment was intended use `//`
 }
diff --git a/src/test/ui/parser/doc-before-fn-rbrace.stderr b/src/test/ui/parser/doc-before-fn-rbrace.stderr
index 56241de7092..6ea68e42b4c 100644
--- a/src/test/ui/parser/doc-before-fn-rbrace.stderr
+++ b/src/test/ui/parser/doc-before-fn-rbrace.stderr
@@ -4,7 +4,7 @@ error[E0585]: found a documentation comment that doesn't document anything
 LL |     /// document
    |     ^^^^^^^^^^^^
    |
-   = help: doc comments must come before what they document, maybe a comment was intended with `//`?
+   = help: doc comments must come before what they document, if a comment was intended use `//`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/doc-before-rbrace.rs b/src/test/ui/parser/doc-before-rbrace.rs
index 8ff946344ae..570306f2cdf 100644
--- a/src/test/ui/parser/doc-before-rbrace.rs
+++ b/src/test/ui/parser/doc-before-rbrace.rs
@@ -1,5 +1,5 @@
 fn main() {
     println!("Hi"); /// hi
     //~^ ERROR found a documentation comment that doesn't document anything
-    //~| HELP maybe a comment was intended
+    //~| HELP if a comment was intended use `//`
 }
diff --git a/src/test/ui/parser/doc-before-rbrace.stderr b/src/test/ui/parser/doc-before-rbrace.stderr
index 55719cf6411..4d4741dfe59 100644
--- a/src/test/ui/parser/doc-before-rbrace.stderr
+++ b/src/test/ui/parser/doc-before-rbrace.stderr
@@ -4,7 +4,7 @@ error[E0585]: found a documentation comment that doesn't document anything
 LL |     println!("Hi"); /// hi
    |                     ^^^^^^
    |
-   = help: doc comments must come before what they document, maybe a comment was intended with `//`?
+   = help: doc comments must come before what they document, if a comment was intended use `//`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/doc-before-semi.rs b/src/test/ui/parser/doc-before-semi.rs
index 405a7e1e2a3..444b5874ea2 100644
--- a/src/test/ui/parser/doc-before-semi.rs
+++ b/src/test/ui/parser/doc-before-semi.rs
@@ -1,6 +1,6 @@
 fn main() {
     /// hi
     //~^ ERROR found a documentation comment that doesn't document anything
-    //~| HELP maybe a comment was intended
+    //~| HELP if a comment was intended use `//`
     ;
 }
diff --git a/src/test/ui/parser/doc-before-semi.stderr b/src/test/ui/parser/doc-before-semi.stderr
index e6bade18d0a..a879e13ffbd 100644
--- a/src/test/ui/parser/doc-before-semi.stderr
+++ b/src/test/ui/parser/doc-before-semi.stderr
@@ -4,7 +4,7 @@ error[E0585]: found a documentation comment that doesn't document anything
 LL |     /// hi
    |     ^^^^^^
    |
-   = help: doc comments must come before what they document, maybe a comment was intended with `//`?
+   = help: doc comments must come before what they document, if a comment was intended use `//`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/doc-before-struct-rbrace-1.rs b/src/test/ui/parser/doc-before-struct-rbrace-1.rs
index 3866a3105c2..0c8d90c3b03 100644
--- a/src/test/ui/parser/doc-before-struct-rbrace-1.rs
+++ b/src/test/ui/parser/doc-before-struct-rbrace-1.rs
@@ -2,7 +2,7 @@ struct X {
     a: u8,
     /// document
     //~^ ERROR found a documentation comment that doesn't document anything
-    //~| HELP maybe a comment was intended
+    //~| HELP if a comment was intended use `//`
 }
 
 fn main() {
diff --git a/src/test/ui/parser/doc-before-struct-rbrace-1.stderr b/src/test/ui/parser/doc-before-struct-rbrace-1.stderr
index 92b5133b74d..94934f734b3 100644
--- a/src/test/ui/parser/doc-before-struct-rbrace-1.stderr
+++ b/src/test/ui/parser/doc-before-struct-rbrace-1.stderr
@@ -7,7 +7,7 @@ LL |     a: u8,
 LL |     /// document
    |     ^^^^^^^^^^^^
    |
-   = help: doc comments must come before what they document, maybe a comment was intended with `//`?
+   = help: doc comments must come before what they document, if a comment was intended use `//`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/doc-before-struct-rbrace-2.rs b/src/test/ui/parser/doc-before-struct-rbrace-2.rs
index dda138f1a88..2b2aadf7984 100644
--- a/src/test/ui/parser/doc-before-struct-rbrace-2.rs
+++ b/src/test/ui/parser/doc-before-struct-rbrace-2.rs
@@ -1,7 +1,7 @@
 struct X {
     a: u8 /// document
     //~^ ERROR found a documentation comment that doesn't document anything
-    //~| HELP maybe a comment was intended
+    //~| HELP if a comment was intended use `//`
 }
 
 fn main() {
diff --git a/src/test/ui/parser/doc-before-struct-rbrace-2.stderr b/src/test/ui/parser/doc-before-struct-rbrace-2.stderr
index b25ccab79f9..6b5c8c1f8b5 100644
--- a/src/test/ui/parser/doc-before-struct-rbrace-2.stderr
+++ b/src/test/ui/parser/doc-before-struct-rbrace-2.stderr
@@ -4,7 +4,7 @@ error[E0585]: found a documentation comment that doesn't document anything
 LL |     a: u8 /// document
    |           ^^^^^^^^^^^^
    |
-   = help: doc comments must come before what they document, maybe a comment was intended with `//`?
+   = help: doc comments must come before what they document, if a comment was intended use `//`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/doc-inside-trait-item.stderr b/src/test/ui/parser/doc-inside-trait-item.stderr
index 246255a0a46..900124adcc3 100644
--- a/src/test/ui/parser/doc-inside-trait-item.stderr
+++ b/src/test/ui/parser/doc-inside-trait-item.stderr
@@ -4,7 +4,7 @@ error[E0584]: found a documentation comment that doesn't document anything
 LL |     /// empty doc
    |     ^^^^^^^^^^^^^ this doc comment doesn't document anything
    |
-   = help: doc comments must come before what they document, maybe a comment was intended with `//`?
+   = help: doc comments must come before what they document, if a comment was intended use `//`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/issues/issue-34222-1.stderr b/src/test/ui/parser/issues/issue-34222-1.stderr
index 0799656b06b..b451484ba22 100644
--- a/src/test/ui/parser/issues/issue-34222-1.stderr
+++ b/src/test/ui/parser/issues/issue-34222-1.stderr
@@ -4,7 +4,7 @@ error[E0585]: found a documentation comment that doesn't document anything
 LL |     /// comment
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: doc comments must come before what they document, maybe a comment was intended with `//`?
+   = help: doc comments must come before what they document, if a comment was intended use `//`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/issues/issue-48636.stderr b/src/test/ui/parser/issues/issue-48636.stderr
index 1a6e4cfd2b2..6177870d1ce 100644
--- a/src/test/ui/parser/issues/issue-48636.stderr
+++ b/src/test/ui/parser/issues/issue-48636.stderr
@@ -8,7 +8,7 @@ LL |     x: u8
 LL |     /// The ID of the parent core
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: doc comments must come before what they document, maybe a comment was intended with `//`?
+   = help: doc comments must come before what they document, if a comment was intended use `//`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/regions/issue-102374.rs b/src/test/ui/regions/issue-102374.rs
new file mode 100644
index 00000000000..e0a1164211a
--- /dev/null
+++ b/src/test/ui/regions/issue-102374.rs
@@ -0,0 +1,20 @@
+use std::cell::Cell;
+
+#[rustfmt::skip]
+fn f(
+    f: for<'a, 'b, 'c, 'd, 'e, 'f, 'g,
+           'h, 'i, 'j, 'k, 'l, 'm, 'n,
+           'o, 'p, 'q, 'r, 's, 't, 'u,
+           'v, 'w, 'x, 'y, 'z, 'z0>
+        fn(Cell<(&   i32, &'a i32, &'b i32, &'c i32, &'d i32,
+                 &'e i32, &'f i32, &'g i32, &'h i32, &'i i32,
+                 &'j i32, &'k i32, &'l i32, &'m i32, &'n i32,
+                 &'o i32, &'p i32, &'q i32, &'r i32, &'s i32,
+                 &'t i32, &'u i32, &'v i32, &'w i32, &'x i32,
+                 &'y i32, &'z i32, &'z0 i32)>),
+) -> i32 {
+    f
+    //~^ ERROR mismatched types
+}
+
+fn main() {}
diff --git a/src/test/ui/regions/issue-102374.stderr b/src/test/ui/regions/issue-102374.stderr
new file mode 100644
index 00000000000..31b855c36be
--- /dev/null
+++ b/src/test/ui/regions/issue-102374.stderr
@@ -0,0 +1,14 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-102374.rs:16:5
+   |
+LL | ) -> i32 {
+   |      --- expected `i32` because of return type
+LL |     f
+   |     ^ expected `i32`, found fn pointer
+   |
+   = note:    expected type `i32`
+           found fn pointer `for<'z1, 'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n, 'o, 'p, 'q, 'r, 's, 't, 'u, 'v, 'w, 'x, 'y, 'z, 'z0> fn(Cell<(&'z1 i32, &'a i32, &'b i32, &'c i32, &'d i32, &'e i32, &'f i32, &'g i32, &'h i32, &'i i32, &'j i32, &'k i32, &'l i32, &'m i32, &'n i32, &'o i32, &'p i32, &'q i32, &'r i32, &'s i32, &'t i32, &'u i32, &'v i32, &'w i32, &'x i32, &'y i32, &'z i32, &'z0 i32)>)`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.