mirror of
https://github.com/ziglang/zig.git
synced 2026-02-15 22:09:49 +00:00
stage2: handle tuple init edge cases
This commit is contained in:
parent
9e0a930ce3
commit
fdaf9c40d6
@ -1349,7 +1349,10 @@ fn arrayInitExpr(
|
||||
}
|
||||
}
|
||||
const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr);
|
||||
_ = try gz.addUnNode(.validate_array_init_ty, array_type_inst, array_init.ast.type_expr);
|
||||
_ = try gz.addPlNode(.validate_array_init_ty, node, Zir.Inst.ArrayInit{
|
||||
.ty = array_type_inst,
|
||||
.init_count = @intCast(u32, array_init.ast.elements.len),
|
||||
});
|
||||
break :inst .{
|
||||
.array = array_type_inst,
|
||||
.elem = .none,
|
||||
|
||||
@ -2728,6 +2728,21 @@ pub const SrcLoc = struct {
|
||||
};
|
||||
return nodeToSpan(tree, full.ast.value_expr);
|
||||
},
|
||||
.node_offset_init_ty => |node_off| {
|
||||
const tree = try src_loc.file_scope.getTree(gpa);
|
||||
const node_tags = tree.nodes.items(.tag);
|
||||
const parent_node = src_loc.declRelativeToNodeIndex(node_off);
|
||||
|
||||
var buf: [2]Ast.Node.Index = undefined;
|
||||
const full: Ast.full.ArrayInit = switch (node_tags[parent_node]) {
|
||||
.array_init_one, .array_init_one_comma => tree.arrayInitOne(buf[0..1], parent_node),
|
||||
.array_init_dot_two, .array_init_dot_two_comma => tree.arrayInitDotTwo(&buf, parent_node),
|
||||
.array_init_dot, .array_init_dot_comma => tree.arrayInitDot(parent_node),
|
||||
.array_init, .array_init_comma => tree.arrayInit(parent_node),
|
||||
else => unreachable,
|
||||
};
|
||||
return nodeToSpan(tree, full.ast.type_expr);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -3048,6 +3063,9 @@ pub const LazySrcLoc = union(enum) {
|
||||
/// The source location points to the default value of a field.
|
||||
/// The Decl is determined contextually.
|
||||
node_offset_field_default: i32,
|
||||
/// The source location points to the type of an array or struct initializer.
|
||||
/// The Decl is determined contextually.
|
||||
node_offset_init_ty: i32,
|
||||
|
||||
pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease;
|
||||
|
||||
@ -3126,6 +3144,7 @@ pub const LazySrcLoc = union(enum) {
|
||||
.node_offset_ptr_hostsize,
|
||||
.node_offset_container_tag,
|
||||
.node_offset_field_default,
|
||||
.node_offset_init_ty,
|
||||
=> .{
|
||||
.file_scope = decl.getFileScope(),
|
||||
.parent_decl_node = decl.src_node,
|
||||
|
||||
126
src/Sema.zig
126
src/Sema.zig
@ -3493,19 +3493,43 @@ fn validateArrayInitTy(
|
||||
block: *Block,
|
||||
inst: Zir.Inst.Index,
|
||||
) CompileError!void {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
const ty = try sema.resolveType(block, src, inst_data.operand);
|
||||
const ty_src: LazySrcLoc = .{ .node_offset_init_ty = inst_data.src_node };
|
||||
const extra = sema.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data;
|
||||
const ty = try sema.resolveType(block, ty_src, extra.ty);
|
||||
|
||||
switch (ty.zigTypeTag()) {
|
||||
.Array, .Vector => return,
|
||||
.Array => {
|
||||
const array_len = ty.arrayLen();
|
||||
if (extra.init_count != array_len) {
|
||||
return sema.fail(block, src, "expected {d} array elements; found {d}", .{
|
||||
array_len, extra.init_count,
|
||||
});
|
||||
}
|
||||
return;
|
||||
},
|
||||
.Vector => {
|
||||
const array_len = ty.arrayLen();
|
||||
if (extra.init_count != array_len) {
|
||||
return sema.fail(block, src, "expected {d} vector elements; found {d}", .{
|
||||
array_len, extra.init_count,
|
||||
});
|
||||
}
|
||||
return;
|
||||
},
|
||||
.Struct => if (ty.isTuple()) {
|
||||
// TODO validate element count
|
||||
const array_len = ty.arrayLen();
|
||||
if (extra.init_count > array_len) {
|
||||
return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{
|
||||
array_len, extra.init_count,
|
||||
});
|
||||
}
|
||||
return;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return sema.failWithArrayInitNotSupported(block, src, ty);
|
||||
return sema.failWithArrayInitNotSupported(block, ty_src, ty);
|
||||
}
|
||||
|
||||
fn validateStructInitTy(
|
||||
@ -3741,6 +3765,15 @@ fn validateStructInit(
|
||||
|
||||
const default_val = struct_ty.structFieldDefaultValue(i);
|
||||
if (default_val.tag() == .unreachable_value) {
|
||||
if (struct_ty.isTuple()) {
|
||||
const template = "missing tuple field with index {d}";
|
||||
if (root_msg) |msg| {
|
||||
try sema.errNote(block, init_src, msg, template, .{i});
|
||||
} else {
|
||||
root_msg = try sema.errMsg(block, init_src, template, .{i});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const field_name = struct_ty.structFieldName(i);
|
||||
const template = "missing struct field: {s}";
|
||||
const args = .{field_name};
|
||||
@ -3753,7 +3786,10 @@ fn validateStructInit(
|
||||
}
|
||||
|
||||
const field_src = init_src; // TODO better source location
|
||||
const default_field_ptr = try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(u32, i), field_src, struct_ty, true);
|
||||
const default_field_ptr = if (struct_ty.isTuple())
|
||||
try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(u32, i), true)
|
||||
else
|
||||
try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(u32, i), field_src, struct_ty, true);
|
||||
const field_ty = sema.typeOf(default_field_ptr).childType();
|
||||
const init = try sema.addConstant(field_ty, default_val);
|
||||
try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store);
|
||||
@ -3868,6 +3904,15 @@ fn validateStructInit(
|
||||
|
||||
const default_val = struct_ty.structFieldDefaultValue(i);
|
||||
if (default_val.tag() == .unreachable_value) {
|
||||
if (struct_ty.isTuple()) {
|
||||
const template = "missing tuple field with index {d}";
|
||||
if (root_msg) |msg| {
|
||||
try sema.errNote(block, init_src, msg, template, .{i});
|
||||
} else {
|
||||
root_msg = try sema.errMsg(block, init_src, template, .{i});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const field_name = struct_ty.structFieldName(i);
|
||||
const template = "missing struct field: {s}";
|
||||
const args = .{field_name};
|
||||
@ -3911,7 +3956,10 @@ fn validateStructInit(
|
||||
if (field_ptr != 0) continue;
|
||||
|
||||
const field_src = init_src; // TODO better source location
|
||||
const default_field_ptr = try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(u32, i), field_src, struct_ty, true);
|
||||
const default_field_ptr = if (struct_ty.isTuple())
|
||||
try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(u32, i), true)
|
||||
else
|
||||
try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(u32, i), field_src, struct_ty, true);
|
||||
const field_ty = sema.typeOf(default_field_ptr).childType();
|
||||
const init = try sema.addConstant(field_ty, field_values[i]);
|
||||
try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store);
|
||||
@ -3934,15 +3982,24 @@ fn zirValidateArrayInit(
|
||||
const array_ty = sema.typeOf(array_ptr).childType();
|
||||
const array_len = array_ty.arrayLen();
|
||||
|
||||
if (instrs.len != array_len) {
|
||||
if (array_ty.zigTypeTag() == .Array) {
|
||||
return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{
|
||||
array_len, instrs.len,
|
||||
});
|
||||
} else {
|
||||
return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{
|
||||
array_len, instrs.len,
|
||||
});
|
||||
if (instrs.len != array_len and array_ty.isTuple()) {
|
||||
const struct_obj = array_ty.castTag(.tuple).?.data;
|
||||
var root_msg: ?*Module.ErrorMsg = null;
|
||||
for (struct_obj.values) |default_val, i| {
|
||||
if (i < instrs.len) continue;
|
||||
|
||||
if (default_val.tag() == .unreachable_value) {
|
||||
const template = "missing tuple field with index {d}";
|
||||
if (root_msg) |msg| {
|
||||
try sema.errNote(block, init_src, msg, template, .{i});
|
||||
} else {
|
||||
root_msg = try sema.errMsg(block, init_src, template, .{i});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (root_msg) |msg| {
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3995,10 +4052,17 @@ fn zirValidateArrayInit(
|
||||
}
|
||||
first_block_index = @minimum(first_block_index, block_index);
|
||||
|
||||
// Array has one possible value, so value is always comptime-known
|
||||
if (opt_opv) |opv| {
|
||||
element_vals[i] = opv;
|
||||
continue;
|
||||
if (array_ty.isTuple()) {
|
||||
if (array_ty.structFieldValueComptime(i)) |opv| {
|
||||
element_vals[i] = opv;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Array has one possible value, so value is always comptime-known
|
||||
if (opt_opv) |opv| {
|
||||
element_vals[i] = opv;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If the next instructon is a store with a comptime operand, this element
|
||||
@ -14710,6 +14774,22 @@ fn finishStructInit(
|
||||
field_inits[i] = try sema.addConstant(struct_obj.types[i], default_val);
|
||||
}
|
||||
}
|
||||
} else if (struct_ty.isTuple()) {
|
||||
const struct_obj = struct_ty.castTag(.tuple).?.data;
|
||||
for (struct_obj.values) |default_val, i| {
|
||||
if (field_inits[i] != .none) continue;
|
||||
|
||||
if (default_val.tag() == .unreachable_value) {
|
||||
const template = "missing tuple field with index {d}";
|
||||
if (root_msg) |msg| {
|
||||
try sema.errNote(block, init_src, msg, template, .{i});
|
||||
} else {
|
||||
root_msg = try sema.errMsg(block, init_src, template, .{i});
|
||||
}
|
||||
} else {
|
||||
field_inits[i] = try sema.addConstant(struct_obj.types[i], default_val);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const struct_obj = struct_ty.castTag(.@"struct").?.data;
|
||||
for (struct_obj.fields.values()) |field, i| {
|
||||
@ -20255,7 +20335,7 @@ fn tupleFieldVal(
|
||||
return tupleFieldValByIndex(sema, block, src, tuple_byval, field_index, tuple_ty);
|
||||
}
|
||||
|
||||
/// Don't forget to check for "len" before calling this.
|
||||
/// Asserts that `field_name` is not "len".
|
||||
fn tupleFieldIndex(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
@ -20263,8 +20343,12 @@ fn tupleFieldIndex(
|
||||
field_name: []const u8,
|
||||
field_name_src: LazySrcLoc,
|
||||
) CompileError!u32 {
|
||||
assert(!std.mem.eql(u8, field_name, "len"));
|
||||
if (std.fmt.parseUnsigned(u32, field_name, 10)) |field_index| {
|
||||
if (field_index < tuple_ty.structFieldCount()) return field_index;
|
||||
return sema.fail(block, field_name_src, "index '{s}' out of bounds of tuple '{}'", .{
|
||||
field_name, tuple_ty.fmt(sema.mod),
|
||||
});
|
||||
} else |_| {}
|
||||
|
||||
return sema.fail(block, field_name_src, "no field named '{s}' in tuple '{}'", .{
|
||||
|
||||
@ -1709,7 +1709,7 @@ pub const Inst = struct {
|
||||
.switch_capture_multi_ref = .switch_capture,
|
||||
.array_base_ptr = .un_node,
|
||||
.field_base_ptr = .un_node,
|
||||
.validate_array_init_ty = .un_node,
|
||||
.validate_array_init_ty = .pl_node,
|
||||
.validate_struct_init_ty = .un_node,
|
||||
.validate_struct_init = .pl_node,
|
||||
.validate_struct_init_comptime = .pl_node,
|
||||
@ -3543,6 +3543,11 @@ pub const Inst = struct {
|
||||
line: u32,
|
||||
column: u32,
|
||||
};
|
||||
|
||||
pub const ArrayInit = struct {
|
||||
ty: Ref,
|
||||
init_count: u32,
|
||||
};
|
||||
};
|
||||
|
||||
pub const SpecialProng = enum { none, @"else", under };
|
||||
|
||||
@ -229,7 +229,6 @@ const Writer = struct {
|
||||
.switch_cond_ref,
|
||||
.array_base_ptr,
|
||||
.field_base_ptr,
|
||||
.validate_array_init_ty,
|
||||
.validate_struct_init_ty,
|
||||
.make_ptr_const,
|
||||
.validate_deref,
|
||||
@ -246,6 +245,7 @@ const Writer = struct {
|
||||
.bool_br_or,
|
||||
=> try self.writeBoolBr(stream, inst),
|
||||
|
||||
.validate_array_init_ty => try self.writeValidateArrayInitTy(stream, inst),
|
||||
.array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst),
|
||||
.param_type => try self.writeParamType(stream, inst),
|
||||
.ptr_type => try self.writePtrType(stream, inst),
|
||||
@ -577,6 +577,18 @@ const Writer = struct {
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writeValidateArrayInitTy(
|
||||
self: *Writer,
|
||||
stream: anytype,
|
||||
inst: Zir.Inst.Index,
|
||||
) (@TypeOf(stream).Error || error{OutOfMemory})!void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
|
||||
const extra = self.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data;
|
||||
try self.writeInstRef(stream, extra.ty);
|
||||
try stream.print(", {d}) ", .{extra.init_count});
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writeArrayTypeSentinel(
|
||||
self: *Writer,
|
||||
stream: anytype,
|
||||
|
||||
44
test/cases/compile_errors/tuple_init_edge_cases.zig
Normal file
44
test/cases/compile_errors/tuple_init_edge_cases.zig
Normal file
@ -0,0 +1,44 @@
|
||||
pub export fn entry1() void {
|
||||
const T = @TypeOf(.{ 123, 3 });
|
||||
var b = T{ .@"1" = 3 }; _ = b;
|
||||
var c = T{ 123, 3 }; _ = c;
|
||||
var d = T{}; _ = d;
|
||||
}
|
||||
pub export fn entry2() void {
|
||||
var a: u32 = 2;
|
||||
const T = @TypeOf(.{ 123, a });
|
||||
var b = T{ .@"1" = 3 }; _ = b;
|
||||
var c = T{ 123, 3 }; _ = c;
|
||||
var d = T{}; _ = d;
|
||||
}
|
||||
pub export fn entry3() void {
|
||||
var a: u32 = 2;
|
||||
const T = @TypeOf(.{ 123, a });
|
||||
var b = T{ .@"0" = 123 }; _ = b;
|
||||
}
|
||||
comptime {
|
||||
var a: u32 = 2;
|
||||
const T = @TypeOf(.{ 123, a });
|
||||
var b = T{ .@"0" = 123 }; _ = b;
|
||||
var c = T{ 123, 2 }; _ = c;
|
||||
var d = T{}; _ = d;
|
||||
}
|
||||
pub export fn entry4() void {
|
||||
var a: u32 = 2;
|
||||
const T = @TypeOf(.{ 123, a });
|
||||
var b = T{ 123, 4, 5 }; _ = b;
|
||||
}
|
||||
pub export fn entry5() void {
|
||||
var a: u32 = 2;
|
||||
const T = @TypeOf(.{ 123, a });
|
||||
var b = T{ .@"0" = 123, .@"2" = 123, .@"1" = 123 }; _ = b;
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :12:14: error: missing tuple field with index 1
|
||||
// :17:14: error: missing tuple field with index 1
|
||||
// :29:14: error: expected at most 2 tuple fields; found 3
|
||||
// :34:30: error: index '2' out of bounds of tuple 'tuple{comptime comptime_int = 123, u32}'
|
||||
@ -7,4 +7,4 @@ comptime {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :2:31: error: index 2 outside array of length 2
|
||||
// :2:24: error: expected 2 array elements; found 3
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user