Commit b75197ef88

Veikka Tuominen <git@vexu.eu>
2022-11-26 21:03:49
Merge pull request #13637 from Vexu/stage2-fixes
Stage2 bug fixes
1 parent ddfcf02
src/AstGen.zig
@@ -7693,6 +7693,7 @@ fn typeOf(
 
         var typeof_scope = gz.makeSubBlock(scope);
         typeof_scope.force_comptime = false;
+        typeof_scope.c_import = false;
         defer typeof_scope.unstack();
 
         const ty_expr = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, args[0], node);
@@ -8551,6 +8552,8 @@ fn cImport(
     const astgen = gz.astgen;
     const gpa = astgen.gpa;
 
+    if (gz.c_import) return gz.astgen.failNode(node, "cannot nest @cImport", .{});
+
     var block_scope = gz.makeSubBlock(scope);
     block_scope.force_comptime = true;
     block_scope.c_import = true;
src/Sema.zig
@@ -5105,6 +5105,7 @@ fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErro
         .is_typeof = parent_block.is_typeof,
         .want_safety = parent_block.want_safety,
         .float_mode = parent_block.float_mode,
+        .c_import_buf = parent_block.c_import_buf,
         .runtime_cond = parent_block.runtime_cond,
         .runtime_loop = parent_block.runtime_loop,
         .runtime_index = parent_block.runtime_index,
@@ -7031,16 +7032,21 @@ fn instantiateGenericCall(
             }
             const arg = uncasted_args[arg_i];
             if (is_comptime) {
-                if (try sema.resolveMaybeUndefVal(arg)) |arg_val| {
-                    const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
-                    child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
-                } else {
-                    return sema.failWithNeededComptime(block, .unneeded, "");
-                }
+                const arg_val = (try sema.resolveMaybeUndefVal(arg)).?;
+                const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
+                child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
             } else if (is_anytype) {
                 const arg_ty = sema.typeOf(arg);
                 if (try sema.typeRequiresComptime(arg_ty)) {
-                    const arg_val = try sema.resolveConstValue(block, .unneeded, arg, "");
+                    const arg_val = sema.resolveConstValue(block, .unneeded, arg, "") catch |err| switch (err) {
+                        error.NeededSourceLocation => {
+                            const decl = sema.mod.declPtr(block.src_decl);
+                            const arg_src = Module.argSrc(call_src.node_offset.x, sema.gpa, decl, arg_i, bound_arg_src);
+                            _ = try sema.resolveConstValue(block, arg_src, arg, "argument to parameter with comptime-only type must be comptime-known");
+                            return error.AnalysisFail;
+                        },
+                        else => |e| return e,
+                    };
                     const child_arg = try child_sema.addConstant(arg_ty, arg_val);
                     child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
                 } else {
@@ -7575,7 +7581,8 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag()) {
         .Enum => operand,
         .Union => blk: {
-            const tag_ty = operand_ty.unionTagType() orelse {
+            const union_ty = try sema.resolveTypeFields(operand_ty);
+            const tag_ty = union_ty.unionTagType() orelse {
                 return sema.fail(
                     block,
                     operand_src,
@@ -9691,10 +9698,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     };
 
     const maybe_union_ty = blk: {
+        const zir_tags = sema.code.instructions.items(.tag);
         const zir_data = sema.code.instructions.items(.data);
         const cond_index = Zir.refToIndex(extra.data.operand).?;
         const raw_operand = sema.resolveInst(zir_data[cond_index].un_node.operand) catch unreachable;
-        break :blk sema.typeOf(raw_operand);
+        const target_ty = sema.typeOf(raw_operand);
+        break :blk if (zir_tags[cond_index] == .switch_cond_ref) target_ty.elemType() else target_ty;
     };
     const union_originally = maybe_union_ty.zigTypeTag() == .Union;
 
@@ -10260,6 +10269,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
         .comptime_reason = block.comptime_reason,
         .is_typeof = block.is_typeof,
         .switch_else_err_ty = else_error_ty,
+        .c_import_buf = block.c_import_buf,
         .runtime_cond = block.runtime_cond,
         .runtime_loop = block.runtime_loop,
         .runtime_index = block.runtime_index,
@@ -13468,7 +13478,6 @@ fn zirOverflowArithmetic(
     const maybe_rhs_val = try sema.resolveMaybeUndefVal(rhs);
 
     const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty);
-    const ov_ty = tuple_ty.tupleFields().types[1];
     // TODO: Remove and use `ov_ty` instead.
     //       This is a temporary type used until overflow arithmetic properly returns `u1` instead of `bool`.
     const overflowed_ty = if (dest_ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, dest_ty.vectorLen(), Type.@"bool") else Type.@"bool";
@@ -13619,14 +13628,7 @@ fn zirOverflowArithmetic(
         try sema.storePtr2(block, src, ptr, ptr_src, wrapped, src, .store);
 
         const overflow_bit = try sema.tupleFieldValByIndex(block, src, tuple, 1, tuple_ty);
-        const zero_ov_val = if (dest_ty.zigTypeTag() == .Vector) try Value.Tag.repeated.create(sema.arena, Value.zero) else Value.zero;
-        const zero_ov = try sema.addConstant(ov_ty, zero_ov_val);
-
-        const overflowed_inst = if (dest_ty.zigTypeTag() == .Vector)
-            block.addCmpVector(overflow_bit, .zero, .neq, try sema.addType(ov_ty))
-        else
-            block.addBinOp(.cmp_neq, overflow_bit, zero_ov);
-        return overflowed_inst;
+        return block.addBitCast(overflowed_ty, overflow_bit);
     };
 
     try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store);
@@ -22714,7 +22716,8 @@ fn fieldPtr(
                             return inst;
                         }
                     }
-                    if (child_type.unionTagType()) |enum_ty| {
+                    const union_ty = try sema.resolveTypeFields(child_type);
+                    if (union_ty.unionTagType()) |enum_ty| {
                         if (enum_ty.enumFieldIndex(field_name)) |field_index| {
                             const field_index_u32 = @intCast(u32, field_index);
                             var anon_decl = try block.startAnonDecl();
@@ -29122,20 +29125,7 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
         }
 
         struct_obj.status = .have_layout;
-
-        // In case of querying the ABI alignment of this struct, we will ask
-        // for hasRuntimeBits() of each field, so we need "requires comptime"
-        // to be known already before this function returns.
-        for (struct_obj.fields.values()) |field, i| {
-            _ = sema.typeRequiresComptime(field.ty) catch |err| switch (err) {
-                error.AnalysisFail => {
-                    const msg = sema.err orelse return err;
-                    try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{});
-                    return err;
-                },
-                else => return err,
-            };
-        }
+        _ = try sema.resolveTypeRequiresComptime(resolved_ty);
     }
     // otherwise it's a tuple; no need to resolve anything
 }
@@ -29299,6 +29289,198 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void {
         };
     }
     union_obj.status = .have_layout;
+    _ = try sema.resolveTypeRequiresComptime(resolved_ty);
+}
+
+// In case of querying the ABI alignment of this struct, we will ask
+// for hasRuntimeBits() of each field, so we need "requires comptime"
+// to be known already before this function returns.
+pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
+    return switch (ty.tag()) {
+        .u1,
+        .u8,
+        .i8,
+        .u16,
+        .i16,
+        .u29,
+        .u32,
+        .i32,
+        .u64,
+        .i64,
+        .u128,
+        .i128,
+        .usize,
+        .isize,
+        .c_short,
+        .c_ushort,
+        .c_int,
+        .c_uint,
+        .c_long,
+        .c_ulong,
+        .c_longlong,
+        .c_ulonglong,
+        .c_longdouble,
+        .f16,
+        .f32,
+        .f64,
+        .f80,
+        .f128,
+        .anyopaque,
+        .bool,
+        .void,
+        .anyerror,
+        .noreturn,
+        .@"anyframe",
+        .null,
+        .undefined,
+        .atomic_order,
+        .atomic_rmw_op,
+        .calling_convention,
+        .address_space,
+        .float_mode,
+        .reduce_op,
+        .call_options,
+        .prefetch_options,
+        .export_options,
+        .extern_options,
+        .manyptr_u8,
+        .manyptr_const_u8,
+        .manyptr_const_u8_sentinel_0,
+        .const_slice_u8,
+        .const_slice_u8_sentinel_0,
+        .anyerror_void_error_union,
+        .empty_struct_literal,
+        .empty_struct,
+        .error_set,
+        .error_set_single,
+        .error_set_inferred,
+        .error_set_merged,
+        .@"opaque",
+        .generic_poison,
+        .array_u8,
+        .array_u8_sentinel_0,
+        .int_signed,
+        .int_unsigned,
+        .enum_simple,
+        => false,
+
+        .single_const_pointer_to_comptime_int,
+        .type,
+        .comptime_int,
+        .comptime_float,
+        .enum_literal,
+        .type_info,
+        // These are function bodies, not function pointers.
+        .fn_noreturn_no_args,
+        .fn_void_no_args,
+        .fn_naked_noreturn_no_args,
+        .fn_ccc_void_no_args,
+        .function,
+        => true,
+
+        .var_args_param => unreachable,
+        .inferred_alloc_mut => unreachable,
+        .inferred_alloc_const => unreachable,
+        .bound_fn => unreachable,
+
+        .array,
+        .array_sentinel,
+        .vector,
+        => return sema.resolveTypeRequiresComptime(ty.childType()),
+
+        .pointer,
+        .single_const_pointer,
+        .single_mut_pointer,
+        .many_const_pointer,
+        .many_mut_pointer,
+        .c_const_pointer,
+        .c_mut_pointer,
+        .const_slice,
+        .mut_slice,
+        => {
+            const child_ty = ty.childType();
+            if (child_ty.zigTypeTag() == .Fn) {
+                return child_ty.fnInfo().is_generic;
+            } else {
+                return sema.resolveTypeRequiresComptime(child_ty);
+            }
+        },
+
+        .optional,
+        .optional_single_mut_pointer,
+        .optional_single_const_pointer,
+        => {
+            var buf: Type.Payload.ElemType = undefined;
+            return sema.resolveTypeRequiresComptime(ty.optionalChild(&buf));
+        },
+
+        .tuple, .anon_struct => {
+            const tuple = ty.tupleFields();
+            for (tuple.types) |field_ty, i| {
+                const have_comptime_val = tuple.values[i].tag() != .unreachable_value;
+                if (!have_comptime_val and try sema.resolveTypeRequiresComptime(field_ty)) {
+                    return true;
+                }
+            }
+            return false;
+        },
+
+        .@"struct" => {
+            const struct_obj = ty.castTag(.@"struct").?.data;
+            switch (struct_obj.requires_comptime) {
+                .no, .wip => return false,
+                .yes => return true,
+                .unknown => {
+                    var requires_comptime = false;
+                    struct_obj.requires_comptime = .wip;
+                    for (struct_obj.fields.values()) |field| {
+                        if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true;
+                    }
+                    if (requires_comptime) {
+                        struct_obj.requires_comptime = .yes;
+                    } else {
+                        struct_obj.requires_comptime = .no;
+                    }
+                    return requires_comptime;
+                },
+            }
+        },
+
+        .@"union", .union_safety_tagged, .union_tagged => {
+            const union_obj = ty.cast(Type.Payload.Union).?.data;
+            switch (union_obj.requires_comptime) {
+                .no, .wip => return false,
+                .yes => return true,
+                .unknown => {
+                    var requires_comptime = false;
+                    union_obj.requires_comptime = .wip;
+                    for (union_obj.fields.values()) |field| {
+                        if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true;
+                    }
+                    if (requires_comptime) {
+                        union_obj.requires_comptime = .yes;
+                    } else {
+                        union_obj.requires_comptime = .no;
+                    }
+                    return requires_comptime;
+                },
+            }
+        },
+
+        .error_union => return sema.resolveTypeRequiresComptime(ty.errorUnionPayload()),
+        .anyframe_T => {
+            const child_ty = ty.castTag(.anyframe_T).?.data;
+            return sema.resolveTypeRequiresComptime(child_ty);
+        },
+        .enum_numbered => {
+            const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty;
+            return sema.resolveTypeRequiresComptime(tag_ty);
+        },
+        .enum_full, .enum_nonexhaustive => {
+            const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty;
+            return sema.resolveTypeRequiresComptime(tag_ty);
+        },
+    };
 }
 
 /// Returns `error.AnalysisFail` if any of the types (recursively) failed to
