Skip to content

Avoid recursive blanket impl checks#155765

Open
chenyukang wants to merge 2 commits intorust-lang:mainfrom
chenyukang:yukang-fix-155759-rustdoc-blanket-impl
Open

Avoid recursive blanket impl checks#155765
chenyukang wants to merge 2 commits intorust-lang:mainfrom
chenyukang:yukang-fix-155759-rustdoc-blanket-impl

Conversation

@chenyukang
Copy link
Copy Markdown
Member

@chenyukang chenyukang commented Apr 25, 2026

Fixes #155759

Avoid recursively re-checking rustdoc blanket impl candidates while synthesizing impls.

Also record the polarity of synthesized auto-trait impls and reuses that result when checking blanket impl predicates.

If we want to assert the test of rustdoc must finished in specific time, we need to add a run-make test instead?

I tests it now runs about 2.2s for 60 variants on my local machine.

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Apr 25, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 25, 2026

r? @fmease

rustbot has assigned @fmease.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: rustdoc
  • rustdoc expanded to 9 candidates
  • Random selection from GuillaumeGomez, fmease, lolbinarycat, notriddle

@rustbot rustbot added T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output. labels Apr 25, 2026
@rust-log-analyzer

This comment has been minimized.

@rustbot rustbot added A-rustdoc-js Area: Rustdoc's JS front-end A-rustdoc-search Area: Rustdoc's search feature labels Apr 25, 2026
@chenyukang
Copy link
Copy Markdown
Member Author

chenyukang commented Apr 25, 2026

The job aarch64-gnu-llvm-21-1 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)

Executing "/scripts/stage_2_test_set1.sh"
+ /scripts/stage_2_test_set1.sh
PR_CI_JOB set; skipping tidy
+ '[' 1 == 1 ']'
+ echo 'PR_CI_JOB set; skipping tidy'
+ SKIP_TIDY='--skip tidy'
+ ../x.py --stage 2 test --skip tidy --skip compiler --skip src
##[group]Building bootstrap
    Finished `dev` profile [unoptimized] target(s) in 0.04s
##[endgroup]
downloading https://static.rust-lang.org/dist/2026-04-14/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz
---

---- [rustdoc-js] tests/rustdoc-js/never-search.rs stdout ----
------node stdout------------------------------
Testing /checkout/tests/rustdoc-js/never-search.js ... FAILED
[ query `! ->`]==> Expected exactly 2 results but found 3 in 'others'
[ query `! ->`]==> Exact check failed at position 0: expected '{"path":"never_search","name":"impossible"}' but found '{"crate":"never_search","name":"from","path":"never_search","exactPath":"never_search","ty":13,"parent":{"path":"never_search","exactPath":"never_search","name":"never","ty":5},"type":{"inputs":[{"id":11,"name":"never","ty":1,"path":"","exactPath":"","generics":[],"bindings":{},"unboxFlag":false}],"output":[{"id":-1,"name":"","ty":26,"path":null,"exactPath":null,"generics":[],"bindings":{},"unboxFlag":true}],"where_clause":[[]]},"paramNames":["T"],"dist":2,"path_dist":0,"index":-1,"desc":"","item":{"id":7,"crate":"never_search","ty":13,"name":"from","normalizedName":"from","modulePath":"never_search","exactModulePath":"never_search","entry":{"krate":27,"ty":13,"modulePath":27,"exactModulePath":27,"parent":12,"traitParent":4,"deprecated":false,"unstable":false,"associatedItemDisambiguatorOrExternCrateUrl":null},"path":null,"functionData":{"functionSignature":{"inputs":[{"id":11,"name":"never","ty":1,"path":"","exactPath":"","generics":[],"bindings":{},"unboxFlag":false}],"output":[{"id":-1,"name":"","ty":26,"path":null,"exactPath":null,"generics":[],"bindings":{},"unboxFlag":true}],"where_clause":[[]]},"paramNames":["T"],"elemCount":2},"deprecated":false,"unstable":false,"parent":{"name":"never","path":{"ty":5,"modulePath":"never_search","exactModulePath":"never_search"}},"traitParent":{"name":"From","path":{"ty":10,"modulePath":"core::convert","exactModulePath":"core::convert"}}},"displayPath":"<span>never_search::</span><span>never::</span>","fullPath":"never_search::never::from|13","traitPath":"core::convert::From::from|13","href":"../never_search/struct.never.html#method.from","displayTypeSignature":{"type":["","!"," -> T"],"mappedNames":{},"whereClause":{}},"id":7,"elems":[{"name":"never","id":11,"typeFilter":1,"generics":[],"bindings":{},"fullPath":["never"],"pathLast":"never","normalizedPathLast":"never","pathWithoutLast":[]}],"returned":[],"is_alias":false,"displayType":"`!` -> T","displayMappedNames":"","displayWhereClause":""}'
[ query `! ->`]==> Exact check failed at position 0: expected '{"path":"never_search","name":"box_impossible"}' but found '{"crate":"never_search","name":"impossible","path":"never_search","exactPath":"never_search","ty":7,"type":{"inputs":[{"id":11,"name":"never","ty":1,"path":"","exactPath":"","generics":[],"bindings":{},"unboxFlag":false}],"output":[{"id":9,"name":"unit","ty":1,"path":"","exactPath":"","generics":[],"bindings":{},"unboxFlag":false}],"where_clause":[]},"paramNames":[],"dist":2,"path_dist":0,"index":-1,"desc":"","item":{"id":26,"crate":"never_search","ty":7,"name":"impossible","normalizedName":"impossible","modulePath":"never_search","exactModulePath":"never_search","entry":{"krate":27,"ty":7,"modulePath":27,"exactModulePath":null,"parent":null,"traitParent":null,"deprecated":false,"unstable":false,"associatedItemDisambiguatorOrExternCrateUrl":null},"path":null,"functionData":{"functionSignature":{"inputs":[{"id":11,"name":"never","ty":1,"path":"","exactPath":"","generics":[],"bindings":{},"unboxFlag":false}],"output":[{"id":9,"name":"unit","ty":1,"path":"","exactPath":"","generics":[],"bindings":{},"unboxFlag":false}],"where_clause":[]},"paramNames":[],"elemCount":2},"deprecated":false,"unstable":false,"parent":null,"traitParent":null},"displayPath":"<span>never_search::</span>","fullPath":"never_search::impossible|7","traitPath":null,"href":"../never_search/fn.impossible.html","displayTypeSignature":{"type":["","!"," -> ()"],"mappedNames":{},"whereClause":{}},"id":26,"elems":[{"name":"never","id":11,"typeFilter":1,"generics":[],"bindings":{},"fullPath":["never"],"pathLast":"never","normalizedPathLast":"never","pathWithoutLast":[]}],"returned":[],"is_alias":false,"displayType":"`!` -> ()","displayMappedNames":"","displayWhereClause":""}'

------node stderr------------------------------

------------------------------------------

error: rustdoc-js test failed!
status: exit status: 1
command: "/usr/bin/node" "/checkout/src/tools/rustdoc-js/tester.js" "--doc-folder" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/rustdoc-js/never-search" "--crate-name" "never_search" "--test-file" "/checkout/tests/rustdoc-js/never-search.js" "--revision" ""
--- stdout -------------------------------
Testing /checkout/tests/rustdoc-js/never-search.js ... FAILED
[ query `! ->`]==> Expected exactly 2 results but found 3 in 'others'
[ query `! ->`]==> Exact check failed at position 0: expected '{"path":"never_search","name":"impossible"}' but found '{"crate":"never_search","name":"from","path":"never_search","exactPath":"never_search","ty":13,"parent":{"path":"never_search","exactPath":"never_search","name":"never","ty":5},"type":{"inputs":[{"id":11,"name":"never","ty":1,"path":"","exactPath":"","generics":[],"bindings":{},"unboxFlag":false}],"output":[{"id":-1,"name":"","ty":26,"path":null,"exactPath":null,"generics":[],"bindings":{},"unboxFlag":true}],"where_clause":[[]]},"paramNames":["T"],"dist":2,"path_dist":0,"index":-1,"desc":"","item":{"id":7,"crate":"never_search","ty":13,"name":"from","normalizedName":"from","modulePath":"never_search","exactModulePath":"never_search","entry":{"krate":27,"ty":13,"modulePath":27,"exactModulePath":27,"parent":12,"traitParent":4,"deprecated":false,"unstable":false,"associatedItemDisambiguatorOrExternCrateUrl":null},"path":null,"functionData":{"functionSignature":{"inputs":[{"id":11,"name":"never","ty":1,"path":"","exactPath":"","generics":[],"bindings":{},"unboxFlag":false}],"output":[{"id":-1,"name":"","ty":26,"path":null,"exactPath":null,"generics":[],"bindings":{},"unboxFlag":true}],"where_clause":[[]]},"paramNames":["T"],"elemCount":2},"deprecated":false,"unstable":false,"parent":{"name":"never","path":{"ty":5,"modulePath":"never_search","exactModulePath":"never_search"}},"traitParent":{"name":"From","path":{"ty":10,"modulePath":"core::convert","exactModulePath":"core::convert"}}},"displayPath":"<span>never_search::</span><span>never::</span>","fullPath":"never_search::never::from|13","traitPath":"core::convert::From::from|13","href":"../never_search/struct.never.html#method.from","displayTypeSignature":{"type":["","!"," -> T"],"mappedNames":{},"whereClause":{}},"id":7,"elems":[{"name":"never","id":11,"typeFilter":1,"generics":[],"bindings":{},"fullPath":["never"],"pathLast":"never","normalizedPathLast":"never","pathWithoutLast":[]}],"returned":[],"is_alias":false,"displayType":"`!` -> T","displayMappedNames":"","displayWhereClause":""}'
[ query `! ->`]==> Exact check failed at position 0: expected '{"path":"never_search","name":"box_impossible"}' but found '{"crate":"never_search","name":"impossible","path":"never_search","exactPath":"never_search","ty":7,"type":{"inputs":[{"id":11,"name":"never","ty":1,"path":"","exactPath":"","generics":[],"bindings":{},"unboxFlag":false}],"output":[{"id":9,"name":"unit","ty":1,"path":"","exactPath":"","generics":[],"bindings":{},"unboxFlag":false}],"where_clause":[]},"paramNames":[],"dist":2,"path_dist":0,"index":-1,"desc":"","item":{"id":26,"crate":"never_search","ty":7,"name":"impossible","normalizedName":"impossible","modulePath":"never_search","exactModulePath":"never_search","entry":{"krate":27,"ty":7,"modulePath":27,"exactModulePath":null,"parent":null,"traitParent":null,"deprecated":false,"unstable":false,"associatedItemDisambiguatorOrExternCrateUrl":null},"path":null,"functionData":{"functionSignature":{"inputs":[{"id":11,"name":"never","ty":1,"path":"","exactPath":"","generics":[],"bindings":{},"unboxFlag":false}],"output":[{"id":9,"name":"unit","ty":1,"path":"","exactPath":"","generics":[],"bindings":{},"unboxFlag":false}],"where_clause":[]},"paramNames":[],"elemCount":2},"deprecated":false,"unstable":false,"parent":null,"traitParent":null},"displayPath":"<span>never_search::</span>","fullPath":"never_search::impossible|7","traitPath":null,"href":"../never_search/fn.impossible.html","displayTypeSignature":{"type":["","!"," -> ()"],"mappedNames":{},"whereClause":{}},"id":26,"elems":[{"name":"never","id":11,"typeFilter":1,"generics":[],"bindings":{},"fullPath":["never"],"pathLast":"never","normalizedPathLast":"never","pathWithoutLast":[]}],"returned":[],"is_alias":false,"displayType":"`!` -> ()","displayMappedNames":"","displayWhereClause":""}'
------------------------------------------
stderr: none

---- [rustdoc-js] tests/rustdoc-js/never-search.rs stdout end ----

The CI failure is caused after this PR,
rustdoc now includes the blanket impl method from:

impl<T> From<!> for T

when searching ! ->.

I think it's ok to update the test case.

Copy link
Copy Markdown
Member

@fmease fmease Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can just be a rustdoc UI test since we don't care about the HTML. For that move, it into tests/rustdoc-ui/, remove the #![crate_name], the htmldocck directives and add //@ check-pass

View changes since the review

@@ -0,0 +1,268 @@
#![crate_name = "foo"]
Copy link
Copy Markdown
Member

@fmease fmease Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a source code comment at the top, can you add which initial (auto trait, ADT) pairing(s) used to cause the blowout (unless that info isn't meaningful)

View changes since the review

Copy link
Copy Markdown
Member

@fmease fmease Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious, does this have any effect on issue #114891?

View changes since the review

Comment on lines +72 to +74
let predicate = infcx.resolve_vars_if_possible(predicate);
if let Some(may_apply) =
cached_auto_trait_predicate_result(cx, predicate, impl_ty, item_ty)
Copy link
Copy Markdown
Member

@fmease fmease Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's very odd to me that we have to special case auto traits here when synthesizing blanket impls, it feels like we're manually helping out the trait solver here when it's likely the old trait solver's fault?

Well, I don't actually know the actually fast-expanding tree of obligations in dtolnay's example. Could you enlighten me?

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm gonna rebase my dusty PR #125907, impl lcnr's final suggestion and check if that PR also fixes the issue.

Comment thread src/librustdoc/core.rs
Comment on lines 61 to +63
pub(crate) generated_synthetics: FxHashSet<(Ty<'tcx>, DefId)>,
/// Polarity of synthesized auto-trait impls processed so far.
pub(crate) generated_auto_trait_impls: FxHashMap<(Ty<'tcx>, DefId), ty::ImplPolarity>,
Copy link
Copy Markdown
Member

@fmease fmease Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't try to address this now since I want to test my other PR first. I'd just like to note that it's confusing to name this generated_auto_trait_impls because generated_synthetics directly above caches both synthetic blanket and synthetic auto trait impls (which is actually problematic, they should be separate caches, see #148980) meaning we would now have "two" auto trait impl caches which are slightly different.

View changes since the review

@@ -4,6 +4,7 @@ const EXPECTED = [
{
'query': '! ->',
Copy link
Copy Markdown
Member

@fmease fmease Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to ponder about this behavior change; obviously optimizations like caching shouldn't observably affect the semantics of a program and if they do that indicates a bug somewhere even if the change is 'desirable'.

I've just woken up and I've only skimmed this PR, so this is just a hunch: I see you're somehow involving ImplPolarity in your cache and the impl in question is impl<T> From<!> for T which is a reservation impl (ImplPolarity::Reservation), so you might not be handling that 'correctly'.

Again, it's possible that the change is desirable (I haven't thought about that yet) but then that should be explicitly encoded outside of the caching logic.

View changes since the review

E58(std::sync::Arc<E58<Span>>),
E59(std::sync::Arc<E59<Span>>),
}
}
Copy link
Copy Markdown
Member

@fmease fmease Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to assert the test of rustdoc must finished in specific time, we need to add a run-make test instead?

That seems iffy to me as it can easily render the test flaky. It could lead to the test failing on slow machines or in CI if the OS is under heavy workload and suspends the process for a while.

View changes since the review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-rustdoc-js Area: Rustdoc's JS front-end A-rustdoc-search Area: Rustdoc's search feature S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Exponentially slow rustdoc trait impl computation

4 participants