Commit b75197ef88
2022-11-26 21:03:49
1 parent
ddfcf02Changed files (7)
test
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