| author | bors <bors@rust-lang.org> 2026-01-17 16:15:18 UTC |
| committer | bors <bors@rust-lang.org> 2026-01-17 16:15:18 UTC |
| log | fe98ddcfcfb6f185dbf4adeaf439d8a756da0273 |
| tree | f18aad59d84cb99132bb0545af5ac2e8b3b23e6f |
| parent | 9f6cd6defbd7ef13f6777aa8e43b14d69f0a830a |
| parent | ac8e8505b7c12d826f970e8e8ec8c18b067c6dc8 |
rustdoc: Stop unconditionally evaluating the initializer of associated consts
See the descriptions of the added tests for details.
Fixes rust-lang/rust#131625.
Fixes [after beta-1.93 backport] rust-lang/rust#149635.
Fixes rust-lang/rust#150312.
Supersedes rust-lang/rust#150629 IINM.
CC @cuviper (https://github.com/rust-lang/rust/issues/149635#issuecomment-3761125727)
r? @GuillaumeGomez or @yotamofek (rust-lang/rust#150629)9 files changed, 75 insertions(+), 14 deletions(-)
src/librustdoc/html/render/mod.rs+3-6| ... | ... | @@ -1050,14 +1050,11 @@ fn assoc_const( |
| 1050 | 1050 | ty = print_type(ty, cx), |
| 1051 | 1051 | )?; |
| 1052 | 1052 | if let AssocConstValue::TraitDefault(konst) | AssocConstValue::Impl(konst) = value { |
| 1053 | // FIXME: `.value()` uses `clean::utils::format_integer_with_underscore_sep` under the | |
| 1054 | // hood which adds noisy underscores and a type suffix to number literals. | |
| 1055 | // This hurts readability in this context especially when more complex expressions | |
| 1056 | // are involved and it doesn't add much of value. | |
| 1057 | // Find a way to print constants here without all that jazz. | |
| 1058 | let repr = konst.value(tcx).unwrap_or_else(|| konst.expr(tcx)); | |
| 1053 | let repr = konst.expr(tcx); | |
| 1059 | 1054 | if match value { |
| 1060 | 1055 | AssocConstValue::TraitDefault(_) => true, // always show |
| 1056 | // FIXME: Comparing against the special string "_" denoting overly complex const exprs | |
| 1057 | // is rather hacky; `ConstKind::expr` should have a richer return type. | |
| 1061 | 1058 | AssocConstValue::Impl(_) => repr != "_", // show if there is a meaningful value to show |
| 1062 | 1059 | AssocConstValue::None => unreachable!(), |
| 1063 | 1060 | } { |
tests/rustdoc-html/anchors/anchors.no_const_anchor2.html+1-1| ... | ... | @@ -1 +1 @@ |
| 1 | <section id="associatedconstant.X" class="associatedconstant"><a class="src rightside" href="../src/foo/anchors.rs.html#42">Source</a><h4 class="code-header">pub const <a href="#associatedconstant.X" class="constant">X</a>: <a class="primitive" href="{{channel}}/std/primitive.i32.html">i32</a> = 0i32</h4></section> | |
| \ No newline at end of file | ||
| 1 | <section id="associatedconstant.X" class="associatedconstant"><a class="src rightside" href="../src/foo/anchors.rs.html#42">Source</a><h4 class="code-header">pub const <a href="#associatedconstant.X" class="constant">X</a>: <a class="primitive" href="{{channel}}/std/primitive.i32.html">i32</a> = 0</h4></section> | |
| \ No newline at end of file |
tests/rustdoc-html/attributes.rs+1-1| ... | ... | @@ -59,7 +59,7 @@ pub enum Enum { |
| 59 | 59 | pub trait Trait { |
| 60 | 60 | //@ has 'foo/trait.Trait.html' |
| 61 | 61 | //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]/*[@class="code-attribute"]' '#[unsafe(link_section = "bar")]' |
| 62 | //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]' 'const BAR: u32 = 0u32' | |
| 62 | //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]' 'const BAR: u32 = 0' | |
| 63 | 63 | #[unsafe(link_section = "bar")] |
| 64 | 64 | const BAR: u32 = 0; |
| 65 | 65 |
tests/rustdoc-html/constant/assoc-const-has-projection-ty.rs created+31| ... | ... | @@ -0,0 +1,31 @@ |
| 1 | // Ensure that we properly print the value `1` as `1` in the initializer of associated constants | |
| 2 | // that have user type "projection". | |
| 3 | // | |
| 4 | // We once used to evaluate the initializer in rustdoc and use rustc's MIR pretty-printer to | |
| 5 | // render the resulting MIR const value. This pretty printer matches on the type to interpret | |
| 6 | // the data and falls back to a cryptic `"{transmute(0x$data): $ty}"` for types it can't handle. | |
| 7 | // Crucially, when constructing the MIR const we passed the unnormalized type of the initializer, | |
| 8 | // i.e., the projection `<Struct as Trait>::Ty` instead of the normalized `u32` which the | |
| 9 | // pretty printer obviously can't handle. | |
| 10 | // | |
| 11 | // Now we no longer evaluate it and use a custom printer for the const expr. | |
| 12 | // | |
| 13 | // issue: <https://github.com/rust-lang/rust/issues/150312> | |
| 14 | ||
| 15 | #![crate_name = "it"] | |
| 16 | ||
| 17 | pub trait Trait { | |
| 18 | type Ty; | |
| 19 | ||
| 20 | const CT: Self::Ty; | |
| 21 | } | |
| 22 | ||
| 23 | pub struct Struct; | |
| 24 | ||
| 25 | impl Trait for Struct { | |
| 26 | type Ty = u32; | |
| 27 | ||
| 28 | //@ has it/struct.Struct.html | |
| 29 | //@ has - '//*[@id="associatedconstant.CT"]' 'const CT: Self::Ty = 1' | |
| 30 | const CT: Self::Ty = 1; | |
| 31 | } |
tests/rustdoc-html/constant/assoc-consts.rs+1-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | pub trait Foo { |
| 2 | 2 | //@ has assoc_consts/trait.Foo.html '//pre[@class="rust item-decl"]' \ |
| 3 | // 'const FOO: usize = 13usize;' | |
| 3 | // 'const FOO: usize = _;' | |
| 4 | 4 | //@ has - '//*[@id="associatedconstant.FOO"]' 'const FOO: usize' |
| 5 | 5 | const FOO: usize = 12 + 1; |
| 6 | 6 | //@ has - '//*[@id="associatedconstant.FOO_NO_DEFAULT"]' 'const FOO_NO_DEFAULT: bool' |
tests/rustdoc-html/deref/deref-to-primitive.rs+1-1| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | //@ has 'foo/struct.Foo.html' |
| 4 | 4 | //@ has - '//*[@id="deref-methods-i32"]' 'Methods from Deref<Target = i32>' |
| 5 | 5 | //@ has - '//*[@id="deref-methods-i32-1"]//*[@id="associatedconstant.BITS"]/h4' \ |
| 6 | // 'pub const BITS: u32 = 32u32' | |
| 6 | // 'pub const BITS: u32 = u32::BITS' | |
| 7 | 7 | pub struct Foo(i32); |
| 8 | 8 | |
| 9 | 9 | impl std::ops::Deref for Foo { |
tests/rustdoc-html/display-hidden-items.rs+2-2| ... | ... | @@ -20,7 +20,7 @@ pub trait TraitHidden {} |
| 20 | 20 | //@ has 'foo/index.html' '//dt/a[@class="trait"]' 'Trait' |
| 21 | 21 | pub trait Trait { |
| 22 | 22 | //@ has 'foo/trait.Trait.html' |
| 23 | //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]' '#[doc(hidden)] const BAR: u32 = 0u32' | |
| 23 | //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]' '#[doc(hidden)] const BAR: u32 = 0' | |
| 24 | 24 | #[doc(hidden)] |
| 25 | 25 | const BAR: u32 = 0; |
| 26 | 26 | |
| ... | ... | @@ -44,7 +44,7 @@ impl Struct { |
| 44 | 44 | } |
| 45 | 45 | |
| 46 | 46 | impl Trait for Struct { |
| 47 | //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]' '#[doc(hidden)] const BAR: u32 = 0u32' | |
| 47 | //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]' '#[doc(hidden)] const BAR: u32 = 0' | |
| 48 | 48 | //@ has - '//*[@id="method.foo"]/*[@class="code-header"]' '#[doc(hidden)] fn foo()' |
| 49 | 49 | } |
| 50 | 50 | //@ has - '//*[@id="impl-TraitHidden-for-Struct"]/*[@class="code-header"]' 'impl TraitHidden for Struct' |
tests/rustdoc-html/impl/impl-associated-items-order.rs+2-2| ... | ... | @@ -16,7 +16,7 @@ impl Bar { |
| 16 | 16 | // 'pub fn foo()' |
| 17 | 17 | pub fn foo() {} |
| 18 | 18 | //@ has - '//*[@id="implementations-list"]//*[@class="impl-items"]/section[1]/h4' \ |
| 19 | // 'pub const X: u8 = 12u8' | |
| 19 | // 'pub const X: u8 = 12' | |
| 20 | 20 | pub const X: u8 = 12; |
| 21 | 21 | //@ has - '//*[@id="implementations-list"]//*[@class="impl-items"]/section[2]/h4' \ |
| 22 | 22 | // 'pub type Y = u8' |
| ... | ... | @@ -34,7 +34,7 @@ impl Foo for Bar { |
| 34 | 34 | // 'type Z = u8' |
| 35 | 35 | type Z = u8; |
| 36 | 36 | //@ has - '//*[@id="trait-implementations-list"]//*[@class="impl-items"]/section[1]/h4' \ |
| 37 | // 'const W: u32 = 12u32' | |
| 37 | // 'const W: u32 = 12' | |
| 38 | 38 | const W: u32 = 12; |
| 39 | 39 | //@ has - '//*[@id="trait-implementations-list"]//*[@class="impl-items"]/section[3]/h4' \ |
| 40 | 40 | // 'fn yeay()' |
tests/rustdoc-ui/diverging-assoc-consts.rs created+33| ... | ... | @@ -0,0 +1,33 @@ |
| 1 | // Ensure that we don't unconditionally evaluate the initializer of associated constants. | |
| 2 | // | |
| 3 | // We once used to evaluate them so we could display more kinds of expressions | |
| 4 | // (like `1 + 1` as `2`) given the fact that we generally only want to render | |
| 5 | // literals (otherwise we would risk dumping extremely large exprs or leaking | |
| 6 | // private struct fields). | |
| 7 | // | |
| 8 | // However, that deviated from rustc's behavior, made rustdoc accept less code | |
| 9 | // and was understandably surprising to users. So let's not. | |
| 10 | // | |
| 11 | // In the future we *might* provide users a mechanism to control this behavior. | |
| 12 | // E.g., via a new `#[doc(...)]` attribute. | |
| 13 | // | |
| 14 | // See also: | |
| 15 | // issue: <https://github.com/rust-lang/rust/issues/131625> | |
| 16 | // issue: <https://github.com/rust-lang/rust/issues/149635> | |
| 17 | ||
| 18 | //@ check-pass | |
| 19 | ||
| 20 | pub struct Type; | |
| 21 | ||
| 22 | impl Type { | |
| 23 | pub const K0: () = panic!(); | |
| 24 | pub const K1: std::convert::Infallible = loop {}; | |
| 25 | } | |
| 26 | ||
| 27 | pub trait Trait { | |
| 28 | const K2: i32 = panic!(); | |
| 29 | } | |
| 30 | ||
| 31 | impl Trait for Type { | |
| 32 | const K2: i32 = loop {}; | |
| 33 | } |