I think a standard is what you need when you have a complete mess in the first place. That said there are tricky evolving/unstable areas in Rust where it isn't completely clear (to me at least) where the language is going, but that has few practical consequences ( just stay away from those areas ).
Yes, precisely. And very often writing tests is actually harder than writing both code and specifications. Just last year clang 22 broke our production code and the investigation have shown that it was
ing because someone suggested, on review, to remove piece of code that wasn't covered by tests… they even left behind helpful comment that was saying that they couldn't think about how to test that case thus this code is probably dead. Well… our attempt to bump the revision version quickly gave them that much-coveted test and they fixed the code, but this shows that these days even humans often demand tests when they are not trivial to write… LLMs are entirely hopeless without test coverage!
Now imagine writing compiler (or interpreter) for a new language… how would you write tests if language doesn't even exist? And how would use the tool that couldn't write 1000 lines of code correctly without tests? You couldn't. You either would need to spend so much time creating all these tests that time savings from the use of LLMs would be negative or, alternatively, you have to write some implementation manually… and then language that you use would matter and you couldn't say “just write the spec and let LLM do the rest”.
I would say that C++ standards committee did a fantastic job, given the circumstances. Yes, in some cases they botched things pretty heavily (here is one pretty well-known example), but you have to remember that they work in precisely the environment that LLMs fail totally: spec exist as the paper (PDF or HTML), there are some discussion, and zero tests that you may actually run.
Doing good decision in that position is hard, the fact that humans can actually do that, even if with some rare problems shows why we are years (if not decades) away from “give AI the spec, take the working compiler, from the other side”. And if we not close yet to that point then we may discuss other things.
Sure, if you can keep the subject relevant to the thread like that, but as much as I might agree with you, the financials of AI vendors are straying pretty far afield.
More on topic, I suspect that if a spec alone could create a meaningfully compatible implementation one-shot (eg it's a novel language that's not in the training set and not a simple transliteration of one, and assuming we're comparing different models just to be sure it's not just coming up with the same solutions), then that spec is pretty likely defined in the more modern style of operational semantics like Ecmascript, where it's essentially just a weirdly written reference implementation.
I do strongly prefer that style, but I can only imagine just how much work it is...
That one is actually easy, these days: just write an implementation with tests and ask LLM to write spec from that.
Yes, LLMs are not perfect when you ask them to do that, too – but in my experience doing things in that order is much more reliable that hoping that LLM would learn to program.
You still need some handholding here and there, maybe some comments… but much less then if you would try to go from spec to the code.
I wrote a compiler for Coco Programming Language (https://cocolang.dev) in pure Rust. It's a very good language for writing compilers, don't believe what haters say. But write specs for your language, AI skills will love it.
Please stay on topic everyone. It is neither useful to keep baiting the LLM haters, nor useful to keep telling ppl not to use LLMs when that is clearly not the topic here. Since that seems to be hard, all mentions of LLMs in this topic are now disallowed.
While others tried to defuse before, now that I have to be involved: further such behaviour gets you timeouts with no further warning.
As far as I know, one reason to write your compiler in a standardized language like C is easy interoperability. Writing the compiler in C allows you to also expose some sort of C API which could be used as a bridge between your language and the C language.
If, however, you write the compiler in Rust, interoperability will be rather easier with Rust itself than with C.
i'm not sure how the language a compiler is built in affects the ability of the language it compiles to interop with other languages,
for example:
-
A language with the only compiler written in unspecified-obscure-language-with-no-c-interop that transpiles the language to C will have little issue with C interop,
-
A language that doesn't have pointers and uses only ternary numbers with a compiler written directly in C, but that compiles the code directly to assembly with a non-standard calling convention and a custom runtime (yes, C has a runtime) will have issues with C-Interop
what happens is not what the compiler is written in, but what and how the compiler outputs and the rules of the language being compiled.
the rust compiler could be rewritten in 3-lisp and it wouldn't change how well rust can interop with C, you could do the same thing to the C compiler.
When I said that, I was thinking about the "compiler" as in the language's runtime or virtual machine. I did not even think of transpiling to C.
So I think this depends a lot on what kind of programming language is this, which currently is written in Rust.
What you presented earlier were two extremes, honestly, as I understand it. ![]()
as in the language's runtime or virtual machine.
oh, for interpreted/jit-compiled (unless everything is JIT compiled at once before the program languages with no possibility of interpretation, then you have a compiler again)
languages the language the interpreter is written in impacts interop a lot,
usually interpreters are not referred to as compilers (although there are edge cases like java where the language is both interpreted and compiled (to a virtual machine bytecode the interpreter reads), so it has both a interopreter and compiler, which may or may not be different programs)
I agree but that's a smaller part of bigger problem it gets you where you want , we have advanced coding capabilities which can help reinforced fixing it !
I would be very surprised if most compilers were self-hosted. I think the opposite is true. Only a few (well-known) compilers for general-purpose languages (C/C++, Java, C#, Haskell, etc.) are self-hosted.
The majority of compilers are for special targets, for example small microcontrollers, or compilers for special languages in specific fields. For example, virtually the whole industrial automation industry depends on languages and compilers that most readers here have never heard of. The same is true for hardware description compilers (VHDL, Verilog), shader compilers, protocol compilers, and even regex compilers, etc.
Having a standard is completely independent of whether or not it specifies any interoperability. Keeping with your own example, I'd say that the opposite is the case. Any true interop with C depends on ABI which the specification rather infamously says nothing about despite considering it highly in its decision making. Most interesting interfaces are rather implementation-defined meaning you'll need not 'the C specification' but rather each individual compiler implementation of your target and hope that these each provide you with a well-defined definition of types / functions, layouts, and then you'll need to somehow dispatch depending on that which may or may not require you to run that C-compiler as a subprogram. Sys-V is maybe the most sensible collection of targets that at least tries to fix this but it's hardly to the credit of C or its having a specification.
Writing something in C locks you into C much stronger than writing anything in Rust, which specifies at least the memory representation of its integer types and float to be IEEE as well as multiple target-independent layout algorithms that you can selectively apply to types that should be interoperable: repr(packed), repr(transparent) and one misnomered repr(C) for aggregate types, plus more ideas for changing that.
I feel that rustc is actually cheating by calling itself a compiler. It really just transpiles rust into LLVM-IR and passes that to LLVM to compile into binary.
NOTE : I don't write compilers so my opinion is probably meaningless.
That is the modern architecture of modern compiler
Clang (C, C++, Objective C), Swift, Haskell, Odin, Zig, Julia, Mojo, Fortan, Ada, etc
I created transpiler toy project to transpile Typescript to Rust. Transpiler does not perform type checking, borrow checking (in Rust case). It is pure translation, pure string convertion, no complex analysis, no panic stack unwinding, no debug symbol. All I did is just pure if user write "function", translate it to "fn". The type check, runtime, etc happen in the target language. It is what I call transpiler. But rustc does much more than it, it does all the thing that Clang, Swift, Haskell, etc other language compiler that uses LLVM that I listed above. It is just the modern architecture of compiler, to not reinvent the wheel and can focus on what makes the language offer something different that solves problem. About optimization, rustc has some optimizations on its own called Rust MIR optimization, to optimize thing that can be optimized before continue to LLVM, where some of the compilers I listed above does not has this like Clang (it is in progress to add this intermediate optimization named ClangIR)
"What is a 'real' compiler" is a philosophical question that's spawned a million arguments for decades. Historically looking, the first compilers were themselves very trivial by our standards of course; barely anyone you could call type checking, leaving all name resolution to the linker, and pretty much only turning "begin" and "end" into the appropriate jumps.
On the other end, languages like typescript as a transformation is barely more than a regex, but the checking it does requires far more analysis to form a structural understanding than any language you've ever used, including Rust arguably (there's different areas of complexity that are hard to compare directly).
LLVM as a compilation target can be viewed as the compiler being for the virtual machine that LLVM describes (despite it's disclaimer that it's not a VM, it does actually provide an interpreter), in which case there's really no distinction to a direct compiler.
The closest I can come up with to a definition for a compiler is that it serializes the logic, but it's very hard to nail even that down to anything specific: certainly a linker doesn't count, but they do a lot of non-linear tracking of definitions and uses that is a bit tricky to exclude; and of course LLVM does a huge amount of complex analysis. Surely it's not as simple as "creating a syntax tree", since plenty of obvious compilers can skip that directly to a more flattened form.
If it makes you feel better, there's a from-scratch backend called Cranelift being worked on that should become the default debug backend at some point.
Rustc (the official Rust compiler) does not produce machine code directly. Internally, it acts as a front-end that:
- Parses your Rust code
- Borrow-checks it (the unique Rust safety feature)
- Lowers it to LLVM Intermediate Representation (IR)
- Hands that IR to LLVM, which then optimizes it and generates native machine code.
So in the strictest sense, rustc is a translator to LLVM IR, and LLVM is the actual "complete compiler" backend.
However, here's the nuance:
· Rust can use alternative backends (like Cranelift for faster debug builds, or GCC via rustc_codegen_gcc), so it's not permanently tied to LLVM.
· In common language, we still call rustc a "compiler" because it handles the entire pipeline—from source to executable—even if it delegates the final machine-code generation to another tool.
So: Rustc is a compiler that uses LLVM as its backend.