authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-04-02 13:17:34+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-04-02 13:17:34+01:00
log4303b43519fe8a3b21a264642076d30c2f0927bc
treeb3968e3b47f0d71770498577084e98a34b6320cc
parentbfab9582c7353c85963b1abfd9844218de9a0cd0
parentdd3f01eadfff28651847d56b2c52c98d6d73d8c9
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #22907 from MasonRemaley/import-zon-anon-type

Allow importing ZON without a result type (you can import build.zig.zon now)

15 files changed, 329 insertions(+), 37 deletions(-)

src/InternPool.zig+1
......@@ -2136,6 +2136,7 @@ pub const Key = union(enum) {
21362136 /// To avoid making this key overly complex, the type-specific data is hashed by Sema.
21372137 reified: struct {
21382138 /// A `reify`, `struct_init`, `struct_init_ref`, or `struct_init_anon` instruction.
2139 /// Alternatively, this is `main_struct_inst` of a ZON file.
21392140 zir_index: TrackedInst.Index,
21402141 /// A hash of this type's attributes, fields, etc, generated by Sema.
21412142 type_hash: u64,
src/Sema.zig+9-10
......@@ -2998,7 +2998,7 @@ fn zirStructDecl(
29982998 return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
29992999}
30003000
3001fn createTypeName(
3001pub fn createTypeName(
30023002 sema: *Sema,
30033003 block: *Block,
30043004 name_strategy: Zir.Inst.NameStrategy,
......@@ -14065,14 +14065,13 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
1406514065 return Air.internedToRef(ty);
1406614066 },
1406714067 .zon => {
14068 if (extra.res_ty == .none) {
14069 return sema.fail(block, operand_src, "'@import' of ZON must have a known result type", .{});
14070 }
14071 const res_ty_inst = try sema.resolveInst(extra.res_ty);
14072 const res_ty = try sema.analyzeAsType(block, operand_src, res_ty_inst);
14073 if (res_ty.isGenericPoison()) {
14074 return sema.fail(block, operand_src, "'@import' of ZON must have a known result type", .{});
14075 }
14068 const res_ty: InternPool.Index = b: {
14069 if (extra.res_ty == .none) break :b .none;
14070 const res_ty_inst = try sema.resolveInst(extra.res_ty);
14071 const res_ty = try sema.analyzeAsType(block, operand_src, res_ty_inst);
14072 if (res_ty.isGenericPoison()) break :b .none;
14073 break :b res_ty.toIntern();
14074 };
1407614075
1407714076 try sema.declareDependency(.{ .zon_file = result.file_index });
1407814077 const interned = try LowerZon.run(
......@@ -31699,7 +31698,7 @@ fn addReferenceEntry(
3169931698 try zcu.addUnitReference(sema.owner, referenced_unit, src);
3170031699}
3170131700
31702fn addTypeReferenceEntry(
31701pub fn addTypeReferenceEntry(
3170331702 sema: *Sema,
3170431703 src: LazySrcLoc,
3170531704 referenced_type: InternPool.Index,
src/Sema/LowerZon.zig+170-16
......@@ -33,7 +33,7 @@ pub fn run(
3333 sema: *Sema,
3434 file: *File,
3535 file_index: Zcu.File.Index,
36 res_ty: Type,
36 res_ty_interned: InternPool.Index,
3737 import_loc: LazySrcLoc,
3838 block: *Sema.Block,
3939) CompileError!InternPool.Index {
......@@ -53,13 +53,167 @@ pub fn run(
5353 .base_node_inst = tracked_inst,
5454 };
5555
56 try lower_zon.checkType(res_ty);
56 if (res_ty_interned == .none) {
57 return lower_zon.lowerExprAnonResTy(.root);
58 } else {
59 const res_ty: Type = .fromInterned(res_ty_interned);
60 try lower_zon.checkType(res_ty);
61 return lower_zon.lowerExprKnownResTy(.root, res_ty);
62 }
63}
5764
58 return lower_zon.lowerExpr(.root, res_ty);
65fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!InternPool.Index {
66 const gpa = self.sema.gpa;
67 const pt = self.sema.pt;
68 const ip = &pt.zcu.intern_pool;
69 switch (node.get(self.file.zoir.?)) {
70 .true => return .bool_true,
71 .false => return .bool_false,
72 .null => return .null_value,
73 .pos_inf => return self.fail(node, "infinity requires a known result type", .{}),
74 .neg_inf => return self.fail(node, "negative infinity requires a known result type", .{}),
75 .nan => return self.fail(node, "NaN requires a known result type", .{}),
76 .int_literal => |int| switch (int) {
77 .small => |val| return pt.intern(.{ .int = .{
78 .ty = .comptime_int_type,
79 .storage = .{ .i64 = val },
80 } }),
81 .big => |val| return pt.intern(.{ .int = .{
82 .ty = .comptime_int_type,
83 .storage = .{ .big_int = val },
84 } }),
85 },
86 .float_literal => |val| {
87 const result = try pt.floatValue(.comptime_float, val);
88 return result.toIntern();
89 },
90 .char_literal => |val| return pt.intern(.{ .int = .{
91 .ty = .comptime_int_type,
92 .storage = .{ .i64 = val },
93 } }),
94 .enum_literal => |val| return pt.intern(.{
95 .enum_literal = try ip.getOrPutString(
96 gpa,
97 pt.tid,
98 val.get(self.file.zoir.?),
99 .no_embedded_nulls,
100 ),
101 }),
102 .string_literal => |val| {
103 const ip_str = try ip.getOrPutString(gpa, pt.tid, val, .maybe_embedded_nulls);
104 const result = try self.sema.addStrLit(ip_str, val.len);
105 return result.toInterned().?;
106 },
107 .empty_literal => return .empty_tuple,
108 .array_literal => |nodes| {
109 const types = try self.sema.arena.alloc(InternPool.Index, nodes.len);
110 const values = try self.sema.arena.alloc(InternPool.Index, nodes.len);
111 for (0..nodes.len) |i| {
112 values[i] = try self.lowerExprAnonResTy(nodes.at(@intCast(i)));
113 types[i] = Value.fromInterned(values[i]).typeOf(pt.zcu).toIntern();
114 }
115 const ty = try ip.getTupleType(
116 gpa,
117 pt.tid,
118 .{
119 .types = types,
120 .values = values,
121 },
122 );
123 return pt.intern(.{ .aggregate = .{
124 .ty = ty,
125 .storage = .{ .elems = values },
126 } });
127 },
128 .struct_literal => |init| {
129 const elems = try self.sema.arena.alloc(InternPool.Index, init.names.len);
130 for (0..init.names.len) |i| {
131 elems[i] = try self.lowerExprAnonResTy(init.vals.at(@intCast(i)));
132 }
133 const struct_ty = switch (try ip.getStructType(
134 gpa,
135 pt.tid,
136 .{
137 .layout = .auto,
138 .fields_len = @intCast(init.names.len),
139 .known_non_opv = false,
140 .requires_comptime = .no,
141 .any_comptime_fields = true,
142 .any_default_inits = true,
143 .inits_resolved = true,
144 .any_aligned_fields = false,
145 .key = .{ .reified = .{
146 .zir_index = self.base_node_inst,
147 .type_hash = hash: {
148 var hasher: std.hash.Wyhash = .init(0);
149 hasher.update(std.mem.asBytes(&node));
150 hasher.update(std.mem.sliceAsBytes(elems));
151 hasher.update(std.mem.sliceAsBytes(init.names));
152 break :hash hasher.final();
153 },
154 } },
155 },
156 false,
157 )) {
158 .wip => |wip| ty: {
159 errdefer wip.cancel(ip, pt.tid);
160 wip.setName(ip, try self.sema.createTypeName(
161 self.block,
162 .anon,
163 "struct",
164 self.base_node_inst.resolve(ip),
165 wip.index,
166 ));
167
168 const struct_type = ip.loadStructType(wip.index);
169
170 for (init.names, 0..) |name, field_idx| {
171 const name_interned = try ip.getOrPutString(
172 gpa,
173 pt.tid,
174 name.get(self.file.zoir.?),
175 .no_embedded_nulls,
176 );
177 assert(struct_type.addFieldName(ip, name_interned) == null);
178 struct_type.setFieldComptime(ip, field_idx);
179 }
180
181 @memcpy(struct_type.field_inits.get(ip), elems);
182 const types = struct_type.field_types.get(ip);
183 for (0..init.names.len) |i| {
184 types[i] = Value.fromInterned(elems[i]).typeOf(pt.zcu).toIntern();
185 }
186
187 const new_namespace_index = try pt.createNamespace(.{
188 .parent = self.block.namespace.toOptional(),
189 .owner_type = wip.index,
190 .file_scope = self.block.getFileScopeIndex(pt.zcu),
191 .generation = pt.zcu.generation,
192 });
193 try pt.zcu.comp.queueJob(.{ .resolve_type_fully = wip.index });
194 codegen_type: {
195 if (pt.zcu.comp.config.use_llvm) break :codegen_type;
196 if (self.block.ownerModule().strip) break :codegen_type;
197 try pt.zcu.comp.queueJob(.{ .codegen_type = wip.index });
198 }
199 break :ty wip.finish(ip, new_namespace_index);
200 },
201 .existing => |ty| ty,
202 };
203 try self.sema.declareDependency(.{ .interned = struct_ty });
204 try self.sema.addTypeReferenceEntry(self.nodeSrc(node), struct_ty);
205
206 return try pt.intern(.{ .aggregate = .{
207 .ty = struct_ty,
208 .storage = .{ .elems = elems },
209 } });
210 },
211 }
59212}
60213
61/// Validate that `ty` is a valid ZON type. If not, emit a compile error.
62/// i.e. no nested optionals, no error sets, etc.
214/// Validate that `ty` is a valid ZON type, or emit a compile error.
215///
216/// Rules out nested optionals, error sets, etc.
63217fn checkType(self: *LowerZon, ty: Type) !void {
64218 var visited: std.AutoHashMapUnmanaged(InternPool.Index, void) = .empty;
65219 try self.checkTypeInner(ty, null, &visited);
......@@ -201,9 +355,9 @@ fn fail(
201355 return self.sema.failWithOwnedErrorMsg(self.block, err_msg);
202356}
203357
204fn lowerExpr(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) CompileError!InternPool.Index {
358fn lowerExprKnownResTy(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) CompileError!InternPool.Index {
205359 const pt = self.sema.pt;
206 return self.lowerExprInner(node, res_ty) catch |err| switch (err) {
360 return self.lowerExprKnownResTyInner(node, res_ty) catch |err| switch (err) {
207361 error.WrongType => return self.fail(
208362 node,
209363 "expected type '{}'",
......@@ -213,7 +367,7 @@ fn lowerExpr(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) CompileError!
213367 };
214368}
215369
216fn lowerExprInner(
370fn lowerExprKnownResTyInner(
217371 self: *LowerZon,
218372 node: Zoir.Node.Index,
219373 res_ty: Type,
......@@ -227,7 +381,7 @@ fn lowerExprInner(
227381 break :b .none;
228382 } else b: {
229383 const child_type = res_ty.optionalChild(pt.zcu);
230 break :b try self.lowerExprInner(node, child_type);
384 break :b try self.lowerExprKnownResTyInner(node, child_type);
231385 },
232386 },
233387 }),
......@@ -239,7 +393,7 @@ fn lowerExprInner(
239393 .base_addr = .{
240394 .uav = .{
241395 .orig_ty = res_ty.toIntern(),
242 .val = try self.lowerExprInner(node, .fromInterned(ptr_info.child)),
396 .val = try self.lowerExprKnownResTyInner(node, .fromInterned(ptr_info.child)),
243397 },
244398 },
245399 .byte_offset = 0,
......@@ -486,7 +640,7 @@ fn lowerArray(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
486640 );
487641
488642 for (0..nodes.len) |i| {
489 elems[i] = try self.lowerExpr(nodes.at(@intCast(i)), array_info.elem_type);
643 elems[i] = try self.lowerExprKnownResTy(nodes.at(@intCast(i)), array_info.elem_type);
490644 }
491645
492646 if (array_info.sentinel) |sentinel| {
......@@ -587,7 +741,7 @@ fn lowerTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
587741 );
588742 }
589743
590 const val = try self.lowerExpr(elem_nodes.at(@intCast(i)), .fromInterned(field_types[i]));
744 const val = try self.lowerExprKnownResTy(elem_nodes.at(@intCast(i)), .fromInterned(field_types[i]));
591745
592746 if (elems[i] != .none and val != elems[i]) {
593747 const elem_node = elem_nodes.at(@intCast(i));
......@@ -650,7 +804,7 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool
650804 };
651805
652806 const field_type: Type = .fromInterned(struct_info.field_types.get(ip)[name_index]);
653 field_values[name_index] = try self.lowerExpr(field_node, field_type);
807 field_values[name_index] = try self.lowerExprKnownResTy(field_node, field_type);
654808
655809 if (struct_info.comptime_bits.getBit(ip, name_index)) {
656810 const val = ip.indexToKey(field_values[name_index]);
......@@ -715,7 +869,7 @@ fn lowerSlice(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
715869 const elems = try self.sema.arena.alloc(InternPool.Index, elem_nodes.len + @intFromBool(ptr_info.sentinel != .none));
716870
717871 for (elems, 0..) |*elem, i| {
718 elem.* = try self.lowerExpr(elem_nodes.at(@intCast(i)), .fromInterned(ptr_info.child));
872 elem.* = try self.lowerExprKnownResTy(elem_nodes.at(@intCast(i)), .fromInterned(ptr_info.child));
719873 }
720874
721875 if (ptr_info.sentinel != .none) {
......@@ -810,7 +964,7 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
810964 if (field_type.toIntern() == .void_type) {
811965 return self.fail(field_node, "expected type 'void'", .{});
812966 }
813 break :b try self.lowerExpr(field_node, field_type);
967 break :b try self.lowerExprKnownResTy(field_node, field_type);
814968 } else b: {
815969 if (field_type.toIntern() != .void_type) {
816970 return error.WrongType;
......@@ -846,7 +1000,7 @@ fn lowerVector(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool
8461000 }
8471001
8481002 for (elems, 0..) |*elem, i| {
849 elem.* = try self.lowerExpr(elem_nodes.at(@intCast(i)), .fromInterned(vector_info.child));
1003 elem.* = try self.lowerExprKnownResTy(elem_nodes.at(@intCast(i)), .fromInterned(vector_info.child));
8501004 }
8511005
8521006 return self.sema.pt.intern(.{ .aggregate = .{
src/Type.zig+4-1
......@@ -3589,7 +3589,10 @@ pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 {
35893589 };
35903590 const info = tracked.resolveFull(&zcu.intern_pool) orelse return null;
35913591 const file = zcu.fileByIndex(info.file);
3592 const zir = file.zir.?;
3592 const zir = switch (file.getMode()) {
3593 .zig => file.zir.?,
3594 .zon => return 0,
3595 };
35933596 const inst = zir.instructions.get(@intFromEnum(info.inst));
35943597 return switch (inst.tag) {
35953598 .struct_init, .struct_init_ref => zir.extraData(Zir.Inst.StructInit, inst.data.pl_node.payload_index).data.abs_line,
src/Zcu.zig+1-1
......@@ -2753,7 +2753,7 @@ pub fn saveZoirCache(cache_file: std.fs.File, stat: std.fs.File.Stat, zoir: Zoir
27532753 },
27542754 .{
27552755 .base = @ptrCast(zoir.limbs),
2756 .len = zoir.limbs.len * 4,
2756 .len = zoir.limbs.len * @sizeOf(std.math.big.Limb),
27572757 },
27582758 .{
27592759 .base = zoir.string_bytes.ptr,
src/Zcu/PerThread.zig+1
......@@ -234,6 +234,7 @@ pub fn updateFile(
234234 error.FileTooBig => unreachable, // 0 is not too big
235235 else => |e| return e,
236236 };
237 try cache_file.seekTo(0);
237238
238239 if (stat.size > std.math.maxInt(u32))
239240 return error.FileTooBig;
stage1/wasi.c+2
......@@ -1003,6 +1003,8 @@ uint32_t wasi_snapshot_preview1_fd_seek(uint32_t fd, uint64_t in_offset, uint32_
10031003 default: panic("unimplemented");
10041004 }
10051005
1006 if (fds[fd].stream == NULL) return wasi_errno_success;
1007
10061008 int seek_whence;
10071009 switch (whence) {
10081010 case wasi_whence_set:
test/behavior/zon.zig+54
......@@ -517,3 +517,57 @@ test "recursive" {
517517 const expected: Recursive = .{ .foo = &.{ .foo = null } };
518518 try expectEqualDeep(expected, @as(Recursive, @import("zon/recursive.zon")));
519519}
520
521test "anon" {
522 const expected = .{
523 .{
524 .bool_true = true,
525 .bool_false = false,
526 .string = "foo",
527 },
528 .{
529 null,
530 10,
531 36893488147419103232,
532 1.234,
533 'z',
534 .bar,
535 .{},
536 },
537 };
538
539 const actual = @import("zon/anon.zon");
540 try expectEqual(expected.len, actual.len);
541 try expectEqual(expected[1], actual[1]);
542 const expected_struct = expected[0];
543 const actual_struct = actual[0];
544 const expected_fields = @typeInfo(@TypeOf(expected_struct)).@"struct".fields;
545 const actual_fields = @typeInfo(@TypeOf(actual_struct)).@"struct".fields;
546 try expectEqual(expected_fields.len, actual_fields.len);
547 inline for (expected_fields) |field| {
548 try expectEqual(@field(expected_struct, field.name), @field(actual_struct, field.name));
549 }
550}
551
552test "build.zig.zon" {
553 const build = @import("zon/build.zig.zon");
554
555 try expectEqual(4, @typeInfo(@TypeOf(build)).@"struct".fields.len);
556 try expectEqualStrings("temp", build.name);
557 try expectEqualStrings("0.0.0", build.version);
558
559 const dependencies = build.dependencies;
560 try expectEqual(2, @typeInfo(@TypeOf(dependencies)).@"struct".fields.len);
561
562 const example_0 = dependencies.example_0;
563 try expectEqual(2, @typeInfo(@TypeOf(dependencies)).@"struct".fields.len);
564 try expectEqualStrings("https://example.com/foo.tar.gz", example_0.url);
565 try expectEqualStrings("...", example_0.hash);
566
567 const example_1 = dependencies.example_1;
568 try expectEqual(2, @typeInfo(@TypeOf(dependencies)).@"struct".fields.len);
569 try expectEqualStrings("../foo", example_1.path);
570 try expectEqual(false, example_1.lazy);
571
572 try expectEqual(.{ "build.zig", "build.zig.zon", "src" }, build.paths);
573}
test/behavior/zon/anon.zon created+16
......@@ -0,0 +1,16 @@
1.{
2 .{
3 .bool_true = true,
4 .bool_false = false,
5 .string = "foo",
6 },
7 .{
8 null,
9 10,
10 36893488147419103232,
11 1.234,
12 'z',
13 .bar,
14 .{},
15 },
16}
test/behavior/zon/build.zig.zon created+20
......@@ -0,0 +1,20 @@
1.{
2 // Comment
3 .name = "temp",
4 .version = "0.0.0",
5 .dependencies = .{
6 .example_0 = .{
7 .url = "https://example.com/foo.tar.gz",
8 .hash = "...",
9 },
10 .example_1 = .{
11 .path = "../foo",
12 .lazy = false,
13 },
14 },
15 .paths = .{
16 "build.zig",
17 "build.zig.zon",
18 "src",
19 },
20}
test/cases/compile_errors/@import_zon_anon_inf.zig created+9
......@@ -0,0 +1,9 @@
1export fn entry() void {
2 _ = @import("zon/inf.zon");
3}
4
5// error
6// imports=zon/inf.zon
7//
8// inf.zon:1:1: error: infinity requires a known result type
9// tmp.zig:2:17: note: imported here
test/cases/compile_errors/@import_zon_anon_nan.zig created+9
......@@ -0,0 +1,9 @@
1export fn entry() void {
2 _ = @import("zon/nan.zon");
3}
4
5// error
6// imports=zon/nan.zon
7//
8// nan.zon:1:1: error: NaN requires a known result type
9// tmp.zig:2:17: note: imported here
test/cases/compile_errors/@import_zon_anon_neg_inf.zig created+9
......@@ -0,0 +1,9 @@
1export fn entry() void {
2 _ = @import("zon/neg_inf.zon");
3}
4
5// error
6// imports=zon/neg_inf.zon
7//
8// neg_inf.zon:1:1: error: negative infinity requires a known result type
9// tmp.zig:2:17: note: imported here
test/cases/compile_errors/@import_zon_no_rt.zig deleted-9
......@@ -1,9 +0,0 @@
1export fn entry() void {
2 const f = @import("zon/simple_union.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/simple_union.zon
8//
9// tmp.zig:2:23: error: '@import' of ZON must have a known result type
test/incremental/change_zon_file_no_result_type created+24
......@@ -0,0 +1,24 @@
1#target=x86_64-linux-selfhosted
2#target=x86_64-linux-cbe
3#target=x86_64-windows-cbe
4//#target=wasm32-wasi-selfhosted
5#update=initial version
6#file=main.zig
7const std = @import("std");
8pub fn main() !void {
9 try std.io.getStdOut().writeAll(@import("foo.zon").message);
10}
11#file=foo.zon
12.{
13 .message = "Hello, World!\n",
14 .a_number = 0,
15}
16#expect_stdout="Hello, World!\n"
17
18#update=change ZON file
19#file=foo.zon
20.{
21 .message = "Hello again, World!\n",
22 .b_number = 0,
23}
24#expect_stdout="Hello again, World!\n"