Today we ran into a weird issue while generating the rustdoc page for our project. As you can see here we run into a recursion limit issue. However increasing the recursion limit (#![recursion_limit = "4096"]) does not resolve it.
It worked on version 1.37.0-nightly-2019-06-21 but breaks on 1.37.0-nightly-2019-06-22
Any help would be appreciated.
Edit:
For reference as to how we got this error, here is our repo.
Most likely caused by https://github.com/rust-lang/rust/pull/60293, but my understanding was that with https://github.com/rust-lang/rust/pull/60444 increasing the recursion limit should help.
Just to make sure you are adding the recursion limit attribute to the synstructure crate, right?
We do not directly use the synstructure crate directly ourselves. It is listed as a dependency of PyO3.
Therefore we do not directly add it to the synstructure crate (or know how to).
Minimal Cargo.toml to reproduce:
[package]
name = "a"
version = "0.1.0"
[dependencies]
synstructure = "0.10"
syn = { version = "0.15", features = ["full"] }
Adding #![recursion_limit="128"] to synstructure helps, however the leaf crates will still fail to document if they do something like this: pub struct A<'a>(pub synstructure::Structure<'a>); as those leaf crates will still have the original limit of 64. This is unlike
fn assert_unpin<T: std::marker::Unpin>() {}
pub fn foo() {
assert_unpin::<Vec<syn::GenericParam>>();
}
which only needs the attribute in the crate which actually contains this code (i.e. it does not necessarily infect the dependent crates).
This is, sadly a deeper issue and the change in https://github.com/rust-lang/rust/pull/60293 only happens to expose it. Any ideas what we could do here to remove/propagate/etc this limit when documenting cc @rust-lang/compiler?
cc @dtolnay you may want to be aware about std::vec::Vec<syn::generics::GenericParam> crating deep stacks when trying to prove implementations of autotraits.
Thanks @nagisa. Is there anything I can do about this on my end?
The trace in the error looks correct to me; this is a consequence of almost all Rust syntax being allowed inside of almost all other Rust syntax.
std::vec::Vec<syn::GenericParam>alloc::raw_vec::RawVec<syn::GenericParam>std::ptr::Unique<syn::GenericParam>*const syn::GenericParamsyn::GenericParamsyn::TypeParamstd::vec::Vec<syn::Attribute>alloc::raw_vec::RawVec<syn::Attribute>std::ptr::Unique<syn::Attribute>*const syn::Attributesyn::Attributesyn::Pathsyn::punctuated::Punctuated<syn::PathSegment, syn::token::Colon2>std::vec::Vec<(syn::PathSegment, syn::token::Colon2)>alloc::raw_vec::RawVec<(syn::PathSegment, syn::token::Colon2)>std::ptr::Unique<(syn::PathSegment, syn::token::Colon2)>*const (syn::PathSegment, syn::token::Colon2)(syn::PathSegment, syn::token::Colon2)syn::PathSegmentsyn::PathArgumentssyn::AngleBracketedGenericArgumentssyn::punctuated::Punctuated<syn::GenericArgument, syn::token::Comma>std::vec::Vec<(syn::GenericArgument, syn::token::Comma)>alloc::raw_vec::RawVec<(syn::GenericArgument, syn::token::Comma)>std::ptr::Unique<(syn::GenericArgument, syn::token::Comma)>*const (syn::GenericArgument, syn::token::Comma)(syn::GenericArgument, syn::token::Comma)syn::GenericArgumentsyn::Typesyn::TypeArraysyn::Exprsyn::ExprIfsyn::Blockstd::vec::Vec<syn::Stmt>alloc::raw_vec::RawVec<syn::Stmt>std::ptr::Unique<syn::Stmt>*const syn::Stmtsyn::Stmtsyn::Itemsyn::ItemTypesyn::Genericsstd::option::Option<syn::WhereClause>syn::WhereClausesyn::punctuated::Punctuated<syn::WherePredicate, syn::token::Comma>std::vec::Vec<(syn::WherePredicate, syn::token::Comma)>alloc::raw_vec::RawVec<(syn::WherePredicate, syn::token::Comma)>std::ptr::Unique<(syn::WherePredicate, syn::token::Comma)>*const (syn::WherePredicate, syn::token::Comma)(syn::WherePredicate, syn::token::Comma)syn::WherePredicatesyn::PredicateTypestd::option::Option<syn::BoundLifetimes>syn::BoundLifetimessyn::punctuated::Punctuated<syn::LifetimeDef, syn::token::Comma>std::vec::Vec<(syn::LifetimeDef, syn::token::Comma)>alloc::raw_vec::RawVec<(syn::LifetimeDef, syn::token::Comma)>std::ptr::Unique<(syn::LifetimeDef, syn::token::Comma)>*const (syn::LifetimeDef, syn::token::Comma)(syn::LifetimeDef, syn::token::Comma)syn::LifetimeDefsyn::punctuated::Punctuated<syn::Lifetime, syn::token::Add>std::vec::Vec<(syn::Lifetime, syn::token::Add)>@dtolnay Other than reducing the depth of types somehow, there鈥檚 very little that can be done on your side. You could manually implement all the marker types for certain types, but the same issue will resurface for new/external auto-traits.
Does this warrant increasing the default recursion limit? I would expect the default to be selected such that it is higher than what you would need in any "reasonable" real world code. If the data structures in Syn are "reasonable" i.e. neither contrived nor somehow unsolvable regardless of recursion limit, then this suggests that the current limit is too aggressive. What are the implications of changing it?
This also affected fuzzy-pickles but there were some fixes that seemed to help there. Wonder why the difference.
Most likely caused by #60293, but my understanding was that with #60444 increasing the recursion limit should help.
@nagisa, are you saying that the expected fix is to go through the ecosystem and add #![recursion_limit = "something"] as needed? Or, what do you think of raising the default limit, as suggested by dtolnay above?
cc #61960, which tracks the instances where #60444 has hit an overflow during recursive trait constraint resolution.
(Also, I am assuming that this issue (#62059) is not resolved by PR #61754, but it would be good to double-check that assumption.)
@SimonSapin asked
@nagisa, are you saying that the expected fix is to go through the ecosystem and add
#![recursion_limit = "something"]as needed? Or, what do you think of raising the default limit, as suggested by dtolnay above?
my hope is that many of the cases affected by PR #60444 will be addressed by PR #61754, which we recently approved for beta backport so that it lands in time for the release.
Servo hit this issue in rustc 1.37.0-nightly (929b48ec9 2019-06-21), see https://github.com/rust-lang/rust/issues/62132. This version does include https://github.com/rust-lang/rust/pull/61754, so https://github.com/rust-lang/rust/pull/61754 is not a sufficient fix.
@SimonSapin I will probably just revert the patch to rustdoc if there is no other option than annotating crates with an attribute. rustdoc鈥檚 case is special in that it infects dependent crates much more easily and having such an attribute in a large number of crates is not a tenable solution.
Ideally rustc/rustdoc would work for arbitrarily deep types, but I can see raising the default limit as a good stop-gap as well.
@pnkfelix at this point I doubt either of #60444 or #61754 are very related as the type-stack here seems to be very linear.
Nominating for a short section of time in the meeting to discuss the best way to proceed here. The non-involved solutions I know at the moment are:
Send and Sync.Will see if raising the limit is something that we could or should do and perhaps whether there are other options that I have missed.
pre-triage: P-high. Leaving nominated in hopes we discuss it today. Leaving unassigned to reflect reality that I don't think I can take this on myself. (But maybe @nagisa can take point on ensuring this gets resolved in short order...?)
discussed at compiler team meeting. @eddyb raised the idea that rustdoc could just silently ignore the cases where we hit the existing limit, and treat them as unimplemented traits.
In any case, based on assumption that this is indeed a stable-to-nightly regression, I think we have a little bit of breathing room to figure out how we want to address this long term.
Assigning to @nagisa to take point on resolving this, potentially via delegation. (@nagisa , let me know if that does not work for you.)
This is blocking the upgrade of rustc in Servo. (And I get nagged about it every day by the corresponding CI job failing.)
@rust-lang/compiler Is anyone working on a revert or other fix, or should I go add recursion_limit to synstructure and other crates as needed?
I鈥檓 looking into this and investigating the feasibility of implementing @eddyb鈥檚 idea or some other fix. Alas, I only have sufficiently long uninterrupted chunks of time on the weekends, so the progress is slow and is very plausible that this won鈥檛 be fixed within a few days.
triage: assigning to @nagisa and myself for follow-up.
@SimonSapin do you have any estimate of how many crates would require the recursion_limit addition?
There is one that we know of in Servo鈥檚 dependency graph (synstructure). I don鈥檛 know if there are more.
For the purposes of rustdoc, you鈥檒l need to add attribute not only for the synstructure crate, but also recursively to any crate which depends on synstructure. I consider that to be unfeasible. So far I鈥檓 inclined to just bump the default recursion limit to 128 or something and solve it properly later, but as e.g. stack size increase has showed, proper fix sometimes fizzles out and the resource bump stays in place for unlimited amount of time.
By running cargo doc -j 100 repeatedly, I find two affected crates in Servo鈥檚 dependency graph: synstructure and derivative. It looks like none of the dependent crates make types that are deeper still based on those.
I've just bumped into this issue when trying to build glutin's docs.
rustc 1.38.0-nightly (dfd52ba6a 2019-07-06)
Hoping this gets raised.
Most helpful comment
Does this warrant increasing the default recursion limit? I would expect the default to be selected such that it is higher than what you would need in any "reasonable" real world code. If the data structures in Syn are "reasonable" i.e. neither contrived nor somehow unsolvable regardless of recursion limit, then this suggests that the current limit is too aggressive. What are the implications of changing it?