Commit 9dd4fb4130

Andrew Kelley <andrew@ziglang.org>
2021-12-28 01:56:33
stage2: fix 0-bit function parameters
Before this commit, Zig would incorrectly emit `arg` AIR instructions for parameters whose types were 0-bit.
1 parent 886df77
Changed files (4)
src/Module.zig
@@ -4327,16 +4327,16 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem
     var runtime_param_index: usize = 0;
     var total_param_index: usize = 0;
     for (fn_info.param_body) |inst| {
-        const name = switch (zir_tags[inst]) {
+        const param: struct { name: u32, src: LazySrcLoc } = switch (zir_tags[inst]) {
             .param, .param_comptime => blk: {
-                const inst_data = sema.code.instructions.items(.data)[inst].pl_tok;
-                const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index).data;
-                break :blk extra.name;
+                const pl_tok = sema.code.instructions.items(.data)[inst].pl_tok;
+                const extra = sema.code.extraData(Zir.Inst.Param, pl_tok.payload_index).data;
+                break :blk .{ .name = extra.name, .src = pl_tok.src() };
             },
 
             .param_anytype, .param_anytype_comptime => blk: {
                 const str_tok = sema.code.instructions.items(.data)[inst].str_tok;
-                break :blk str_tok.start;
+                break :blk .{ .name = str_tok.start, .src = str_tok.src() };
             },
 
             else => continue,
@@ -4352,6 +4352,18 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem
             }
         }
         const param_type = fn_ty.fnParamType(runtime_param_index);
+        const opt_opv = sema.typeHasOnePossibleValue(&inner_block, param.src, param_type) catch |err| switch (err) {
+            error.NeededSourceLocation => unreachable,
+            error.GenericPoison => unreachable,
+            error.ComptimeReturn => unreachable,
+            else => |e| return e,
+        };
+        if (opt_opv) |opv| {
+            const arg = try sema.addConstant(param_type, opv);
+            sema.inst_map.putAssumeCapacityNoClobber(inst, arg);
+            total_param_index += 1;
+            continue;
+        }
         const ty_ref = try sema.addType(param_type);
         const arg_index = @intCast(u32, sema.air_instructions.len);
         inner_block.instructions.appendAssumeCapacity(arg_index);
@@ -4359,7 +4371,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem
             .tag = .arg,
             .data = .{ .ty_str = .{
                 .ty = ty_ref,
-                .str = name,
+                .str = param.name,
             } },
         });
         sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index));
src/Sema.zig
@@ -266,7 +266,7 @@ pub const Block = struct {
         });
     }
 
-    pub fn addBinOp(
+    fn addBinOp(
         block: *Block,
         tag: Air.Inst.Tag,
         lhs: Air.Inst.Ref,
@@ -281,7 +281,7 @@ pub const Block = struct {
         });
     }
 
-    pub fn addArg(block: *Block, ty: Type, name: u32) error{OutOfMemory}!Air.Inst.Ref {
+    fn addArg(block: *Block, ty: Type, name: u32) error{OutOfMemory}!Air.Inst.Ref {
         return block.addInst(.{
             .tag = .arg,
             .data = .{ .ty_str = .{
@@ -291,7 +291,7 @@ pub const Block = struct {
         });
     }
 
-    pub fn addStructFieldPtr(
+    fn addStructFieldPtr(
         block: *Block,
         struct_ptr: Air.Inst.Ref,
         field_index: u32,
@@ -15339,7 +15339,7 @@ fn getBuiltinType(
 /// in `Sema` is for calling during semantic analysis, and performs field resolution
 /// to get the answer. The one in `Type` is for calling during codegen and asserts
 /// that the types are already resolved.
-fn typeHasOnePossibleValue(
+pub fn typeHasOnePossibleValue(
     sema: *Sema,
     block: *Block,
     src: LazySrcLoc,
test/behavior/enum_llvm.zig
@@ -90,3 +90,35 @@ fn getB(data: *const BitFieldOfEnums) B {
 fn getC(data: *const BitFieldOfEnums) C {
     return data.c;
 }
+
+const EnumWithOneMember = enum { Eof };
+
+fn doALoopThing(id: EnumWithOneMember) void {
+    while (true) {
+        if (id == EnumWithOneMember.Eof) {
+            break;
+        }
+        @compileError("above if condition should be comptime");
+    }
+}
+
+test "comparison operator on enum with one member is comptime known" {
+    doALoopThing(EnumWithOneMember.Eof);
+}
+
+const State = enum { Start };
+test "switch on enum with one member is comptime known" {
+    var state = State.Start;
+    switch (state) {
+        State.Start => return,
+    }
+    @compileError("analysis should not reach here");
+}
+
+test "enum literal in array literal" {
+    const Items = enum { one, two };
+    const array = [_]Items{ .one, .two };
+
+    try expect(array[0] == .one);
+    try expect(array[1] == .two);
+}
test/behavior/enum_stage1.zig
@@ -2,38 +2,6 @@ const expect = @import("std").testing.expect;
 const mem = @import("std").mem;
 const Tag = @import("std").meta.Tag;
 
-const EnumWithOneMember = enum { Eof };
-
-fn doALoopThing(id: EnumWithOneMember) void {
-    while (true) {
-        if (id == EnumWithOneMember.Eof) {
-            break;
-        }
-        @compileError("above if condition should be comptime");
-    }
-}
-
-test "comparison operator on enum with one member is comptime known" {
-    doALoopThing(EnumWithOneMember.Eof);
-}
-
-const State = enum { Start };
-test "switch on enum with one member is comptime known" {
-    var state = State.Start;
-    switch (state) {
-        State.Start => return,
-    }
-    @compileError("analysis should not reach here");
-}
-
-test "enum literal in array literal" {
-    const Items = enum { one, two };
-    const array = [_]Items{ .one, .two };
-
-    try expect(array[0] == .one);
-    try expect(array[1] == .two);
-}
-
 test "enum value allocation" {
     const LargeEnum = enum(u32) {
         A0 = 0x80000000,