Commit 6ea3d3e19f

Veikka Tuominen <git@vexu.eu>
2022-11-07 13:17:26
Merge pull request #13446 from Vexu/stage2-fixes
Stage2 bug fixes
1 parent 6a94bcb
src/Sema.zig
@@ -1917,6 +1917,7 @@ fn resolveMaybeUndefValAllowVariablesMaybeRuntime(
             const ty_pl = sema.air_instructions.items(.data)[i].ty_pl;
             const val = sema.air_values.items[ty_pl.payload];
             if (val.tag() == .runtime_value) make_runtime.* = true;
+            if (val.isPtrToThreadLocal(sema.mod)) make_runtime.* = true;
             return val;
         },
         .const_ty => {
@@ -4380,7 +4381,7 @@ fn zirValidateArrayInit(
         var block_index = block.instructions.items.len - 1;
         while (block.instructions.items[block_index] != elem_ptr_air_inst) {
             if (block_index == 0) {
-                array_is_comptime = true;
+                array_is_comptime = false;
                 continue :outer;
             }
             block_index -= 1;
@@ -10343,6 +10344,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
         }
         if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, special.body, operand);
         if (special.is_inline) child_block.inline_case_capture = operand;
+        if (empty_enum) {
+            return Air.Inst.Ref.void_value;
+        }
         return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);
     }
 
