Merge pull request #13637 from Vexu/stage2-fixes

Stage2 bug fixes
This commit is contained in:
Veikka Tuominen
2022-11-26 22:03:49 +02:00
committed by Andrew Kelley
parent ddfcf0246e
commit b75197ef88
7 changed files with 310 additions and 50 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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" {

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View File

@@ -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