mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
Merge pull request #10152 from drew-gpf/master
C backend: fix most cast and all pointer+generics behavior tests
This commit is contained in:
commit
0e8673f534
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
67
test/behavior/cast_llvm.zig
Normal file
67
test/behavior/cast_llvm.zig
Normal 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
51
test/behavior/int128.zig
Normal 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));
|
||||
}
|
||||
@ -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" {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user