authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-03-25 13:48:11+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-03-25 22:32:15+02:00
log2f326f24dd812281e321ec03aee3a8150201d389
tree971eef1e3575ac531fcf5d7e8a969a763e4fde7f
parent17d214a249e8d28aa01bc89325ba57918d5bb525

Sema: implement zirSwitchCapture multi for unions


2 files changed, 70 insertions(+), 56 deletions(-)

src/Sema.zig+68-55
...@@ -6938,12 +6938,19 @@ fn zirSwitchCapture(...@@ -6938,12 +6938,19 @@ fn zirSwitchCapture(
6938 const switch_info = zir_datas[capture_info.switch_inst].pl_node;6938 const switch_info = zir_datas[capture_info.switch_inst].pl_node;
6939 const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index);6939 const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index);
6940 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node };6940 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node };
6941 const switch_src = switch_info.src();
6941 const operand_is_ref = switch_extra.data.bits.is_ref;6942 const operand_is_ref = switch_extra.data.bits.is_ref;
6942 const cond_inst = Zir.refToIndex(switch_extra.data.operand).?;6943 const cond_inst = Zir.refToIndex(switch_extra.data.operand).?;
6943 const cond_info = sema.code.instructions.items(.data)[cond_inst].un_node;6944 const cond_info = sema.code.instructions.items(.data)[cond_inst].un_node;
6944 const operand_ptr = sema.resolveInst(cond_info.operand);6945 const operand_ptr = sema.resolveInst(cond_info.operand);
6945 const operand_ptr_ty = sema.typeOf(operand_ptr);6946 const operand_ptr_ty = sema.typeOf(operand_ptr);
6946 const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty;6947 const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty;
6948 const target = sema.mod.getTarget();
6949
6950 const operand = if (operand_is_ref)
6951 try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
6952 else
6953 operand_ptr;
69476954
6948 if (capture_info.prong_index == std.math.maxInt(@TypeOf(capture_info.prong_index))) {6955 if (capture_info.prong_index == std.math.maxInt(@TypeOf(capture_info.prong_index))) {
6949 // It is the else/`_` prong.6956 // It is the else/`_` prong.
...@@ -6952,64 +6959,57 @@ fn zirSwitchCapture(...@@ -6952,64 +6959,57 @@ fn zirSwitchCapture(
6952 return operand_ptr;6959 return operand_ptr;
6953 }6960 }
69546961
6955 const operand = if (operand_is_ref)
6956 try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
6957 else
6958 operand_ptr;
6959
6960 switch (operand_ty.zigTypeTag()) {6962 switch (operand_ty.zigTypeTag()) {
6961 .ErrorSet => return sema.bitCast(block, block.switch_else_err_ty.?, operand, operand_src),6963 .ErrorSet => return sema.bitCast(block, block.switch_else_err_ty.?, operand, operand_src),
6962 else => return operand,6964 else => return operand,
6963 }6965 }
6964 }6966 }
69656967
6966 if (is_multi) {6968 const items = if (is_multi)
6967 const items = switch_extra.data.getMultiProng(sema.code, switch_extra.end, capture_info.prong_index).items;6969 switch_extra.data.getMultiProng(sema.code, switch_extra.end, capture_info.prong_index).items
69686970 else
6969 var names: Module.ErrorSet.NameMap = .{};6971 &[_]Zir.Inst.Ref{
6970 try names.ensureUnusedCapacity(sema.arena, items.len);6972 switch_extra.data.getScalarProng(sema.code, switch_extra.end, capture_info.prong_index).item,
6971 for (items) |item| {6973 };
6972 const item_ref = sema.resolveInst(item);
6973 // Previous switch validation ensured this will succeed
6974 const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable;
6975 names.putAssumeCapacityNoClobber(
6976 item_val.getError().?,
6977 {},
6978 );
6979 }
6980
6981 // names must be sorted
6982 Module.ErrorSet.sortNames(&names);
6983 const else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names);
6984
6985 const operand = if (operand_is_ref)
6986 try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
6987 else
6988 operand_ptr;
6989 return sema.bitCast(block, else_error_ty, operand, operand_src);
6990 }
6991 const scalar_prong = switch_extra.data.getScalarProng(sema.code, switch_extra.end, capture_info.prong_index);
6992 const item = sema.resolveInst(scalar_prong.item);
6993 // Previous switch validation ensured this will succeed
6994 const item_val = sema.resolveConstValue(block, .unneeded, item) catch unreachable;
6995 const target = sema.mod.getTarget();
69966974
6997 switch (operand_ty.zigTypeTag()) {6975 switch (operand_ty.zigTypeTag()) {
6998 .Union => {6976 .Union => {
6999 const union_obj = operand_ty.cast(Type.Payload.Union).?.data;6977 const union_obj = operand_ty.cast(Type.Payload.Union).?.data;
7000 const enum_ty = union_obj.tag_ty;6978 const enum_ty = union_obj.tag_ty;
70016979
7002 const field_index_usize = enum_ty.enumTagFieldIndex(item_val, target).?;6980 const first_item = sema.resolveInst(items[0]);
7003 const field_index = @intCast(u32, field_index_usize);6981 // Previous switch validation ensured this will succeed
7004 const field = union_obj.fields.values()[field_index];6982 const first_item_val = sema.resolveConstValue(block, .unneeded, first_item) catch unreachable;
70056983
7006 // TODO handle multiple union tags which have compatible types6984 const first_field_index = @intCast(u32, enum_ty.enumTagFieldIndex(first_item_val, target).?);
6985 const first_field = union_obj.fields.values()[first_field_index];
6986
6987 for (items[1..]) |item| {
6988 const item_ref = sema.resolveInst(item);
6989 // Previous switch validation ensured this will succeed
6990 const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable;
6991
6992 const field_index = enum_ty.enumTagFieldIndex(item_val, target).?;
6993 const field = union_obj.fields.values()[field_index];
6994 if (!field.ty.eql(first_field.ty, target)) {
6995 const first_item_src = switch_src; // TODO better source location
6996 const item_src = switch_src;
6997 const msg = msg: {
6998 const msg = try sema.errMsg(block, switch_src, "capture group with incompatible types", .{});
6999 errdefer msg.destroy(sema.gpa);
7000 try sema.errNote(block, first_item_src, msg, "type '{}' here", .{first_field.ty.fmt(target)});
7001 try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(target)});
7002 break :msg msg;
7003 };
7004 return sema.failWithOwnedErrorMsg(block, msg);
7005 }
7006 }
70077007
7008 if (is_ref) {7008 if (is_ref) {
7009 assert(operand_is_ref);7009 assert(operand_is_ref);
70107010
7011 const field_ty_ptr = try Type.ptr(sema.arena, target, .{7011 const field_ty_ptr = try Type.ptr(sema.arena, target, .{
7012 .pointee_type = field.ty,7012 .pointee_type = first_field.ty,
7013 .@"addrspace" = .generic,7013 .@"addrspace" = .generic,
7014 .mutable = operand_ptr_ty.ptrIsMutable(),7014 .mutable = operand_ptr_ty.ptrIsMutable(),
7015 });7015 });
...@@ -7020,35 +7020,48 @@ fn zirSwitchCapture(...@@ -7020,35 +7020,48 @@ fn zirSwitchCapture(
7020 try Value.Tag.field_ptr.create(sema.arena, .{7020 try Value.Tag.field_ptr.create(sema.arena, .{
7021 .container_ptr = op_ptr_val,7021 .container_ptr = op_ptr_val,
7022 .container_ty = operand_ty,7022 .container_ty = operand_ty,
7023 .field_index = field_index,7023 .field_index = first_field_index,
7024 }),7024 }),
7025 );7025 );
7026 }7026 }
7027 try sema.requireRuntimeBlock(block, operand_src);7027 try sema.requireRuntimeBlock(block, operand_src);
7028 return block.addStructFieldPtr(operand_ptr, field_index, field_ty_ptr);7028 return block.addStructFieldPtr(operand_ptr, first_field_index, field_ty_ptr);
7029 }7029 }
70307030
7031 const operand = if (operand_is_ref)
7032 try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
7033 else
7034 operand_ptr;
7035
7036 if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| {7031 if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| {
7037 return sema.addConstant(7032 return sema.addConstant(
7038 field.ty,7033 first_field.ty,
7039 operand_val.castTag(.@"union").?.data.val,7034 operand_val.castTag(.@"union").?.data.val,
7040 );7035 );
7041 }7036 }
7042 try sema.requireRuntimeBlock(block, operand_src);7037 try sema.requireRuntimeBlock(block, operand_src);
7043 return block.addStructFieldVal(operand, field_index, field.ty);7038 return block.addStructFieldVal(operand, first_field_index, first_field.ty);
7044 },7039 },
7045 .ErrorSet => {7040 .ErrorSet => {
7046 const item_ty = try Type.Tag.error_set_single.create(sema.arena, item_val.getError().?);7041 if (is_multi) {
7047 const operand = if (operand_is_ref)7042 var names: Module.ErrorSet.NameMap = .{};
7048 try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)7043 try names.ensureUnusedCapacity(sema.arena, items.len);
7049 else7044 for (items) |item| {
7050 operand_ptr;7045 const item_ref = sema.resolveInst(item);
7051 return sema.bitCast(block, item_ty, operand, operand_src);7046 // Previous switch validation ensured this will succeed
7047 const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable;
7048 names.putAssumeCapacityNoClobber(
7049 item_val.getError().?,
7050 {},
7051 );
7052 }
7053 // names must be sorted
7054 Module.ErrorSet.sortNames(&names);
7055 const else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names);
7056
7057 return sema.bitCast(block, else_error_ty, operand, operand_src);
7058 } else {
7059 // Previous switch validation ensured this will succeed
7060 const item_val = sema.resolveConstValue(block, .unneeded, items[0]) catch unreachable;
7061
7062 const item_ty = try Type.Tag.error_set_single.create(sema.arena, item_val.getError().?);
7063 return sema.bitCast(block, item_ty, operand, operand_src);
7064 }
7052 },7065 },
7053 else => {7066 else => {
7054 return sema.fail(block, operand_src, "switch on type '{}' provides no capture value", .{7067 return sema.fail(block, operand_src, "switch on type '{}' provides no capture value", .{
test/behavior/switch.zig+2-1
...@@ -541,7 +541,8 @@ test "switch with null and T peer types and inferred result location type" {...@@ -541,7 +541,8 @@ test "switch with null and T peer types and inferred result location type" {
541}541}
542542
543test "switch prongs with cases with identical payload types" {543test "switch prongs with cases with identical payload types" {
544 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO544 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
545 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
545546
546 const Union = union(enum) {547 const Union = union(enum) {
547 A: usize,548 A: usize,