test/behavior/union.zig
@@ -1287,7 +1287,14 @@ test "noreturn field in union" {
             try expect(a == .a);
         },
     }
-    try expect(count == 5);
+    switch (a) {
+        .a => count += 1,
+        .b, .c => |*val| {
+            _ = val;
+            @compileError("bad");
+        },
+    }
+    try expect(count == 6);
 }
 
 test "union and enum field order doesn't match" {
test/behavior/vector.zig
@@ -977,27 +977,35 @@ test "@addWithOverflow" {
         fn doTheTest() !void {
             {
                 var result: @Vector(4, u8) = undefined;
-                var overflow = @addWithOverflow(@Vector(4, u8), @Vector(4, u8){ 250, 250, 250, 250 }, @Vector(4, u8){ 0, 5, 6, 10 }, &result);
+                var lhs = @Vector(4, u8){ 250, 250, 250, 250 };
+                var rhs = @Vector(4, u8){ 0, 5, 6, 10 };
+                var overflow = @addWithOverflow(@Vector(4, u8), lhs, rhs, &result);
                 var expected: @Vector(4, bool) = .{ false, false, true, true };
-                try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected)));
+                try expectEqual(expected, overflow);
             }
             {
                 var result: @Vector(4, i8) = undefined;
-                var overflow = @addWithOverflow(@Vector(4, i8), @Vector(4, i8){ -125, -125, 125, 125 }, @Vector(4, i8){ -3, -4, 2, 3 }, &result);
+                var lhs = @Vector(4, i8){ -125, -125, 125, 125 };
+                var rhs = @Vector(4, i8){ -3, -4, 2, 3 };
+                var overflow = @addWithOverflow(@Vector(4, i8), lhs, rhs, &result);
                 var expected: @Vector(4, bool) = .{ false, true, false, true };
-                try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected)));
+                try expectEqual(expected, overflow);
             }
             {
                 var result: @Vector(4, u1) = undefined;
-                var overflow = @addWithOverflow(@Vector(4, u1), @Vector(4, u1){ 0, 0, 1, 1 }, @Vector(4, u1){ 0, 1, 0, 1 }, &result);
+                var lhs = @Vector(4, u1){ 0, 0, 1, 1 };
+                var rhs = @Vector(4, u1){ 0, 1, 0, 1 };
+                var overflow = @addWithOverflow(@Vector(4, u1), lhs, rhs, &result);
                 var expected: @Vector(4, bool) = .{ false, false, false, true };
-                try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected)));
+                try expectEqual(expected, overflow);
             }
             {
                 var result: @Vector(4, u0) = undefined;
-                var overflow = @addWithOverflow(@Vector(4, u0), @Vector(4, u0){ 0, 0, 0, 0 }, @Vector(4, u0){ 0, 0, 0, 0 }, &result);
+                var lhs = @Vector(4, u0){ 0, 0, 0, 0 };
+                var rhs = @Vector(4, u0){ 0, 0, 0, 0 };
+                var overflow = @addWithOverflow(@Vector(4, u0), lhs, rhs, &result);
                 var expected: @Vector(4, bool) = .{ false, false, false, false };
-                try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected)));
+                try expectEqual(expected, overflow);
             }
         }
     };
