| author | |
| committer | |
| log | a0e195120dd3dd7919fd249a6ce2201da9395898 |
| tree | 1f06851929602eb29932c60418e774f0cb1cab03 |
| parent | 3b2e25ed8718d8aee085497886967fa21a9697cc |
* New AIR instruction: slice, which constructs a slice out of a pointer
and a length.
* AstGen: use `coerced_ty` for start and end expressions, use `none`
for the sentinel, and don't try to load the result of the slice
operation because it returns a by-value result.
* Sema: pointer arithmetic is extracted into analyzePointerArithmetic
and it is used by the implementation of slice.
- Also I implemented comptime pointer addition.
* Sema: extract logic into analyzeSlicePtr, analyzeSliceLen and use them
inside the slice semantic analysis.
- The approach in stage2 is much cleaner than stage1 because it uses
more granular analysis calls for obtaining the slice pointer, doing
arithmetic on it, and checking if the length is comptime-known.
* Sema: use the slice Value Tag for slices when doing coercion from
pointer-to-array.
* LLVM backend: detect when emitting a GEP instruction into a
pointer-to-array and add the extra index that is required.
* Type: ptrAlignment for c_void returns 0.
* Implement Value.hash and Value.eql for slices.
* Remove accidentally duplicated behavior test.16 files changed, 383 insertions(+), 226 deletions(-)
src/Air.zig+4| ... | @@ -360,6 +360,9 @@ pub const Inst = struct { | ... | @@ -360,6 +360,9 @@ pub const Inst = struct { |
| 360 | /// Given a tagged union value, get its tag value. | 360 | /// Given a tagged union value, get its tag value. |
| 361 | /// Uses the `ty_op` field. | 361 | /// Uses the `ty_op` field. |
| 362 | get_union_tag, | 362 | get_union_tag, |
| 363 | /// Constructs a slice from a pointer and a length. | ||
| 364 | /// Uses the `ty_pl` field, payload is `Bin`. lhs is ptr, rhs is len. | ||
| 365 | slice, | ||
| 363 | /// Given a slice value, return the length. | 366 | /// Given a slice value, return the length. |
| 364 | /// Result type is always usize. | 367 | /// Result type is always usize. |
| 365 | /// Uses the `ty_op` field. | 368 | /// Uses the `ty_op` field. |
| ... | @@ -694,6 +697,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { | ... | @@ -694,6 +697,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 694 | .ptr_elem_ptr, | 697 | .ptr_elem_ptr, |
| 695 | .cmpxchg_weak, | 698 | .cmpxchg_weak, |
| 696 | .cmpxchg_strong, | 699 | .cmpxchg_strong, |
| 700 | .slice, | ||
| 697 | => return air.getRefType(datas[inst].ty_pl.ty), | 701 | => return air.getRefType(datas[inst].ty_pl.ty), |
| 698 | 702 | ||
| 699 | .not, | 703 | .not, |
src/AstGen.zig+9-27| ... | @@ -727,56 +727,38 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr | ... | @@ -727,56 +727,38 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr |
| 727 | 727 | ||
| 728 | .slice_open => { | 728 | .slice_open => { |
| 729 | const lhs = try expr(gz, scope, .ref, node_datas[node].lhs); | 729 | const lhs = try expr(gz, scope, .ref, node_datas[node].lhs); |
| 730 | const start = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs); | 730 | const start = try expr(gz, scope, .{ .coerced_ty = .usize_type }, node_datas[node].rhs); |
| 731 | const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{ | 731 | const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{ |
| 732 | .lhs = lhs, | 732 | .lhs = lhs, |
| 733 | .start = start, | 733 | .start = start, |
| 734 | }); | 734 | }); |
| 735 | switch (rl) { | 735 | return rvalue(gz, rl, result, node); |
| 736 | .ref => return result, | ||
| 737 | else => { | ||
| 738 | const dereffed = try gz.addUnNode(.load, result, node); | ||
| 739 | return rvalue(gz, rl, dereffed, node); | ||
| 740 | }, | ||
| 741 | } | ||
| 742 | }, | 736 | }, |
| 743 | .slice => { | 737 | .slice => { |
| 744 | const lhs = try expr(gz, scope, .ref, node_datas[node].lhs); | 738 | const lhs = try expr(gz, scope, .ref, node_datas[node].lhs); |
| 745 | const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice); | 739 | const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice); |
| 746 | const start = try expr(gz, scope, .{ .ty = .usize_type }, extra.start); | 740 | const start = try expr(gz, scope, .{ .coerced_ty = .usize_type }, extra.start); |
| 747 | const end = try expr(gz, scope, .{ .ty = .usize_type }, extra.end); | 741 | const end = try expr(gz, scope, .{ .coerced_ty = .usize_type }, extra.end); |
| 748 | const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{ | 742 | const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{ |
| 749 | .lhs = lhs, | 743 | .lhs = lhs, |
| 750 | .start = start, | 744 | .start = start, |
| 751 | .end = end, | 745 | .end = end, |
| 752 | }); | 746 | }); |
| 753 | switch (rl) { | 747 | return rvalue(gz, rl, result, node); |
| 754 | .ref => return result, | ||
| 755 | else => { | ||
| 756 | const dereffed = try gz.addUnNode(.load, result, node); | ||
| 757 | return rvalue(gz, rl, dereffed, node); | ||
| 758 | }, | ||
| 759 | } | ||
| 760 | }, | 748 | }, |
| 761 | .slice_sentinel => { | 749 | .slice_sentinel => { |
| 762 | const lhs = try expr(gz, scope, .ref, node_datas[node].lhs); | 750 | const lhs = try expr(gz, scope, .ref, node_datas[node].lhs); |
| 763 | const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel); | 751 | const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel); |
| 764 | const start = try expr(gz, scope, .{ .ty = .usize_type }, extra.start); | 752 | const start = try expr(gz, scope, .{ .coerced_ty = .usize_type }, extra.start); |
| 765 | const end = if (extra.end != 0) try expr(gz, scope, .{ .ty = .usize_type }, extra.end) else .none; | 753 | const end = if (extra.end != 0) try expr(gz, scope, .{ .coerced_ty = .usize_type }, extra.end) else .none; |
| 766 | const sentinel = try expr(gz, scope, .{ .ty = .usize_type }, extra.sentinel); | 754 | const sentinel = try expr(gz, scope, .none, extra.sentinel); |
| 767 | const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{ | 755 | const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{ |
| 768 | .lhs = lhs, | 756 | .lhs = lhs, |
| 769 | .start = start, | 757 | .start = start, |
| 770 | .end = end, | 758 | .end = end, |
| 771 | .sentinel = sentinel, | 759 | .sentinel = sentinel, |
| 772 | }); | 760 | }); |
| 773 | switch (rl) { | 761 | return rvalue(gz, rl, result, node); |
| 774 | .ref => return result, | ||
| 775 | else => { | ||
| 776 | const dereffed = try gz.addUnNode(.load, result, node); | ||
| 777 | return rvalue(gz, rl, dereffed, node); | ||
| 778 | }, | ||
| 779 | } | ||
| 780 | }, | 762 | }, |
| 781 | 763 | ||
| 782 | .deref => { | 764 | .deref => { |
src/Liveness.zig+1| ... | @@ -266,6 +266,7 @@ fn analyzeInst( | ... | @@ -266,6 +266,7 @@ fn analyzeInst( |
| 266 | .set_union_tag, | 266 | .set_union_tag, |
| 267 | .min, | 267 | .min, |
| 268 | .max, | 268 | .max, |
| 269 | .slice, | ||
| 269 | => { | 270 | => { |
| 270 | const o = inst_datas[inst].bin_op; | 271 | const o = inst_datas[inst].bin_op; |
| 271 | return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none }); | 272 | return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none }); |
src/Sema.zig+164-92| ... | @@ -7083,7 +7083,6 @@ fn analyzeArithmetic( | ... | @@ -7083,7 +7083,6 @@ fn analyzeArithmetic( |
| 7083 | if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize()) { | 7083 | if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize()) { |
| 7084 | .One, .Slice => {}, | 7084 | .One, .Slice => {}, |
| 7085 | .Many, .C => { | 7085 | .Many, .C => { |
| 7086 | // Pointer arithmetic. | ||
| 7087 | const op_src = src; // TODO better source location | 7086 | const op_src = src; // TODO better source location |
| 7088 | const air_tag: Air.Inst.Tag = switch (zir_tag) { | 7087 | const air_tag: Air.Inst.Tag = switch (zir_tag) { |
| 7089 | .add => .ptr_add, | 7088 | .add => .ptr_add, |
| ... | @@ -7095,24 +7094,7 @@ fn analyzeArithmetic( | ... | @@ -7095,24 +7094,7 @@ fn analyzeArithmetic( |
| 7095 | .{@tagName(zir_tag)}, | 7094 | .{@tagName(zir_tag)}, |
| 7096 | ), | 7095 | ), |
| 7097 | }; | 7096 | }; |
| 7098 | // TODO if the operand is comptime-known to be negative, or is a negative int, | 7097 | return analyzePtrArithmetic(sema, block, op_src, lhs, rhs, air_tag, lhs_src, rhs_src); |
| 7099 | // coerce to isize instead of usize. | ||
| 7100 | const casted_rhs = try sema.coerce(block, Type.usize, rhs, rhs_src); | ||
| 7101 | const runtime_src = runtime_src: { | ||
| 7102 | if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { | ||
| 7103 | if (try sema.resolveDefinedValue(block, rhs_src, casted_rhs)) |rhs_val| { | ||
| 7104 | _ = lhs_val; | ||
| 7105 | _ = rhs_val; | ||
| 7106 | return sema.fail(block, src, "TODO implement Sema for comptime pointer arithmetic", .{}); | ||
| 7107 | } else { | ||
| 7108 | break :runtime_src rhs_src; | ||
| 7109 | } | ||
| 7110 | } else { | ||
| 7111 | break :runtime_src lhs_src; | ||
| 7112 | } | ||
| 7113 | }; | ||
| 7114 | try sema.requireRuntimeBlock(block, runtime_src); | ||
| 7115 | return block.addBinOp(air_tag, lhs, casted_rhs); | ||
| 7116 | }, | 7098 | }, |
| 7117 | }; | 7099 | }; |
| 7118 | 7100 | ||
| ... | @@ -7716,6 +7698,38 @@ fn analyzeArithmetic( | ... | @@ -7716,6 +7698,38 @@ fn analyzeArithmetic( |
| 7716 | return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs); | 7698 | return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs); |
| 7717 | } | 7699 | } |
| 7718 | 7700 | ||
| 7701 | fn analyzePtrArithmetic( | ||
| 7702 | sema: *Sema, | ||
| 7703 | block: *Block, | ||
| 7704 | op_src: LazySrcLoc, | ||
| 7705 | ptr: Air.Inst.Ref, | ||
| 7706 | uncasted_offset: Air.Inst.Ref, | ||
| 7707 | air_tag: Air.Inst.Tag, | ||
| 7708 | ptr_src: LazySrcLoc, | ||
| 7709 | offset_src: LazySrcLoc, | ||
| 7710 | ) CompileError!Air.Inst.Ref { | ||
| 7711 | // TODO if the operand is comptime-known to be negative, or is a negative int, | ||
| 7712 | // coerce to isize instead of usize. | ||
| 7713 | const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src); | ||
| 7714 | // TODO adjust the return type according to alignment and other factors | ||
| 7715 | const runtime_src = rs: { | ||
| 7716 | if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { | ||
| 7717 | if (try sema.resolveDefinedValue(block, offset_src, offset)) |offset_val| { | ||
| 7718 | if (air_tag == .ptr_sub) { | ||
| 7719 | return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{}); | ||
| 7720 | } | ||
| 7721 | const offset_int = offset_val.toUnsignedInt(); | ||
| 7722 | const new_ptr_val = try ptr_val.elemPtr(sema.arena, offset_int); | ||
| 7723 | const new_ptr_ty = sema.typeOf(ptr); | ||
| 7724 | return sema.addConstant(new_ptr_ty, new_ptr_val); | ||
| 7725 | } else break :rs offset_src; | ||
| 7726 | } else break :rs ptr_src; | ||
| 7727 | }; | ||
| 7728 | |||
| 7729 | try sema.requireRuntimeBlock(block, runtime_src); | ||
| 7730 | return block.addBinOp(air_tag, ptr, offset); | ||
| 7731 | } | ||
| 7732 | |||
| 7719 | fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 7733 | fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7720 | const tracy = trace(@src()); | 7734 | const tracy = trace(@src()); |
| 7721 | defer tracy.end(); | 7735 | defer tracy.end(); |
| ... | @@ -10820,33 +10834,13 @@ fn fieldVal( | ... | @@ -10820,33 +10834,13 @@ fn fieldVal( |
| 10820 | try sema.analyzeLoad(block, src, object, object_src) | 10834 | try sema.analyzeLoad(block, src, object, object_src) |
| 10821 | else | 10835 | else |
| 10822 | object; | 10836 | object; |
| 10823 | 10837 | return sema.analyzeSlicePtr(block, src, slice, inner_ty, object_src); | |
| 10824 | const buf = try arena.create(Type.SlicePtrFieldTypeBuffer); | ||
| 10825 | const result_ty = inner_ty.slicePtrFieldType(buf); | ||
| 10826 | |||
| 10827 | if (try sema.resolveMaybeUndefVal(block, object_src, slice)) |val| { | ||
| 10828 | if (val.isUndef()) return sema.addConstUndef(result_ty); | ||
| 10829 | return sema.addConstant(result_ty, val.slicePtr()); | ||
| 10830 | } | ||
| 10831 | try sema.requireRuntimeBlock(block, src); | ||
| 10832 | return block.addTyOp(.slice_ptr, result_ty, slice); | ||
| 10833 | } else if (mem.eql(u8, field_name, "len")) { | 10838 | } else if (mem.eql(u8, field_name, "len")) { |
| 10834 | const slice = if (is_pointer_to) | 10839 | const slice = if (is_pointer_to) |
| 10835 | try sema.analyzeLoad(block, src, object, object_src) | 10840 | try sema.analyzeLoad(block, src, object, object_src) |
| 10836 | else | 10841 | else |
| 10837 | object; | 10842 | object; |
| 10838 | 10843 | return sema.analyzeSliceLen(block, src, slice); | |
| 10839 | const result_ty = Type.usize; | ||
| 10840 | |||
| 10841 | if (try sema.resolveMaybeUndefVal(block, object_src, slice)) |val| { | ||
| 10842 | if (val.isUndef()) return sema.addConstUndef(result_ty); | ||
| 10843 | return sema.addConstant( | ||
| 10844 | result_ty, | ||
| 10845 | try Value.Tag.int_u64.create(arena, val.sliceLen()), | ||
| 10846 | ); | ||
| 10847 | } | ||
| 10848 | try sema.requireRuntimeBlock(block, src); | ||
| 10849 | return block.addTyOp(.slice_len, result_ty, slice); | ||
| 10850 | } else { | 10844 | } else { |
| 10851 | return sema.fail( | 10845 | return sema.fail( |
| 10852 | block, | 10846 | block, |
| ... | @@ -12349,8 +12343,13 @@ fn coerceArrayPtrToSlice( | ... | @@ -12349,8 +12343,13 @@ fn coerceArrayPtrToSlice( |
| 12349 | inst_src: LazySrcLoc, | 12343 | inst_src: LazySrcLoc, |
| 12350 | ) CompileError!Air.Inst.Ref { | 12344 | ) CompileError!Air.Inst.Ref { |
| 12351 | if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { | 12345 | if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { |
| 12352 | // The comptime Value representation is compatible with both types. | 12346 | const ptr_array_ty = sema.typeOf(inst); |
| 12353 | return sema.addConstant(dest_ty, val); | 12347 | const array_ty = ptr_array_ty.childType(); |
| 12348 | const slice_val = try Value.Tag.slice.create(sema.arena, .{ | ||
| 12349 | .ptr = val, | ||
| 12350 | .len = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen()), | ||
| 12351 | }); | ||
| 12352 | return sema.addConstant(dest_ty, slice_val); | ||
| 12354 | } | 12353 | } |
| 12355 | try sema.requireRuntimeBlock(block, inst_src); | 12354 | try sema.requireRuntimeBlock(block, inst_src); |
| 12356 | return block.addTyOp(.array_to_slice, dest_ty, inst); | 12355 | return block.addTyOp(.array_to_slice, dest_ty, inst); |
| ... | @@ -12632,6 +12631,25 @@ fn analyzeLoad( | ... | @@ -12632,6 +12631,25 @@ fn analyzeLoad( |
| 12632 | return block.addTyOp(.load, elem_ty, ptr); | 12631 | return block.addTyOp(.load, elem_ty, ptr); |
| 12633 | } | 12632 | } |
| 12634 | 12633 | ||
| 12634 | fn analyzeSlicePtr( | ||
| 12635 | sema: *Sema, | ||
| 12636 | block: *Block, | ||
| 12637 | src: LazySrcLoc, | ||
| 12638 | slice: Air.Inst.Ref, | ||
| 12639 | slice_ty: Type, | ||
| 12640 | slice_src: LazySrcLoc, | ||
| 12641 | ) CompileError!Air.Inst.Ref { | ||
| 12642 | const buf = try sema.arena.create(Type.SlicePtrFieldTypeBuffer); | ||
| 12643 | const result_ty = slice_ty.slicePtrFieldType(buf); | ||
| 12644 | |||
| 12645 | if (try sema.resolveMaybeUndefVal(block, slice_src, slice)) |val| { | ||
| 12646 | if (val.isUndef()) return sema.addConstUndef(result_ty); | ||
| 12647 | return sema.addConstant(result_ty, val.slicePtr()); | ||
| 12648 | } | ||
| 12649 | try sema.requireRuntimeBlock(block, src); | ||
| 12650 | return block.addTyOp(.slice_ptr, result_ty, slice); | ||
| 12651 | } | ||
| 12652 | |||
| 12635 | fn analyzeSliceLen( | 12653 | fn analyzeSliceLen( |
| 12636 | sema: *Sema, | 12654 | sema: *Sema, |
| 12637 | block: *Block, | 12655 | block: *Block, |
| ... | @@ -12703,74 +12721,128 @@ fn analyzeSlice( | ... | @@ -12703,74 +12721,128 @@ fn analyzeSlice( |
| 12703 | sema: *Sema, | 12721 | sema: *Sema, |
| 12704 | block: *Block, | 12722 | block: *Block, |
| 12705 | src: LazySrcLoc, | 12723 | src: LazySrcLoc, |
| 12706 | array_ptr: Air.Inst.Ref, | 12724 | ptr_ptr: Air.Inst.Ref, |
| 12707 | start: Air.Inst.Ref, | 12725 | uncasted_start: Air.Inst.Ref, |
| 12708 | end_opt: Air.Inst.Ref, | 12726 | uncasted_end_opt: Air.Inst.Ref, |
| 12709 | sentinel_opt: Air.Inst.Ref, | 12727 | sentinel_opt: Air.Inst.Ref, |
| 12710 | sentinel_src: LazySrcLoc, | 12728 | sentinel_src: LazySrcLoc, |
| 12711 | ) CompileError!Air.Inst.Ref { | 12729 | ) CompileError!Air.Inst.Ref { |
| 12712 | const array_ptr_ty = sema.typeOf(array_ptr); | 12730 | const ptr_src = src; // TODO better source location |
| 12713 | const ptr_child = switch (array_ptr_ty.zigTypeTag()) { | 12731 | const start_src = src; // TODO better source location |
| 12714 | .Pointer => array_ptr_ty.elemType(), | 12732 | const end_src = src; // TODO better source location |
| 12715 | else => return sema.fail(block, src, "expected pointer, found '{}'", .{array_ptr_ty}), | 12733 | // Slice expressions can operate on a variable whose type is an array. This requires |
| 12734 | // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer. | ||
| 12735 | const ptr_ptr_ty = sema.typeOf(ptr_ptr); | ||
| 12736 | const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag()) { | ||
| 12737 | .Pointer => ptr_ptr_ty.elemType(), | ||
| 12738 | else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty}), | ||
| 12716 | }; | 12739 | }; |
| 12717 | 12740 | ||
| 12718 | var array_type = ptr_child; | 12741 | var array_ty = ptr_ptr_child_ty; |
| 12719 | const elem_type = switch (ptr_child.zigTypeTag()) { | 12742 | var slice_ty = ptr_ptr_ty; |
| 12720 | .Array => ptr_child.elemType(), | 12743 | var ptr_or_slice = ptr_ptr; |
| 12721 | .Pointer => blk: { | 12744 | var elem_ty = ptr_ptr_child_ty.childType(); |
| 12722 | if (ptr_child.isSinglePointer()) { | 12745 | switch (ptr_ptr_child_ty.zigTypeTag()) { |
| 12723 | if (ptr_child.elemType().zigTypeTag() == .Array) { | 12746 | .Array => {}, |
| 12724 | array_type = ptr_child.elemType(); | 12747 | .Pointer => { |
| 12725 | break :blk ptr_child.elemType().elemType(); | 12748 | if (ptr_ptr_child_ty.isSinglePointer()) { |
| 12749 | const double_child_ty = ptr_ptr_child_ty.childType(); | ||
| 12750 | if (double_child_ty.zigTypeTag() == .Array) { | ||
| 12751 | ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); | ||
| 12752 | slice_ty = ptr_ptr_child_ty; | ||
| 12753 | array_ty = double_child_ty; | ||
| 12754 | elem_ty = double_child_ty.childType(); | ||
| 12755 | } else { | ||
| 12756 | return sema.fail(block, ptr_src, "slice of single-item pointer", .{}); | ||
| 12726 | } | 12757 | } |
| 12727 | |||
| 12728 | return sema.fail(block, src, "slice of single-item pointer", .{}); | ||
| 12729 | } | 12758 | } |
| 12730 | break :blk ptr_child.elemType(); | ||
| 12731 | }, | 12759 | }, |
| 12732 | else => return sema.fail(block, src, "slice of non-array type '{}'", .{ptr_child}), | 12760 | else => return sema.fail(block, ptr_src, "slice of non-array type '{}'", .{ptr_ptr_child_ty}), |
| 12761 | } | ||
| 12762 | const ptr = if (slice_ty.isSlice()) | ||
| 12763 | try sema.analyzeSlicePtr(block, src, ptr_or_slice, slice_ty, ptr_src) | ||
| 12764 | else | ||
| 12765 | ptr_or_slice; | ||
| 12766 | |||
| 12767 | const start = try sema.coerce(block, Type.usize, uncasted_start, start_src); | ||
| 12768 | const new_ptr = try analyzePtrArithmetic(sema, block, src, ptr, start, .ptr_add, ptr_src, start_src); | ||
| 12769 | |||
| 12770 | const end = e: { | ||
| 12771 | if (uncasted_end_opt != .none) { | ||
| 12772 | break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); | ||
| 12773 | } | ||
| 12774 | |||
| 12775 | if (array_ty.zigTypeTag() == .Array) { | ||
| 12776 | break :e try sema.addConstant( | ||
| 12777 | Type.usize, | ||
| 12778 | try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen()), | ||
| 12779 | ); | ||
| 12780 | } else if (slice_ty.isSlice()) { | ||
| 12781 | break :e try sema.analyzeSliceLen(block, src, ptr_or_slice); | ||
| 12782 | } | ||
| 12783 | return sema.fail(block, end_src, "slice of pointer must include end value", .{}); | ||
| 12733 | }; | 12784 | }; |
| 12734 | 12785 | ||
| 12735 | const slice_sentinel = if (sentinel_opt != .none) blk: { | 12786 | const slice_sentinel = if (sentinel_opt != .none) blk: { |
| 12736 | const casted = try sema.coerce(block, elem_type, sentinel_opt, sentinel_src); | 12787 | const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src); |
| 12737 | break :blk try sema.resolveConstValue(block, sentinel_src, casted); | 12788 | break :blk try sema.resolveConstValue(block, sentinel_src, casted); |
| 12738 | } else null; | 12789 | } else null; |
| 12739 | 12790 | ||
| 12740 | var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice; | 12791 | const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src); |
| 12741 | var return_elem_type = elem_type; | ||
| 12742 | if (end_opt != .none) { | ||
| 12743 | if (try sema.resolveDefinedValue(block, src, end_opt)) |end_val| { | ||
| 12744 | if (try sema.resolveDefinedValue(block, src, start)) |start_val| { | ||
| 12745 | const start_u64 = start_val.toUnsignedInt(); | ||
| 12746 | const end_u64 = end_val.toUnsignedInt(); | ||
| 12747 | if (start_u64 > end_u64) { | ||
| 12748 | return sema.fail(block, src, "out of bounds slice", .{}); | ||
| 12749 | } | ||
| 12750 | 12792 | ||
| 12751 | const len = end_u64 - start_u64; | 12793 | const opt_new_ptr_val = try sema.resolveDefinedValue(block, ptr_src, new_ptr); |
| 12752 | const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen()) | 12794 | const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); |
| 12753 | array_type.sentinel() | 12795 | |
| 12754 | else | 12796 | const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data; |
| 12755 | slice_sentinel; | 12797 | |
| 12756 | return_elem_type = try Type.array(sema.arena, len, array_sentinel, elem_type); | 12798 | if (opt_new_len_val) |new_len_val| { |
| 12757 | return_ptr_size = .One; | 12799 | const new_len_int = new_len_val.toUnsignedInt(); |
| 12758 | } | 12800 | |
| 12801 | const sentinel = if (array_ty.zigTypeTag() == .Array and new_len_int == array_ty.arrayLen()) | ||
| 12802 | array_ty.sentinel() | ||
| 12803 | else | ||
| 12804 | slice_sentinel; | ||
| 12805 | |||
| 12806 | const return_ty = try Type.ptr(sema.arena, .{ | ||
| 12807 | .pointee_type = try Type.array(sema.arena, new_len_int, sentinel, elem_ty), | ||
| 12808 | .sentinel = null, | ||
| 12809 | .@"align" = new_ptr_ty_info.@"align", | ||
| 12810 | .@"addrspace" = new_ptr_ty_info.@"addrspace", | ||
| 12811 | .mutable = new_ptr_ty_info.mutable, | ||
| 12812 | .@"allowzero" = new_ptr_ty_info.@"allowzero", | ||
| 12813 | .@"volatile" = new_ptr_ty_info.@"volatile", | ||
| 12814 | .size = .One, | ||
| 12815 | }); | ||
| 12816 | |||
| 12817 | if (opt_new_ptr_val) |new_ptr_val| { | ||
| 12818 | return sema.addConstant(return_ty, new_ptr_val); | ||
| 12819 | } else { | ||
| 12820 | return block.addTyOp(.bitcast, return_ty, new_ptr); | ||
| 12759 | } | 12821 | } |
| 12760 | } | 12822 | } |
| 12761 | const return_type = try Type.ptr(sema.arena, .{ | 12823 | |
| 12762 | .pointee_type = return_elem_type, | 12824 | const return_ty = try Type.ptr(sema.arena, .{ |
| 12763 | .sentinel = if (end_opt == .none) slice_sentinel else null, | 12825 | .pointee_type = elem_ty, |
| 12764 | .@"align" = 0, // TODO alignment | 12826 | .sentinel = slice_sentinel, |
| 12765 | .@"addrspace" = if (ptr_child.zigTypeTag() == .Pointer) ptr_child.ptrAddressSpace() else .generic, | 12827 | .@"align" = new_ptr_ty_info.@"align", |
| 12766 | .mutable = !ptr_child.isConstPtr(), | 12828 | .@"addrspace" = new_ptr_ty_info.@"addrspace", |
| 12767 | .@"allowzero" = ptr_child.isAllowzeroPtr(), | 12829 | .mutable = new_ptr_ty_info.mutable, |
| 12768 | .@"volatile" = ptr_child.isVolatilePtr(), | 12830 | .@"allowzero" = new_ptr_ty_info.@"allowzero", |
| 12769 | .size = return_ptr_size, | 12831 | .@"volatile" = new_ptr_ty_info.@"volatile", |
| 12832 | .size = .Slice, | ||
| 12770 | }); | 12833 | }); |
| 12771 | _ = return_type; | ||
| 12772 | 12834 | ||
| 12773 | return sema.fail(block, src, "TODO implement analysis of slice", .{}); | 12835 | try sema.requireRuntimeBlock(block, src); |
| 12836 | return block.addInst(.{ | ||
| 12837 | .tag = .slice, | ||
| 12838 | .data = .{ .ty_pl = .{ | ||
| 12839 | .ty = try sema.addType(return_ty), | ||
| 12840 | .payload = try sema.addExtra(Air.Bin{ | ||
| 12841 | .lhs = new_ptr, | ||
| 12842 | .rhs = new_len, | ||
| 12843 | }), | ||
| 12844 | } }, | ||
| 12845 | }); | ||
| 12774 | } | 12846 | } |
| 12775 | 12847 | ||
| 12776 | /// Asserts that lhs and rhs types are both numeric. | 12848 | /// Asserts that lhs and rhs types are both numeric. |
src/Zir.zig+1| ... | @@ -482,6 +482,7 @@ pub const Inst = struct { | ... | @@ -482,6 +482,7 @@ pub const Inst = struct { |
| 482 | /// Includes a token source location. | 482 | /// Includes a token source location. |
| 483 | /// Uses the `un_tok` union field. | 483 | /// Uses the `un_tok` union field. |
| 484 | /// The operand needs to get coerced to the function's return type. | 484 | /// The operand needs to get coerced to the function's return type. |
| 485 | /// TODO rename this to `ret_tok` because coercion is now done unconditionally in Sema. | ||
| 485 | ret_coerce, | 486 | ret_coerce, |
| 486 | /// Sends control flow back to the function's callee. | 487 | /// Sends control flow back to the function's callee. |
| 487 | /// The return operand is `error.foo` where `foo` is given by the string. | 488 | /// The return operand is `error.foo` where `foo` is given by the string. |
src/arch/aarch64/CodeGen.zig+7| ... | @@ -417,6 +417,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -417,6 +417,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 417 | .shl_sat => try self.airShlSat(inst), | 417 | .shl_sat => try self.airShlSat(inst), |
| 418 | .min => try self.airMin(inst), | 418 | .min => try self.airMin(inst), |
| 419 | .max => try self.airMax(inst), | 419 | .max => try self.airMax(inst), |
| 420 | .slice => try self.airSlice(inst), | ||
| 420 | 421 | ||
| 421 | .cmp_lt => try self.airCmp(inst, .lt), | 422 | .cmp_lt => try self.airCmp(inst, .lt), |
| 422 | .cmp_lte => try self.airCmp(inst, .lte), | 423 | .cmp_lte => try self.airCmp(inst, .lte), |
| ... | @@ -874,6 +875,12 @@ fn airMax(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -874,6 +875,12 @@ fn airMax(self: *Self, inst: Air.Inst.Index) !void { |
| 874 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 875 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 875 | } | 876 | } |
| 876 | 877 | ||
| 878 | fn airSlice(self: *Self, inst: Air.Inst.Index) !void { | ||
| 879 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | ||
| 880 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice for {}", .{self.target.cpu.arch}); | ||
| 881 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | ||
| 882 | } | ||
| 883 | |||
| 877 | fn airAdd(self: *Self, inst: Air.Inst.Index) !void { | 884 | fn airAdd(self: *Self, inst: Air.Inst.Index) !void { |
| 878 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 885 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 879 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add for {}", .{self.target.cpu.arch}); | 886 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add for {}", .{self.target.cpu.arch}); |
src/codegen.zig+9| ... | @@ -765,6 +765,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -765,6 +765,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 765 | .shl_sat => try self.airShlSat(inst), | 765 | .shl_sat => try self.airShlSat(inst), |
| 766 | .min => try self.airMin(inst), | 766 | .min => try self.airMin(inst), |
| 767 | .max => try self.airMax(inst), | 767 | .max => try self.airMax(inst), |
| 768 | .slice => try self.airSlice(inst), | ||
| 768 | 769 | ||
| 769 | .cmp_lt => try self.airCmp(inst, .lt), | 770 | .cmp_lt => try self.airCmp(inst, .lt), |
| 770 | .cmp_lte => try self.airCmp(inst, .lte), | 771 | .cmp_lte => try self.airCmp(inst, .lte), |
| ... | @@ -1244,6 +1245,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1244,6 +1245,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1244 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1245 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1245 | } | 1246 | } |
| 1246 | 1247 | ||
| 1248 | fn airSlice(self: *Self, inst: Air.Inst.Index) !void { | ||
| 1249 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | ||
| 1250 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | ||
| 1251 | else => return self.fail("TODO implement slice for {}", .{self.target.cpu.arch}), | ||
| 1252 | }; | ||
| 1253 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | ||
| 1254 | } | ||
| 1255 | |||
| 1247 | fn airAdd(self: *Self, inst: Air.Inst.Index) !void { | 1256 | fn airAdd(self: *Self, inst: Air.Inst.Index) !void { |
| 1248 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1257 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1249 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | 1258 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { |
src/codegen/c.zig+23-2| ... | @@ -992,6 +992,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO | ... | @@ -992,6 +992,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 992 | .min => try airMinMax(f, inst, "<"), | 992 | .min => try airMinMax(f, inst, "<"), |
| 993 | .max => try airMinMax(f, inst, ">"), | 993 | .max => try airMinMax(f, inst, ">"), |
| 994 | 994 | ||
| 995 | .slice => try airSlice(f, inst), | ||
| 996 | |||
| 995 | .cmp_eq => try airBinOp(f, inst, " == "), | 997 | .cmp_eq => try airBinOp(f, inst, " == "), |
| 996 | .cmp_gt => try airBinOp(f, inst, " > "), | 998 | .cmp_gt => try airBinOp(f, inst, " > "), |
| 997 | .cmp_gte => try airBinOp(f, inst, " >= "), | 999 | .cmp_gte => try airBinOp(f, inst, " >= "), |
| ... | @@ -1104,8 +1106,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO | ... | @@ -1104,8 +1106,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1104 | } | 1106 | } |
| 1105 | 1107 | ||
| 1106 | fn airSliceField(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue { | 1108 | fn airSliceField(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue { |
| 1107 | if (f.liveness.isUnused(inst)) | 1109 | if (f.liveness.isUnused(inst)) return CValue.none; |
| 1108 | return CValue.none; | ||
| 1109 | 1110 | ||
| 1110 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; | 1111 | const ty_op = f.air.instructions.items(.data)[inst].ty_op; |
| 1111 | const operand = try f.resolveInst(ty_op.operand); | 1112 | const operand = try f.resolveInst(ty_op.operand); |
| ... | @@ -1641,6 +1642,26 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValu | ... | @@ -1641,6 +1642,26 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValu |
| 1641 | return local; | 1642 | return local; |
| 1642 | } | 1643 | } |
| 1643 | 1644 | ||
| 1645 | fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { | ||
| 1646 | if (f.liveness.isUnused(inst)) return CValue.none; | ||
| 1647 | |||
| 1648 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | ||
| 1649 | const ptr = try f.resolveInst(bin_op.lhs); | ||
| 1650 | const len = try f.resolveInst(bin_op.rhs); | ||
| 1651 | |||
| 1652 | const writer = f.object.writer(); | ||
| 1653 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 1654 | const local = try f.allocLocal(inst_ty, .Const); | ||
| 1655 | |||
| 1656 | try writer.writeAll(" = {"); | ||
| 1657 | try f.writeCValue(writer, ptr); | ||
| 1658 | try writer.writeAll(", "); | ||
| 1659 | try f.writeCValue(writer, len); | ||
| 1660 | try writer.writeAll("};\n"); | ||
| 1661 | |||
| 1662 | return local; | ||
| 1663 | } | ||
| 1664 | |||
| 1644 | fn airCall(f: *Function, inst: Air.Inst.Index) !CValue { | 1665 | fn airCall(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1645 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; | 1666 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; |
| 1646 | const extra = f.air.extraData(Air.Call, pl_op.payload); | 1667 | const extra = f.air.extraData(Air.Call, pl_op.payload); |
src/codegen/llvm.zig+55-15| ... | @@ -1091,11 +1091,22 @@ pub const DeclGen = struct { | ... | @@ -1091,11 +1091,22 @@ pub const DeclGen = struct { |
| 1091 | const elem_ptr = tv.val.castTag(.elem_ptr).?.data; | 1091 | const elem_ptr = tv.val.castTag(.elem_ptr).?.data; |
| 1092 | const parent_ptr = try self.lowerParentPtr(elem_ptr.array_ptr); | 1092 | const parent_ptr = try self.lowerParentPtr(elem_ptr.array_ptr); |
| 1093 | const llvm_usize = try self.llvmType(Type.usize); | 1093 | const llvm_usize = try self.llvmType(Type.usize); |
| 1094 | const indices: [2]*const llvm.Value = .{ | 1094 | if (parent_ptr.typeOf().getElementType().getTypeKind() == .Array) { |
| 1095 | llvm_usize.constInt(0, .False), | 1095 | const indices: [2]*const llvm.Value = .{ |
| 1096 | llvm_usize.constInt(elem_ptr.index, .False), | 1096 | llvm_usize.constInt(0, .False), |
| 1097 | }; | 1097 | llvm_usize.constInt(elem_ptr.index, .False), |
| 1098 | return parent_ptr.constInBoundsGEP(&indices, indices.len); | 1098 | }; |
| 1099 | return parent_ptr.constInBoundsGEP(&indices, indices.len); | ||
| 1100 | } else { | ||
| 1101 | const indices: [1]*const llvm.Value = .{ | ||
| 1102 | llvm_usize.constInt(elem_ptr.index, .False), | ||
| 1103 | }; | ||
| 1104 | return parent_ptr.constInBoundsGEP(&indices, indices.len); | ||
| 1105 | } | ||
| 1106 | }, | ||
| 1107 | .null_value => { | ||
| 1108 | const llvm_type = try self.llvmType(tv.ty); | ||
| 1109 | return llvm_type.constNull(); | ||
| 1099 | }, | 1110 | }, |
| 1100 | else => |tag| return self.todo("implement const of pointer type '{}' ({})", .{ tv.ty, tag }), | 1111 | else => |tag| return self.todo("implement const of pointer type '{}' ({})", .{ tv.ty, tag }), |
| 1101 | }, | 1112 | }, |
| ... | @@ -1666,6 +1677,7 @@ pub const FuncGen = struct { | ... | @@ -1666,6 +1677,7 @@ pub const FuncGen = struct { |
| 1666 | .shl_exact => try self.airShlExact(inst), | 1677 | .shl_exact => try self.airShlExact(inst), |
| 1667 | .min => try self.airMin(inst), | 1678 | .min => try self.airMin(inst), |
| 1668 | .max => try self.airMax(inst), | 1679 | .max => try self.airMax(inst), |
| 1680 | .slice => try self.airSlice(inst), | ||
| 1669 | 1681 | ||
| 1670 | .bit_and, .bool_and => try self.airAnd(inst), | 1682 | .bit_and, .bool_and => try self.airAnd(inst), |
| 1671 | .bit_or, .bool_or => try self.airOr(inst), | 1683 | .bit_or, .bool_or => try self.airOr(inst), |
| ... | @@ -2124,8 +2136,7 @@ pub const FuncGen = struct { | ... | @@ -2124,8 +2136,7 @@ pub const FuncGen = struct { |
| 2124 | } | 2136 | } |
| 2125 | 2137 | ||
| 2126 | fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value { | 2138 | fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value { |
| 2127 | if (self.liveness.isUnused(inst)) | 2139 | if (self.liveness.isUnused(inst)) return null; |
| 2128 | return null; | ||
| 2129 | 2140 | ||
| 2130 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2141 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2131 | const operand = try self.resolveInst(ty_op.operand); | 2142 | const operand = try self.resolveInst(ty_op.operand); |
| ... | @@ -2721,6 +2732,19 @@ pub const FuncGen = struct { | ... | @@ -2721,6 +2732,19 @@ pub const FuncGen = struct { |
| 2721 | return self.builder.buildUMax(lhs, rhs, ""); | 2732 | return self.builder.buildUMax(lhs, rhs, ""); |
| 2722 | } | 2733 | } |
| 2723 | 2734 | ||
| 2735 | fn airSlice(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | ||
| 2736 | if (self.liveness.isUnused(inst)) return null; | ||
| 2737 | |||
| 2738 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | ||
| 2739 | const ptr = try self.resolveInst(bin_op.lhs); | ||
| 2740 | const len = try self.resolveInst(bin_op.rhs); | ||
| 2741 | const inst_ty = self.air.typeOfIndex(inst); | ||
| 2742 | const llvm_slice_ty = try self.dg.llvmType(inst_ty); | ||
| 2743 | |||
| 2744 | const partial = self.builder.buildInsertValue(llvm_slice_ty.getUndef(), ptr, 0, ""); | ||
| 2745 | return self.builder.buildInsertValue(partial, len, 1, ""); | ||
| 2746 | } | ||
| 2747 | |||
| 2724 | fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | 2748 | fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 2725 | if (self.liveness.isUnused(inst)) return null; | 2749 | if (self.liveness.isUnused(inst)) return null; |
| 2726 | 2750 | ||
| ... | @@ -2886,26 +2910,42 @@ pub const FuncGen = struct { | ... | @@ -2886,26 +2910,42 @@ pub const FuncGen = struct { |
| 2886 | } | 2910 | } |
| 2887 | 2911 | ||
| 2888 | fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | 2912 | fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 2889 | if (self.liveness.isUnused(inst)) | 2913 | if (self.liveness.isUnused(inst)) return null; |
| 2890 | return null; | ||
| 2891 | 2914 | ||
| 2892 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 2915 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2893 | const base_ptr = try self.resolveInst(bin_op.lhs); | 2916 | const base_ptr = try self.resolveInst(bin_op.lhs); |
| 2894 | const offset = try self.resolveInst(bin_op.rhs); | 2917 | const offset = try self.resolveInst(bin_op.rhs); |
| 2895 | const indices: [1]*const llvm.Value = .{offset}; | 2918 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 2896 | return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); | 2919 | if (ptr_ty.ptrSize() == .One) { |
| 2920 | // It's a pointer to an array, so according to LLVM we need an extra GEP index. | ||
| 2921 | const indices: [2]*const llvm.Value = .{ | ||
| 2922 | self.context.intType(32).constNull(), offset, | ||
| 2923 | }; | ||
| 2924 | return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); | ||
| 2925 | } else { | ||
| 2926 | const indices: [1]*const llvm.Value = .{offset}; | ||
| 2927 | return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); | ||
| 2928 | } | ||
| 2897 | } | 2929 | } |
| 2898 | 2930 | ||
| 2899 | fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | 2931 | fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 2900 | if (self.liveness.isUnused(inst)) | 2932 | if (self.liveness.isUnused(inst)) return null; |
| 2901 | return null; | ||
| 2902 | 2933 | ||
| 2903 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 2934 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2904 | const base_ptr = try self.resolveInst(bin_op.lhs); | 2935 | const base_ptr = try self.resolveInst(bin_op.lhs); |
| 2905 | const offset = try self.resolveInst(bin_op.rhs); | 2936 | const offset = try self.resolveInst(bin_op.rhs); |
| 2906 | const negative_offset = self.builder.buildNeg(offset, ""); | 2937 | const negative_offset = self.builder.buildNeg(offset, ""); |
| 2907 | const indices: [1]*const llvm.Value = .{negative_offset}; | 2938 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 2908 | return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); | 2939 | if (ptr_ty.ptrSize() == .One) { |
| 2940 | // It's a pointer to an array, so according to LLVM we need an extra GEP index. | ||
| 2941 | const indices: [2]*const llvm.Value = .{ | ||
| 2942 | self.context.intType(32).constNull(), negative_offset, | ||
| 2943 | }; | ||
| 2944 | return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); | ||
| 2945 | } else { | ||
| 2946 | const indices: [1]*const llvm.Value = .{negative_offset}; | ||
| 2947 | return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); | ||
| 2948 | } | ||
| 2909 | } | 2949 | } |
| 2910 | 2950 | ||
| 2911 | fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | 2951 | fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
src/codegen/llvm/bindings.zig+3| ... | @@ -234,6 +234,9 @@ pub const Type = opaque { | ... | @@ -234,6 +234,9 @@ pub const Type = opaque { |
| 234 | 234 | ||
| 235 | pub const getTypeKind = LLVMGetTypeKind; | 235 | pub const getTypeKind = LLVMGetTypeKind; |
| 236 | extern fn LLVMGetTypeKind(Ty: *const Type) TypeKind; | 236 | extern fn LLVMGetTypeKind(Ty: *const Type) TypeKind; |
| 237 | |||
| 238 | pub const getElementType = LLVMGetElementType; | ||
| 239 | extern fn LLVMGetElementType(Ty: *const Type) *const Type; | ||
| 237 | }; | 240 | }; |
| 238 | 241 | ||
| 239 | pub const Module = opaque { | 242 | pub const Module = opaque { |
src/print_air.zig+1| ... | @@ -140,6 +140,7 @@ const Writer = struct { | ... | @@ -140,6 +140,7 @@ const Writer = struct { |
| 140 | .set_union_tag, | 140 | .set_union_tag, |
| 141 | .min, | 141 | .min, |
| 142 | .max, | 142 | .max, |
| 143 | .slice, | ||
| 143 | => try w.writeBinOp(s, inst), | 144 | => try w.writeBinOp(s, inst), |
| 144 | 145 | ||
| 145 | .is_null, | 146 | .is_null, |
src/type.zig+2-1| ... | @@ -1529,6 +1529,7 @@ pub const Type = extern union { | ... | @@ -1529,6 +1529,7 @@ pub const Type = extern union { |
| 1529 | return fast_result; | 1529 | return fast_result; |
| 1530 | } | 1530 | } |
| 1531 | 1531 | ||
| 1532 | /// Returns 0 if the pointer is naturally aligned and the element type is 0-bit. | ||
| 1532 | pub fn ptrAlignment(self: Type, target: Target) u32 { | 1533 | pub fn ptrAlignment(self: Type, target: Target) u32 { |
| 1533 | switch (self.tag()) { | 1534 | switch (self.tag()) { |
| 1534 | .single_const_pointer, | 1535 | .single_const_pointer, |
| ... | @@ -1739,10 +1740,10 @@ pub const Type = extern union { | ... | @@ -1739,10 +1740,10 @@ pub const Type = extern union { |
| 1739 | 1740 | ||
| 1740 | .empty_struct, | 1741 | .empty_struct, |
| 1741 | .void, | 1742 | .void, |
| 1743 | .c_void, | ||
| 1742 | => return 0, | 1744 | => return 0, |
| 1743 | 1745 | ||
| 1744 | .empty_struct_literal, | 1746 | .empty_struct_literal, |
| 1745 | .c_void, | ||
| 1746 | .type, | 1747 | .type, |
| 1747 | .comptime_int, | 1748 | .comptime_int, |
| 1748 | .comptime_float, | 1749 | .comptime_float, |
src/value.zig+19| ... | @@ -761,6 +761,7 @@ pub const Value = extern union { | ... | @@ -761,6 +761,7 @@ pub const Value = extern union { |
| 761 | return decl_val.toAllocatedBytes(decl.ty, allocator); | 761 | return decl_val.toAllocatedBytes(decl.ty, allocator); |
| 762 | }, | 762 | }, |
| 763 | .the_only_possible_value => return &[_]u8{}, | 763 | .the_only_possible_value => return &[_]u8{}, |
| 764 | .slice => return toAllocatedBytes(val.castTag(.slice).?.data.ptr, ty, allocator), | ||
| 764 | else => unreachable, | 765 | else => unreachable, |
| 765 | } | 766 | } |
| 766 | } | 767 | } |
| ... | @@ -1402,6 +1403,16 @@ pub const Value = extern union { | ... | @@ -1402,6 +1403,16 @@ pub const Value = extern union { |
| 1402 | var buffer: Type.Payload.ElemType = undefined; | 1403 | var buffer: Type.Payload.ElemType = undefined; |
| 1403 | return eql(a_payload, b_payload, ty.optionalChild(&buffer)); | 1404 | return eql(a_payload, b_payload, ty.optionalChild(&buffer)); |
| 1404 | }, | 1405 | }, |
| 1406 | .slice => { | ||
| 1407 | const a_payload = a.castTag(.slice).?.data; | ||
| 1408 | const b_payload = b.castTag(.slice).?.data; | ||
| 1409 | if (!eql(a_payload.len, b_payload.len, Type.usize)) return false; | ||
| 1410 | |||
| 1411 | var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; | ||
| 1412 | const ptr_ty = ty.slicePtrFieldType(&ptr_buf); | ||
| 1413 | |||
| 1414 | return eql(a_payload.ptr, b_payload.ptr, ptr_ty); | ||
| 1415 | }, | ||
| 1405 | .elem_ptr => @panic("TODO: Implement more pointer eql cases"), | 1416 | .elem_ptr => @panic("TODO: Implement more pointer eql cases"), |
| 1406 | .field_ptr => @panic("TODO: Implement more pointer eql cases"), | 1417 | .field_ptr => @panic("TODO: Implement more pointer eql cases"), |
| 1407 | .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"), | 1418 | .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"), |
| ... | @@ -1475,6 +1486,14 @@ pub const Value = extern union { | ... | @@ -1475,6 +1486,14 @@ pub const Value = extern union { |
| 1475 | .variable, | 1486 | .variable, |
| 1476 | => std.hash.autoHash(hasher, val.pointerDecl().?), | 1487 | => std.hash.autoHash(hasher, val.pointerDecl().?), |
| 1477 | 1488 | ||
| 1489 | .slice => { | ||
| 1490 | const slice = val.castTag(.slice).?.data; | ||
| 1491 | var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; | ||
| 1492 | const ptr_ty = ty.slicePtrFieldType(&ptr_buf); | ||
| 1493 | hash(slice.ptr, ptr_ty, hasher); | ||
| 1494 | hash(slice.len, Type.usize, hasher); | ||
| 1495 | }, | ||
| 1496 | |||
| 1478 | .elem_ptr => @panic("TODO: Implement more pointer hashing cases"), | 1497 | .elem_ptr => @panic("TODO: Implement more pointer hashing cases"), |
| 1479 | .field_ptr => @panic("TODO: Implement more pointer hashing cases"), | 1498 | .field_ptr => @panic("TODO: Implement more pointer hashing cases"), |
| 1480 | .eu_payload_ptr => @panic("TODO: Implement more pointer hashing cases"), | 1499 | .eu_payload_ptr => @panic("TODO: Implement more pointer hashing cases"), |
test/behavior/eval.zig-4| ... | @@ -395,10 +395,6 @@ test "f32 at compile time is lossy" { | ... | @@ -395,10 +395,6 @@ test "f32 at compile time is lossy" { |
| 395 | try expect(@as(f32, 1 << 24) + 1 == 1 << 24); | 395 | try expect(@as(f32, 1 << 24) + 1 == 1 << 24); |
| 396 | } | 396 | } |
| 397 | 397 | ||
| 398 | test "f32 at compile time is lossy" { | ||
| 399 | try expect(@as(f32, 1 << 24) + 1 == 1 << 24); | ||
| 400 | } | ||
| 401 | |||
| 402 | test "f64 at compile time is lossy" { | 398 | test "f64 at compile time is lossy" { |
| 403 | try expect(@as(f64, 1 << 53) + 1 == 1 << 53); | 399 | try expect(@as(f64, 1 << 53) + 1 == 1 << 53); |
| 404 | } | 400 | } |
test/behavior/slice.zig+85| ... | @@ -24,3 +24,88 @@ comptime { | ... | @@ -24,3 +24,88 @@ comptime { |
| 24 | var pos = S.indexOfScalar(type, list, c_ulong).?; | 24 | var pos = S.indexOfScalar(type, list, c_ulong).?; |
| 25 | if (pos != 1) @compileError("bad pos"); | 25 | if (pos != 1) @compileError("bad pos"); |
| 26 | } | 26 | } |
| 27 | |||
| 28 | test "slicing" { | ||
| 29 | var array: [20]i32 = undefined; | ||
| 30 | |||
| 31 | array[5] = 1234; | ||
| 32 | |||
| 33 | var slice = array[5..10]; | ||
| 34 | |||
| 35 | if (slice.len != 5) unreachable; | ||
| 36 | |||
| 37 | const ptr = &slice[0]; | ||
| 38 | if (ptr.* != 1234) unreachable; | ||
| 39 | |||
| 40 | var slice_rest = array[10..]; | ||
| 41 | if (slice_rest.len != 10) unreachable; | ||
| 42 | } | ||
| 43 | |||
| 44 | test "const slice" { | ||
| 45 | comptime { | ||
| 46 | const a = "1234567890"; | ||
| 47 | try expect(a.len == 10); | ||
| 48 | const b = a[1..2]; | ||
| 49 | try expect(b.len == 1); | ||
| 50 | try expect(b[0] == '2'); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | test "comptime slice of undefined pointer of length 0" { | ||
| 55 | const slice1 = @as([*]i32, undefined)[0..0]; | ||
| 56 | try expect(slice1.len == 0); | ||
| 57 | const slice2 = @as([*]i32, undefined)[100..100]; | ||
| 58 | try expect(slice2.len == 0); | ||
| 59 | } | ||
| 60 | |||
| 61 | test "implicitly cast array of size 0 to slice" { | ||
| 62 | var msg = [_]u8{}; | ||
| 63 | try assertLenIsZero(&msg); | ||
| 64 | } | ||
| 65 | |||
| 66 | fn assertLenIsZero(msg: []const u8) !void { | ||
| 67 | try expect(msg.len == 0); | ||
| 68 | } | ||
| 69 | |||
| 70 | test "access len index of sentinel-terminated slice" { | ||
| 71 | const S = struct { | ||
| 72 | fn doTheTest() !void { | ||
| 73 | var slice: [:0]const u8 = "hello"; | ||
| 74 | |||
| 75 | try expect(slice.len == 5); | ||
| 76 | try expect(slice[5] == 0); | ||
| 77 | } | ||
| 78 | }; | ||
| 79 | try S.doTheTest(); | ||
| 80 | comptime try S.doTheTest(); | ||
| 81 | } | ||
| 82 | |||
| 83 | test "comptime slice of slice preserves comptime var" { | ||
| 84 | comptime { | ||
| 85 | var buff: [10]u8 = undefined; | ||
| 86 | buff[0..][0..][0] = 1; | ||
| 87 | try expect(buff[0..][0..][0] == 1); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | test "slice of type" { | ||
| 92 | comptime { | ||
| 93 | var types_array = [_]type{ i32, f64, type }; | ||
| 94 | for (types_array) |T, i| { | ||
| 95 | switch (i) { | ||
| 96 | 0 => try expect(T == i32), | ||
| 97 | 1 => try expect(T == f64), | ||
| 98 | 2 => try expect(T == type), | ||
| 99 | else => unreachable, | ||
| 100 | } | ||
| 101 | } | ||
| 102 | for (types_array[0..]) |T, i| { | ||
| 103 | switch (i) { | ||
| 104 | 0 => try expect(T == i32), | ||
| 105 | 1 => try expect(T == f64), | ||
| 106 | 2 => try expect(T == type), | ||
| 107 | else => unreachable, | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } |
test/behavior/slice_stage1.zig-85| ... | @@ -4,39 +4,6 @@ const expectEqualSlices = std.testing.expectEqualSlices; | ... | @@ -4,39 +4,6 @@ const expectEqualSlices = std.testing.expectEqualSlices; |
| 4 | const expectEqual = std.testing.expectEqual; | 4 | const expectEqual = std.testing.expectEqual; |
| 5 | const mem = std.mem; | 5 | const mem = std.mem; |
| 6 | 6 | ||
| 7 | test "slicing" { | ||
| 8 | var array: [20]i32 = undefined; | ||
| 9 | |||
| 10 | array[5] = 1234; | ||
| 11 | |||
| 12 | var slice = array[5..10]; | ||
| 13 | |||
| 14 | if (slice.len != 5) unreachable; | ||
| 15 | |||
| 16 | const ptr = &slice[0]; | ||
| 17 | if (ptr.* != 1234) unreachable; | ||
| 18 | |||
| 19 | var slice_rest = array[10..]; | ||
| 20 | if (slice_rest.len != 10) unreachable; | ||
| 21 | } | ||
| 22 | |||
| 23 | test "const slice" { | ||
| 24 | comptime { | ||
| 25 | const a = "1234567890"; | ||
| 26 | try expect(a.len == 10); | ||
| 27 | const b = a[1..2]; | ||
| 28 | try expect(b.len == 1); | ||
| 29 | try expect(b[0] == '2'); | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | test "comptime slice of undefined pointer of length 0" { | ||
| 34 | const slice1 = @as([*]i32, undefined)[0..0]; | ||
| 35 | try expect(slice1.len == 0); | ||
| 36 | const slice2 = @as([*]i32, undefined)[100..100]; | ||
| 37 | try expect(slice2.len == 0); | ||
| 38 | } | ||
| 39 | |||
| 40 | test "slicing zero length array" { | 7 | test "slicing zero length array" { |
| 41 | const s1 = ""[0..]; | 8 | const s1 = ""[0..]; |
| 42 | const s2 = ([_]u32{})[0..]; | 9 | const s2 = ([_]u32{})[0..]; |
| ... | @@ -97,15 +64,6 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { | ... | @@ -97,15 +64,6 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { |
| 97 | return a_slice[start..end]; | 64 | return a_slice[start..end]; |
| 98 | } | 65 | } |
| 99 | 66 | ||
| 100 | test "implicitly cast array of size 0 to slice" { | ||
| 101 | var msg = [_]u8{}; | ||
| 102 | try assertLenIsZero(&msg); | ||
| 103 | } | ||
| 104 | |||
| 105 | fn assertLenIsZero(msg: []const u8) !void { | ||
| 106 | try expect(msg.len == 0); | ||
| 107 | } | ||
| 108 | |||
| 109 | test "C pointer" { | 67 | test "C pointer" { |
| 110 | var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf"; | 68 | var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf"; |
| 111 | var len: u32 = 10; | 69 | var len: u32 = 10; |
| ... | @@ -150,19 +108,6 @@ test "slice type with custom alignment" { | ... | @@ -150,19 +108,6 @@ test "slice type with custom alignment" { |
| 150 | try expect(array[1].anything == 42); | 108 | try expect(array[1].anything == 42); |
| 151 | } | 109 | } |
| 152 | 110 | ||
| 153 | test "access len index of sentinel-terminated slice" { | ||
| 154 | const S = struct { | ||
| 155 | fn doTheTest() !void { | ||
| 156 | var slice: [:0]const u8 = "hello"; | ||
| 157 | |||
| 158 | try expect(slice.len == 5); | ||
| 159 | try expect(slice[5] == 0); | ||
| 160 | } | ||
| 161 | }; | ||
| 162 | try S.doTheTest(); | ||
| 163 | comptime try S.doTheTest(); | ||
| 164 | } | ||
| 165 | |||
| 166 | test "obtaining a null terminated slice" { | 111 | test "obtaining a null terminated slice" { |
| 167 | // here we have a normal array | 112 | // here we have a normal array |
| 168 | var buf: [50]u8 = undefined; | 113 | var buf: [50]u8 = undefined; |
| ... | @@ -407,14 +352,6 @@ test "type coercion of pointer to anon struct literal to pointer to slice" { | ... | @@ -407,14 +352,6 @@ test "type coercion of pointer to anon struct literal to pointer to slice" { |
| 407 | comptime try S.doTheTest(); | 352 | comptime try S.doTheTest(); |
| 408 | } | 353 | } |
| 409 | 354 | ||
| 410 | test "comptime slice of slice preserves comptime var" { | ||
| 411 | comptime { | ||
| 412 | var buff: [10]u8 = undefined; | ||
| 413 | buff[0..][0..][0] = 1; | ||
| 414 | try expect(buff[0..][0..][0] == 1); | ||
| 415 | } | ||
| 416 | } | ||
| 417 | |||
| 418 | test "comptime slice of pointer preserves comptime var" { | 355 | test "comptime slice of pointer preserves comptime var" { |
| 419 | comptime { | 356 | comptime { |
| 420 | var buff: [10]u8 = undefined; | 357 | var buff: [10]u8 = undefined; |
| ... | @@ -433,28 +370,6 @@ test "array concat of slices gives slice" { | ... | @@ -433,28 +370,6 @@ test "array concat of slices gives slice" { |
| 433 | } | 370 | } |
| 434 | } | 371 | } |
| 435 | 372 | ||
| 436 | test "slice of type" { | ||
| 437 | comptime { | ||
| 438 | var types_array = [_]type{ i32, f64, type }; | ||
| 439 | for (types_array) |T, i| { | ||
| 440 | switch (i) { | ||
| 441 | 0 => try expect(T == i32), | ||
| 442 | 1 => try expect(T == f64), | ||
| 443 | 2 => try expect(T == type), | ||
| 444 | else => unreachable, | ||
| 445 | } | ||
| 446 | } | ||
| 447 | for (types_array[0..]) |T, i| { | ||
| 448 | switch (i) { | ||
| 449 | 0 => try expect(T == i32), | ||
| 450 | 1 => try expect(T == f64), | ||
| 451 | 2 => try expect(T == type), | ||
| 452 | else => unreachable, | ||
| 453 | } | ||
| 454 | } | ||
| 455 | } | ||
| 456 | } | ||
| 457 | |||
| 458 | test "comptime pointer cast array and then slice" { | 373 | test "comptime pointer cast array and then slice" { |
| 459 | const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; | 374 | const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; |
| 460 | 375 |