@@ -11992,10 +11996,18 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             const element_vals = try sema.arena.alloc(Value, final_len_including_sent);
             var elem_i: usize = 0;
             while (elem_i < lhs_len) : (elem_i += 1) {
-                element_vals[elem_i] = try lhs_sub_val.elemValue(sema.mod, sema.arena, elem_i);
+                const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, elem_i);
+                const elem_val_inst = try sema.addConstant(lhs_info.elem_type, elem_val);
+                const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded);
+                const coereced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, "");
+                element_vals[elem_i] = coereced_elem_val;
             }
             while (elem_i < result_len) : (elem_i += 1) {
-                element_vals[elem_i] = try rhs_sub_val.elemValue(sema.mod, sema.arena, elem_i - lhs_len);
+                const elem_val = try rhs_sub_val.elemValue(sema.mod, sema.arena, elem_i - lhs_len);
+                const elem_val_inst = try sema.addConstant(lhs_info.elem_type, elem_val);
+                const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded);
+                const coereced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, "");
+                element_vals[elem_i] = coereced_elem_val;
             }
             if (res_sent_val) |sent_val| {
                 element_vals[result_len] = sent_val;
@@ -12469,10 +12481,12 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
 
             if (maybe_rhs_val) |rhs_val| {
                 if (is_int) {
-                    return sema.addConstant(
-                        resolved_type,
-                        try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
-                    );
+                    const res = try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target);
+                    var vector_index: usize = undefined;
+                    if (!(try sema.intFitsInType(block, src, res, resolved_type, &vector_index))) {
+                        return sema.failWithIntegerOverflow(block, src, resolved_type, res, vector_index);
+                    }
+                    return sema.addConstant(resolved_type, res);
                 } else {
                     return sema.addConstant(
                         resolved_type,
@@ -12584,10 +12598,12 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                     if (modulus_val.compareWithZero(.neq)) {
                         return sema.fail(block, src, "exact division produced remainder", .{});
                     }
-                    return sema.addConstant(
-                        resolved_type,
-                        try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
-                    );
+                    const res = try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target);
+                    var vector_index: usize = undefined;
+                    if (!(try sema.intFitsInType(block, src, res, resolved_type, &vector_index))) {
+                        return sema.failWithIntegerOverflow(block, src, resolved_type, res, vector_index);
+                    }
+                    return sema.addConstant(resolved_type, res);
                 } else {
                     const modulus_val = try lhs_val.floatMod(rhs_val, resolved_type, sema.arena, target);
                     if (modulus_val.compareWithZero(.neq)) {
@@ -12862,10 +12878,12 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 
             if (maybe_rhs_val) |rhs_val| {
                 if (is_int) {
-                    return sema.addConstant(
-                        resolved_type,
-                        try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
-                    );
+                    const res = try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target);
+                    var vector_index: usize = undefined;
+                    if (!(try sema.intFitsInType(block, src, res, resolved_type, &vector_index))) {
+                        return sema.failWithIntegerOverflow(block, src, resolved_type, res, vector_index);
+                    }
+                    return sema.addConstant(resolved_type, res);
                 } else {
                     return sema.addConstant(
                         resolved_type,
@@ -22457,7 +22475,10 @@ fn fieldVal(
                     } else (try sema.mod.getErrorValue(field_name)).key;
 
                     return sema.addConstant(
-                        try child_type.copy(arena),
+                        if (!child_type.isAnyError())
+                            try child_type.copy(arena)
+                        else
+                            try Type.Tag.error_set_single.create(arena, name),
                         try Value.Tag.@"error".create(arena, .{ .name = name }),
                     );
                 },
@@ -22668,7 +22689,10 @@ fn fieldPtr(
                     var anon_decl = try block.startAnonDecl(src);
                     defer anon_decl.deinit();
                     return sema.analyzeDeclRef(try anon_decl.finish(
-                        try child_type.copy(anon_decl.arena()),
+                        if (!child_type.isAnyError())
+                            try child_type.copy(anon_decl.arena())
+                        else
+                            try Type.Tag.error_set_single.create(anon_decl.arena(), name),
                         try Value.Tag.@"error".create(anon_decl.arena(), .{ .name = name }),
                         0, // default alignment
                     ));
@@ -22827,6 +22851,7 @@ fn fieldCallBind(
                     {
                         const first_param_type = decl_type.fnParamType(0);
                         const first_param_tag = first_param_type.tag();
+                        var opt_buf: Type.Payload.ElemType = undefined;
                         // zig fmt: off
                         if (first_param_tag == .var_args_param or
                             first_param_tag == .generic_poison or (
@@ -22845,7 +22870,27 @@ fn fieldCallBind(
                             });
                             return sema.addConstant(ty, value);
                         } else if (first_param_type.eql(concrete_ty, sema.mod)) {
-                            var deref = try sema.analyzeLoad(block, src, object_ptr, src);
+                            const deref = try sema.analyzeLoad(block, src, object_ptr, src);
+                            const ty = Type.Tag.bound_fn.init();
+                            const value = try Value.Tag.bound_fn.create(arena, .{
+                                .func_inst = decl_val,
+                                .arg0_inst = deref,
+                            });
+                            return sema.addConstant(ty, value);
+                        } else if (first_param_tag != .generic_poison and first_param_type.zigTypeTag() == .Optional and
+                            first_param_type.optionalChild(&opt_buf).eql(concrete_ty, sema.mod))
+                        {
+                            const deref = try sema.analyzeLoad(block, src, object_ptr, src);
+                            const ty = Type.Tag.bound_fn.init();
+                            const value = try Value.Tag.bound_fn.create(arena, .{
+                                .func_inst = decl_val,
+                                .arg0_inst = deref,
+                            });
+                            return sema.addConstant(ty, value);
+                        } else if (first_param_tag != .generic_poison and first_param_type.zigTypeTag() == .ErrorUnion and
+                            first_param_type.errorUnionPayload().eql(concrete_ty, sema.mod))
+                        {
+                            const deref = try sema.analyzeLoad(block, src, object_ptr, src);
                             const ty = Type.Tag.bound_fn.init();
                             const value = try Value.Tag.bound_fn.create(arena, .{
                                 .func_inst = decl_val,
@@ -28763,6 +28808,13 @@ fn resolvePeerTypes(
                             }
                         }
                     },
+                    .Fn => {
+                        if (!cand_info.mutable and cand_info.pointee_type.zigTypeTag() == .Fn and .ok == try sema.coerceInMemoryAllowedFns(block, chosen_ty, cand_info.pointee_type, target, src, src)) {
+                            chosen = candidate;
+                            chosen_i = candidate_i + 1;
+                            continue;
+                        }
+                    },
                     else => {},
                 }
             },
@@ -28793,6 +28845,11 @@ fn resolvePeerTypes(
                 .Vector => continue,
                 else => {},
             },
+            .Fn => if (chosen_ty.isSinglePointer() and chosen_ty.isConstPtr() and chosen_ty.childType().zigTypeTag() == .Fn) {
+                if (.ok == try sema.coerceInMemoryAllowedFns(block, chosen_ty.childType(), candidate_ty, target, src, src)) {
+                    continue;
+                }
+            },
             else => {},
         }
 
@@ -30440,23 +30497,23 @@ pub fn typeHasOnePossibleValue(
             if (enum_obj.tag_ty.hasRuntimeBits()) {
                 return null;
             }
-            if (enum_obj.fields.count() == 1) {
-                if (enum_obj.values.count() == 0) {
+            switch (enum_obj.fields.count()) {
+                0 => return Value.initTag(.unreachable_value),
+                1 => if (enum_obj.values.count() == 0) {
                     return Value.zero; // auto-numbered
                 } else {
                     return enum_obj.values.keys()[0];
-                }
-            } else {
-                return null;
+                },
+                else => return null,
             }
         },
         .enum_simple => {
             const resolved_ty = try sema.resolveTypeFields(block, src, ty);
             const enum_simple = resolved_ty.castTag(.enum_simple).?.data;
-            if (enum_simple.fields.count() == 1) {
-                return Value.zero;
-            } else {
-                return null;
+            switch (enum_simple.fields.count()) {
+                0 => return Value.initTag(.unreachable_value),
+                1 => return Value.zero,
+                else => return null,
             }
         },
         .enum_nonexhaustive => {
@@ -30473,7 +30530,7 @@ pub fn typeHasOnePossibleValue(
             const tag_val = (try sema.typeHasOnePossibleValue(block, src, union_obj.tag_ty)) orelse
                 return null;
             const fields = union_obj.fields.values();
-            if (fields.len == 0) return Value.initTag(.empty_struct_value);
+            if (fields.len == 0) return Value.initTag(.unreachable_value);
             const only_field = fields[0];
             if (only_field.ty.eql(resolved_ty, sema.mod)) {
                 const msg = try Module.ErrorMsg.create(
@@ -32036,15 +32093,36 @@ fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type {
     const ptr_info = ptr_ty.ptrInfo().data;
     const elem_ty = ptr_ty.elemType2();
     const allow_zero = ptr_info.@"allowzero" and (offset orelse 0) == 0;
+    const target = sema.mod.getTarget();
+    const parent_ty = ptr_ty.childType();
+
+    const vector_info: struct {
+        host_size: u16,
+        bit_offset: u16,
+        alignment: u32,
+    } = if (parent_ty.tag() == .vector) blk: {
+        const elem_bits = elem_ty.bitSize(target);
+        const is_packed = elem_bits != 0 and (elem_bits & (elem_bits - 1)) != 0;
+        // TODO: runtime-known index
+        assert(!is_packed or offset != null);
+        const is_packed_with_offset = is_packed and offset != null and offset.? != 0;
+        const target_offset = if (is_packed_with_offset) (if (target.cpu.arch.endian() == .Big) (parent_ty.vectorLen() - 1 - offset.?) else offset.?) else 0;
+        break :blk .{
+            .host_size = if (is_packed_with_offset) @intCast(u16, parent_ty.abiSize(target)) else 0,
+            .bit_offset = if (is_packed_with_offset) @intCast(u16, elem_bits * target_offset) else 0,
+            .alignment = if (is_packed_with_offset) @intCast(u16, parent_ty.abiAlignment(target)) else 0,
+        };
+    } else .{ .host_size = 0, .bit_offset = 0, .alignment = 0 };
+
     const alignment: u32 = a: {
         // Calculate the new pointer alignment.
         if (ptr_info.@"align" == 0) {
+            if (vector_info.alignment != 0) break :a vector_info.alignment;
             // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness.
             break :a 0;
         }
         // If the addend is not a comptime-known value we can still count on
         // it being a multiple of the type size.
-        const target = sema.mod.getTarget();
         const elem_size = elem_ty.abiSize(target);
         const addend = if (offset) |off| elem_size * off else elem_size;
 
@@ -32061,5 +32139,7 @@ fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type {
         .@"allowzero" = allow_zero,
         .@"volatile" = ptr_info.@"volatile",
         .@"align" = alignment,
+        .host_size = vector_info.host_size,
+        .bit_offset = vector_info.bit_offset,
     });
 }
src/type.zig
@@ -3574,15 +3574,13 @@ pub const Type = extern union {
             .u128, .i128, .f128 => return 128,
 
             .@"struct" => {
-                if (sema_kit) |sk| _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty);
-                if (ty.containerLayout() != .Packed) {
+                const struct_obj = ty.castTag(.@"struct").?.data;
+                if (struct_obj.layout != .Packed) {
                     return (try ty.abiSizeAdvanced(target, if (sema_kit) |sk| .{ .sema_kit = sk } else .eager)).scalar * 8;
                 }
-                var total: u64 = 0;
-                for (ty.structFields().values()) |field| {
-                    total += try bitSizeAdvanced(field.ty, target, sema_kit);
-                }
-                return total;
+                if (sema_kit) |sk| _ = try sk.sema.resolveTypeLayout(sk.block, sk.src, ty);
+                assert(struct_obj.haveLayout());
+                return try struct_obj.backing_int_ty.bitSizeAdvanced(target, sema_kit);
             },
 
             .tuple, .anon_struct => {
@@ -5015,22 +5013,22 @@ pub const Type = extern union {
                 if (enum_full.tag_ty.hasRuntimeBits()) {
                     return null;
                 }
-                if (enum_full.fields.count() == 1) {
-                    if (enum_full.values.count() == 0) {
-                        return Value.zero;
+                switch (enum_full.fields.count()) {
+                    0 => return Value.initTag(.unreachable_value),
+                    1 => if (enum_full.values.count() == 0) {
+                        return Value.zero; // auto-numbered
                     } else {
                         return enum_full.values.keys()[0];
-                    }
-                } else {
-                    return null;
+                    },
+                    else => return null,
                 }
             },
             .enum_simple => {
                 const enum_simple = ty.castTag(.enum_simple).?.data;
-                if (enum_simple.fields.count() == 1) {
-                    return Value.zero;
-                } else {
-                    return null;
+                switch (enum_simple.fields.count()) {
+                    0 => return Value.initTag(.unreachable_value),
+                    1 => return Value.zero,
+                    else => return null,
                 }
             },
             .enum_nonexhaustive => {
@@ -5044,6 +5042,7 @@ pub const Type = extern union {
             .@"union", .union_safety_tagged, .union_tagged => {
                 const union_obj = ty.cast(Payload.Union).?.data;
                 const tag_val = union_obj.tag_ty.onePossibleValue() orelse return null;
+                if (union_obj.fields.count() == 0) return Value.initTag(.unreachable_value);
                 const only_field = union_obj.fields.values()[0];
                 const val_val = only_field.ty.onePossibleValue() orelse return null;
                 _ = tag_val;
src/value.zig
@@ -2806,6 +2806,29 @@ pub const Value = extern union {
         };
     }
 
+    pub fn isPtrToThreadLocal(val: Value, mod: *Module) bool {
+        return switch (val.tag()) {
+            .variable => false,
+            else => val.isPtrToThreadLocalInner(mod),
+        };
+    }
+
+    fn isPtrToThreadLocalInner(val: Value, mod: *Module) bool {
+        return switch (val.tag()) {
+            .slice => val.castTag(.slice).?.data.ptr.isPtrToThreadLocalInner(mod),
+            .comptime_field_ptr => val.castTag(.comptime_field_ptr).?.data.field_val.isPtrToThreadLocalInner(mod),
+            .elem_ptr => val.castTag(.elem_ptr).?.data.array_ptr.isPtrToThreadLocalInner(mod),
+            .field_ptr => val.castTag(.field_ptr).?.data.container_ptr.isPtrToThreadLocalInner(mod),
+            .eu_payload_ptr => val.castTag(.eu_payload_ptr).?.data.container_ptr.isPtrToThreadLocalInner(mod),
+            .opt_payload_ptr => val.castTag(.opt_payload_ptr).?.data.container_ptr.isPtrToThreadLocalInner(mod),
+            .decl_ref => mod.declPtr(val.castTag(.decl_ref).?.data).val.isPtrToThreadLocalInner(mod),
+            .decl_ref_mut => mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index).val.isPtrToThreadLocalInner(mod),
+
+            .variable => val.castTag(.variable).?.data.is_threadlocal,
+            else => false,
+        };
+    }
+
     // Asserts that the provided start/end are in-bounds.
     pub fn sliceArray(
         val: Value,
test/behavior/array.zig
@@ -574,3 +574,25 @@ test "tuple to array handles sentinel" {
     };
     try expect(S.b[0] == 1);
 }
+
+test "array init of container level array variable" {
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+
+    const S = struct {
+        var pair: [2]usize = .{ 1, 2 };
+        noinline fn foo(x: usize, y: usize) void {
+            pair = [2]usize{ x, y };
+        }
+        noinline fn bar(x: usize, y: usize) void {
+            var tmp: [2]usize = .{ x, y };
+            pair = tmp;
+        }
+    };
+    try expectEqual([2]usize{ 1, 2 }, S.pair);
+    S.foo(3, 4);
+    try expectEqual([2]usize{ 3, 4 }, S.pair);
+    S.bar(5, 6);
+    try expectEqual([2]usize{ 5, 6 }, S.pair);
+}
test/behavior/cast.zig
@@ -1419,3 +1419,13 @@ test "floatToInt to zero-bit int" {
     var a: f32 = 0.0;
     comptime try std.testing.expect(@floatToInt(u0, a) == 0);
 }
+
+test "peer type resolution of function pointer and function body" {
+    if (builtin.zig_backend == .stage1) return error.SkipZigTest;
+
+    const T = fn () u32;
+    const a: T = undefined;
+    const b: *const T = undefined;
+    try expect(@TypeOf(a, b) == *const fn () u32);
+    try expect(@TypeOf(b, a) == *const fn () u32);
+}
test/behavior/empty_union.zig
@@ -48,3 +48,21 @@ test "empty extern union" {
     try expect(@sizeOf(U) == 0);
     try expect(@alignOf(U) == 1);
 }
+
+test "empty union passed as argument" {
+    const U = union(enum) {
+        fn f(u: @This()) void {
+            switch (u) {}
+        }
+    };
+    U.f(@as(U, undefined));
+}
+
+test "empty enum passed as argument" {
+    const E = enum {
+        fn f(e: @This()) void {
+            switch (e) {}
+        }
+    };
+    E.f(@as(E, undefined));
+}
test/behavior/error.zig
@@ -855,3 +855,13 @@ test "error from comptime string" {
         try expect(mem.eql(u8, name, @errorName(err)));
     }
 }
+
+test "field access of anyerror results in smaller error set" {
+    if (builtin.zig_backend == .stage1) return error.SkipZigTest;
+
+    const E1 = @TypeOf(error.Foo);
+    try expect(@TypeOf(E1.Foo) == E1);
+    const E2 = error{ A, B, C };
+    try expect(@TypeOf(E2.A) == E2);
+    try expect(@TypeOf(@field(anyerror, "NotFound")) == error{NotFound});
+}
test/behavior/eval.zig
@@ -1488,3 +1488,14 @@ test "x or true is comptime-known true" {
     }
     try expect(T.x == 3);
 }
+
+test "non-optional and optional array elements concatenated" {
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+
+    const array = [1]u8{'A'} ++ [1]?u8{null};
+    var index: usize = 0;
+    try expect(array[index].? == 'A');
+}
test/behavior/fn.zig
@@ -434,3 +434,22 @@ test "implicit cast function to function ptr" {
     var fnPtr2: *const fn () callconv(.C) c_int = S2.someFunctionThatReturnsAValue;
     try expect(fnPtr2() == 123);
 }
+
+test "method call with optional and error union first param" {
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+
+    const S = struct {
+        x: i32 = 1234,
+
+        fn opt(s: ?@This()) !void {
+            try expect(s.?.x == 1234);
+        }
+        fn errUnion(s: anyerror!@This()) !void {
+            try expect((try s).x == 1234);
+        }
+    };
+    var s: S = .{};
+    try s.opt();
+    try s.errUnion();
+}
test/cases/compile_errors/address_of_threadlocal_not_comptime_known.zig
@@ -0,0 +1,13 @@
+threadlocal var global: u32 = 23;
+threadlocal var global_ptr: *u32 = &global;
+
+pub export fn entry() void {
+    if (global_ptr.* != 23) unreachable;
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :2:36: error: unable to resolve comptime value
+// :2:36: note: container level variable initializers must be comptime-known
test/cases/compile_errors/bitsize_of_packed_struct_checks_backing_int_ty.zig
@@ -0,0 +1,13 @@
+const Foo = packed struct(u32) {
+    x: u1,
+};
+fn bar(_: Foo) callconv(.C) void {}
+pub export fn entry() void {
+    bar(.{ .x = 0 });
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :1:27: error: backing integer type 'u32' has bit size 32 but the struct fields have a total bit size of 1
test/cases/compile_errors/div_overflow.zig
@@ -0,0 +1,11 @@
+comptime {
+    const a = -128;
+    const b: i8 = -1;
+    _ = a / b;
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :4:11: error: overflow of integer type 'i8' with value '128'