authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-27 17:56:33-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-27 17:56:33-07:00
log9dd4fb4130a71d2aaaaa361d8ee0b7135cb84162
tree569ebd4a536b1df12976d6c6c19e3725ef6b7e98
parent886df772f06377df95f867e8b18ee47bbd0fcd8b

stage2: fix 0-bit function parameters

Before this commit, Zig would incorrectly emit `arg` AIR instructions for parameters whose types were 0-bit.

4 files changed, 54 insertions(+), 42 deletions(-)

src/Module.zig+18-6
...@@ -4327,16 +4327,16 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem...@@ -4327,16 +4327,16 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem
4327 var runtime_param_index: usize = 0;4327 var runtime_param_index: usize = 0;
4328 var total_param_index: usize = 0;4328 var total_param_index: usize = 0;
4329 for (fn_info.param_body) |inst| {4329 for (fn_info.param_body) |inst| {
4330 const name = switch (zir_tags[inst]) {4330 const param: struct { name: u32, src: LazySrcLoc } = switch (zir_tags[inst]) {
4331 .param, .param_comptime => blk: {4331 .param, .param_comptime => blk: {
4332 const inst_data = sema.code.instructions.items(.data)[inst].pl_tok;4332 const pl_tok = sema.code.instructions.items(.data)[inst].pl_tok;
4333 const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index).data;4333 const extra = sema.code.extraData(Zir.Inst.Param, pl_tok.payload_index).data;
4334 break :blk extra.name;4334 break :blk .{ .name = extra.name, .src = pl_tok.src() };
4335 },4335 },
43364336
4337 .param_anytype, .param_anytype_comptime => blk: {4337 .param_anytype, .param_anytype_comptime => blk: {
4338 const str_tok = sema.code.instructions.items(.data)[inst].str_tok;4338 const str_tok = sema.code.instructions.items(.data)[inst].str_tok;
4339 break :blk str_tok.start;4339 break :blk .{ .name = str_tok.start, .src = str_tok.src() };
4340 },4340 },
43414341
4342 else => continue,4342 else => continue,
...@@ -4352,6 +4352,18 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem...@@ -4352,6 +4352,18 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem
4352 }4352 }
4353 }4353 }
4354 const param_type = fn_ty.fnParamType(runtime_param_index);4354 const param_type = fn_ty.fnParamType(runtime_param_index);
4355 const opt_opv = sema.typeHasOnePossibleValue(&inner_block, param.src, param_type) catch |err| switch (err) {
4356 error.NeededSourceLocation => unreachable,
4357 error.GenericPoison => unreachable,
4358 error.ComptimeReturn => unreachable,
4359 else => |e| return e,
4360 };
4361 if (opt_opv) |opv| {
4362 const arg = try sema.addConstant(param_type, opv);
4363 sema.inst_map.putAssumeCapacityNoClobber(inst, arg);
4364 total_param_index += 1;
4365 continue;
4366 }
4355 const ty_ref = try sema.addType(param_type);4367 const ty_ref = try sema.addType(param_type);
4356 const arg_index = @intCast(u32, sema.air_instructions.len);4368 const arg_index = @intCast(u32, sema.air_instructions.len);
4357 inner_block.instructions.appendAssumeCapacity(arg_index);4369 inner_block.instructions.appendAssumeCapacity(arg_index);
...@@ -4359,7 +4371,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem...@@ -4359,7 +4371,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem
4359 .tag = .arg,4371 .tag = .arg,
4360 .data = .{ .ty_str = .{4372 .data = .{ .ty_str = .{
4361 .ty = ty_ref,4373 .ty = ty_ref,
4362 .str = name,4374 .str = param.name,
4363 } },4375 } },
4364 });4376 });
4365 sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index));4377 sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index));
src/Sema.zig+4-4
...@@ -266,7 +266,7 @@ pub const Block = struct {...@@ -266,7 +266,7 @@ pub const Block = struct {
266 });266 });
267 }267 }
268268
269 pub fn addBinOp(269 fn addBinOp(
270 block: *Block,270 block: *Block,
271 tag: Air.Inst.Tag,271 tag: Air.Inst.Tag,
272 lhs: Air.Inst.Ref,272 lhs: Air.Inst.Ref,
...@@ -281,7 +281,7 @@ pub const Block = struct {...@@ -281,7 +281,7 @@ pub const Block = struct {
281 });281 });
282 }282 }
283283
284 pub fn addArg(block: *Block, ty: Type, name: u32) error{OutOfMemory}!Air.Inst.Ref {284 fn addArg(block: *Block, ty: Type, name: u32) error{OutOfMemory}!Air.Inst.Ref {
285 return block.addInst(.{285 return block.addInst(.{
286 .tag = .arg,286 .tag = .arg,
287 .data = .{ .ty_str = .{287 .data = .{ .ty_str = .{
...@@ -291,7 +291,7 @@ pub const Block = struct {...@@ -291,7 +291,7 @@ pub const Block = struct {
291 });291 });
292 }292 }
293293
294 pub fn addStructFieldPtr(294 fn addStructFieldPtr(
295 block: *Block,295 block: *Block,
296 struct_ptr: Air.Inst.Ref,296 struct_ptr: Air.Inst.Ref,
297 field_index: u32,297 field_index: u32,
...@@ -15339,7 +15339,7 @@ fn getBuiltinType(...@@ -15339,7 +15339,7 @@ fn getBuiltinType(
15339/// in `Sema` is for calling during semantic analysis, and performs field resolution15339/// in `Sema` is for calling during semantic analysis, and performs field resolution
15340/// to get the answer. The one in `Type` is for calling during codegen and asserts15340/// to get the answer. The one in `Type` is for calling during codegen and asserts
15341/// that the types are already resolved.15341/// that the types are already resolved.
15342fn typeHasOnePossibleValue(15342pub fn typeHasOnePossibleValue(
15343 sema: *Sema,15343 sema: *Sema,
15344 block: *Block,15344 block: *Block,
15345 src: LazySrcLoc,15345 src: LazySrcLoc,
test/behavior/enum_llvm.zig+32
...@@ -90,3 +90,35 @@ fn getB(data: *const BitFieldOfEnums) B {...@@ -90,3 +90,35 @@ fn getB(data: *const BitFieldOfEnums) B {
90fn getC(data: *const BitFieldOfEnums) C {90fn getC(data: *const BitFieldOfEnums) C {
91 return data.c;91 return data.c;
92}92}
93
94const EnumWithOneMember = enum { Eof };
95
96fn doALoopThing(id: EnumWithOneMember) void {
97 while (true) {
98 if (id == EnumWithOneMember.Eof) {
99 break;
100 }
101 @compileError("above if condition should be comptime");
102 }
103}
104
105test "comparison operator on enum with one member is comptime known" {
106 doALoopThing(EnumWithOneMember.Eof);
107}
108
109const State = enum { Start };
110test "switch on enum with one member is comptime known" {
111 var state = State.Start;
112 switch (state) {
113 State.Start => return,
114 }
115 @compileError("analysis should not reach here");
116}
117
118test "enum literal in array literal" {
119 const Items = enum { one, two };
120 const array = [_]Items{ .one, .two };
121
122 try expect(array[0] == .one);
123 try expect(array[1] == .two);
124}
test/behavior/enum_stage1.zig-32
...@@ -2,38 +2,6 @@ const expect = @import("std").testing.expect;...@@ -2,38 +2,6 @@ const expect = @import("std").testing.expect;
2const mem = @import("std").mem;2const mem = @import("std").mem;
3const Tag = @import("std").meta.Tag;3const Tag = @import("std").meta.Tag;
44
5const EnumWithOneMember = enum { Eof };
6
7fn doALoopThing(id: EnumWithOneMember) void {
8 while (true) {
9 if (id == EnumWithOneMember.Eof) {
10 break;
11 }
12 @compileError("above if condition should be comptime");
13 }
14}
15
16test "comparison operator on enum with one member is comptime known" {
17 doALoopThing(EnumWithOneMember.Eof);
18}
19
20const State = enum { Start };
21test "switch on enum with one member is comptime known" {
22 var state = State.Start;
23 switch (state) {
24 State.Start => return,
25 }
26 @compileError("analysis should not reach here");
27}
28
29test "enum literal in array literal" {
30 const Items = enum { one, two };
31 const array = [_]Items{ .one, .two };
32
33 try expect(array[0] == .one);
34 try expect(array[1] == .two);
35}
36
37test "enum value allocation" {5test "enum value allocation" {
38 const LargeEnum = enum(u32) {6 const LargeEnum = enum(u32) {
39 A0 = 0x80000000,7 A0 = 0x80000000,