Merge pull request #10152 from drew-gpf/master

C backend: fix most cast and all pointer+generics behavior tests
This commit is contained in:
Andrew Kelley 2021-11-16 19:50:39 -05:00 committed by GitHub
commit 0e8673f534
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 349 additions and 101 deletions

View File

@ -19,6 +19,7 @@ const Zir = @import("../Zir.zig");
const Liveness = @import("../Liveness.zig");
const Mutability = enum { Const, Mut };
const BigIntConst = std.math.big.int.Const;
pub const CValue = union(enum) {
none: void,
@ -226,13 +227,59 @@ pub const DeclGen = struct {
try dg.renderDeclName(decl, writer);
}
fn renderInt128(
writer: anytype,
int_val: anytype,
) error{ OutOfMemory, AnalysisFail }!void {
const int_info = @typeInfo(@TypeOf(int_val)).Int;
const is_signed = int_info.signedness == .signed;
const is_neg = int_val < 0;
comptime assert(int_info.bits > 64 and int_info.bits <= 128);
// Clang and GCC don't support 128-bit integer constants but will hopefully unfold them
// if we construct one manually.
const magnitude = std.math.absCast(int_val);
const high = @truncate(u64, magnitude >> 64);
const low = @truncate(u64, magnitude);
// (int128_t)/<->( ( (uint128_t)( val_high << 64 )u ) + (uint128_t)val_low/u )
if (is_signed) try writer.writeAll("(int128_t)");
if (is_neg) try writer.writeByte('-');
assert(high > 0);
try writer.print("(((uint128_t)0x{x}u<<64)", .{high});
if (low > 0)
try writer.print("+(uint128_t)0x{x}u", .{low});
return writer.writeByte(')');
}
fn renderBigIntConst(
dg: *DeclGen,
writer: anytype,
val: BigIntConst,
signed: bool,
) error{ OutOfMemory, AnalysisFail }!void {
if (signed) {
try renderInt128(writer, val.to(i128) catch {
return dg.fail("TODO implement integer constants larger than 128 bits", .{});
});
} else {
try renderInt128(writer, val.to(u128) catch {
return dg.fail("TODO implement integer constants larger than 128 bits", .{});
});
}
}
fn renderValue(
dg: *DeclGen,
writer: anytype,
ty: Type,
val: Value,
) error{ OutOfMemory, AnalysisFail }!void {
if (val.isUndef()) {
if (val.isUndefDeep()) {
switch (ty.zigTypeTag()) {
// Using '{}' for integer and floats seemed to error C compilers (both GCC and Clang)
// with 'error: expected expression' (including when built with 'zig cc')
@ -240,18 +287,18 @@ pub const DeclGen = struct {
const c_bits = toCIntBits(ty.intInfo(dg.module.getTarget()).bits) orelse
return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
switch (c_bits) {
8 => return writer.writeAll("0xaaU"),
16 => return writer.writeAll("0xaaaaU"),
32 => return writer.writeAll("0xaaaaaaaaU"),
64 => return writer.writeAll("0xaaaaaaaaaaaaaaaaUL"),
128 => return writer.writeAll("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaULL"),
8 => return writer.writeAll("0xaau"),
16 => return writer.writeAll("0xaaaau"),
32 => return writer.writeAll("0xaaaaaaaau"),
64 => return writer.writeAll("0xaaaaaaaaaaaaaaaau"),
128 => return renderInt128(writer, @as(u128, 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)),
else => unreachable,
}
},
.Float => {
switch (ty.floatBits(dg.module.getTarget())) {
32 => return writer.writeAll("zig_bitcast_f32_u32(0xaaaaaaaa)"),
64 => return writer.writeAll("zig_bitcast_f64_u64(0xaaaaaaaaaaaaaaaa)"),
32 => return writer.writeAll("zig_bitcast_f32_u32(0xaaaaaaaau)"),
64 => return writer.writeAll("zig_bitcast_f64_u64(0xaaaaaaaaaaaaaaaau)"),
else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}),
}
},
@ -265,10 +312,14 @@ pub const DeclGen = struct {
}
}
switch (ty.zigTypeTag()) {
.Int => {
if (ty.isSignedInt())
return writer.print("{d}", .{val.toSignedInt()});
return writer.print("{d}", .{val.toUnsignedInt()});
.Int => switch (val.tag()) {
.int_big_positive => try dg.renderBigIntConst(writer, val.castTag(.int_big_positive).?.asBigInt(), ty.isSignedInt()),
.int_big_negative => try dg.renderBigIntConst(writer, val.castTag(.int_big_negative).?.asBigInt(), true),
else => {
if (ty.isSignedInt())
return writer.print("{d}", .{val.toSignedInt()});
return writer.print("{d}u", .{val.toUnsignedInt()});
},
},
.Float => {
if (ty.floatBits(dg.module.getTarget()) <= 64) {
@ -286,8 +337,11 @@ pub const DeclGen = struct {
return dg.fail("TODO: C backend: implement lowering large float values", .{});
},
.Pointer => switch (val.tag()) {
.null_value, .zero => try writer.writeAll("NULL"),
.one => try writer.writeAll("1"),
.null_value => try writer.writeAll("NULL"),
// Technically this should produce NULL but the integer literal 0 will always coerce
// to the assigned pointer type. Note this is just a hack to fix warnings from ordered comparisons (<, >, etc)
// between pointers and 0, which is an extension to begin with.
.zero => try writer.writeByte('0'),
.decl_ref => {
const decl = val.castTag(.decl_ref).?.data;
return dg.renderDeclValue(writer, ty, val, decl);
@ -316,6 +370,11 @@ pub const DeclGen = struct {
const decl = val.castTag(.extern_fn).?.data;
try dg.renderDeclName(decl, writer);
},
.int_u64, .one => {
try writer.writeAll("((");
try dg.renderType(writer, ty);
try writer.print(")0x{x}u)", .{val.toUnsignedInt()});
},
else => unreachable,
},
.Array => {
@ -728,6 +787,8 @@ pub const DeclGen = struct {
.i32 => try w.writeAll("int32_t"),
.u64 => try w.writeAll("uint64_t"),
.i64 => try w.writeAll("int64_t"),
.u128 => try w.writeAll("uint128_t"),
.i128 => try w.writeAll("int128_t"),
.usize => try w.writeAll("uintptr_t"),
.isize => try w.writeAll("intptr_t"),
.c_short => try w.writeAll("short"),
@ -787,8 +848,9 @@ pub const DeclGen = struct {
},
.Array => {
// We are referencing the array so it will decay to a C pointer.
try dg.renderType(w, t.elemType());
return w.writeAll(" *");
// NB: arrays are not really types in C so they are either specified in the declaration
// or are already pointed to; our only job is to render the element type.
return dg.renderType(w, t.elemType());
},
.Optional => {
var opt_buf: Type.Payload.ElemType = undefined;
@ -987,7 +1049,7 @@ pub fn genDecl(o: *Object) !void {
}
try fwd_decl_writer.writeAll(";\n");
if (variable.init.isUndef()) {
if (variable.init.isUndefDeep()) {
return;
}
@ -1070,10 +1132,12 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
// TODO use a different strategy for add that communicates to the optimizer
// that wrapping is UB.
.add, .ptr_add => try airBinOp (f, inst, " + "),
.add => try airBinOp (f, inst, " + "),
.ptr_add => try airPtrAddSub (f, inst, " + "),
// TODO use a different strategy for sub that communicates to the optimizer
// that wrapping is UB.
.sub, .ptr_sub => try airBinOp (f, inst, " - "),
.sub => try airBinOp (f, inst, " - "),
.ptr_sub => try airPtrAddSub (f, inst, " - "),
// TODO use a different strategy for mul that communicates to the optimizer
// that wrapping is UB.
.mul => try airBinOp (f, inst, " * "),
@ -1187,7 +1251,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
.ptr_slice_len_ptr => try airPtrSliceFieldPtr(f, inst, ".len;\n"),
.ptr_slice_ptr_ptr => try airPtrSliceFieldPtr(f, inst, ".ptr;\n"),
.ptr_elem_val => try airPtrElemVal(f, inst, "["),
.ptr_elem_val => try airPtrElemVal(f, inst),
.ptr_elem_ptr => try airPtrElemPtr(f, inst),
.slice_elem_val => try airSliceElemVal(f, inst),
.slice_elem_ptr => try airSliceElemPtr(f, inst),
@ -1240,20 +1304,39 @@ fn airPtrSliceFieldPtr(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !
return f.fail("TODO: C backend: airPtrSliceFieldPtr", .{});
}
fn airPtrElemVal(f: *Function, inst: Air.Inst.Index, prefix: []const u8) !CValue {
const is_volatile = false; // TODO
if (!is_volatile and f.liveness.isUnused(inst))
return CValue.none;
fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
const bin_op = f.air.instructions.items(.data)[inst].bin_op;
const ptr_ty = f.air.typeOf(bin_op.lhs);
if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) return CValue.none;
_ = prefix;
return f.fail("TODO: C backend: airPtrElemVal", .{});
const ptr = try f.resolveInst(bin_op.lhs);
const index = try f.resolveInst(bin_op.rhs);
const writer = f.object.writer();
const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
try writer.writeAll(" = ");
try f.writeCValue(writer, ptr);
try writer.writeByte('[');
try f.writeCValue(writer, index);
try writer.writeAll("];\n");
return local;
}
fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
if (f.liveness.isUnused(inst))
return CValue.none;
if (f.liveness.isUnused(inst)) return CValue.none;
return f.fail("TODO: C backend: airPtrElemPtr", .{});
const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
const ptr = try f.resolveInst(bin_op.lhs);
const index = try f.resolveInst(bin_op.rhs);
const writer = f.object.writer();
const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
try writer.writeAll(" = &");
try f.writeCValue(writer, ptr);
try writer.writeByte('[');
try f.writeCValue(writer, index);
try writer.writeAll("];\n");
return local;
}
fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
@ -1317,6 +1400,10 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
const local = try f.allocLocal(elem_type, mutability);
try writer.writeAll(";\n");
// Arrays are already pointers so they don't need to be referenced.
if (elem_type.zigTypeTag() == .Array)
return CValue{ .local = local.local };
return CValue{ .local_ref = local.local };
}
@ -1344,6 +1431,8 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
if (!is_volatile and f.liveness.isUnused(inst))
return CValue.none;
const inst_ty = f.air.typeOfIndex(inst);
if (inst_ty.zigTypeTag() == .Array)
return f.fail("TODO: C backend: implement airLoad for arrays", .{});
const operand = try f.resolveInst(ty_op.operand);
const writer = f.object.writer();
const local = try f.allocLocal(inst_ty, .Const);
@ -1470,7 +1559,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
return local;
}
fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue {
fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_type: Type) !CValue {
const is_debug_build = f.object.dg.module.optimizeMode() == .Debug;
if (!is_debug_build)
return CValue.none;
@ -1494,9 +1583,11 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue {
try writer.writeAll("));\n");
},
else => {
const indirection = if (dest_type.childType().zigTypeTag() == .Array) "" else "*";
try writer.writeAll("memset(");
try f.writeCValue(writer, dest_ptr);
try writer.writeAll(", 0xaa, sizeof(*");
try writer.print(", 0xaa, sizeof({s}", .{indirection});
try f.writeCValue(writer, dest_ptr);
try writer.writeAll("));\n");
},
@ -1509,11 +1600,18 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
const bin_op = f.air.instructions.items(.data)[inst].bin_op;
const dest_ptr = try f.resolveInst(bin_op.lhs);
const src_val = try f.resolveInst(bin_op.rhs);
const lhs_type = f.air.typeOf(bin_op.lhs);
// TODO Sema should emit a different instruction when the store should
// possibly do the safety 0xaa bytes for undefined.
const src_val_is_undefined =
if (f.air.value(bin_op.rhs)) |v| v.isUndef() else false;
if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
if (src_val_is_undefined)
return try airStoreUndefined(f, dest_ptr);
return try airStoreUndefined(f, dest_ptr, lhs_type);
// Don't check this for airStoreUndefined as that will work for arrays already
if (lhs_type.childType().zigTypeTag() == .Array)
return f.fail("TODO: C backend: implement airStore for arrays", .{});
const writer = f.object.writer();
switch (dest_ptr) {
@ -1810,6 +1908,33 @@ fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue
return local;
}
fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
if (f.liveness.isUnused(inst))
return CValue.none;
const bin_op = f.air.instructions.items(.data)[inst].bin_op;
const lhs = try f.resolveInst(bin_op.lhs);
const rhs = try f.resolveInst(bin_op.rhs);
const writer = f.object.writer();
const inst_ty = f.air.typeOfIndex(inst);
const local = try f.allocLocal(inst_ty, .Const);
// We must convert to and from integer types to prevent UB if the operation results in a NULL pointer,
// or if LHS is NULL. The operation is only UB if the result is NULL and then dereferenced.
try writer.writeAll(" = (");
try f.renderType(writer, inst_ty);
try writer.writeAll(")(((uintptr_t)");
try f.writeCValue(writer, lhs);
try writer.print("){s}(", .{operator});
try f.writeCValue(writer, rhs);
try writer.writeAll("*sizeof(");
try f.renderType(writer, inst_ty.childType());
try writer.print(")));\n", .{});
return local;
}
fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
if (f.liveness.isUnused(inst)) return CValue.none;
@ -2306,15 +2431,17 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
const writer = f.object.writer();
const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data;
const field_name = struct_obj.fields.keys()[index];
const field_val = struct_obj.fields.values()[index];
const addrof = if (field_val.ty.zigTypeTag() == .Array) "" else "&";
const inst_ty = f.air.typeOfIndex(inst);
const local = try f.allocLocal(inst_ty, .Const);
switch (struct_ptr) {
.local_ref => |i| {
try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) });
try writer.print(" = {s}t{d}.{};\n", .{ addrof, i, fmtIdent(field_name) });
},
else => {
try writer.writeAll(" = &");
try writer.print(" = {s}", .{addrof});
try f.writeCValue(writer, struct_ptr);
try writer.print("->{};\n", .{fmtIdent(field_name)});
},
@ -2529,7 +2656,9 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {
const writer = f.object.writer();
const operand = try f.resolveInst(un_op);
try writer.writeAll(" = ");
try writer.writeAll(" = (");
try f.renderType(writer, inst_ty);
try writer.writeAll(")");
try f.writeCValue(writer, operand);
try writer.writeAll(";\n");
return local;

View File

@ -1078,7 +1078,7 @@ pub const DeclGen = struct {
};
return self.context.constStruct(&fields, fields.len, .False);
},
.int_u64 => {
.int_u64, .one, .int_big_positive => {
const llvm_usize = try self.llvmType(Type.usize);
const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False);
return llvm_int.constIntToPtr(try self.llvmType(tv.ty));
@ -3464,8 +3464,30 @@ pub const FuncGen = struct {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const dest_ptr = try self.resolveInst(bin_op.lhs);
const ptr_ty = self.air.typeOf(bin_op.lhs);
const src_operand = try self.resolveInst(bin_op.rhs);
self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);
// TODO Sema should emit a different instruction when the store should
// possibly do the safety 0xaa bytes for undefined.
const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;
if (val_is_undef) {
const elem_ty = ptr_ty.childType();
const target = self.dg.module.getTarget();
const elem_size = elem_ty.abiSize(target);
const u8_llvm_ty = self.context.intType(8);
const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0);
const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, ptr_u8_llvm_ty, "");
const fill_char = u8_llvm_ty.constInt(0xaa, .False);
const dest_ptr_align = ptr_ty.ptrAlignment(target);
const usize_llvm_ty = try self.dg.llvmType(Type.usize);
const len = usize_llvm_ty.constInt(elem_size, .False);
_ = self.builder.buildMemSet(dest_ptr_u8, fill_char, len, dest_ptr_align, ptr_ty.isVolatilePtr());
if (self.dg.module.comp.bin_file.options.valgrind) {
// TODO generate valgrind client request to mark byte range as undefined
// see gen_valgrind_undef() in codegen.cpp
}
} else {
const src_operand = try self.resolveInst(bin_op.rhs);
self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);
}
return null;
}
@ -3651,7 +3673,7 @@ pub const FuncGen = struct {
const dest_ptr = try self.resolveInst(pl_op.operand);
const ptr_ty = self.air.typeOf(pl_op.operand);
const value = try self.resolveInst(extra.lhs);
const val_is_undef = if (self.air.value(extra.lhs)) |val| val.isUndef() else false;
const val_is_undef = if (self.air.value(extra.lhs)) |val| val.isUndefDeep() else false;
const len = try self.resolveInst(extra.rhs);
const u8_llvm_ty = self.context.intType(8);
const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0);

View File

@ -1802,6 +1802,13 @@ pub const Value = extern union {
return self.tag() == .undef;
}
/// TODO: check for cases such as array that is not marked undef but all the element
/// values are marked undef, or struct that is not marked undef but all fields are marked
/// undef, etc.
pub fn isUndefDeep(self: Value) bool {
return self.isUndef();
}
/// Asserts the value is not undefined and not unreachable.
/// Integer value 0 is considered null because of C pointers.
pub fn isNull(self: Value) bool {

View File

@ -19,16 +19,18 @@ test {
_ = @import("behavior/bugs/4769_b.zig");
_ = @import("behavior/bugs/6850.zig");
_ = @import("behavior/call.zig");
_ = @import("behavior/cast.zig");
_ = @import("behavior/defer.zig");
_ = @import("behavior/enum.zig");
_ = @import("behavior/hasdecl.zig");
_ = @import("behavior/hasfield.zig");
_ = @import("behavior/if.zig");
_ = @import("behavior/struct.zig");
_ = @import("behavior/truncate.zig");
_ = @import("behavior/int128.zig");
_ = @import("behavior/null.zig");
_ = @import("behavior/pointers.zig");
_ = @import("behavior/ptrcast.zig");
_ = @import("behavior/pub_enum.zig");
_ = @import("behavior/struct.zig");
_ = @import("behavior/truncate.zig");
_ = @import("behavior/underscore.zig");
_ = @import("behavior/usingnamespace.zig");
@ -36,6 +38,7 @@ test {
_ = @import("behavior/this.zig");
_ = @import("behavior/member_func.zig");
_ = @import("behavior/translate_c_macros.zig");
_ = @import("behavior/generics.zig");
if (builtin.object_format != .c) {
// Tests that pass for stage1 and stage2 but not the C backend.
@ -49,18 +52,16 @@ test {
_ = @import("behavior/bugs/1741.zig");
_ = @import("behavior/bugs/2006.zig");
_ = @import("behavior/bugs/3112.zig");
_ = @import("behavior/cast.zig");
_ = @import("behavior/cast_llvm.zig");
_ = @import("behavior/error.zig");
_ = @import("behavior/eval.zig");
_ = @import("behavior/floatop.zig");
_ = @import("behavior/fn.zig");
_ = @import("behavior/for.zig");
_ = @import("behavior/generics.zig");
_ = @import("behavior/math.zig");
_ = @import("behavior/maximum_minimum.zig");
_ = @import("behavior/null_llvm.zig");
_ = @import("behavior/optional.zig");
_ = @import("behavior/pointers.zig");
_ = @import("behavior/popcount.zig");
_ = @import("behavior/saturating_arithmetic.zig");
_ = @import("behavior/sizeof_and_typeof.zig");

View File

@ -2,8 +2,6 @@ const std = @import("std");
const expect = std.testing.expect;
const mem = std.mem;
const maxInt = std.math.maxInt;
const Vector = std.meta.Vector;
const native_endian = @import("builtin").target.cpu.arch.endian();
test "int to ptr cast" {
const x = @as(usize, 13);
@ -66,18 +64,6 @@ test "implicit cast comptime_int to comptime_float" {
try expect(2 == 2.0);
}
test "pointer reinterpret const float to int" {
// The hex representation is 0x3fe3333333333303.
const float: f64 = 5.99999999999994648725e-01;
const float_ptr = &float;
const int_ptr = @ptrCast(*const i32, float_ptr);
const int_val = int_ptr.*;
if (native_endian == .Little)
try expect(int_val == 0x33333303)
else
try expect(int_val == 0x3fe33333);
}
test "comptime_int @intToFloat" {
{
const result = @intToFloat(f16, 1234);
@ -117,9 +103,6 @@ fn testFloatToInts() !void {
try expect(x == 10000);
const y = @floatToInt(i32, @as(f32, 1e4));
try expect(y == 10000);
try expectFloatToInt(f16, 255.1, u8, 255);
try expectFloatToInt(f16, 127.2, i8, 127);
try expectFloatToInt(f16, -128.2, i8, -128);
try expectFloatToInt(f32, 255.1, u8, 255);
try expectFloatToInt(f32, 127.2, i8, 127);
try expectFloatToInt(f32, -128.2, i8, -128);
@ -129,20 +112,6 @@ fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {
try expect(@floatToInt(I, f) == i);
}
test "implicit cast from [*]T to ?*c_void" {
var a = [_]u8{ 3, 2, 1 };
var runtime_zero: usize = 0;
incrementVoidPtrArray(a[runtime_zero..].ptr, 3);
try expect(std.mem.eql(u8, &a, &[_]u8{ 4, 3, 2 }));
}
fn incrementVoidPtrArray(array: ?*c_void, len: usize) void {
var n: usize = 0;
while (n < len) : (n += 1) {
@ptrCast([*]u8, array.?)[n] += 1;
}
}
test "implicitly cast indirect pointer to maybe-indirect pointer" {
const S = struct {
const Self = @This();
@ -232,27 +201,6 @@ test "*usize to *void" {
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);
@ -299,3 +247,22 @@ test "*const ?[*]const T to [*c]const [*c]const T" {
try expect(b.*[0] == 'o');
try expect(b[0][1] == 'k');
}
test "array coersion to undefined at runtime" {
@setRuntimeSafety(true);
// TODO implement @setRuntimeSafety in stage2
if (@import("builtin").zig_is_stage2 and
@import("builtin").mode != .Debug and
@import("builtin").mode != .ReleaseSafe)
{
return error.SkipZigTest;
}
var array = [4]u8{ 3, 4, 5, 6 };
var undefined_val = [4]u8{ 0xAA, 0xAA, 0xAA, 0xAA };
try expect(std.mem.eql(u8, &array, &array));
array = undefined;
try expect(std.mem.eql(u8, &array, &undefined_val));
}

View File

@ -0,0 +1,67 @@
const std = @import("std");
const expect = std.testing.expect;
const mem = std.mem;
const maxInt = std.math.maxInt;
const native_endian = @import("builtin").target.cpu.arch.endian();
test "pointer reinterpret const float to int" {
// The hex representation is 0x3fe3333333333303.
const float: f64 = 5.99999999999994648725e-01;
const float_ptr = &float;
const int_ptr = @ptrCast(*const i32, float_ptr);
const int_val = int_ptr.*;
if (native_endian == .Little)
try expect(int_val == 0x33333303)
else
try expect(int_val == 0x3fe33333);
}
test "@floatToInt" {
try testFloatToInts();
comptime try testFloatToInts();
}
fn testFloatToInts() !void {
try expectFloatToInt(f16, 255.1, u8, 255);
try expectFloatToInt(f16, 127.2, i8, 127);
try expectFloatToInt(f16, -128.2, i8, -128);
}
fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {
try expect(@floatToInt(I, f) == i);
}
test "implicit cast from [*]T to ?*c_void" {
var a = [_]u8{ 3, 2, 1 };
var runtime_zero: usize = 0;
incrementVoidPtrArray(a[runtime_zero..].ptr, 3);
try expect(std.mem.eql(u8, &a, &[_]u8{ 4, 3, 2 }));
}
fn incrementVoidPtrArray(array: ?*c_void, len: usize) void {
var n: usize = 0;
while (n < len) : (n += 1) {
@ptrCast([*]u8, array.?)[n] += 1;
}
}
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);
}

51
test/behavior/int128.zig Normal file
View File

@ -0,0 +1,51 @@
const std = @import("std");
const expect = std.testing.expect;
const maxInt = std.math.maxInt;
const minInt = std.math.minInt;
test "uint128" {
var buff: u128 = maxInt(u128);
try expect(buff == maxInt(u128));
const magic_const = 0x12341234123412341234123412341234;
buff = magic_const;
try expect(buff == magic_const);
try expect(magic_const == 0x12341234123412341234123412341234);
buff = 0;
try expect(buff == @as(u128, 0));
}
test "undefined 128 bit int" {
@setRuntimeSafety(true);
// TODO implement @setRuntimeSafety in stage2
if (@import("builtin").zig_is_stage2 and
@import("builtin").mode != .Debug and
@import("builtin").mode != .ReleaseSafe)
{
return error.SkipZigTest;
}
var undef: u128 = undefined;
var undef_signed: i128 = undefined;
try expect(undef == 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa and @bitCast(u128, undef_signed) == undef);
}
test "int128" {
var buff: i128 = -1;
try expect(buff < 0 and (buff + 1) == 0);
try expect(@intCast(i8, buff) == @as(i8, -1));
buff = minInt(i128);
try expect(buff < 0);
buff = -0x12341234123412341234123412341234;
try expect(-buff == 0x12341234123412341234123412341234);
}
test "truncate int128" {
var buff: u128 = maxInt(u128);
try expect(@truncate(u64, buff) == maxInt(u64));
}

View File

@ -61,12 +61,16 @@ test "initialize const optional C pointer to null" {
test "assigning integer to C pointer" {
var x: i32 = 0;
var y: i32 = 1;
var ptr: [*c]u8 = 0;
var ptr2: [*c]u8 = x;
if (false) {
ptr;
ptr2;
}
var ptr3: [*c]u8 = 1;
var ptr4: [*c]u8 = y;
try expect(ptr == ptr2);
try expect(ptr3 == ptr4);
try expect(ptr3 > ptr and ptr4 > ptr2 and y > x);
try expect(1 > ptr and y > ptr2 and 0 < ptr3 and x < ptr4);
}
test "C pointer comparison and arithmetic" {