authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-01-19 00:16:26+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-01-19 00:16:26+00:00
logebf782e9444fc97eaecc851a2c9e76ca56d639a7
tree069112ab9e9821a19109bc16b1acd63d6e210fe0
parenta5d2aaa936dc74b85d7cf6695e6a7627d7a645d2
parent3b6e5ba4909b7db68bfde7f19bb23a3b9431649b
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #22531 from mlugg/various-fixes

incremental, Sema: minor fixes

4 files changed, 177 insertions(+), 28 deletions(-)

src/Sema.zig+91-22
...@@ -3190,6 +3190,15 @@ fn zirEnumDecl(...@@ -3190,6 +3190,15 @@ fn zirEnumDecl(
31903190
3191 try sema.declareDependency(.{ .interned = new_ty });3191 try sema.declareDependency(.{ .interned = new_ty });
3192 try sema.addTypeReferenceEntry(src, new_ty);3192 try sema.addTypeReferenceEntry(src, new_ty);
3193
3194 // Since this is an enum, it has to be resolved immediately.
3195 // `ensureTypeUpToDate` has resolved the new type if necessary.
3196 // We just need to check for resolution failures.
3197 const ty_unit: AnalUnit = .wrap(.{ .type = new_ty });
3198 if (zcu.failed_analysis.contains(ty_unit) or zcu.transitive_failed_analysis.contains(ty_unit)) {
3199 return error.AnalysisFail;
3200 }
3201
3193 return Air.internedToRef(new_ty);3202 return Air.internedToRef(new_ty);
3194 },3203 },
3195 .wip => |wip| wip,3204 .wip => |wip| wip,
...@@ -17801,12 +17810,23 @@ fn zirThis(...@@ -17801,12 +17810,23 @@ fn zirThis(
17801) CompileError!Air.Inst.Ref {17810) CompileError!Air.Inst.Ref {
17802 _ = extended;17811 _ = extended;
17803 const pt = sema.pt;17812 const pt = sema.pt;
17813 const zcu = pt.zcu;
17804 const namespace = pt.zcu.namespacePtr(block.namespace);17814 const namespace = pt.zcu.namespacePtr(block.namespace);
1780517815
17806 const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type);17816 const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type);
1780717817
17808 switch (pt.zcu.intern_pool.indexToKey(new_ty)) {17818 switch (pt.zcu.intern_pool.indexToKey(new_ty)) {
17809 .struct_type, .union_type, .enum_type => try sema.declareDependency(.{ .interned = new_ty }),17819 .struct_type, .union_type => try sema.declareDependency(.{ .interned = new_ty }),
17820 .enum_type => {
17821 try sema.declareDependency(.{ .interned = new_ty });
17822 // Since this is an enum, it has to be resolved immediately.
17823 // `ensureTypeUpToDate` has resolved the new type if necessary.
17824 // We just need to check for resolution failures.
17825 const ty_unit: AnalUnit = .wrap(.{ .type = new_ty });
17826 if (zcu.failed_analysis.contains(ty_unit) or zcu.transitive_failed_analysis.contains(ty_unit)) {
17827 return error.AnalysisFail;
17828 }
17829 },
17810 .opaque_type => {},17830 .opaque_type => {},
17811 else => unreachable,17831 else => unreachable,
17812 }17832 }
...@@ -28478,6 +28498,10 @@ fn unionFieldPtr(...@@ -28478,6 +28498,10 @@ fn unionFieldPtr(
28478 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: {28498 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: {
28479 switch (union_obj.flagsUnordered(ip).layout) {28499 switch (union_obj.flagsUnordered(ip).layout) {
28480 .auto => if (initializing) {28500 .auto => if (initializing) {
28501 if (!sema.isComptimeMutablePtr(union_ptr_val)) {
28502 // The initialization is a runtime operation.
28503 break :ct;
28504 }
28481 // Store to the union to initialize the tag.28505 // Store to the union to initialize the tag.
28482 const field_tag = try pt.enumValueFieldIndex(Type.fromInterned(union_obj.enum_tag_ty), enum_field_index);28506 const field_tag = try pt.enumValueFieldIndex(Type.fromInterned(union_obj.enum_tag_ty), enum_field_index);
28483 const payload_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);28507 const payload_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
...@@ -38330,22 +38354,16 @@ pub fn resolveDeclaredEnum(...@@ -38330,22 +38354,16 @@ pub fn resolveDeclaredEnum(
38330 fields_len: u32,38354 fields_len: u32,
38331 zir: Zir,38355 zir: Zir,
38332 body_end: usize,38356 body_end: usize,
38333) Zcu.CompileError!void {38357) Zcu.SemaError!void {
38334 const zcu = pt.zcu;38358 const zcu = pt.zcu;
38335 const gpa = zcu.gpa;38359 const gpa = zcu.gpa;
38336 const ip = &zcu.intern_pool;
38337
38338 const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
3833938360
38340 const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(0) };38361 const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(0) };
38341 const tag_ty_src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = .{ .node_offset_container_tag = 0 } };
3834238362
38343 const anal_unit = AnalUnit.wrap(.{ .type = wip_ty.index });38363 var arena: std.heap.ArenaAllocator = .init(gpa);
38344
38345 var arena = std.heap.ArenaAllocator.init(gpa);
38346 defer arena.deinit();38364 defer arena.deinit();
3834738365
38348 var comptime_err_ret_trace = std.ArrayList(Zcu.LazySrcLoc).init(gpa);38366 var comptime_err_ret_trace: std.ArrayList(Zcu.LazySrcLoc) = .init(gpa);
38349 defer comptime_err_ret_trace.deinit();38367 defer comptime_err_ret_trace.deinit();
3835038368
38351 var sema: Sema = .{38369 var sema: Sema = .{
...@@ -38353,7 +38371,7 @@ pub fn resolveDeclaredEnum(...@@ -38353,7 +38371,7 @@ pub fn resolveDeclaredEnum(
38353 .gpa = gpa,38371 .gpa = gpa,
38354 .arena = arena.allocator(),38372 .arena = arena.allocator(),
38355 .code = zir,38373 .code = zir,
38356 .owner = anal_unit,38374 .owner = .wrap(.{ .type = wip_ty.index }),
38357 .func_index = .none,38375 .func_index = .none,
38358 .func_is_naked = false,38376 .func_is_naked = false,
38359 .fn_ret_ty = Type.void,38377 .fn_ret_ty = Type.void,
...@@ -38379,15 +38397,66 @@ pub fn resolveDeclaredEnum(...@@ -38379,15 +38397,66 @@ pub fn resolveDeclaredEnum(
38379 };38397 };
38380 defer block.instructions.deinit(gpa);38398 defer block.instructions.deinit(gpa);
3838138399
38400 sema.resolveDeclaredEnumInner(
38401 &block,
38402 wip_ty,
38403 inst,
38404 tracked_inst,
38405 src,
38406 small,
38407 body,
38408 tag_type_ref,
38409 any_values,
38410 fields_len,
38411 zir,
38412 body_end,
38413 ) catch |err| switch (err) {
38414 error.GenericPoison => unreachable,
38415 error.ComptimeBreak => unreachable,
38416 error.ComptimeReturn => unreachable,
38417 error.OutOfMemory => |e| return e,
38418 error.AnalysisFail => {
38419 if (!zcu.failed_analysis.contains(sema.owner)) {
38420 try zcu.transitive_failed_analysis.put(gpa, sema.owner, {});
38421 }
38422 return error.AnalysisFail;
38423 },
38424 };
38425}
38426
38427fn resolveDeclaredEnumInner(
38428 sema: *Sema,
38429 block: *Block,
38430 wip_ty: InternPool.WipEnumType,
38431 inst: Zir.Inst.Index,
38432 tracked_inst: InternPool.TrackedInst.Index,
38433 src: LazySrcLoc,
38434 small: Zir.Inst.EnumDecl.Small,
38435 body: []const Zir.Inst.Index,
38436 tag_type_ref: Zir.Inst.Ref,
38437 any_values: bool,
38438 fields_len: u32,
38439 zir: Zir,
38440 body_end: usize,
38441) Zcu.CompileError!void {
38442 const pt = sema.pt;
38443 const zcu = pt.zcu;
38444 const gpa = zcu.gpa;
38445 const ip = &zcu.intern_pool;
38446
38447 const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
38448
38449 const tag_ty_src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = .{ .node_offset_container_tag = 0 } };
38450
38382 const int_tag_ty = ty: {38451 const int_tag_ty = ty: {
38383 if (body.len != 0) {38452 if (body.len != 0) {
38384 _ = try sema.analyzeInlineBody(&block, body, inst);38453 _ = try sema.analyzeInlineBody(block, body, inst);
38385 }38454 }
3838638455
38387 if (tag_type_ref != .none) {38456 if (tag_type_ref != .none) {
38388 const ty = try sema.resolveType(&block, tag_ty_src, tag_type_ref);38457 const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref);
38389 if (ty.zigTypeTag(zcu) != .int and ty.zigTypeTag(zcu) != .comptime_int) {38458 if (ty.zigTypeTag(zcu) != .int and ty.zigTypeTag(zcu) != .comptime_int) {
38390 return sema.fail(&block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(pt)});38459 return sema.fail(block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(pt)});
38391 }38460 }
38392 break :ty ty;38461 break :ty ty;
38393 } else if (fields_len == 0) {38462 } else if (fields_len == 0) {
...@@ -38402,7 +38471,7 @@ pub fn resolveDeclaredEnum(...@@ -38402,7 +38471,7 @@ pub fn resolveDeclaredEnum(
3840238471
38403 if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) {38472 if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) {
38404 if (fields_len > 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(zcu)) {38473 if (fields_len > 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(zcu)) {
38405 return sema.fail(&block, src, "non-exhaustive enum specifies every value", .{});38474 return sema.fail(block, src, "non-exhaustive enum specifies every value", .{});
38406 }38475 }
38407 }38476 }
3840838477
...@@ -38434,7 +38503,7 @@ pub fn resolveDeclaredEnum(...@@ -38434,7 +38503,7 @@ pub fn resolveDeclaredEnum(
38434 const tag_val_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);38503 const tag_val_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
38435 extra_index += 1;38504 extra_index += 1;
38436 const tag_inst = try sema.resolveInst(tag_val_ref);38505 const tag_inst = try sema.resolveInst(tag_val_ref);
38437 last_tag_val = try sema.resolveConstDefinedValue(&block, .{38506 last_tag_val = try sema.resolveConstDefinedValue(block, .{
38438 .base_node_inst = tracked_inst,38507 .base_node_inst = tracked_inst,
38439 .offset = .{ .container_field_name = field_i },38508 .offset = .{ .container_field_name = field_i },
38440 }, tag_inst, .{ .simple = .enum_field_tag_value });38509 }, tag_inst, .{ .simple = .enum_field_tag_value });
...@@ -38447,12 +38516,12 @@ pub fn resolveDeclaredEnum(...@@ -38447,12 +38516,12 @@ pub fn resolveDeclaredEnum(
38447 .offset = .{ .container_field_value = conflict.prev_field_idx },38516 .offset = .{ .container_field_value = conflict.prev_field_idx },
38448 };38517 };
38449 const msg = msg: {38518 const msg = msg: {
38450 const msg = try sema.errMsg(value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValueSema(pt, &sema)});38519 const msg = try sema.errMsg(value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)});
38451 errdefer msg.destroy(gpa);38520 errdefer msg.destroy(gpa);
38452 try sema.errNote(other_field_src, msg, "other occurrence here", .{});38521 try sema.errNote(other_field_src, msg, "other occurrence here", .{});
38453 break :msg msg;38522 break :msg msg;
38454 };38523 };
38455 return sema.failWithOwnedErrorMsg(&block, msg);38524 return sema.failWithOwnedErrorMsg(block, msg);
38456 }38525 }
38457 break :overflow false;38526 break :overflow false;
38458 } else if (any_values) overflow: {38527 } else if (any_values) overflow: {
...@@ -38469,12 +38538,12 @@ pub fn resolveDeclaredEnum(...@@ -38469,12 +38538,12 @@ pub fn resolveDeclaredEnum(
38469 .offset = .{ .container_field_value = conflict.prev_field_idx },38538 .offset = .{ .container_field_value = conflict.prev_field_idx },
38470 };38539 };
38471 const msg = msg: {38540 const msg = msg: {
38472 const msg = try sema.errMsg(value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValueSema(pt, &sema)});38541 const msg = try sema.errMsg(value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)});
38473 errdefer msg.destroy(gpa);38542 errdefer msg.destroy(gpa);
38474 try sema.errNote(other_field_src, msg, "other occurrence here", .{});38543 try sema.errNote(other_field_src, msg, "other occurrence here", .{});
38475 break :msg msg;38544 break :msg msg;
38476 };38545 };
38477 return sema.failWithOwnedErrorMsg(&block, msg);38546 return sema.failWithOwnedErrorMsg(block, msg);
38478 }38547 }
38479 break :overflow false;38548 break :overflow false;
38480 } else overflow: {38549 } else overflow: {
...@@ -38487,9 +38556,9 @@ pub fn resolveDeclaredEnum(...@@ -38487,9 +38556,9 @@ pub fn resolveDeclaredEnum(
3848738556
38488 if (tag_overflow) {38557 if (tag_overflow) {
38489 const msg = try sema.errMsg(value_src, "enumeration value '{}' too large for type '{}'", .{38558 const msg = try sema.errMsg(value_src, "enumeration value '{}' too large for type '{}'", .{
38490 last_tag_val.?.fmtValueSema(pt, &sema), int_tag_ty.fmt(pt),38559 last_tag_val.?.fmtValueSema(pt, sema), int_tag_ty.fmt(pt),
38491 });38560 });
38492 return sema.failWithOwnedErrorMsg(&block, msg);38561 return sema.failWithOwnedErrorMsg(block, msg);
38493 }38562 }
38494 }38563 }
38495}38564}
src/Zcu/PerThread.zig+8-6
...@@ -3669,6 +3669,8 @@ pub fn navAlignment(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) InternPo...@@ -3669,6 +3669,8 @@ pub fn navAlignment(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) InternPo
3669/// If the type cannot be recreated because it has been lost, `error.AnalysisFail` is returned.3669/// If the type cannot be recreated because it has been lost, `error.AnalysisFail` is returned.
3670/// If `ty` is not outdated, that same `InternPool.Index` is returned.3670/// If `ty` is not outdated, that same `InternPool.Index` is returned.
3671/// If `ty` has already been replaced by this function, the new index will not be returned again.3671/// If `ty` has already been replaced by this function, the new index will not be returned again.
3672/// Also, if `ty` is an enum, this function will resolve the new type if needed, and the call site
3673/// is responsible for checking `[transitive_]failed_analysis` to detect resolution failures.
3672pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError!InternPool.Index {3674pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError!InternPool.Index {
3673 const zcu = pt.zcu;3675 const zcu = pt.zcu;
3674 const gpa = zcu.gpa;3676 const gpa = zcu.gpa;
...@@ -3878,12 +3880,14 @@ fn recreateUnionType(...@@ -3878,12 +3880,14 @@ fn recreateUnionType(
3878 return wip_ty.finish(ip, namespace_index);3880 return wip_ty.finish(ip, namespace_index);
3879}3881}
38803882
3881// TODO: is it safe for this to return `SemaError`? enum type resolution is a bit weird...3883/// This *does* call `Sema.resolveDeclaredEnum`, but errors from it are not propagated.
3884/// Call sites are resposible for checking `[transitive_]failed_analysis` after `ensureTypeUpToDate`
3885/// returns in order to detect resolution failures.
3882fn recreateEnumType(3886fn recreateEnumType(
3883 pt: Zcu.PerThread,3887 pt: Zcu.PerThread,
3884 old_ty: InternPool.Index,3888 old_ty: InternPool.Index,
3885 key: InternPool.Key.NamespaceType.Declared,3889 key: InternPool.Key.NamespaceType.Declared,
3886) Zcu.SemaError!InternPool.Index {3890) Allocator.Error!InternPool.Index {
3887 const zcu = pt.zcu;3891 const zcu = pt.zcu;
3888 const gpa = zcu.gpa;3892 const gpa = zcu.gpa;
3889 const ip = &zcu.intern_pool;3893 const ip = &zcu.intern_pool;
...@@ -3993,10 +3997,8 @@ fn recreateEnumType(...@@ -3993,10 +3997,8 @@ fn recreateEnumType(
3993 zir,3997 zir,
3994 body_end,3998 body_end,
3995 ) catch |err| switch (err) {3999 ) catch |err| switch (err) {
3996 error.GenericPoison => unreachable,4000 error.OutOfMemory => |e| return e,
3997 error.ComptimeBreak => unreachable,4001 error.AnalysisFail => {}, // call sites are responsible for checking `[transitive_]failed_analysis` to detect this
3998 error.ComptimeReturn => unreachable,
3999 error.AnalysisFail, error.OutOfMemory => |e| return e,
4000 };4002 };
40014003
4002 return wip_ty.index;4004 return wip_ty.index;
test/behavior/union.zig+19
...@@ -2303,3 +2303,22 @@ test "extern union @FieldType" {...@@ -2303,3 +2303,22 @@ test "extern union @FieldType" {
2303 comptime assert(@FieldType(U, "b") == f64);2303 comptime assert(@FieldType(U, "b") == f64);
2304 comptime assert(@FieldType(U, "c") == *U);2304 comptime assert(@FieldType(U, "c") == *U);
2305}2305}
2306
2307test "assign global tagged union" {
2308 const U = union(enum) {
2309 a: u16,
2310 b: u32,
2311
2312 var global: @This() = undefined;
2313 };
2314
2315 U.global = .{ .a = 123 };
2316 try expect(U.global == .a);
2317 try expect(U.global != .b);
2318 try expect(U.global.a == 123);
2319
2320 U.global = .{ .b = 123456 };
2321 try expect(U.global != .a);
2322 try expect(U.global == .b);
2323 try expect(U.global.b == 123456);
2324}
test/incremental/change_enum_tag_type created+59
...@@ -0,0 +1,59 @@
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 Tag = u2;
8const Foo = enum(Tag) {
9 a,
10 b,
11 c,
12 d,
13};
14pub fn main() !void {
15 var val: Foo = undefined;
16 val = .a;
17 try std.io.getStdOut().writer().print("{s}\n", .{@tagName(val)});
18}
19const std = @import("std");
20#expect_stdout="a\n"
21#update=too many enum fields
22#file=main.zig
23const Tag = u2;
24const Foo = enum(Tag) {
25 a,
26 b,
27 c,
28 d,
29 e,
30};
31pub fn main() !void {
32 var val: Foo = undefined;
33 val = .a;
34 try std.io.getStdOut().writer().print("{s}\n", .{@tagName(val)});
35}
36comptime {
37 // These can't be true at the same time; analysis should stop as soon as it sees `Foo`
38 std.debug.assert(@intFromEnum(Foo.e) == 4);
39 std.debug.assert(@TypeOf(@intFromEnum(Foo.e)) == Tag);
40}
41const std = @import("std");
42#expect_error=ignored
43#update=increase tag size
44#file=main.zig
45const Tag = u3;
46const Foo = enum(Tag) {
47 a,
48 b,
49 c,
50 d,
51 e,
52};
53pub fn main() !void {
54 var val: Foo = undefined;
55 val = .a;
56 try std.io.getStdOut().writer().print("{s}\n", .{@tagName(val)});
57}
58const std = @import("std");
59#expect_stdout="a\n"