| author | |
| committer | |
| log | 9dd4fb4130a71d2aaaaa361d8ee0b7135cb84162 |
| tree | 569ebd4a536b1df12976d6c6c19e3725ef6b7e98 |
| parent | 886df772f06377df95f867e8b18ee47bbd0fcd8b |
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 | 4327 | var runtime_param_index: usize = 0; |
| 4328 | 4328 | var total_param_index: usize = 0; |
| 4329 | 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 | 4331 | .param, .param_comptime => blk: { |
| 4332 | const inst_data = sema.code.instructions.items(.data)[inst].pl_tok; | |
| 4333 | const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index).data; | |
| 4334 | break :blk extra.name; | |
| 4332 | const pl_tok = sema.code.instructions.items(.data)[inst].pl_tok; | |
| 4333 | const extra = sema.code.extraData(Zir.Inst.Param, pl_tok.payload_index).data; | |
| 4334 | break :blk .{ .name = extra.name, .src = pl_tok.src() }; | |
| 4335 | 4335 | }, |
| 4336 | 4336 | |
| 4337 | 4337 | .param_anytype, .param_anytype_comptime => blk: { |
| 4338 | 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 | }, |
| 4341 | 4341 | |
| 4342 | 4342 | else => continue, |
| ... | ... | @@ -4352,6 +4352,18 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem |
| 4352 | 4352 | } |
| 4353 | 4353 | } |
| 4354 | 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 | 4367 | const ty_ref = try sema.addType(param_type); |
| 4356 | 4368 | const arg_index = @intCast(u32, sema.air_instructions.len); |
| 4357 | 4369 | inner_block.instructions.appendAssumeCapacity(arg_index); |
| ... | ... | @@ -4359,7 +4371,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem |
| 4359 | 4371 | .tag = .arg, |
| 4360 | 4372 | .data = .{ .ty_str = .{ |
| 4361 | 4373 | .ty = ty_ref, |
| 4362 | .str = name, | |
| 4374 | .str = param.name, | |
| 4363 | 4375 | } }, |
| 4364 | 4376 | }); |
| 4365 | 4377 | sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index)); |
src/Sema.zig+4-4| ... | ... | @@ -266,7 +266,7 @@ pub const Block = struct { |
| 266 | 266 | }); |
| 267 | 267 | } |
| 268 | 268 | |
| 269 | pub fn addBinOp( | |
| 269 | fn addBinOp( | |
| 270 | 270 | block: *Block, |
| 271 | 271 | tag: Air.Inst.Tag, |
| 272 | 272 | lhs: Air.Inst.Ref, |
| ... | ... | @@ -281,7 +281,7 @@ pub const Block = struct { |
| 281 | 281 | }); |
| 282 | 282 | } |
| 283 | 283 | |
| 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 | 285 | return block.addInst(.{ |
| 286 | 286 | .tag = .arg, |
| 287 | 287 | .data = .{ .ty_str = .{ |
| ... | ... | @@ -291,7 +291,7 @@ pub const Block = struct { |
| 291 | 291 | }); |
| 292 | 292 | } |
| 293 | 293 | |
| 294 | pub fn addStructFieldPtr( | |
| 294 | fn addStructFieldPtr( | |
| 295 | 295 | block: *Block, |
| 296 | 296 | struct_ptr: Air.Inst.Ref, |
| 297 | 297 | field_index: u32, |
| ... | ... | @@ -15339,7 +15339,7 @@ fn getBuiltinType( |
| 15339 | 15339 | /// in `Sema` is for calling during semantic analysis, and performs field resolution |
| 15340 | 15340 | /// to get the answer. The one in `Type` is for calling during codegen and asserts |
| 15341 | 15341 | /// that the types are already resolved. |
| 15342 | fn typeHasOnePossibleValue( | |
| 15342 | pub fn typeHasOnePossibleValue( | |
| 15343 | 15343 | sema: *Sema, |
| 15344 | 15344 | block: *Block, |
| 15345 | 15345 | src: LazySrcLoc, |
test/behavior/enum_llvm.zig+32| ... | ... | @@ -90,3 +90,35 @@ fn getB(data: *const BitFieldOfEnums) B { |
| 90 | 90 | fn getC(data: *const BitFieldOfEnums) C { |
| 91 | 91 | return data.c; |
| 92 | 92 | } |
| 93 | ||
| 94 | const EnumWithOneMember = enum { Eof }; | |
| 95 | ||
| 96 | fn 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 | ||
| 105 | test "comparison operator on enum with one member is comptime known" { | |
| 106 | doALoopThing(EnumWithOneMember.Eof); | |
| 107 | } | |
| 108 | ||
| 109 | const State = enum { Start }; | |
| 110 | test "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 | ||
| 118 | test "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 | 2 | const mem = @import("std").mem; |
| 3 | 3 | const Tag = @import("std").meta.Tag; |
| 4 | 4 | |
| 5 | const EnumWithOneMember = enum { Eof }; | |
| 6 | ||
| 7 | fn 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 | ||
| 16 | test "comparison operator on enum with one member is comptime known" { | |
| 17 | doALoopThing(EnumWithOneMember.Eof); | |
| 18 | } | |
| 19 | ||
| 20 | const State = enum { Start }; | |
| 21 | test "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 | ||
| 29 | test "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 | ||
| 37 | 5 | test "enum value allocation" { |
| 38 | 6 | const LargeEnum = enum(u32) { |
| 39 | 7 | A0 = 0x80000000, |