Skip to content

Commit ebf782e

Browse files
authored
Merge pull request #22531 from mlugg/various-fixes
incremental, Sema: minor fixes
2 parents a5d2aaa + 3b6e5ba commit ebf782e

File tree

4 files changed

+177
-28
lines changed

4 files changed

+177
-28
lines changed

src/Sema.zig

Lines changed: 91 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3190,6 +3190,15 @@ fn zirEnumDecl(
31903190

31913191
try sema.declareDependency(.{ .interned = new_ty });
31923192
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+
31933202
return Air.internedToRef(new_ty);
31943203
},
31953204
.wip => |wip| wip,
@@ -17801,12 +17810,23 @@ fn zirThis(
1780117810
) CompileError!Air.Inst.Ref {
1780217811
_ = extended;
1780317812
const pt = sema.pt;
17813+
const zcu = pt.zcu;
1780417814
const namespace = pt.zcu.namespacePtr(block.namespace);
1780517815

1780617816
const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type);
1780717817

1780817818
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+
},
1781017830
.opaque_type => {},
1781117831
else => unreachable,
1781217832
}
@@ -28478,6 +28498,10 @@ fn unionFieldPtr(
2847828498
if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: {
2847928499
switch (union_obj.flagsUnordered(ip).layout) {
2848028500
.auto => if (initializing) {
28501+
if (!sema.isComptimeMutablePtr(union_ptr_val)) {
28502+
// The initialization is a runtime operation.
28503+
break :ct;
28504+
}
2848128505
// Store to the union to initialize the tag.
2848228506
const field_tag = try pt.enumValueFieldIndex(Type.fromInterned(union_obj.enum_tag_ty), enum_field_index);
2848328507
const payload_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
@@ -38330,30 +38354,24 @@ pub fn resolveDeclaredEnum(
3833038354
fields_len: u32,
3833138355
zir: Zir,
3833238356
body_end: usize,
38333-
) Zcu.CompileError!void {
38357+
) Zcu.SemaError!void {
3833438358
const zcu = pt.zcu;
3833538359
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

3834038361
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 });
38344-
38345-
var arena = std.heap.ArenaAllocator.init(gpa);
38363+
var arena: std.heap.ArenaAllocator = .init(gpa);
3834638364
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);
3834938367
defer comptime_err_ret_trace.deinit();
3835038368

3835138369
var sema: Sema = .{
3835238370
.pt = pt,
3835338371
.gpa = gpa,
3835438372
.arena = arena.allocator(),
3835538373
.code = zir,
38356-
.owner = anal_unit,
38374+
.owner = .wrap(.{ .type = wip_ty.index }),
3835738375
.func_index = .none,
3835838376
.func_is_naked = false,
3835938377
.fn_ret_ty = Type.void,
@@ -38379,15 +38397,66 @@ pub fn resolveDeclaredEnum(
3837938397
};
3838038398
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+
38427+
fn 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+
3838238451
const int_tag_ty = ty: {
3838338452
if (body.len != 0) {
38384-
_ = try sema.analyzeInlineBody(&block, body, inst);
38453+
_ = try sema.analyzeInlineBody(block, body, inst);
3838538454
}
3838638455

3838738456
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);
3838938458
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)});
3839138460
}
3839238461
break :ty ty;
3839338462
} else if (fields_len == 0) {
@@ -38402,7 +38471,7 @@ pub fn resolveDeclaredEnum(
3840238471

3840338472
if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) {
3840438473
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", .{});
3840638475
}
3840738476
}
3840838477

