Code change:
<br<img width="300" src="https://user-"<br<img width="300" src="https://user-"<brI used the following script to bisect: It bisects to nightly-2022-04-14 and specifically , of which seems the most relevant because it involves associated constants in rustdoc. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
is indeed the cause. More specifically the following line: If we “fail” to evaluate (“failure” also includes “the constant is a struct”) the const value of a provided associated const with , we fall back to which prints the constant in raw uncensored form including structs with their private fields. I assume in the non-associated const version (shown below) the call to does not happen and if fails in such a case, neither the value nor the is being printed. . I am not sure how to proceed This is another instance of constant-printing for provided assoc consts being problematic. Previous incident: Performance ().
Maybe we could update the method so that it can only show the public items to fulfill our need?
The ideal solution (non-hotfix) would be to generally improve the printing of (unevaluated) constants (as returned by . . Not sure how easy it is to implement though.
Seems like we want something similar. I don't think it's very difficult but might involve a lot of code. Want to give it a try?
I might as well try. If successful, my PR needs backporting. claim
Error: Parsing assign command in failed: ...'bot assign' ''... Please let know if you're having trouble with this bot.
I'm not convinced that displaying the value of (provided associated) constants in rustdoc is ever worthwhile and indeed it could be harmful. Users should not need to know what value a constant holds, only what it represents; indeed they should only ever be using the constant itself to represent that value when it's required—that is to say, the value assigned is an implementation detail and nobody should be relying upon it.
I strongly disagree. Associated constants are meant to be used by externals users, otherwise they'd not be created. They are definitely not an implementation detail and they are part of the public API so everyone should rely on them. There are a lot of examples so I'll just take one: all  associated constants in the crate.
I'm not sure I follow. Why do I need to know that is ? Surely I will write code that switches on and the compiler handles the rest?
I agree with They are not an implementation detail, at least not those that are of a type that exposes structural equality (deriving and ) since downstream users may depend (as in “compilation may fail if the implementation changes”) on the concrete value of a constant if they use it in a pattern [exhaustiveness checking] or inside of a const argument [type checking] (which may currently require if the type is not numeric). ( I even vaguely remember seeing a Zulip discussion about how public constants always leak their implementation / concrete value, not just those of a type, but I am not sure anymore )
Isn't that just relying on an implementation detail in order to work-around current limitations in the language? If/when lands, the constant should be used in place of its value?
What I am saying is that changing the value of a public constant is a breaking change since it is part of the public API. As a simplified example, consider the case where a crate called exports two constants of type , named and . Crate depending on crate contains the following code: Crate also depending on : In version A of , constant is not equal to and the dependent crates and compile just fine. Now, in the succeeding version B of , and are set equal to each other. and no longer compile with the new version. The inequality was part of the public contract but it was broken. If we wanted to follow SemVer, version B would need to be incompatible with A.
Ah, understood! Sorry for being slow on the uptake :). That all makes sense now.
I understand your argument to some extent, but changing a function's body can also be a SemVer breaking change. Why should constants — which also hide a complex expression and present a simple API, like functions — be treated differently?
I'm a bit lost. We only show public parts of the API unless using and and I are supporting the idea to only show the public parts of "complex" associated constants' value to make it treated like the other items. Or did I completely miss your comment?
My point is that we never show function bodies (even with ), and IMO constants are a subset of () functions, so we also should not show constant bodies. We do show the bodies of free (non-associated) constants already, but I think I'd rather stop doing that instead of also displaying associated constants' values.
I see your point. I disagree though. If you update a constant value, it's the same as changing a function's header: it's a breaking change. And it's also super useful to be able to see the value of a constant (associated or not) and I think it's definitely a big plus.
Well, I at least think we shouldn't show the raw expression from the constant's body; we should only show the fully-computed value.
Yes, we should prefer printing the evaluated const. I am working on this. It's not as trivial and I am going to do it incrementally. The biggest issue is the missing support for const substitutions as you might remember. Yesterday I almost had a working PR done, in some sense a hotfix for this issue, that “censors” complex unevaluated expressions like struct literals and match expressions by replacing them with (over time this procedure can be fine-tuned). However, I ran into some nasty corner cases (certain qualified paths that contain const arguments) and regressions. I will try to simplify the patch, add tests and open it as a PR today or tomorrow (hopefully). I have further plans for improving const support but the current representation of constant expressions and the lack of const substitutions in rustdoc is giving me headaches. I have already prepared a more sophisticated PR that adds hyperlinks to const exprs and understands the concept of private and fields when pretty-printing but it's not quite ready yet.
Yeah, the current state of the code is quite unfortunate :( I've tried to improve it, but as you've seen it's very hard to understand and change.
I don't like this. <img width="1440" alt="image" src="https://user-"
PR coming up within the next hour. I just need to write some more tests.

Related discussion:
text: false } <ins> /// Build a textual representation of an unevaluated constant expression. /// /// If the const expression is too complex, an underscore `_` is returned. /// For const arguments, it's `{ _ }` to be precise. /// This means that the output is not necessarily valid Rust code. /// /// Currently, only /// /// * literals (optionally with a leading `-`) /// * unit `()` /// * blocks (`{ … }`) around simple expressions and /// * paths without arguments /// /// are considered simple enough. Simple blocks are included since they are /// necessary to disambiguate unit from the unit type. /// This list might get extended in the future. /// /// Without this censoring, in a lot of cases the output would get too large /// and verbose. Consider `match` expressions, blocks and deeply nested ADTs. /// Further, private and `doc(hidden)` fields of structs would get leaked /// since HIR datatypes like the `body` parameter do not contain enough /// semantic information for this function to be able to hide them – /// at least not without significant performance overhead. /// /// Whenever possible, prefer to evaluate the constant first and try to /// use a different method for pretty-printing. Ideally this function /// should only ever be used as a fallback. </ins> pub(crate) fn print_const_expr(tcx: TyCtxt<'_>, body: hir::BodyId) -> String { let hir = tcx.hir(); let value = &hir.body(body).value; <del> let snippet = if !value.span.from_expansion() { tcx.sess.source_map().span_to_snippet(value.span).ok() } else { None }; </del> <ins> #[derive(PartialEq, Eq)] enum Classification { Literal, Simple, Complex, } </ins> <del> snippet.unwrap_or_else(|| rustc_hir_pretty::id_to_string(&hir, body.hir_id)) </del> <ins> use Classification::*; fn classify(expr: &hir::Expr<'_>) -> Classification { match &expr.kind { hir::ExprKind::Unary(hir::UnOp::Neg, expr) => { if matches!(expr.kind, hir::ExprKind::Lit(_)) { Literal } else { Complex } } hir::ExprKind::Lit(_) => Literal, hir::ExprKind::Tup([]) => Simple, hir::ExprKind::Block(hir::Block { stmts: [], expr: Some(expr), .. }, _) => { if classify(expr) == Complex { Complex } else { Simple } } // Paths with a self-type or arguments are too “complex” following our measure since // they may leak private fields of structs (with feature `adt_const_params`). // Consider: `<Self as Trait<{ Struct { private: () } }>>::CONSTANT`. // Paths without arguments are definitely harmless though. hir::ExprKind::Path(hir::QPath::Resolved(_, hir::Path { segments, .. })) => { if segments.iter().all(|segment| segment.args.is_none()) { Simple } else { Complex } } // FIXME: Claiming that those kinds of QPaths are simple is probably not true if the Ty //        contains const arguments. Is there a *concise* way to check for this? hir::ExprKind::Path(hir::QPath::TypeRelative(..)) => Simple, // FIXME: Can they contain const arguments and thus leak private struct fields? hir::ExprKind::Path(hir::QPath::LangItem(..)) => Simple, _ => Complex, } } let classification = classify(value); if classification == Literal && !value.span.from_expansion() && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(value.span) { // For literals, we avoid invoking the pretty-printer and use the source snippet instead to // preserve certain stylistic choices the user likely made for the sake legibility like // // * hexadecimal notation // * underscores // * character escapes // // FIXME: This passes through `-/*spacer*/0` verbatim. snippet } else if classification == Simple { // Otherwise we prefer pretty-printing to get rid of extraneous whitespace, comments and // other formatting artifacts. rustc_hir_pretty::id_to_string(&hir, body.hir_id) } else if tcx.def_kind(hir.body_owner_def_id(body).to_def_id()) == DefKind::AnonConst { // FIXME: Omit the curly braces if the enclosing expression is an array literal //        with a repeated element (an `ExprKind::Repeat`) as in such case it //        would not actually need any disambiguation. "{ _ }".to_owned() } else { "_".to_owned() } </ins> } /// Given a type Path, resolve it to a Type using the TyCtxt