mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 14:25:16 +00:00
stage2: pass some pointer tests
* New AIR instructions: ptr_add, ptr_sub, ptr_elem_val, ptr_ptr_elem_val - See the doc comments for details. * Sema: implement runtime pointer arithmetic. * Sema: implement elem_val for many-pointers. * Sema: support coercion from `*[N:s]T` to `[*]T`. * Type: isIndexable handles many-pointers.
This commit is contained in:
parent
ade85471e2
commit
f81b2531cb
33
src/Air.zig
33
src/Air.zig
@ -69,6 +69,18 @@ pub const Inst = struct {
|
||||
/// is the same as both operands.
|
||||
/// Uses the `bin_op` field.
|
||||
div,
|
||||
/// Add an offset to a pointer, returning a new pointer.
|
||||
/// The offset is in element type units, not bytes.
|
||||
/// Wrapping is undefined behavior.
|
||||
/// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
|
||||
/// Uses the `bin_op` field.
|
||||
ptr_add,
|
||||
/// Subtract an offset from a pointer, returning a new pointer.
|
||||
/// The offset is in element type units, not bytes.
|
||||
/// Wrapping is undefined behavior.
|
||||
/// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
|
||||
/// Uses the `bin_op` field.
|
||||
ptr_sub,
|
||||
/// Allocates stack local memory.
|
||||
/// Uses the `ty` field.
|
||||
alloc,
|
||||
@ -264,6 +276,15 @@ pub const Inst = struct {
|
||||
/// Result type is the element type of the slice operand (2 element type operations).
|
||||
/// Uses the `bin_op` field.
|
||||
ptr_slice_elem_val,
|
||||
/// Given a pointer value, and element index, return the element value at that index.
|
||||
/// Result type is the element type of the pointer operand.
|
||||
/// Uses the `bin_op` field.
|
||||
ptr_elem_val,
|
||||
/// Given a pointer to a pointer, and element index, return the element value of the inner
|
||||
/// pointer at that index.
|
||||
/// Result type is the element type of the inner pointer operand.
|
||||
/// Uses the `bin_op` field.
|
||||
ptr_ptr_elem_val,
|
||||
|
||||
pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
|
||||
return switch (op) {
|
||||
@ -422,6 +443,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||
.bit_and,
|
||||
.bit_or,
|
||||
.xor,
|
||||
.ptr_add,
|
||||
.ptr_sub,
|
||||
=> return air.typeOf(datas[inst].bin_op.lhs),
|
||||
|
||||
.cmp_lt,
|
||||
@ -495,14 +518,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||
return callee_ty.fnReturnType();
|
||||
},
|
||||
|
||||
.slice_elem_val => {
|
||||
.slice_elem_val, .ptr_elem_val => {
|
||||
const slice_ty = air.typeOf(datas[inst].bin_op.lhs);
|
||||
return slice_ty.elemType();
|
||||
},
|
||||
.ptr_slice_elem_val => {
|
||||
const ptr_slice_ty = air.typeOf(datas[inst].bin_op.lhs);
|
||||
const slice_ty = ptr_slice_ty.elemType();
|
||||
return slice_ty.elemType();
|
||||
.ptr_slice_elem_val, .ptr_ptr_elem_val => {
|
||||
const outer_ptr_ty = air.typeOf(datas[inst].bin_op.lhs);
|
||||
const inner_ptr_ty = outer_ptr_ty.elemType();
|
||||
return inner_ptr_ty.elemType();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,6 +231,8 @@ fn analyzeInst(
|
||||
.mul,
|
||||
.mulwrap,
|
||||
.div,
|
||||
.ptr_add,
|
||||
.ptr_sub,
|
||||
.bit_and,
|
||||
.bit_or,
|
||||
.xor,
|
||||
@ -245,6 +247,8 @@ fn analyzeInst(
|
||||
.store,
|
||||
.slice_elem_val,
|
||||
.ptr_slice_elem_val,
|
||||
.ptr_elem_val,
|
||||
.ptr_ptr_elem_val,
|
||||
=> {
|
||||
const o = inst_datas[inst].bin_op;
|
||||
return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none });
|
||||
|
||||
148
src/Sema.zig
148
src/Sema.zig
@ -5471,6 +5471,41 @@ fn analyzeArithmetic(
|
||||
lhs_ty, rhs_ty,
|
||||
});
|
||||
}
|
||||
if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize()) {
|
||||
.One, .Slice => {},
|
||||
.Many, .C => {
|
||||
// Pointer arithmetic.
|
||||
const op_src = src; // TODO better source location
|
||||
const air_tag: Air.Inst.Tag = switch (zir_tag) {
|
||||
.add => .ptr_add,
|
||||
.sub => .ptr_sub,
|
||||
else => return sema.mod.fail(
|
||||
&block.base,
|
||||
op_src,
|
||||
"invalid pointer arithmetic operand: '{s}''",
|
||||
.{@tagName(zir_tag)},
|
||||
),
|
||||
};
|
||||
// TODO if the operand is comptime-known to be negative, or is a negative int,
|
||||
// coerce to isize instead of usize.
|
||||
const casted_rhs = try sema.coerce(block, Type.initTag(.usize), rhs, rhs_src);
|
||||
const runtime_src = runtime_src: {
|
||||
if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
|
||||
if (try sema.resolveDefinedValue(block, rhs_src, casted_rhs)) |rhs_val| {
|
||||
_ = lhs_val;
|
||||
_ = rhs_val;
|
||||
return sema.mod.fail(&block.base, src, "TODO implement Sema for comptime pointer arithmetic", .{});
|
||||
} else {
|
||||
break :runtime_src rhs_src;
|
||||
}
|
||||
} else {
|
||||
break :runtime_src lhs_src;
|
||||
}
|
||||
};
|
||||
try sema.requireRuntimeBlock(block, runtime_src);
|
||||
return block.addBinOp(air_tag, lhs, casted_rhs);
|
||||
},
|
||||
};
|
||||
|
||||
const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
|
||||
const resolved_type = try sema.resolvePeerTypes(block, src, instructions);
|
||||
@ -7959,38 +7994,83 @@ fn elemVal(
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const array_ptr_src = src; // TODO better source location
|
||||
const maybe_ptr_ty = sema.typeOf(array_maybe_ptr);
|
||||
if (maybe_ptr_ty.isSinglePointer()) {
|
||||
const indexable_ty = maybe_ptr_ty.elemType();
|
||||
if (indexable_ty.isSlice()) {
|
||||
// We have a pointer to a slice and we want an element value.
|
||||
if (try sema.isComptimeKnown(block, src, array_maybe_ptr)) {
|
||||
const slice = try sema.analyzeLoad(block, src, array_maybe_ptr, array_ptr_src);
|
||||
if (try sema.resolveDefinedValue(block, src, slice)) |slice_val| {
|
||||
switch (maybe_ptr_ty.zigTypeTag()) {
|
||||
.Pointer => switch (maybe_ptr_ty.ptrSize()) {
|
||||
.Slice => {
|
||||
if (try sema.resolveDefinedValue(block, src, array_maybe_ptr)) |slice_val| {
|
||||
_ = slice_val;
|
||||
return sema.mod.fail(&block.base, src, "TODO implement Sema for elemVal for comptime known slice", .{});
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addBinOp(.slice_elem_val, slice, elem_index);
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addBinOp(.ptr_slice_elem_val, array_maybe_ptr, elem_index);
|
||||
}
|
||||
return block.addBinOp(.slice_elem_val, array_maybe_ptr, elem_index);
|
||||
},
|
||||
.Many, .C => {
|
||||
if (try sema.resolveDefinedValue(block, src, array_maybe_ptr)) |ptr_val| {
|
||||
_ = ptr_val;
|
||||
return sema.mod.fail(&block.base, src, "TODO implement Sema for elemVal for comptime known pointer", .{});
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addBinOp(.ptr_elem_val, array_maybe_ptr, elem_index);
|
||||
},
|
||||
.One => {
|
||||
const indexable_ty = maybe_ptr_ty.elemType();
|
||||
switch (indexable_ty.zigTypeTag()) {
|
||||
.Pointer => switch (indexable_ty.ptrSize()) {
|
||||
.Slice => {
|
||||
// We have a pointer to a slice and we want an element value.
|
||||
if (try sema.isComptimeKnown(block, src, array_maybe_ptr)) {
|
||||
const slice = try sema.analyzeLoad(block, src, array_maybe_ptr, array_ptr_src);
|
||||
if (try sema.resolveDefinedValue(block, src, slice)) |slice_val| {
|
||||
_ = slice_val;
|
||||
return sema.mod.fail(&block.base, src, "TODO implement Sema for elemVal for comptime known slice", .{});
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addBinOp(.slice_elem_val, slice, elem_index);
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addBinOp(.ptr_slice_elem_val, array_maybe_ptr, elem_index);
|
||||
},
|
||||
.Many, .C => {
|
||||
// We have a pointer to a pointer and we want an element value.
|
||||
if (try sema.isComptimeKnown(block, src, array_maybe_ptr)) {
|
||||
const ptr = try sema.analyzeLoad(block, src, array_maybe_ptr, array_ptr_src);
|
||||
if (try sema.resolveDefinedValue(block, src, ptr)) |ptr_val| {
|
||||
_ = ptr_val;
|
||||
return sema.mod.fail(&block.base, src, "TODO implement Sema for elemVal for comptime known pointer", .{});
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addBinOp(.ptr_elem_val, ptr, elem_index);
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addBinOp(.ptr_ptr_elem_val, array_maybe_ptr, elem_index);
|
||||
},
|
||||
.One => return sema.mod.fail(
|
||||
&block.base,
|
||||
array_ptr_src,
|
||||
"expected pointer, found '{}'",
|
||||
.{indexable_ty.elemType()},
|
||||
),
|
||||
},
|
||||
.Array => {
|
||||
const ptr = try sema.elemPtr(block, src, array_maybe_ptr, elem_index, elem_index_src);
|
||||
return sema.analyzeLoad(block, src, ptr, elem_index_src);
|
||||
},
|
||||
else => return sema.mod.fail(
|
||||
&block.base,
|
||||
array_ptr_src,
|
||||
"expected pointer, found '{}'",
|
||||
.{indexable_ty},
|
||||
),
|
||||
}
|
||||
},
|
||||
},
|
||||
else => return sema.mod.fail(
|
||||
&block.base,
|
||||
array_ptr_src,
|
||||
"expected pointer, found '{}'",
|
||||
.{maybe_ptr_ty},
|
||||
),
|
||||
}
|
||||
if (maybe_ptr_ty.isSlice()) {
|
||||
if (try sema.resolveDefinedValue(block, src, array_maybe_ptr)) |slice_val| {
|
||||
_ = slice_val;
|
||||
return sema.mod.fail(&block.base, src, "TODO implement Sema for elemVal for comptime known slice", .{});
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addBinOp(.slice_elem_val, array_maybe_ptr, elem_index);
|
||||
}
|
||||
|
||||
const array_ptr = if (maybe_ptr_ty.zigTypeTag() == .Pointer)
|
||||
array_maybe_ptr
|
||||
else
|
||||
try sema.analyzeRef(block, src, array_maybe_ptr);
|
||||
const ptr = try sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);
|
||||
return sema.analyzeLoad(block, src, ptr, elem_index_src);
|
||||
}
|
||||
|
||||
fn elemPtrArray(
|
||||
@ -8107,17 +8187,15 @@ fn coerce(
|
||||
.Many => {
|
||||
// *[N]T to [*]T
|
||||
// *[N:s]T to [*:s]T
|
||||
const src_sentinel = array_type.sentinel();
|
||||
const dst_sentinel = dest_type.sentinel();
|
||||
if (src_sentinel == null and dst_sentinel == null)
|
||||
return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src);
|
||||
|
||||
if (src_sentinel) |src_s| {
|
||||
if (dst_sentinel) |dst_s| {
|
||||
if (src_s.eql(dst_s, dst_elem_type)) {
|
||||
// *[N:s]T to [*]T
|
||||
if (dest_type.sentinel()) |dst_sentinel| {
|
||||
if (array_type.sentinel()) |src_sentinel| {
|
||||
if (src_sentinel.eql(dst_sentinel, dst_elem_type)) {
|
||||
return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src);
|
||||
}
|
||||
},
|
||||
.One => {},
|
||||
|
||||
@ -802,13 +802,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
|
||||
switch (air_tags[inst]) {
|
||||
// zig fmt: off
|
||||
.add => try self.airAdd(inst),
|
||||
.addwrap => try self.airAddWrap(inst),
|
||||
.sub => try self.airSub(inst),
|
||||
.subwrap => try self.airSubWrap(inst),
|
||||
.mul => try self.airMul(inst),
|
||||
.mulwrap => try self.airMulWrap(inst),
|
||||
.div => try self.airDiv(inst),
|
||||
.add, .ptr_add => try self.airAdd(inst),
|
||||
.addwrap => try self.airAddWrap(inst),
|
||||
.sub, .ptr_sub => try self.airSub(inst),
|
||||
.subwrap => try self.airSubWrap(inst),
|
||||
.mul => try self.airMul(inst),
|
||||
.mulwrap => try self.airMulWrap(inst),
|
||||
.div => try self.airDiv(inst),
|
||||
|
||||
.cmp_lt => try self.airCmp(inst, .lt),
|
||||
.cmp_lte => try self.airCmp(inst, .lte),
|
||||
@ -859,6 +859,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
|
||||
.slice_elem_val => try self.airSliceElemVal(inst),
|
||||
.ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),
|
||||
.ptr_elem_val => try self.airPtrElemVal(inst),
|
||||
.ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst),
|
||||
|
||||
.constant => unreachable, // excluded from function bodies
|
||||
.const_ty => unreachable, // excluded from function bodies
|
||||
@ -1369,21 +1371,41 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
}
|
||||
|
||||
fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const is_volatile = false; // TODO
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
|
||||
const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else switch (arch) {
|
||||
else => return self.fail("TODO implement slice_elem_val for {}", .{self.target.cpu.arch}),
|
||||
};
|
||||
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
|
||||
}
|
||||
|
||||
fn airPtrSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const is_volatile = false; // TODO
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
|
||||
const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else switch (arch) {
|
||||
else => return self.fail("TODO implement ptr_slice_elem_val for {}", .{self.target.cpu.arch}),
|
||||
};
|
||||
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
|
||||
}
|
||||
|
||||
fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const is_volatile = false; // TODO
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else switch (arch) {
|
||||
else => return self.fail("TODO implement ptr_elem_val for {}", .{self.target.cpu.arch}),
|
||||
};
|
||||
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
|
||||
}
|
||||
|
||||
fn airPtrPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const is_volatile = false; // TODO
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else switch (arch) {
|
||||
else => return self.fail("TODO implement ptr_ptr_elem_val for {}", .{self.target.cpu.arch}),
|
||||
};
|
||||
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
|
||||
}
|
||||
|
||||
fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
|
||||
if (!self.liveness.operandDies(inst, op_index))
|
||||
return false;
|
||||
|
||||
@ -850,19 +850,19 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
|
||||
|
||||
// TODO use a different strategy for add that communicates to the optimizer
|
||||
// that wrapping is UB.
|
||||
.add => try airBinOp( o, inst, " + "),
|
||||
.addwrap => try airWrapOp(o, inst, " + ", "addw_"),
|
||||
.add, .ptr_add => try airBinOp( o, inst, " + "),
|
||||
.addwrap => try airWrapOp(o, inst, " + ", "addw_"),
|
||||
// TODO use a different strategy for sub that communicates to the optimizer
|
||||
// that wrapping is UB.
|
||||
.sub => try airBinOp( o, inst, " - "),
|
||||
.subwrap => try airWrapOp(o, inst, " - ", "subw_"),
|
||||
.sub, .ptr_sub => try airBinOp( o, inst, " - "),
|
||||
.subwrap => try airWrapOp(o, inst, " - ", "subw_"),
|
||||
// TODO use a different strategy for mul that communicates to the optimizer
|
||||
// that wrapping is UB.
|
||||
.mul => try airBinOp( o, inst, " * "),
|
||||
.mulwrap => try airWrapOp(o, inst, " * ", "mulw_"),
|
||||
.mul => try airBinOp( o, inst, " * "),
|
||||
.mulwrap => try airWrapOp(o, inst, " * ", "mulw_"),
|
||||
// TODO use a different strategy for div that communicates to the optimizer
|
||||
// that wrapping is UB.
|
||||
.div => try airBinOp( o, inst, " / "),
|
||||
.div => try airBinOp( o, inst, " / "),
|
||||
|
||||
.cmp_eq => try airBinOp(o, inst, " == "),
|
||||
.cmp_gt => try airBinOp(o, inst, " > "),
|
||||
@ -915,6 +915,8 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
|
||||
.slice_ptr => try airSliceField(o, inst, ".ptr;\n"),
|
||||
.slice_len => try airSliceField(o, inst, ".len;\n"),
|
||||
|
||||
.ptr_elem_val => try airPtrElemVal(o, inst, "["),
|
||||
.ptr_ptr_elem_val => try airPtrElemVal(o, inst, "[0]["),
|
||||
.slice_elem_val => try airSliceElemVal(o, inst, "["),
|
||||
.ptr_slice_elem_val => try airSliceElemVal(o, inst, "[0]["),
|
||||
|
||||
@ -953,8 +955,18 @@ fn airSliceField(o: *Object, inst: Air.Inst.Index, suffix: []const u8) !CValue {
|
||||
return local;
|
||||
}
|
||||
|
||||
fn airPtrElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue {
|
||||
const is_volatile = false; // TODO
|
||||
if (!is_volatile and o.liveness.isUnused(inst))
|
||||
return CValue.none;
|
||||
|
||||
_ = prefix;
|
||||
return o.dg.fail("TODO: C backend: airPtrElemVal", .{});
|
||||
}
|
||||
|
||||
fn airSliceElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue {
|
||||
if (o.liveness.isUnused(inst))
|
||||
const is_volatile = false; // TODO
|
||||
if (!is_volatile and o.liveness.isUnused(inst))
|
||||
return CValue.none;
|
||||
|
||||
const bin_op = o.air.instructions.items(.data)[inst].bin_op;
|
||||
|
||||
@ -966,6 +966,8 @@ pub const FuncGen = struct {
|
||||
.mul => try self.airMul(inst, false),
|
||||
.mulwrap => try self.airMul(inst, true),
|
||||
.div => try self.airDiv(inst),
|
||||
.ptr_add => try self.airPtrAdd(inst),
|
||||
.ptr_sub => try self.airPtrSub(inst),
|
||||
|
||||
.bit_and, .bool_and => try self.airAnd(inst),
|
||||
.bit_or, .bool_or => try self.airOr(inst),
|
||||
@ -1015,6 +1017,8 @@ pub const FuncGen = struct {
|
||||
|
||||
.slice_elem_val => try self.airSliceElemVal(inst),
|
||||
.ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),
|
||||
.ptr_elem_val => try self.airPtrElemVal(inst),
|
||||
.ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst),
|
||||
|
||||
.optional_payload => try self.airOptionalPayload(inst, false),
|
||||
.optional_payload_ptr => try self.airOptionalPayload(inst, true),
|
||||
@ -1229,7 +1233,8 @@ pub const FuncGen = struct {
|
||||
}
|
||||
|
||||
fn airSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst))
|
||||
const is_volatile = false; // TODO
|
||||
if (!is_volatile and self.liveness.isUnused(inst))
|
||||
return null;
|
||||
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
@ -1242,7 +1247,8 @@ pub const FuncGen = struct {
|
||||
}
|
||||
|
||||
fn airPtrSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst))
|
||||
const is_volatile = false; // TODO
|
||||
if (!is_volatile and self.liveness.isUnused(inst))
|
||||
return null;
|
||||
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
@ -1264,6 +1270,33 @@ pub const FuncGen = struct {
|
||||
return self.builder.buildLoad(ptr, "");
|
||||
}
|
||||
|
||||
fn airPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
const is_volatile = false; // TODO
|
||||
if (!is_volatile and self.liveness.isUnused(inst))
|
||||
return null;
|
||||
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
const base_ptr = try self.resolveInst(bin_op.lhs);
|
||||
const rhs = try self.resolveInst(bin_op.rhs);
|
||||
const indices: [1]*const llvm.Value = .{rhs};
|
||||
const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
|
||||
return self.builder.buildLoad(ptr, "");
|
||||
}
|
||||
|
||||
fn airPtrPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
const is_volatile = false; // TODO
|
||||
if (!is_volatile and self.liveness.isUnused(inst))
|
||||
return null;
|
||||
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
const lhs = try self.resolveInst(bin_op.lhs);
|
||||
const rhs = try self.resolveInst(bin_op.rhs);
|
||||
const base_ptr = self.builder.buildLoad(lhs, "");
|
||||
const indices: [1]*const llvm.Value = .{rhs};
|
||||
const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
|
||||
return self.builder.buildLoad(ptr, "");
|
||||
}
|
||||
|
||||
fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst))
|
||||
return null;
|
||||
@ -1624,6 +1657,29 @@ pub const FuncGen = struct {
|
||||
return self.builder.buildUDiv(lhs, rhs, "");
|
||||
}
|
||||
|
||||
fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst))
|
||||
return null;
|
||||
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
const base_ptr = try self.resolveInst(bin_op.lhs);
|
||||
const offset = try self.resolveInst(bin_op.rhs);
|
||||
const indices: [1]*const llvm.Value = .{offset};
|
||||
return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
|
||||
}
|
||||
|
||||
fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst))
|
||||
return null;
|
||||
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
const base_ptr = try self.resolveInst(bin_op.lhs);
|
||||
const offset = try self.resolveInst(bin_op.rhs);
|
||||
const negative_offset = self.builder.buildNeg(offset, "");
|
||||
const indices: [1]*const llvm.Value = .{negative_offset};
|
||||
return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
|
||||
}
|
||||
|
||||
fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
if (self.liveness.isUnused(inst))
|
||||
return null;
|
||||
|
||||
@ -324,6 +324,9 @@ pub const Builder = opaque {
|
||||
pub const buildLoad = LLVMBuildLoad;
|
||||
extern fn LLVMBuildLoad(*const Builder, PointerVal: *const Value, Name: [*:0]const u8) *const Value;
|
||||
|
||||
pub const buildNeg = LLVMBuildNeg;
|
||||
extern fn LLVMBuildNeg(*const Builder, V: *const Value, Name: [*:0]const u8) *const Value;
|
||||
|
||||
pub const buildNot = LLVMBuildNot;
|
||||
extern fn LLVMBuildNot(*const Builder, V: *const Value, Name: [*:0]const u8) *const Value;
|
||||
|
||||
|
||||
@ -109,6 +109,8 @@ const Writer = struct {
|
||||
.mul,
|
||||
.mulwrap,
|
||||
.div,
|
||||
.ptr_add,
|
||||
.ptr_sub,
|
||||
.bit_and,
|
||||
.bit_or,
|
||||
.xor,
|
||||
@ -123,6 +125,8 @@ const Writer = struct {
|
||||
.store,
|
||||
.slice_elem_val,
|
||||
.ptr_slice_elem_val,
|
||||
.ptr_elem_val,
|
||||
.ptr_ptr_elem_val,
|
||||
=> try w.writeBinOp(s, inst),
|
||||
|
||||
.is_null,
|
||||
|
||||
14
src/type.zig
14
src/type.zig
@ -2753,11 +2753,15 @@ pub const Type = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isIndexable(self: Type) bool {
|
||||
const zig_tag = self.zigTypeTag();
|
||||
// TODO tuples are indexable
|
||||
return zig_tag == .Array or zig_tag == .Vector or self.isSlice() or
|
||||
(self.isSinglePointer() and self.elemType().zigTypeTag() == .Array);
|
||||
pub fn isIndexable(ty: Type) bool {
|
||||
return switch (ty.zigTypeTag()) {
|
||||
.Array, .Vector => true,
|
||||
.Pointer => switch (ty.ptrSize()) {
|
||||
.Slice, .Many, .C => true,
|
||||
.One => ty.elemType().zigTypeTag() == .Array,
|
||||
},
|
||||
else => false, // TODO tuples are indexable
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns null if the type has no namespace.
|
||||
|
||||
@ -6,6 +6,7 @@ test {
|
||||
_ = @import("behavior/basic.zig");
|
||||
_ = @import("behavior/generics.zig");
|
||||
_ = @import("behavior/eval.zig");
|
||||
_ = @import("behavior/pointers.zig");
|
||||
|
||||
if (!builtin.zig_is_stage2) {
|
||||
// Tests that only pass for stage1.
|
||||
@ -112,7 +113,7 @@ test {
|
||||
_ = @import("behavior/namespace_depends_on_compile_var.zig");
|
||||
_ = @import("behavior/null.zig");
|
||||
_ = @import("behavior/optional.zig");
|
||||
_ = @import("behavior/pointers.zig");
|
||||
_ = @import("behavior/pointers_stage1.zig");
|
||||
_ = @import("behavior/popcount.zig");
|
||||
_ = @import("behavior/ptrcast.zig");
|
||||
_ = @import("behavior/pub_enum.zig");
|
||||
|
||||
@ -15,22 +15,6 @@ fn testDerefPtr() !void {
|
||||
try expect(x == 1235);
|
||||
}
|
||||
|
||||
const Foo1 = struct {
|
||||
x: void,
|
||||
};
|
||||
|
||||
test "dereference pointer again" {
|
||||
try testDerefPtrOneVal();
|
||||
comptime try testDerefPtrOneVal();
|
||||
}
|
||||
|
||||
fn testDerefPtrOneVal() !void {
|
||||
// Foo1 satisfies the OnePossibleValueYes criteria
|
||||
const x = &Foo1{ .x = {} };
|
||||
const y = x.*;
|
||||
try expect(@TypeOf(y.x) == void);
|
||||
}
|
||||
|
||||
test "pointer arithmetic" {
|
||||
var ptr: [*]const u8 = "abcd";
|
||||
|
||||
@ -60,288 +44,3 @@ test "double pointer parsing" {
|
||||
fn PtrOf(comptime T: type) type {
|
||||
return *T;
|
||||
}
|
||||
|
||||
test "assigning integer to C pointer" {
|
||||
var x: i32 = 0;
|
||||
var ptr: [*c]u8 = 0;
|
||||
var ptr2: [*c]u8 = x;
|
||||
if (false) {
|
||||
ptr;
|
||||
ptr2;
|
||||
}
|
||||
}
|
||||
|
||||
test "implicit cast single item pointer to C pointer and back" {
|
||||
var y: u8 = 11;
|
||||
var x: [*c]u8 = &y;
|
||||
var z: *u8 = x;
|
||||
z.* += 1;
|
||||
try expect(y == 12);
|
||||
}
|
||||
|
||||
test "C pointer comparison and arithmetic" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var ptr1: [*c]u32 = 0;
|
||||
var ptr2 = ptr1 + 10;
|
||||
try expect(ptr1 == 0);
|
||||
try expect(ptr1 >= 0);
|
||||
try expect(ptr1 <= 0);
|
||||
// expect(ptr1 < 1);
|
||||
// expect(ptr1 < one);
|
||||
// expect(1 > ptr1);
|
||||
// expect(one > ptr1);
|
||||
try expect(ptr1 < ptr2);
|
||||
try expect(ptr2 > ptr1);
|
||||
try expect(ptr2 >= 40);
|
||||
try expect(ptr2 == 40);
|
||||
try expect(ptr2 <= 40);
|
||||
ptr2 -= 10;
|
||||
try expect(ptr1 == ptr2);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "peer type resolution with C pointers" {
|
||||
var ptr_one: *u8 = undefined;
|
||||
var ptr_many: [*]u8 = undefined;
|
||||
var ptr_c: [*c]u8 = undefined;
|
||||
var t = true;
|
||||
var x1 = if (t) ptr_one else ptr_c;
|
||||
var x2 = if (t) ptr_many else ptr_c;
|
||||
var x3 = if (t) ptr_c else ptr_one;
|
||||
var x4 = if (t) ptr_c else ptr_many;
|
||||
try expect(@TypeOf(x1) == [*c]u8);
|
||||
try expect(@TypeOf(x2) == [*c]u8);
|
||||
try expect(@TypeOf(x3) == [*c]u8);
|
||||
try expect(@TypeOf(x4) == [*c]u8);
|
||||
}
|
||||
|
||||
test "implicit casting between C pointer and optional non-C pointer" {
|
||||
var slice: []const u8 = "aoeu";
|
||||
const opt_many_ptr: ?[*]const u8 = slice.ptr;
|
||||
var ptr_opt_many_ptr = &opt_many_ptr;
|
||||
var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr;
|
||||
try expect(c_ptr.*.* == 'a');
|
||||
ptr_opt_many_ptr = c_ptr;
|
||||
try expect(ptr_opt_many_ptr.*.?[1] == 'o');
|
||||
}
|
||||
|
||||
test "implicit cast error unions with non-optional to optional pointer" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try expectError(error.Fail, foo());
|
||||
}
|
||||
fn foo() anyerror!?*u8 {
|
||||
return bar() orelse error.Fail;
|
||||
}
|
||||
fn bar() ?*u8 {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "initialize const optional C pointer to null" {
|
||||
const a: ?[*c]i32 = null;
|
||||
try expect(a == null);
|
||||
comptime try expect(a == null);
|
||||
}
|
||||
|
||||
test "compare equality of optional and non-optional pointer" {
|
||||
const a = @intToPtr(*const usize, 0x12345678);
|
||||
const b = @intToPtr(?*usize, 0x12345678);
|
||||
try expect(a == b);
|
||||
try expect(b == a);
|
||||
}
|
||||
|
||||
test "allowzero pointer and slice" {
|
||||
var ptr = @intToPtr([*]allowzero i32, 0);
|
||||
var opt_ptr: ?[*]allowzero i32 = ptr;
|
||||
try expect(opt_ptr != null);
|
||||
try expect(@ptrToInt(ptr) == 0);
|
||||
var runtime_zero: usize = 0;
|
||||
var slice = ptr[runtime_zero..10];
|
||||
comptime try expect(@TypeOf(slice) == []allowzero i32);
|
||||
try expect(@ptrToInt(&slice[5]) == 20);
|
||||
|
||||
comptime try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero);
|
||||
comptime try expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero);
|
||||
}
|
||||
|
||||
test "assign null directly to C pointer and test null equality" {
|
||||
var x: [*c]i32 = null;
|
||||
try expect(x == null);
|
||||
try expect(null == x);
|
||||
try expect(!(x != null));
|
||||
try expect(!(null != x));
|
||||
if (x) |same_x| {
|
||||
_ = same_x;
|
||||
@panic("fail");
|
||||
}
|
||||
var otherx: i32 = undefined;
|
||||
try expect((x orelse &otherx) == &otherx);
|
||||
|
||||
const y: [*c]i32 = null;
|
||||
comptime try expect(y == null);
|
||||
comptime try expect(null == y);
|
||||
comptime try expect(!(y != null));
|
||||
comptime try expect(!(null != y));
|
||||
if (y) |same_y| {
|
||||
_ = same_y;
|
||||
@panic("fail");
|
||||
}
|
||||
const othery: i32 = undefined;
|
||||
comptime try expect((y orelse &othery) == &othery);
|
||||
|
||||
var n: i32 = 1234;
|
||||
var x1: [*c]i32 = &n;
|
||||
try expect(!(x1 == null));
|
||||
try expect(!(null == x1));
|
||||
try expect(x1 != null);
|
||||
try expect(null != x1);
|
||||
try expect(x1.?.* == 1234);
|
||||
if (x1) |same_x1| {
|
||||
try expect(same_x1.* == 1234);
|
||||
} else {
|
||||
@panic("fail");
|
||||
}
|
||||
try expect((x1 orelse &otherx) == x1);
|
||||
|
||||
const nc: i32 = 1234;
|
||||
const y1: [*c]const i32 = &nc;
|
||||
comptime try expect(!(y1 == null));
|
||||
comptime try expect(!(null == y1));
|
||||
comptime try expect(y1 != null);
|
||||
comptime try expect(null != y1);
|
||||
comptime try expect(y1.?.* == 1234);
|
||||
if (y1) |same_y1| {
|
||||
try expect(same_y1.* == 1234);
|
||||
} else {
|
||||
@compileError("fail");
|
||||
}
|
||||
comptime try expect((y1 orelse &othery) == y1);
|
||||
}
|
||||
|
||||
test "null terminated pointer" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var array_with_zero = [_:0]u8{ 'h', 'e', 'l', 'l', 'o' };
|
||||
var zero_ptr: [*:0]const u8 = @ptrCast([*:0]const u8, &array_with_zero);
|
||||
var no_zero_ptr: [*]const u8 = zero_ptr;
|
||||
var zero_ptr_again = @ptrCast([*:0]const u8, no_zero_ptr);
|
||||
try expect(std.mem.eql(u8, std.mem.spanZ(zero_ptr_again), "hello"));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "allow any sentinel" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var array = [_:std.math.minInt(i32)]i32{ 1, 2, 3, 4 };
|
||||
var ptr: [*:std.math.minInt(i32)]i32 = &array;
|
||||
try expect(ptr[4] == std.math.minInt(i32));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "pointer sentinel with enums" {
|
||||
const S = struct {
|
||||
const Number = enum {
|
||||
one,
|
||||
two,
|
||||
sentinel,
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var ptr: [*:.sentinel]const Number = &[_:.sentinel]Number{ .one, .two, .two, .one };
|
||||
try expect(ptr[4] == .sentinel); // TODO this should be comptime try expect, see #3731
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "pointer sentinel with optional element" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var ptr: [*:null]const ?i32 = &[_:null]?i32{ 1, 2, 3, 4 };
|
||||
try expect(ptr[4] == null); // TODO this should be comptime try expect, see #3731
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "pointer sentinel with +inf" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
const inf = std.math.inf_f32;
|
||||
var ptr: [*:inf]const f32 = &[_:inf]f32{ 1.1, 2.2, 3.3, 4.4 };
|
||||
try expect(ptr[4] == inf); // TODO this should be comptime try expect, see #3731
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "pointer to array at fixed address" {
|
||||
const array = @intToPtr(*volatile [1]u32, 0x10);
|
||||
// Silly check just to reference `array`
|
||||
try expect(@ptrToInt(&array[0]) == 0x10);
|
||||
}
|
||||
|
||||
test "pointer arithmetic affects the alignment" {
|
||||
{
|
||||
var ptr: [*]align(8) u32 = undefined;
|
||||
var x: usize = 1;
|
||||
|
||||
try expect(@typeInfo(@TypeOf(ptr)).Pointer.alignment == 8);
|
||||
const ptr1 = ptr + 1; // 1 * 4 = 4 -> lcd(4,8) = 4
|
||||
try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 4);
|
||||
const ptr2 = ptr + 4; // 4 * 4 = 16 -> lcd(16,8) = 8
|
||||
try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 8);
|
||||
const ptr3 = ptr + 0; // no-op
|
||||
try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8);
|
||||
const ptr4 = ptr + x; // runtime-known addend
|
||||
try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4);
|
||||
}
|
||||
{
|
||||
var ptr: [*]align(8) [3]u8 = undefined;
|
||||
var x: usize = 1;
|
||||
|
||||
const ptr1 = ptr + 17; // 3 * 17 = 51
|
||||
try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 1);
|
||||
const ptr2 = ptr + x; // runtime-known addend
|
||||
try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 1);
|
||||
const ptr3 = ptr + 8; // 3 * 8 = 24 -> lcd(8,24) = 8
|
||||
try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8);
|
||||
const ptr4 = ptr + 4; // 3 * 4 = 12 -> lcd(8,12) = 4
|
||||
try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4);
|
||||
}
|
||||
}
|
||||
|
||||
test "@ptrToInt on null optional at comptime" {
|
||||
{
|
||||
const pointer = @intToPtr(?*u8, 0x000);
|
||||
const x = @ptrToInt(pointer);
|
||||
_ = x;
|
||||
comptime try expect(0 == @ptrToInt(pointer));
|
||||
}
|
||||
{
|
||||
const pointer = @intToPtr(?*u8, 0xf00);
|
||||
comptime try expect(0xf00 == @ptrToInt(pointer));
|
||||
}
|
||||
}
|
||||
|
||||
test "indexing array with sentinel returns correct type" {
|
||||
var s: [:0]const u8 = "abc";
|
||||
try testing.expectEqualSlices(u8, "*const u8", @typeName(@TypeOf(&s[0])));
|
||||
}
|
||||
|
||||
305
test/behavior/pointers_stage1.zig
Normal file
305
test/behavior/pointers_stage1.zig
Normal file
@ -0,0 +1,305 @@
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const expect = testing.expect;
|
||||
const expectError = testing.expectError;
|
||||
|
||||
const Foo1 = struct {
|
||||
x: void,
|
||||
};
|
||||
|
||||
test "dereference pointer again" {
|
||||
try testDerefPtrOneVal();
|
||||
comptime try testDerefPtrOneVal();
|
||||
}
|
||||
|
||||
fn testDerefPtrOneVal() !void {
|
||||
// Foo1 satisfies the OnePossibleValueYes criteria
|
||||
const x = &Foo1{ .x = {} };
|
||||
const y = x.*;
|
||||
try expect(@TypeOf(y.x) == void);
|
||||
}
|
||||
|
||||
test "assigning integer to C pointer" {
|
||||
var x: i32 = 0;
|
||||
var ptr: [*c]u8 = 0;
|
||||
var ptr2: [*c]u8 = x;
|
||||
if (false) {
|
||||
ptr;
|
||||
ptr2;
|
||||
}
|
||||
}
|
||||
|
||||
test "implicit cast single item pointer to C pointer and back" {
|
||||
var y: u8 = 11;
|
||||
var x: [*c]u8 = &y;
|
||||
var z: *u8 = x;
|
||||
z.* += 1;
|
||||
try expect(y == 12);
|
||||
}
|
||||
|
||||
test "C pointer comparison and arithmetic" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var ptr1: [*c]u32 = 0;
|
||||
var ptr2 = ptr1 + 10;
|
||||
try expect(ptr1 == 0);
|
||||
try expect(ptr1 >= 0);
|
||||
try expect(ptr1 <= 0);
|
||||
// expect(ptr1 < 1);
|
||||
// expect(ptr1 < one);
|
||||
// expect(1 > ptr1);
|
||||
// expect(one > ptr1);
|
||||
try expect(ptr1 < ptr2);
|
||||
try expect(ptr2 > ptr1);
|
||||
try expect(ptr2 >= 40);
|
||||
try expect(ptr2 == 40);
|
||||
try expect(ptr2 <= 40);
|
||||
ptr2 -= 10;
|
||||
try expect(ptr1 == ptr2);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "peer type resolution with C pointers" {
|
||||
var ptr_one: *u8 = undefined;
|
||||
var ptr_many: [*]u8 = undefined;
|
||||
var ptr_c: [*c]u8 = undefined;
|
||||
var t = true;
|
||||
var x1 = if (t) ptr_one else ptr_c;
|
||||
var x2 = if (t) ptr_many else ptr_c;
|
||||
var x3 = if (t) ptr_c else ptr_one;
|
||||
var x4 = if (t) ptr_c else ptr_many;
|
||||
try expect(@TypeOf(x1) == [*c]u8);
|
||||
try expect(@TypeOf(x2) == [*c]u8);
|
||||
try expect(@TypeOf(x3) == [*c]u8);
|
||||
try expect(@TypeOf(x4) == [*c]u8);
|
||||
}
|
||||
|
||||
test "implicit casting between C pointer and optional non-C pointer" {
|
||||
var slice: []const u8 = "aoeu";
|
||||
const opt_many_ptr: ?[*]const u8 = slice.ptr;
|
||||
var ptr_opt_many_ptr = &opt_many_ptr;
|
||||
var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr;
|
||||
try expect(c_ptr.*.* == 'a');
|
||||
ptr_opt_many_ptr = c_ptr;
|
||||
try expect(ptr_opt_many_ptr.*.?[1] == 'o');
|
||||
}
|
||||
|
||||
test "implicit cast error unions with non-optional to optional pointer" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try expectError(error.Fail, foo());
|
||||
}
|
||||
fn foo() anyerror!?*u8 {
|
||||
return bar() orelse error.Fail;
|
||||
}
|
||||
fn bar() ?*u8 {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "initialize const optional C pointer to null" {
|
||||
const a: ?[*c]i32 = null;
|
||||
try expect(a == null);
|
||||
comptime try expect(a == null);
|
||||
}
|
||||
|
||||
test "compare equality of optional and non-optional pointer" {
|
||||
const a = @intToPtr(*const usize, 0x12345678);
|
||||
const b = @intToPtr(?*usize, 0x12345678);
|
||||
try expect(a == b);
|
||||
try expect(b == a);
|
||||
}
|
||||
|
||||
test "allowzero pointer and slice" {
|
||||
var ptr = @intToPtr([*]allowzero i32, 0);
|
||||
var opt_ptr: ?[*]allowzero i32 = ptr;
|
||||
try expect(opt_ptr != null);
|
||||
try expect(@ptrToInt(ptr) == 0);
|
||||
var runtime_zero: usize = 0;
|
||||
var slice = ptr[runtime_zero..10];
|
||||
comptime try expect(@TypeOf(slice) == []allowzero i32);
|
||||
try expect(@ptrToInt(&slice[5]) == 20);
|
||||
|
||||
comptime try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero);
|
||||
comptime try expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero);
|
||||
}
|
||||
|
||||
test "assign null directly to C pointer and test null equality" {
|
||||
var x: [*c]i32 = null;
|
||||
try expect(x == null);
|
||||
try expect(null == x);
|
||||
try expect(!(x != null));
|
||||
try expect(!(null != x));
|
||||
if (x) |same_x| {
|
||||
_ = same_x;
|
||||
@panic("fail");
|
||||
}
|
||||
var otherx: i32 = undefined;
|
||||
try expect((x orelse &otherx) == &otherx);
|
||||
|
||||
const y: [*c]i32 = null;
|
||||
comptime try expect(y == null);
|
||||
comptime try expect(null == y);
|
||||
comptime try expect(!(y != null));
|
||||
comptime try expect(!(null != y));
|
||||
if (y) |same_y| {
|
||||
_ = same_y;
|
||||
@panic("fail");
|
||||
}
|
||||
const othery: i32 = undefined;
|
||||
comptime try expect((y orelse &othery) == &othery);
|
||||
|
||||
var n: i32 = 1234;
|
||||
var x1: [*c]i32 = &n;
|
||||
try expect(!(x1 == null));
|
||||
try expect(!(null == x1));
|
||||
try expect(x1 != null);
|
||||
try expect(null != x1);
|
||||
try expect(x1.?.* == 1234);
|
||||
if (x1) |same_x1| {
|
||||
try expect(same_x1.* == 1234);
|
||||
} else {
|
||||
@panic("fail");
|
||||
}
|
||||
try expect((x1 orelse &otherx) == x1);
|
||||
|
||||
const nc: i32 = 1234;
|
||||
const y1: [*c]const i32 = &nc;
|
||||
comptime try expect(!(y1 == null));
|
||||
comptime try expect(!(null == y1));
|
||||
comptime try expect(y1 != null);
|
||||
comptime try expect(null != y1);
|
||||
comptime try expect(y1.?.* == 1234);
|
||||
if (y1) |same_y1| {
|
||||
try expect(same_y1.* == 1234);
|
||||
} else {
|
||||
@compileError("fail");
|
||||
}
|
||||
comptime try expect((y1 orelse &othery) == y1);
|
||||
}
|
||||
|
||||
test "null terminated pointer" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var array_with_zero = [_:0]u8{ 'h', 'e', 'l', 'l', 'o' };
|
||||
var zero_ptr: [*:0]const u8 = @ptrCast([*:0]const u8, &array_with_zero);
|
||||
var no_zero_ptr: [*]const u8 = zero_ptr;
|
||||
var zero_ptr_again = @ptrCast([*:0]const u8, no_zero_ptr);
|
||||
try expect(std.mem.eql(u8, std.mem.spanZ(zero_ptr_again), "hello"));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "allow any sentinel" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var array = [_:std.math.minInt(i32)]i32{ 1, 2, 3, 4 };
|
||||
var ptr: [*:std.math.minInt(i32)]i32 = &array;
|
||||
try expect(ptr[4] == std.math.minInt(i32));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "pointer sentinel with enums" {
|
||||
const S = struct {
|
||||
const Number = enum {
|
||||
one,
|
||||
two,
|
||||
sentinel,
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var ptr: [*:.sentinel]const Number = &[_:.sentinel]Number{ .one, .two, .two, .one };
|
||||
try expect(ptr[4] == .sentinel); // TODO this should be comptime try expect, see #3731
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "pointer sentinel with optional element" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var ptr: [*:null]const ?i32 = &[_:null]?i32{ 1, 2, 3, 4 };
|
||||
try expect(ptr[4] == null); // TODO this should be comptime try expect, see #3731
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "pointer sentinel with +inf" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
const inf = std.math.inf_f32;
|
||||
var ptr: [*:inf]const f32 = &[_:inf]f32{ 1.1, 2.2, 3.3, 4.4 };
|
||||
try expect(ptr[4] == inf); // TODO this should be comptime try expect, see #3731
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "pointer to array at fixed address" {
|
||||
const array = @intToPtr(*volatile [1]u32, 0x10);
|
||||
// Silly check just to reference `array`
|
||||
try expect(@ptrToInt(&array[0]) == 0x10);
|
||||
}
|
||||
|
||||
test "pointer arithmetic affects the alignment" {
|
||||
{
|
||||
var ptr: [*]align(8) u32 = undefined;
|
||||
var x: usize = 1;
|
||||
|
||||
try expect(@typeInfo(@TypeOf(ptr)).Pointer.alignment == 8);
|
||||
const ptr1 = ptr + 1; // 1 * 4 = 4 -> lcd(4,8) = 4
|
||||
try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 4);
|
||||
const ptr2 = ptr + 4; // 4 * 4 = 16 -> lcd(16,8) = 8
|
||||
try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 8);
|
||||
const ptr3 = ptr + 0; // no-op
|
||||
try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8);
|
||||
const ptr4 = ptr + x; // runtime-known addend
|
||||
try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4);
|
||||
}
|
||||
{
|
||||
var ptr: [*]align(8) [3]u8 = undefined;
|
||||
var x: usize = 1;
|
||||
|
||||
const ptr1 = ptr + 17; // 3 * 17 = 51
|
||||
try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 1);
|
||||
const ptr2 = ptr + x; // runtime-known addend
|
||||
try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 1);
|
||||
const ptr3 = ptr + 8; // 3 * 8 = 24 -> lcd(8,24) = 8
|
||||
try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8);
|
||||
const ptr4 = ptr + 4; // 3 * 4 = 12 -> lcd(8,12) = 4
|
||||
try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4);
|
||||
}
|
||||
}
|
||||
|
||||
test "@ptrToInt on null optional at comptime" {
|
||||
{
|
||||
const pointer = @intToPtr(?*u8, 0x000);
|
||||
const x = @ptrToInt(pointer);
|
||||
_ = x;
|
||||
comptime try expect(0 == @ptrToInt(pointer));
|
||||
}
|
||||
{
|
||||
const pointer = @intToPtr(?*u8, 0xf00);
|
||||
comptime try expect(0xf00 == @ptrToInt(pointer));
|
||||
}
|
||||
}
|
||||
|
||||
test "indexing array with sentinel returns correct type" {
|
||||
var s: [:0]const u8 = "abc";
|
||||
try testing.expectEqualSlices(u8, "*const u8", @typeName(@TypeOf(&s[0])));
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user