@@ -1019,15 +1027,19 @@ test "@subWithOverflow" {
         fn doTheTest() !void {
             {
                 var result: @Vector(2, u8) = undefined;
-                var overflow = @subWithOverflow(@Vector(2, u8), @Vector(2, u8){ 5, 5 }, @Vector(2, u8){ 5, 6 }, &result);
+                var lhs = @Vector(2, u8){ 5, 5 };
+                var rhs = @Vector(2, u8){ 5, 6 };
+                var overflow = @subWithOverflow(@Vector(2, u8), lhs, rhs, &result);
                 var expected: @Vector(2, bool) = .{ false, true };
-                try expect(mem.eql(bool, &@as([2]bool, overflow), &@as([2]bool, expected)));
+                try expectEqual(expected, overflow);
             }
             {
                 var result: @Vector(4, i8) = undefined;
-                var overflow = @subWithOverflow(@Vector(4, i8), @Vector(4, i8){ -120, -120, 120, 120 }, @Vector(4, i8){ 8, 9, -7, -8 }, &result);
+                var lhs = @Vector(4, i8){ -120, -120, 120, 120 };
+                var rhs = @Vector(4, i8){ 8, 9, -7, -8 };
+                var overflow = @subWithOverflow(@Vector(4, i8), lhs, rhs, &result);
                 var expected: @Vector(4, bool) = .{ false, true, false, true };
-                try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected)));
+                try expectEqual(expected, overflow);
             }
         }
     };
