mirror of
https://github.com/ziglang/zig.git
synced 2025-12-17 19:53:06 +00:00
Sema: implement coercion of tuples to structs
This commit is contained in:
parent
6f560c9909
commit
bb73775d40
107
src/Sema.zig
107
src/Sema.zig
@ -15724,7 +15724,7 @@ fn tupleField(
|
|||||||
field_index_src: LazySrcLoc,
|
field_index_src: LazySrcLoc,
|
||||||
) CompileError!Air.Inst.Ref {
|
) CompileError!Air.Inst.Ref {
|
||||||
const tuple_ty = sema.typeOf(tuple);
|
const tuple_ty = sema.typeOf(tuple);
|
||||||
const tuple_info = tuple_ty.castTag(.tuple).?.data;
|
const tuple_info = tuple_ty.tupleFields();
|
||||||
|
|
||||||
if (field_index > tuple_info.types.len) {
|
if (field_index > tuple_info.types.len) {
|
||||||
return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
|
return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
|
||||||
@ -16195,6 +16195,9 @@ fn coerce(
|
|||||||
if (inst == .empty_struct) {
|
if (inst == .empty_struct) {
|
||||||
return structInitEmpty(sema, block, dest_ty, dest_ty_src, inst_src);
|
return structInitEmpty(sema, block, dest_ty, dest_ty_src, inst_src);
|
||||||
}
|
}
|
||||||
|
if (inst_ty.isTupleOrAnonStruct()) {
|
||||||
|
return sema.coerceTupleToStruct(block, dest_ty, dest_ty_src, inst, inst_src);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
@ -17442,6 +17445,94 @@ fn coerceTupleToArray(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handles both tuples and anon struct literals. Coerces field-wise. Reports
|
||||||
|
/// errors for both extra fields and missing fields.
|
||||||
|
fn coerceTupleToStruct(
|
||||||
|
sema: *Sema,
|
||||||
|
block: *Block,
|
||||||
|
dest_ty: Type,
|
||||||
|
dest_ty_src: LazySrcLoc,
|
||||||
|
inst: Air.Inst.Ref,
|
||||||
|
inst_src: LazySrcLoc,
|
||||||
|
) !Air.Inst.Ref {
|
||||||
|
if (dest_ty.isTupleOrAnonStruct()) {
|
||||||
|
return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to tuples", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
const fields = dest_ty.structFields();
|
||||||
|
const field_vals = try sema.arena.alloc(Value, fields.count());
|
||||||
|
const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
|
||||||
|
mem.set(Air.Inst.Ref, field_refs, .none);
|
||||||
|
|
||||||
|
const inst_ty = sema.typeOf(inst);
|
||||||
|
const tuple = inst_ty.tupleFields();
|
||||||
|
var runtime_src: ?LazySrcLoc = null;
|
||||||
|
for (tuple.types) |_, i_usize| {
|
||||||
|
const i = @intCast(u32, i_usize);
|
||||||
|
const field_src = inst_src; // TODO better source location
|
||||||
|
const field_name = if (inst_ty.castTag(.anon_struct)) |payload|
|
||||||
|
payload.data.names[i]
|
||||||
|
else
|
||||||
|
try std.fmt.allocPrint(sema.arena, "{d}", .{i});
|
||||||
|
const field_index = try sema.structFieldIndex(block, dest_ty, field_name, field_src);
|
||||||
|
const field = fields.values()[field_index];
|
||||||
|
if (field.is_comptime) {
|
||||||
|
return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to structs when one of the destination struct fields is comptime", .{});
|
||||||
|
}
|
||||||
|
const elem_ref = try tupleField(sema, block, inst, i, inst_src, field_src);
|
||||||
|
const coerced = try sema.coerce(block, field.ty, elem_ref, field_src);
|
||||||
|
field_refs[field_index] = coerced;
|
||||||
|
if (runtime_src == null) {
|
||||||
|
if (try sema.resolveMaybeUndefVal(block, field_src, coerced)) |field_val| {
|
||||||
|
field_vals[field_index] = field_val;
|
||||||
|
} else {
|
||||||
|
runtime_src = field_src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate default field values and report errors for missing fields.
|
||||||
|
var root_msg: ?*Module.ErrorMsg = null;
|
||||||
|
|
||||||
|
for (field_refs) |*field_ref, i| {
|
||||||
|
if (field_ref.* != .none) continue;
|
||||||
|
|
||||||
|
const field_name = fields.keys()[i];
|
||||||
|
const field = fields.values()[i];
|
||||||
|
const field_src = inst_src; // TODO better source location
|
||||||
|
if (field.default_val.tag() == .unreachable_value) {
|
||||||
|
const template = "missing struct field: {s}";
|
||||||
|
const args = .{field_name};
|
||||||
|
if (root_msg) |msg| {
|
||||||
|
try sema.errNote(block, field_src, msg, template, args);
|
||||||
|
} else {
|
||||||
|
root_msg = try sema.errMsg(block, field_src, template, args);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (runtime_src == null) {
|
||||||
|
field_vals[i] = field.default_val;
|
||||||
|
} else {
|
||||||
|
field_ref.* = try sema.addConstant(field.ty, field.default_val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root_msg) |msg| {
|
||||||
|
try sema.addDeclaredHereNote(msg, dest_ty);
|
||||||
|
return sema.failWithOwnedErrorMsg(block, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runtime_src) |rs| {
|
||||||
|
try sema.requireRuntimeBlock(block, rs);
|
||||||
|
return block.addAggregateInit(dest_ty, field_refs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sema.addConstant(
|
||||||
|
dest_ty,
|
||||||
|
try Value.Tag.@"struct".create(sema.arena, field_vals),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn analyzeDeclVal(
|
fn analyzeDeclVal(
|
||||||
sema: *Sema,
|
sema: *Sema,
|
||||||
block: *Block,
|
block: *Block,
|
||||||
@ -20365,3 +20456,17 @@ fn unionFieldIndex(
|
|||||||
return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
|
return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
|
||||||
return @intCast(u32, field_index_usize);
|
return @intCast(u32, field_index_usize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn structFieldIndex(
|
||||||
|
sema: *Sema,
|
||||||
|
block: *Block,
|
||||||
|
unresolved_struct_ty: Type,
|
||||||
|
field_name: []const u8,
|
||||||
|
field_src: LazySrcLoc,
|
||||||
|
) !u32 {
|
||||||
|
const struct_ty = try sema.resolveTypeFields(block, field_src, unresolved_struct_ty);
|
||||||
|
const struct_obj = struct_ty.castTag(.@"struct").?.data;
|
||||||
|
const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
|
||||||
|
return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
|
||||||
|
return @intCast(u32, field_index_usize);
|
||||||
|
}
|
||||||
|
|||||||
12
src/type.zig
12
src/type.zig
@ -4519,21 +4519,23 @@ pub const Type = extern union {
|
|||||||
if (field.is_comptime) {
|
if (field.is_comptime) {
|
||||||
return field.default_val;
|
return field.default_val;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return field.ty.onePossibleValue();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.tuple => {
|
.tuple => {
|
||||||
const val = ty.castTag(.tuple).?.data.values[index];
|
const tuple = ty.castTag(.tuple).?.data;
|
||||||
|
const val = tuple.values[index];
|
||||||
if (val.tag() == .unreachable_value) {
|
if (val.tag() == .unreachable_value) {
|
||||||
return null;
|
return tuple.types[index].onePossibleValue();
|
||||||
} else {
|
} else {
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.anon_struct => {
|
.anon_struct => {
|
||||||
const val = ty.castTag(.anon_struct).?.data.values[index];
|
const anon_struct = ty.castTag(.anon_struct).?.data;
|
||||||
|
const val = anon_struct.values[index];
|
||||||
if (val.tag() == .unreachable_value) {
|
if (val.tag() == .unreachable_value) {
|
||||||
return null;
|
return anon_struct.types[index].onePossibleValue();
|
||||||
} else {
|
} else {
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -995,7 +995,11 @@ test "comptime struct field" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "tuple element initialized with fn call" {
|
test "tuple element initialized with fn call" {
|
||||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||||
|
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||||
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||||
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||||
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||||
|
|
||||||
const S = struct {
|
const S = struct {
|
||||||
fn doTheTest() !void {
|
fn doTheTest() !void {
|
||||||
@ -1011,7 +1015,7 @@ test "tuple element initialized with fn call" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "struct with union field" {
|
test "struct with union field" {
|
||||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||||
|
|
||||||
const Value = struct {
|
const Value = struct {
|
||||||
ref: u32 = 2,
|
ref: u32 = 2,
|
||||||
@ -1029,7 +1033,11 @@ test "struct with union field" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "type coercion of anon struct literal to struct" {
|
test "type coercion of anon struct literal to struct" {
|
||||||
if (builtin.zig_backend != .stage1) 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_c) return error.SkipZigTest; // TODO
|
||||||
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||||
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||||
|
|
||||||
const S = struct {
|
const S = struct {
|
||||||
const S2 = struct {
|
const S2 = struct {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user