authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-07 14:18:14-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-07 14:18:14-07:00
log47531b7d9389c45af3e46b623235792f14a40ff2
tree9412cb8c20a690381e58d607e6348028e19ab3d2
parenta7221ef4e902e63e72524559a067afcf6c1dfd17

Sema: support enough to check main calling convention via `@typeInfo`

After this commit, `pub export fn main() c_int { ... }` will be correctly detected as the intended entry point, and therefore start code will not try to export its own conflicting `main` function. * Implement basic union support - lots of stuff is still TODO, including runtime field access - also TODO: resolving the union tag type - comptime field access is implemented * DRY up some code by using the `Zir.DeclIterator` for skipping over decls in structs and unions. * Start to clean up Sema with regards to calling `.value()` to find out a const value. Instead, Sema code should call one of these two: - `resolvePossiblyUndefinedValue` (followed by logic dealing with undefined values) - `resolveDefinedValue` (a compile error will be emitted if the value is undefined) * An exported function with an unspecified calling convention gets the C calling convention. * Implement comptime field access for structs. * Add another implementation of "type has one possible value" in Sema. This is a bit unfortunate since the logic is duplicated, but the one in Type asserts that the types are resolved already, and is appropriate to call from codegen, while the one in Sema performs type resolution if necessary, reporting any compile errors that occur in the process.

6 files changed, 705 insertions(+), 72 deletions(-)

