| ... | ... | @@ -5119,16 +5119,10 @@ fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 5119 | 5119 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 5120 | 5120 | const src = inst_data.src(); |
| 5121 | 5121 | const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; |
| 5122 | | const lhs_src: LazySrcLoc = src; // TODO |
| 5123 | 5122 | const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; |
| 5124 | 5123 | const field_name = sema.code.nullTerminatedString(extra.field_name_start); |
| 5125 | 5124 | const object = sema.resolveInst(extra.lhs); |
| 5126 | | if (sema.typeOf(object).isSinglePointer()) { |
| 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 | | } |
| 5125 | return sema.fieldVal(block, src, object, field_name, field_name_src); |
| 5132 | 5126 | } |
| 5133 | 5127 | |
| 5134 | 5128 | fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -10688,12 +10682,22 @@ fn fieldVal( |
| 10688 | 10682 | const object_src = src; // TODO better source location |
| 10689 | 10683 | const object_ty = sema.typeOf(object); |
| 10690 | 10684 | |
| 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 | 10696 | .Array => { |
| 10693 | 10697 | if (mem.eql(u8, field_name, "len")) { |
| 10694 | 10698 | return sema.addConstant( |
| 10695 | 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 | 10702 | } else { |
| 10699 | 10703 | return sema.fail( |
| ... | ... | @@ -10704,75 +10708,60 @@ fn fieldVal( |
| 10704 | 10708 | ); |
| 10705 | 10709 | } |
| 10706 | 10710 | }, |
| 10707 | | .Pointer => switch (object_ty.ptrSize()) { |
| 10708 | | .Slice => { |
| 10709 | | if (mem.eql(u8, field_name, "ptr")) { |
| 10710 | | const buf = try arena.create(Type.SlicePtrFieldTypeBuffer); |
| 10711 | | const result_ty = object_ty.slicePtrFieldType(buf); |
| 10712 | | if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| { |
| 10713 | | if (val.isUndef()) return sema.addConstUndef(result_ty); |
| 10714 | | return sema.addConstant(result_ty, val.slicePtr()); |
| 10715 | | } |
| 10716 | | try sema.requireRuntimeBlock(block, src); |
| 10717 | | return block.addTyOp(.slice_ptr, result_ty, object); |
| 10718 | | } else if (mem.eql(u8, field_name, "len")) { |
| 10719 | | const result_ty = Type.usize; |
| 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 | | ); |
| 10711 | .Pointer => if (inner_ty.isSlice()) { |
| 10712 | if (mem.eql(u8, field_name, "ptr")) { |
| 10713 | const slice = if (is_pointer_to) |
| 10714 | try sema.analyzeLoad(block, src, object, object_src) |
| 10715 | else |
| 10716 | object; |
| 10717 | |
| 10718 | const buf = try arena.create(Type.SlicePtrFieldTypeBuffer); |
| 10719 | const result_ty = inner_ty.slicePtrFieldType(buf); |
| 10720 | |
| 10721 | if (try sema.resolveMaybeUndefVal(block, object_src, slice)) |val| { |
| 10722 | if (val.isUndef()) return sema.addConstUndef(result_ty); |
| 10723 | return sema.addConstant(result_ty, val.slicePtr()); |
| 10736 | 10724 | } |
| 10737 | | }, |
| 10738 | | .One => { |
| 10739 | | const ptr_child = object_ty.elemType(); |
| 10740 | | switch (ptr_child.zigTypeTag()) { |
| 10741 | | .Array => { |
| 10742 | | if (mem.eql(u8, field_name, "len")) { |
| 10743 | | return sema.addConstant( |
| 10744 | | Type.initTag(.comptime_int), |
| 10745 | | try Value.Tag.int_u64.create(arena, ptr_child.arrayLen()), |
| 10746 | | ); |
| 10747 | | } else { |
| 10748 | | return sema.fail( |
| 10749 | | block, |
| 10750 | | field_name_src, |
| 10751 | | "no member named '{s}' in '{}'", |
| 10752 | | .{ field_name, object_ty }, |
| 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 => {}, |
| 10725 | try sema.requireRuntimeBlock(block, src); |
| 10726 | return block.addTyOp(.slice_ptr, result_ty, slice); |
| 10727 | } else if (mem.eql(u8, field_name, "len")) { |
| 10728 | const slice = if (is_pointer_to) |
| 10729 | try sema.analyzeLoad(block, src, object, object_src) |
| 10730 | else |
| 10731 | object; |
| 10732 | |
| 10733 | const result_ty = Type.usize; |
| 10734 | |
| 10735 | if (try sema.resolveMaybeUndefVal(block, object_src, slice)) |val| { |
| 10736 | if (val.isUndef()) return sema.addConstUndef(result_ty); |
| 10737 | return sema.addConstant( |
| 10738 | result_ty, |
| 10739 | try Value.Tag.int_u64.create(arena, val.sliceLen()), |
| 10740 | ); |
| 10765 | 10741 | } |
| 10766 | | }, |
| 10767 | | .Many, .C => {}, |
| 10742 | try sema.requireRuntimeBlock(block, src); |
| 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 | 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 | 10760 | var to_type_buffer: Value.ToTypeBuffer = undefined; |
| 10772 | 10761 | const child_type = val.toType(&to_type_buffer); |
| 10762 | |
| 10773 | 10763 | switch (child_type.zigTypeTag()) { |
| 10774 | 10764 | .ErrorSet => { |
| 10775 | | // TODO resolve inferred error sets |
| 10776 | 10765 | const name: []const u8 = if (child_type.castTag(.error_set)) |payload| blk: { |
| 10777 | 10766 | const error_set = payload.data; |
| 10778 | 10767 | // TODO this is O(N). I'm putting off solving this until we solve inferred |
| ... | ... | @@ -10842,8 +10831,20 @@ fn fieldVal( |
| 10842 | 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), |
| 10846 | | .Union => return sema.unionFieldVal(block, src, object, field_name, field_name_src, object_ty), |
| 10834 | .Struct => if (is_pointer_to) { |
| 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 | 10848 | else => {}, |
| 10848 | 10849 | } |
| 10849 | 10850 | return sema.fail(block, src, "type '{}' does not support field access", .{object_ty}); |
| ... | ... | @@ -10866,14 +10867,25 @@ fn fieldPtr( |
| 10866 | 10867 | .Pointer => object_ptr_ty.elemType(), |
| 10867 | 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 | 10882 | .Array => { |
| 10871 | 10883 | if (mem.eql(u8, field_name, "len")) { |
| 10872 | 10884 | var anon_decl = try block.startAnonDecl(); |
| 10873 | 10885 | defer anon_decl.deinit(); |
| 10874 | 10886 | return sema.analyzeDeclRef(try anon_decl.finish( |
| 10875 | 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 | 10890 | } else { |
| 10879 | 10891 | return sema.fail( |
| ... | ... | @@ -10884,77 +10896,49 @@ fn fieldPtr( |
| 10884 | 10896 | ); |
| 10885 | 10897 | } |
| 10886 | 10898 | }, |
| 10887 | | .Pointer => switch (object_ty.ptrSize()) { |
| 10888 | | .Slice => { |
| 10889 | | // Here for the ptr and len fields what we need to do is the situation |
| 10890 | | // when a temporary has its address taken, e.g. `&a[c..d].len`. |
| 10891 | | // This value may be known at compile-time or runtime. In the former |
| 10892 | | // case, it should create an anonymous Decl and return a decl_ref to it. |
| 10893 | | // In the latter case, it should add an `alloc` instruction, store |
| 10894 | | // the runtime value to it, and then return the `alloc`. |
| 10895 | | // In both cases the pointer should be const. |
| 10896 | | if (mem.eql(u8, field_name, "ptr")) { |
| 10897 | | return sema.fail( |
| 10898 | | block, |
| 10899 | | field_name_src, |
| 10900 | | "TODO: implement reference to 'ptr' field of slice '{}'", |
| 10901 | | .{object_ty}, |
| 10902 | | ); |
| 10903 | | } else if (mem.eql(u8, field_name, "len")) { |
| 10904 | | return sema.fail( |
| 10905 | | block, |
| 10906 | | field_name_src, |
| 10907 | | "TODO: implement reference to 'len' field of slice '{}'", |
| 10908 | | .{object_ty}, |
| 10909 | | ); |
| 10910 | | } else { |
| 10911 | | return sema.fail( |
| 10912 | | block, |
| 10913 | | field_name_src, |
| 10914 | | "no member named '{s}' in '{}'", |
| 10915 | | .{ field_name, object_ty }, |
| 10916 | | ); |
| 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 => {}, |
| 10899 | .Pointer => if (inner_ty.isSlice()) { |
| 10900 | // Here for the ptr and len fields what we need to do is the situation |
| 10901 | // 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 |
| 10903 | // 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 |
| 10905 | // the runtime value to it, and then return the `alloc`. |
| 10906 | // In both cases the pointer should be const. |
| 10907 | if (mem.eql(u8, field_name, "ptr")) { |
| 10908 | return sema.fail( |
| 10909 | block, |
| 10910 | field_name_src, |
| 10911 | "TODO: implement reference to 'ptr' field of slice '{}'", |
| 10912 | .{inner_ty}, |
| 10913 | ); |
| 10914 | } else if (mem.eql(u8, field_name, "len")) { |
| 10915 | return sema.fail( |
| 10916 | block, |
| 10917 | field_name_src, |
| 10918 | "TODO: implement reference to 'len' field of slice '{}'", |
| 10919 | .{inner_ty}, |
| 10920 | ); |
| 10921 | } else { |
| 10922 | return sema.fail( |
| 10923 | block, |
| 10924 | field_name_src, |
| 10925 | "no member named '{s}' in '{}'", |
| 10926 | .{ field_name, object_ty }, |
| 10927 | ); |
| 10928 | } |
| 10951 | 10929 | }, |
| 10952 | 10930 | .Type => { |
| 10953 | 10931 | _ = try sema.resolveConstValue(block, object_ptr_src, object_ptr); |
| 10954 | 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 | 10939 | var to_type_buffer: Value.ToTypeBuffer = undefined; |
| 10957 | 10940 | const child_type = val.toType(&to_type_buffer); |
| 10941 | |
| 10958 | 10942 | switch (child_type.zigTypeTag()) { |
| 10959 | 10943 | .ErrorSet => { |
| 10960 | 10944 | // TODO resolve inferred error sets |
| ... | ... | @@ -11033,8 +11017,20 @@ fn fieldPtr( |
| 11033 | 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), |
| 11037 | | .Union => return sema.unionFieldPtr(block, src, object_ptr, field_name, field_name_src, object_ty), |
| 11020 | .Struct => { |
| 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 | 11034 | else => {}, |
| 11039 | 11035 | } |
| 11040 | 11036 | return sema.fail(block, src, "type '{}' does not support field access", .{object_ty}); |