| author | |
| committer | |
| log | f81b2531cb4904064446f84a06f6e09e4120e28a |
| tree | e4bac7a1a13c56b728309086d983091e9e009a65 |
| parent | ade85471e2cdab466ba685a38c2c7949c9dd1632 |
* 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, | ||
| 267 | 288 | ||
| 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), |
| 426 | 449 | ||
| 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 | }, |
| 497 | 520 | ||
| 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 | }; | ||
| 5474 | 5509 | ||
| 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 location | 7995 | 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 | } |
| 7995 | 8075 | ||
| 7996 | fn elemPtrArray( | 8076 | fn elemPtrArray( |
| ... | @@ -8107,17 +8187,15 @@ fn coerce( | ... | @@ -8107,17 +8187,15 @@ fn coerce( |
| 8107 | .Many => { | 8187 | .Many => { |
| 8108 | // *[N]T to [*]T | 8188 | // *[N]T to [*]T |
| 8109 | // *[N:s]T to [*:s]T | 8189 | // *[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 { |
| 802 | 802 | ||
| 803 | switch (air_tags[inst]) { | 803 | switch (air_tags[inst]) { |
| 804 | // zig fmt: off | 804 | // 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), |
| 812 | 812 | ||
| 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 { |
| 859 | 859 | ||
| 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), | ||
| 862 | 864 | ||
| 863 | .constant => unreachable, // excluded from function bodies | 865 | .constant => unreachable, // excluded from function bodies |
| 864 | .const_ty => unreachable, // excluded from function bodies | 866 | .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 | } |
| 1370 | 1372 | ||
| 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 | } |
| 1378 | 1381 | ||
| 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 | } |
| 1386 | 1390 | ||
| 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 |
| 850 | 850 | ||
| 851 | // TODO use a different strategy for add that communicates to the optimizer | 851 | // 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 optimizer | 855 | // 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 optimizer | 859 | // 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 optimizer | 863 | // 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, " / "), |
| 866 | 866 | ||
| 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"), |
| 917 | 917 | ||
| 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]["), |
| 920 | 922 | ||
| ... | @@ -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 | } |
| 955 | 957 | ||
| 958 | fn 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 | |||
| 956 | fn airSliceElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue { | 967 | fn 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; |
| 959 | 971 | ||
| 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), | ||
| 969 | 971 | ||
| 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 { |
| 1015 | 1017 | ||
| 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), | ||
| 1018 | 1022 | ||
| 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 | } |
| 1230 | 1234 | ||
| 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; |
| 1234 | 1239 | ||
| 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 | } |
| 1243 | 1248 | ||
| 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; |
| 1247 | 1253 | ||
| 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 | } |
| 1266 | 1272 | ||
| 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 | } |
| 1626 | 1659 | ||
| 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; |
| 326 | 326 | ||
| 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; |
| 329 | 332 |
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), |
| 127 | 131 | ||
| 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 | } |
| 2755 | 2755 | ||
| 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 indexable | 2758 | .Array, .Vector => true, |
| 2759 | return zig_tag == .Array or zig_tag == .Vector or self.isSlice() or | 2759 | .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 | } |
| 2762 | 2766 | ||
| 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"); | ||
| 9 | 10 | ||
| 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 | } |
| 17 | 17 | ||
| 18 | const Foo1 = struct { | ||
| 19 | x: void, | ||
| 20 | }; | ||
| 21 | |||
| 22 | test "dereference pointer again" { | ||
| 23 | try testDerefPtrOneVal(); | ||
| 24 | comptime try testDerefPtrOneVal(); | ||
| 25 | } | ||
| 26 | |||
| 27 | fn 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 | |||
| 34 | test "pointer arithmetic" { | 18 | test "pointer arithmetic" { |
| 35 | var ptr: [*]const u8 = "abcd"; | 19 | var ptr: [*]const u8 = "abcd"; |
| 36 | 20 | ||
| ... | @@ -60,288 +44,3 @@ test "double pointer parsing" { | ... | @@ -60,288 +44,3 @@ test "double pointer parsing" { |
| 60 | fn PtrOf(comptime T: type) type { | 44 | fn PtrOf(comptime T: type) type { |
| 61 | return *T; | 45 | return *T; |
| 62 | } | 46 | } |
| 63 | |||
| 64 | test "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 | |||
| 74 | test "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 | |||
| 82 | test "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 | |||
| 107 | test "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 | |||
| 122 | test "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 | |||
| 132 | test "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 | |||
| 148 | test "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 | |||
| 154 | test "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 | |||
| 161 | test "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 | |||
| 175 | test "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 | |||
| 229 | test "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 | |||
| 243 | test "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 | |||
| 255 | test "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 | |||
| 272 | test "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 | |||
| 283 | test "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 | |||
| 295 | test "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 | |||
| 301 | test "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 | |||
| 331 | test "@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 | |||
| 344 | test "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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const testing = std.testing; | ||
| 3 | const expect = testing.expect; | ||
| 4 | const expectError = testing.expectError; | ||
| 5 | |||
| 6 | const Foo1 = struct { | ||
| 7 | x: void, | ||
| 8 | }; | ||
| 9 | |||
| 10 | test "dereference pointer again" { | ||
| 11 | try testDerefPtrOneVal(); | ||
| 12 | comptime try testDerefPtrOneVal(); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn 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 | |||
| 22 | test "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 | |||
| 32 | test "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 | |||
| 40 | test "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 | |||
| 65 | test "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 | |||
| 80 | test "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 | |||
| 90 | test "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 | |||
| 106 | test "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 | |||
| 112 | test "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 | |||
| 119 | test "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 | |||
| 133 | test "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 | |||
| 187 | test "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 | |||
| 201 | test "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 | |||
| 213 | test "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 | |||
| 230 | test "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 | |||
| 241 | test "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 | |||
| 253 | test "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 | |||
| 259 | test "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 | |||
| 289 | test "@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 | |||
| 302 | test "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 | } | ||