GCC 13 and the State of Gccrs
rust-gcc.github.io
rust-gcc.github.io
1. Type inference taking into account higher ranked trait bounds
- Slices in libcore work via the Index lang item so its like taking an index operator overload to a range lang item but the generic types in the range need to be unified with the higher ranked trait bound to eventually figure out that they are meant to be of usize.
2. Method resolution its almost a joke at this point how complicated it is in Rust. - The autoderef cycle sounds simple when you read this: https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/first-edition/deref-coercions.html
- But this misses so much extra context information
3. Macro invocations there are really subtle rules on how you treat macro invocations such as this which is not documented at all https://github.com/Rust-GCC/gccrs/blob/master/gcc/rust/expan...</rant>
Some day I personally want to write a blog post about how complicated and under spec'd Rust is, then write one about the stuff i do like it such as iterators being part of libcore so i don't need reactive extensions.
Libcore just gets compiled like any other rust crate just it does not have access to abstractions.
For example, you can still write C code without libc because C is a language libc is just a library libcore is pretty much the same. Though Rust makes alot of assumptions that libcore _should_ be there but its possible for it not to be.
The way that rustc currently splits this up is an implementation detail of rustc, not something that must be copied exactly. Rust without core is not Rust. It's not even usable.
The same core/std distinction exists in C. The headers float.h, iso646.h, limits.h, stdalign.h, stdarg.h, stdbit.h, stdbool.h, stddef.h, stdint.h, stdnoreturn.h, and parts of string.h, stdlib.h, fenv.h, and math.h are required to be supported in freestanding mode.
And, frankly, just about every language has this kind of core library/standard library distinction; at some point, parts of the compiler implementation of the language need to work with the library implementation details, and vice versa. Languages like Rust and C are somewhat unusual in actually identifying a subset of the standard library that is usable without a complete implementation of the standard library.
I was like, Option isn't special but, well, you do need to provide Some and None, and those are clearly two halves of an enum, so - that's Option is what that is.
You need Try, and unless you're going to write Try yourself to have some other behaviour that means you're writing ControlFlow and Result as well as Option.
I think that the work needed to make Ipv4Addr::is_documentation - a predicate which tells you whether the IPv4 Address you've got is, in fact, one reserved for documentation by the IETF RFC 5737 - is tiny compared to the struggle to get u32::is_power_of_two - a predicate which tells you if a 32-bit integer is a power of two - and so even though doubtless Rust doesn't care whether the former part of the core library works you might just as well.
Now we're writing per-ISA intrinsics, what was our goal again? Maybe I was too oblique, this stuff is all rabbit holes is what I was getting at. We're lucky these people even re-surface periodically with work and a blog post.
By "3-year releases" you're probably meaning Editions, but Rust Editions aren't like the C++ Standard, where they're bundling together a bunch of new features† the Edition changes language syntax, usually in small ways. The features people care about are released every six weeks and aren't saved up for new Editions.
For example, 1.69 released last week, so now: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080)
... is a constant, at compile time the data structure equivalent to 127.0.0.1:8080 is constructed and baked into your binary program.
If I write a Rust program which relies on this for a constant, it works fine, because it is constant, but if I try to compile it with Rust 1.68 (from earlier this year) it won't work. If an alternative compiler doesn't have the features of Rust 1.69, then code written for Rust 1.69 won't necessarily work with it. Sometimes such differences will be tiny, other times huge.
† And notice that just because say, Modules was a C++ 20 feature, did not mean that you woke up one day in 2020 and had working Modules, C++ 20 is just an ISO document, the features have to get implemented by vendors, and then tested, and then shipped, which can take weeks, or months, or years.
> If I write a Rust program which relies on this for a constant, it works fine, because it is constant, but if I try to compile it with Rust 1.68 (from earlier this year) it won't work.
How would it not work? If your program compiles and runs with 1.69, then surely that's because it never tries to modify the value. But if your program never tries to modify the value, how would it fail with 1.68?
* Modulo soundness bugs being fixed. I've personally encountered 2 instances of that since 1.0, and they all occurred before 2017.
That's a bold claim for a tool that doesn't even have a Wikipedia entry.
Edit: or a subreddit.
I would implore you to open your mind up to resources that aren't popular social media because that's where the real gold/value is in development/engineering.
I'm not claiming to be "one of the absolutely most valuable and greatest tools available" though. (Well, maybe the "greatest tool" part ;-) And if someone else claimed that about me, I'd think that either there was something very wrong with them, or that one of my parents had started using alt accounts I didn't know about.
Considering some of the software tools that do have dedicated wikipedia pages and subreddits, and that I'd never heard of godbolt before, I don't think it's out of line to be skeptical of such a hyperbolic claim.
In this case, the fn `std::net::SocketAddr::new` was made a const fn recently--but was a regular fn before.
>that's because it never tries to modify the value
That's something different. If you want variables that are read-only, you don't need any extra keyword. It's the normal case:
let x = 5;
If you want a mutable variable, you add a `mut` keyword.
let mut x = 5;
But if you want a compile-time constant, you use `const` instead.
const x: u32 = 5;
The latter will be evaluated by the compiler at compile time (so interpreted) and substituted BEFORE the user runs the program. This is new-ish stuff and is slowly worming its way into the standard library.
When a language develops quickly it is a good if people use the latest version to test new features to get experience.
When a language become mainstream, change will be much slower. Rust compilers will be shipped with operating systems, and people will write code that can be compiled by those older compilers.
There is a lot of C code that targets C99. Because that's what you can rely on if it has to run everywhere.
Compiling the Firefox browser is often an exercise in futility because of this.
Rust might be the best but not being able to compile existing programs looks really bad.
I wouldn't assume that. JavaScript is about as mainstream as it gets, and that's also on a 6-weekly release schedule.
> Rust compilers will be shipped with operating systems, and people will write code that can be compiled by those older compilers.
Rust compilers are already shipped with operating systems, but for the most part (a few really foundational crates aside), people are not writing code that can be compiled by those older compilers. They're telling people to install a newer version of Rust. Which is pretty reasonable given how easy it is to manage rustc versions with rustup.
This may change with the creation of certified compilers for things like the automotive industry. But then, they're probably pretty used to maintaining their own library ecosystem anyway.
> There is a lot of C code that targets C99. Because that's what you can rely on if it has to run everywhere.
Yes, but one of the best things about Rust is that you can target the latest compiler version, and it still runs everywhere! That's why people don't like the possibility that this might change with the introduction of gcc-rs. We shouldn't accept crappy C toolchains as the standard.
Take for example reproducible builds. Obviously, you only get the same binary output if you use the same compiler version. In that context, it doesn't make sense to keep around every version ever released of the Rust compiler.
In particular, if operating systems are going to use Rust in their kernels or base systems, then it is very unlikely that they are just going to use rustup to get the latest compiler. They will carefully vet each compiler release to make sure that everything that worked in the past, still works.
Rust being a compiler means that for every processor architecture somebody has to create the back-end. This means that if the current LLVM-based rust compiler will remain the only usable Rust compiler, there will be a lot of places where Rust cannot be used (unless LLVM will be the only compiler back-end in the future).
As with Firefox, Linux (ab)uses the compiler's own environment hack to say "No, I want nightly features, I don't care that this invalidates my warranty" despite using a stable compiler.
The actual version used is, I think, currently 1.66 (so, about three months old).
Rust is so simple and clear :3
Of course if you just want to convert a string to a SocketAddr (returning an error at runtime if it's invalid), you just do `let addr = "127.0.0.1:8080".parse();`.
A separate Rust compiler may also uncover problems in the language or standard library design (e.g. if it's too much effort to write a Rust compiler from scratch that definitely isn't a good thing IMHO).
clang made gcc and g++ better compilers. gcc-rs can do the same and help making rustc a better compiler and Rust a better language.
For the record, I don't write Rust, because GCC has no Rust support yet. More specifically, I don't use LLVM-Specific languages due to LLVM's license and stance against GCC.
In other words, "If you want to go fast, go alone. If you want to go far, go together".
As far as I can tell, GCC is licensed under GPLv3 these days. LLVM is under a variation of the Apache License.
https://www.apache.org/licenses/GPL-compatibility.html suggests that you can use code under Apache License in a project that's under GPLv3.
So someone could take legally LLVM, change the Readme and slap a GPLv3 license on it, and have the same license as GCC? (Assuming that the exceptions that LLVM's license has aren't a problem.)
Would that fix your issues? Or what is your specific problem?
This whole exercise has a point, if and only if there's a point to insisting that GCC's license is fine, but LLVM's license ain't.
It's just a reduction. https://en.wikipedia.org/wiki/Reduction_(complexity)
If you don't have any problem with the license of LLVM in the first place, the reduction is obviously useless to you.
Can you name even one other compiler which would match those requirements? Only thing that comes to my mind is Borland, although I have no idea whether their compilers shared common backend or where they completely independent. I guess at some point Microsoft might have shared some of the parts for their .NET CLR langauges (Basic, C#, F# and managed C++/CLI ). While you could make a Rust frontend for .NET it would be somewhat pointless. Rust makes certain sacrifices in terms of ease of use, for the purpose of safe low level memory management. In a VM made for higher level garbage collected languages thats just unnecessarily complexity, without the benefits. After looking more I was also able to find thing called "Amsterdam compiler kit", but that seems more like academic exercise.
Why two separate projects -> Think of it as short term vs long term solutions. Ideally in long term the multiple implementations for programing language would be completely independent, but it takes more work. In short term it's easier to glue together existing Rust frontend with GCC backend, while still getting many of the benefits of having multiple programming language implementations.
It is not that simple, there are pros and significant cons
2. The GCC developers are interested in supporting a broad set of languages that compile to native executables, and Rust is an increasingly popular language for systems programming.
3. Whether something is "necessary" or not has never been decisive in whether or not it happens. When presented with a programming challenge, why not try it and see what comes out?
It's more insane to me that one wants the same tool to do everything rather than have different tools for different tasks.
Like should Perl be compiled with GCC just to satisfy that requirement because it's used during the kernel build? That's ridiculous!
PS. GCC stands for “GNU Compiler Collection”. All of the above is already there.
I don't think you should expect or want that.
m68k is already supported by LLVM and Rust supports it as a tier-3 target platform.
Xtensa got upstream support, but so many embedd vendors seem to want to double dip and sell build tools, so they don't upstream their llvm based build tools. Shortsighted imo. Vendors that don't charge for build tools will make it up in sales volume.
It's unclear to me what negatives a new, independent, Free implementation brings.
On the other hand, the positives of it seem clear: an independent, Free implementation helps establish Rust the language and differentiate the language from a specific implementation of it.
Rust have too many underspecified behavior, and the only source of truth is the official implementation.
Everybody know the type system in rust is NP-complete. The compiler use different heuristics and hints to do a best effort solution. I can't imagine how this can be reimplemented
gccrs on the other hand, aims to be a full implementation with all those checkers.
More specifically they're aiming to steal the next gen borrow checker, not the current one
C++ templates are Turing-complete, and GCC implements them just fine.
The idea that Rust requires a formal specification (outside very rare situations, like aeronautic firmware) is silly.
GCC supports various "flavors" of C, including fully standard compliant versions.
GNU extensions are all documented.
That is also the situation Rust currently is in. There is no Rust spec, only a reference manual for rustc.
People who complain about unspecified behavior in Rust (or, equally, about Rust's lack of a language spec) are not satisfied with mere documentation. They want something like the Java language specification and conformance test suite.
The underspecified part should not be a deal breaker, even though it's certainly a difficulty. But it will also be an opportunity to spot suboptimal implementation choices in rustc. Exposing these will improve the language as a whole.
The NP-complete problem is optimal register allocation (through graph coloring). Register allocation in itself is not NP-complete. You can always use a suboptimal but fast algorithm because optimizations are optional. On the other hand, type checking is not optional, so having to solve a NP-complete problem for that would indeed be problematic.
In the PR dug out by a sibling commenter, the improvement to the compiler was indeed to apply the workaround that the programmer had to do. I believe that the churn of new heuristics is going to become smaller, as presumably most of the low-hanging fruits have been harvested by now.
Not necessarily. There are plenty of NP-complete problems that can be solved optimally fast enough to be useful for the instances that actually come up in real world applications.
For example the Traveling Salesman Problem (TSP) is NP-complete, but there are exact solvers that run in reasonable time for instances of a few hundred vertices. That's fine if you are say a delivery company trying to plan the day's itinerary for one of your delivery trucks.
but... we don't want same sourcecode compile with one compiler implementation, but not the other... right?
When I say "under-specified", I meant something like this:
https://github.com/rust-lang/rust/pull/105300/
quote> I claim it's more correct as well because it fixes #104639.
There is no spec. Just a bunch of implementation-defined behavior. Without a spec, this would be a forever catching up game. It is not easy to come up with a spec -- the implement is filled with heuristics because the intrinsic NP-completeness.
It's not ideal, but I can live with it so long as when I write a program which compiles it has the desired behaviour. That's the problem in C++ and to a lesser extent C which ultimately falls out of this same constraint. In C++ it's easy to write programs which compiler A and compiler B will both compile but they have different results and neither of them is wrong. I have no use for this whatsoever.
If some Rust compilers say my program is unacceptable, and others build it correctly, obviously that's not brilliant news but I can choose whether to fix the program to be acceptable to more compilers, or not worry - the results at least are correct.
Implementations accepting different sets of inputs is certainly annoying, but acceptable as long as they are erring on the side of caution. We definitely don't want code to compile which is actually not type-safe. At a quick glance, the PR can be understood as automating a workaround that programmers had to do by themselves before.
> This PR (similarly, and building on top of #104765) allows RPITs and async fns to untangle their lifetime bounds to figure out the set of lifetimes that actually get caputed instead of falling back to a larger one or even 'static. For most cases it was was already possible for users to manually reorder their lifetimes to make their code compile, this PR just makes that automatic, as the order of generic params and bounds should not matter.
I once read a text about someone implementing C++ before it was standardized. Whenever something wasn't clear, he tried it on CFront. CFront would generally crash. That's a lot worse than rust today, and there are a lot of C++ compilers nevertheless.
For the NP-complete problem, here is how Java solved this: The compiler has to validate that every function's execution ends with a valid return statement. This is literally the halting problem, impossible for the compiler to solve.
As a way out of this conundrum, the java standard dictates the algorithm. It is conservative, so it rejects some valid programs. But it is part of the standard, so if one java compiler accepts a program, you know all the other ones will too. So all java compilers make the same mistakes, as required by the java standard. A compiler is simply not allowed to implement a better algorithm.
While it is a huge effort, gccrs will probably succeed, and help clarify the rust standard in the process. Real risks are more long term: gcc java slowly petered out. For rust, things seem better, as gccrs has a stable funding source.
Update: Java's problem/algorithm is not (only) the return statement. It is the requirement that every variable is defined before being used. You find it as 'definite assignement' in the compiler standard.
It is not the halting problem though. The compiler does not have to prove the return is ever reached. Just that the instruction pointer does not fall off the end of the function. Just check the branch targets one by one?
See for some interesting cases the examples at the start of https://docs.oracle.com/javase/specs/jls/se17/html/jls-16.ht...
Update: come to think of it, it probably is the halting problem simply for returning:
int somefunc(){
if(this_returns_true()){
throw Something();
}else{
// No throw here
}
// Do I need return 1 here?
}
The famous Sufficiently Smart Compiler can decide the function always throws, and does not need a return statement. Let this_returns_true depend on deciding the halting problem, and there you are. Clearly, this level of analysis is out of reach for javac. The standard in this case just claims the then and else side can both be executed, so the return is required. If the else had a second throw, the compiler would not need the return.