aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
AgeCommit message (Collapse)Author
2025-05-31Legalize: implement scalarization of binary operationsJacob Young
2025-05-29Legalize: introduce a new pass before livenessJacob Young
Each target can opt into different sets of legalize features. By performing these transformations before liveness, instructions that become unreferenced will have up-to-date liveness information.
2025-05-28x86_64: implement integer `@reduce(.Max)`Jacob Young
2025-05-28x86_64: implement integer `@reduce(.Min)`Jacob Young
2025-05-28x86_64: implement optimized float `@reduce(.Mul)`Jacob Young
2025-05-28Sema: don't emit AIR `reduce` for single-element vectorsmlugg
This is equivalent to `array_elem_val`, and doing that conversion in Sema (seems reasonable since it's just a simple branch) is much easier for the self-hosted x86_64 backend then actually handling this case.
2025-05-28x86_64: rewrite bitwise `@reduce`Jacob Young
2025-05-27compiler: tlv pointers are not comptime-knownmlugg
Pointers to thread-local variables do not have their addresses known until runtime, so it is nonsensical for them to be comptime-known. There was logic in the compiler which was essentially attempting to treat them as not being comptime-known despite the pointer being an interned value. This was a bit of a mess, the check was frequent enough to actually show up in compiler profiles, and it was very awkward for backends to deal with, because they had to grapple with the fact that a "constant" they were lowering might actually require runtime operations. So, instead, do not consider these pointers to be comptime-known in *any* way. Never intern such a pointer; instead, when the address of a threadlocal is taken, emit an AIR instruction which computes the pointer at runtime. This avoids lots of special handling for TLVs across basically all codegen backends; of all somewhat-functional backends, the only one which wasn't improved by this change was the LLVM backend, because LLVM pretends this complexity around threadlocals doesn't exist. This change simplifies Sema and codegen, avoids a potential source of bugs, and potentially improves Sema performance very slightly by avoiding a non-trivial check on a hot path.
2025-05-27Sema: simplify comptime `@intFromPtr` logicDavid Rubin
2025-05-25compiler: introduce incremental debug servermlugg
In a compiler built with debug extensions, pass `--debug-incremental` to spawn the "incremental debug server". This is a TCP server exposing a REPL which allows querying a bunch of compiler state, some of which is stored only when that flag is passed. Eventually, this will probably move into `std.zig.Server`/`std.zig.Client`, but this is easier to work with right now. The easiest way to interact with the server is `telnet`.
2025-05-20Merge pull request #23836 from mlugg/incr-fixesMatthew Lugg
Incremental fixes, refactor `Zcu.File`
2025-05-19Sema: allow `@ptrCast` single-item pointer to slicemlugg
Also, rework this logic a little to make it simpler. The length of the result slice is now computed in one place.
2025-05-19Sema: rewrite `analyzeMinMax`mlugg
I only wanted to fix a bug originally, but this logic was kind of a rat's nest. But now... okay, it still *is*, but it's now a slightly more navigable nest, with cute little signs occasionally, painted by adorable rats desparately trying to follow the specification. Hopefully #3806 comes along at some point to simplify this logic a little. Resolves: #23139
2025-05-18compiler: refactor `Zcu.File` and path representationmlugg
This commit makes some big changes to how we track state for Zig source files. In particular, it changes: * How `File` tracks its path on-disk * How AstGen discovers files * How file-level errors are tracked * How `builtin.zig` files and modules are created The original motivation here was to address incremental compilation bugs with the handling of files, such as #22696. To fix this, a few changes are necessary. Just like declarations may become unreferenced on an incremental update, meaning we suppress analysis errors associated with them, it is also possible for all imports of a file to be removed on an incremental update, in which case file-level errors for that file should be suppressed. As such, after AstGen, the compiler must traverse files (starting from analysis roots) and discover the set of "live files" for this update. Additionally, the compiler's previous handling of retryable file errors was not very good; the source location the error was reported as was based only on the first discovered import of that file. This source location also disappeared on future incremental updates. So, as a part of the file traversal above, we also need to figure out the source locations of imports which errors should be reported against. Another observation I made is that the "file exists in multiple modules" error was not implemented in a particularly good way (I get to say that because I wrote it!). It was subject to races, where the order in which different imports of a file were discovered affects both how errors are printed, and which module the file is arbitrarily assigned, with the latter in turn affecting which other files are considered for import. The thing I realised here is that while the AstGen worker pool is running, we cannot know for sure which module(s) a file is in; we could always discover an import later which changes the answer. So, here's how the AstGen workers have changed. We initially ensure that `zcu.import_table` contains the root files for all modules in this Zcu, even if we don't know any imports for them yet. Then, the AstGen workers do not need to be aware of modules. Instead, they simply ignore module imports, and only spin off more workers when they see a by-path import. During AstGen, we can't use module-root-relative paths, since we don't know which modules files are in; but we don't want to unnecessarily use absolute files either, because those are non-portable and can make `error.NameTooLong` more likely. As such, I have introduced a new abstraction, `Compilation.Path`. This type is a way of representing a filesystem path which has a *canonical form*. The path is represented relative to one of a few special directories: the lib directory, the global cache directory, or the local cache directory. As a fallback, we use absolute (or cwd-relative on WASI) paths. This is kind of similar to `std.Build.Cache.Path` with a pre-defined list of possible `std.Build.Cache.Directory`, but has stricter canonicalization rules based on path resolution to make sure deduplicating files works properly. A `Compilation.Path` can be trivially converted to a `std.Build.Cache.Path` from a `Compilation`, but is smaller, has a canonical form, and has a digest which will be consistent across different compiler processes with the same lib and cache directories (important when we serialize incremental compilation state in the future). `Zcu.File` and `Zcu.EmbedFile` both contain a `Compilation.Path`, which is used to access the file on-disk; module-relative sub paths are used quite rarely (`EmbedFile` doesn't even have one now for simplicity). After the AstGen workers all complete, we know that any file which might be imported is definitely in `import_table` and up-to-date. So, we perform a single-threaded graph traversal; similar to what `resolveReferences` plays for `AnalUnit`s, but for files instead. We figure out which files are alive, and which module each file is in. If a file turns out to be in multiple modules, we set a field on `Zcu` to indicate this error. If a file is in a different module to a prior update, we set a flag instructing `updateZirRefs` to invalidate all dependencies on the file. This traversal also discovers "import errors"; these are errors associated with a specific `@import`. With Zig's current design, there is only one possible error here: "import outside of module root". This must be identified during this traversal instead of during AstGen, because it depends on which module the file is in. I tried also representing "module not found" errors in this same way, but it turns out to be much more useful to report those in Sema, because of use cases like optional dependencies where a module import is behind a comptime-known build option. For simplicity, `failed_files` now just maps to `?[]u8`, since the source location is always the whole file. In fact, this allows removing `LazySrcLoc.Offset.entire_file` completely, slightly simplifying some error reporting logic. File-level errors are now directly built in the `std.zig.ErrorBundle.Wip`. If the payload is not `null`, it is the message for a retryable error (i.e. an error loading the source file), and will be reported with a "file imported here" note pointing to the import site discovered during the single-threaded file traversal. The last piece of fallout here is how `Builtin` works. Rather than constructing "builtin" modules when creating `Package.Module`s, they are now constructed on-the-fly by `Zcu`. The map `Zcu.builtin_modules` maps from digests to `*Package.Module`s. These digests are abstract hashes of the `Builtin` value; i.e. all of the options which are placed into "builtin.zig". During the file traversal, we populate `builtin_modules` as needed, so that when we see this imports in Sema, we just grab the relevant entry from this map. This eliminates a bunch of awkward state tracking during construction of the module graph. It's also now clearer exactly what options the builtin module has, since previously it inherited some options arbitrarily from the first-created module with that "builtin" module! The user-visible effects of this commit are: * retryable file errors are now consistently reported against the whole file, with a note pointing to a live import of that file * some theoretical bugs where imports are wrongly considered distinct (when the import path moves out of the cwd and then back in) are fixed * some consistency issues with how file-level errors are reported are fixed; these errors will now always be printed in the same order regardless of how the AstGen pass assigns file indices * incremental updates do not print retryable file errors differently between updates or depending on file structure/contents * incremental updates support files changing modules * incremental updates support files becoming unreferenced Resolves: #22696
2025-05-17x86_64: rewrite `@splat`Jacob Young
2025-05-17x86_64: rewrite scalar `<<|`Jacob Young
Closes #23035
2025-05-17x86_64: rewrite vector `+|`Jacob Young
2025-05-16Sema: improve "called from here" notesmlugg
To an average user, it may be unclear why these notes are not just in the reference trace; that's because they are more important, because they are inline calls through which comptime values may propagate. There are now 3 possible wordings for this note: * "called at comptime here" * "called inline here" * "generic function instantiated here" An alternative could be these wordings: * "while analyzing comptime call here" * "while analyzing inline call here" * "while analyzing generic instantiation here" I'm not sure which is better -- but this commit is certainly better than status quo.
2025-05-16compiler: include inline calls in the reference tracemlugg
Inline calls which happened in the erroring `AnalUnit` still show as error notes, because they tend to make very important context (e.g. to see how comptime values propagate through them). However, "earlier" inline calls are still useful to see to understand how something is being referenced, so we should include them in the reference trace.
2025-05-16Compilation: fix reference trace behavior without `-freference-trace`mlugg
When `-freference-trace` is not passed, we want to show exactly one reference trace. Previously, we set the reference trace root in `Sema` iff there were no other failed analyses. However, this results in an arbitrary error being the one with the reference trace after error sorting. It is also incompatible with incremental compilation, where some errors might be unreferenced. Instead, set the field on all analysis errors, and decide in `Compilation.getAllErrorsAlloc` which reference trace[s] to actually show.
2025-05-03Merge pull request #23263 from mlugg/comptime-field-ptrMatthew Lugg
Sema: fix pointers to comptime fields of comptime-known aggregate pointers
2025-04-28Merge pull request #23708 from ziglang/memmove-followupsAndrew Kelley
`@memmove` followups
2025-04-28Sema: fix a few indexing bugsmlugg
* Indexing zero-bit types should not produce AIR indexing instructions * Getting a runtime-known element pointer from a many-pointer should check that the many-pointer is not comptime-only Resolves: #23405
2025-04-28sema: do checked cast when resolving aggregate sizedweiller
2025-04-27make `@memcpy` and `@memmove` share panic handlersAndrew Kelley
2025-04-28Sema: fix memcpy with C pointersxdBronch
2025-04-28Sema: fix alignment of runtime field pointer of underaligned tuplemlugg
2025-04-28Sema: fix pointers to comptime fields of comptime-known aggregate pointersmlugg
Resolves: #23190
2025-04-27Sema: Fix some ptr alignment checks to handle a potential ISA tag bit.Alex Rønne Petersen
Closes #23570.
2025-04-26compiler: add @memmove builtindweiller
2025-04-20compiler: integrate `@compileLog` with incremental compilationmlugg
Compile log output is now separated based on the `AnalUnit` which perfomred the `@compileLog` call, so that we can omit the output for unreferenced ("dead") units. The units are also sorted when collecting the `ErrorBundle`, so that compile logs are always printed in a consistent order, like compile errors are. This is important not only for incremental compilation, but also for parallel analysis. Resolves: #23609
2025-04-10AstGen: redistribute inline asm limitsJacob Young
2025-04-04Sema: Prevent tail calls of std.builtin.returnError().Alex Rønne Petersen
LLVM 20 started tail-calling it in some of our test cases, resulting in: error: AndMyCarIsOutOfGas /home/alexrp/Source/ziglang/zig-llvm20/repro.zig:2:5: 0x103ef9d in main (repro) return error.TheSkyIsFalling; ^ /home/alexrp/Source/ziglang/zig-llvm20/repro.zig:6:5: 0x103efa5 in main (repro) return error.AndMyCarIsOutOfGas; ^ /home/alexrp/Source/ziglang/zig-llvm20/lib/std/start.zig:656:37: 0x103ee83 in posixCallMainAndExit (repro) const result = root.main() catch |err| { ^ instead of the expected: error: AndMyCarIsOutOfGas /home/alexrp/Source/ziglang/zig-llvm20/repro.zig:2:5: 0x103f00d in main (repro) return error.TheSkyIsFalling; ^ /home/alexrp/Source/ziglang/zig-llvm20/repro.zig:6:5: 0x103f015 in main (repro) return error.AndMyCarIsOutOfGas; ^ /home/alexrp/Source/ziglang/zig-llvm20/repro.zig:11:9: 0x103f01d in main (repro) try bar(); ^
2025-04-02compiler: allow `@import` of ZON without a result typeMason Remaley
In particular, this allows importing `build.zig.zon` at comptime.
2025-04-02Sema: increment extra index even if return type is genericDavid Rubin
2025-03-31Sema: allow `@ptrCast` slice of zero-bit type to slice of non-zero-bit typemlugg
This is actually completely well-defined. The resulting slice always has 0 elements. The only disallowed case is casting *to* a slice of a zero-bit type, because in that case, you cna't figure out how many destination elements to use (and there's *no* valid destination length if the source slice corresponds to more than 0 bits).
2025-03-30Sema: convert slice sentinel to single pointer correctlyDavid Rubin
2025-03-29compiler: "illegal behavior", not "undefined behavior", in errorsmlugg
2025-03-27std.meta.FieldType -> @FieldTypeАндрей Краевский
2025-03-24Sema: fix in-memory coercion of functions introducing new generic parametersmlugg
While it is not allowed for a function coercion to change whether a function is generic, it *is* okay to make existing concrete parameters of a generic function also generic, or vice versa. Either of these cases implies that the result is a generic function, so comptime type checks will happen when the function is ultimately called. Resolves: #21099
2025-03-21x86_64: rewrite wrapping multiplicationJacob Young
2025-03-17Sema: error on illegal code when targeting spirvAli Cheraghi
2025-03-16Sema: rewrite comptime arithmeticmlugg
This commit reworks how Sema handles arithmetic on comptime-known values, fixing many bugs in the process. The general pattern is that arithmetic on comptime-known values is now handled by the new namespace `Sema.arith`. Functions handling comptime arithmetic no longer live on `Value`; this is because some of them can emit compile errors, so some *can't* go on `Value`. Only semantic analysis should really be doing arithmetic on `Value`s anyway, so it makes sense for it to integrate more tightly with `Sema`. This commit also implements more coherent rules surrounding how `undefined` interacts with comptime and mixed-comptime-runtime arithmetic. The rules are as follows. * If an operation cannot trigger Illegal Behavior, and any operand is `undefined`, the result is `undefined`. This includes operations like `0 *| undef`, where the LHS logically *could* be used to determine a defined result. This is partly to simplify the language, but mostly to permit codegen backends to represent `undefined` values as completely invalid states. * If an operation *can* trigger Illegal Behvaior, and any operand is `undefined`, then Illegal Behavior results. This occurs even if the operand in question isn't the one that "decides" illegal behavior; for instance, `undef / 1` is undefined. This is for the same reasons as described above. * An operation which would trigger Illegal Behavior, when evaluated at comptime, instead triggers a compile error. Additionally, if one operand is comptime-known undef, such that the other (runtime-known) operand isn't needed to determine that Illegal Behavior would occur, the compile error is triggered. * The only situation in which an operation with one comptime-known operand has a comptime-known result is if that operand is undefined, in which case the result is either undefined or a compile error per the above rules. This could potentially be loosened in future (for instance, `0 * rt` could be comptime-known 0 with a runtime assertion that `rt` is not undefined), but at least for now, defining it more conservatively simplifies the language and allows us to easily change this in future if desired. This commit fixes many bugs regarding the handling of `undefined`, particularly in vectors. Along with a collection of smaller tests, two very large test cases are added to check arithmetic on `undefined`. The operations which have been rewritten in this PR are: * `+`, `+%`, `+|`, `@addWithOverflow` * `-`, `-%`, `-|`, `@subWithOverflow` * `*`, `*%`, `*|`, `@mulWithOverflow` * `/`, `@divFloor`, `@divTrunc`, `@divExact` * `%`, `@rem`, `@mod` Other arithmetic operations are currently unchanged. Resolves: #22743 Resolves: #22745 Resolves: #22748 Resolves: #22749 Resolves: #22914
2025-03-16Sema: correctly handle empty by-ref initializersmlugg
Resolves: #23210
2025-03-12Merge pull request #22397 from Techatrix/type-safe-astMatthew Lugg
improve type safety of std.zig.Ast
2025-03-11Sema: fix handling of `@This()` on opaquesmlugg
Resolves: #22869
2025-03-09Merge pull request #21933 from kcbanner/comptime_nan_comparisonAndrew Kelley
Fix float vector comparisons with signed zero and NaN, add test coverage
2025-03-08Sema: handle generated tag enums in union field order checkIan Johnson
Fixes #23059 The "note: enum field here" now references the field in the base union type rather than crashing.
2025-03-07std.zig.Ast: improve type safetyTechatrix
This commits adds the following distinct integer types to std.zig.Ast: - OptionalTokenIndex - TokenOffset - OptionalTokenOffset - Node.OptionalIndex - Node.Offset - Node.OptionalOffset The `Node.Index` type has also been converted to a distinct type while `TokenIndex` remains unchanged. `Ast.Node.Data` has also been changed to a (untagged) union to provide safety checks.
2025-03-03Value: fix comparison of NaN in compareHeteroAdvanacedkcbanner
Sema: fix equality comparison of signed zeroes and NaN in compareScalar tests: add test coverage for vector float comparisons