I am learning about compilers for self-improvement. Best way I think is writing my own very basic and primitive compiler in Swift.
I read Crenshaw‘s tutorial a few years ago and now I am following along Nystrom’s website.
I am following the tokenizer-parser-interpreter (tokens - abstract syntax tree - visitor pattern) route. That’s the easiest route from what I read thus far.
I want to add static typing though which immediately lead to needing to store type information together with the value. That lead to typechecking in a comparison operator (double < string) and thus to types knowing to which protocols they adhere.
Now I think I “understand“ why a Double in Swift is a struct. A least it makes more sense than before.
So I went looking in the Swift repository for Double (and Integer). I found String, Dictionary, and also a bunch of howto’s, docs and guides some of which I read. Though to be honest that last part seems kind of a mess and outdated(?).
That’s pretty much all I know about compilers. Unfortunately my progress is limited by time constraints. Hopefully nobody will mind too much me asking basic questions once in a while. I promise to leave out a long introduction next time.
My question now is: where are the Double and Integer source files. Could somebody point them out or show me a way to efficiently find them.
Both Int and Double are defined in source files that end up generating similar implementations for many types (Int → Int8, Int16, ...; Double → Float, Float80, ...):
Because these are source files intended to be used to generate other files, they can be a little difficult to read through (and search for).
The gyb tool used to expand these template files can be found at swift/utils/gyb.py, alongside utility definitions like swift/utils/SwiftIntTypes.py which are imported by the above gyb files.
I went through the FloatingPointTypes.swift.gyb file. The explanation on NSHipster by Matt was helpful. After reading that the file wasn’t hard to parse.
So a Double in Swift is really just like any other struct! Any swift developer could have written it bar for the use of builtin.FPIEEE and similar functions - and knowhow about floating point standards of course.
Where do these builtin functions come from (and how deep down the rabbit hole am I going here)? At some point I will end up at assembly I guess.
As to the original question: since I thought of Double as a primitive type in Swift, I expected/thought Swift was like a c/c++ struct because it needed to keep track of which protocols it implements. The struct being the “true” c++ double primitive + whatever storage for tracking protocols. So no, hypthosis wrong. I don’t understand it. Nice
I have to look at the metatype of Double, right? To figure out how a type tracks its protocols. But that implementation would be the same for all types. Why bother making one separately for Double.
The Builtin module is provided by the Swift compiler itself (it is the Swift source representation of the SIL and IR types, instructions and intrinsics). E.g. the LLVM fadd instruction is exposed in Swift¹ as Builtin.fadd_<type>. You can find some of the machinery that handles this in include/swift/AST/Builtins.def and the compiler sources that include it.
¹ When the -parse-stdlib flag is passed to swiftc; non-standard-library code generally should not be doing this.
This sort of "the stdlib isn't magic" thing is a nice guiding principle for the design of Swift. It helps make sure that the functionality we're exposing to developers is actually flexible enough to do whatever they might need, as well as making working on the stdlib itself more approachable.
For better and worse there are some significant deviations from this principle we haven't been able to avoid (@_transparent, @semantics, Builtin.*, "preferred" types for literals, etc…), but I'm still glad it's there.