As it stands, the backend is incomplete, and there is no active contributor,
making it dead weight.
However, anyone is free to resurrect this backend at any time.
Just like when new parse errors occur during an update, when new AstGen
errors occur during an update, we do not reveal compile errors for Decl
objects which are inside of a newly failed File. Once the File passes
AstGen successfully, it will be compared with the previously succeeded
ZIR and the saved Decl compile errors will be handled properly.
* Do not report export collision errors until the very end, because it
is possible, during an update, for a new export to be added before an
old one is semantically analyzed to be deleted. In such a case there
should be no compile error.
- Likewise we defer emitting exports until the end when we know for
sure what will happen.
* Sema: Fix not adding a Decl dependency on imported files.
* Sema: Properly add Decl dependencies for all identifier and namespace
lookups.
* After semantic analysis for a Decl, if it is still marked as
`in_progress`, change it to `dependency_failure` because if the Decl
itself failed, it would have already been changed during the call to
add the compile error.
* Remove the ability for GenZir parent Scope to be null. Now there is a
Top Scope at the top.
* Introduce Scope.Namespace to contain a table of decl names in order
to emit a compile error for name conflicts.
* Fix use of invalid memory when reporting compile errors by
duplicating decl names into a temporary heap allocated buffer.
* Fix memory leak in while and for loops, not cleaning up their
labeled_breaks and store_to_block_ptr_list arrays.
* Fix stage2 test cases because now the source location of redundant
comptime keyword compile errors is improved.
* Implement compile error for local variable shadowing declaration.
Conflicts:
* lib/std/os/linux.zig
* lib/std/os/windows/bits.zig
* src/Module.zig
* src/Sema.zig
* test/stage2/test.zig
Mainly I wanted Jakub's new macOS code for respecting stack size, since
we now depend on it for debug builds able to pass one of the test cases
for recursive comptime function calls with `@setEvalBranchQuota`.
The conflicts were all trivial.
* Compilation: iteration over the deletion_set only tries to delete the
first one, relying on Decl destroy to remove itself from the deletion
set.
* link: `freeDecl` now has to handle the possibility of freeing a Decl
that was never called with `allocateDeclIndexes`.
* `deleteDecl` recursively iterates over a Decl's Namespace sub-Decl
objects and calls `deleteDecl` on them.
- Prevents Decl objects from being destroyed when they are still in
`deletion_set`.
* Sema: fix cleanup of anonymous Decl objects when an error occurs
during semantic analysis.
* tests: update test cases for fully qualified names
Decl objects need to know whether they are the owner of the Type/Value
associated with them, in order to decide whether to destroy the
associated Namespace, Fn, or Var when cleaning up.
* Sema: implement global variables
- Improved global constants to stop needlessly creating a Var
structure; they can just store the value directly.
- This required making memory management a bit more sophisticated to
detect when a Decl owns the Namespace associated with it, for the
purposes of deinitialization.
* Decl.name and Namespace decl table keys no longer directly
reference ZIR; instead they have heap-duped names, so that deleted
decls, which no longer have any ZIR to reference for their names, can
be removed from the parent Namespace table.
- In the future I would like to explore going a different direction
with this, where the strings would still point to the ZIR however
they would be removed from their owner Namespace objects during the
update detection. The design principle here is that the existence
of incremental compilation as a feature should not incur any cost
for the use case when it is not used. In this example Decl names
could simply point to ZIR string table memory, and it is only
because of incremental compilation that we duplicate their names.
* AstGen: implement threadlocal variables
* CLI: call cleanExit after building a compilation so that in release
modes we don't bother freeing memory or closing file descriptors,
allowing the OS to do it more efficiently.
* Avoid calling `freeDecl` in the linker for unreferenced Decl objects.
* Fix CBE test case expecting the compile error to point to the wrong
column.
This was also an experiment to see if it were easier to implement a new
feature when using the instruction encoder.
Verdict: It's not that much easier, but I think it's certainly much more
readable, because the description of the Instruction annotates what each
field means. Right now, precise knowledge of x86_64 instructions is
still required because things like when to set the 64-bit flag, how to
read x86_64 instruction references, etc. are still not automatically
done for you.
In the future, this interface might make it sligtly easier to write an
assembler for x86_64, by abstracting the bit-fiddling aspects of
instruction encoding.
From my very cursory reading, it seems that the register manager doesn't
distinguish between registers that are physically the same but have
different sizes.
In that case, this means that during codegen, we can't rely on
`reg.size()` when determining the width of the operations we have to
perform. Instead, we must use some form of `ty.abiSize(self.target.*)`
to determine the size of the type we're operating with. If this size is
64 bits, then we should enable 64-bit operation.
This fixed a bug in the codegen for spilling instructions, which was
overwriting the previous stack entry with zeroes. See the modified test
case in this commit.
Instead of Module setting up the root_scope with the root source file,
instead, Module relies on the package table graph being set up properly,
and inside `update()`, it does the equivalent of `_ = @import("std");`.
This, in term, imports start.zig, which has the logic to call main (or
not). `Module` no longer has `root_scope` - the root source file is no
longer special, it's just in the package table mapped to "root".
I also went ahead and implemented proper detection of updated files.
mtime, inode, size, and source hash are kept in `Scope.File`.
During an update, iterate over `import_table` and stat each file to find
out which ones are updated.
The source hash is redundant with the source hash used by the struct
decl that corresponds to the file, so it should be removed in a future
commit before merging the branch.
* AstGen: add "previously declared here" notes for variables shadowing
decls.
* Parse imports as structs. Module now calls `AstGen.structDeclInner`,
which is called by `AstGen.containerDecl`.
- `importFile` is a bit kludgy with how it handles the top level Decl
that kinda gets merged into the struct decl at the end of the
function. Be on the look out for bugs related to that as well as
possibly cleaner ways to implement this.
* Module: factor out lookupDeclName into lookupIdentifier and lookupNa
* Rename `Scope.Container` to `Scope.Namespace`.
* Delete some dead code.
This branch won't work until `usingnamespace` is implemented because it
relies on `@import("builtin").OutputMode` and `OutputMode` comes from a
`usingnamespace`.
There were several problems, all fixed:
* AstGen was storing field names as references to the original
source code bytes. However, that data would be destroyed when the
source file is updated. Now, it correctly stores the field names in
the Decl arena for the enum. The same fix applies to error set field
names.
* Sema was missing a memset inside `analyzeSwitch`, leaving the "seen
enum fields" array with undefined memory. Now that they are all
properly set to null, the validation works.
* Moved the "enum declared here" note to the end. It looked weird
interrupting the notes for which enum values were missing.
Before, incremental compilation would crash when trying to emit compile
errors for the update after introducing a parse error.
Parse errors are handled by not invalidating any existing semantic
analysis. However, only the parse error must be reported, with all the
other errors suppressed. Once the parse error is fixed, the new file can
be treated as an update to the previously-succeeded update.
* `analyzeContainer` now has an `outdated_decls` set as well as
`deleted_decls`. Instead of queuing up outdated Decls for re-analysis
right away, they are added to this new set. When processing the
`deleted_decls` set, we remove deleted Decls from the
`outdated_decls` set, to avoid deleted Decl pointers from being in
the work_queue. Only after processing the deleted decls do we add
analyze_decl work items to the queue.
* Module.deletion_set is now an `AutoArrayHashMap` rather than `ArrayList`.
`declareDeclDependency` will now remove a Decl from it as appropriate.
When processing the `deletion_set` in `Compilation.performAllTheWork`,
it now assumes all Decl in the set are to be deleted.
* Fix crash when handling parse errors. Currently we unload the
`ast.Tree` if any parse errors occur. Previously the code emitted a
LazySrcLoc pointing to a token index, but then when we try to resolve
the token index to a byte offset to create a compile error message,
the ast.Tree` would be unloaded. Now we use
`LazySrcLoc.byte_abs` instead of `token_abs` so the error message can
be created even with the `ast.Tree` unloaded.
Together, these changes solve a crash that happened with incremental
compilation when Decls were added and removed in some combinations.