@@ -38434,7 +38503,7 @@ pub fn resolveDeclaredEnum(
3843438503
const tag_val_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
3843538504
extra_index += 1;
3843638505
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, .{
3843838507
.base_node_inst = tracked_inst,
3843938508
.offset = .{ .container_field_name = field_i },
3844038509
}, tag_inst, .{ .simple = .enum_field_tag_value });
@@ -38447,12 +38516,12 @@ pub fn resolveDeclaredEnum(
3844738516
.offset = .{ .container_field_value = conflict.prev_field_idx },
3844838517
};
3844938518
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)});
3845138520
errdefer msg.destroy(gpa);
3845238521
try sema.errNote(other_field_src, msg, "other occurrence here", .{});
3845338522
break :msg msg;
3845438523
};
38455-
return sema.failWithOwnedErrorMsg(&block, msg);
38524+
return sema.failWithOwnedErrorMsg(block, msg);
3845638525
}
3845738526
break :overflow false;
3845838527
} else if (any_values) overflow: {
@@ -38469,12 +38538,12 @@ pub fn resolveDeclaredEnum(
3846938538
.offset = .{ .container_field_value = conflict.prev_field_idx },
3847038539
};
3847138540
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)});
3847338542
errdefer msg.destroy(gpa);
3847438543
try sema.errNote(other_field_src, msg, "other occurrence here", .{});
3847538544
break :msg msg;
3847638545
};
38477-
return sema.failWithOwnedErrorMsg(&block, msg);
38546+
return sema.failWithOwnedErrorMsg(block, msg);
3847838547
}
3847938548
break :overflow false;
3848038549
} else overflow: {
@@ -38487,9 +38556,9 @@ pub fn resolveDeclaredEnum(
3848738556

3848838557
if (tag_overflow) {
3848938558
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),
3849138560
});
38492-
return sema.failWithOwnedErrorMsg(&block, msg);
38561+
return sema.failWithOwnedErrorMsg(block, msg);
3849338562
}
3849438563
}
3849538564
}

src/Zcu/PerThread.zig

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3669,6 +3669,8 @@ pub fn navAlignment(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) InternPo
36693669
/// If the type cannot be recreated because it has been lost, `error.AnalysisFail` is returned.
36703670
/// If `ty` is not outdated, that same `InternPool.Index` is returned.
36713671
/// 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.
36723674
pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError!InternPool.Index {
36733675
const zcu = pt.zcu;
36743676
const gpa = zcu.gpa;
@@ -3878,12 +3880,14 @@ fn recreateUnionType(
38783880
return wip_ty.finish(ip, namespace_index);
38793881
}
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.
38823886
fn recreateEnumType(
38833887
pt: Zcu.PerThread,
38843888
old_ty: InternPool.Index,
38853889
key: InternPool.Key.NamespaceType.Declared,
3886-
) Zcu.SemaError!InternPool.Index {
3890+
) Allocator.Error!InternPool.Index {
38873891
const zcu = pt.zcu;
38883892
const gpa = zcu.gpa;
38893893
const ip = &zcu.intern_pool;
@@ -3993,10 +3997,8 @@ fn recreateEnumType(
39933997
zir,
39943998
body_end,
39953999
) catch |err| switch (err) {
3996-
error.GenericPoison => unreachable,
3997-
error.ComptimeBreak => unreachable,
3998-
error.ComptimeReturn => unreachable,
3999-
error.AnalysisFail, error.OutOfMemory => |e| return e,
4000+
error.OutOfMemory => |e| return e,
4001+
error.AnalysisFail => {}, // call sites are responsible for checking `[transitive_]failed_analysis` to detect this
40004002
};
40014003

40024004
return wip_ty.index;

test/behavior/union.zig

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2303,3 +2303,22 @@ test "extern union @FieldType" {
23032303
comptime assert(@FieldType(U, "b") == f64);
23042304
comptime assert(@FieldType(U, "c") == *U);
23052305
}
2306+
2307+
test "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+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
7+
const Tag = u2;
8+
const Foo = enum(Tag) {
9+
a,
10+
b,
11+
c,
12+
d,
13+
};
14+
pub fn main() !void {
15+
var val: Foo = undefined;
16+
val = .a;
17+
try std.io.getStdOut().writer().print("{s}\n", .{@tagName(val)});
18+
}
19+
const std = @import("std");
20+
#expect_stdout="a\n"
21+
#update=too many enum fields
22+
#file=main.zig
23+
const Tag = u2;
24+
const Foo = enum(Tag) {
25+
a,
26+
b,
27+
c,
28+
d,
29+
e,
30+
};
31+
pub fn main() !void {
32+
var val: Foo = undefined;
33+
val = .a;
34+
try std.io.getStdOut().writer().print("{s}\n", .{@tagName(val)});
35+
}
36+
comptime {
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+
}
41+
const std = @import("std");
42+
#expect_error=ignored
43+
#update=increase tag size
44+
#file=main.zig
45+
const Tag = u3;
46+
const Foo = enum(Tag) {
47+
a,
48+
b,
49+
c,
50+
d,
51+
e,
52+
};
53+
pub fn main() !void {
54+
var val: Foo = undefined;
55+
val = .a;
56+
try std.io.getStdOut().writer().print("{s}\n", .{@tagName(val)});
57+
}
58+
const std = @import("std");
59+
#expect_stdout="a\n"

0 commit comments

Comments
 (0)