@@ -1048,9 +1060,11 @@ test "@mulWithOverflow" {
     const S = struct {
         fn doTheTest() !void {
             var result: @Vector(4, u8) = undefined;
-            var overflow = @mulWithOverflow(@Vector(4, u8), @Vector(4, u8){ 10, 10, 10, 10 }, @Vector(4, u8){ 25, 26, 0, 30 }, &result);
+            var lhs = @Vector(4, u8){ 10, 10, 10, 10 };
+            var rhs = @Vector(4, u8){ 25, 26, 0, 30 };
+            var overflow = @mulWithOverflow(@Vector(4, u8), lhs, rhs, &result);
             var expected: @Vector(4, bool) = .{ false, true, false, true };
-            try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected)));
+            try expectEqual(expected, overflow);
         }
     };
     try S.doTheTest();
@@ -1062,6 +1076,7 @@ test "@shlWithOverflow" {
         // stage1 doesn't support vector args
         return error.SkipZigTest;
     }
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
@@ -1070,9 +1085,11 @@ test "@shlWithOverflow" {
     const S = struct {
         fn doTheTest() !void {
             var result: @Vector(4, u8) = undefined;
-            var overflow = @shlWithOverflow(@Vector(4, u8), @Vector(4, u8){ 0, 1, 8, 255 }, @Vector(4, u3){ 7, 7, 7, 7 }, &result);
+            var lhs = @Vector(4, u8){ 0, 1, 8, 255 };
+            var rhs = @Vector(4, u3){ 7, 7, 7, 7 };
+            var overflow = @shlWithOverflow(@Vector(4, u8), lhs, rhs, &result);
             var expected: @Vector(4, bool) = .{ false, false, true, true };
-            try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected)));
+            try expectEqual(expected, overflow);
         }
     };
     try S.doTheTest();
