| 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 | 69 | /// is the same as both operands. |
| 70 | 70 | /// Uses the `bin_op` field. |
| 71 | 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 | 84 | /// Allocates stack local memory. |
| 73 | 85 | /// Uses the `ty` field. |
| 74 | 86 | alloc, |
| ... | ... | @@ -264,6 +276,15 @@ pub const Inst = struct { |
| 264 | 276 | /// Result type is the element type of the slice operand (2 element type operations). |
| 265 | 277 | /// Uses the `bin_op` field. |
| 266 | 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 | 289 | pub fn fromCmpOp(op: std.math.CompareOperator) Tag { |
| 269 | 290 | return switch (op) { |
| ... | ... | @@ -422,6 +443,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 422 | 443 | .bit_and, |
| 423 | 444 | .bit_or, |
| 424 | 445 | .xor, |
| 446 | .ptr_add, | |
| 447 | .ptr_sub, | |
| 425 | 448 | => return air.typeOf(datas[inst].bin_op.lhs), |
| 426 | 449 | |
| 427 | 450 | .cmp_lt, |
| ... | ... | @@ -495,14 +518,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 495 | 518 | return callee_ty.fnReturnType(); |
| 496 | 519 | }, |
| 497 | 520 | |
| 498 | .slice_elem_val => { | |
| 521 | .slice_elem_val, .ptr_elem_val => { | |
| 499 | 522 | const slice_ty = air.typeOf(datas[inst].bin_op.lhs); |
| 500 | 523 | return slice_ty.elemType(); |
| 501 | 524 | }, |
| 502 | .ptr_slice_elem_val => { | |
| 503 | const ptr_slice_ty = air.typeOf(datas[inst].bin_op.lhs); | |
| 504 | const slice_ty = ptr_slice_ty.elemType(); | |
| 505 | return slice_ty.elemType(); | |
| 525 | .ptr_slice_elem_val, .ptr_ptr_elem_val => { | |
| 526 | const outer_ptr_ty = air.typeOf(datas[inst].bin_op.lhs); | |
| 527 | const inner_ptr_ty = outer_ptr_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 | 231 | .mul, |
| 232 | 232 | .mulwrap, |
| 233 | 233 | .div, |
| 234 | .ptr_add, | |
| 235 | .ptr_sub, | |
| 234 | 236 | .bit_and, |
| 235 | 237 | .bit_or, |
| 236 | 238 | .xor, |
| ... | ... | @@ -245,6 +247,8 @@ fn analyzeInst( |
| 245 | 247 | .store, |
| 246 | 248 | .slice_elem_val, |
| 247 | 249 | .ptr_slice_elem_val, |
| 250 | .ptr_elem_val, | |
| 251 | .ptr_ptr_elem_val, | |
| 248 | 252 | => { |
| 249 | 253 | const o = inst_datas[inst].bin_op; |
| 250 | 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 | 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 | 5510 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 5476 | 5511 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions); |
| ... | ... | @@ -7959,38 +7994,83 @@ fn elemVal( |
| 7959 | 7994 | ) CompileError!Air.Inst.Ref { |
| 7960 | 7995 | const array_ptr_src = src; // TODO better source location |
| 7961 | 7996 | const maybe_ptr_ty = sema.typeOf(array_maybe_ptr); |
| 7962 | if (maybe_ptr_ty.isSinglePointer()) { | |
| 7963 | const indexable_ty = maybe_ptr_ty.elemType(); | |
| 7964 | if (indexable_ty.isSlice()) { | |
| 7965 | // We have a pointer to a slice and we want an element value. | |
| 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| { | |
| 7997 | switch (maybe_ptr_ty.zigTypeTag()) { | |
| 7998 | .Pointer => switch (maybe_ptr_ty.ptrSize()) { | |
| 7999 | .Slice => { | |
| 8000 | if (try sema.resolveDefinedValue(block, src, array_maybe_ptr)) |slice_val| { | |
| 7969 | 8001 | _ = slice_val; |
| 7970 | 8002 | return sema.mod.fail(&block.base, src, "TODO implement Sema for elemVal for comptime known slice", .{}); |
| 7971 | 8003 | } |
| 7972 | 8004 | try sema.requireRuntimeBlock(block, src); |
| 7973 | return block.addBinOp(.slice_elem_val, slice, elem_index); | |
| 7974 | } | |
| 7975 | try sema.requireRuntimeBlock(block, src); | |
| 7976 | return block.addBinOp(.ptr_slice_elem_val, array_maybe_ptr, elem_index); | |
| 7977 | } | |
| 7978 | } | |
| 7979 | if (maybe_ptr_ty.isSlice()) { | |
| 7980 | if (try sema.resolveDefinedValue(block, src, array_maybe_ptr)) |slice_val| { | |
| 7981 | _ = slice_val; | |
| 7982 | return sema.mod.fail(&block.base, src, "TODO implement Sema for elemVal for comptime known slice", .{}); | |
| 7983 | } | |
| 7984 | try sema.requireRuntimeBlock(block, src); | |
| 7985 | return block.addBinOp(.slice_elem_val, array_maybe_ptr, elem_index); | |
| 8005 | return block.addBinOp(.slice_elem_val, array_maybe_ptr, elem_index); | |
| 8006 | }, | |
| 8007 | .Many, .C => { | |
| 8008 | if (try sema.resolveDefinedValue(block, src, array_maybe_ptr)) |ptr_val| { | |
| 8009 | _ = ptr_val; | |
| 8010 | return sema.mod.fail(&block.base, src, "TODO implement Sema for elemVal for comptime known pointer", .{}); | |
| 8011 | } | |
| 8012 | try sema.requireRuntimeBlock(block, src); | |
| 8013 | return block.addBinOp(.ptr_elem_val, array_maybe_ptr, elem_index); | |
| 8014 | }, | |
| 8015 | .One => { | |
| 8016 | const indexable_ty = maybe_ptr_ty.elemType(); | |
| 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 | 8076 | fn elemPtrArray( |
| ... | ... | @@ -8107,17 +8187,15 @@ fn coerce( |
| 8107 | 8187 | .Many => { |
| 8108 | 8188 | // *[N]T to [*]T |
| 8109 | 8189 | // *[N:s]T to [*:s]T |
| 8110 | const src_sentinel = array_type.sentinel(); | |
| 8111 | const dst_sentinel = dest_type.sentinel(); | |
| 8112 | if (src_sentinel == null and dst_sentinel == null) | |
| 8113 | return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src); | |
| 8114 | ||
| 8115 | if (src_sentinel) |src_s| { | |
| 8116 | if (dst_sentinel) |dst_s| { | |
| 8117 | if (src_s.eql(dst_s, dst_elem_type)) { | |
| 8190 | // *[N:s]T to [*]T | |
| 8191 | if (dest_type.sentinel()) |dst_sentinel| { | |
| 8192 | if (array_type.sentinel()) |src_sentinel| { | |
| 8193 | if (src_sentinel.eql(dst_sentinel, dst_elem_type)) { | |
| 8118 | 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 | 8201 | .One => {}, |
src/codegen.zig+31-9| ... | ... | @@ -802,13 +802,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 802 | 802 | |
| 803 | 803 | switch (air_tags[inst]) { |
| 804 | 804 | // zig fmt: off |
| 805 | .add => try self.airAdd(inst), | |
| 806 | .addwrap => try self.airAddWrap(inst), | |
| 807 | .sub => try self.airSub(inst), | |
| 808 | .subwrap => try self.airSubWrap(inst), | |
| 809 | .mul => try self.airMul(inst), | |
| 810 | .mulwrap => try self.airMulWrap(inst), | |
| 811 | .div => try self.airDiv(inst), | |
| 805 | .add, .ptr_add => try self.airAdd(inst), | |
| 806 | .addwrap => try self.airAddWrap(inst), | |
| 807 | .sub, .ptr_sub => try self.airSub(inst), | |
| 808 | .subwrap => try self.airSubWrap(inst), | |
| 809 | .mul => try self.airMul(inst), | |
| 810 | .mulwrap => try self.airMulWrap(inst), | |
| 811 | .div => try self.airDiv(inst), | |
| 812 | 812 | |
| 813 | 813 | .cmp_lt => try self.airCmp(inst, .lt), |
| 814 | 814 | .cmp_lte => try self.airCmp(inst, .lte), |
| ... | ... | @@ -859,6 +859,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 859 | 859 | |
| 860 | 860 | .slice_elem_val => try self.airSliceElemVal(inst), |
| 861 | 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 | 865 | .constant => unreachable, // excluded from function bodies |
| 864 | 866 | .const_ty => unreachable, // excluded from function bodies |
| ... | ... | @@ -1369,21 +1371,41 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1369 | 1371 | } |
| 1370 | 1372 | |
| 1371 | 1373 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1374 | const is_volatile = false; // TODO | |
| 1372 | 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 | 1377 | else => return self.fail("TODO implement slice_elem_val for {}", .{self.target.cpu.arch}), |
| 1375 | 1378 | }; |
| 1376 | 1379 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1377 | 1380 | } |
| 1378 | 1381 | |
| 1379 | 1382 | fn airPtrSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1383 | const is_volatile = false; // TODO | |
| 1380 | 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 | 1386 | else => return self.fail("TODO implement ptr_slice_elem_val for {}", .{self.target.cpu.arch}), |
| 1383 | 1387 | }; |
| 1384 | 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 | 1409 | fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { |
| 1388 | 1410 | if (!self.liveness.operandDies(inst, op_index)) |
| 1389 | 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 | 850 | |
| 851 | 851 | // TODO use a different strategy for add that communicates to the optimizer |
| 852 | 852 | // that wrapping is UB. |
| 853 | .add => try airBinOp( o, inst, " + "), | |
| 854 | .addwrap => try airWrapOp(o, inst, " + ", "addw_"), | |
| 853 | .add, .ptr_add => try airBinOp( o, inst, " + "), | |
| 854 | .addwrap => try airWrapOp(o, inst, " + ", "addw_"), | |
| 855 | 855 | // TODO use a different strategy for sub that communicates to the optimizer |
| 856 | 856 | // that wrapping is UB. |
| 857 | .sub => try airBinOp( o, inst, " - "), | |
| 858 | .subwrap => try airWrapOp(o, inst, " - ", "subw_"), | |
| 857 | .sub, .ptr_sub => try airBinOp( o, inst, " - "), | |
| 858 | .subwrap => try airWrapOp(o, inst, " - ", "subw_"), | |
| 859 | 859 | // TODO use a different strategy for mul that communicates to the optimizer |
| 860 | 860 | // that wrapping is UB. |
| 861 | .mul => try airBinOp( o, inst, " * "), | |
| 862 | .mulwrap => try airWrapOp(o, inst, " * ", "mulw_"), | |
| 861 | .mul => try airBinOp( o, inst, " * "), | |
| 862 | .mulwrap => try airWrapOp(o, inst, " * ", "mulw_"), | |
| 863 | 863 | // TODO use a different strategy for div that communicates to the optimizer |
| 864 | 864 | // that wrapping is UB. |
| 865 | .div => try airBinOp( o, inst, " / "), | |
| 865 | .div => try airBinOp( o, inst, " / "), | |
| 866 | 866 | |
| 867 | 867 | .cmp_eq => try airBinOp(o, inst, " == "), |
| 868 | 868 | .cmp_gt => try airBinOp(o, inst, " > "), |
| ... | ... | @@ -915,6 +915,8 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM |
| 915 | 915 | .slice_ptr => try airSliceField(o, inst, ".ptr;\n"), |
| 916 | 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 | 920 | .slice_elem_val => try airSliceElemVal(o, inst, "["), |
| 919 | 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 | 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 | 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 | 970 | return CValue.none; |
| 959 | 971 | |
| 960 | 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 | 966 | .mul => try self.airMul(inst, false), |
| 967 | 967 | .mulwrap => try self.airMul(inst, true), |
| 968 | 968 | .div => try self.airDiv(inst), |
| 969 | .ptr_add => try self.airPtrAdd(inst), | |
| 970 | .ptr_sub => try self.airPtrSub(inst), | |
| 969 | 971 | |
| 970 | 972 | .bit_and, .bool_and => try self.airAnd(inst), |
| 971 | 973 | .bit_or, .bool_or => try self.airOr(inst), |
| ... | ... | @@ -1015,6 +1017,8 @@ pub const FuncGen = struct { |
| 1015 | 1017 | |
| 1016 | 1018 | .slice_elem_val => try self.airSliceElemVal(inst), |
| 1017 | 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 | 1023 | .optional_payload => try self.airOptionalPayload(inst, false), |
| 1020 | 1024 | .optional_payload_ptr => try self.airOptionalPayload(inst, true), |
| ... | ... | @@ -1229,7 +1233,8 @@ pub const FuncGen = struct { |
| 1229 | 1233 | } |
| 1230 | 1234 | |
| 1231 | 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 | 1238 | return null; |
| 1234 | 1239 | |
| 1235 | 1240 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| ... | ... | @@ -1242,7 +1247,8 @@ pub const FuncGen = struct { |
| 1242 | 1247 | } |
| 1243 | 1248 | |
| 1244 | 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 | 1252 | return null; |
| 1247 | 1253 | |
| 1248 | 1254 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| ... | ... | @@ -1264,6 +1270,33 @@ pub const FuncGen = struct { |
| 1264 | 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 | 1300 | fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1268 | 1301 | if (self.liveness.isUnused(inst)) |
| 1269 | 1302 | return null; |
| ... | ... | @@ -1624,6 +1657,29 @@ pub const FuncGen = struct { |
| 1624 | 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 | 1683 | fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1628 | 1684 | if (self.liveness.isUnused(inst)) |
| 1629 | 1685 | return null; |
src/codegen/llvm/bindings.zig+3| ... | ... | @@ -324,6 +324,9 @@ pub const Builder = opaque { |
| 324 | 324 | pub const buildLoad = LLVMBuildLoad; |
| 325 | 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 | 330 | pub const buildNot = LLVMBuildNot; |
| 328 | 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 | 109 | .mul, |
| 110 | 110 | .mulwrap, |
| 111 | 111 | .div, |
| 112 | .ptr_add, | |
| 113 | .ptr_sub, | |
| 112 | 114 | .bit_and, |
| 113 | 115 | .bit_or, |
| 114 | 116 | .xor, |
| ... | ... | @@ -123,6 +125,8 @@ const Writer = struct { |
| 123 | 125 | .store, |
| 124 | 126 | .slice_elem_val, |
| 125 | 127 | .ptr_slice_elem_val, |
| 128 | .ptr_elem_val, | |
| 129 | .ptr_ptr_elem_val, | |
| 126 | 130 | => try w.writeBinOp(s, inst), |
| 127 | 131 | |
| 128 | 132 | .is_null, |
src/type.zig+9-5| ... | ... | @@ -2753,11 +2753,15 @@ pub const Type = extern union { |
| 2753 | 2753 | }; |
| 2754 | 2754 | } |
| 2755 | 2755 | |
| 2756 | pub fn isIndexable(self: Type) bool { | |
| 2757 | const zig_tag = self.zigTypeTag(); | |
| 2758 | // TODO tuples are indexable | |
| 2759 | return zig_tag == .Array or zig_tag == .Vector or self.isSlice() or | |
| 2760 | (self.isSinglePointer() and self.elemType().zigTypeTag() == .Array); | |
| 2756 | pub fn isIndexable(ty: Type) bool { | |
| 2757 | return switch (ty.zigTypeTag()) { | |
| 2758 | .Array, .Vector => true, | |
| 2759 | .Pointer => switch (ty.ptrSize()) { | |
| 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 | 2767 | /// Returns null if the type has no namespace. |
test/behavior.zig+2-1| ... | ... | @@ -6,6 +6,7 @@ test { |
| 6 | 6 | _ = @import("behavior/basic.zig"); |
| 7 | 7 | _ = @import("behavior/generics.zig"); |
| 8 | 8 | _ = @import("behavior/eval.zig"); |
| 9 | _ = @import("behavior/pointers.zig"); | |
| 9 | 10 | |
| 10 | 11 | if (!builtin.zig_is_stage2) { |
| 11 | 12 | // Tests that only pass for stage1. |
| ... | ... | @@ -112,7 +113,7 @@ test { |
| 112 | 113 | _ = @import("behavior/namespace_depends_on_compile_var.zig"); |
| 113 | 114 | _ = @import("behavior/null.zig"); |
| 114 | 115 | _ = @import("behavior/optional.zig"); |
| 115 | _ = @import("behavior/pointers.zig"); | |
| 116 | _ = @import("behavior/pointers_stage1.zig"); | |
| 116 | 117 | _ = @import("behavior/popcount.zig"); |
| 117 | 118 | _ = @import("behavior/ptrcast.zig"); |
| 118 | 119 | _ = @import("behavior/pub_enum.zig"); |
test/behavior/pointers.zig-301| ... | ... | @@ -15,22 +15,6 @@ fn testDerefPtr() !void { |
| 15 | 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 | 18 | test "pointer arithmetic" { |
| 35 | 19 | var ptr: [*]const u8 = "abcd"; |
| 36 | 20 | |
| ... | ... | @@ -60,288 +44,3 @@ test "double pointer parsing" { |
| 60 | 44 | fn PtrOf(comptime T: type) type { |
| 61 | 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 | } |