diff --git a/src/AstGen.zig b/src/AstGen.zig index 58727099eb..e6f83dc00b 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -7717,6 +7717,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); @@ -8575,6 +8576,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; diff --git a/src/Sema.zig b/src/Sema.zig index ad78d81800..c06afb9c51 100644 --- a/src/Sema.zig +++ b/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, @@ -13479,7 +13489,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; @@ -13630,14 +13639,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); @@ -22675,7 +22677,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(); @@ -29101,20 +29104,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 } @@ -29278,6 +29268,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 diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 37cd72e319..aaeb586cdf 100644 --- a/test/behavior/union.zig +++ b/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" { diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index e756264418..30dd563abe 100644 --- a/test/behavior/vector.zig +++ b/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(); diff --git a/test/cases/compile_errors/anytype_param_requires_comptime.zig b/test/cases/compile_errors/anytype_param_requires_comptime.zig new file mode 100644 index 0000000000..3e2b32b408 --- /dev/null +++ b/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 diff --git a/test/cases/compile_errors/cimport.zig b/test/cases/compile_errors/cimport.zig new file mode 100644 index 0000000000..855f9aeb40 --- /dev/null +++ b/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 diff --git a/test/cases/compile_errors/union_fields_are_resolved_before_tag_type_is_needed.zig b/test/cases/compile_errors/union_fields_are_resolved_before_tag_type_is_needed.zig new file mode 100644 index 0000000000..7ef4c80d5a --- /dev/null +++ b/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