authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-07 15:41:52-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-07 15:46:53-07:00
logf81b2531cb4904064446f84a06f6e09e4120e28a
treee4bac7a1a13c56b728309086d983091e9e009a65
parentade85471e2cdab466ba685a38c2c7949c9dd1632

stage2: pass some pointer tests

* New AIR instructions: ptr_add, ptr_sub, ptr_elem_val, ptr_ptr_elem_val - See the doc comments for details. * Sema: implement runtime pointer arithmetic. * Sema: implement elem_val for many-pointers. * Sema: support coercion from `*[N:s]T` to `[*]T`. * Type: isIndexable handles many-pointers.

12 files changed, 577 insertions(+), 366 deletions(-)

src/Air.zig+28-5
...@@ -69,6 +69,18 @@ pub const Inst = struct {...@@ -69,6 +69,18 @@ pub const Inst = struct {
69 /// is the same as both operands.69 /// is the same as both operands.
70 /// Uses the `bin_op` field.70 /// Uses the `bin_op` field.
71 div,71 div,
72 /// Add an offset to a pointer, returning a new pointer.
73 /// The offset is in element type units, not bytes.
74 /// Wrapping is undefined behavior.
75 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
76 /// Uses the `bin_op` field.
77 ptr_add,
78 /// Subtract an offset from a pointer, returning a new pointer.
79 /// The offset is in element type units, not bytes.
80 /// Wrapping is undefined behavior.
81 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
82 /// Uses the `bin_op` field.
83 ptr_sub,
72 /// Allocates stack local memory.84 /// Allocates stack local memory.
73 /// Uses the `ty` field.85 /// Uses the `ty` field.
74 alloc,86 alloc,
...@@ -264,6 +276,15 @@ pub const Inst = struct {...@@ -264,6 +276,15 @@ pub const Inst = struct {
264 /// Result type is the element type of the slice operand (2 element type operations).276 /// Result type is the element type of the slice operand (2 element type operations).
265 /// Uses the `bin_op` field.277 /// Uses the `bin_op` field.
266 ptr_slice_elem_val,278 ptr_slice_elem_val,
279 /// Given a pointer value, and element index, return the element value at that index.
280 /// Result type is the element type of the pointer operand.
281 /// Uses the `bin_op` field.
282 ptr_elem_val,
283 /// Given a pointer to a pointer, and element index, return the element value of the inner
284 /// pointer at that index.
285 /// Result type is the element type of the inner pointer operand.
286 /// Uses the `bin_op` field.
287 ptr_ptr_elem_val,
267288
268 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {289 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
269 return switch (op) {290 return switch (op) {
...@@ -422,6 +443,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -422,6 +443,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
422 .bit_and,443 .bit_and,
423 .bit_or,444 .bit_or,
424 .xor,445 .xor,
446 .ptr_add,
447 .ptr_sub,
425 => return air.typeOf(datas[inst].bin_op.lhs),448 => return air.typeOf(datas[inst].bin_op.lhs),
426449
427 .cmp_lt,450 .cmp_lt,
...@@ -495,14 +518,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -495,14 +518,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
495 return callee_ty.fnReturnType();518 return callee_ty.fnReturnType();
496 },519 },
497520
498 .slice_elem_val => {521 .slice_elem_val, .ptr_elem_val => {
499 const slice_ty = air.typeOf(datas[inst].bin_op.lhs);522 const slice_ty = air.typeOf(datas[inst].bin_op.lhs);
500 return slice_ty.elemType();523 return slice_ty.elemType();
501 },524 },
502 .ptr_slice_elem_val => {525 .ptr_slice_elem_val, .ptr_ptr_elem_val => {
503 const ptr_slice_ty = air.typeOf(datas[inst].bin_op.lhs);526 const outer_ptr_ty = air.typeOf(datas[inst].bin_op.lhs);
504 const slice_ty = ptr_slice_ty.elemType();527 const inner_ptr_ty = outer_ptr_ty.elemType();
505 return slice_ty.elemType();528 return inner_ptr_ty.elemType();
506 },529 },
507 }530 }
508}531}
src/Liveness.zig+4
...@@ -231,6 +231,8 @@ fn analyzeInst(...@@ -231,6 +231,8 @@ fn analyzeInst(
231 .mul,231 .mul,
232 .mulwrap,232 .mulwrap,
233 .div,233 .div,
234 .ptr_add,
235 .ptr_sub,
234 .bit_and,236 .bit_and,
235 .bit_or,237 .bit_or,
236 .xor,238 .xor,
...@@ -245,6 +247,8 @@ fn analyzeInst(...@@ -245,6 +247,8 @@ fn analyzeInst(
245 .store,247 .store,
246 .slice_elem_val,248 .slice_elem_val,
247 .ptr_slice_elem_val,249 .ptr_slice_elem_val,
250 .ptr_elem_val,
251 .ptr_ptr_elem_val,
248 => {252 => {
249 const o = inst_datas[inst].bin_op;253 const o = inst_datas[inst].bin_op;
250 return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none });254 return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none });
src/Sema.zig+113-35
...@@ -5471,6 +5471,41 @@ fn analyzeArithmetic(...@@ -5471,6 +5471,41 @@ fn analyzeArithmetic(
5471 lhs_ty, rhs_ty,5471 lhs_ty, rhs_ty,
5472 });5472 });
5473 }5473 }
5474 if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize()) {
5475 .One, .Slice => {},
5476 .Many, .C => {
5477 // Pointer arithmetic.
5478 const op_src = src; // TODO better source location
5479 const air_tag: Air.Inst.Tag = switch (zir_tag) {
5480 .add => .ptr_add,
5481 .sub => .ptr_sub,
5482 else => return sema.mod.fail(
5483 &block.base,
5484 op_src,
5485 "invalid pointer arithmetic operand: '{s}''",
5486 .{@tagName(zir_tag)},
5487 ),
5488 };
5489 // TODO if the operand is comptime-known to be negative, or is a negative int,
5490 // coerce to isize instead of usize.
5491 const casted_rhs = try sema.coerce(block, Type.initTag(.usize), rhs, rhs_src);
5492 const runtime_src = runtime_src: {
5493 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
5494 if (try sema.resolveDefinedValue(block, rhs_src, casted_rhs)) |rhs_val| {
5495 _ = lhs_val;
5496 _ = rhs_val;
5497 return sema.mod.fail(&block.base, src, "TODO implement Sema for comptime pointer arithmetic", .{});
5498 } else {
5499 break :runtime_src rhs_src;
5500 }
5501 } else {
5502 break :runtime_src lhs_src;
5503 }
5504 };
5505 try sema.requireRuntimeBlock(block, runtime_src);
5506 return block.addBinOp(air_tag, lhs, casted_rhs);
5507 },
5508 };
54745509
5475 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };5510 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
5476 const resolved_type = try sema.resolvePeerTypes(block, src, instructions);5511 const resolved_type = try sema.resolvePeerTypes(block, src, instructions);
...@@ -7959,38 +7994,83 @@ fn elemVal(...@@ -7959,38 +7994,83 @@ fn elemVal(
7959) CompileError!Air.Inst.Ref {7994) CompileError!Air.Inst.Ref {
7960 const array_ptr_src = src; // TODO better source location7995 const array_ptr_src = src; // TODO better source location
7961 const maybe_ptr_ty = sema.typeOf(array_maybe_ptr);7996 const maybe_ptr_ty = sema.typeOf(array_maybe_ptr);
7962 if (maybe_ptr_ty.isSinglePointer()) {7997 switch (maybe_ptr_ty.zigTypeTag()) {
7963 const indexable_ty = maybe_ptr_ty.elemType();7998 .Pointer => switch (maybe_ptr_ty.ptrSize()) {
7964 if (indexable_ty.isSlice()) {7999 .Slice => {
7965 // We have a pointer to a slice and we want an element value.8000 if (try sema.resolveDefinedValue(block, src, array_maybe_ptr)) |slice_val| {
7966 if (try sema.isComptimeKnown(block, src, array_maybe_ptr)) {
7967 const slice = try sema.analyzeLoad(block, src, array_maybe_ptr, array_ptr_src);
7968 if (try sema.resolveDefinedValue(block, src, slice)) |slice_val| {
7969 _ = slice_val;8001 _ = slice_val;
7970 return sema.mod.fail(&block.base, src, "TODO implement Sema for elemVal for comptime known slice", .{});8002 return sema.mod.fail(&block.base, src, "TODO implement Sema for elemVal for comptime known slice", .{});
7971 }8003 }
7972 try sema.requireRuntimeBlock(block, src);8004 try sema.requireRuntimeBlock(block, src);
7973 return block.addBinOp(.slice_elem_val, slice, elem_index);8005 return block.addBinOp(.slice_elem_val, array_maybe_ptr, elem_index);
7974 }8006 },
7975 try sema.requireRuntimeBlock(block, src);8007 .Many, .C => {
7976 return block.addBinOp(.ptr_slice_elem_val, array_maybe_ptr, elem_index);8008 if (try sema.resolveDefinedValue(block, src, array_maybe_ptr)) |ptr_val| {
7977 }8009 _ = ptr_val;
7978 }8010 return sema.mod.fail(&block.base, src, "TODO implement Sema for elemVal for comptime known pointer", .{});
7979 if (maybe_ptr_ty.isSlice()) {8011 }
7980 if (try sema.resolveDefinedValue(block, src, array_maybe_ptr)) |slice_val| {8012 try sema.requireRuntimeBlock(block, src);
7981 _ = slice_val;8013 return block.addBinOp(.ptr_elem_val, array_maybe_ptr, elem_index);
7982 return sema.mod.fail(&block.base, src, "TODO implement Sema for elemVal for comptime known slice", .{});8014 },
7983 }8015 .One => {
7984 try sema.requireRuntimeBlock(block, src);8016 const indexable_ty = maybe_ptr_ty.elemType();
7985 return block.addBinOp(.slice_elem_val, array_maybe_ptr, elem_index);8017 switch (indexable_ty.zigTypeTag()) {
8018 .Pointer => switch (indexable_ty.ptrSize()) {
8019 .Slice => {
8020 // We have a pointer to a slice and we want an element value.
8021 if (try sema.isComptimeKnown(block, src, array_maybe_ptr)) {
8022 const slice = try sema.analyzeLoad(block, src, array_maybe_ptr, array_ptr_src);
8023 if (try sema.resolveDefinedValue(block, src, slice)) |slice_val| {
8024 _ = slice_val;
8025 return sema.mod.fail(&block.base, src, "TODO implement Sema for elemVal for comptime known slice", .{});
8026 }
8027 try sema.requireRuntimeBlock(block, src);
8028 return block.addBinOp(.slice_elem_val, slice, elem_index);
8029 }
8030 try sema.requireRuntimeBlock(block, src);
8031 return block.addBinOp(.ptr_slice_elem_val, array_maybe_ptr, elem_index);
8032 },
8033 .Many, .C => {
8034 // We have a pointer to a pointer and we want an element value.
8035 if (try sema.isComptimeKnown(block, src, array_maybe_ptr)) {
8036 const ptr = try sema.analyzeLoad(block, src, array_maybe_ptr, array_ptr_src);
8037 if (try sema.resolveDefinedValue(block, src, ptr)) |ptr_val| {
8038 _ = ptr_val;
8039 return sema.mod.fail(&block.base, src, "TODO implement Sema for elemVal for comptime known pointer", .{});
8040 }
8041 try sema.requireRuntimeBlock(block, src);
8042 return block.addBinOp(.ptr_elem_val, ptr, elem_index);
8043 }
8044 try sema.requireRuntimeBlock(block, src);
8045 return block.addBinOp(.ptr_ptr_elem_val, array_maybe_ptr, elem_index);
8046 },
8047 .One => return sema.mod.fail(
8048 &block.base,
8049 array_ptr_src,
8050 "expected pointer, found '{}'",
8051 .{indexable_ty.elemType()},
8052 ),
8053 },
8054 .Array => {
8055 const ptr = try sema.elemPtr(block, src, array_maybe_ptr, elem_index, elem_index_src);
8056 return sema.analyzeLoad(block, src, ptr, elem_index_src);
8057 },
8058 else => return sema.mod.fail(
8059 &block.base,
8060 array_ptr_src,
8061 "expected pointer, found '{}'",
8062 .{indexable_ty},
8063 ),
8064 }
8065 },
8066 },
8067 else => return sema.mod.fail(
8068 &block.base,
8069 array_ptr_src,
8070 "expected pointer, found '{}'",
8071 .{maybe_ptr_ty},
8072 ),
7986 }8073 }
7987
7988 const array_ptr = if (maybe_ptr_ty.zigTypeTag() == .Pointer)
7989 array_maybe_ptr
7990 else
7991 try sema.analyzeRef(block, src, array_maybe_ptr);
7992 const ptr = try sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);
7993 return sema.analyzeLoad(block, src, ptr, elem_index_src);
7994}8074}
79958075
7996fn elemPtrArray(8076fn elemPtrArray(
...@@ -8107,17 +8187,15 @@ fn coerce(...@@ -8107,17 +8187,15 @@ fn coerce(
8107 .Many => {8187 .Many => {
8108 // *[N]T to [*]T8188 // *[N]T to [*]T
8109 // *[N:s]T to [*:s]T8189 // *[N:s]T to [*:s]T
8110 const src_sentinel = array_type.sentinel();8190 // *[N:s]T to [*]T
8111 const dst_sentinel = dest_type.sentinel();8191 if (dest_type.sentinel()) |dst_sentinel| {
8112 if (src_sentinel == null and dst_sentinel == null)8192 if (array_type.sentinel()) |src_sentinel| {
8113 return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src);8193 if (src_sentinel.eql(dst_sentinel, dst_elem_type)) {
8114
8115 if (src_sentinel) |src_s| {
8116 if (dst_sentinel) |dst_s| {
8117 if (src_s.eql(dst_s, dst_elem_type)) {
8118 return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src);8194 return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src);
8119 }8195 }
8120 }8196 }
8197 } else {
8198 return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src);
8121 }8199 }
8122 },8200 },
8123 .One => {},8201 .One => {},
src/codegen.zig+31-9
...@@ -802,13 +802,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -802,13 +802,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
802802
803 switch (air_tags[inst]) {803 switch (air_tags[inst]) {
804 // zig fmt: off804 // zig fmt: off
805 .add => try self.airAdd(inst),805 .add, .ptr_add => try self.airAdd(inst),
806 .addwrap => try self.airAddWrap(inst),806 .addwrap => try self.airAddWrap(inst),
807 .sub => try self.airSub(inst),807 .sub, .ptr_sub => try self.airSub(inst),
808 .subwrap => try self.airSubWrap(inst),808 .subwrap => try self.airSubWrap(inst),
809 .mul => try self.airMul(inst),809 .mul => try self.airMul(inst),
810 .mulwrap => try self.airMulWrap(inst),810 .mulwrap => try self.airMulWrap(inst),
811 .div => try self.airDiv(inst),811 .div => try self.airDiv(inst),
812812
813 .cmp_lt => try self.airCmp(inst, .lt),813 .cmp_lt => try self.airCmp(inst, .lt),
814 .cmp_lte => try self.airCmp(inst, .lte),814 .cmp_lte => try self.airCmp(inst, .lte),
...@@ -859,6 +859,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -859,6 +859,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
859859
860 .slice_elem_val => try self.airSliceElemVal(inst),860 .slice_elem_val => try self.airSliceElemVal(inst),
861 .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),861 .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),
862 .ptr_elem_val => try self.airPtrElemVal(inst),
863 .ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst),
862864
863 .constant => unreachable, // excluded from function bodies865 .constant => unreachable, // excluded from function bodies
864 .const_ty => unreachable, // excluded from function bodies866 .const_ty => unreachable, // excluded from function bodies
...@@ -1369,21 +1371,41 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1369,21 +1371,41 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1369 }1371 }
13701372
1371 fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {1373 fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
1374 const is_volatile = false; // TODO
1372 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1375 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1373 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {1376 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else switch (arch) {
1374 else => return self.fail("TODO implement slice_elem_val for {}", .{self.target.cpu.arch}),1377 else => return self.fail("TODO implement slice_elem_val for {}", .{self.target.cpu.arch}),
1375 };1378 };
1376 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1379 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1377 }1380 }
13781381
1379 fn airPtrSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {1382 fn airPtrSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
1383 const is_volatile = false; // TODO
1380 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1384 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1381 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {1385 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else switch (arch) {
1382 else => return self.fail("TODO implement ptr_slice_elem_val for {}", .{self.target.cpu.arch}),1386 else => return self.fail("TODO implement ptr_slice_elem_val for {}", .{self.target.cpu.arch}),
1383 };1387 };
1384 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1388 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1385 }1389 }
13861390
1391 fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
1392 const is_volatile = false; // TODO
1393 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1394 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else switch (arch) {
1395 else => return self.fail("TODO implement ptr_elem_val for {}", .{self.target.cpu.arch}),
1396 };
1397 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1398 }
1399
1400 fn airPtrPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
1401 const is_volatile = false; // TODO
1402 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1403 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else switch (arch) {
1404 else => return self.fail("TODO implement ptr_ptr_elem_val for {}", .{self.target.cpu.arch}),
1405 };
1406 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1407 }
1408
1387 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {1409 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
1388 if (!self.liveness.operandDies(inst, op_index))1410 if (!self.liveness.operandDies(inst, op_index))
1389 return false;1411 return false;
src/codegen/c.zig+20-8
...@@ -850,19 +850,19 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM...@@ -850,19 +850,19 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
850850
851 // TODO use a different strategy for add that communicates to the optimizer851 // TODO use a different strategy for add that communicates to the optimizer
852 // that wrapping is UB.852 // that wrapping is UB.
853 .add => try airBinOp( o, inst, " + "),853 .add, .ptr_add => try airBinOp( o, inst, " + "),
854 .addwrap => try airWrapOp(o, inst, " + ", "addw_"),854 .addwrap => try airWrapOp(o, inst, " + ", "addw_"),
855 // TODO use a different strategy for sub that communicates to the optimizer855 // TODO use a different strategy for sub that communicates to the optimizer
856 // that wrapping is UB.856 // that wrapping is UB.
857 .sub => try airBinOp( o, inst, " - "),857 .sub, .ptr_sub => try airBinOp( o, inst, " - "),
858 .subwrap => try airWrapOp(o, inst, " - ", "subw_"),858 .subwrap => try airWrapOp(o, inst, " - ", "subw_"),
859 // TODO use a different strategy for mul that communicates to the optimizer859 // TODO use a different strategy for mul that communicates to the optimizer
860 // that wrapping is UB.860 // that wrapping is UB.
861 .mul => try airBinOp( o, inst, " * "),861 .mul => try airBinOp( o, inst, " * "),
862 .mulwrap => try airWrapOp(o, inst, " * ", "mulw_"),862 .mulwrap => try airWrapOp(o, inst, " * ", "mulw_"),
863 // TODO use a different strategy for div that communicates to the optimizer863 // TODO use a different strategy for div that communicates to the optimizer
864 // that wrapping is UB.864 // that wrapping is UB.
865 .div => try airBinOp( o, inst, " / "),865 .div => try airBinOp( o, inst, " / "),
866866
867 .cmp_eq => try airBinOp(o, inst, " == "),867 .cmp_eq => try airBinOp(o, inst, " == "),
868 .cmp_gt => try airBinOp(o, inst, " > "),868 .cmp_gt => try airBinOp(o, inst, " > "),
...@@ -915,6 +915,8 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM...@@ -915,6 +915,8 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
915 .slice_ptr => try airSliceField(o, inst, ".ptr;\n"),915 .slice_ptr => try airSliceField(o, inst, ".ptr;\n"),
916 .slice_len => try airSliceField(o, inst, ".len;\n"),916 .slice_len => try airSliceField(o, inst, ".len;\n"),
917917
918 .ptr_elem_val => try airPtrElemVal(o, inst, "["),
919 .ptr_ptr_elem_val => try airPtrElemVal(o, inst, "[0]["),
918 .slice_elem_val => try airSliceElemVal(o, inst, "["),920 .slice_elem_val => try airSliceElemVal(o, inst, "["),
919 .ptr_slice_elem_val => try airSliceElemVal(o, inst, "[0]["),921 .ptr_slice_elem_val => try airSliceElemVal(o, inst, "[0]["),
920922
...@@ -953,8 +955,18 @@ fn airSliceField(o: *Object, inst: Air.Inst.Index, suffix: []const u8) !CValue {...@@ -953,8 +955,18 @@ fn airSliceField(o: *Object, inst: Air.Inst.Index, suffix: []const u8) !CValue {
953 return local;955 return local;
954}956}
955957
958fn airPtrElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue {
959 const is_volatile = false; // TODO
960 if (!is_volatile and o.liveness.isUnused(inst))
961 return CValue.none;
962
963 _ = prefix;
964 return o.dg.fail("TODO: C backend: airPtrElemVal", .{});
965}
966
956fn airSliceElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue {967fn airSliceElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue {
957 if (o.liveness.isUnused(inst))968 const is_volatile = false; // TODO
969 if (!is_volatile and o.liveness.isUnused(inst))
958 return CValue.none;970 return CValue.none;
959971
960 const bin_op = o.air.instructions.items(.data)[inst].bin_op;972 const bin_op = o.air.instructions.items(.data)[inst].bin_op;
src/codegen/llvm.zig+58-2
...@@ -966,6 +966,8 @@ pub const FuncGen = struct {...@@ -966,6 +966,8 @@ pub const FuncGen = struct {
966 .mul => try self.airMul(inst, false),966 .mul => try self.airMul(inst, false),
967 .mulwrap => try self.airMul(inst, true),967 .mulwrap => try self.airMul(inst, true),
968 .div => try self.airDiv(inst),968 .div => try self.airDiv(inst),
969 .ptr_add => try self.airPtrAdd(inst),
970 .ptr_sub => try self.airPtrSub(inst),
969971
970 .bit_and, .bool_and => try self.airAnd(inst),972 .bit_and, .bool_and => try self.airAnd(inst),
971 .bit_or, .bool_or => try self.airOr(inst),973 .bit_or, .bool_or => try self.airOr(inst),
...@@ -1015,6 +1017,8 @@ pub const FuncGen = struct {...@@ -1015,6 +1017,8 @@ pub const FuncGen = struct {
10151017
1016 .slice_elem_val => try self.airSliceElemVal(inst),1018 .slice_elem_val => try self.airSliceElemVal(inst),
1017 .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),1019 .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),
1020 .ptr_elem_val => try self.airPtrElemVal(inst),
1021 .ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst),
10181022
1019 .optional_payload => try self.airOptionalPayload(inst, false),1023 .optional_payload => try self.airOptionalPayload(inst, false),
1020 .optional_payload_ptr => try self.airOptionalPayload(inst, true),1024 .optional_payload_ptr => try self.airOptionalPayload(inst, true),
...@@ -1229,7 +1233,8 @@ pub const FuncGen = struct {...@@ -1229,7 +1233,8 @@ pub const FuncGen = struct {
1229 }1233 }
12301234
1231 fn airSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1235 fn airSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1232 if (self.liveness.isUnused(inst))1236 const is_volatile = false; // TODO
1237 if (!is_volatile and self.liveness.isUnused(inst))
1233 return null;1238 return null;
12341239
1235 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1240 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
...@@ -1242,7 +1247,8 @@ pub const FuncGen = struct {...@@ -1242,7 +1247,8 @@ pub const FuncGen = struct {
1242 }1247 }
12431248
1244 fn airPtrSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1249 fn airPtrSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1245 if (self.liveness.isUnused(inst))1250 const is_volatile = false; // TODO
1251 if (!is_volatile and self.liveness.isUnused(inst))
1246 return null;1252 return null;
12471253
1248 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1254 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
...@@ -1264,6 +1270,33 @@ pub const FuncGen = struct {...@@ -1264,6 +1270,33 @@ pub const FuncGen = struct {
1264 return self.builder.buildLoad(ptr, "");1270 return self.builder.buildLoad(ptr, "");
1265 }1271 }
12661272
1273 fn airPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1274 const is_volatile = false; // TODO
1275 if (!is_volatile and self.liveness.isUnused(inst))
1276 return null;
1277
1278 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1279 const base_ptr = try self.resolveInst(bin_op.lhs);
1280 const rhs = try self.resolveInst(bin_op.rhs);
1281 const indices: [1]*const llvm.Value = .{rhs};
1282 const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
1283 return self.builder.buildLoad(ptr, "");
1284 }
1285
1286 fn airPtrPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1287 const is_volatile = false; // TODO
1288 if (!is_volatile and self.liveness.isUnused(inst))
1289 return null;
1290
1291 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1292 const lhs = try self.resolveInst(bin_op.lhs);
1293 const rhs = try self.resolveInst(bin_op.rhs);
1294 const base_ptr = self.builder.buildLoad(lhs, "");
1295 const indices: [1]*const llvm.Value = .{rhs};
1296 const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
1297 return self.builder.buildLoad(ptr, "");
1298 }
1299
1267 fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1300 fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1268 if (self.liveness.isUnused(inst))1301 if (self.liveness.isUnused(inst))
1269 return null;1302 return null;
...@@ -1624,6 +1657,29 @@ pub const FuncGen = struct {...@@ -1624,6 +1657,29 @@ pub const FuncGen = struct {
1624 return self.builder.buildUDiv(lhs, rhs, "");1657 return self.builder.buildUDiv(lhs, rhs, "");
1625 }1658 }
16261659
1660 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1661 if (self.liveness.isUnused(inst))
1662 return null;
1663
1664 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1665 const base_ptr = try self.resolveInst(bin_op.lhs);
1666 const offset = try self.resolveInst(bin_op.rhs);
1667 const indices: [1]*const llvm.Value = .{offset};
1668 return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
1669 }
1670
1671 fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1672 if (self.liveness.isUnused(inst))
1673 return null;
1674
1675 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1676 const base_ptr = try self.resolveInst(bin_op.lhs);
1677 const offset = try self.resolveInst(bin_op.rhs);
1678 const negative_offset = self.builder.buildNeg(offset, "");
1679 const indices: [1]*const llvm.Value = .{negative_offset};
1680 return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
1681 }
1682
1627 fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1683 fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1628 if (self.liveness.isUnused(inst))1684 if (self.liveness.isUnused(inst))
1629 return null;1685 return null;
src/codegen/llvm/bindings.zig+3
...@@ -324,6 +324,9 @@ pub const Builder = opaque {...@@ -324,6 +324,9 @@ pub const Builder = opaque {
324 pub const buildLoad = LLVMBuildLoad;324 pub const buildLoad = LLVMBuildLoad;
325 extern fn LLVMBuildLoad(*const Builder, PointerVal: *const Value, Name: [*:0]const u8) *const Value;325 extern fn LLVMBuildLoad(*const Builder, PointerVal: *const Value, Name: [*:0]const u8) *const Value;
326326
327 pub const buildNeg = LLVMBuildNeg;
328 extern fn LLVMBuildNeg(*const Builder, V: *const Value, Name: [*:0]const u8) *const Value;
329
327 pub const buildNot = LLVMBuildNot;330 pub const buildNot = LLVMBuildNot;
328 extern fn LLVMBuildNot(*const Builder, V: *const Value, Name: [*:0]const u8) *const Value;331 extern fn LLVMBuildNot(*const Builder, V: *const Value, Name: [*:0]const u8) *const Value;
329332
src/print_air.zig+4
...@@ -109,6 +109,8 @@ const Writer = struct {...@@ -109,6 +109,8 @@ const Writer = struct {
109 .mul,109 .mul,
110 .mulwrap,110 .mulwrap,
111 .div,111 .div,
112 .ptr_add,
113 .ptr_sub,
112 .bit_and,114 .bit_and,
113 .bit_or,115 .bit_or,
114 .xor,116 .xor,
...@@ -123,6 +125,8 @@ const Writer = struct {...@@ -123,6 +125,8 @@ const Writer = struct {
123 .store,125 .store,
124 .slice_elem_val,126 .slice_elem_val,
125 .ptr_slice_elem_val,127 .ptr_slice_elem_val,
128 .ptr_elem_val,
129 .ptr_ptr_elem_val,
126 => try w.writeBinOp(s, inst),130 => try w.writeBinOp(s, inst),
127131
128 .is_null,132 .is_null,
src/type.zig+9-5
...@@ -2753,11 +2753,15 @@ pub const Type = extern union {...@@ -2753,11 +2753,15 @@ pub const Type = extern union {
2753 };2753 };
2754 }2754 }
27552755
2756 pub fn isIndexable(self: Type) bool {2756 pub fn isIndexable(ty: Type) bool {
2757 const zig_tag = self.zigTypeTag();2757 return switch (ty.zigTypeTag()) {
2758 // TODO tuples are indexable2758 .Array, .Vector => true,
2759 return zig_tag == .Array or zig_tag == .Vector or self.isSlice() or2759 .Pointer => switch (ty.ptrSize()) {
2760 (self.isSinglePointer() and self.elemType().zigTypeTag() == .Array);2760 .Slice, .Many, .C => true,
2761 .One => ty.elemType().zigTypeTag() == .Array,
2762 },
2763 else => false, // TODO tuples are indexable
2764 };
2761 }2765 }
27622766
2763 /// Returns null if the type has no namespace.2767 /// Returns null if the type has no namespace.
test/behavior.zig+2-1
...@@ -6,6 +6,7 @@ test {...@@ -6,6 +6,7 @@ test {
6 _ = @import("behavior/basic.zig");6 _ = @import("behavior/basic.zig");
7 _ = @import("behavior/generics.zig");7 _ = @import("behavior/generics.zig");
8 _ = @import("behavior/eval.zig");8 _ = @import("behavior/eval.zig");
9 _ = @import("behavior/pointers.zig");
910
10 if (!builtin.zig_is_stage2) {11 if (!builtin.zig_is_stage2) {
11 // Tests that only pass for stage1.12 // Tests that only pass for stage1.
...@@ -112,7 +113,7 @@ test {...@@ -112,7 +113,7 @@ test {
112 _ = @import("behavior/namespace_depends_on_compile_var.zig");113 _ = @import("behavior/namespace_depends_on_compile_var.zig");
113 _ = @import("behavior/null.zig");114 _ = @import("behavior/null.zig");
114 _ = @import("behavior/optional.zig");115 _ = @import("behavior/optional.zig");
115 _ = @import("behavior/pointers.zig");116 _ = @import("behavior/pointers_stage1.zig");
116 _ = @import("behavior/popcount.zig");117 _ = @import("behavior/popcount.zig");
117 _ = @import("behavior/ptrcast.zig");118 _ = @import("behavior/ptrcast.zig");
118 _ = @import("behavior/pub_enum.zig");119 _ = @import("behavior/pub_enum.zig");
test/behavior/pointers.zig-301
...@@ -15,22 +15,6 @@ fn testDerefPtr() !void {...@@ -15,22 +15,6 @@ fn testDerefPtr() !void {
15 try expect(x == 1235);15 try expect(x == 1235);
16}16}
1717
18const Foo1 = struct {
19 x: void,
20};
21
22test "dereference pointer again" {
23 try testDerefPtrOneVal();
24 comptime try testDerefPtrOneVal();
25}
26
27fn testDerefPtrOneVal() !void {
28 // Foo1 satisfies the OnePossibleValueYes criteria
29 const x = &Foo1{ .x = {} };
30 const y = x.*;
31 try expect(@TypeOf(y.x) == void);
32}
33
34test "pointer arithmetic" {18test "pointer arithmetic" {
35 var ptr: [*]const u8 = "abcd";19 var ptr: [*]const u8 = "abcd";
3620
...@@ -60,288 +44,3 @@ test "double pointer parsing" {...@@ -60,288 +44,3 @@ test "double pointer parsing" {
60fn PtrOf(comptime T: type) type {44fn PtrOf(comptime T: type) type {
61 return *T;45 return *T;
62}46}
63
64test "assigning integer to C pointer" {
65 var x: i32 = 0;
66 var ptr: [*c]u8 = 0;
67 var ptr2: [*c]u8 = x;
68 if (false) {
69 ptr;
70 ptr2;
71 }
72}
73
74test "implicit cast single item pointer to C pointer and back" {
75 var y: u8 = 11;
76 var x: [*c]u8 = &y;
77 var z: *u8 = x;
78 z.* += 1;
79 try expect(y == 12);
80}
81
82test "C pointer comparison and arithmetic" {
83 const S = struct {
84 fn doTheTest() !void {
85 var ptr1: [*c]u32 = 0;
86 var ptr2 = ptr1 + 10;
87 try expect(ptr1 == 0);
88 try expect(ptr1 >= 0);
89 try expect(ptr1 <= 0);
90 // expect(ptr1 < 1);
91 // expect(ptr1 < one);
92 // expect(1 > ptr1);
93 // expect(one > ptr1);
94 try expect(ptr1 < ptr2);
95 try expect(ptr2 > ptr1);
96 try expect(ptr2 >= 40);
97 try expect(ptr2 == 40);
98 try expect(ptr2 <= 40);
99 ptr2 -= 10;
100 try expect(ptr1 == ptr2);
101 }
102 };
103 try S.doTheTest();
104 comptime try S.doTheTest();
105}
106
107test "peer type resolution with C pointers" {
108 var ptr_one: *u8 = undefined;
109 var ptr_many: [*]u8 = undefined;
110 var ptr_c: [*c]u8 = undefined;
111 var t = true;
112 var x1 = if (t) ptr_one else ptr_c;
113 var x2 = if (t) ptr_many else ptr_c;
114 var x3 = if (t) ptr_c else ptr_one;
115 var x4 = if (t) ptr_c else ptr_many;
116 try expect(@TypeOf(x1) == [*c]u8);
117 try expect(@TypeOf(x2) == [*c]u8);
118 try expect(@TypeOf(x3) == [*c]u8);
119 try expect(@TypeOf(x4) == [*c]u8);
120}
121
122test "implicit casting between C pointer and optional non-C pointer" {
123 var slice: []const u8 = "aoeu";
124 const opt_many_ptr: ?[*]const u8 = slice.ptr;
125 var ptr_opt_many_ptr = &opt_many_ptr;
126 var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr;
127 try expect(c_ptr.*.* == 'a');
128 ptr_opt_many_ptr = c_ptr;
129 try expect(ptr_opt_many_ptr.*.?[1] == 'o');
130}
131
132test "implicit cast error unions with non-optional to optional pointer" {
133 const S = struct {
134 fn doTheTest() !void {
135 try expectError(error.Fail, foo());
136 }
137 fn foo() anyerror!?*u8 {
138 return bar() orelse error.Fail;
139 }
140 fn bar() ?*u8 {
141 return null;
142 }
143 };
144 try S.doTheTest();
145 comptime try S.doTheTest();
146}
147
148test "initialize const optional C pointer to null" {
149 const a: ?[*c]i32 = null;
150 try expect(a == null);
151 comptime try expect(a == null);
152}
153
154test "compare equality of optional and non-optional pointer" {
155 const a = @intToPtr(*const usize, 0x12345678);
156 const b = @intToPtr(?*usize, 0x12345678);
157 try expect(a == b);
158 try expect(b == a);
159}
160
161test "allowzero pointer and slice" {
162 var ptr = @intToPtr([*]allowzero i32, 0);
163 var opt_ptr: ?[*]allowzero i32 = ptr;
164 try expect(opt_ptr != null);
165 try expect(@ptrToInt(ptr) == 0);
166 var runtime_zero: usize = 0;
167 var slice = ptr[runtime_zero..10];
168 comptime try expect(@TypeOf(slice) == []allowzero i32);
169 try expect(@ptrToInt(&slice[5]) == 20);
170
171 comptime try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero);
172 comptime try expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero);
173}
174
175test "assign null directly to C pointer and test null equality" {
176 var x: [*c]i32 = null;
177 try expect(x == null);
178 try expect(null == x);
179 try expect(!(x != null));
180 try expect(!(null != x));
181 if (x) |same_x| {
182 _ = same_x;
183 @panic("fail");
184 }
185 var otherx: i32 = undefined;
186 try expect((x orelse &otherx) == &otherx);
187
188 const y: [*c]i32 = null;
189 comptime try expect(y == null);
190 comptime try expect(null == y);
191 comptime try expect(!(y != null));
192 comptime try expect(!(null != y));
193 if (y) |same_y| {
194 _ = same_y;
195 @panic("fail");
196 }
197 const othery: i32 = undefined;
198 comptime try expect((y orelse &othery) == &othery);
199
200 var n: i32 = 1234;
201 var x1: [*c]i32 = &n;
202 try expect(!(x1 == null));
203 try expect(!(null == x1));
204 try expect(x1 != null);
205 try expect(null != x1);
206 try expect(x1.?.* == 1234);
207 if (x1) |same_x1| {
208 try expect(same_x1.* == 1234);
209 } else {
210 @panic("fail");
211 }
212 try expect((x1 orelse &otherx) == x1);
213
214 const nc: i32 = 1234;
215 const y1: [*c]const i32 = &nc;
216 comptime try expect(!(y1 == null));
217 comptime try expect(!(null == y1));
218 comptime try expect(y1 != null);
219 comptime try expect(null != y1);
220 comptime try expect(y1.?.* == 1234);
221 if (y1) |same_y1| {
222 try expect(same_y1.* == 1234);
223 } else {
224 @compileError("fail");
225 }
226 comptime try expect((y1 orelse &othery) == y1);
227}
228
229test "null terminated pointer" {
230 const S = struct {
231 fn doTheTest() !void {
232 var array_with_zero = [_:0]u8{ 'h', 'e', 'l', 'l', 'o' };
233 var zero_ptr: [*:0]const u8 = @ptrCast([*:0]const u8, &array_with_zero);
234 var no_zero_ptr: [*]const u8 = zero_ptr;
235 var zero_ptr_again = @ptrCast([*:0]const u8, no_zero_ptr);
236 try expect(std.mem.eql(u8, std.mem.spanZ(zero_ptr_again), "hello"));
237 }
238 };
239 try S.doTheTest();
240 comptime try S.doTheTest();
241}
242
243test "allow any sentinel" {
244 const S = struct {
245 fn doTheTest() !void {
246 var array = [_:std.math.minInt(i32)]i32{ 1, 2, 3, 4 };
247 var ptr: [*:std.math.minInt(i32)]i32 = &array;
248 try expect(ptr[4] == std.math.minInt(i32));
249 }
250 };
251 try S.doTheTest();
252 comptime try S.doTheTest();
253}
254
255test "pointer sentinel with enums" {
256 const S = struct {
257 const Number = enum {
258 one,
259 two,
260 sentinel,
261 };
262
263 fn doTheTest() !void {
264 var ptr: [*:.sentinel]const Number = &[_:.sentinel]Number{ .one, .two, .two, .one };
265 try expect(ptr[4] == .sentinel); // TODO this should be comptime try expect, see #3731
266 }
267 };
268 try S.doTheTest();
269 comptime try S.doTheTest();
270}
271
272test "pointer sentinel with optional element" {
273 const S = struct {
274 fn doTheTest() !void {
275 var ptr: [*:null]const ?i32 = &[_:null]?i32{ 1, 2, 3, 4 };
276 try expect(ptr[4] == null); // TODO this should be comptime try expect, see #3731
277 }
278 };
279 try S.doTheTest();
280 comptime try S.doTheTest();
281}
282
283test "pointer sentinel with +inf" {
284 const S = struct {
285 fn doTheTest() !void {
286 const inf = std.math.inf_f32;
287 var ptr: [*:inf]const f32 = &[_:inf]f32{ 1.1, 2.2, 3.3, 4.4 };
288 try expect(ptr[4] == inf); // TODO this should be comptime try expect, see #3731
289 }
290 };
291 try S.doTheTest();
292 comptime try S.doTheTest();
293}
294
295test "pointer to array at fixed address" {
296 const array = @intToPtr(*volatile [1]u32, 0x10);
297 // Silly check just to reference `array`
298 try expect(@ptrToInt(&array[0]) == 0x10);
299}
300
301test "pointer arithmetic affects the alignment" {
302 {
303 var ptr: [*]align(8) u32 = undefined;
304 var x: usize = 1;
305
306 try expect(@typeInfo(@TypeOf(ptr)).Pointer.alignment == 8);
307 const ptr1 = ptr + 1; // 1 * 4 = 4 -> lcd(4,8) = 4
308 try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 4);
309 const ptr2 = ptr + 4; // 4 * 4 = 16 -> lcd(16,8) = 8
310 try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 8);
311 const ptr3 = ptr + 0; // no-op
312 try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8);
313 const ptr4 = ptr + x; // runtime-known addend
314 try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4);
315 }
316 {
317 var ptr: [*]align(8) [3]u8 = undefined;
318 var x: usize = 1;
319
320 const ptr1 = ptr + 17; // 3 * 17 = 51
321 try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 1);
322 const ptr2 = ptr + x; // runtime-known addend
323 try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 1);
324 const ptr3 = ptr + 8; // 3 * 8 = 24 -> lcd(8,24) = 8
325 try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8);
326 const ptr4 = ptr + 4; // 3 * 4 = 12 -> lcd(8,12) = 4
327 try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4);
328 }
329}
330
331test "@ptrToInt on null optional at comptime" {
332 {
333 const pointer = @intToPtr(?*u8, 0x000);
334 const x = @ptrToInt(pointer);
335 _ = x;
336 comptime try expect(0 == @ptrToInt(pointer));
337 }
338 {
339 const pointer = @intToPtr(?*u8, 0xf00);
340 comptime try expect(0xf00 == @ptrToInt(pointer));
341 }
342}
343
344test "indexing array with sentinel returns correct type" {
345 var s: [:0]const u8 = "abc";
346 try testing.expectEqualSlices(u8, "*const u8", @typeName(@TypeOf(&s[0])));
347}
test/behavior/pointers_stage1.zig created+305
...@@ -0,0 +1,305 @@
1const std = @import("std");
2const testing = std.testing;
3const expect = testing.expect;
4const expectError = testing.expectError;
5
6const Foo1 = struct {
7 x: void,
8};
9
10test "dereference pointer again" {
11 try testDerefPtrOneVal();
12 comptime try testDerefPtrOneVal();
13}
14
15fn testDerefPtrOneVal() !void {
16 // Foo1 satisfies the OnePossibleValueYes criteria
17 const x = &Foo1{ .x = {} };
18 const y = x.*;
19 try expect(@TypeOf(y.x) == void);
20}
21
22test "assigning integer to C pointer" {
23 var x: i32 = 0;
24 var ptr: [*c]u8 = 0;
25 var ptr2: [*c]u8 = x;
26 if (false) {
27 ptr;
28 ptr2;
29 }
30}
31
32test "implicit cast single item pointer to C pointer and back" {
33 var y: u8 = 11;
34 var x: [*c]u8 = &y;
35 var z: *u8 = x;
36 z.* += 1;
37 try expect(y == 12);
38}
39
40test "C pointer comparison and arithmetic" {
41 const S = struct {
42 fn doTheTest() !void {
43 var ptr1: [*c]u32 = 0;
44 var ptr2 = ptr1 + 10;
45 try expect(ptr1 == 0);
46 try expect(ptr1 >= 0);
47 try expect(ptr1 <= 0);
48 // expect(ptr1 < 1);
49 // expect(ptr1 < one);
50 // expect(1 > ptr1);
51 // expect(one > ptr1);
52 try expect(ptr1 < ptr2);
53 try expect(ptr2 > ptr1);
54 try expect(ptr2 >= 40);
55 try expect(ptr2 == 40);
56 try expect(ptr2 <= 40);
57 ptr2 -= 10;
58 try expect(ptr1 == ptr2);
59 }
60 };
61 try S.doTheTest();
62 comptime try S.doTheTest();
63}
64
65test "peer type resolution with C pointers" {
66 var ptr_one: *u8 = undefined;
67 var ptr_many: [*]u8 = undefined;
68 var ptr_c: [*c]u8 = undefined;
69 var t = true;
70 var x1 = if (t) ptr_one else ptr_c;
71 var x2 = if (t) ptr_many else ptr_c;
72 var x3 = if (t) ptr_c else ptr_one;
73 var x4 = if (t) ptr_c else ptr_many;
74 try expect(@TypeOf(x1) == [*c]u8);
75 try expect(@TypeOf(x2) == [*c]u8);
76 try expect(@TypeOf(x3) == [*c]u8);
77 try expect(@TypeOf(x4) == [*c]u8);
78}
79
80test "implicit casting between C pointer and optional non-C pointer" {
81 var slice: []const u8 = "aoeu";
82 const opt_many_ptr: ?[*]const u8 = slice.ptr;
83 var ptr_opt_many_ptr = &opt_many_ptr;
84 var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr;
85 try expect(c_ptr.*.* == 'a');
86 ptr_opt_many_ptr = c_ptr;
87 try expect(ptr_opt_many_ptr.*.?[1] == 'o');
88}
89
90test "implicit cast error unions with non-optional to optional pointer" {
91 const S = struct {
92 fn doTheTest() !void {
93 try expectError(error.Fail, foo());
94 }
95 fn foo() anyerror!?*u8 {
96 return bar() orelse error.Fail;
97 }
98 fn bar() ?*u8 {
99 return null;
100 }
101 };
102 try S.doTheTest();
103 comptime try S.doTheTest();
104}
105
106test "initialize const optional C pointer to null" {
107 const a: ?[*c]i32 = null;
108 try expect(a == null);
109 comptime try expect(a == null);
110}
111
112test "compare equality of optional and non-optional pointer" {
113 const a = @intToPtr(*const usize, 0x12345678);
114 const b = @intToPtr(?*usize, 0x12345678);
115 try expect(a == b);
116 try expect(b == a);
117}
118
119test "allowzero pointer and slice" {
120 var ptr = @intToPtr([*]allowzero i32, 0);
121 var opt_ptr: ?[*]allowzero i32 = ptr;
122 try expect(opt_ptr != null);
123 try expect(@ptrToInt(ptr) == 0);
124 var runtime_zero: usize = 0;
125 var slice = ptr[runtime_zero..10];
126 comptime try expect(@TypeOf(slice) == []allowzero i32);
127 try expect(@ptrToInt(&slice[5]) == 20);
128
129 comptime try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero);
130 comptime try expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero);
131}
132
133test "assign null directly to C pointer and test null equality" {
134 var x: [*c]i32 = null;
135 try expect(x == null);
136 try expect(null == x);
137 try expect(!(x != null));
138 try expect(!(null != x));
139 if (x) |same_x| {
140 _ = same_x;
141 @panic("fail");
142 }
143 var otherx: i32 = undefined;
144 try expect((x orelse &otherx) == &otherx);
145
146 const y: [*c]i32 = null;
147 comptime try expect(y == null);
148 comptime try expect(null == y);
149 comptime try expect(!(y != null));
150 comptime try expect(!(null != y));
151 if (y) |same_y| {
152 _ = same_y;
153 @panic("fail");
154 }
155 const othery: i32 = undefined;
156 comptime try expect((y orelse &othery) == &othery);
157
158 var n: i32 = 1234;
159 var x1: [*c]i32 = &n;
160 try expect(!(x1 == null));
161 try expect(!(null == x1));
162 try expect(x1 != null);
163 try expect(null != x1);
164 try expect(x1.?.* == 1234);
165 if (x1) |same_x1| {
166 try expect(same_x1.* == 1234);
167 } else {
168 @panic("fail");
169 }
170 try expect((x1 orelse &otherx) == x1);
171
172 const nc: i32 = 1234;
173 const y1: [*c]const i32 = &nc;
174 comptime try expect(!(y1 == null));
175 comptime try expect(!(null == y1));
176 comptime try expect(y1 != null);
177 comptime try expect(null != y1);
178 comptime try expect(y1.?.* == 1234);
179 if (y1) |same_y1| {
180 try expect(same_y1.* == 1234);
181 } else {
182 @compileError("fail");
183 }
184 comptime try expect((y1 orelse &othery) == y1);
185}
186
187test "null terminated pointer" {
188 const S = struct {
189 fn doTheTest() !void {
190 var array_with_zero = [_:0]u8{ 'h', 'e', 'l', 'l', 'o' };
191 var zero_ptr: [*:0]const u8 = @ptrCast([*:0]const u8, &array_with_zero);
192 var no_zero_ptr: [*]const u8 = zero_ptr;
193 var zero_ptr_again = @ptrCast([*:0]const u8, no_zero_ptr);
194 try expect(std.mem.eql(u8, std.mem.spanZ(zero_ptr_again), "hello"));
195 }
196 };
197 try S.doTheTest();
198 comptime try S.doTheTest();
199}
200
201test "allow any sentinel" {
202 const S = struct {
203 fn doTheTest() !void {
204 var array = [_:std.math.minInt(i32)]i32{ 1, 2, 3, 4 };
205 var ptr: [*:std.math.minInt(i32)]i32 = &array;
206 try expect(ptr[4] == std.math.minInt(i32));
207 }
208 };
209 try S.doTheTest();
210 comptime try S.doTheTest();
211}
212
213test "pointer sentinel with enums" {
214 const S = struct {
215 const Number = enum {
216 one,
217 two,
218 sentinel,
219 };
220
221 fn doTheTest() !void {
222 var ptr: [*:.sentinel]const Number = &[_:.sentinel]Number{ .one, .two, .two, .one };
223 try expect(ptr[4] == .sentinel); // TODO this should be comptime try expect, see #3731
224 }
225 };
226 try S.doTheTest();
227 comptime try S.doTheTest();
228}
229
230test "pointer sentinel with optional element" {
231 const S = struct {
232 fn doTheTest() !void {
233 var ptr: [*:null]const ?i32 = &[_:null]?i32{ 1, 2, 3, 4 };
234 try expect(ptr[4] == null); // TODO this should be comptime try expect, see #3731
235 }
236 };
237 try S.doTheTest();
238 comptime try S.doTheTest();
239}
240
241test "pointer sentinel with +inf" {
242 const S = struct {
243 fn doTheTest() !void {
244 const inf = std.math.inf_f32;
245 var ptr: [*:inf]const f32 = &[_:inf]f32{ 1.1, 2.2, 3.3, 4.4 };
246 try expect(ptr[4] == inf); // TODO this should be comptime try expect, see #3731
247 }
248 };
249 try S.doTheTest();
250 comptime try S.doTheTest();
251}
252
253test "pointer to array at fixed address" {
254 const array = @intToPtr(*volatile [1]u32, 0x10);
255 // Silly check just to reference `array`
256 try expect(@ptrToInt(&array[0]) == 0x10);
257}
258
259test "pointer arithmetic affects the alignment" {
260 {
261 var ptr: [*]align(8) u32 = undefined;
262 var x: usize = 1;
263
264 try expect(@typeInfo(@TypeOf(ptr)).Pointer.alignment == 8);
265 const ptr1 = ptr + 1; // 1 * 4 = 4 -> lcd(4,8) = 4
266 try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 4);
267 const ptr2 = ptr + 4; // 4 * 4 = 16 -> lcd(16,8) = 8
268 try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 8);
269 const ptr3 = ptr + 0; // no-op
270 try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8);
271 const ptr4 = ptr + x; // runtime-known addend
272 try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4);
273 }
274 {
275 var ptr: [*]align(8) [3]u8 = undefined;
276 var x: usize = 1;
277
278 const ptr1 = ptr + 17; // 3 * 17 = 51
279 try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 1);
280 const ptr2 = ptr + x; // runtime-known addend
281 try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 1);
282 const ptr3 = ptr + 8; // 3 * 8 = 24 -> lcd(8,24) = 8
283 try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8);
284 const ptr4 = ptr + 4; // 3 * 4 = 12 -> lcd(8,12) = 4
285 try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4);
286 }
287}
288
289test "@ptrToInt on null optional at comptime" {
290 {
291 const pointer = @intToPtr(?*u8, 0x000);
292 const x = @ptrToInt(pointer);
293 _ = x;
294 comptime try expect(0 == @ptrToInt(pointer));
295 }
296 {
297 const pointer = @intToPtr(?*u8, 0xf00);
298 comptime try expect(0xf00 == @ptrToInt(pointer));
299 }
300}
301
302test "indexing array with sentinel returns correct type" {
303 var s: [:0]const u8 = "abc";
304 try testing.expectEqualSlices(u8, "*const u8", @typeName(@TypeOf(&s[0])));
305}