zig icon indicating copy to clipboard operation
zig copied to clipboard

compiler: implement analysis-local comptime-mutable memory

Open mlugg opened this issue 1 year ago • 2 comments

This commit changes how we represent comptime-mutable memory (comptime var) in the compiler in order to implement the intended behavior that references to such memory can only exist at comptime.

It does not clean up the representation of mutable values, improve the representation of comptime-known pointers, or fix the many bugs in the comptime pointer access code. These will be future enhancements.

Comptime memory lives for the duration of a single Sema, and is not permitted to escape that one analysis, either by becoming runtime-known or by becoming comptime-known to other analyses. These restrictions mean that we can represent comptime allocations not via Decl, but with state local to Sema - specifically, the new Sema.comptime_allocs field. All comptime-mutable allocations, as well as any comptime-known const allocs containing references to such memory, live in here. This allows for relatively fast checking of whether a value references any comptime-mtuable memory, since we need only traverse values up to pointers: pointers to Decls can never reference comptime-mutable memory, and pointers into Sema.comptime_allocs always do.

This change exposed some faulty pointer access logic in Value.zig. I've fixed the important cases, but there are some TODOs I've put in which are definitely possible to hit with sufficiently esoteric code. I plan to resolve these by auditing all direct accesses to pointers (most of them ought to use Sema to perform the pointer access!), but for now this is sufficient for all realistic code and to get tests passing.

This change eliminates Zcu.tmp_hack_arena, instead using the Sema arena for comptime memory mutations, which is possible since comptime memory is now local to the current Sema.

This change should allow Decl to store only an InternPool.Index rather than a full-blown ty: Type, val: Value. This commit does not perform this refactor.

mlugg avatar Mar 24 '24 04:03 mlugg

I don't fully understand the details of what this does, so I'll just ask directly: Is it still possible to construct a self-referential value at comptime? (minimalistic contrived example:)

const x = comptime x: {
  const X = struct {self: *const @This()};
  var x: X = undefined;
  x.self = &x;
  break :x x;
};

Update: Looking at the usage in my actual code again, I might not need this capability personally, since I can instead calculate an upper bound of the result size and pass a pre-allocated buffer into the function, as would be done for runtime code. The one scenario that maybe can't be expressed this way would be initializing a self-referential global const, but there might also be technical reasons for why that may be particularly difficult to support by the language.

rohlem avatar Mar 24 '24 09:03 rohlem

You can of course construct such a value, but yes, making it the value of a global declaration will raise a compile error. However, that code isn't doing what you seem to think it is - the global x is not technically self-referential! The pointer within it doesn't refer back to the global x, but rather the comptime var x lowered as a constant.

Initializing a self-referential global is #131 (it's not the issue title, but resolving the issue will also allow direct self-reference).

mlugg avatar Mar 24 '24 14:03 mlugg

10/10 would merge again

andrewrk avatar Mar 25 '24 23:03 andrewrk

More release notes at https://ziggit.dev/t/comptime-mutable-memory-changes/3702

andrewrk avatar Mar 27 '24 04:03 andrewrk