BRANCH_TODO-4
......@@ -1,4 +1,3 @@
1 * start.zig should support pub export fn main with -ofmt=c
21 * get stage2 tests passing
32 * modify stage2 tests so that only 1 uses _start and the rest use
43 pub fn main
......@@ -61,6 +60,3 @@
6160
6261 * AstGen threadlocal
6362 * extern "foo" for vars
64
65 * TODO all decls should probably store source hash. Without this,
66 we currently unnecessarily mark all anon decls outdated here.
src/Module.zig+216-22
......@@ -456,6 +456,16 @@ pub const Decl = struct {
456456 return struct_obj;
457457 }
458458
459 /// If the Decl has a value and it is a union, return it,
460 /// otherwise null.
461 pub fn getUnion(decl: *Decl) ?*Union {
462 if (!decl.has_tv) return null;
463 const ty = (decl.val.castTag(.ty) orelse return null).data;
464 const union_obj = (ty.cast(Type.Payload.Union) orelse return null).data;
465 if (union_obj.owner_decl != decl) return null;
466 return union_obj;
467 }
468
459469 /// If the Decl has a value and it is a function, return it,
460470 /// otherwise null.
461471 pub fn getFunction(decl: *Decl) ?*Fn {
......@@ -571,6 +581,18 @@ pub const Struct = struct {
571581 .lazy = .{ .node_offset = s.node_offset },
572582 };
573583 }
584
585 pub fn haveFieldTypes(s: Struct) bool {
586 return switch (s.status) {
587 .none,
588 .field_types_wip,
589 => false,
590 .have_field_types,
591 .layout_wip,
592 .have_layout,
593 => true,
594 };
595 }
574596};
575597
576598/// Represents the data that an enum declaration provides, when the fields
......@@ -624,6 +646,52 @@ pub const EnumFull = struct {
624646 }
625647};
626648
649pub const Union = struct {
650 /// The Decl that corresponds to the union itself.
651 owner_decl: *Decl,
652 /// An enum type which is used for the tag of the union.
653 /// This type is created even for untagged unions, even when the memory
654 /// layout does not store the tag.
655 /// Whether zig chooses this type or the user specifies it, it is stored here.
656 /// This will be set to the null type until status is `have_field_types`.
657 tag_ty: Type,
658 /// Set of field names in declaration order.
659 fields: std.StringArrayHashMapUnmanaged(Field),
660 /// Represents the declarations inside this union.
661 namespace: Scope.Namespace,
662 /// Offset from `owner_decl`, points to the union decl AST node.
663 node_offset: i32,
664 /// Index of the union_decl ZIR instruction.
665 zir_index: Zir.Inst.Index,
666
667 layout: std.builtin.TypeInfo.ContainerLayout,
668 status: enum {
669 none,
670 field_types_wip,
671 have_field_types,
672 layout_wip,
673 have_layout,
674 },
675
676 pub const Field = struct {
677 /// undefined until `status` is `have_field_types` or `have_layout`.
678 ty: Type,
679 abi_align: Value,
680 };
681
682 pub fn getFullyQualifiedName(s: *Union, gpa: *Allocator) ![]u8 {
683 return s.owner_decl.getFullyQualifiedName(gpa);
684 }
685
686 pub fn srcLoc(self: Union) SrcLoc {
687 return .{
688 .file_scope = self.owner_decl.getFileScope(),
689 .parent_decl_node = self.owner_decl.src_node,
690 .lazy = .{ .node_offset = self.node_offset },
691 };
692 }
693};
694
627695/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
628696/// Extern functions do not have this data structure; they are represented by
629697/// the `Decl` only, with a `Value` tag of `extern_fn`.
......@@ -2401,6 +2469,7 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
24012469
24022470/// Patch ups:
24032471/// * Struct.zir_index
2472/// * Decl.zir_index
24042473/// * Fn.zir_body_inst
24052474/// * Decl.zir_decl_index
24062475/// * Decl.name
......@@ -2479,6 +2548,13 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void {
24792548 };
24802549 }
24812550
2551 if (decl.getUnion()) |union_obj| {
2552 union_obj.zir_index = inst_map.get(union_obj.zir_index) orelse {
2553 try file.deleted_decls.append(gpa, decl);
2554 continue;
2555 };
2556 }
2557
24822558 if (decl.getFunction()) |func| {
24832559 func.zir_body_inst = inst_map.get(func.zir_body_inst) orelse {
24842560 try file.deleted_decls.append(gpa, decl);
......@@ -2769,7 +2845,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
27692845 };
27702846
27712847 if (decl.isRoot()) {
2772 log.debug("semaDecl root {*} ({s})", .{decl, decl.name});
2848 log.debug("semaDecl root {*} ({s})", .{ decl, decl.name });
27732849 const main_struct_inst = zir.getMainStruct();
27742850 const struct_obj = decl.getStruct().?;
27752851 try sema.analyzeStructDecl(decl, main_struct_inst, struct_obj);
......@@ -4271,7 +4347,7 @@ pub const SwitchProngSrc = union(enum) {
42714347 }
42724348};
42734349
4274pub fn analyzeStructFields(mod: *Module, struct_obj: *Module.Struct) InnerError!void {
4350pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) InnerError!void {
42754351 const tracy = trace(@src());
42764352 defer tracy.end();
42774353
......@@ -4284,26 +4360,9 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Module.Struct) InnerError!
42844360 const decls_len = extra.data.decls_len;
42854361
42864362 // Skip over decls.
4287 var extra_index = extra.end;
4288 {
4289 const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
4290 var bit_bag_index: usize = extra_index;
4291 extra_index += bit_bags_count;
4292 var cur_bit_bag: u32 = undefined;
4293 var decl_i: u32 = 0;
4294 while (decl_i < decls_len) : (decl_i += 1) {
4295 if (decl_i % 8 == 0) {
4296 cur_bit_bag = zir.extra[bit_bag_index];
4297 bit_bag_index += 1;
4298 }
4299 const flags = @truncate(u4, cur_bit_bag);
4300 cur_bit_bag >>= 4;
4301
4302 extra_index += 7; // src_hash(4) + line(1) + name(1) + value(1)
4303 extra_index += @truncate(u1, flags >> 2);
4304 extra_index += @truncate(u1, flags >> 3);
4305 }
4306 }
4363 var decls_it = zir.declIterator(struct_obj.zir_index);
4364 while (decls_it.next()) |_| {}
4365 var extra_index = decls_it.extra_index;
43074366
43084367 const body = zir.extra[extra_index..][0..extra.data.body_len];
43094368 if (fields_len == 0) {
......@@ -4417,6 +4476,141 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Module.Struct) InnerError!
44174476 }
44184477}
44194478
4479pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) InnerError!void {
4480 const tracy = trace(@src());
4481 defer tracy.end();
4482
4483 const gpa = mod.gpa;
4484 const zir = union_obj.owner_decl.namespace.file_scope.zir;
4485 const inst_data = zir.instructions.items(.data)[union_obj.zir_index].pl_node;
4486 const src = inst_data.src();
4487 const extra = zir.extraData(Zir.Inst.UnionDecl, inst_data.payload_index);
4488 const fields_len = extra.data.fields_len;
4489 const decls_len = extra.data.decls_len;
4490
4491 // Skip over decls.
4492 var decls_it = zir.declIterator(union_obj.zir_index);
4493 while (decls_it.next()) |_| {}
4494 var extra_index = decls_it.extra_index;
4495
4496 const body = zir.extra[extra_index..][0..extra.data.body_len];
4497 if (fields_len == 0) {
4498 assert(body.len == 0);
4499 return;
4500 }
4501 extra_index += body.len;
4502
4503 var decl_arena = union_obj.owner_decl.value_arena.?.promote(gpa);
4504 defer union_obj.owner_decl.value_arena.?.* = decl_arena.state;
4505
4506 try union_obj.fields.ensureCapacity(&decl_arena.allocator, fields_len);
4507
4508 // We create a block for the field type instructions because they
4509 // may need to reference Decls from inside the struct namespace.
4510 // Within the field type, default value, and alignment expressions, the "owner decl"
4511 // should be the struct itself. Thus we need a new Sema.
4512 var sema: Sema = .{
4513 .mod = mod,
4514 .gpa = gpa,
4515 .arena = &decl_arena.allocator,
4516 .code = zir,
4517 .inst_map = try gpa.alloc(*ir.Inst, zir.instructions.len),
4518 .owner_decl = union_obj.owner_decl,
4519 .namespace = &union_obj.namespace,
4520 .owner_func = null,
4521 .func = null,
4522 .param_inst_list = &.{},
4523 };
4524 defer gpa.free(sema.inst_map);
4525
4526 var block: Scope.Block = .{
4527 .parent = null,
4528 .sema = &sema,
4529 .src_decl = union_obj.owner_decl,
4530 .instructions = .{},
4531 .inlining = null,
4532 .is_comptime = true,
4533 };
4534 defer assert(block.instructions.items.len == 0); // should all be comptime instructions
4535
4536 _ = try sema.analyzeBody(&block, body);
4537
4538 var auto_enum_tag: ?bool = null;
4539
4540 const bits_per_field = 4;
4541 const fields_per_u32 = 32 / bits_per_field;
4542 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
4543 var bit_bag_index: usize = extra_index;
4544 extra_index += bit_bags_count;
4545 var cur_bit_bag: u32 = undefined;
4546 var field_i: u32 = 0;
4547 while (field_i < fields_len) : (field_i += 1) {
4548 if (field_i % fields_per_u32 == 0) {
4549 cur_bit_bag = zir.extra[bit_bag_index];
4550 bit_bag_index += 1;
4551 }
4552 const has_type = @truncate(u1, cur_bit_bag) != 0;
4553 cur_bit_bag >>= 1;
4554 const has_align = @truncate(u1, cur_bit_bag) != 0;
4555 cur_bit_bag >>= 1;
4556 const has_tag = @truncate(u1, cur_bit_bag) != 0;
4557 cur_bit_bag >>= 1;
4558 const unused = @truncate(u1, cur_bit_bag) != 0;
4559 cur_bit_bag >>= 1;
4560
4561 if (auto_enum_tag == null) {
4562 auto_enum_tag = unused;
4563 }
4564
4565 const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
4566 extra_index += 1;
4567
4568 const field_type_ref: Zir.Inst.Ref = if (has_type) blk: {
4569 const field_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
4570 extra_index += 1;
4571 break :blk field_type_ref;
4572 } else .none;
4573
4574 const align_ref: Zir.Inst.Ref = if (has_align) blk: {
4575 const align_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
4576 extra_index += 1;
4577 break :blk align_ref;
4578 } else .none;
4579
4580 const tag_ref: Zir.Inst.Ref = if (has_tag) blk: {
4581 const tag_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
4582 extra_index += 1;
4583 break :blk tag_ref;
4584 } else .none;
4585
4586 // This string needs to outlive the ZIR code.
4587 const field_name = try decl_arena.allocator.dupe(u8, field_name_zir);
4588 const field_ty: Type = if (field_type_ref == .none)
4589 Type.initTag(.void)
4590 else
4591 // TODO: if we need to report an error here, use a source location
4592 // that points to this type expression rather than the union.
4593 // But only resolve the source location if we need to emit a compile error.
4594 try sema.resolveType(&block, src, field_type_ref);
4595
4596 const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
4597 assert(!gop.found_existing);
4598 gop.entry.value = .{
4599 .ty = field_ty,
4600 .abi_align = Value.initTag(.abi_align_default),
4601 };
4602
4603 if (align_ref != .none) {
4604 // TODO: if we need to report an error here, use a source location
4605 // that points to this alignment expression rather than the struct.
4606 // But only resolve the source location if we need to emit a compile error.
4607 gop.entry.value.abi_align = (try sema.resolveInstConst(&block, src, align_ref)).val;
4608 }
4609 }
4610
4611 // TODO resolve the union tag type
4612}
4613
44204614/// Called from `performAllTheWork`, after all AstGen workers have finished,
44214615/// and before the main semantic analysis loop begins.
44224616pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {
src/Sema.zig+322-31
......@@ -591,7 +591,7 @@ fn resolveConstValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *i
591591}
592592
593593fn resolveDefinedValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *ir.Inst) !?Value {
594 if (base.value()) |val| {
594 if (try sema.resolvePossiblyUndefinedValue(block, src, base)) |val| {
595595 if (val.isUndef()) {
596596 return sema.failWithUseOfUndef(block, src);
597597 }
......@@ -600,6 +600,19 @@ fn resolveDefinedValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base:
600600 return null;
601601}
602602
603fn resolvePossiblyUndefinedValue(
604 sema: *Sema,
605 block: *Scope.Block,
606 src: LazySrcLoc,
607 base: *ir.Inst,
608) !?Value {
609 if (try sema.typeHasOnePossibleValue(block, src, base.ty)) |opv| {
610 return opv;
611 }
612 const inst = base.castTag(.constant) orelse return null;
613 return inst.val;
614}
615
603616fn failWithNeededComptime(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) InnerError {
604617 return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{});
605618}
......@@ -889,9 +902,40 @@ fn zirUnionDecl(
889902
890903 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
891904 const src = inst_data.src();
892 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
905 const extra = sema.code.extraData(Zir.Inst.UnionDecl, inst_data.payload_index);
906 const decls_len = extra.data.decls_len;
893907
894 return sema.mod.fail(&block.base, sema.src, "TODO implement zirUnionDecl", .{});
908 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
909
910 const union_obj = try new_decl_arena.allocator.create(Module.Union);
911 const union_ty = try Type.Tag.@"union".create(&new_decl_arena.allocator, union_obj);
912 const union_val = try Value.Tag.ty.create(&new_decl_arena.allocator, union_ty);
913 const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{
914 .ty = Type.initTag(.type),
915 .val = union_val,
916 });
917 union_obj.* = .{
918 .owner_decl = new_decl,
919 .tag_ty = Type.initTag(.@"null"),
920 .fields = .{},
921 .node_offset = inst_data.src_node,
922 .zir_index = inst,
923 .layout = layout,
924 .status = .none,
925 .namespace = .{
926 .parent = sema.owner_decl.namespace,
927 .ty = union_ty,
928 .file_scope = block.getFileScope(),
929 },
930 };
931 std.log.scoped(.module).debug("create union {*} owned by {*} ({s})", .{
932 &union_obj.namespace, new_decl, new_decl.name,
933 });
934
935 _ = try sema.mod.scanNamespace(&union_obj.namespace, extra.end, decls_len, new_decl);
936
937 try new_decl.finalizeNewArena(&new_decl_arena);
938 return sema.analyzeDeclVal(block, src, new_decl);
895939}
896940
897941fn zirOpaqueDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
......@@ -1277,6 +1321,33 @@ fn failWithBadFieldAccess(
12771321 return mod.failWithOwnedErrorMsg(&block.base, msg);
12781322}
12791323
1324fn failWithBadUnionFieldAccess(
1325 sema: *Sema,
1326 block: *Scope.Block,
1327 union_obj: *Module.Union,
1328 field_src: LazySrcLoc,
1329 field_name: []const u8,
1330) InnerError {
1331 const mod = sema.mod;
1332 const gpa = sema.gpa;
1333
1334 const fqn = try union_obj.getFullyQualifiedName(gpa);
1335 defer gpa.free(fqn);
1336
1337 const msg = msg: {
1338 const msg = try mod.errMsg(
1339 &block.base,
1340 field_src,
1341 "no field named '{s}' in union '{s}'",
1342 .{ field_name, fqn },
1343 );
1344 errdefer msg.destroy(gpa);
1345 try mod.errNoteNonLazy(union_obj.srcLoc(), msg, "union declared here", .{});
1346 break :msg msg;
1347 };
1348 return mod.failWithOwnedErrorMsg(&block.base, msg);
1349}
1350
12801351fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
12811352 const tracy = trace(@src());
12821353 defer tracy.end();
......@@ -1484,7 +1555,7 @@ fn zirCompileLog(
14841555 if (i != 0) try writer.print(", ", .{});
14851556
14861557 const arg = try sema.resolveInst(arg_ref);
1487 if (arg.value()) |val| {
1558 if (try sema.resolvePossiblyUndefinedValue(block, src, arg)) |val| {
14881559 try writer.print("@as({}, {})", .{ arg.ty, val });
14891560 } else {
14901561 try writer.print("@as({}, [runtime value])", .{arg.ty});
......@@ -2204,21 +2275,25 @@ fn zirErrorToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
22042275 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
22052276 const op = try sema.resolveInst(inst_data.operand);
22062277 const op_coerced = try sema.coerce(block, Type.initTag(.anyerror), op, operand_src);
2278 const result_ty = Type.initTag(.u16);
22072279
2208 if (op_coerced.value()) |val| {
2280 if (try sema.resolvePossiblyUndefinedValue(block, src, op_coerced)) |val| {
2281 if (val.isUndef()) {
2282 return sema.mod.constUndef(sema.arena, src, result_ty);
2283 }
22092284 const payload = try sema.arena.create(Value.Payload.U64);
22102285 payload.* = .{
22112286 .base = .{ .tag = .int_u64 },
22122287 .data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value,
22132288 };
22142289 return sema.mod.constInst(sema.arena, src, .{
2215 .ty = Type.initTag(.u16),
2290 .ty = result_ty,
22162291 .val = Value.initPayload(&payload.base),
22172292 });
22182293 }
22192294
22202295 try sema.requireRuntimeBlock(block, src);
2221 return block.addUnOp(src, Type.initTag(.u16), .error_to_int, op_coerced);
2296 return block.addUnOp(src, result_ty, .error_to_int, op_coerced);
22222297}
22232298
22242299fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
......@@ -2377,7 +2452,7 @@ fn zirEnumToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErr
23772452 var int_tag_type_buffer: Type.Payload.Bits = undefined;
23782453 const int_tag_ty = try enum_tag.ty.intTagType(&int_tag_type_buffer).copy(arena);
23792454
2380 if (enum_tag.ty.onePossibleValue()) |opv| {
2455 if (try sema.typeHasOnePossibleValue(block, src, enum_tag.ty)) |opv| {
23812456 return mod.constInst(arena, src, .{
23822457 .ty = int_tag_ty,
23832458 .val = opv,
......@@ -2729,13 +2804,18 @@ fn zirFunc(
27292804 src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
27302805 }
27312806
2807 const cc: std.builtin.CallingConvention = if (sema.owner_decl.is_exported)
2808 .C
2809 else
2810 .Unspecified;
2811
27322812 return sema.funcCommon(
27332813 block,
27342814 inst_data.src_node,
27352815 param_types,
27362816 body_inst,
27372817 extra.data.return_type,
2738 .Unspecified,
2818 cc,
27392819 Value.initTag(.null_value),
27402820 false,
27412821 inferred_error_set,
......@@ -4268,10 +4348,7 @@ fn zirBitwise(
42684348 if (casted_lhs.value()) |lhs_val| {
42694349 if (casted_rhs.value()) |rhs_val| {
42704350 if (lhs_val.isUndef() or rhs_val.isUndef()) {
4271 return sema.mod.constInst(sema.arena, src, .{
4272 .ty = resolved_type,
4273 .val = Value.initTag(.undef),
4274 });
4351 return sema.mod.constUndef(sema.arena, src, resolved_type);
42754352 }
42764353 return sema.mod.fail(&block.base, src, "TODO implement comptime bitwise operations", .{});
42774354 }
......@@ -4395,10 +4472,7 @@ fn analyzeArithmetic(
43954472 if (casted_lhs.value()) |lhs_val| {
43964473 if (casted_rhs.value()) |rhs_val| {
43974474 if (lhs_val.isUndef() or rhs_val.isUndef()) {
4398 return sema.mod.constInst(sema.arena, src, .{
4399 .ty = resolved_type,
4400 .val = Value.initTag(.undef),
4401 });
4475 return sema.mod.constUndef(sema.arena, src, resolved_type);
44024476 }
44034477 // incase rhs is 0, simply return lhs without doing any calculations
44044478 // TODO Once division is implemented we should throw an error when dividing by 0.
......@@ -4635,10 +4709,7 @@ fn zirCmp(
46354709 if (casted_lhs.value()) |lhs_val| {
46364710 if (casted_rhs.value()) |rhs_val| {
46374711 if (lhs_val.isUndef() or rhs_val.isUndef()) {
4638 return sema.mod.constInst(sema.arena, src, .{
4639 .ty = resolved_type,
4640 .val = Value.initTag(.undef),
4641 });
4712 return sema.mod.constUndef(sema.arena, src, resolved_type);
46424713 }
46434714 const result = lhs_val.compare(op, rhs_val);
46444715 return sema.mod.constBool(sema.arena, src, result);
......@@ -4721,7 +4792,7 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
47214792 @enumToInt(ty.fnCallingConvention()),
47224793 );
47234794 // alignment: comptime_int,
4724 field_values[1] = try Value.Tag.int_u64.create(sema.arena, ty.ptrAlignment(target));
4795 field_values[1] = try Value.Tag.int_u64.create(sema.arena, ty.abiAlignment(target));
47254796 // is_generic: bool,
47264797 field_values[2] = Value.initTag(.bool_false); // TODO
47274798 // is_var_args: bool,
......@@ -6033,6 +6104,7 @@ fn namedFieldPtr(
60336104 }
60346105 },
60356106 .Struct => return sema.analyzeStructFieldPtr(block, src, object_ptr, field_name, field_name_src, elem_ty),
6107 .Union => return sema.analyzeUnionFieldPtr(block, src, object_ptr, field_name, field_name_src, elem_ty),
60366108 else => {},
60376109 }
60386110 return mod.fail(&block.base, src, "type '{}' does not support field access", .{elem_ty});
......@@ -6083,11 +6155,58 @@ fn analyzeStructFieldPtr(
60836155 return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);
60846156 const field = struct_obj.fields.entries.items[field_index].value;
60856157 const ptr_field_ty = try mod.simplePtrType(arena, field.ty, true, .One);
6086 // TODO comptime field access
6158
6159 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
6160 return mod.constInst(arena, src, .{
6161 .ty = ptr_field_ty,
6162 .val = try Value.Tag.field_ptr.create(arena, .{
6163 .container_ptr = struct_ptr_val,
6164 .field_index = field_index,
6165 }),
6166 });
6167 }
6168
60876169 try sema.requireRuntimeBlock(block, src);
60886170 return block.addStructFieldPtr(src, ptr_field_ty, struct_ptr, @intCast(u32, field_index));
60896171}
60906172
6173fn analyzeUnionFieldPtr(
6174 sema: *Sema,
6175 block: *Scope.Block,
6176 src: LazySrcLoc,
6177 union_ptr: *Inst,
6178 field_name: []const u8,
6179 field_name_src: LazySrcLoc,
6180 unresolved_union_ty: Type,
6181) InnerError!*Inst {
6182 const mod = sema.mod;
6183 const arena = sema.arena;
6184 assert(unresolved_union_ty.zigTypeTag() == .Union);
6185
6186 const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);
6187 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
6188
6189 const field_index = union_obj.fields.getIndex(field_name) orelse
6190 return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
6191
6192 const field = union_obj.fields.entries.items[field_index].value;
6193 const ptr_field_ty = try mod.simplePtrType(arena, field.ty, true, .One);
6194
6195 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| {
6196 // TODO detect inactive union field and emit compile error
6197 return mod.constInst(arena, src, .{
6198 .ty = ptr_field_ty,
6199 .val = try Value.Tag.field_ptr.create(arena, .{
6200 .container_ptr = union_ptr_val,
6201 .field_index = field_index,
6202 }),
6203 });
6204 }
6205
6206 try sema.requireRuntimeBlock(block, src);
6207 return mod.fail(&block.base, src, "TODO implement runtime union field access", .{});
6208}
6209
60916210fn elemPtr(
60926211 sema: *Sema,
60936212 block: *Scope.Block,
......@@ -6382,7 +6501,7 @@ fn storePtr(
63826501
63836502 const elem_ty = ptr.ty.elemType();
63846503 const value = try sema.coerce(block, elem_ty, uncasted_value, src);
6385 if (elem_ty.onePossibleValue() != null)
6504 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
63866505 return;
63876506
63886507 // TODO handle comptime pointer writes
......@@ -6477,7 +6596,7 @@ fn analyzeRef(
64776596) InnerError!*Inst {
64786597 const ptr_type = try sema.mod.simplePtrType(sema.arena, operand.ty, false, .One);
64796598
6480 if (operand.value()) |val| {
6599 if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |val| {
64816600 return sema.mod.constInst(sema.arena, src, .{
64826601 .ty = ptr_type,
64836602 .val = try Value.Tag.ref_val.create(sema.arena, val),
......@@ -6499,10 +6618,10 @@ fn analyzeLoad(
64996618 .Pointer => ptr.ty.elemType(),
65006619 else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
65016620 };
6502 if (ptr.value()) |val| {
6621 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
65036622 return sema.mod.constInst(sema.arena, src, .{
65046623 .ty = elem_ty,
6505 .val = try val.pointerDeref(sema.arena),
6624 .val = try ptr_val.pointerDeref(sema.arena),
65066625 });
65076626 }
65086627
......@@ -6517,14 +6636,18 @@ fn analyzeIsNull(
65176636 operand: *Inst,
65186637 invert_logic: bool,
65196638) InnerError!*Inst {
6520 if (operand.value()) |opt_val| {
6639 const result_ty = Type.initTag(.bool);
6640 if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |opt_val| {
6641 if (opt_val.isUndef()) {
6642 return sema.mod.constUndef(sema.arena, src, result_ty);
6643 }
65216644 const is_null = opt_val.isNull();
65226645 const bool_value = if (invert_logic) !is_null else is_null;
65236646 return sema.mod.constBool(sema.arena, src, bool_value);
65246647 }
65256648 try sema.requireRuntimeBlock(block, src);
65266649 const inst_tag: Inst.Tag = if (invert_logic) .is_non_null else .is_null;
6527 return block.addUnOp(src, Type.initTag(.bool), inst_tag, operand);
6650 return block.addUnOp(src, result_ty, inst_tag, operand);
65286651}
65296652
65306653fn analyzeIsErr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, operand: *Inst) InnerError!*Inst {
......@@ -6532,11 +6655,15 @@ fn analyzeIsErr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, operand: *Ins
65326655 if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, false);
65336656 if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, true);
65346657 assert(ot == .ErrorUnion);
6535 if (operand.value()) |err_union| {
6658 const result_ty = Type.initTag(.bool);
6659 if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |err_union| {
6660 if (err_union.isUndef()) {
6661 return sema.mod.constUndef(sema.arena, src, result_ty);
6662 }
65366663 return sema.mod.constBool(sema.arena, src, err_union.getError() != null);
65376664 }
65386665 try sema.requireRuntimeBlock(block, src);
6539 return block.addUnOp(src, Type.initTag(.bool), .is_err, operand);
6666 return block.addUnOp(src, result_ty, .is_err, operand);
65406667}
65416668
65426669fn analyzeSlice(
......@@ -6953,6 +7080,23 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type
69537080 .float_mode => return sema.resolveBuiltinTypeFields(block, src, ty, "FloatMode"),
69547081 .reduce_op => return sema.resolveBuiltinTypeFields(block, src, ty, "ReduceOp"),
69557082 .call_options => return sema.resolveBuiltinTypeFields(block, src, ty, "CallOptions"),
7083
7084 .@"union", .union_tagged => {
7085 const union_obj = ty.cast(Type.Payload.Union).?.data;
7086 switch (union_obj.status) {
7087 .none => {},
7088 .field_types_wip => {
7089 return sema.mod.fail(&block.base, src, "union {} depends on itself", .{
7090 ty,
7091 });
7092 },
7093 .have_field_types, .have_layout, .layout_wip => return ty,
7094 }
7095 union_obj.status = .field_types_wip;
7096 try sema.mod.analyzeUnionFields(union_obj);
7097 union_obj.status = .have_field_types;
7098 return ty;
7099 },
69567100 else => return ty,
69577101 }
69587102}
......@@ -6994,3 +7138,150 @@ fn getBuiltinType(
69947138 const ty_inst = try sema.analyzeLoad(block, src, opt_ty_inst.?, src);
69957139 return sema.resolveAirAsType(block, src, ty_inst);
69967140}
7141
7142/// There is another implementation of this in `Type.onePossibleValue`. This one
7143/// in `Sema` is for calling during semantic analysis, and peforms field resolution
7144/// to get the answer. The one in `Type` is for calling during codegen and asserts
7145/// that the types are already resolved.
7146fn typeHasOnePossibleValue(
7147 sema: *Sema,
7148 block: *Scope.Block,
7149 src: LazySrcLoc,
7150 starting_type: Type,
7151) InnerError!?Value {
7152 var ty = starting_type;
7153 while (true) switch (ty.tag()) {
7154 .f16,
7155 .f32,
7156 .f64,
7157 .f128,
7158 .c_longdouble,
7159 .comptime_int,
7160 .comptime_float,
7161 .u8,
7162 .i8,
7163 .u16,
7164 .i16,
7165 .u32,
7166 .i32,
7167 .u64,
7168 .i64,
7169 .u128,
7170 .i128,
7171 .usize,
7172 .isize,
7173 .c_short,
7174 .c_ushort,
7175 .c_int,
7176 .c_uint,
7177 .c_long,
7178 .c_ulong,
7179 .c_longlong,
7180 .c_ulonglong,
7181 .bool,
7182 .type,
7183 .anyerror,
7184 .fn_noreturn_no_args,
7185 .fn_void_no_args,
7186 .fn_naked_noreturn_no_args,
7187 .fn_ccc_void_no_args,
7188 .function,
7189 .single_const_pointer_to_comptime_int,
7190 .array_sentinel,
7191 .array_u8_sentinel_0,
7192 .const_slice_u8,
7193 .const_slice,
7194 .mut_slice,
7195 .c_void,
7196 .optional,
7197 .optional_single_mut_pointer,
7198 .optional_single_const_pointer,
7199 .enum_literal,
7200 .anyerror_void_error_union,
7201 .error_union,
7202 .error_set,
7203 .error_set_single,
7204 .@"opaque",
7205 .var_args_param,
7206 .manyptr_u8,
7207 .manyptr_const_u8,
7208 .atomic_ordering,
7209 .atomic_rmw_op,
7210 .calling_convention,
7211 .float_mode,
7212 .reduce_op,
7213 .call_options,
7214 .export_options,
7215 .extern_options,
7216 .@"anyframe",
7217 .anyframe_T,
7218 .many_const_pointer,
7219 .many_mut_pointer,
7220 .c_const_pointer,
7221 .c_mut_pointer,
7222 .single_const_pointer,
7223 .single_mut_pointer,
7224 .pointer,
7225 => return null,
7226
7227 .@"struct" => {
7228 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
7229 const s = resolved_ty.castTag(.@"struct").?.data;
7230 for (s.fields.entries.items) |entry| {
7231 const field_ty = entry.value.ty;
7232 if ((try sema.typeHasOnePossibleValue(block, src, field_ty)) == null) {
7233 return null;
7234 }
7235 }
7236 return Value.initTag(.empty_struct_value);
7237 },
7238 .enum_full => {
7239 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
7240 const enum_full = resolved_ty.castTag(.enum_full).?.data;
7241 if (enum_full.fields.count() == 1) {
7242 return enum_full.values.entries.items[0].key;
7243 } else {
7244 return null;
7245 }
7246 },
7247 .enum_simple => {
7248 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
7249 const enum_simple = resolved_ty.castTag(.enum_simple).?.data;
7250 if (enum_simple.fields.count() == 1) {
7251 return Value.initTag(.zero);
7252 } else {
7253 return null;
7254 }
7255 },
7256 .enum_nonexhaustive => ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty,
7257 .@"union" => {
7258 return null; // TODO
7259 },
7260 .union_tagged => {
7261 return null; // TODO
7262 },
7263
7264 .empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value),
7265 .void => return Value.initTag(.void_value),
7266 .noreturn => return Value.initTag(.unreachable_value),
7267 .@"null" => return Value.initTag(.null_value),
7268 .@"undefined" => return Value.initTag(.undef),
7269
7270 .int_unsigned, .int_signed => {
7271 if (ty.cast(Type.Payload.Bits).?.data == 0) {
7272 return Value.initTag(.zero);
7273 } else {
7274 return null;
7275 }
7276 },
7277 .vector, .array, .array_u8 => {
7278 if (ty.arrayLen() == 0)
7279 return Value.initTag(.empty_array);
7280 ty = ty.elemType();
7281 continue;
7282 },
7283
7284 .inferred_alloc_const => unreachable,
7285 .inferred_alloc_mut => unreachable,
7286 };
7287}
src/ir.zig+3
......@@ -255,6 +255,9 @@ pub const Inst = struct {
255255 }
256256
257257 /// Returns `null` if runtime-known.
258 /// Should be called by codegen, not by Sema. Sema functions should call
259 /// `resolvePossiblyUndefinedValue` or `resolveDefinedValue` instead.
260 /// TODO audit Sema code for violations to the above guidance.
258261 pub fn value(base: *Inst) ?Value {
259262 if (base.ty.onePossibleValue()) |opv| return opv;
260263
src/type.zig+107-15
......@@ -122,6 +122,10 @@ pub const Type = extern union {
122122 .reduce_op,
123123 => return .Enum,
124124
125 .@"union",
126 .union_tagged,
127 => return .Union,
128
125129 .var_args_param => unreachable, // can be any type
126130 }
127131 }
......@@ -506,11 +510,18 @@ pub const Type = extern union {
506510 }
507511 return a.tag() == b.tag();
508512 },
513 .Union => {
514 if (a.cast(Payload.Union)) |a_payload| {
515 if (b.cast(Payload.Union)) |b_payload| {
516 return a_payload.data == b_payload.data;
517 }
518 }
519 return a.tag() == b.tag();
520 },
509521 .Opaque,
510522 .Float,
511523 .ErrorUnion,
512524 .ErrorSet,
513 .Union,
514525 .BoundFn,
515526 .Frame,
516527 => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
......@@ -735,6 +746,7 @@ pub const Type = extern union {
735746 .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),
736747 .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope),
737748 .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),
749 .@"union", .union_tagged => return self.copyPayloadShallow(allocator, Payload.Union),
738750 .enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple),
739751 .enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull),
740752 .@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque),
......@@ -806,6 +818,10 @@ pub const Type = extern union {
806818 const struct_obj = ty.castTag(.@"struct").?.data;
807819 return struct_obj.owner_decl.renderFullyQualifiedName(writer);
808820 },
821 .@"union", .union_tagged => {
822 const union_obj = ty.cast(Payload.Union).?.data;
823 return union_obj.owner_decl.renderFullyQualifiedName(writer);
824 },
809825 .enum_full, .enum_nonexhaustive => {
810826 const enum_full = ty.cast(Payload.EnumFull).?.data;
811827 return enum_full.owner_decl.renderFullyQualifiedName(writer);
......@@ -1151,6 +1167,27 @@ pub const Type = extern union {
11511167 const int_tag_ty = self.intTagType(&buffer);
11521168 return int_tag_ty.hasCodeGenBits();
11531169 },
1170 .@"union" => {
1171 const union_obj = self.castTag(.@"union").?.data;
1172 for (union_obj.fields.entries.items) |entry| {
1173 if (entry.value.ty.hasCodeGenBits())
1174 return true;
1175 } else {
1176 return false;
1177 }
1178 },
1179 .union_tagged => {
1180 const union_obj = self.castTag(.@"union").?.data;
1181 if (union_obj.tag_ty.hasCodeGenBits()) {
1182 return true;
1183 }
1184 for (union_obj.fields.entries.items) |entry| {
1185 if (entry.value.ty.hasCodeGenBits())
1186 return true;
1187 } else {
1188 return false;
1189 }
1190 },
11541191
11551192 // TODO lazy types
11561193 .array, .vector => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
......@@ -1359,6 +1396,34 @@ pub const Type = extern union {
13591396 const int_tag_ty = self.intTagType(&buffer);
13601397 return int_tag_ty.abiAlignment(target);
13611398 },
1399 .union_tagged => {
1400 const union_obj = self.castTag(.union_tagged).?.data;
1401 var biggest: u32 = union_obj.tag_ty.abiAlignment(target);
1402 for (union_obj.fields.entries.items) |entry| {
1403 const field_ty = entry.value.ty;
1404 if (!field_ty.hasCodeGenBits()) continue;
1405 const field_align = field_ty.abiAlignment(target);
1406 if (field_align > biggest) {
1407 return field_align;
1408 }
1409 }
1410 assert(biggest != 0);
1411 return biggest;
1412 },
1413 .@"union" => {
1414 const union_obj = self.castTag(.@"union").?.data;
1415 var biggest: u32 = 0;
1416 for (union_obj.fields.entries.items) |entry| {
1417 const field_ty = entry.value.ty;
1418 if (!field_ty.hasCodeGenBits()) continue;
1419 const field_align = field_ty.abiAlignment(target);
1420 if (field_align > biggest) {
1421 return field_align;
1422 }
1423 }
1424 assert(biggest != 0);
1425 return biggest;
1426 },
13621427 .c_void,
13631428 .void,
13641429 .type,
......@@ -1411,6 +1476,9 @@ pub const Type = extern union {
14111476 const int_tag_ty = self.intTagType(&buffer);
14121477 return int_tag_ty.abiSize(target);
14131478 },
1479 .@"union", .union_tagged => {
1480 @panic("TODO abiSize unions");
1481 },
14141482
14151483 .u8,
14161484 .i8,
......@@ -1570,6 +1638,9 @@ pub const Type = extern union {
15701638 const int_tag_ty = self.intTagType(&buffer);
15711639 return int_tag_ty.bitSize(target);
15721640 },
1641 .@"union", .union_tagged => {
1642 @panic("TODO bitSize unions");
1643 },
15731644
15741645 .u8, .i8 => 8,
15751646
......@@ -2263,6 +2334,8 @@ pub const Type = extern union {
22632334 };
22642335 }
22652336
2337 /// During semantic analysis, instead call `Sema.typeHasOnePossibleValue` which
2338 /// resolves field types rather than asserting they are already resolved.
22662339 pub fn onePossibleValue(starting_type: Type) ?Value {
22672340 var ty = starting_type;
22682341 while (true) switch (ty.tag()) {
......@@ -2330,10 +2403,18 @@ pub const Type = extern union {
23302403 .extern_options,
23312404 .@"anyframe",
23322405 .anyframe_T,
2406 .many_const_pointer,
2407 .many_mut_pointer,
2408 .c_const_pointer,
2409 .c_mut_pointer,
2410 .single_const_pointer,
2411 .single_mut_pointer,
2412 .pointer,
23332413 => return null,
23342414
23352415 .@"struct" => {
23362416 const s = ty.castTag(.@"struct").?.data;
2417 assert(s.haveFieldTypes());
23372418 for (s.fields.entries.items) |entry| {
23382419 const field_ty = entry.value.ty;
23392420 if (field_ty.onePossibleValue() == null) {
......@@ -2359,6 +2440,12 @@ pub const Type = extern union {
23592440 }
23602441 },
23612442 .enum_nonexhaustive => ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty,
2443 .@"union" => {
2444 return null; // TODO
2445 },
2446 .union_tagged => {
2447 return null; // TODO
2448 },
23622449
23632450 .empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value),
23642451 .void => return Value.initTag(.void_value),
......@@ -2379,20 +2466,7 @@ pub const Type = extern union {
23792466 ty = ty.elemType();
23802467 continue;
23812468 },
2382 .many_const_pointer,
2383 .many_mut_pointer,
2384 .c_const_pointer,
2385 .c_mut_pointer,
2386 .single_const_pointer,
2387 .single_mut_pointer,
2388 => {
2389 ty = ty.castPointer().?.data;
2390 continue;
2391 },
2392 .pointer => {
2393 ty = ty.castTag(.pointer).?.data.pointee_type;
2394 continue;
2395 },
2469
23962470 .inferred_alloc_const => unreachable,
23972471 .inferred_alloc_mut => unreachable,
23982472 };
......@@ -2412,6 +2486,8 @@ pub const Type = extern union {
24122486 .enum_full => &self.castTag(.enum_full).?.data.namespace,
24132487 .empty_struct => self.castTag(.empty_struct).?.data,
24142488 .@"opaque" => &self.castTag(.@"opaque").?.data,
2489 .@"union" => &self.castTag(.@"union").?.data.namespace,
2490 .union_tagged => &self.castTag(.union_tagged).?.data.namespace,
24152491
24162492 else => null,
24172493 };
......@@ -2612,6 +2688,10 @@ pub const Type = extern union {
26122688 const error_set = ty.castTag(.error_set).?.data;
26132689 return error_set.srcLoc();
26142690 },
2691 .@"union", .union_tagged => {
2692 const union_obj = ty.cast(Payload.Union).?.data;
2693 return union_obj.srcLoc();
2694 },
26152695 .atomic_ordering,
26162696 .atomic_rmw_op,
26172697 .calling_convention,
......@@ -2643,6 +2723,10 @@ pub const Type = extern union {
26432723 const error_set = ty.castTag(.error_set).?.data;
26442724 return error_set.owner_decl;
26452725 },
2726 .@"union", .union_tagged => {
2727 const union_obj = ty.cast(Payload.Union).?.data;
2728 return union_obj.owner_decl;
2729 },
26462730 .@"opaque" => @panic("TODO"),
26472731 .atomic_ordering,
26482732 .atomic_rmw_op,
......@@ -2801,6 +2885,8 @@ pub const Type = extern union {
28012885 empty_struct,
28022886 @"opaque",
28032887 @"struct",
2888 @"union",
2889 union_tagged,
28042890 enum_simple,
28052891 enum_full,
28062892 enum_nonexhaustive,
......@@ -2902,6 +2988,7 @@ pub const Type = extern union {
29022988 .error_set_single => Payload.Name,
29032989 .@"opaque" => Payload.Opaque,
29042990 .@"struct" => Payload.Struct,
2991 .@"union", .union_tagged => Payload.Union,
29052992 .enum_full, .enum_nonexhaustive => Payload.EnumFull,
29062993 .enum_simple => Payload.EnumSimple,
29072994 .empty_struct => Payload.ContainerScope,
......@@ -3040,6 +3127,11 @@ pub const Type = extern union {
30403127 data: *Module.Struct,
30413128 };
30423129
3130 pub const Union = struct {
3131 base: Payload,
3132 data: *Module.Union,
3133 };
3134
30433135 pub const EnumFull = struct {
30443136 base: Payload,
30453137 data: *Module.EnumFull,
src/value.zig+57
......@@ -104,6 +104,7 @@ pub const Value = extern union {
104104 /// Represents a pointer to a decl, not the value of the decl.
105105 decl_ref,
106106 elem_ptr,
107 field_ptr,
107108 /// A slice of u8 whose memory is managed externally.
108109 bytes,
109110 /// This value is repeated some number of times. The amount of times to repeat
......@@ -223,6 +224,7 @@ pub const Value = extern union {
223224 .function => Payload.Function,
224225 .variable => Payload.Variable,
225226 .elem_ptr => Payload.ElemPtr,
227 .field_ptr => Payload.FieldPtr,
226228 .float_16 => Payload.Float_16,
227229 .float_32 => Payload.Float_32,
228230 .float_64 => Payload.Float_64,
......@@ -414,6 +416,18 @@ pub const Value = extern union {
414416 };
415417 return Value{ .ptr_otherwise = &new_payload.base };
416418 },
419 .field_ptr => {
420 const payload = self.castTag(.field_ptr).?;
421 const new_payload = try allocator.create(Payload.FieldPtr);
422 new_payload.* = .{
423 .base = payload.base,
424 .data = .{
425 .container_ptr = try payload.data.container_ptr.copy(allocator),
426 .field_index = payload.data.field_index,
427 },
428 };
429 return Value{ .ptr_otherwise = &new_payload.base };
430 },
417431 .bytes => return self.copyPayloadShallow(allocator, Payload.Bytes),
418432 .repeated => {
419433 const payload = self.castTag(.repeated).?;
......@@ -569,6 +583,11 @@ pub const Value = extern union {
569583 try out_stream.print("&[{}] ", .{elem_ptr.index});
570584 val = elem_ptr.array_ptr;
571585 },
586 .field_ptr => {
587 const field_ptr = val.castTag(.field_ptr).?.data;
588 try out_stream.print("fieldptr({d}) ", .{field_ptr.field_index});
589 val = field_ptr.container_ptr;
590 },
572591 .empty_array => return out_stream.writeAll(".{}"),
573592 .enum_literal => return out_stream.print(".{}", .{std.zig.fmtId(self.castTag(.enum_literal).?.data)}),
574593 .enum_field_index => return out_stream.print("(enum field {d})", .{self.castTag(.enum_field_index).?.data}),
......@@ -704,6 +723,7 @@ pub const Value = extern union {
704723 .ref_val,
705724 .decl_ref,
706725 .elem_ptr,
726 .field_ptr,
707727 .bytes,
708728 .repeated,
709729 .float_16,
......@@ -1196,6 +1216,11 @@ pub const Value = extern union {
11961216 std.hash.autoHash(&hasher, payload.array_ptr.hash());
11971217 std.hash.autoHash(&hasher, payload.index);
11981218 },
1219 .field_ptr => {
1220 const payload = self.castTag(.field_ptr).?.data;
1221 std.hash.autoHash(&hasher, payload.container_ptr.hash());
1222 std.hash.autoHash(&hasher, payload.field_index);
1223 },
11991224 .decl_ref => {
12001225 const decl = self.castTag(.decl_ref).?.data;
12011226 std.hash.autoHash(&hasher, decl);
......@@ -1250,6 +1275,11 @@ pub const Value = extern union {
12501275 const array_val = try elem_ptr.array_ptr.pointerDeref(allocator);
12511276 return array_val.elemValue(allocator, elem_ptr.index);
12521277 },
1278 .field_ptr => {
1279 const field_ptr = self.castTag(.field_ptr).?.data;
1280 const container_val = try field_ptr.container_ptr.pointerDeref(allocator);
1281 return container_val.fieldValue(allocator, field_ptr.field_index);
1282 },
12531283
12541284 else => unreachable,
12551285 };
......@@ -1270,6 +1300,22 @@ pub const Value = extern union {
12701300 }
12711301 }
12721302
1303 pub fn fieldValue(val: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value {
1304 switch (val.tag()) {
1305 .@"struct" => {
1306 const field_values = val.castTag(.@"struct").?.data;
1307 return field_values[index];
1308 },
1309 .@"union" => {
1310 const payload = val.castTag(.@"union").?.data;
1311 // TODO assert the tag is correct
1312 return payload.val;
1313 },
1314
1315 else => unreachable,
1316 }
1317 }
1318
12731319 /// Returns a pointer to the element value at the index.
12741320 pub fn elemPtr(self: Value, allocator: *Allocator, index: usize) !Value {
12751321 if (self.castTag(.elem_ptr)) |elem_ptr| {
......@@ -1409,6 +1455,7 @@ pub const Value = extern union {
14091455 .ref_val,
14101456 .decl_ref,
14111457 .elem_ptr,
1458 .field_ptr,
14121459 .bytes,
14131460 .repeated,
14141461 .float_16,
......@@ -1496,6 +1543,16 @@ pub const Value = extern union {
14961543 },
14971544 };
14981545
1546 pub const FieldPtr = struct {
1547 pub const base_tag = Tag.field_ptr;
1548
1549 base: Payload = Payload{ .tag = base_tag },
1550 data: struct {
1551 container_ptr: Value,
1552 field_index: usize,
1553 },
1554 };
1555
14991556 pub const Bytes = struct {
15001557 base: Payload,
15011558 data: []const u8,