| author | |
| committer | |
| log | 09e1f37cb6a8e0df4c521f4b76eab07f0c811852 |
| tree | cce1b86a1cd273841a660e5438f714cfbaea74a7 |
| parent | c2a7542df5e9e93289a5d487ba3bbc37c12ffc11 |
* AIR: add `get_union_tag` instruction
- implement in LLVM backend
* Sema: implement == and != for union and enum literal
- Also implement coercion from union to its own tag type
* Value: implement hashing for union values
The motivating example is this snippet:
comptime assert(@typeInfo(T) == .Float);
This was the next blocker for stage2 building compiler-rt.
Now it is switch at compile-time on an integer.11 files changed, 162 insertions(+), 33 deletions(-)
src/Air.zig+4| ... | @@ -290,6 +290,9 @@ pub const Inst = struct { | ... | @@ -290,6 +290,9 @@ pub const Inst = struct { |
| 290 | /// Result type is always void. | 290 | /// Result type is always void. |
| 291 | /// Uses the `bin_op` field. LHS is union pointer, RHS is new tag value. | 291 | /// Uses the `bin_op` field. LHS is union pointer, RHS is new tag value. |
| 292 | set_union_tag, | 292 | set_union_tag, |
| 293 | /// Given a tagged union value, get its tag value. | ||
| 294 | /// Uses the `ty_op` field. | ||
| 295 | get_union_tag, | ||
| 293 | /// Given a slice value, return the length. | 296 | /// Given a slice value, return the length. |
| 294 | /// Result type is always usize. | 297 | /// Result type is always usize. |
| 295 | /// Uses the `ty_op` field. | 298 | /// Uses the `ty_op` field. |
| ... | @@ -630,6 +633,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { | ... | @@ -630,6 +633,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 630 | .array_to_slice, | 633 | .array_to_slice, |
| 631 | .float_to_int, | 634 | .float_to_int, |
| 632 | .int_to_float, | 635 | .int_to_float, |
| 636 | .get_union_tag, | ||
| 633 | => return air.getRefType(datas[inst].ty_op.ty), | 637 | => return air.getRefType(datas[inst].ty_op.ty), |
| 634 | 638 | ||
| 635 | .loop, | 639 | .loop, |
src/Liveness.zig+1| ... | @@ -297,6 +297,7 @@ fn analyzeInst( | ... | @@ -297,6 +297,7 @@ fn analyzeInst( |
| 297 | .array_to_slice, | 297 | .array_to_slice, |
| 298 | .float_to_int, | 298 | .float_to_int, |
| 299 | .int_to_float, | 299 | .int_to_float, |
| 300 | .get_union_tag, | ||
| 300 | => { | 301 | => { |
| 301 | const o = inst_datas[inst].ty_op; | 302 | const o = inst_datas[inst].ty_op; |
| 302 | return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none }); | 303 | return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none }); |
src/Sema.zig+72-10| ... | @@ -1349,7 +1349,13 @@ fn zirUnionDecl( | ... | @@ -1349,7 +1349,13 @@ fn zirUnionDecl( |
| 1349 | errdefer new_decl_arena.deinit(); | 1349 | errdefer new_decl_arena.deinit(); |
| 1350 | 1350 | ||
| 1351 | const union_obj = try new_decl_arena.allocator.create(Module.Union); | 1351 | const union_obj = try new_decl_arena.allocator.create(Module.Union); |
| 1352 | const union_ty = try Type.Tag.@"union".create(&new_decl_arena.allocator, union_obj); | 1352 | const type_tag: Type.Tag = if (small.has_tag_type or small.auto_enum_tag) .union_tagged else .@"union"; |
| 1353 | const union_payload = try new_decl_arena.allocator.create(Type.Payload.Union); | ||
| 1354 | union_payload.* = .{ | ||
| 1355 | .base = .{ .tag = type_tag }, | ||
| 1356 | .data = union_obj, | ||
| 1357 | }; | ||
| 1358 | const union_ty = Type.initPayload(&union_payload.base); | ||
| 1353 | const union_val = try Value.Tag.ty.create(&new_decl_arena.allocator, union_ty); | 1359 | const union_val = try Value.Tag.ty.create(&new_decl_arena.allocator, union_ty); |
| 1354 | const type_name = try sema.createTypeName(block, small.name_strategy); | 1360 | const type_name = try sema.createTypeName(block, small.name_strategy); |
| 1355 | const new_decl = try sema.mod.createAnonymousDeclNamed(&block.base, .{ | 1361 | const new_decl = try sema.mod.createAnonymousDeclNamed(&block.base, .{ |
| ... | @@ -6477,10 +6483,11 @@ fn zirCmpEq( | ... | @@ -6477,10 +6483,11 @@ fn zirCmpEq( |
| 6477 | const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty; | 6483 | const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty; |
| 6478 | return mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type}); | 6484 | return mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type}); |
| 6479 | } | 6485 | } |
| 6480 | if (((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or | 6486 | if (lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) { |
| 6481 | (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union))) | 6487 | return sema.analyzeCmpUnionTag(block, rhs, rhs_src, lhs, lhs_src, op); |
| 6482 | { | 6488 | } |
| 6483 | return mod.fail(&block.base, src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); | 6489 | if (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union) { |
| 6490 | return sema.analyzeCmpUnionTag(block, lhs, lhs_src, rhs, rhs_src, op); | ||
| 6484 | } | 6491 | } |
| 6485 | if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { | 6492 | if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { |
| 6486 | const runtime_src: LazySrcLoc = src: { | 6493 | const runtime_src: LazySrcLoc = src: { |
| ... | @@ -6521,6 +6528,28 @@ fn zirCmpEq( | ... | @@ -6521,6 +6528,28 @@ fn zirCmpEq( |
| 6521 | return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true); | 6528 | return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true); |
| 6522 | } | 6529 | } |
| 6523 | 6530 | ||
| 6531 | fn analyzeCmpUnionTag( | ||
| 6532 | sema: *Sema, | ||
| 6533 | block: *Scope.Block, | ||
| 6534 | un: Air.Inst.Ref, | ||
| 6535 | un_src: LazySrcLoc, | ||
| 6536 | tag: Air.Inst.Ref, | ||
| 6537 | tag_src: LazySrcLoc, | ||
| 6538 | op: std.math.CompareOperator, | ||
| 6539 | ) CompileError!Air.Inst.Ref { | ||
| 6540 | const union_ty = sema.typeOf(un); | ||
| 6541 | const union_tag_ty = union_ty.unionTagType() orelse { | ||
| 6542 | // TODO note at declaration site that says "union foo is not tagged" | ||
| 6543 | return sema.mod.fail(&block.base, un_src, "comparison of union and enum literal is only valid for tagged union types", .{}); | ||
| 6544 | }; | ||
| 6545 | // Coerce both the union and the tag to the union's tag type, and then execute the | ||
| 6546 | // enum comparison codepath. | ||
| 6547 | const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src); | ||
| 6548 | const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src); | ||
| 6549 | |||
| 6550 | return sema.cmpSelf(block, coerced_union, coerced_tag, op, un_src, tag_src); | ||
| 6551 | } | ||
| 6552 | |||
| 6524 | /// Only called for non-equality operators. See also `zirCmpEq`. | 6553 | /// Only called for non-equality operators. See also `zirCmpEq`. |
| 6525 | fn zirCmp( | 6554 | fn zirCmp( |
| 6526 | sema: *Sema, | 6555 | sema: *Sema, |
| ... | @@ -6567,10 +6596,21 @@ fn analyzeCmp( | ... | @@ -6567,10 +6596,21 @@ fn analyzeCmp( |
| 6567 | @tagName(op), resolved_type, | 6596 | @tagName(op), resolved_type, |
| 6568 | }); | 6597 | }); |
| 6569 | } | 6598 | } |
| 6570 | |||
| 6571 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); | 6599 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 6572 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); | 6600 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 6601 | return sema.cmpSelf(block, casted_lhs, casted_rhs, op, lhs_src, rhs_src); | ||
| 6602 | } | ||
| 6573 | 6603 | ||
| 6604 | fn cmpSelf( | ||
| 6605 | sema: *Sema, | ||
| 6606 | block: *Scope.Block, | ||
| 6607 | casted_lhs: Air.Inst.Ref, | ||
| 6608 | casted_rhs: Air.Inst.Ref, | ||
| 6609 | op: std.math.CompareOperator, | ||
| 6610 | lhs_src: LazySrcLoc, | ||
| 6611 | rhs_src: LazySrcLoc, | ||
| 6612 | ) CompileError!Air.Inst.Ref { | ||
| 6613 | const resolved_type = sema.typeOf(casted_lhs); | ||
| 6574 | const runtime_src: LazySrcLoc = src: { | 6614 | const runtime_src: LazySrcLoc = src: { |
| 6575 | if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { | 6615 | if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { |
| 6576 | if (lhs_val.isUndef()) return sema.addConstUndef(resolved_type); | 6616 | if (lhs_val.isUndef()) return sema.addConstUndef(resolved_type); |
| ... | @@ -9919,9 +9959,9 @@ fn coerce( | ... | @@ -9919,9 +9959,9 @@ fn coerce( |
| 9919 | } | 9959 | } |
| 9920 | } | 9960 | } |
| 9921 | }, | 9961 | }, |
| 9922 | .Enum => { | 9962 | .Enum => switch (inst_ty.zigTypeTag()) { |
| 9923 | // enum literal to enum | 9963 | .EnumLiteral => { |
| 9924 | if (inst_ty.zigTypeTag() == .EnumLiteral) { | 9964 | // enum literal to enum |
| 9925 | const val = try sema.resolveConstValue(block, inst_src, inst); | 9965 | const val = try sema.resolveConstValue(block, inst_src, inst); |
| 9926 | const bytes = val.castTag(.enum_literal).?.data; | 9966 | const bytes = val.castTag(.enum_literal).?.data; |
| 9927 | const resolved_dest_type = try sema.resolveTypeFields(block, inst_src, dest_type); | 9967 | const resolved_dest_type = try sema.resolveTypeFields(block, inst_src, dest_type); |
| ... | @@ -9948,7 +9988,15 @@ fn coerce( | ... | @@ -9948,7 +9988,15 @@ fn coerce( |
| 9948 | resolved_dest_type, | 9988 | resolved_dest_type, |
| 9949 | try Value.Tag.enum_field_index.create(arena, @intCast(u32, field_index)), | 9989 | try Value.Tag.enum_field_index.create(arena, @intCast(u32, field_index)), |
| 9950 | ); | 9990 | ); |
| 9951 | } | 9991 | }, |
| 9992 | .Union => blk: { | ||
| 9993 | // union to its own tag type | ||
| 9994 | const union_tag_ty = inst_ty.unionTagType() orelse break :blk; | ||
| 9995 | if (union_tag_ty.eql(dest_type)) { | ||
| 9996 | return sema.unionToTag(block, dest_type, inst, inst_src); | ||
| 9997 | } | ||
| 9998 | }, | ||
| 9999 | else => {}, | ||
| 9952 | }, | 10000 | }, |
| 9953 | .ErrorUnion => { | 10001 | .ErrorUnion => { |
| 9954 | // T to E!T or E to E!T | 10002 | // T to E!T or E to E!T |
| ... | @@ -10802,6 +10850,20 @@ fn wrapErrorUnion( | ... | @@ -10802,6 +10850,20 @@ fn wrapErrorUnion( |
| 10802 | } | 10850 | } |
| 10803 | } | 10851 | } |
| 10804 | 10852 | ||
| 10853 | fn unionToTag( | ||
| 10854 | sema: *Sema, | ||
| 10855 | block: *Scope.Block, | ||
| 10856 | dest_type: Type, | ||
| 10857 | un: Air.Inst.Ref, | ||
| 10858 | un_src: LazySrcLoc, | ||
| 10859 | ) !Air.Inst.Ref { | ||
| 10860 | if (try sema.resolveMaybeUndefVal(block, un_src, un)) |un_val| { | ||
| 10861 | return sema.addConstant(dest_type, un_val.unionTag()); | ||
| 10862 | } | ||
| 10863 | try sema.requireRuntimeBlock(block, un_src); | ||
| 10864 | return block.addTyOp(.get_union_tag, dest_type, un); | ||
| 10865 | } | ||
| 10866 | |||
| 10805 | fn resolvePeerTypes( | 10867 | fn resolvePeerTypes( |
| 10806 | sema: *Sema, | 10868 | sema: *Sema, |
| 10807 | block: *Scope.Block, | 10869 | block: *Scope.Block, |
src/codegen.zig+9| ... | @@ -890,6 +890,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -890,6 +890,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 890 | .memcpy => try self.airMemcpy(inst), | 890 | .memcpy => try self.airMemcpy(inst), |
| 891 | .memset => try self.airMemset(inst), | 891 | .memset => try self.airMemset(inst), |
| 892 | .set_union_tag => try self.airSetUnionTag(inst), | 892 | .set_union_tag => try self.airSetUnionTag(inst), |
| 893 | .get_union_tag => try self.airGetUnionTag(inst), | ||
| 893 | 894 | ||
| 894 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), | 895 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 895 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), | 896 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| ... | @@ -1552,6 +1553,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1552,6 +1553,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1552 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1553 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1553 | } | 1554 | } |
| 1554 | 1555 | ||
| 1556 | fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { | ||
| 1557 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | ||
| 1558 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | ||
| 1559 | else => return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch}), | ||
| 1560 | }; | ||
| 1561 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | ||
| 1562 | } | ||
| 1563 | |||
| 1555 | fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { | 1564 | fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { |
| 1556 | if (!self.liveness.operandDies(inst, op_index)) | 1565 | if (!self.liveness.operandDies(inst, op_index)) |
| 1557 | return false; | 1566 | return false; |
src/codegen/c.zig+17| ... | @@ -956,6 +956,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO | ... | @@ -956,6 +956,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 956 | .memset => try airMemset(f, inst), | 956 | .memset => try airMemset(f, inst), |
| 957 | .memcpy => try airMemcpy(f, inst), | 957 | .memcpy => try airMemcpy(f, inst), |
| 958 | .set_union_tag => try airSetUnionTag(f, inst), | 958 | .set_union_tag => try airSetUnionTag(f, inst), |
| 959 | .get_union_tag => try airGetUnionTag(f, inst), | ||
| 959 | 960 | ||
| 960 | .int_to_float, | 961 | .int_to_float, |
| 961 | .float_to_int, | 962 | .float_to_int, |
| ... | @@ -2096,6 +2097,22 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -2096,6 +2097,22 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2096 | return CValue.none; | 2097 | return CValue.none; |
| 2097 | } | 2098 | } |
| 2098 | 2099 | ||
| 2100 | fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { | ||
| 2101 | if (f.liveness.isUnused(inst)) | ||
| 2102 | return CValue.none; | ||
| 2103 | |||
| 2104 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 2105 | const local = try f.allocLocal(inst_ty, .Const); | ||
| 2106 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | ||
| 2107 | const writer = f.object.writer(); | ||
| 2108 | const operand = try f.resolveInst(ty_op.operand); | ||
| 2109 | |||
| 2110 | try writer.writeAll("get_union_tag("); | ||
| 2111 | try f.writeCValue(writer, operand); | ||
| 2112 | try writer.writeAll(");\n"); | ||
| 2113 | return local; | ||
| 2114 | } | ||
| 2115 | |||
| 2099 | fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { | 2116 | fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { |
| 2100 | return switch (order) { | 2117 | return switch (order) { |
| 2101 | .Unordered => "memory_order_relaxed", | 2118 | .Unordered => "memory_order_relaxed", |
src/codegen/llvm.zig+13| ... | @@ -1304,6 +1304,7 @@ pub const FuncGen = struct { | ... | @@ -1304,6 +1304,7 @@ pub const FuncGen = struct { |
| 1304 | .memset => try self.airMemset(inst), | 1304 | .memset => try self.airMemset(inst), |
| 1305 | .memcpy => try self.airMemcpy(inst), | 1305 | .memcpy => try self.airMemcpy(inst), |
| 1306 | .set_union_tag => try self.airSetUnionTag(inst), | 1306 | .set_union_tag => try self.airSetUnionTag(inst), |
| 1307 | .get_union_tag => try self.airGetUnionTag(inst), | ||
| 1307 | 1308 | ||
| 1308 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), | 1309 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 1309 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), | 1310 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| ... | @@ -2557,6 +2558,18 @@ pub const FuncGen = struct { | ... | @@ -2557,6 +2558,18 @@ pub const FuncGen = struct { |
| 2557 | return null; | 2558 | return null; |
| 2558 | } | 2559 | } |
| 2559 | 2560 | ||
| 2561 | fn airGetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | ||
| 2562 | if (self.liveness.isUnused(inst)) | ||
| 2563 | return null; | ||
| 2564 | |||
| 2565 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | ||
| 2566 | const un_ty = self.air.typeOf(ty_op.operand); | ||
| 2567 | const un = try self.resolveInst(ty_op.operand); | ||
| 2568 | |||
| 2569 | _ = un_ty; // TODO handle when onlyTagHasCodegenBits() == true and other union forms | ||
| 2570 | return self.builder.buildExtractValue(un, 1, ""); | ||
| 2571 | } | ||
| 2572 | |||
| 2560 | fn fieldPtr( | 2573 | fn fieldPtr( |
| 2561 | self: *FuncGen, | 2574 | self: *FuncGen, |
| 2562 | inst: Air.Inst.Index, | 2575 | inst: Air.Inst.Index, |
src/print_air.zig+1| ... | @@ -179,6 +179,7 @@ const Writer = struct { | ... | @@ -179,6 +179,7 @@ const Writer = struct { |
| 179 | .array_to_slice, | 179 | .array_to_slice, |
| 180 | .int_to_float, | 180 | .int_to_float, |
| 181 | .float_to_int, | 181 | .float_to_int, |
| 182 | .get_union_tag, | ||
| 182 | => try w.writeTyOp(s, inst), | 183 | => try w.writeTyOp(s, inst), |
| 183 | 184 | ||
| 184 | .block, | 185 | .block, |
src/type.zig+8| ... | @@ -2487,6 +2487,12 @@ pub const Type = extern union { | ... | @@ -2487,6 +2487,12 @@ pub const Type = extern union { |
| 2487 | }; | 2487 | }; |
| 2488 | } | 2488 | } |
| 2489 | 2489 | ||
| 2490 | pub fn unionFieldType(ty: Type, enum_tag: Value) Type { | ||
| 2491 | const union_obj = ty.cast(Payload.Union).?.data; | ||
| 2492 | const index = union_obj.tag_ty.enumTagFieldIndex(enum_tag).?; | ||
| 2493 | return union_obj.fields.values()[index].ty; | ||
| 2494 | } | ||
| 2495 | |||
| 2490 | /// Asserts that the type is an error union. | 2496 | /// Asserts that the type is an error union. |
| 2491 | pub fn errorUnionPayload(self: Type) Type { | 2497 | pub fn errorUnionPayload(self: Type) Type { |
| 2492 | return switch (self.tag()) { | 2498 | return switch (self.tag()) { |
| ... | @@ -3801,6 +3807,8 @@ pub const Type = extern union { | ... | @@ -3801,6 +3807,8 @@ pub const Type = extern union { |
| 3801 | }; | 3807 | }; |
| 3802 | }; | 3808 | }; |
| 3803 | 3809 | ||
| 3810 | pub const @"bool" = initTag(.bool); | ||
| 3811 | |||
| 3804 | pub fn ptr(arena: *Allocator, d: Payload.Pointer.Data) !Type { | 3812 | pub fn ptr(arena: *Allocator, d: Payload.Pointer.Data) !Type { |
| 3805 | assert(d.host_size == 0 or d.bit_offset < d.host_size * 8); | 3813 | assert(d.host_size == 0 or d.bit_offset < d.host_size * 8); |
| 3806 | 3814 |
src/value.zig+14-1| ... | @@ -1275,7 +1275,12 @@ pub const Value = extern union { | ... | @@ -1275,7 +1275,12 @@ pub const Value = extern union { |
| 1275 | } | 1275 | } |
| 1276 | }, | 1276 | }, |
| 1277 | .Union => { | 1277 | .Union => { |
| 1278 | @panic("TODO implement hashing union values"); | 1278 | const union_obj = val.castTag(.@"union").?.data; |
| 1279 | if (ty.unionTagType()) |tag_ty| { | ||
| 1280 | union_obj.tag.hash(tag_ty, hasher); | ||
| 1281 | } | ||
| 1282 | const active_field_ty = ty.unionFieldType(union_obj.tag); | ||
| 1283 | union_obj.val.hash(active_field_ty, hasher); | ||
| 1279 | }, | 1284 | }, |
| 1280 | .Fn => { | 1285 | .Fn => { |
| 1281 | @panic("TODO implement hashing function values"); | 1286 | @panic("TODO implement hashing function values"); |
| ... | @@ -1431,6 +1436,14 @@ pub const Value = extern union { | ... | @@ -1431,6 +1436,14 @@ pub const Value = extern union { |
| 1431 | } | 1436 | } |
| 1432 | } | 1437 | } |
| 1433 | 1438 | ||
| 1439 | pub fn unionTag(val: Value) Value { | ||
| 1440 | switch (val.tag()) { | ||
| 1441 | .undef => return val, | ||
| 1442 | .@"union" => return val.castTag(.@"union").?.data.tag, | ||
| 1443 | else => unreachable, | ||
| 1444 | } | ||
| 1445 | } | ||
| 1446 | |||
| 1434 | /// Returns a pointer to the element value at the index. | 1447 | /// Returns a pointer to the element value at the index. |
| 1435 | pub fn elemPtr(self: Value, allocator: *Allocator, index: usize) !Value { | 1448 | pub fn elemPtr(self: Value, allocator: *Allocator, index: usize) !Value { |
| 1436 | if (self.castTag(.elem_ptr)) |elem_ptr| { | 1449 | if (self.castTag(.elem_ptr)) |elem_ptr| { |
test/behavior/union.zig+18| ... | @@ -14,3 +14,21 @@ test "basic unions" { | ... | @@ -14,3 +14,21 @@ test "basic unions" { |
| 14 | foo = Foo{ .float = 12.34 }; | 14 | foo = Foo{ .float = 12.34 }; |
| 15 | try expect(foo.float == 12.34); | 15 | try expect(foo.float == 12.34); |
| 16 | } | 16 | } |
| 17 | |||
| 18 | test "init union with runtime value" { | ||
| 19 | var foo: Foo = undefined; | ||
| 20 | |||
| 21 | setFloat(&foo, 12.34); | ||
| 22 | try expect(foo.float == 12.34); | ||
| 23 | |||
| 24 | setInt(&foo, 42); | ||
| 25 | try expect(foo.int == 42); | ||
| 26 | } | ||
| 27 | |||
| 28 | fn setFloat(foo: *Foo, x: f64) void { | ||
| 29 | foo.* = Foo{ .float = x }; | ||
| 30 | } | ||
| 31 | |||
| 32 | fn setInt(foo: *Foo, x: i32) void { | ||
| 33 | foo.* = Foo{ .int = x }; | ||
| 34 | } |
test/behavior/union_stage1.zig+5-22| ... | @@ -49,24 +49,6 @@ test "comptime union field access" { | ... | @@ -49,24 +49,6 @@ test "comptime union field access" { |
| 49 | } | 49 | } |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | test "init union with runtime value" { | ||
| 53 | var foo: Foo = undefined; | ||
| 54 | |||
| 55 | setFloat(&foo, 12.34); | ||
| 56 | try expect(foo.float == 12.34); | ||
| 57 | |||
| 58 | setInt(&foo, 42); | ||
| 59 | try expect(foo.int == 42); | ||
| 60 | } | ||
| 61 | |||
| 62 | fn setFloat(foo: *Foo, x: f64) void { | ||
| 63 | foo.* = Foo{ .float = x }; | ||
| 64 | } | ||
| 65 | |||
| 66 | fn setInt(foo: *Foo, x: i32) void { | ||
| 67 | foo.* = Foo{ .int = x }; | ||
| 68 | } | ||
| 69 | |||
| 70 | const FooExtern = extern union { | 52 | const FooExtern = extern union { |
| 71 | float: f64, | 53 | float: f64, |
| 72 | int: i32, | 54 | int: i32, |
| ... | @@ -185,12 +167,13 @@ test "union field access gives the enum values" { | ... | @@ -185,12 +167,13 @@ test "union field access gives the enum values" { |
| 185 | } | 167 | } |
| 186 | 168 | ||
| 187 | test "cast union to tag type of union" { | 169 | test "cast union to tag type of union" { |
| 188 | try testCastUnionToTag(TheUnion{ .B = 1234 }); | 170 | try testCastUnionToTag(); |
| 189 | comptime try testCastUnionToTag(TheUnion{ .B = 1234 }); | 171 | comptime try testCastUnionToTag(); |
| 190 | } | 172 | } |
| 191 | 173 | ||
| 192 | fn testCastUnionToTag(x: TheUnion) !void { | 174 | fn testCastUnionToTag() !void { |
| 193 | try expect(@as(TheTag, x) == TheTag.B); | 175 | var u = TheUnion{ .B = 1234 }; |
| 176 | try expect(@as(TheTag, u) == TheTag.B); | ||
| 194 | } | 177 | } |
| 195 | 178 | ||
| 196 | test "cast tag type of union to union" { | 179 | test "cast tag type of union to union" { |