| 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,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 | }, |
| 4336 | 4336 | ||
| 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 | }, |
| 4341 | 4341 | ||
| 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 | } |
| 268 | 268 | ||
| 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 | } |
| 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 | 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 | } |
| 293 | 293 | ||
| 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 resolution | 15339 | /// 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 asserts | 15340 | /// 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. |
| 15342 | fn typeHasOnePossibleValue( | 15342 | pub 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 { |
| 90 | fn getC(data: *const BitFieldOfEnums) C { | 90 | fn getC(data: *const BitFieldOfEnums) C { |
| 91 | return data.c; | 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,38 +2,6 @@ const expect = @import("std").testing.expect; |
| 2 | const mem = @import("std").mem; | 2 | const mem = @import("std").mem; |
| 3 | const Tag = @import("std").meta.Tag; | 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 | test "enum value allocation" { | 5 | test "enum value allocation" { |
| 38 | const LargeEnum = enum(u32) { | 6 | const LargeEnum = enum(u32) { |
| 39 | A0 = 0x80000000, | 7 | A0 = 0x80000000, |