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

overflow => generics that produce trans errors depending on type parameters #32498

Closed
comex opened this issue Mar 26, 2016 · 10 comments
Closed
Labels
A-specialization Area: Trait impl specialization C-bug Category: This is a bug. P-low Low priority T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@comex
Copy link
Contributor

comex commented Mar 26, 2016

In which we sneakily define a type that contains itself:

(EDIT: Actually, this one didn't depend on specialization. There was previously a default impl of Tr2 not depending on Tr, but it wasn't necessary. The second one does seem to depend on it.)

trait Tr {
    type X;
}
trait Tr2 {
    type Y;
}

struct S<T: ?Sized + Tr> {
    x: <<T as Tr>::X as Tr2>::Y,
}

impl<T: Tr> Tr2 for T {
    type Y = S<T>;
}

impl<T> Tr for T { type X = T; }

fn func<T>() {
    let _: S<T> = panic!();
}

fn main() {
    func::<u32>();
}

produces:

error: overflow representing the type S<u32>

This error comes from trans, and it goes away if either the call to func or func's usage of S<T> is commented out. In other words, func claims to allow instantiation with any type but actually supports no types. (By adding more specializations it could be made to support only certain types.)

A similar issue is when overflow occurs while determining whether a given specialization applies:

#![feature(specialization)]

trait Tr {
    type Other;
}
struct S<T>(T);
impl<T> Tr for T {
    default type Other = <S<T> as Tr>::Other;
}
impl Tr for S<S<S<S<u32>>>> {
    type Other = u32;
}
fn func<T>() {
    let _: <T as Tr>::Other = panic!();
}

fn main() {
    func::<u32>();
}

This happens to compile, but if the u32 in main is changed to u64, I get:

error: overflow evaluating the requirement <S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<u64>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> as Tr>::Other

Is this a known consequence of specialization? I haven't seen it discussed. It seems to violate Rust's normal policy of type-safe generics, and I'm not sure what a rule to prevent it upfront would look like (especially considering that the patterns can in some cases be split across multiple crates).

@Aatch Aatch added the A-specialization Area: Trait impl specialization label Mar 26, 2016
@pnkfelix pnkfelix added the T-lang Relevant to the language team, which will review and decide on the PR/issue. label Mar 26, 2016
@pnkfelix
Copy link
Member

triage: I-nominated

@pnkfelix
Copy link
Member

cc @aturon

@arielb1
Copy link
Contributor

arielb1 commented Mar 26, 2016

This is possible even without specialization. At least, it used to be with the old Sized rules.

@comex
Copy link
Contributor Author

comex commented Mar 26, 2016

I made a mistake: the first example doesn't actually require specialization, and the simplified version I edited into the original post works the same way on stable.

Though I think it may be more difficult to fix in the presence of specialization: the only reason func can instantiate S<T>, which requires Tr, with its T, which doesn't, is the blanket impl on Tr, but that blanket impl could also be consulted to "see through" the X in <<T as Tr>::X as Tr2>::Y - writing <T as Tr2>::Y already produces the (somewhat dubiously worded) pre-trans error "overflow evaluating the requirement <T as Tr2>::Y". However, with specialization, there can be both a blanket impl and many specific impls; the compiler could check every possibility, but I don't think this can work correctly with impls split across crates, and in any case it would have exponentially many possibilities as the number of nested projections grows, and might be annoyingly conservative. Disclaimer: I don't know much about compiler internals so I could be completely misguided.

@pnkfelix
Copy link
Member

triage: P-high to investigate

@rust-highfive rust-highfive added P-high High priority and removed I-nominated labels Mar 31, 2016
@aturon
Copy link
Member

aturon commented Apr 1, 2016

So, I see the first example as a case that we should ultimately try to catch and give a better error for (as we do for more direct circular structs). But I don't think it's terribly urgent, personally.

