Hello, dear rustaceans!
I'm currently studying the internal implementation of the Rust to better understand how the standard library works. As an addition to the reading of the comments and the code I also try to implement small examples, to be able to play around with them. With this approach I managed to implement my own and custom Sized, Copy and a few other lang_item-crates. Right now I'm trying to implement my own math-operator traits, like Add and Sub, and the nightly rustc-compiler returns internal compiler errors (ICE), because (as of my understanding) the compiler fails to perform the borrow-check. As I am still learning and could not find a working solution online, I would like to ask for help on this subject.
Code + Structure
While I was searching for solution to my problem I found a very good blog by Manish Goregaokar covering this topic, referenced here: Blog Post. It is written in 2017, so I would assume this can be out of date for the current nightly compiler. Below you can see the code shown in that blog, that of my understanding should be the minimal working example. I also tested it on Rust-Playground and it didn't work for me. My own and first attempt was on the other way simply to copy the current core-implementation with all the macros and attributes. A version without the macros, as they don't affect the errors - only the count of them, can be found here: Rust Playgound My-attempt.
#![feature(no_core, lang_items)]
#![no_core]
#![no_std]
#[lang = "sized"] pub trait Sized {}
#[lang = "copy"] pub trait Copy {}
#[lang = "add"]
trait MyAdd<RHS> {
type Output;
fn add(self, other: RHS) -> Self::Output;
}
impl MyAdd<isize> for isize {
type Output = isize;
fn add(self, other: isize) -> isize {self + other + 50}
}
fn main() {40 + 2;}
My understanding of how it should work:
- First it is important to unlink all libraries injected by the Rust-compiler by default. Therefor
#[no_std]
unlinks the std-lib and#[no_core]
unlinks the core-lib. This way we remove existing rust-implementations from our code. - Also it is necessary to add the used features:
lang_items
to be able to add#[lang = "..."]
-attributes, that are needed for implementing language-features likeSized
,Copy
andAdd
- these are then used by the compiler to be used instead of the core-lib ones, andno_core
to be able to mark the crate to be able of not using the core-lib, as#[no_core]
is unstable as of today. - Then the Add-trait is defined with the appropriate lang-item, the needed functions and their implementation.
- Because the Add-implementation for usize needs to move values, it is important to implement the
Sized
- andCopy
-lang-items, so that the compiler can use them during compilation. - In the main we define a few simple test-lines to check whether everything works as intended.
- Optionally: we can use some attributes as in the core-lib on the code. The background to this is, that while I worked on my implementation of a few intrinsic functions I found out, that some of the attributes are necessary. In particular it is not possible to declare
const
functions as external. To circumvent this, a const-stabelize-attribute is used, to force the compiler to allow these intrinsic functions to be used in the const-context. Therefor, to be sure, that I didn't missed some important line of code, I also copied the used attributes.
The Error
While trying to compile the code I receive the following error: error: internal compiler error: error performing operation: fully_perform
on all occurrences of addition:
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
--> src/lib.rs:17:42
|
17 | fn add(self, other: isize) -> isize {self + other + 50}
| ^^^^
|
note: delayed at /rustc/6f3df08aadf71e8d4bf7e49f5dc10dfa6f254cb4/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs:85:25 - disabled backtrace
--> src/lib.rs:17:42
|
17 | fn add(self, other: isize) -> isize {self + other + 50}
| ^^^^
error: internal compiler error: error performing operation: fully_perform
--> src/lib.rs:17:49
|
17 | fn add(self, other: isize) -> isize {self + other + 50}
| ^^^^^
|
note: delayed at /rustc/6f3df08aadf71e8d4bf7e49f5dc10dfa6f254cb4/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs:85:25 - disabled backtrace
--> src/lib.rs:17:49
|
17 | fn add(self, other: isize) -> isize {self + other + 50}
| ^^^^^
note: using internal features is not supported and expected to cause internal compiler errors when used incorrectly
note: please attach the file at `rustc-ice-2024-05-31T23_03_23-672276.txt` to your bug report
note: compiler flags: --crate-type lib -C embed-bitcode=no -C debuginfo=2 -C incremental=[REDACTED]
note: some of the compiler flags provided by cargo are hidden
query stack during panic:
end of query stack
error: could not compile `Tests` (lib)
It is important to note, that, based on the compiler-version and release- or debug-mode, the concrete output can variate a little. While the current nightly-compiler doesn't show the backtrace in the provided .txt-file (maybe I didn't turned it on), I still have a lot of older files, that contain the same backtrace multiple times and have the version and platform at the end, like this:
delayed bug: error performing operation: fully_perform
0: <rustc_errors::DiagCtxtInner>::emit_diagnostic
1: <rustc_errors::DiagCtxt>::emit_diagnostic
2: <rustc_span::ErrorGuaranteed as rustc_errors::diagnostic::EmissionGuarantee>::emit_producing_guarantee
3: <rustc_borrowck::type_check::TypeChecker>::prove_trait_ref
4: <rustc_borrowck::type_check::TypeVerifier as rustc_middle::mir::visit::Visitor>::visit_operand
5: <rustc_borrowck::type_check::TypeVerifier as rustc_middle::mir::visit::Visitor>::visit_body
6: rustc_borrowck::type_check::type_check
7: rustc_borrowck::nll::compute_regions
8: rustc_borrowck::do_mir_borrowck
9: rustc_query_impl::plumbing::__rust_begin_short_backtrace::<rustc_query_impl::query_impl::mir_borrowck::dynamic_query::{closure#2}::{closure#0}, rustc_middle::query::erase::Erased<[u8; 8]>>
10: rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::DynamicConfig<rustc_query_system::query::caches::VecCache<rustc_span::def_id::LocalDefId, rustc_middle::query::erase::Erased<[u8; 8]>>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, true>
11: rustc_query_impl::query_impl::mir_borrowck::get_query_incr::__rust_end_short_backtrace
12: rustc_interface::passes::analysis
13: rustc_query_impl::plumbing::__rust_begin_short_backtrace::<rustc_query_impl::query_impl::analysis::dynamic_query::{closure#2}::{closure#0}, rustc_middle::query::erase::Erased<[u8; 1]>>
14: rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::DynamicConfig<rustc_query_system::query::caches::SingleCache<rustc_middle::query::erase::Erased<[u8; 1]>>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, true>
15: rustc_query_impl::query_impl::analysis::get_query_incr::__rust_end_short_backtrace
16: rustc_interface::interface::run_compiler::<core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_driver_impl::run_compiler::{closure#0}>::{closure#1}
17: std::sys_common::backtrace::__rust_begin_short_backtrace::<rustc_interface::util::run_in_thread_with_globals<rustc_interface::util::run_in_thread_pool_with_globals<rustc_interface::interface::run_compiler<core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_driver_impl::run_compiler::{closure#0}>::{closure#1}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>
18: <<std::thread::Builder>::spawn_unchecked_<rustc_interface::util::run_in_thread_with_globals<rustc_interface::util::run_in_thread_pool_with_globals<rustc_interface::interface::run_compiler<core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_driver_impl::run_compiler::{closure#0}>::{closure#1}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#2} as core::ops::function::FnOnce<()>>::call_once::{shim:vtable#0}
19: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
at /rustc/6f3df08aadf71e8d4bf7e49f5dc10dfa6f254cb4/library/alloc/src/boxed.rs:2063:9
20: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
at /rustc/6f3df08aadf71e8d4bf7e49f5dc10dfa6f254cb4/library/alloc/src/boxed.rs:2063:9
21: std::sys::pal::unix::thread::Thread::new::thread_start
at /rustc/6f3df08aadf71e8d4bf7e49f5dc10dfa6f254cb4/library/std/src/sys/pal/unix/thread.rs:108:17
22: start_thread
at ./nptl/pthread_create.c:442:8
23: __GI___clone3
at ./misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
... [same backtrace multiple times]
rustc version: 1.80.0-nightly (6f3df08aa 2024-05-30)
platform: x86_64-unknown-linux-gnu
So for me it looks like the compiler is trying to prove, that all variables have a valid lifetime, based on points 6 trough 8. Also it suggests to attach the .txt-file to a bug-report, but for now I would highly assume, that it is me, who does something wrong, and not the compiler it self. As I am using a crate, I also tend to compile with cargo. Even though my Cargo.toml
differs from the core-lib one, it is a generic one, so I think it doesn't have effect on the error:
[package]
name = "Tests"
version = "0.1.0"
edition = "2021"
[dependencies]
My attempts to solve it
This is a summary of what I tried while solving this error
- My first attempt was simply to copy all the code I would think of being related to the Add-trait. That consisted of all the macros, attributes and basically the whole lib.rs file from core-lib as it potentially had features fixing the problem.
- I've updated the nightly compiler with rustup twice (I get this error for about a week), as it would be possible, that this would be magically fixed by an update.
- I have tried googling for this or an related error, but it seems (understandably) to be a very niche problem, so I could not find much useful resources except for the referenced blog-post and a few also very old post on different platforms.
- After that I tried to read the corresponding code in the Rust-compiler with hope to get a hint what I can do to fix it, but it turned out, that I am not able to do this on my own in a meaning-full time, as I don't have any experience with the compiler-code itself.
- Because I saw some information, that the compiler could slightly change it's behavior when some compiler-arguments are passed, I have tried to pass different optimization-flags, but it didn't change anything.
- I've tried to build the whole compiler (it of course worked), then build manually the core-library in
rust/library/core
(it also worked) after what I started reduce the official core-lib to the minimal Add-example - at least that was my plan. But because the core-lib is very intertwined, I would have to fix well over 1k of errors like 'could not found' or 'is not implemented'. So it would take a lot of time for me to everything and would not guarantee a result, so I decided not to go further this road.
Final
I really hope, that someone can point my mistake and help to solve this problem. As for implementing pointers I found out, that I really need the math-functionality for being able to implement functions like for example mem::swap
. While I already used other traits like core::ptr::Pointee
and core::ops::Receiver
, the math-functionality is really challenging me, but by solving the Add-trait I hope I'll be able to fix other math-traits too. If some other information is needed, I'll be happy to provide it. Also I would like to thank everyone in advance for trying to help me!