mirror of
https://github.com/ziglang/zig.git
synced 2026-01-05 13:03:25 +00:00
Sema: improved C pointers and casting
* C pointer types always have allowzero set to true but they omit the word allowzero when printed. * Implement coercion from C pointers to other pointers. * Implement in-memory coercion for slices and pointer-like optionals. * Make slicing a C pointer drop the allowzero bit. * Value representation for pointer-like optionals is now allowed to use pointer tag values in addition to the `opt_payload` tag.
This commit is contained in:
parent
6df26a37d1
commit
c6b3d06535
189
src/Sema.zig
189
src/Sema.zig
@ -9125,7 +9125,7 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
|
||||
.pointee_type = elem_type,
|
||||
.@"addrspace" = .generic,
|
||||
.mutable = inst_data.is_mutable,
|
||||
.@"allowzero" = inst_data.is_allowzero,
|
||||
.@"allowzero" = inst_data.is_allowzero or inst_data.size == .C,
|
||||
.@"volatile" = inst_data.is_volatile,
|
||||
.size = inst_data.size,
|
||||
});
|
||||
@ -9185,7 +9185,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
|
||||
.bit_offset = bit_start,
|
||||
.host_size = bit_end,
|
||||
.mutable = inst_data.flags.is_mutable,
|
||||
.@"allowzero" = inst_data.flags.is_allowzero,
|
||||
.@"allowzero" = inst_data.flags.is_allowzero or inst_data.size == .C,
|
||||
.@"volatile" = inst_data.flags.is_volatile,
|
||||
.size = inst_data.size,
|
||||
});
|
||||
@ -12102,6 +12102,21 @@ fn coerce(
|
||||
}
|
||||
}
|
||||
|
||||
// coercion from C pointer
|
||||
if (inst_ty.isCPtr()) src_c_ptr: {
|
||||
// In this case we must add a safety check because the C pointer
|
||||
// could be null.
|
||||
const src_elem_ty = inst_ty.childType();
|
||||
const dest_is_mut = dest_info.mutable;
|
||||
const dst_elem_type = dest_info.pointee_type;
|
||||
switch (coerceInMemoryAllowed(dst_elem_type, src_elem_ty, dest_is_mut, target)) {
|
||||
.ok => {},
|
||||
.no_match => break :src_c_ptr,
|
||||
}
|
||||
// TODO add safety check for null pointer
|
||||
return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
|
||||
}
|
||||
|
||||
// coercion to C pointer
|
||||
if (dest_info.size == .C) {
|
||||
switch (inst_ty.zigTypeTag()) {
|
||||
@ -12262,86 +12277,109 @@ const InMemoryCoercionResult = enum {
|
||||
/// * sentinel-terminated pointers can coerce into `[*]`
|
||||
/// TODO improve this function to report recursive compile errors like it does in stage1.
|
||||
/// look at the function types_match_const_cast_only
|
||||
fn coerceInMemoryAllowed(dest_ty: Type, src_type: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult {
|
||||
if (dest_ty.eql(src_type))
|
||||
fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult {
|
||||
if (dest_ty.eql(src_ty))
|
||||
return .ok;
|
||||
|
||||
if (dest_ty.zigTypeTag() == .Pointer and
|
||||
src_type.zigTypeTag() == .Pointer)
|
||||
{
|
||||
const dest_info = dest_ty.ptrInfo().data;
|
||||
const src_info = src_type.ptrInfo().data;
|
||||
|
||||
const child = coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target);
|
||||
if (child == .no_match) {
|
||||
return child;
|
||||
// Pointers / Pointer-like Optionals
|
||||
var dest_buf: Type.Payload.ElemType = undefined;
|
||||
var src_buf: Type.Payload.ElemType = undefined;
|
||||
if (dest_ty.ptrOrOptionalPtrTy(&dest_buf)) |dest_ptr_ty| {
|
||||
if (src_ty.ptrOrOptionalPtrTy(&src_buf)) |src_ptr_ty| {
|
||||
return coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target);
|
||||
}
|
||||
|
||||
if (dest_info.@"addrspace" != src_info.@"addrspace") {
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
const ok_sent = dest_info.sentinel == null or src_info.size == .C or
|
||||
(src_info.sentinel != null and
|
||||
dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type));
|
||||
if (!ok_sent) {
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
const ok_ptr_size = src_info.size == dest_info.size or
|
||||
src_info.size == .C or dest_info.size == .C;
|
||||
if (!ok_ptr_size) {
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
const ok_cv_qualifiers =
|
||||
(src_info.mutable or !dest_info.mutable) and
|
||||
(!src_info.@"volatile" or dest_info.@"volatile");
|
||||
|
||||
if (!ok_cv_qualifiers) {
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
const ok_allows_zero = (dest_info.@"allowzero" and
|
||||
(src_info.@"allowzero" or !dest_is_mut)) or
|
||||
(!dest_info.@"allowzero" and !src_info.@"allowzero");
|
||||
if (!ok_allows_zero) {
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
if (dest_ty.hasCodeGenBits() != src_type.hasCodeGenBits()) {
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
if (src_info.host_size != dest_info.host_size or
|
||||
src_info.bit_offset != dest_info.bit_offset)
|
||||
{
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
// If both pointers have alignment 0, it means they both want ABI alignment.
|
||||
// In this case, if they share the same child type, no need to resolve
|
||||
// pointee type alignment. Otherwise both pointee types must have their alignment
|
||||
// resolved and we compare the alignment numerically.
|
||||
if (src_info.@"align" != 0 or dest_info.@"align" != 0 or
|
||||
!dest_info.pointee_type.eql(src_info.pointee_type))
|
||||
{
|
||||
const src_align = src_type.ptrAlignment(target);
|
||||
const dest_align = dest_ty.ptrAlignment(target);
|
||||
|
||||
if (dest_align > src_align) {
|
||||
return .no_match;
|
||||
}
|
||||
}
|
||||
|
||||
return .ok;
|
||||
}
|
||||
|
||||
// TODO: implement more of this function
|
||||
// Slices
|
||||
if (dest_ty.isSlice() and src_ty.isSlice()) {
|
||||
return coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target);
|
||||
}
|
||||
|
||||
// TODO: arrays
|
||||
// TODO: non-pointer-like optionals
|
||||
// TODO: error unions
|
||||
// TODO: error sets
|
||||
// TODO: functions
|
||||
// TODO: vectors
|
||||
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
fn coerceInMemoryAllowedPtrs(
|
||||
dest_ty: Type,
|
||||
src_ty: Type,
|
||||
dest_ptr_ty: Type,
|
||||
src_ptr_ty: Type,
|
||||
dest_is_mut: bool,
|
||||
target: std.Target,
|
||||
) InMemoryCoercionResult {
|
||||
const dest_info = dest_ptr_ty.ptrInfo().data;
|
||||
const src_info = src_ptr_ty.ptrInfo().data;
|
||||
|
||||
const child = coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target);
|
||||
if (child == .no_match) {
|
||||
return child;
|
||||
}
|
||||
|
||||
if (dest_info.@"addrspace" != src_info.@"addrspace") {
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
const ok_sent = dest_info.sentinel == null or src_info.size == .C or
|
||||
(src_info.sentinel != null and
|
||||
dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type));
|
||||
if (!ok_sent) {
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
const ok_ptr_size = src_info.size == dest_info.size or
|
||||
src_info.size == .C or dest_info.size == .C;
|
||||
if (!ok_ptr_size) {
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
const ok_cv_qualifiers =
|
||||
(src_info.mutable or !dest_info.mutable) and
|
||||
(!src_info.@"volatile" or dest_info.@"volatile");
|
||||
|
||||
if (!ok_cv_qualifiers) {
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
const dest_allow_zero = dest_ty.ptrAllowsZero();
|
||||
const src_allow_zero = src_ty.ptrAllowsZero();
|
||||
|
||||
const ok_allows_zero = (dest_allow_zero and
|
||||
(src_allow_zero or !dest_is_mut)) or
|
||||
(!dest_allow_zero and !src_allow_zero);
|
||||
if (!ok_allows_zero) {
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
if (src_info.host_size != dest_info.host_size or
|
||||
src_info.bit_offset != dest_info.bit_offset)
|
||||
{
|
||||
return .no_match;
|
||||
}
|
||||
|
||||
// If both pointers have alignment 0, it means they both want ABI alignment.
|
||||
// In this case, if they share the same child type, no need to resolve
|
||||
// pointee type alignment. Otherwise both pointee types must have their alignment
|
||||
// resolved and we compare the alignment numerically.
|
||||
if (src_info.@"align" != 0 or dest_info.@"align" != 0 or
|
||||
!dest_info.pointee_type.eql(src_info.pointee_type))
|
||||
{
|
||||
const src_align = src_info.@"align";
|
||||
const dest_align = dest_info.@"align";
|
||||
|
||||
if (dest_align > src_align) {
|
||||
return .no_match;
|
||||
}
|
||||
}
|
||||
|
||||
return .ok;
|
||||
}
|
||||
|
||||
fn coerceNum(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
@ -13297,6 +13335,7 @@ fn analyzeSlice(
|
||||
const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
|
||||
|
||||
const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;
|
||||
const new_allowzero = new_ptr_ty_info.@"allowzero" and sema.typeOf(ptr).ptrSize() != .C;
|
||||
|
||||
if (opt_new_len_val) |new_len_val| {
|
||||
const new_len_int = new_len_val.toUnsignedInt();
|
||||
@ -13312,7 +13351,7 @@ fn analyzeSlice(
|
||||
.@"align" = new_ptr_ty_info.@"align",
|
||||
.@"addrspace" = new_ptr_ty_info.@"addrspace",
|
||||
.mutable = new_ptr_ty_info.mutable,
|
||||
.@"allowzero" = new_ptr_ty_info.@"allowzero",
|
||||
.@"allowzero" = new_allowzero,
|
||||
.@"volatile" = new_ptr_ty_info.@"volatile",
|
||||
.size = .One,
|
||||
});
|
||||
@ -13340,7 +13379,7 @@ fn analyzeSlice(
|
||||
.@"align" = new_ptr_ty_info.@"align",
|
||||
.@"addrspace" = new_ptr_ty_info.@"addrspace",
|
||||
.mutable = new_ptr_ty_info.mutable,
|
||||
.@"allowzero" = new_ptr_ty_info.@"allowzero",
|
||||
.@"allowzero" = new_allowzero,
|
||||
.@"volatile" = new_ptr_ty_info.@"volatile",
|
||||
.size = .Slice,
|
||||
});
|
||||
|
||||
@ -1184,6 +1184,8 @@ pub const DeclGen = struct {
|
||||
if (tv.ty.isPtrLikeOptional()) {
|
||||
if (tv.val.castTag(.opt_payload)) |payload| {
|
||||
return self.genTypedValue(.{ .ty = payload_ty, .val = payload.data });
|
||||
} else if (is_pl) {
|
||||
return self.genTypedValue(.{ .ty = payload_ty, .val = tv.val });
|
||||
} else {
|
||||
const llvm_ty = try self.llvmType(tv.ty);
|
||||
return llvm_ty.constNull();
|
||||
|
||||
87
src/type.zig
87
src/type.zig
@ -390,7 +390,7 @@ pub const Type = extern union {
|
||||
.@"addrspace" = .generic,
|
||||
.bit_offset = 0,
|
||||
.host_size = 0,
|
||||
.@"allowzero" = false,
|
||||
.@"allowzero" = true,
|
||||
.mutable = false,
|
||||
.@"volatile" = false,
|
||||
.size = .C,
|
||||
@ -402,7 +402,7 @@ pub const Type = extern union {
|
||||
.@"addrspace" = .generic,
|
||||
.bit_offset = 0,
|
||||
.host_size = 0,
|
||||
.@"allowzero" = false,
|
||||
.@"allowzero" = true,
|
||||
.mutable = true,
|
||||
.@"volatile" = false,
|
||||
.size = .C,
|
||||
@ -1153,7 +1153,7 @@ pub const Type = extern union {
|
||||
}
|
||||
if (!payload.mutable) try writer.writeAll("const ");
|
||||
if (payload.@"volatile") try writer.writeAll("volatile ");
|
||||
if (payload.@"allowzero") try writer.writeAll("allowzero ");
|
||||
if (payload.@"allowzero" and payload.size != .C) try writer.writeAll("allowzero ");
|
||||
|
||||
ty = payload.pointee_type;
|
||||
continue;
|
||||
@ -2347,7 +2347,48 @@ pub const Type = extern union {
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts that the type is an optional or a pointer that can be null.
|
||||
/// For pointer-like optionals, returns true, otherwise returns the allowzero property
|
||||
/// of pointers.
|
||||
pub fn ptrAllowsZero(ty: Type) bool {
|
||||
if (ty.isPtrLikeOptional()) {
|
||||
return true;
|
||||
}
|
||||
return ty.ptrInfo().data.@"allowzero";
|
||||
}
|
||||
|
||||
/// For pointer-like optionals, it returns the pointer type. For pointers,
|
||||
/// the type is returned unmodified.
|
||||
pub fn ptrOrOptionalPtrTy(ty: Type, buf: *Payload.ElemType) ?Type {
|
||||
if (isPtrLikeOptional(ty)) return ty.optionalChild(buf);
|
||||
switch (ty.tag()) {
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.manyptr_u8,
|
||||
.manyptr_const_u8,
|
||||
=> return ty,
|
||||
|
||||
.pointer => {
|
||||
if (ty.ptrSize() == .Slice) {
|
||||
return null;
|
||||
} else {
|
||||
return ty;
|
||||
}
|
||||
},
|
||||
|
||||
.inferred_alloc_const => unreachable,
|
||||
.inferred_alloc_mut => unreachable,
|
||||
|
||||
else => return null,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the type is optional and would be lowered to a single pointer
|
||||
/// address value, using 0 for null. Note that this returns true for C pointers.
|
||||
pub fn isPtrLikeOptional(self: Type) bool {
|
||||
switch (self.tag()) {
|
||||
.optional_single_const_pointer,
|
||||
@ -2371,7 +2412,8 @@ pub const Type = extern union {
|
||||
},
|
||||
|
||||
.pointer => return self.castTag(.pointer).?.data.size == .C,
|
||||
else => unreachable,
|
||||
|
||||
else => return false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -2532,38 +2574,50 @@ pub const Type = extern union {
|
||||
|
||||
/// Asserts that the type is an optional.
|
||||
/// Resulting `Type` will have inner memory referencing `buf`.
|
||||
pub fn optionalChild(self: Type, buf: *Payload.ElemType) Type {
|
||||
return switch (self.tag()) {
|
||||
.optional => self.castTag(.optional).?.data,
|
||||
/// Note that for C pointers this returns the type unmodified.
|
||||
pub fn optionalChild(ty: Type, buf: *Payload.ElemType) Type {
|
||||
return switch (ty.tag()) {
|
||||
.optional => ty.castTag(.optional).?.data,
|
||||
.optional_single_mut_pointer => {
|
||||
buf.* = .{
|
||||
.base = .{ .tag = .single_mut_pointer },
|
||||
.data = self.castPointer().?.data,
|
||||
.data = ty.castPointer().?.data,
|
||||
};
|
||||
return Type.initPayload(&buf.base);
|
||||
},
|
||||
.optional_single_const_pointer => {
|
||||
buf.* = .{
|
||||
.base = .{ .tag = .single_const_pointer },
|
||||
.data = self.castPointer().?.data,
|
||||
.data = ty.castPointer().?.data,
|
||||
};
|
||||
return Type.initPayload(&buf.base);
|
||||
},
|
||||
|
||||
.pointer, // here we assume it is a C pointer
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
=> return ty,
|
||||
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts that the type is an optional.
|
||||
/// Same as `optionalChild` but allocates the buffer if needed.
|
||||
pub fn optionalChildAlloc(self: Type, allocator: *Allocator) !Type {
|
||||
switch (self.tag()) {
|
||||
.optional => return self.castTag(.optional).?.data,
|
||||
pub fn optionalChildAlloc(ty: Type, allocator: *Allocator) !Type {
|
||||
switch (ty.tag()) {
|
||||
.optional => return ty.castTag(.optional).?.data,
|
||||
.optional_single_mut_pointer => {
|
||||
return Tag.single_mut_pointer.create(allocator, self.castPointer().?.data);
|
||||
return Tag.single_mut_pointer.create(allocator, ty.castPointer().?.data);
|
||||
},
|
||||
.optional_single_const_pointer => {
|
||||
return Tag.single_const_pointer.create(allocator, self.castPointer().?.data);
|
||||
return Tag.single_const_pointer.create(allocator, ty.castPointer().?.data);
|
||||
},
|
||||
.pointer, // here we assume it is a C pointer
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
=> return ty,
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
@ -4050,6 +4104,9 @@ pub const Type = extern union {
|
||||
if (d.sentinel != null or d.@"align" != 0 or d.@"addrspace" != .generic or
|
||||
d.bit_offset != 0 or d.host_size != 0 or d.@"allowzero" or d.@"volatile")
|
||||
{
|
||||
if (d.size == .C) {
|
||||
assert(d.@"allowzero"); // All C pointers must set allowzero to true.
|
||||
}
|
||||
return Type.Tag.pointer.create(arena, d);
|
||||
}
|
||||
|
||||
|
||||
@ -1819,7 +1819,8 @@ pub const Value = extern union {
|
||||
.unreachable_value => unreachable,
|
||||
.inferred_alloc => unreachable,
|
||||
.inferred_alloc_comptime => unreachable,
|
||||
else => unreachable,
|
||||
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -120,3 +120,160 @@ fn incrementVoidPtrArray(array: ?*c_void, len: usize) void {
|
||||
@ptrCast([*]u8, array.?)[n] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
test "implicitly cast indirect pointer to maybe-indirect pointer" {
|
||||
const S = struct {
|
||||
const Self = @This();
|
||||
x: u8,
|
||||
fn constConst(p: *const *const Self) u8 {
|
||||
return p.*.x;
|
||||
}
|
||||
fn maybeConstConst(p: ?*const *const Self) u8 {
|
||||
return p.?.*.x;
|
||||
}
|
||||
fn constConstConst(p: *const *const *const Self) u8 {
|
||||
return p.*.*.x;
|
||||
}
|
||||
fn maybeConstConstConst(p: ?*const *const *const Self) u8 {
|
||||
return p.?.*.*.x;
|
||||
}
|
||||
};
|
||||
const s = S{ .x = 42 };
|
||||
const p = &s;
|
||||
const q = &p;
|
||||
const r = &q;
|
||||
try expect(42 == S.constConst(q));
|
||||
try expect(42 == S.maybeConstConst(q));
|
||||
try expect(42 == S.constConstConst(r));
|
||||
try expect(42 == S.maybeConstConstConst(r));
|
||||
}
|
||||
|
||||
test "@intCast comptime_int" {
|
||||
const result = @intCast(i32, 1234);
|
||||
try expect(@TypeOf(result) == i32);
|
||||
try expect(result == 1234);
|
||||
}
|
||||
|
||||
test "@floatCast comptime_int and comptime_float" {
|
||||
{
|
||||
const result = @floatCast(f16, 1234);
|
||||
try expect(@TypeOf(result) == f16);
|
||||
try expect(result == 1234.0);
|
||||
}
|
||||
{
|
||||
const result = @floatCast(f16, 1234.0);
|
||||
try expect(@TypeOf(result) == f16);
|
||||
try expect(result == 1234.0);
|
||||
}
|
||||
{
|
||||
const result = @floatCast(f32, 1234);
|
||||
try expect(@TypeOf(result) == f32);
|
||||
try expect(result == 1234.0);
|
||||
}
|
||||
{
|
||||
const result = @floatCast(f32, 1234.0);
|
||||
try expect(@TypeOf(result) == f32);
|
||||
try expect(result == 1234.0);
|
||||
}
|
||||
}
|
||||
|
||||
test "coerce undefined to optional" {
|
||||
try expect(MakeType(void).getNull() == null);
|
||||
try expect(MakeType(void).getNonNull() != null);
|
||||
}
|
||||
|
||||
fn MakeType(comptime T: type) type {
|
||||
return struct {
|
||||
fn getNull() ?T {
|
||||
return null;
|
||||
}
|
||||
|
||||
fn getNonNull() ?T {
|
||||
return @as(T, undefined);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "implicit cast from *[N]T to [*c]T" {
|
||||
var x: [4]u16 = [4]u16{ 0, 1, 2, 3 };
|
||||
var y: [*c]u16 = &x;
|
||||
|
||||
try expect(std.mem.eql(u16, x[0..4], y[0..4]));
|
||||
x[0] = 8;
|
||||
y[3] = 6;
|
||||
try expect(std.mem.eql(u16, x[0..4], y[0..4]));
|
||||
}
|
||||
|
||||
test "*usize to *void" {
|
||||
var i = @as(usize, 0);
|
||||
var v = @ptrCast(*void, &i);
|
||||
v.* = {};
|
||||
}
|
||||
|
||||
test "compile time int to ptr of function" {
|
||||
try foobar(FUNCTION_CONSTANT);
|
||||
}
|
||||
|
||||
pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize));
|
||||
pub const PFN_void = fn (*c_void) callconv(.C) void;
|
||||
|
||||
fn foobar(func: PFN_void) !void {
|
||||
try std.testing.expect(@ptrToInt(func) == maxInt(usize));
|
||||
}
|
||||
|
||||
test "implicit ptr to *c_void" {
|
||||
var a: u32 = 1;
|
||||
var ptr: *align(@alignOf(u32)) c_void = &a;
|
||||
var b: *u32 = @ptrCast(*u32, ptr);
|
||||
try expect(b.* == 1);
|
||||
var ptr2: ?*align(@alignOf(u32)) c_void = &a;
|
||||
var c: *u32 = @ptrCast(*u32, ptr2.?);
|
||||
try expect(c.* == 1);
|
||||
}
|
||||
|
||||
test "@intToEnum passed a comptime_int to an enum with one item" {
|
||||
const E = enum { A };
|
||||
const x = @intToEnum(E, 0);
|
||||
try expect(x == E.A);
|
||||
}
|
||||
|
||||
test "@intCast to u0 and use the result" {
|
||||
const S = struct {
|
||||
fn doTheTest(zero: u1, one: u1, bigzero: i32) !void {
|
||||
try expect((one << @intCast(u0, bigzero)) == 1);
|
||||
try expect((zero << @intCast(u0, bigzero)) == 0);
|
||||
}
|
||||
};
|
||||
try S.doTheTest(0, 1, 0);
|
||||
comptime try S.doTheTest(0, 1, 0);
|
||||
}
|
||||
|
||||
test "peer result null and comptime_int" {
|
||||
const S = struct {
|
||||
fn blah(n: i32) ?i32 {
|
||||
if (n == 0) {
|
||||
return null;
|
||||
} else if (n < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try expect(S.blah(0) == null);
|
||||
comptime try expect(S.blah(0) == null);
|
||||
try expect(S.blah(10).? == 1);
|
||||
comptime try expect(S.blah(10).? == 1);
|
||||
try expect(S.blah(-10).? == -1);
|
||||
comptime try expect(S.blah(-10).? == -1);
|
||||
}
|
||||
|
||||
test "*const ?[*]const T to [*c]const [*c]const T" {
|
||||
var array = [_]u8{ 'o', 'k' };
|
||||
const opt_array_ptr: ?[*]const u8 = &array;
|
||||
const a: *const ?[*]const u8 = &opt_array_ptr;
|
||||
const b: [*c]const [*c]const u8 = a;
|
||||
try expect(b.*[0] == 'o');
|
||||
try expect(b[0][1] == 'k');
|
||||
}
|
||||
|
||||
@ -5,33 +5,6 @@ const maxInt = std.math.maxInt;
|
||||
const Vector = std.meta.Vector;
|
||||
const native_endian = @import("builtin").target.cpu.arch.endian();
|
||||
|
||||
test "implicitly cast indirect pointer to maybe-indirect pointer" {
|
||||
const S = struct {
|
||||
const Self = @This();
|
||||
x: u8,
|
||||
fn constConst(p: *const *const Self) u8 {
|
||||
return p.*.x;
|
||||
}
|
||||
fn maybeConstConst(p: ?*const *const Self) u8 {
|
||||
return p.?.*.x;
|
||||
}
|
||||
fn constConstConst(p: *const *const *const Self) u8 {
|
||||
return p.*.*.x;
|
||||
}
|
||||
fn maybeConstConstConst(p: ?*const *const *const Self) u8 {
|
||||
return p.?.*.*.x;
|
||||
}
|
||||
};
|
||||
const s = S{ .x = 42 };
|
||||
const p = &s;
|
||||
const q = &p;
|
||||
const r = &q;
|
||||
try expect(42 == S.constConst(q));
|
||||
try expect(42 == S.maybeConstConst(q));
|
||||
try expect(42 == S.constConstConst(r));
|
||||
try expect(42 == S.maybeConstConstConst(r));
|
||||
}
|
||||
|
||||
test "explicit cast from integer to error type" {
|
||||
try testCastIntToErr(error.ItBroke);
|
||||
comptime try testCastIntToErr(error.ItBroke);
|
||||
@ -175,7 +148,7 @@ fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 {
|
||||
return slice[0..1];
|
||||
}
|
||||
|
||||
test "implicit cast from &const [N]T to []const T" {
|
||||
test "implicit cast from *const [N]T to []const T" {
|
||||
try testCastConstArrayRefToConstSlice();
|
||||
comptime try testCastConstArrayRefToConstSlice();
|
||||
}
|
||||
@ -258,7 +231,7 @@ fn cast128Float(x: u128) f128 {
|
||||
return @bitCast(f128, x);
|
||||
}
|
||||
|
||||
test "single-item pointer of array to slice and to unknown length pointer" {
|
||||
test "single-item pointer of array to slice to unknown length pointer" {
|
||||
try testCastPtrOfArrayToSliceAndPtr();
|
||||
comptime try testCastPtrOfArrayToSliceAndPtr();
|
||||
}
|
||||
@ -290,35 +263,6 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
|
||||
try expect(mem.eql(u8, std.mem.spanZ(@ptrCast([*:0]const u8, x[0].?)), "window name"));
|
||||
}
|
||||
|
||||
test "@intCast comptime_int" {
|
||||
const result = @intCast(i32, 1234);
|
||||
try expect(@TypeOf(result) == i32);
|
||||
try expect(result == 1234);
|
||||
}
|
||||
|
||||
test "@floatCast comptime_int and comptime_float" {
|
||||
{
|
||||
const result = @floatCast(f16, 1234);
|
||||
try expect(@TypeOf(result) == f16);
|
||||
try expect(result == 1234.0);
|
||||
}
|
||||
{
|
||||
const result = @floatCast(f16, 1234.0);
|
||||
try expect(@TypeOf(result) == f16);
|
||||
try expect(result == 1234.0);
|
||||
}
|
||||
{
|
||||
const result = @floatCast(f32, 1234);
|
||||
try expect(@TypeOf(result) == f32);
|
||||
try expect(result == 1234.0);
|
||||
}
|
||||
{
|
||||
const result = @floatCast(f32, 1234.0);
|
||||
try expect(@TypeOf(result) == f32);
|
||||
try expect(result == 1234.0);
|
||||
}
|
||||
}
|
||||
|
||||
test "vector casts" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
@ -369,23 +313,6 @@ test "@floatCast cast down" {
|
||||
}
|
||||
}
|
||||
|
||||
test "implicit cast undefined to optional" {
|
||||
try expect(MakeType(void).getNull() == null);
|
||||
try expect(MakeType(void).getNonNull() != null);
|
||||
}
|
||||
|
||||
fn MakeType(comptime T: type) type {
|
||||
return struct {
|
||||
fn getNull() ?T {
|
||||
return null;
|
||||
}
|
||||
|
||||
fn getNonNull() ?T {
|
||||
return @as(T, undefined);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "implicit cast from *[N]T to ?[*]T" {
|
||||
var x: ?[*]u16 = null;
|
||||
var y: [4]u16 = [4]u16{ 0, 1, 2, 3 };
|
||||
@ -397,16 +324,6 @@ test "implicit cast from *[N]T to ?[*]T" {
|
||||
try expect(std.mem.eql(u16, x.?[0..4], y[0..4]));
|
||||
}
|
||||
|
||||
test "implicit cast from *[N]T to [*c]T" {
|
||||
var x: [4]u16 = [4]u16{ 0, 1, 2, 3 };
|
||||
var y: [*c]u16 = &x;
|
||||
|
||||
try expect(std.mem.eql(u16, x[0..4], y[0..4]));
|
||||
x[0] = 8;
|
||||
y[3] = 6;
|
||||
try expect(std.mem.eql(u16, x[0..4], y[0..4]));
|
||||
}
|
||||
|
||||
test "implicit cast from *T to ?*c_void" {
|
||||
var a: u8 = 1;
|
||||
incrementVoidPtrValue(&a);
|
||||
@ -417,50 +334,6 @@ fn incrementVoidPtrValue(value: ?*c_void) void {
|
||||
@ptrCast(*u8, value.?).* += 1;
|
||||
}
|
||||
|
||||
test "*usize to *void" {
|
||||
var i = @as(usize, 0);
|
||||
var v = @ptrCast(*void, &i);
|
||||
v.* = {};
|
||||
}
|
||||
|
||||
test "compile time int to ptr of function" {
|
||||
try foobar(FUNCTION_CONSTANT);
|
||||
}
|
||||
|
||||
pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize));
|
||||
pub const PFN_void = fn (*c_void) callconv(.C) void;
|
||||
|
||||
fn foobar(func: PFN_void) !void {
|
||||
try std.testing.expect(@ptrToInt(func) == maxInt(usize));
|
||||
}
|
||||
|
||||
test "implicit ptr to *c_void" {
|
||||
var a: u32 = 1;
|
||||
var ptr: *align(@alignOf(u32)) c_void = &a;
|
||||
var b: *u32 = @ptrCast(*u32, ptr);
|
||||
try expect(b.* == 1);
|
||||
var ptr2: ?*align(@alignOf(u32)) c_void = &a;
|
||||
var c: *u32 = @ptrCast(*u32, ptr2.?);
|
||||
try expect(c.* == 1);
|
||||
}
|
||||
|
||||
test "@intToEnum passed a comptime_int to an enum with one item" {
|
||||
const E = enum { A };
|
||||
const x = @intToEnum(E, 0);
|
||||
try expect(x == E.A);
|
||||
}
|
||||
|
||||
test "@intCast to u0 and use the result" {
|
||||
const S = struct {
|
||||
fn doTheTest(zero: u1, one: u1, bigzero: i32) !void {
|
||||
try expect((one << @intCast(u0, bigzero)) == 1);
|
||||
try expect((zero << @intCast(u0, bigzero)) == 0);
|
||||
}
|
||||
};
|
||||
try S.doTheTest(0, 1, 0);
|
||||
comptime try S.doTheTest(0, 1, 0);
|
||||
}
|
||||
|
||||
test "peer type resolution: unreachable, null, slice" {
|
||||
const S = struct {
|
||||
fn doTheTest(num: usize, word: []const u8) !void {
|
||||
@ -639,27 +512,6 @@ test "return u8 coercing into ?u32 return type" {
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "peer result null and comptime_int" {
|
||||
const S = struct {
|
||||
fn blah(n: i32) ?i32 {
|
||||
if (n == 0) {
|
||||
return null;
|
||||
} else if (n < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try expect(S.blah(0) == null);
|
||||
comptime try expect(S.blah(0) == null);
|
||||
try expect(S.blah(10).? == 1);
|
||||
comptime try expect(S.blah(10).? == 1);
|
||||
try expect(S.blah(-10).? == -1);
|
||||
comptime try expect(S.blah(-10).? == -1);
|
||||
}
|
||||
|
||||
test "peer type resolution implicit cast to return type" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user