test/cases/compile_errors/anytype_param_requires_comptime.zig
@@ -0,0 +1,20 @@
+const S = struct {
+    fn foo(b: u32, c: anytype) void {
+        const C = struct {
+            c: @TypeOf(c),
+            b: u32,
+        };
+        bar(C{ .c = c, .b = b });
+    }
+    fn bar(_: anytype) void {}
+};
+pub export fn entry() void {
+    S.foo(0, u32);
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :7:14: error: unable to resolve comptime value
+// :7:14: note: argument to parameter with comptime-only type must be comptime-known
test/cases/compile_errors/cimport.zig
@@ -0,0 +1,15 @@
+const b = @cDefine("foo", "1");
+const c = @cImport({
+    _ = @TypeOf(@cDefine("foo", "1"));
+});
+const d = @cImport({
+    _ = @cImport(@cDefine("foo", "1"));
+});
+
+// error
+// backend=stage2
+// target=native
+//
+// :1:11: error: C define valid only inside C import block
+// :3:17: error: C define valid only inside C import block
+// :6:9: error: cannot nest @cImport
test/cases/compile_errors/union_fields_are_resolved_before_tag_type_is_needed.zig
@@ -0,0 +1,16 @@
+const T = union(enum) {
+    a,
+    pub fn f(self: T) void {
+        _ = self;
+    }
+};
+pub export fn entry() void {
+    T.a.f();
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :8:8: error: no field or member function named 'f' in '@typeInfo(tmp.T).Union.tag_type.?'
+// :1:11: note: enum declared here