I finally found the cheat-code for disabling the type-checker /s

Is such problem caused by recursion and type erase? E.g, type Gender defined in trait Trans recursively uses Trans. No warnings at all. But for the following code

fn a(mut u: u8) -> u8 {
    u *= a(u) + 1;
    a(1)
}

The compiler just showed warning about function a instead of saying it's an error.

Recursive functions are a normal programming language feature. In bad cases, they might never terminate at run-time when you call them, but that's fine. (You can get a warning at compile time, but that's just for convenience, the run-time behavior of this function is well-defined and not problematic to the type system at large: it just never finishes - or overflows the stack.)

Bad cases of recursively defined type synonyms on the other hand, should be erraneous at compile-time, at least if they're used somewhere. Type synonyms/aliases don't exist at run-time. The issue in Typesystem soundness hole involving cyclically-defined ATIT, and normalization in trait bounds · Issue #135011 · rust-lang/rust · GitHub is with a situation where such a type was “used” in some sense - to make use of its trait bound - but ended up “unused” in another sense - so that the non-terminating recursive nature of it didn't result in a compilation error.

That's a compiler bug, as it results in undesirable consequences[1].

This particular “minimized” version of the code ends up not really being recursive anymore though. If you pay attention to the definition, the right-hand side is merely = L. Badly cyclic reasoning is however still present in the trait bounds that are required & used.

The relevant point in the linked issue, where a bit more explanation is given (for a slightly different variant of this code), is this comment, offering some comments in the code, and linking some related discussions. Also after that comment a separate thread was opened for this “minimized” code, as it was deemed sufficiently different from the original issue.

Understanding the code in much more detail is hard, the issue involves subtle points in the interaction of various aspects of the type system – ultimately it's arguably just about understanding all the relevant implementation details of Rust's type and trait system. Remember, it is a bug, there's only limited learning opportunity from asking “why this code works” from pure language userʼs perspective, because it isn't even supposed to work. Some compiler team members who've commented on the issue definitely have an even better understanding of the underlying issue than I have; I've already linked a relevant comment above.

Please do note that the main purpose of these issues threads on GitHub is to track any progress with fixing the problems, and I expect it might take a while until they're fixable. So even though the Github issues are a relevant place to look for leaning more about the details of this exploitation, to keep the issues' discussion threads manageable[2], they might not be the best place to ask very basic questions about it. Thank you for asking here instead.


  1. unsound code without requiring the unsafe keyword; some cases of “internal compiler error”s of bringing the compiler into a state where e. g. the possibility of mismatching types (rightfully) wasn't anticipated by transformations or code generation that happen later than the trait and type checking; even some cases of bad code being generated for LLVM which then crashes from thinfstlike incompatible calling conventions ↩︎

  2. not growing too large over time ↩︎

6 Likes

Yeah, we don't pretend to fully understand the causes for the bug either, we just saw the post in a Discord and decided to make an account exactly for that one singular shitpost :stuck_out_tongue:.

This was a very good read! Thank you for all the explanation and the time taken to explain it. We read the code and just assumed "yeah something probably got erased here and some invariant unchecked".

this ended up being an actual tutorial /s

2 Likes

For a more sensible debugging solution maybe just hack std to impl Debug for everything, printing its name and turn on unstable specialization?