about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-09-19 18:52:10 +0000
committerbors <bors@rust-lang.org>2023-09-19 18:52:10 +0000
commitac5ac4754a4a837f7138a1fa5ab01475b63fc9fe (patch)
tree8d4ed7161ccfe6c3121efe178f6336f1bf515079
parent42f5828b01817e2aa67458c0c50db0b1c240f0bd (diff)
parent1b862186a78a2718cf9e644a647e5ff7288a4149 (diff)
downloadrust-ac5ac4754a4a837f7138a1fa5ab01475b63fc9fe.tar.gz
rust-ac5ac4754a4a837f7138a1fa5ab01475b63fc9fe.zip
Auto merge of #115979 - GuillaumeGomez:rollup-06ujzgh, r=GuillaumeGomez
Rollup of 6 pull requests

Successful merges:

 - #113383 (style-guide: Add section on bugs, and resolving bugs)
 - #115499 (rustc_target/riscv: Fix passing of transparent unions with only one non-ZST member)
 - #115801 (Detect cycle errors hidden by opaques during monomorphization)
 - #115947 (Custom code classes in docs warning)
 - #115957 (fix mismatched symbols)
 - #115958 (explain mysterious addition in float minimum/maximum)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs4
-rw-r--r--compiler/rustc_target/src/abi/call/riscv.rs11
-rw-r--r--compiler/rustc_target/src/abi/mod.rs8
-rw-r--r--compiler/rustc_traits/src/normalize_projection_ty.rs26
-rw-r--r--library/core/src/num/f32.rs1
-rw-r--r--library/core/src/num/f64.rs1
-rw-r--r--src/bootstrap/job.rs2
-rw-r--r--src/doc/style-guide/src/README.md13
-rw-r--r--src/librustdoc/html/markdown.rs275
-rw-r--r--src/librustdoc/html/markdown/tests.rs21
-rw-r--r--src/librustdoc/passes/check_custom_code_classes.rs6
-rw-r--r--tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs8
-rw-r--r--tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr26
-rw-r--r--tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs5
-rw-r--r--tests/ui/abi/compatibility.rs3
-rw-r--r--tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs38
-rw-r--r--tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr5
-rw-r--r--tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs29
-rw-r--r--tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr5
-rw-r--r--tests/ui/type-alias-impl-trait/mututally-recursive-overflow.rs40
-rw-r--r--tests/ui/type-alias-impl-trait/mututally-recursive-overflow.stderr5
21 files changed, 385 insertions, 147 deletions
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 9a0e72d7b64..8b425ce0267 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -1118,6 +1118,10 @@ where
     fn is_unit(this: TyAndLayout<'tcx>) -> bool {
         matches!(this.ty.kind(), ty::Tuple(list) if list.len() == 0)
     }
+
+    fn is_transparent(this: TyAndLayout<'tcx>) -> bool {
+        matches!(this.ty.kind(), ty::Adt(def, _) if def.repr().transparent())
+    }
 }
 
 /// Calculates whether a function's ABI can unwind or not.
diff --git a/compiler/rustc_target/src/abi/call/riscv.rs b/compiler/rustc_target/src/abi/call/riscv.rs
index d90dce2a087..93a2045632a 100644
--- a/compiler/rustc_target/src/abi/call/riscv.rs
+++ b/compiler/rustc_target/src/abi/call/riscv.rs
@@ -89,6 +89,17 @@ where
             }
             FieldsShape::Union(_) => {
                 if !arg_layout.is_zst() {
+                    if arg_layout.is_transparent() {
+                        let non_1zst_elem = arg_layout.non_1zst_field(cx).expect("not exactly one non-1-ZST field in non-ZST repr(transparent) union").1;
+                        return should_use_fp_conv_helper(
+                            cx,
+                            &non_1zst_elem,
+                            xlen,
+                            flen,
+                            field1_kind,
+                            field2_kind,
+                        );
+                    }
                     return Err(CannotUseFpConv);
                 }
             }
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
index 636adcf6b17..74fe98920c4 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -66,6 +66,7 @@ pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug {
     fn is_never(this: TyAndLayout<'a, Self>) -> bool;
     fn is_tuple(this: TyAndLayout<'a, Self>) -> bool;
     fn is_unit(this: TyAndLayout<'a, Self>) -> bool;
+    fn is_transparent(this: TyAndLayout<'a, Self>) -> bool;
 }
 
 impl<'a, Ty> TyAndLayout<'a, Ty> {
@@ -136,6 +137,13 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
         Ty::is_unit(self)
     }
 
+    pub fn is_transparent<C>(self) -> bool
+    where
+        Ty: TyAbiInterface<'a, C>,
+    {
+        Ty::is_transparent(self)
+    }
+
     pub fn offset_of_subfield<C>(self, cx: &C, indices: impl Iterator<Item = usize>) -> Size
     where
         Ty: TyAbiInterface<'a, C>,
diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs
index 0dbac56b47d..01bb1ca70eb 100644
--- a/compiler/rustc_traits/src/normalize_projection_ty.rs
+++ b/compiler/rustc_traits/src/normalize_projection_ty.rs
@@ -3,10 +3,13 @@ use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
 use rustc_trait_selection::infer::InferCtxtBuilderExt;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::query::{
     normalize::NormalizationResult, CanonicalProjectionGoal, NoSolution,
 };
-use rustc_trait_selection::traits::{self, ObligationCause, SelectionContext};
+use rustc_trait_selection::traits::{
+    self, FulfillmentErrorCode, ObligationCause, SelectionContext,
+};
 use std::sync::atomic::Ordering;
 
 pub(crate) fn provide(p: &mut Providers) {
@@ -40,6 +43,27 @@ fn normalize_projection_ty<'tcx>(
                 &mut obligations,
             );
             ocx.register_obligations(obligations);
+            // #112047: With projections and opaques, we are able to create opaques that
+            // are recursive (given some substitution of the opaque's type variables).
+            // In that case, we may only realize a cycle error when calling
+            // `normalize_erasing_regions` in mono.
+            if !ocx.infcx.next_trait_solver() {
+                let errors = ocx.select_where_possible();
+                if !errors.is_empty() {
+                    // Rustdoc may attempt to normalize type alias types which are not
+                    // well-formed. Rustdoc also normalizes types that are just not
+                    // well-formed, since we don't do as much HIR analysis (checking
+                    // that impl vars are constrained by the signature, for example).
+                    if !tcx.sess.opts.actually_rustdoc {
+                        for error in &errors {
+                            if let FulfillmentErrorCode::CodeCycle(cycle) = &error.code {
+                                ocx.infcx.err_ctxt().report_overflow_obligation_cycle(cycle);
+                            }
+                        }
+                    }
+                    return Err(NoSolution);
+                }
+            }
             // FIXME(associated_const_equality): All users of normalize_projection_ty expected
             // a type, but there is the possibility it could've been a const now. Maybe change
             // it to a Term later?
diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs
index 3144db19707..290f649f9ac 100644
--- a/library/core/src/num/f32.rs
+++ b/library/core/src/num/f32.rs
@@ -957,6 +957,7 @@ impl f32 {
         } else if self == other {
             if self.is_sign_negative() && other.is_sign_positive() { self } else { other }
         } else {
+            // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
             self + other
         }
     }
diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs
index 20833defc41..7569d2cd6ca 100644
--- a/library/core/src/num/f64.rs
+++ b/library/core/src/num/f64.rs
@@ -968,6 +968,7 @@ impl f64 {
         } else if self == other {
             if self.is_sign_negative() && other.is_sign_positive() { self } else { other }
         } else {
+            // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
             self + other
         }
     }
diff --git a/src/bootstrap/job.rs b/src/bootstrap/job.rs
index 4fb00f65dc1..b0a97b540ec 100644
--- a/src/bootstrap/job.rs
+++ b/src/bootstrap/job.rs
@@ -134,7 +134,7 @@ pub unsafe fn setup(build: &mut Build) {
     // If this failed, well at least we tried! An example of DuplicateHandle
     // failing in the past has been when the wrong python2 package spawned this
     // build system (e.g., the `python2` package in MSYS instead of
-    // `mingw-w64-x86_64-python2`. Not sure why it failed, but the "failure
+    // `mingw-w64-x86_64-python2`). Not sure why it failed, but the "failure
     // mode" here is that we only clean everything up when the build system
     // dies, not when the python parent does, so not too bad.
     if r.is_err() {
diff --git a/src/doc/style-guide/src/README.md b/src/doc/style-guide/src/README.md
index f4d75967370..9a59d80556e 100644
--- a/src/doc/style-guide/src/README.md
+++ b/src/doc/style-guide/src/README.md
@@ -32,6 +32,19 @@ This should not be interpreted as forbidding developers from following a
 non-default style, or forbidding tools from adding any particular configuration
 options.
 
+## Bugs
+
+If the style guide differs from rustfmt, that may represent a bug in rustfmt,
+or a bug in the style guide; either way, please report it to the style team or
+the rustfmt team or both, for investigation and fix.
+
+If implementing a new formatting tool based on the style guide and default Rust
+style, please test it on the corpus of existing Rust code, and avoid causing
+widespread breakage. The implementation and testing of such a tool may surface
+bugs in either the style guide or rustfmt, as well as bugs in the tool itself.
+
+We typically resolve bugs in a fashion that avoids widespread breakage.
+
 ## Formatting conventions
 
 ### Indentation and line width
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 59958fbaef9..6dbf2185e3d 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -868,7 +868,7 @@ impl<'tcx> ExtraInfo<'tcx> {
 
 #[derive(Eq, PartialEq, Clone, Debug)]
 pub(crate) struct LangString {
-    original: String,
+    pub(crate) original: String,
     pub(crate) should_panic: bool,
     pub(crate) no_run: bool,
     pub(crate) ignore: Ignore,
@@ -893,11 +893,13 @@ pub(crate) enum Ignore {
 /// ```eBNF
 /// lang-string = *(token-list / delimited-attribute-list / comment)
 ///
-/// bareword = CHAR *(CHAR)
+/// bareword = LEADINGCHAR *(CHAR)
+/// bareword-without-leading-char = CHAR *(CHAR)
 /// quoted-string = QUOTE *(NONQUOTE) QUOTE
 /// token = bareword / quoted-string
+/// token-without-leading-char = bareword-without-leading-char / quoted-string
 /// sep = COMMA/WS *(COMMA/WS)
-/// attribute = (DOT token)/(token EQUAL token)
+/// attribute = (DOT token)/(token EQUAL token-without-leading-char)
 /// attribute-list = [sep] attribute *(sep attribute) [sep]
 /// delimited-attribute-list = OPEN-CURLY-BRACKET attribute-list CLOSE-CURLY-BRACKET
 /// token-list = [sep] token *(sep token) [sep]
@@ -907,8 +909,15 @@ pub(crate) enum Ignore {
 /// CLOSE_PARENT = ")"
 /// OPEN-CURLY-BRACKET = "{"
 /// CLOSE-CURLY-BRACKET = "}"
-/// CHAR = ALPHA / DIGIT / "_" / "-" / ":"
-/// QUOTE = %x22
+/// LEADINGCHAR = ALPHA | DIGIT | "_" | "-" | ":"
+/// ; All ASCII punctuation except comma, quote, equals, backslash, grave (backquote) and braces.
+/// ; Comma is used to separate language tokens, so it can't be used in one.
+/// ; Quote is used to allow otherwise-disallowed characters in language tokens.
+/// ; Equals is used to make key=value pairs in attribute blocks.
+/// ; Backslash and grave are special Markdown characters.
+/// ; Braces are used to start an attribute block.
+/// CHAR = ALPHA | DIGIT | "_" | "-" | ":" | "." | "!" | "#" | "$" | "%" | "&" | "*" | "+" | "/" |
+///        ";" | "<" | ">" | "?" | "@" | "^" | "|" | "~"
 /// NONQUOTE = %x09 / %x20 / %x21 / %x23-7E ; TAB / SPACE / all printable characters except `"`
 /// COMMA = ","
 /// DOT = "."
@@ -932,9 +941,12 @@ pub(crate) enum LangStringToken<'a> {
     KeyValueAttribute(&'a str, &'a str),
 }
 
-fn is_bareword_char(c: char) -> bool {
+fn is_leading_char(c: char) -> bool {
     c == '_' || c == '-' || c == ':' || c.is_ascii_alphabetic() || c.is_ascii_digit()
 }
+fn is_bareword_char(c: char) -> bool {
+    is_leading_char(c) || ".!#$%&*+/;<>?@^|~".contains(c)
+}
 fn is_separator(c: char) -> bool {
     c == ' ' || c == ',' || c == '\t'
 }
@@ -1077,7 +1089,7 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> {
                 return self.next();
             } else if c == '.' {
                 return self.parse_class(pos);
-            } else if c == '"' || is_bareword_char(c) {
+            } else if c == '"' || is_leading_char(c) {
                 return self.parse_key_value(c, pos);
             } else {
                 self.emit_error(format!("unexpected character `{c}`"));
@@ -1107,7 +1119,11 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> {
                     return None;
                 }
                 let indices = self.parse_string(pos)?;
-                if let Some((_, c)) = self.inner.peek().copied() && c != '{' && !is_separator(c) && c != '(' {
+                if let Some((_, c)) = self.inner.peek().copied() &&
+                    c != '{' &&
+                    !is_separator(c) &&
+                    c != '('
+                {
                     self.emit_error(format!("expected ` `, `{{` or `,` after `\"`, found `{c}`"));
                     return None;
                 }
@@ -1115,8 +1131,6 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> {
             } else if c == '{' {
                 self.is_in_attribute_block = true;
                 return self.next();
-            } else if is_bareword_char(c) {
-                continue;
             } else if is_separator(c) {
                 if pos != start {
                     return Some(LangStringToken::LangToken(&self.data[start..pos]));
@@ -1130,6 +1144,10 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> {
                     return Some(LangStringToken::LangToken(&self.data[start..pos]));
                 }
                 return self.next();
+            } else if pos == start && is_leading_char(c) {
+                continue;
+            } else if pos != start && is_bareword_char(c) {
+                continue;
             } else {
                 self.emit_error(format!("unexpected character `{c}`"));
                 return None;
@@ -1158,6 +1176,29 @@ impl<'a, 'tcx> Iterator for TagIterator<'a, 'tcx> {
     }
 }
 
+fn tokens(string: &str) -> impl Iterator<Item = LangStringToken<'_>> {
+    // Pandoc, which Rust once used for generating documentation,
+    // expects lang strings to be surrounded by `{}` and for each token
+    // to be proceeded by a `.`. Since some of these lang strings are still
+    // loose in the wild, we strip a pair of surrounding `{}` from the lang
+    // string and a leading `.` from each token.
+
+    let string = string.trim();
+
+    let first = string.chars().next();
+    let last = string.chars().last();
+
+    let string =
+        if first == Some('{') && last == Some('}') { &string[1..string.len() - 1] } else { string };
+
+    string
+        .split(|c| c == ',' || c == ' ' || c == '\t')
+        .map(str::trim)
+        .map(|token| token.strip_prefix('.').unwrap_or(token))
+        .filter(|token| !token.is_empty())
+        .map(|token| LangStringToken::LangToken(token))
+}
+
 impl Default for LangString {
     fn default() -> Self {
         Self {
@@ -1208,122 +1249,130 @@ impl LangString {
 
         data.original = string.to_owned();
 
-        for token in TagIterator::new(string, extra) {
-            match token {
-                LangStringToken::LangToken("should_panic") => {
-                    data.should_panic = true;
-                    seen_rust_tags = !seen_other_tags;
-                }
-                LangStringToken::LangToken("no_run") => {
-                    data.no_run = true;
-                    seen_rust_tags = !seen_other_tags;
-                }
-                LangStringToken::LangToken("ignore") => {
-                    data.ignore = Ignore::All;
-                    seen_rust_tags = !seen_other_tags;
-                }
-                LangStringToken::LangToken(x) if x.starts_with("ignore-") => {
-                    if enable_per_target_ignores {
-                        ignores.push(x.trim_start_matches("ignore-").to_owned());
+        let mut call = |tokens: &mut dyn Iterator<Item = LangStringToken<'_>>| {
+            for token in tokens {
+                match token {
+                    LangStringToken::LangToken("should_panic") => {
+                        data.should_panic = true;
                         seen_rust_tags = !seen_other_tags;
                     }
-                }
-                LangStringToken::LangToken("rust") => {
-                    data.rust = true;
-                    seen_rust_tags = true;
-                }
-                LangStringToken::LangToken("custom") => {
-                    if custom_code_classes_in_docs {
-                        seen_custom_tag = true;
-                    } else {
-                        seen_other_tags = true;
+                    LangStringToken::LangToken("no_run") => {
+                        data.no_run = true;
+                        seen_rust_tags = !seen_other_tags;
                     }
-                }
-                LangStringToken::LangToken("test_harness") => {
-                    data.test_harness = true;
-                    seen_rust_tags = !seen_other_tags || seen_rust_tags;
-                }
-                LangStringToken::LangToken("compile_fail") => {
-                    data.compile_fail = true;
-                    seen_rust_tags = !seen_other_tags || seen_rust_tags;
-                    data.no_run = true;
-                }
-                LangStringToken::LangToken(x) if x.starts_with("edition") => {
-                    data.edition = x[7..].parse::<Edition>().ok();
-                }
-                LangStringToken::LangToken(x)
-                    if allow_error_code_check && x.starts_with('E') && x.len() == 5 =>
-                {
-                    if x[1..].parse::<u32>().is_ok() {
-                        data.error_codes.push(x.to_owned());
+                    LangStringToken::LangToken("ignore") => {
+                        data.ignore = Ignore::All;
+                        seen_rust_tags = !seen_other_tags;
+                    }
+                    LangStringToken::LangToken(x) if x.starts_with("ignore-") => {
+                        if enable_per_target_ignores {
+                            ignores.push(x.trim_start_matches("ignore-").to_owned());
+                            seen_rust_tags = !seen_other_tags;
+                        }
+                    }
+                    LangStringToken::LangToken("rust") => {
+                        data.rust = true;
+                        seen_rust_tags = true;
+                    }
+                    LangStringToken::LangToken("custom") => {
+                        if custom_code_classes_in_docs {
+                            seen_custom_tag = true;
+                        } else {
+                            seen_other_tags = true;
+                        }
+                    }
+                    LangStringToken::LangToken("test_harness") => {
+                        data.test_harness = true;
                         seen_rust_tags = !seen_other_tags || seen_rust_tags;
-                    } else {
-                        seen_other_tags = true;
                     }
-                }
-                LangStringToken::LangToken(x) if extra.is_some() => {
-                    let s = x.to_lowercase();
-                    if let Some((flag, help)) = if s == "compile-fail"
-                        || s == "compile_fail"
-                        || s == "compilefail"
+                    LangStringToken::LangToken("compile_fail") => {
+                        data.compile_fail = true;
+                        seen_rust_tags = !seen_other_tags || seen_rust_tags;
+                        data.no_run = true;
+                    }
+                    LangStringToken::LangToken(x) if x.starts_with("edition") => {
+                        data.edition = x[7..].parse::<Edition>().ok();
+                    }
+                    LangStringToken::LangToken(x)
+                        if allow_error_code_check && x.starts_with('E') && x.len() == 5 =>
                     {
-                        Some((
-                            "compile_fail",
-                            "the code block will either not be tested if not marked as a rust one \
-                             or won't fail if it compiles successfully",
-                        ))
-                    } else if s == "should-panic" || s == "should_panic" || s == "shouldpanic" {
-                        Some((
-                            "should_panic",
-                            "the code block will either not be tested if not marked as a rust one \
-                             or won't fail if it doesn't panic when running",
-                        ))
-                    } else if s == "no-run" || s == "no_run" || s == "norun" {
-                        Some((
-                            "no_run",
-                            "the code block will either not be tested if not marked as a rust one \
-                             or will be run (which you might not want)",
-                        ))
-                    } else if s == "test-harness" || s == "test_harness" || s == "testharness" {
-                        Some((
-                            "test_harness",
-                            "the code block will either not be tested if not marked as a rust one \
-                             or the code will be wrapped inside a main function",
-                        ))
-                    } else {
-                        None
-                    } {
-                        if let Some(extra) = extra {
-                            extra.error_invalid_codeblock_attr_with_help(
-                                format!("unknown attribute `{x}`. Did you mean `{flag}`?"),
-                                help,
-                            );
+                        if x[1..].parse::<u32>().is_ok() {
+                            data.error_codes.push(x.to_owned());
+                            seen_rust_tags = !seen_other_tags || seen_rust_tags;
+                        } else {
+                            seen_other_tags = true;
                         }
                     }
-                    seen_other_tags = true;
-                    data.unknown.push(x.to_owned());
-                }
-                LangStringToken::LangToken(x) => {
-                    seen_other_tags = true;
-                    data.unknown.push(x.to_owned());
-                }
-                LangStringToken::KeyValueAttribute(key, value) => {
-                    if custom_code_classes_in_docs {
-                        if key == "class" {
-                            data.added_classes.push(value.to_owned());
-                        } else if let Some(extra) = extra {
-                            extra.error_invalid_codeblock_attr(format!(
-                                "unsupported attribute `{key}`"
-                            ));
+                    LangStringToken::LangToken(x) if extra.is_some() => {
+                        let s = x.to_lowercase();
+                        if let Some((flag, help)) = if s == "compile-fail"
+                            || s == "compile_fail"
+                            || s == "compilefail"
+                        {
+                            Some((
+                                "compile_fail",
+                                "the code block will either not be tested if not marked as a rust one \
+                                 or won't fail if it compiles successfully",
+                            ))
+                        } else if s == "should-panic" || s == "should_panic" || s == "shouldpanic" {
+                            Some((
+                                "should_panic",
+                                "the code block will either not be tested if not marked as a rust one \
+                                 or won't fail if it doesn't panic when running",
+                            ))
+                        } else if s == "no-run" || s == "no_run" || s == "norun" {
+                            Some((
+                                "no_run",
+                                "the code block will either not be tested if not marked as a rust one \
+                                 or will be run (which you might not want)",
+                            ))
+                        } else if s == "test-harness" || s == "test_harness" || s == "testharness" {
+                            Some((
+                                "test_harness",
+                                "the code block will either not be tested if not marked as a rust one \
+                                 or the code will be wrapped inside a main function",
+                            ))
+                        } else {
+                            None
+                        } {
+                            if let Some(extra) = extra {
+                                extra.error_invalid_codeblock_attr_with_help(
+                                    format!("unknown attribute `{x}`. Did you mean `{flag}`?"),
+                                    help,
+                                );
+                            }
                         }
-                    } else {
                         seen_other_tags = true;
+                        data.unknown.push(x.to_owned());
+                    }
+                    LangStringToken::LangToken(x) => {
+                        seen_other_tags = true;
+                        data.unknown.push(x.to_owned());
+                    }
+                    LangStringToken::KeyValueAttribute(key, value) => {
+                        if custom_code_classes_in_docs {
+                            if key == "class" {
+                                data.added_classes.push(value.to_owned());
+                            } else if let Some(extra) = extra {
+                                extra.error_invalid_codeblock_attr(format!(
+                                    "unsupported attribute `{key}`"
+                                ));
+                            }
+                        } else {
+                            seen_other_tags = true;
+                        }
+                    }
+                    LangStringToken::ClassAttribute(class) => {
+                        data.added_classes.push(class.to_owned());
                     }
-                }
-                LangStringToken::ClassAttribute(class) => {
-                    data.added_classes.push(class.to_owned());
                 }
             }
+        };
+
+        if custom_code_classes_in_docs {
+            call(&mut TagIterator::new(string, extra).into_iter())
+        } else {
+            call(&mut tokens(string))
         }
 
         // ignore-foo overrides ignore
diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs
index 32957ac57fa..5eba1d0609f 100644
--- a/src/librustdoc/html/markdown/tests.rs
+++ b/src/librustdoc/html/markdown/tests.rs
@@ -226,13 +226,28 @@ fn test_lang_string_parse() {
         ..Default::default()
     });
     // error
-    t(LangString { original: "{.first.second}".into(), rust: true, ..Default::default() });
+    t(LangString {
+        original: "{.first.second}".into(),
+        rust: true,
+        added_classes: vec!["first.second".into()],
+        ..Default::default()
+    });
     // error
     t(LangString { original: "{class=first=second}".into(), rust: true, ..Default::default() });
     // error
-    t(LangString { original: "{class=first.second}".into(), rust: true, ..Default::default() });
+    t(LangString {
+        original: "{class=first.second}".into(),
+        rust: true,
+        added_classes: vec!["first.second".into()],
+        ..Default::default()
+    });
     // error
-    t(LangString { original: "{class=.first}".into(), rust: true, ..Default::default() });
+    t(LangString {
+        original: "{class=.first}".into(),
+        added_classes: vec![".first".into()],
+        rust: true,
+        ..Default::default()
+    });
     t(LangString {
         original: r#"{class="first"}"#.into(),
         added_classes: vec!["first".into()],
diff --git a/src/librustdoc/passes/check_custom_code_classes.rs b/src/librustdoc/passes/check_custom_code_classes.rs
index 1a703a4e967..6266d3ff51d 100644
--- a/src/librustdoc/passes/check_custom_code_classes.rs
+++ b/src/librustdoc/passes/check_custom_code_classes.rs
@@ -21,6 +21,10 @@ pub(crate) const CHECK_CUSTOM_CODE_CLASSES: Pass = Pass {
 };
 
 pub(crate) fn check_custom_code_classes(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
+    if cx.tcx.features().custom_code_classes_in_docs {
+        // Nothing to check here if the feature is enabled.
+        return krate;
+    }
     let mut coll = CustomCodeClassLinter { cx };
 
     coll.fold_crate(krate)
@@ -59,7 +63,7 @@ pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item)
     let dox = item.attrs.doc_value();
     find_codes(&dox, &mut tests, ErrorCodes::No, false, None, true, true);
 
-    if !tests.custom_classes_found.is_empty() && !cx.tcx.features().custom_code_classes_in_docs {
+    if !tests.custom_classes_found.is_empty() {
         let span = item.attr_span(cx.tcx);
         let sess = &cx.tcx.sess.parse_sess;
         let mut err = sess
diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs
index dd8759b7e37..5398d5833c7 100644
--- a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs
+++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs
@@ -57,25 +57,23 @@ pub fn foo8() {}
 /// ```{class=one=two}
 /// main;
 /// ```
-//~^^^ ERROR unexpected `=`
+//~^^^ ERROR unexpected `=` character
 pub fn foo9() {}
 
 /// ```{.one.two}
 /// main;
 /// ```
-//~^^^ ERROR unexpected `.` character
 pub fn foo10() {}
 
-/// ```{class=.one}
+/// ```{class=(one}
 /// main;
 /// ```
-//~^^^ ERROR unexpected `.` character after `=`
+//~^^^ ERROR unexpected `(` character after `=`
 pub fn foo11() {}
 
 /// ```{class=one.two}
 /// main;
 /// ```
-//~^^^ ERROR unexpected `.` character
 pub fn foo12() {}
 
 /// ```{(comment)}
diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr
index 3e0dc4b2f7a..14b4b3bab3f 100644
--- a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr
+++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr
@@ -77,37 +77,21 @@ LL | | /// main;
 LL | | /// ```
    | |_______^
 
-error: unexpected `.` character
-  --> $DIR/custom_code_classes_in_docs-warning.rs:63:1
+error: unexpected `(` character after `=`
+  --> $DIR/custom_code_classes_in_docs-warning.rs:68:1
    |
-LL | / /// ```{.one.two}
-LL | | /// main;
-LL | | /// ```
-   | |_______^
-
-error: unexpected `.` character after `=`
-  --> $DIR/custom_code_classes_in_docs-warning.rs:69:1
-   |
-LL | / /// ```{class=.one}
-LL | | /// main;
-LL | | /// ```
-   | |_______^
-
-error: unexpected `.` character
-  --> $DIR/custom_code_classes_in_docs-warning.rs:75:1
-   |
-LL | / /// ```{class=one.two}
+LL | / /// ```{class=(one}
 LL | | /// main;
 LL | | /// ```
    | |_______^
 
 error: unexpected character `(`
-  --> $DIR/custom_code_classes_in_docs-warning.rs:81:1
+  --> $DIR/custom_code_classes_in_docs-warning.rs:79:1
    |
 LL | / /// ```{(comment)}
 LL | | /// main;
 LL | | /// ```
    | |_______^
 
-error: aborting due to 13 previous errors
+error: aborting due to 11 previous errors
 
diff --git a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs
index 3f0f8b5b9f9..99263a944f8 100644
--- a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs
+++ b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs
@@ -8,3 +8,8 @@
 //~| NOTE see issue #79483 <https://github.com/rust-lang/rust/issues/79483>
 //~| HELP add `#![feature(custom_code_classes_in_docs)]` to the crate attributes to enable
 pub struct Bar;
+
+/// ```ASN.1
+/// int main(void) { return 0; }
+/// ```
+pub struct Bar2;
diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs
index b3e75bb8233..d4f42cdda97 100644
--- a/tests/ui/abi/compatibility.rs
+++ b/tests/ui/abi/compatibility.rs
@@ -10,7 +10,6 @@ use std::ptr::NonNull;
 // Hence there are `cfg` throughout this test to disable parts of it on those targets.
 // sparc64: https://github.com/rust-lang/rust/issues/115336
 // mips64: https://github.com/rust-lang/rust/issues/115404
-// riscv64: https://github.com/rust-lang/rust/issues/115481
 // loongarch64: https://github.com/rust-lang/rust/issues/115509
 
 macro_rules! assert_abi_compatible {
@@ -110,7 +109,7 @@ macro_rules! test_transparent {
             test_abi_compatible!(wrap1, $t, Wrapper1<$t>);
             test_abi_compatible!(wrap2, $t, Wrapper2<$t>);
             test_abi_compatible!(wrap3, $t, Wrapper3<$t>);
-            #[cfg(not(any(target_arch = "riscv64", target_arch = "loongarch64")))]
+            #[cfg(not(target_arch = "loongarch64"))]
             test_abi_compatible!(wrap4, $t, WrapperUnion<$t>);
         }
     };
diff --git a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs
new file mode 100644
index 00000000000..85d17ddff94
--- /dev/null
+++ b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs
@@ -0,0 +1,38 @@
+// edition: 2021
+// build-fail
+//~^^ ERROR overflow evaluating the requirement `<A as Second>::{opaque#0} == _`
+
+#![feature(async_fn_in_trait)]
+
+fn main() {
+    let _ = async {
+        A.first().await.second().await;
+    };
+}
+
+pub trait First {
+    type Second: Second;
+    async fn first(self) -> Self::Second;
+}
+
+struct A;
+
+impl First for A {
+    type Second = A;
+    async fn first(self) -> Self::Second {
+        A
+    }
+}
+
+pub trait Second {
+    async fn second(self);
+}
+
+impl<C> Second for C
+where
+    C: First,
+{
+    async fn second(self) {
+        self.first().await.second().await;
+    }
+}
diff --git a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr
new file mode 100644
index 00000000000..3f487a6e5fe
--- /dev/null
+++ b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr
@@ -0,0 +1,5 @@
+error[E0275]: overflow evaluating the requirement `<A as Second>::{opaque#0} == _`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs
new file mode 100644
index 00000000000..baa22e1ce18
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs
@@ -0,0 +1,29 @@
+// edition: 2021
+// build-fail
+//~^^ ERROR overflow evaluating the requirement `<() as Recur>::Recur == _`
+
+#![feature(impl_trait_in_assoc_type)]
+
+use core::future::Future;
+
+trait Recur {
+    type Recur: Future<Output = ()>;
+
+    fn recur(self) -> Self::Recur;
+}
+
+async fn recur(t: impl Recur) {
+    t.recur().await;
+}
+
+impl Recur for () {
+    type Recur = impl Future<Output = ()>;
+
+    fn recur(self) -> Self::Recur {
+        async move { recur(self).await; }
+    }
+}
+
+fn main() {
+    recur(());
+}
diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr
new file mode 100644
index 00000000000..0238694c24d
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr
@@ -0,0 +1,5 @@
+error[E0275]: overflow evaluating the requirement `<() as Recur>::Recur == _`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.rs b/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.rs
new file mode 100644
index 00000000000..1ccd1b0cbad
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.rs
@@ -0,0 +1,40 @@
+// edition: 2021
+// build-fail
+//~^^ ERROR overflow evaluating the requirement `<() as B>::Assoc == _`
+
+#![feature(rustc_attrs)]
+#![feature(impl_trait_in_assoc_type)]
+
+#[rustc_coinductive]
+trait A {
+    type Assoc;
+
+    fn test() -> Self::Assoc;
+}
+
+#[rustc_coinductive]
+trait B {
+    type Assoc;
+
+    fn test() -> Self::Assoc;
+}
+
+impl<T: A> B for T {
+    type Assoc = impl Sized;
+
+    fn test() -> <Self as B>::Assoc {
+        <T as A>::test()
+    }
+}
+
+fn main() {
+    <() as A>::test();
+}
+
+impl<T: B> A for T {
+    type Assoc = impl Sized;
+
+    fn test() -> <Self as A>::Assoc {
+        <T as B>::test()
+    }
+}
diff --git a/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.stderr b/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.stderr
new file mode 100644
index 00000000000..49c59f7eb37
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.stderr
@@ -0,0 +1,5 @@
+error[E0275]: overflow evaluating the requirement `<() as B>::Assoc == _`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0275`.