For the second case: I suspect that this is fundamental. That is, the proof search for traits fundamentally relies on an overflow cutoff to avoid infinite loops, and the fact that specialization lets you dispatch on types will always mean that you can selectively create these overflowing situations. I see the "blame" here as mostly the fact that the trait system can fail due to overflow, but that's inevitable in having as powerful of a system as we do.

I'm going to renominate for triage of the bug listed in the top comment, which we could try to provide a better error for. I suggest that this is P-low, however.

triage: I-nominated

@rust-highfive rust-highfive added I-nominated and removed P-high High priority labels Apr 1, 2016
@nikomatsakis
Copy link
Contributor

triage: P-low

@aturon's summary seems accurate.

@rust-highfive rust-highfive added P-low Low priority and removed I-nominated labels Apr 7, 2016
@Mark-Simulacrum Mark-Simulacrum added the C-bug Category: This is a bug. label Jul 24, 2017
@aturon aturon removed their assignment Apr 10, 2019
@deavid
Copy link

deavid commented Apr 14, 2020

I got the same while playing around with Rust:

struct L<T> {
    n: Option<T>,
}
type L8<T> = L<L<L<L<L<L<L<L<T>>>>>>>>;
type L64<T> = L8<L8<L8<L8<T>>>>;

fn main() {
    use std::mem::size_of;
    // This prints "128: 1":
    println!("128: {}", size_of::<L64<L64<()>>>()); 
    // This line does not compile: error: overflow representing the type std::option::Option<()>
    println!("129: {}", size_of::<L<L64<L64<()>>>>()); 
}

While something like this might not make much sense in real programs I thought it might be useful to you to have a minimal source that demonstrates the problem.

Option is not needed here, but it cuts down the amount of "recursivity" in half. So it seems there is some kind of limit of 256 levels when building a struct.

I wonder if it's possible to reach the limit with real-life programs. Most recursive or complex structs hold pointers, not actual data, so I guess it's not a problem for most people.

@comex comex changed the title specialization + overflow => generics that produce trans errors depending on type parameters overflow => generics that produce trans errors depending on type parameters May 16, 2020
vandenheuvel added a commit to vandenheuvel/rust that referenced this issue Feb 9, 2021
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Feb 14, 2021
…mulacrum

Add a regression test for rust-lang#32498

[This](rust-lang#32498 (comment)) test mentioned at issue rust-lang#32498 now passes. This PR adds this regression test.
bors added a commit to rust-lang-ci/rust that referenced this issue Feb 14, 2021
Rollup of 11 pull requests

Successful merges:

 - rust-lang#80523 (#[doc(inline)] sym_generated)
 - rust-lang#80920 (Visit more targets when validating attributes)
 - rust-lang#81720 (Updated smallvec version due to RUSTSEC-2021-0003)
 - rust-lang#81891 ([rustdoc-json] Make `header` a vec of modifiers, and FunctionPointer consistent)
 - rust-lang#81912 (Implement the precise analysis pass for lint `disjoint_capture_drop_reorder`)
 - rust-lang#81914 (Fixing bad suggestion for `_` in `const` type when a function rust-lang#81885)
 - rust-lang#81919 (BTreeMap: fix internal comments)
 - rust-lang#81927 (Add a regression test for rust-lang#32498)
 - rust-lang#81965 (Fix MIR pretty printer for non-local DefIds)
 - rust-lang#82029 (Use debug log level for developer oriented logs)
 - rust-lang#82056 (fix ice (rust-lang#82032))

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@bstrie
Copy link
Contributor

bstrie commented Jul 3, 2021

deavid's minimized reproduction compiles and runs for me (Rust 1.53). comex's original example still fails, but the error messages are different, and it's no longer possible to bypass the error by commenting out usage. Since this bug report is less along the lines of "types should be allowed to recurse indefinitely" and more along the lines of "too-large types shouldn't require usage in order to be rejected", I suspect this can be closed. However, a UI test might be nice.

@Dylan-DPC
Copy link
Member

Closing this based on the [comment](#32498 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-specialization Area: Trait impl specialization C-bug Category: This is a bug. P-low Low priority T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests