I am implementing core from scratch for fun (I know, I know, you're not supposed to do that). I've gotten to the point where I'm defining the Add trait for builtin types such as i32. To implement such a trait, I think I should use the + operator inside add, like found in the actual core crate. However, this begs the question, how does the compiler know to not use the Add trait implementation in add (to avoid a self-referential issue) and to use the Add implementation outside?
Take the following code, which emulates the behavior inside of core:
How does this code work? (I compile with a nightly rustc fully_perform_ice.rs)
It doesn't in fact, for reasons I don't understand. I get an ICE:
warning: the feature `lang_items` is internal to the compiler or standard library
--> fully_perform_ice.rs:2:12
|
2 | #![feature(lang_items)]
| ^^^^^^^^^^
|
= note: using it is strongly discouraged
= note: `#[warn(internal_features)]` on by default
warning: 1 warning emitted
note: no errors encountered even though delayed bugs were created
note: those delayed bugs will now be shown as internal compiler errors
error: internal compiler error: error performing operation: fully_perform
--> fully_perform_ice.rs:41:9
|
41 | self + other
| ^^^^
|
note: delayed at /Users/dylngg/Source/rust/git/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs:87:25 - disabled backtrace
--> fully_perform_ice.rs:41:9
|
41 | self + other
| ^^^^
error: internal compiler error: error performing operation: fully_perform
--> fully_perform_ice.rs:41:16
|
41 | self + other
| ^^^^^
|
note: delayed at /Users/dylngg/Source/rust/git/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs:87:25 - disabled backtrace
--> fully_perform_ice.rs:41:16
|
41 | self + other
| ^^^^^
error: internal compiler error: error performing operation: fully_perform
--> fully_perform_ice.rs:41:9
|
41 | self + other
| ^^^^^^^^^^^^
|
note: delayed at /Users/dylngg/Source/rust/git/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs:87:25 - disabled backtrace
--> fully_perform_ice.rs:41:9
|
41 | self + other
| ^^^^^^^^^^^^
note: using internal features is not supported and expected to cause internal compiler errors when used incorrectly
note: please attach the file at `/Users/dylngg/Desktop/rust/rustc-ice-2024-09-01T21_06_27-62553.txt` to your bug report
query stack during panic:
end of query stack
Can any rust compiler experts point out how my Add usage is wrong? It seems to be nearly the same as core.
I do see the issue with the same error for a seemingly unrelated bug. However, this is core behavior, so I think there must be something minor I am overlooking in my implementation.
To be fair, something like this (i.e. effectively an intrinsic) was always going to be unavoidable at some point, for any PL implementation. The only alternative I know of would be circular definitions, which may as well be an antonym to the word "useful".
IIRC the reason this occurs is that while the operation is lowered as an intrinsic and not a call to Add::add, the trait obligation that ensures Add is implemented is still validated.
Lang items are for most intents and purposes part of the compiler and doing them wrong is unsupported.
Also, the compiler hard-codes knowledge that primitives like i32 are copy -- because going to check traits for that is a waste of CPU -- but that means if you don't have i32: Copy you'll get a MIR typecheck error later.
it isn't impossible; you can totally avoid it by delegating to an intrinsic function call like extern "rust-operator" fn add_i32(). The compiler just doesn't do so because (afaik) of unnecessary compile speed impact for OCD.
That would just be punting the existence of that intrinsic down the line though. In particular, it wouldn't get rid of it.
The point I was making was that such an intrinsic will always exist somewhere.