authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-10-17 20:11:22+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-10-20 03:44:02+02:00
loge5d6fe18b90cf2602e7e7712b4dd807b4e243356
treeb2f3392731b2a67d494526d79fc99ad2e3c7bc65
parent372e9709ad2f4af24461f0fa601754a469091c2b

stage2: restructure Sema.fieldVal and sema.fieldPtr

Dereferencing single pointers is now handled outside of the main switch, which allows deduplication of some cases. This also implements the relevant operations for pointers to types and pointers to slices.

1 files changed, 137 insertions(+), 141 deletions(-)

src/Sema.zig+137-141
...@@ -5119,16 +5119,10 @@ fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -5119,16 +5119,10 @@ fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
5119 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;5119 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5120 const src = inst_data.src();5120 const src = inst_data.src();
5121 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };5121 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
5122 const lhs_src: LazySrcLoc = src; // TODO
5123 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;5122 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
5124 const field_name = sema.code.nullTerminatedString(extra.field_name_start);5123 const field_name = sema.code.nullTerminatedString(extra.field_name_start);
5125 const object = sema.resolveInst(extra.lhs);5124 const object = sema.resolveInst(extra.lhs);
5126 if (sema.typeOf(object).isSinglePointer()) {5125 return sema.fieldVal(block, src, object, field_name, field_name_src);
5127 const result_ptr = try sema.fieldPtr(block, src, object, field_name, field_name_src);
5128 return sema.analyzeLoad(block, src, result_ptr, lhs_src);
5129 } else {
5130 return sema.fieldVal(block, src, object, field_name, field_name_src);
5131 }
5132}5126}
51335127
5134fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5128fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -10688,12 +10682,22 @@ fn fieldVal(...@@ -10688,12 +10682,22 @@ fn fieldVal(
10688 const object_src = src; // TODO better source location10682 const object_src = src; // TODO better source location
10689 const object_ty = sema.typeOf(object);10683 const object_ty = sema.typeOf(object);
1069010684
10691 switch (object_ty.zigTypeTag()) {10685 // Zig allows dereferencing a single pointer during field lookup. Note that
10686 // we don't actually need to generate the dereference some field lookups, like the
10687 // length of arrays and other comptime operations.
10688 const is_pointer_to = object_ty.isSinglePointer();
10689
10690 const inner_ty = if (is_pointer_to)
10691 object_ty.childType()
10692 else
10693 object_ty;
10694
10695 switch (inner_ty.zigTypeTag()) {
10692 .Array => {10696 .Array => {
10693 if (mem.eql(u8, field_name, "len")) {10697 if (mem.eql(u8, field_name, "len")) {
10694 return sema.addConstant(10698 return sema.addConstant(
10695 Type.initTag(.comptime_int),10699 Type.initTag(.comptime_int),
10696 try Value.Tag.int_u64.create(arena, object_ty.arrayLen()),10700 try Value.Tag.int_u64.create(arena, inner_ty.arrayLen()),
10697 );10701 );
10698 } else {10702 } else {
10699 return sema.fail(10703 return sema.fail(
...@@ -10704,75 +10708,60 @@ fn fieldVal(...@@ -10704,75 +10708,60 @@ fn fieldVal(
10704 );10708 );
10705 }10709 }
10706 },10710 },
10707 .Pointer => switch (object_ty.ptrSize()) {10711 .Pointer => if (inner_ty.isSlice()) {
10708 .Slice => {10712 if (mem.eql(u8, field_name, "ptr")) {
10709 if (mem.eql(u8, field_name, "ptr")) {10713 const slice = if (is_pointer_to)
10710 const buf = try arena.create(Type.SlicePtrFieldTypeBuffer);10714 try sema.analyzeLoad(block, src, object, object_src)
10711 const result_ty = object_ty.slicePtrFieldType(buf);10715 else
10712 if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| {10716 object;
10713 if (val.isUndef()) return sema.addConstUndef(result_ty);10717
10714 return sema.addConstant(result_ty, val.slicePtr());10718 const buf = try arena.create(Type.SlicePtrFieldTypeBuffer);
10715 }10719 const result_ty = inner_ty.slicePtrFieldType(buf);
10716 try sema.requireRuntimeBlock(block, src);10720
10717 return block.addTyOp(.slice_ptr, result_ty, object);10721 if (try sema.resolveMaybeUndefVal(block, object_src, slice)) |val| {
10718 } else if (mem.eql(u8, field_name, "len")) {10722 if (val.isUndef()) return sema.addConstUndef(result_ty);
10719 const result_ty = Type.usize;10723 return sema.addConstant(result_ty, val.slicePtr());
10720 if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| {
10721 if (val.isUndef()) return sema.addConstUndef(result_ty);
10722 return sema.addConstant(
10723 result_ty,
10724 try Value.Tag.int_u64.create(arena, val.sliceLen()),
10725 );
10726 }
10727 try sema.requireRuntimeBlock(block, src);
10728 return block.addTyOp(.slice_len, result_ty, object);
10729 } else {
10730 return sema.fail(
10731 block,
10732 field_name_src,
10733 "no member named '{s}' in '{}'",
10734 .{ field_name, object_ty },
10735 );
10736 }10724 }
10737 },10725 try sema.requireRuntimeBlock(block, src);
10738 .One => {10726 return block.addTyOp(.slice_ptr, result_ty, slice);
10739 const ptr_child = object_ty.elemType();10727 } else if (mem.eql(u8, field_name, "len")) {
10740 switch (ptr_child.zigTypeTag()) {10728 const slice = if (is_pointer_to)
10741 .Array => {10729 try sema.analyzeLoad(block, src, object, object_src)
10742 if (mem.eql(u8, field_name, "len")) {10730 else
10743 return sema.addConstant(10731 object;
10744 Type.initTag(.comptime_int),10732
10745 try Value.Tag.int_u64.create(arena, ptr_child.arrayLen()),10733 const result_ty = Type.usize;
10746 );10734
10747 } else {10735 if (try sema.resolveMaybeUndefVal(block, object_src, slice)) |val| {
10748 return sema.fail(10736 if (val.isUndef()) return sema.addConstUndef(result_ty);
10749 block,10737 return sema.addConstant(
10750 field_name_src,10738 result_ty,
10751 "no member named '{s}' in '{}'",10739 try Value.Tag.int_u64.create(arena, val.sliceLen()),
10752 .{ field_name, object_ty },10740 );
10753 );
10754 }
10755 },
10756 .Struct => {
10757 const struct_ptr_deref = try sema.analyzeLoad(block, src, object, object_src);
10758 return sema.unionFieldVal(block, src, struct_ptr_deref, field_name, field_name_src, ptr_child);
10759 },
10760 .Union => {
10761 const union_ptr_deref = try sema.analyzeLoad(block, src, object, object_src);
10762 return sema.unionFieldVal(block, src, union_ptr_deref, field_name, field_name_src, ptr_child);
10763 },
10764 else => {},
10765 }10741 }
10766 },10742 try sema.requireRuntimeBlock(block, src);
10767 .Many, .C => {},10743 return block.addTyOp(.slice_len, result_ty, slice);
10744 } else {
10745 return sema.fail(
10746 block,
10747 field_name_src,
10748 "no member named '{s}' in '{}'",
10749 .{ field_name, object_ty },
10750 );
10751 }
10768 },10752 },
10769 .Type => {10753 .Type => {
10770 const val = (try sema.resolveDefinedValue(block, object_src, object)).?;10754 const dereffed_type = if (is_pointer_to)
10755 try sema.analyzeLoad(block, src, object, object_src)
10756 else
10757 object;
10758
10759 const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?;
10771 var to_type_buffer: Value.ToTypeBuffer = undefined;10760 var to_type_buffer: Value.ToTypeBuffer = undefined;
10772 const child_type = val.toType(&to_type_buffer);10761 const child_type = val.toType(&to_type_buffer);
10762
10773 switch (child_type.zigTypeTag()) {10763 switch (child_type.zigTypeTag()) {
10774 .ErrorSet => {10764 .ErrorSet => {
10775 // TODO resolve inferred error sets
10776 const name: []const u8 = if (child_type.castTag(.error_set)) |payload| blk: {10765 const name: []const u8 = if (child_type.castTag(.error_set)) |payload| blk: {
10777 const error_set = payload.data;10766 const error_set = payload.data;
10778 // TODO this is O(N). I'm putting off solving this until we solve inferred10767 // TODO this is O(N). I'm putting off solving this until we solve inferred
...@@ -10842,8 +10831,20 @@ fn fieldVal(...@@ -10842,8 +10831,20 @@ fn fieldVal(
10842 else => return sema.fail(block, src, "type '{}' has no members", .{child_type}),10831 else => return sema.fail(block, src, "type '{}' has no members", .{child_type}),
10843 }10832 }
10844 },10833 },
10845 .Struct => return sema.structFieldVal(block, src, object, field_name, field_name_src, object_ty),10834 .Struct => if (is_pointer_to) {
10846 .Union => return sema.unionFieldVal(block, src, object, field_name, field_name_src, object_ty),10835 // Avoid loading the entire struct by fetching a pointer and loading that
10836 const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty);
10837 return sema.analyzeLoad(block, src, field_ptr, object_src);
10838 } else {
10839 return sema.structFieldVal(block, src, object, field_name, field_name_src, inner_ty);
10840 },
10841 .Union => if (is_pointer_to) {
10842 // Avoid loading the entire union by fetching a pointer and loading that
10843 const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty);
10844 return sema.analyzeLoad(block, src, field_ptr, object_src);
10845 } else {
10846 return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty);
10847 },
10847 else => {},10848 else => {},
10848 }10849 }
10849 return sema.fail(block, src, "type '{}' does not support field access", .{object_ty});10850 return sema.fail(block, src, "type '{}' does not support field access", .{object_ty});
...@@ -10866,14 +10867,25 @@ fn fieldPtr(...@@ -10866,14 +10867,25 @@ fn fieldPtr(
10866 .Pointer => object_ptr_ty.elemType(),10867 .Pointer => object_ptr_ty.elemType(),
10867 else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty}),10868 else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty}),
10868 };10869 };
10869 switch (object_ty.zigTypeTag()) {10870
10871 // Zig allows dereferencing a single pointer during field lookup. Note that
10872 // we don't actually need to generate the dereference some field lookups, like the
10873 // length of arrays and other comptime operations.
10874 const is_pointer_to = object_ty.isSinglePointer();
10875
10876 const inner_ty = if (is_pointer_to)
10877 object_ty.childType()
10878 else
10879 object_ty;
10880
10881 switch (inner_ty.zigTypeTag()) {
10870 .Array => {10882 .Array => {
10871 if (mem.eql(u8, field_name, "len")) {10883 if (mem.eql(u8, field_name, "len")) {
10872 var anon_decl = try block.startAnonDecl();10884 var anon_decl = try block.startAnonDecl();
10873 defer anon_decl.deinit();10885 defer anon_decl.deinit();
10874 return sema.analyzeDeclRef(try anon_decl.finish(10886 return sema.analyzeDeclRef(try anon_decl.finish(
10875 Type.initTag(.comptime_int),10887 Type.initTag(.comptime_int),
10876 try Value.Tag.int_u64.create(anon_decl.arena(), object_ty.arrayLen()),10888 try Value.Tag.int_u64.create(anon_decl.arena(), inner_ty.arrayLen()),
10877 ));10889 ));
10878 } else {10890 } else {
10879 return sema.fail(10891 return sema.fail(
...@@ -10884,77 +10896,49 @@ fn fieldPtr(...@@ -10884,77 +10896,49 @@ fn fieldPtr(
10884 );10896 );
10885 }10897 }
10886 },10898 },
10887 .Pointer => switch (object_ty.ptrSize()) {10899 .Pointer => if (inner_ty.isSlice()) {
10888 .Slice => {10900 // Here for the ptr and len fields what we need to do is the situation
10889 // Here for the ptr and len fields what we need to do is the situation10901 // when a temporary has its address taken, e.g. `&a[c..d].len`.
10890 // when a temporary has its address taken, e.g. `&a[c..d].len`.10902 // This value may be known at compile-time or runtime. In the former
10891 // This value may be known at compile-time or runtime. In the former10903 // case, it should create an anonymous Decl and return a decl_ref to it.
10892 // case, it should create an anonymous Decl and return a decl_ref to it.10904 // In the latter case, it should add an `alloc` instruction, store
10893 // In the latter case, it should add an `alloc` instruction, store10905 // the runtime value to it, and then return the `alloc`.
10894 // the runtime value to it, and then return the `alloc`.10906 // In both cases the pointer should be const.
10895 // In both cases the pointer should be const.10907 if (mem.eql(u8, field_name, "ptr")) {
10896 if (mem.eql(u8, field_name, "ptr")) {10908 return sema.fail(
10897 return sema.fail(10909 block,
10898 block,10910 field_name_src,
10899 field_name_src,10911 "TODO: implement reference to 'ptr' field of slice '{}'",
10900 "TODO: implement reference to 'ptr' field of slice '{}'",10912 .{inner_ty},
10901 .{object_ty},10913 );
10902 );10914 } else if (mem.eql(u8, field_name, "len")) {
10903 } else if (mem.eql(u8, field_name, "len")) {10915 return sema.fail(
10904 return sema.fail(10916 block,
10905 block,10917 field_name_src,
10906 field_name_src,10918 "TODO: implement reference to 'len' field of slice '{}'",
10907 "TODO: implement reference to 'len' field of slice '{}'",10919 .{inner_ty},
10908 .{object_ty},10920 );
10909 );10921 } else {
10910 } else {10922 return sema.fail(
10911 return sema.fail(10923 block,
10912 block,10924 field_name_src,
10913 field_name_src,10925 "no member named '{s}' in '{}'",
10914 "no member named '{s}' in '{}'",10926 .{ field_name, object_ty },
10915 .{ field_name, object_ty },10927 );
10916 );10928 }
10917 }
10918 },
10919 .One => {
10920 const ptr_child = object_ty.elemType();
10921 switch (ptr_child.zigTypeTag()) {
10922 .Array => {
10923 if (mem.eql(u8, field_name, "len")) {
10924 var anon_decl = try block.startAnonDecl();
10925 defer anon_decl.deinit();
10926 return sema.analyzeDeclRef(try anon_decl.finish(
10927 Type.initTag(.comptime_int),
10928 try Value.Tag.int_u64.create(anon_decl.arena(), ptr_child.arrayLen()),
10929 ));
10930 } else {
10931 return sema.fail(
10932 block,
10933 field_name_src,
10934 "no member named '{s}' in '{}'",
10935 .{ field_name, object_ty },
10936 );
10937 }
10938 },
10939 .Struct => {
10940 const struct_ptr_deref = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
10941 return sema.structFieldPtr(block, src, struct_ptr_deref, field_name, field_name_src, ptr_child);
10942 },
10943 .Union => {
10944 const union_ptr_deref = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
10945 return sema.unionFieldPtr(block, src, union_ptr_deref, field_name, field_name_src, ptr_child);
10946 },
10947 else => {},
10948 }
10949 },
10950 .Many, .C => {},
10951 },10929 },
10952 .Type => {10930 .Type => {
10953 _ = try sema.resolveConstValue(block, object_ptr_src, object_ptr);10931 _ = try sema.resolveConstValue(block, object_ptr_src, object_ptr);
10954 const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);10932 const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
10955 const val = (sema.resolveDefinedValue(block, src, result) catch unreachable).?;10933 const inner = if (is_pointer_to)
10934 try sema.analyzeLoad(block, src, result, object_ptr_src)
10935 else
10936 result;
10937
10938 const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?;
10956 var to_type_buffer: Value.ToTypeBuffer = undefined;10939 var to_type_buffer: Value.ToTypeBuffer = undefined;
10957 const child_type = val.toType(&to_type_buffer);10940 const child_type = val.toType(&to_type_buffer);
10941
10958 switch (child_type.zigTypeTag()) {10942 switch (child_type.zigTypeTag()) {
10959 .ErrorSet => {10943 .ErrorSet => {
10960 // TODO resolve inferred error sets10944 // TODO resolve inferred error sets
...@@ -11033,8 +11017,20 @@ fn fieldPtr(...@@ -11033,8 +11017,20 @@ fn fieldPtr(
11033 else => return sema.fail(block, src, "type '{}' has no members", .{child_type}),11017 else => return sema.fail(block, src, "type '{}' has no members", .{child_type}),
11034 }11018 }
11035 },11019 },
11036 .Struct => return sema.structFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty),11020 .Struct => {
11037 .Union => return sema.unionFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty),11021 const inner_ptr = if (is_pointer_to)
11022 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
11023 else
11024 object_ptr;
11025 return sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty);
11026 },
11027 .Union => {
11028 const inner_ptr = if (is_pointer_to)
11029 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
11030 else
11031 object_ptr;
11032 return sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty);
11033 },
11038 else => {},11034 else => {},
11039 }11035 }
11040 return sema.fail(block, src, "type '{}' does not support field access", .{object_ty});11036 return sema.fail(block, src, "type '{}' does not support field access", .{object_ty});