authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-20 21:45:11-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-20 21:45:11-07:00
loga0e195120dd3dd7919fd249a6ce2201da9395898
tree1f06851929602eb29932c60418e774f0cb1cab03
parent3b2e25ed8718d8aee085497886967fa21a9697cc

stage2: implement slicing

* 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),
698702
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
727727
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 },
781763
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 location7086 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 };
71187100
...@@ -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}
77187700
7701fn 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
7719fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {7733fn 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 else10835 else
10822 object;10836 object;
1082310837 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 else10841 else
10837 object;10842 object;
1083810843 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}
1263412633
12634fn 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
12635fn analyzeSliceLen(12653fn 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 };
1271712740
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 };
1273412785
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;
1273912790
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 }
1275012792
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 else12796 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 alignment12826 .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;
1277212834
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}
1277512847
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),
420421
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}
876877
878fn 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
877fn airAdd(self: *Self, inst: Air.Inst.Index) !void {884fn 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),
768769
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 }
12461247
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, ">"),
994994
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}
11051107
1106fn airSliceField(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue {1108fn 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;
11091110
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}
16431644
1645fn 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
1644fn airCall(f: *Function, inst: Air.Inst.Index) !CValue {1665fn 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),
16691681
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 }
21252137
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;
21292140
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 }
27232734
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;
27262750
...@@ -2886,26 +2910,42 @@ pub const FuncGen = struct {...@@ -2886,26 +2910,42 @@ pub const FuncGen = struct {
2886 }2910 }
28872911
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;
28912914
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 }
28982930
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;
29022933
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 }
29102950
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 {
234234
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};
238241
239pub const Module = opaque {242pub 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),
144145
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 }
15311531
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 {
17391740
1740 .empty_struct,1741 .empty_struct,
1741 .void,1742 .void,
1743 .c_void,
1742 => return 0,1744 => return 0,
17431745
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().?),
14771488
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}
397397
398test "f32 at compile time is lossy" {
399 try expect(@as(f32, 1 << 24) + 1 == 1 << 24);
400}
401
402test "f64 at compile time is lossy" {398test "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
28test "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
44test "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
54test "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
61test "implicitly cast array of size 0 to slice" {
62 var msg = [_]u8{};
63 try assertLenIsZero(&msg);
64}
65
66fn assertLenIsZero(msg: []const u8) !void {
67 try expect(msg.len == 0);
68}
69
70test "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
83test "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
91test "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;
4const expectEqual = std.testing.expectEqual;4const expectEqual = std.testing.expectEqual;
5const mem = std.mem;5const mem = std.mem;
66
7test "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
23test "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
33test "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
40test "slicing zero length array" {7test "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}
9966
100test "implicitly cast array of size 0 to slice" {
101 var msg = [_]u8{};
102 try assertLenIsZero(&msg);
103}
104
105fn assertLenIsZero(msg: []const u8) !void {
106 try expect(msg.len == 0);
107}
108
109test "C pointer" {67test "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}
152110
153test "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
166test "obtaining a null terminated slice" {111test "obtaining a null terminated slice" {
167 // here we have a normal array112 // 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}
409354
410test "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
418test "comptime slice of pointer preserves comptime var" {355test "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}
435372
436test "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
458test "comptime pointer cast array and then slice" {373test "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 };
460375