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

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

Open
comex opened this Issue Mar 26, 2016 · 7 comments

Comments

Projects
None yet
8 participants
@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).

@pnkfelix

This comment has been minimized.

Copy link
Member

pnkfelix commented Mar 26, 2016

triage: I-nominated

@pnkfelix

This comment has been minimized.

Copy link
Member

pnkfelix commented Mar 26, 2016

cc @aturon

@arielb1

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

Copy link
Member

pnkfelix commented Mar 31, 2016

triage: P-high to investigate

@aturon

This comment has been minimized.

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 labels Apr 1, 2016

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Apr 7, 2016

triage: P-low

@aturon's summary seems accurate.

@rust-highfive rust-highfive added P-low and removed I-nominated labels Apr 7, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.