rust-lang / rust-lang/rust

Rustdoc hangs with this recursive generic type

Open
#160,280 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-synthetic-impls C-bug fixed-by-next-solver I-hang I-prioritize T-rustdoc
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Code

pub struct Recursive<T> {
    child: Box<Recursive<Box<T>>>,
    data: T,
}

Reproduction Steps

Run cargo doc, hangs.

Expected Outcome

Not hang

Actual Output

With RUSTDOC_LOG="rustdoc=debug"

warning: some trace filter directives would enable traces that are disabled statically                                                                                                                  
 | `rustdoc=debug` would enable the DEBUG level for the `rustdoc` target                                                                                                                                
 = note: the static max level is `info`
 = help: to enable DEBUG logging, remove the `max_level_info` feature from the `tracing` crate
 DEBUG rustdoc::config merge_doctests: Auto
 INFO rustdoc starting to run rustc
 DEBUG rustdoc::clean trait_ref=Binder { value: <Self as std::marker::MetaSized>, bound_vars: [] }                                                                                                      
 DEBUG rustdoc::visit_ast Going through module Mod { spans: ModSpans { inner_span: src\lib.rs:1:1: 4:2 (#0), inject_use_span: no-location (#0) }, item_ids: [ItemId { owner_id: DefId(0:1 ~ rustdoc_hang_repro[5a4c]::std) }, ItemId { owner_id: DefId(0:2 ~ rustdoc_hang_repro[5a4c]::{use#0}) }, ItemId { owner_id: DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive) }] }
 DEBUG rustdoc::visit_ast visiting item Item { owner_id: DefId(0:1 ~ rustdoc_hang_repro[5a4c]::std), kind: ExternCrate(None, std#1), span: no-location (#1), vis_span: no-location (#1), has_delayed_lints: false, eii: false }
 DEBUG rustdoc::visit_ast visiting item Item { owner_id: DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive), kind: Struct(Recursive#0, Generics { params: [GenericParam { hir_id: HirId(DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive).1), def_id: DefId(0:4 ~ rustdoc_hang_repro[5a4c]::Recursive::T), name: Plain(T#0), span: src\lib.rs:1:22: 1:23 (#0), pure_wrt_drop: false, kind: Type { default: None, synthetic: false }, colon_span: None, source: Generics }], predicates: [], has_where_clause_predicates: false, where_clause_span: src\lib.rs:1:24: 1:24 (#0), span: src\lib.rs:1:21: 1:24 (#0) }, Struct { fields: [FieldDef { span: src\lib.rs:2:5: 2:34 (#0), vis_span: src\lib.rs:2:5: 2:5 (#0), ident: child#0, hir_id: HirId(DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive).10), def_id: DefId(0:5 ~ rustdoc_hang_repro[5a4c]::Recursive::child), ty: Ty { hir_id: HirId(DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive).2), span: src\lib.rs:2:12: 2:34 (#0), kind: Path(Resolved(None, Path { span: src\lib.rs:2:12: 2:34 (#0), res: Def(Struct, DefId(3:669 ~ alloc[61df]::boxed::Box)), segments: [PathSegment { ident: Box#0, hir_id: HirId(DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive).9), res: Def(Struct, DefId(3:669 ~ alloc[61df]::boxed::Box)), args: Some(GenericArgs { args: [Type(Ty { hir_id: HirId(DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive).3), span: src\lib.rs:2:16: 2:33 (#0), kind: Path(Resolved(None, Path { span: src\lib.rs:2:16: 2:33 (#0), res: Def(Struct, DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive)), segments: [PathSegment { ident: Recursive#0, hir_id: HirId(DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive).8), res: Def(Struct, DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive)), args: Some(GenericArgs { args: [Type(Ty { hir_id: HirId(DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive).4), span: src\lib.rs:2:26: 2:32 (#0), kind: Path(Resolved(None, Path { span: src\lib.rs:2:26: 2:32 (#0), res: Def(Struct, DefId(3:669 ~ alloc[61df]::boxed::Box)), segments: [PathSegment { ident: Box#0, hir_id: HirId(DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive).7), res: Def(Struct, DefId(3:669 ~ alloc[61df]::boxed::Box)), args: Some(GenericArgs { args: [Type(Ty { hir_id: HirId(DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive).5), span: src\lib.rs:2:30: 2:31 (#0), kind: Path(Resolved(None, Path { span: src\lib.rs:2:30: 2:31 (#0), res: Def(TyParam, DefId(0:4 ~ rustdoc_hang_repro[5a4c]::Recursive::T)), segments: [PathSegment { ident: T#0, hir_id: HirId(DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive).6), res: Def(TyParam, DefId(0:4 ~ rustdoc_hang_repro[5a4c]::Recursive::T)), args: None, infer_args: false }] })) })], constraints: [], parenthesized: No, span_ext: src\lib.rs:2:29: 2:32 (#0) }), infer_args: false }] })) })], constraints: [], parenthesized: No, span_ext: src\lib.rs:2:25: 2:33 (#0) }), infer_args: false }] })) })], constraints: [], parenthesized: No, span_ext: src\lib.rs:2:15: 2:34 (#0) }), infer_args: false }] })) }, safety: Safe, default: None }, FieldDef { span: src\lib.rs:3:5: 3:12 (#0), vis_span: src\lib.rs:3:5: 3:5 (#0), ident: data#0, hir_id: HirId(DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive).13), def_id: DefId(0:6 ~ rustdoc_hang_repro[5a4c]::Recursive::data), ty: Ty { hir_id: HirId(DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive).11), span: src\lib.rs:3:11: 3:12 (#0), kind: Path(Resolved(None, Path { span: src\lib.rs:3:11: 3:12 (#0), res: Def(TyParam, DefId(0:4 ~ rustdoc_hang_repro[5a4c]::Recursive::T)), segments: [PathSegment { ident: T#0, hir_id: HirId(DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive).12), res: Def(TyParam, DefId(0:4 ~ rustdoc_hang_repro[5a4c]::Recursive::T)), args: None, infer_args: false }] })) }, safety: Safe, default: None }], recovered: No }), span: src\lib.rs:1:1: 4:2 (#0), vis_span: src\lib.rs:1:1: 1:4 (#0), has_delayed_lints: false, eii: false }
 DEBUG rustdoc::visit_ast visiting item Item { owner_id: DefId(0:2 ~ rustdoc_hang_repro[5a4c]::{use#0}), kind: Use(Path { span: no-location (#1), res: PerNS { value_ns: Some(Err), type_ns: Some(Err), macro_ns: Some(Err) }, segments: [PathSegment { ident: std#1, hir_id: HirId(DefId(0:2 ~ rustdoc_hang_repro[5a4c]::{use#0}).1), res: Err, args: None, infer_args: false }, PathSegment { ident: prelude#1, hir_id: HirId(DefId(0:2 ~ rustdoc_hang_repro[5a4c]::{use#0}).2), res: Err, args: None, infer_args: false }, PathSegment { ident: rust_2024#1, hir_id: HirId(DefId(0:2 ~ rustdoc_hang_repro[5a4c]::{use#0}).3), res: Err, args: None, infer_args: false }] }, Glob), span: no-location (#1), vis_span: no-location (#1), has_delayed_lints: false, eii: false }
 DEBUG rustdoc::visit_ast Leaving module Mod { spans: ModSpans { inner_span: src\lib.rs:1:1: 4:2 (#0), inject_use_span: no-location (#0) }, item_ids: [ItemId { owner_id: DefId(0:1 ~ rustdoc_hang_repro[5a4c]::std) }, ItemId { owner_id: DefId(0:2 ~ rustdoc_hang_repro[5a4c]::{use#0}) }, ItemId { owner_id: DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive) }] }
 DEBUG rustdoc::clean::utils resolve_type(Path { res: Def(TyParam, DefId(0:4 ~ rustdoc_hang_repro[5a4c]::Recursive::T)), segments: [PathSegment { name: "T", args: AngleBracketed { args: [], constraints: [] } }] })
 DEBUG rustdoc::clean::utils resolve_type(Path { res: Def(Struct, DefId(3:669 ~ alloc[61df]::boxed::Box)), segments: [PathSegment { name: "Box", args: AngleBracketed { args: [Type(Generic("T"))], constraints: [] } }] })
 DEBUG rustdoc::clean::utils register_res(Def(Struct, DefId(3:669 ~ alloc[61df]::boxed::Box)))
 DEBUG rustdoc::clean::utils resolve_type(Path { res: Def(Struct, DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive)), segments: [PathSegment { name: "Recursive", args: AngleBracketed { args: [Type(Path { path: Path { res: Def(Struct, DefId(3:669 ~ alloc[61df]::boxed::Box)), segments: [PathSegment { name: "Box", args: AngleBracketed { args: [Type(Generic("T"))], constraints: [] } }] } })], constraints: [] } }] })
 DEBUG rustdoc::clean::utils register_res(Def(Struct, DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive)))
 DEBUG rustdoc::clean::utils resolve_type(Path { res: Def(Struct, DefId(3:669 ~ alloc[61df]::boxed::Box)), segments: [PathSegment { name: "Box", args: AngleBracketed { args: [Type(Path { path: Path { res: Def(Struct, DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive)), segments: [PathSegment { name: "Recursive", args: AngleBracketed { args: [Type(Path { path: Path { res: Def(Struct, DefId(3:669 ~ alloc[61df]::boxed::Box)), segments: [PathSegment { name: "Box", args: AngleBracketed { args: [Type(Generic("T"))], constraints: [] } }] } })], constraints: [] } }] } })], constraints: [] } }] })
 DEBUG rustdoc::clean::utils register_res(Def(Struct, DefId(3:669 ~ alloc[61df]::boxed::Box)))
 DEBUG rustdoc::clean::utils resolve_type(Path { res: Def(TyParam, DefId(0:4 ~ rustdoc_hang_repro[5a4c]::Recursive::T)), segments: [PathSegment { name: "T", args: AngleBracketed { args: [], constraints: [] } }] })
 INFO rustdoc::core Executing passes
 DEBUG rustdoc::core running pass collect-trait-impls
 rustdoc::clean::auto_trait::synthesize_auto_trait_impls item_def_id=DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive)
 rustdoc::clean::auto_trait::synthesize_auto_trait_impls item_def_id=DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive)
   rustdoc::clean::auto_trait::synthesize_auto_trait_impl ty=Recursive<T/#0>, trait_def_id=DefId(2:40342 ~ core[3b42]::marker::Send), typing_env=TypingEnv { typing_mode: TypingModeEqWrapper(Analysis { defining_opaque_types_and_generators: [] }), param_env: ParamEnv { caller_bounds: [Binder { value: TraitPredicate(<T as std::marker::Sized>, polarity:Positive), bound_vars: [] }] } }, item_def_id=DefId(0:3 ~ rustdoc_hang_repro[5a4c]::Recursive), discard_positive_impls=No

Version

1.97.1

Additional Details

With RUSTFLAGS="-Znext-solver=globally", cargo +nightly doc also hangs

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the hang from the recursive type in src/lib.rs with cargo doc, then inspect the rustdoc::clean::auto_trait::synthesize_auto_trait_impls trace where processing stops. Compare normal and -Znext-solver=globally runs; done means rustdoc completes instead of hanging for the provided example.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.