Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better errors for polymorphic recursion #136528

Open
jake-87 opened this issue Feb 4, 2025 · 5 comments
Open

Better errors for polymorphic recursion #136528

jake-87 opened this issue Feb 4, 2025 · 5 comments
Labels
A-destructors Area: Destructors (`Drop`, …) A-diagnostics Area: Messages for errors, warnings, and lints D-confusing Diagnostics: Confusing error or lint that should be reworked. D-verbose Diagnostics: Too much output caused by a single piece of incorrect code. I-monomorphization Issue: An error at monomorphization time. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@jake-87
Copy link

jake-87 commented Feb 4, 2025

Code

enum Nested<T> {
    End,
    Cons(T, Box<Nested<Vec<T>>>),
}

fn length<T>(x: Nested<T>) -> u64 {
    match x {
        Nested::End => 1,
        Nested::Cons(_, xs) => 1 + length(*xs)
    }
}

Current output

Compiling playground v0.0.1 (/playground)
error[E0320]: overflow while adding drop-check rules for `Nested<T>`
 --> src/lib.rs:6:14
  |
6 | fn length<T>(x: Nested<T>) -> u64 {
  |              ^
  |
  = note: overflowed on `Nested<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`

error[E0320]: overflow while adding drop-check rules for `Box<Nested<Vec<T>>>`
 --> src/lib.rs:9:25
  |
9 |         Nested::Cons(_, xs) => 1 + length(*xs)
  |                         ^^
  |
  = note: overflowed on `Nested<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`

error[E0320]: overflow while adding drop-check rules for `Nested<Vec<T>>`
 --> src/lib.rs:9:43
  |
9 |         Nested::Cons(_, xs) => 1 + length(*xs)
  |                                           ^^^
  |
  = note: overflowed on `Nested<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<Vec<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`

For more information about this error, try `rustc --explain E0320`.
error: could not compile `playground` (lib) due to 3 previous errors

Desired output

Not sure what an ideal output would be (Is polymorphic recursion too complex/confusing a term? Presumably if someone is writing code like the above they might know what it is), but ideally something that doesn't spam ~120 Vecs at you when erroring.

Rationale and extra context

Polymorphic recursion is not valid in rust, due to both drop restrictions (what causes the error above), and monomorphization restrictions (it cannot be monomorphized). The current errors are not very explanatory as to what the core issue is.

Other cases

Rust Version

$ rustc 1.85.0-nightly (917bfa784 2024-12-26)
binary: rustc
commit-hash: 917bfa78478cbcc77406e5ea37b24c3eedefacf4
commit-date: 2024-12-26
host: x86_64-unknown-linux-gnu
release: 1.85.0-nightly
LLVM version: 19.1.6

Anything else?

No response

@jake-87 jake-87 added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Feb 4, 2025
@theemathas
Copy link
Contributor

theemathas commented Feb 4, 2025

Surprisingly.... polymorphic recursion seems to be accepted by the compiler if you're not dropping values?

This compiles:

enum Nested<T> {
    End,
    Cons(T, Box<Nested<Vec<T>>>),
}

fn length<T>(x: &Nested<T>) -> u64 {
    match x {
        Nested::End => 1,
        Nested::Cons(_, xs) => 1 + length(xs)
    }
}

fn foo(x: &Nested<i32>) -> u64 {
    length::<i32>(x)
}

Edit: It errors as soon as you make foo public, which seems to cause the compiler to attempt monomorphization.

@jake-87
Copy link
Author

jake-87 commented Feb 4, 2025

Interesting, thank you. That is also less than ideal, however. One would hope that marking something pub does not change whether it errors or not, I presume. Fixing this would require some sort of dedicated polymorphic recursion check though, I think - otherwise there's no way to know until monomorphization time.

This is the error, as expected upon making foo public:

error: reached the recursion limit while instantiating `length::<Vec<Vec<Vec<Vec<Vec<...>>>>>>`
 --> src/lib.rs:9:36
  |
9 |         Nested::Cons(_, xs) => 1 + length(xs)
  |                                    ^^^^^^^^^^
  |
note: `length` defined here
 --> src/lib.rs:6:1
  |
6 | fn length<T>(x: &Nested<T>) -> u64 {
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  = note: the full type name has been written to '/playground/target/debug/deps/playground-f300214f41d7cb9c.long-type.txt'

@fmease fmease added the I-monomorphization Issue: An error at monomorphization time. label Feb 4, 2025
@fmease
Copy link
Member

fmease commented Feb 4, 2025

cc #4287 #18874

@saethlin
Copy link
Member

saethlin commented Feb 4, 2025

One would hope that marking something pub does not change whether it errors or not, I presume.

Errors that require monomorphization to occur are not necessarily detected in dead code. We always type-check and borrow-check all code, but from that point onwards the compiler tries to prune dead code from compilation.

@jake-87
Copy link
Author

jake-87 commented Feb 5, 2025

@fmease Thank you for linking those two. I did take note of #4287, but I decided to open this issue as I believe the solution that closed that issue was not satisfactory, error-message-wise.

@saethlin I see - that does make sense, and I assumed that was why it was not "correctly" erroring.
Seeing as a dedicated pass is most likely entirely too complex for what is a quite niche issue, I personally think it would be reasonable to improve the error ergonomics of both mentioned situations separately, if needed.

@fmease fmease added A-destructors Area: Destructors (`Drop`, …) D-verbose Diagnostics: Too much output caused by a single piece of incorrect code. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. D-confusing Diagnostics: Confusing error or lint that should be reworked. and removed D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. labels Feb 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-destructors Area: Destructors (`Drop`, …) A-diagnostics Area: Messages for errors, warnings, and lints D-confusing Diagnostics: Confusing error or lint that should be reworked. D-verbose Diagnostics: Too much output caused by a single piece of incorrect code. I-monomorphization Issue: An error at monomorphization time. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants