diff options
| -rw-r--r-- | src/Sema.zig | 106 | ||||
| -rw-r--r-- | src/Zir.zig | 47 | ||||
| -rw-r--r-- | src/type.zig | 6 | ||||
| -rw-r--r-- | test/behavior/switch.zig | 8 | ||||
| -rw-r--r-- | test/behavior/type.zig | 7 | ||||
| -rw-r--r-- | test/behavior/union.zig | 17 |
6 files changed, 159 insertions, 32 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index a42a4caf38..e1a8d6f09a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6945,6 +6945,12 @@ fn zirSwitchCapture( const operand_ptr = sema.resolveInst(cond_info.operand); const operand_ptr_ty = sema.typeOf(operand_ptr); const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty; + const target = sema.mod.getTarget(); + + const operand = if (operand_is_ref) + try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src) + else + operand_ptr; if (capture_info.prong_index == std.math.maxInt(@TypeOf(capture_info.prong_index))) { // It is the else/`_` prong. @@ -6953,42 +6959,57 @@ fn zirSwitchCapture( return operand_ptr; } - const operand = if (operand_is_ref) - try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src) - else - operand_ptr; - switch (operand_ty.zigTypeTag()) { .ErrorSet => return sema.bitCast(block, block.switch_else_err_ty.?, operand, operand_src), else => return operand, } } - if (is_multi) { - return sema.fail(block, switch_src, "TODO implement Sema for switch capture multi", .{}); - } - const scalar_prong = switch_extra.data.getScalarProng(sema.code, switch_extra.end, capture_info.prong_index); - const item = sema.resolveInst(scalar_prong.item); - // Previous switch validation ensured this will succeed - const item_val = sema.resolveConstValue(block, .unneeded, item) catch unreachable; - const target = sema.mod.getTarget(); + const items = if (is_multi) + switch_extra.data.getMultiProng(sema.code, switch_extra.end, capture_info.prong_index).items + else + &[_]Zir.Inst.Ref{ + switch_extra.data.getScalarProng(sema.code, switch_extra.end, capture_info.prong_index).item, + }; switch (operand_ty.zigTypeTag()) { .Union => { const union_obj = operand_ty.cast(Type.Payload.Union).?.data; const enum_ty = union_obj.tag_ty; - const field_index_usize = enum_ty.enumTagFieldIndex(item_val, target).?; - const field_index = @intCast(u32, field_index_usize); - const field = union_obj.fields.values()[field_index]; + const first_item = sema.resolveInst(items[0]); + // Previous switch validation ensured this will succeed + const first_item_val = sema.resolveConstValue(block, .unneeded, first_item) catch unreachable; + + const first_field_index = @intCast(u32, enum_ty.enumTagFieldIndex(first_item_val, target).?); + const first_field = union_obj.fields.values()[first_field_index]; - // TODO handle multiple union tags which have compatible types + for (items[1..]) |item| { + const item_ref = sema.resolveInst(item); + // Previous switch validation ensured this will succeed + const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable; + + const field_index = enum_ty.enumTagFieldIndex(item_val, target).?; + const field = union_obj.fields.values()[field_index]; + if (!field.ty.eql(first_field.ty, target)) { + const first_item_src = switch_src; // TODO better source location + const item_src = switch_src; + const msg = msg: { + const msg = try sema.errMsg(block, switch_src, "capture group with incompatible types", .{}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, first_item_src, msg, "type '{}' here", .{first_field.ty.fmt(target)}); + try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(target)}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + } if (is_ref) { assert(operand_is_ref); const field_ty_ptr = try Type.ptr(sema.arena, target, .{ - .pointee_type = field.ty, + .pointee_type = first_field.ty, .@"addrspace" = .generic, .mutable = operand_ptr_ty.ptrIsMutable(), }); @@ -6999,30 +7020,48 @@ fn zirSwitchCapture( try Value.Tag.field_ptr.create(sema.arena, .{ .container_ptr = op_ptr_val, .container_ty = operand_ty, - .field_index = field_index, + .field_index = first_field_index, }), ); } try sema.requireRuntimeBlock(block, operand_src); - return block.addStructFieldPtr(operand_ptr, field_index, field_ty_ptr); + return block.addStructFieldPtr(operand_ptr, first_field_index, field_ty_ptr); } - const operand = if (operand_is_ref) - try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src) - else - operand_ptr; - if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| { return sema.addConstant( - field.ty, + first_field.ty, operand_val.castTag(.@"union").?.data.val, ); } try sema.requireRuntimeBlock(block, operand_src); - return block.addStructFieldVal(operand, field_index, field.ty); + return block.addStructFieldVal(operand, first_field_index, first_field.ty); }, .ErrorSet => { - return sema.fail(block, operand_src, "TODO implement Sema for zirSwitchCapture for error sets", .{}); + if (is_multi) { + var names: Module.ErrorSet.NameMap = .{}; + try names.ensureUnusedCapacity(sema.arena, items.len); + for (items) |item| { + const item_ref = sema.resolveInst(item); + // Previous switch validation ensured this will succeed + const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable; + names.putAssumeCapacityNoClobber( + item_val.getError().?, + {}, + ); + } + // names must be sorted + Module.ErrorSet.sortNames(&names); + const else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names); + + return sema.bitCast(block, else_error_ty, operand, operand_src); + } else { + // Previous switch validation ensured this will succeed + const item_val = sema.resolveConstValue(block, .unneeded, items[0]) catch unreachable; + + const item_ty = try Type.Tag.error_set_single.create(sema.arena, item_val.getError().?); + return sema.bitCast(block, item_ty, operand, operand_src); + } }, else => { return sema.fail(block, operand_src, "switch on type '{}' provides no capture value", .{ @@ -7390,6 +7429,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError names.putAssumeCapacityNoClobber(error_name, {}); } + // names must be sorted + Module.ErrorSet.sortNames(&names); else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names); } }, @@ -7743,6 +7784,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError } if (scalar_cases_len + multi_cases_len == 0) { + if (special_prong == .none) { + return sema.fail(block, src, "switch must handle all possibilities", .{}); + } return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges); } @@ -12233,7 +12277,9 @@ fn zirStructInit( return alloc; } - return sema.fail(block, src, "TODO: Sema.zirStructInit for runtime-known union values", .{}); + try sema.requireRuntimeBlock(block, src); + try sema.queueFullTypeResolution(resolved_ty); + return block.addUnionInit(resolved_ty, field_index, init_inst); } unreachable; } @@ -12976,6 +13022,8 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I ); } + // names must be sorted + Module.ErrorSet.sortNames(&names); const ty = try Type.Tag.error_set_merged.create(sema.arena, names); return sema.addType(ty); }, diff --git a/src/Zir.zig b/src/Zir.zig index f9b80c88ef..d6e8486ea7 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -2620,6 +2620,53 @@ pub const Inst = struct { }; } } + + pub const MultiProng = struct { + items: []const Ref, + body: []const Index, + }; + + pub fn getMultiProng( + self: SwitchBlock, + zir: Zir, + extra_end: usize, + prong_index: usize, + ) MultiProng { + // +1 for self.bits.has_multi_cases == true + var extra_index: usize = extra_end + 1; + + if (self.bits.specialProng() != .none) { + const body_len = zir.extra[extra_index]; + extra_index += 1; + const body = zir.extra[extra_index..][0..body_len]; + extra_index += body.len; + } + + var scalar_i: usize = 0; + while (scalar_i < self.bits.scalar_cases_len) : (scalar_i += 1) { + extra_index += 1; + const body_len = zir.extra[extra_index]; + extra_index += 1; + extra_index += body_len; + } + var multi_i: u32 = 0; + while (true) : (multi_i += 1) { + const items_len = zir.extra[extra_index]; + extra_index += 2; + const body_len = zir.extra[extra_index]; + extra_index += 1; + const items = zir.refSlice(extra_index, items_len); + extra_index += items_len; + const body = zir.extra[extra_index..][0..body_len]; + extra_index += body_len; + + if (multi_i < prong_index) continue; + return .{ + .items = items, + .body = body, + }; + } + } }; pub const Field = struct { diff --git a/src/type.zig b/src/type.zig index d5b8e6f5b3..17c8d4d111 100644 --- a/src/type.zig +++ b/src/type.zig @@ -4511,7 +4511,11 @@ pub const Type = extern union { .enum_full => { const enum_full = ty.castTag(.enum_full).?.data; if (enum_full.fields.count() == 1) { - return enum_full.values.keys()[0]; + if (enum_full.values.count() == 0) { + return Value.zero; + } else { + return enum_full.values.keys()[0]; + } } else { return null; } diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index db670e34a7..b988f32a38 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -465,7 +465,10 @@ test "else prong of switch on error set excludes other cases" { } test "switch prongs with error set cases make a new error set type for capture value" { - if (builtin.zig_backend != .stage1) 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 + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -538,7 +541,8 @@ test "switch with null and T peer types and inferred result location type" { } test "switch prongs with cases with identical payload types" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const Union = union(enum) { A: usize, diff --git a/test/behavior/type.zig b/test/behavior/type.zig index e3c896d0f7..65bebff946 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -260,6 +260,13 @@ test "Type.ErrorSet" { .{ .name = "C" }, }, }); + _ = @Type(.{ + .ErrorSet = &.{ + .{ .name = "C" }, + .{ .name = "B" }, + .{ .name = "A" }, + }, + }); } test "Type.Struct" { diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 0541817145..532d4f79eb 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1132,3 +1132,20 @@ test "global variable struct contains union initialized to non-most-aligned fiel T.s.u.a += 1; try expect(T.s.u.a == 4); } + +test "union with no result loc initiated with a runtime value" { + 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 + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const U = union { + a: u32, + b: u32, + fn foo(u: @This()) void { + _ = u; + } + }; + var a: u32 = 1; + U.foo(U{ .a = a }); +} |
