ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
33.69k stars 2.47k forks source link

Proposal: make comptime fields immutable (for realsies) #19483

Open mlugg opened 5 months ago

mlugg commented 5 months ago

Tuples and structs in Zig support comptime fields, which are effectively declarations with a fancy hat on. These fields have a fixed value which is associated with the type itself.

A slightly weird thing about comptime fields is that they're actually semantically considered mutable: the catch is that the value you store to them must be comptime-known to match the current value. For instance:

const S = struct {
    comptime a: [2]u32 = .{ 123, 456 },
};
test "store to comptime field" {
    var s: S = .{};
    s.a[0] = 123; // this is fine
    s.a = .{ 123, 456 }; // this is fine
    s.a[0] = 600; // this emits an error
}
foo.zig:8:12: error: value stored in comptime field does not match the default value of the field
    s.a[0] = 600; // this emits an error
    ~~~~~~~^~~~~

Because pointers to comptime fields are semantically considered mutable, they unfortunately have the same restrictions as pointers to comptime vars, introduced in #19414. This limits the usefulness of these fields, and can be particularly frustrating since fields of untyped anonymous initializations are implicitly made comptime.

However, there does not appear to be any reason these fields have to be mutable. I would guess that the main reason is for RLS-style initialization: in the example above, perhaps we want s = .{ .a = .{ 123, 456 } } to be valid. I think this makes sense, because of the fact that if no type were given, you would get a type with a comptime field; it would be strange if explicit specification of the struct removed the ability to initialize its comptime field. Thus, I do not propose disallowing comptime fields from appearing in struct iniitalization expressions, although the field initializer must -- like today -- be comptime-known and match the field value. I do, however, propose that "normal" pointers to such fields should always be considered immutable. They should have a const pointer type, and attempting to mutate through them at comptime should raise a compile error. This would remove the restrictions in place on these fields, allowing them to be referenced at runtime just like non-comptime fields. I believe this change would improve "perceived simplicity" of the language, i.e. how difficult it is to learn/understand (and the impact on actual spec simplicity should be fairly neural).

Lking03x commented 5 months ago

You are right the language should not accept that but it's not for RLS to work.

Zig struct "field" themselves have NO reason to be const/comptime AFAIK. For the use of static member declarations associated/namespaced with a struct type one do this:

const S = struct {
    // "const" or "var" as a normal variable declaration
    const a = expr;
    var b = expr2;
};

Both a and b are associated as static members, have a type which can be inferred, and their expression can be comptime. a is immutable, b is not.

silversquirl commented 5 months ago

@Lking03x comptime fields are an important and useful feature. Decls do not cover their usecases.

For example, they allow writer.print("{} {}\n", .{runtime_value, 7}) to inline the 7 because it's comptime-known, while still allowing runtime_value to be handled at runtime.

ethernetsellout commented 5 months ago

Would this entail rejecting #5675?

mlugg commented 5 months ago

5675 is a proposal which offers a completely different form of "comptime field" that exists today. This proposal, on the other hand, is a simple augmentation of the current feature.

Thus, acceptance and implementation of this proposal need not directly lead to rejection of #5675; it could still be accepted in future and overrule this behavior. (I should, however, note that I am like 95% confident that proposal will be rejected.)