authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-16 19:50:39-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-11-16 19:50:39-05:00
log0e8673f53415e367ee5db9e1384398e0905c5a35
treed7d2af6503aea7aa499c1a5eabf3c2f5a3591d3a
parent952d865bd231834adad30905c469edc5a46d000a
parent09588c795c08064971f61ee147d06972f0add94e
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10152 from drew-gpf/master

C backend: fix most cast and all pointer+generics behavior tests

8 files changed, 349 insertions(+), 101 deletions(-)

src/codegen/c.zig+165-36
......@@ -19,6 +19,7 @@ const Zir = @import("../Zir.zig");
1919const Liveness = @import("../Liveness.zig");
2020
2121const Mutability = enum { Const, Mut };
22const BigIntConst = std.math.big.int.Const;
2223
2324pub const CValue = union(enum) {
2425 none: void,
......@@ -226,13 +227,59 @@ pub const DeclGen = struct {
226227 try dg.renderDeclName(decl, writer);
227228 }
228229
230 fn renderInt128(
231 writer: anytype,
232 int_val: anytype,
233 ) error{ OutOfMemory, AnalysisFail }!void {
234 const int_info = @typeInfo(@TypeOf(int_val)).Int;
235 const is_signed = int_info.signedness == .signed;
236 const is_neg = int_val < 0;
237 comptime assert(int_info.bits > 64 and int_info.bits <= 128);
238
239 // Clang and GCC don't support 128-bit integer constants but will hopefully unfold them
240 // if we construct one manually.
241 const magnitude = std.math.absCast(int_val);
242
243 const high = @truncate(u64, magnitude >> 64);
244 const low = @truncate(u64, magnitude);
245
246 // (int128_t)/<->( ( (uint128_t)( val_high << 64 )u ) + (uint128_t)val_low/u )
247 if (is_signed) try writer.writeAll("(int128_t)");
248 if (is_neg) try writer.writeByte('-');
249
250 assert(high > 0);
251 try writer.print("(((uint128_t)0x{x}u<<64)", .{high});
252
253 if (low > 0)
254 try writer.print("+(uint128_t)0x{x}u", .{low});
255
256 return writer.writeByte(')');
257 }
258
259 fn renderBigIntConst(
260 dg: *DeclGen,
261 writer: anytype,
262 val: BigIntConst,
263 signed: bool,
264 ) error{ OutOfMemory, AnalysisFail }!void {
265 if (signed) {
266 try renderInt128(writer, val.to(i128) catch {
267 return dg.fail("TODO implement integer constants larger than 128 bits", .{});
268 });
269 } else {
270 try renderInt128(writer, val.to(u128) catch {
271 return dg.fail("TODO implement integer constants larger than 128 bits", .{});
272 });
273 }
274 }
275
229276 fn renderValue(
230277 dg: *DeclGen,
231278 writer: anytype,
232279 ty: Type,
233280 val: Value,
234281 ) error{ OutOfMemory, AnalysisFail }!void {
235 if (val.isUndef()) {
282 if (val.isUndefDeep()) {
236283 switch (ty.zigTypeTag()) {
237284 // Using '{}' for integer and floats seemed to error C compilers (both GCC and Clang)
238285 // with 'error: expected expression' (including when built with 'zig cc')
......@@ -240,18 +287,18 @@ pub const DeclGen = struct {
240287 const c_bits = toCIntBits(ty.intInfo(dg.module.getTarget()).bits) orelse
241288 return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
242289 switch (c_bits) {
243 8 => return writer.writeAll("0xaaU"),
244 16 => return writer.writeAll("0xaaaaU"),
245 32 => return writer.writeAll("0xaaaaaaaaU"),
246 64 => return writer.writeAll("0xaaaaaaaaaaaaaaaaUL"),
247 128 => return writer.writeAll("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaULL"),
290 8 => return writer.writeAll("0xaau"),
291 16 => return writer.writeAll("0xaaaau"),
292 32 => return writer.writeAll("0xaaaaaaaau"),
293 64 => return writer.writeAll("0xaaaaaaaaaaaaaaaau"),
294 128 => return renderInt128(writer, @as(u128, 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)),
248295 else => unreachable,
249296 }
250297 },
251298 .Float => {
252299 switch (ty.floatBits(dg.module.getTarget())) {
253 32 => return writer.writeAll("zig_bitcast_f32_u32(0xaaaaaaaa)"),
254 64 => return writer.writeAll("zig_bitcast_f64_u64(0xaaaaaaaaaaaaaaaa)"),
300 32 => return writer.writeAll("zig_bitcast_f32_u32(0xaaaaaaaau)"),
301 64 => return writer.writeAll("zig_bitcast_f64_u64(0xaaaaaaaaaaaaaaaau)"),
255302 else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}),
256303 }
257304 },
......@@ -265,10 +312,14 @@ pub const DeclGen = struct {
265312 }
266313 }
267314 switch (ty.zigTypeTag()) {
268 .Int => {
269 if (ty.isSignedInt())
270 return writer.print("{d}", .{val.toSignedInt()});
271 return writer.print("{d}", .{val.toUnsignedInt()});
315 .Int => switch (val.tag()) {
316 .int_big_positive => try dg.renderBigIntConst(writer, val.castTag(.int_big_positive).?.asBigInt(), ty.isSignedInt()),
317 .int_big_negative => try dg.renderBigIntConst(writer, val.castTag(.int_big_negative).?.asBigInt(), true),
318 else => {
319 if (ty.isSignedInt())
320 return writer.print("{d}", .{val.toSignedInt()});
321 return writer.print("{d}u", .{val.toUnsignedInt()});
322 },
272323 },
273324 .Float => {
274325 if (ty.floatBits(dg.module.getTarget()) <= 64) {
......@@ -286,8 +337,11 @@ pub const DeclGen = struct {
286337 return dg.fail("TODO: C backend: implement lowering large float values", .{});
287338 },
288339 .Pointer => switch (val.tag()) {
289 .null_value, .zero => try writer.writeAll("NULL"),
290 .one => try writer.writeAll("1"),
340 .null_value => try writer.writeAll("NULL"),
341 // Technically this should produce NULL but the integer literal 0 will always coerce
342 // to the assigned pointer type. Note this is just a hack to fix warnings from ordered comparisons (<, >, etc)
343 // between pointers and 0, which is an extension to begin with.
344 .zero => try writer.writeByte('0'),
291345 .decl_ref => {
292346 const decl = val.castTag(.decl_ref).?.data;
293347 return dg.renderDeclValue(writer, ty, val, decl);
......@@ -316,6 +370,11 @@ pub const DeclGen = struct {
316370 const decl = val.castTag(.extern_fn).?.data;
317371 try dg.renderDeclName(decl, writer);
318372 },
373 .int_u64, .one => {
374 try writer.writeAll("((");
375 try dg.renderType(writer, ty);
376 try writer.print(")0x{x}u)", .{val.toUnsignedInt()});
377 },
319378 else => unreachable,
320379 },
321380 .Array => {
......@@ -728,6 +787,8 @@ pub const DeclGen = struct {
728787 .i32 => try w.writeAll("int32_t"),
729788 .u64 => try w.writeAll("uint64_t"),
730789 .i64 => try w.writeAll("int64_t"),
790 .u128 => try w.writeAll("uint128_t"),
791 .i128 => try w.writeAll("int128_t"),
731792 .usize => try w.writeAll("uintptr_t"),
732793 .isize => try w.writeAll("intptr_t"),
733794 .c_short => try w.writeAll("short"),
......@@ -787,8 +848,9 @@ pub const DeclGen = struct {
787848 },
788849 .Array => {
789850 // We are referencing the array so it will decay to a C pointer.
790 try dg.renderType(w, t.elemType());
791 return w.writeAll(" *");
851 // NB: arrays are not really types in C so they are either specified in the declaration
852 // or are already pointed to; our only job is to render the element type.
853 return dg.renderType(w, t.elemType());
792854 },
793855 .Optional => {
794856 var opt_buf: Type.Payload.ElemType = undefined;
......@@ -987,7 +1049,7 @@ pub fn genDecl(o: *Object) !void {
9871049 }
9881050 try fwd_decl_writer.writeAll(";\n");
9891051
990 if (variable.init.isUndef()) {
1052 if (variable.init.isUndefDeep()) {
9911053 return;
9921054 }
9931055
......@@ -1070,10 +1132,12 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
10701132
10711133 // TODO use a different strategy for add that communicates to the optimizer
10721134 // that wrapping is UB.
1073 .add, .ptr_add => try airBinOp (f, inst, " + "),
1135 .add => try airBinOp (f, inst, " + "),
1136 .ptr_add => try airPtrAddSub (f, inst, " + "),
10741137 // TODO use a different strategy for sub that communicates to the optimizer
10751138 // that wrapping is UB.
1076 .sub, .ptr_sub => try airBinOp (f, inst, " - "),
1139 .sub => try airBinOp (f, inst, " - "),
1140 .ptr_sub => try airPtrAddSub (f, inst, " - "),
10771141 // TODO use a different strategy for mul that communicates to the optimizer
10781142 // that wrapping is UB.
10791143 .mul => try airBinOp (f, inst, " * "),
......@@ -1187,7 +1251,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
11871251 .ptr_slice_len_ptr => try airPtrSliceFieldPtr(f, inst, ".len;\n"),
11881252 .ptr_slice_ptr_ptr => try airPtrSliceFieldPtr(f, inst, ".ptr;\n"),
11891253
1190 .ptr_elem_val => try airPtrElemVal(f, inst, "["),
1254 .ptr_elem_val => try airPtrElemVal(f, inst),
11911255 .ptr_elem_ptr => try airPtrElemPtr(f, inst),
11921256 .slice_elem_val => try airSliceElemVal(f, inst),
11931257 .slice_elem_ptr => try airSliceElemPtr(f, inst),
......@@ -1240,20 +1304,39 @@ fn airPtrSliceFieldPtr(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !
12401304 return f.fail("TODO: C backend: airPtrSliceFieldPtr", .{});
12411305}
12421306
1243fn airPtrElemVal(f: *Function, inst: Air.Inst.Index, prefix: []const u8) !CValue {
1244 const is_volatile = false; // TODO
1245 if (!is_volatile and f.liveness.isUnused(inst))
1246 return CValue.none;
1307fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
1308 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1309 const ptr_ty = f.air.typeOf(bin_op.lhs);
1310 if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) return CValue.none;
12471311
1248 _ = prefix;
1249 return f.fail("TODO: C backend: airPtrElemVal", .{});
1312 const ptr = try f.resolveInst(bin_op.lhs);
1313 const index = try f.resolveInst(bin_op.rhs);
1314 const writer = f.object.writer();
1315 const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
1316 try writer.writeAll(" = ");
1317 try f.writeCValue(writer, ptr);
1318 try writer.writeByte('[');
1319 try f.writeCValue(writer, index);
1320 try writer.writeAll("];\n");
1321 return local;
12501322}
12511323
12521324fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
1253 if (f.liveness.isUnused(inst))
1254 return CValue.none;
1325 if (f.liveness.isUnused(inst)) return CValue.none;
12551326
1256 return f.fail("TODO: C backend: airPtrElemPtr", .{});
1327 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
1328 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
1329
1330 const ptr = try f.resolveInst(bin_op.lhs);
1331 const index = try f.resolveInst(bin_op.rhs);
1332 const writer = f.object.writer();
1333 const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
1334 try writer.writeAll(" = &");
1335 try f.writeCValue(writer, ptr);
1336 try writer.writeByte('[');
1337 try f.writeCValue(writer, index);
1338 try writer.writeAll("];\n");
1339 return local;
12571340}
12581341
12591342fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
......@@ -1317,6 +1400,10 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
13171400 const local = try f.allocLocal(elem_type, mutability);
13181401 try writer.writeAll(";\n");
13191402
1403 // Arrays are already pointers so they don't need to be referenced.
1404 if (elem_type.zigTypeTag() == .Array)
1405 return CValue{ .local = local.local };
1406
13201407 return CValue{ .local_ref = local.local };
13211408}
13221409
......@@ -1344,6 +1431,8 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
13441431 if (!is_volatile and f.liveness.isUnused(inst))
13451432 return CValue.none;
13461433 const inst_ty = f.air.typeOfIndex(inst);
1434 if (inst_ty.zigTypeTag() == .Array)
1435 return f.fail("TODO: C backend: implement airLoad for arrays", .{});
13471436 const operand = try f.resolveInst(ty_op.operand);
13481437 const writer = f.object.writer();
13491438 const local = try f.allocLocal(inst_ty, .Const);
......@@ -1470,7 +1559,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
14701559 return local;
14711560}
14721561
1473fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue {
1562fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_type: Type) !CValue {
14741563 const is_debug_build = f.object.dg.module.optimizeMode() == .Debug;
14751564 if (!is_debug_build)
14761565 return CValue.none;
......@@ -1494,9 +1583,11 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue {
14941583 try writer.writeAll("));\n");
14951584 },
14961585 else => {
1586 const indirection = if (dest_type.childType().zigTypeTag() == .Array) "" else "*";
1587
14971588 try writer.writeAll("memset(");
14981589 try f.writeCValue(writer, dest_ptr);
1499 try writer.writeAll(", 0xaa, sizeof(*");
1590 try writer.print(", 0xaa, sizeof({s}", .{indirection});
15001591 try f.writeCValue(writer, dest_ptr);
15011592 try writer.writeAll("));\n");
15021593 },
......@@ -1509,11 +1600,18 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
15091600 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
15101601 const dest_ptr = try f.resolveInst(bin_op.lhs);
15111602 const src_val = try f.resolveInst(bin_op.rhs);
1603 const lhs_type = f.air.typeOf(bin_op.lhs);
15121604
1605 // TODO Sema should emit a different instruction when the store should
1606 // possibly do the safety 0xaa bytes for undefined.
15131607 const src_val_is_undefined =
1514 if (f.air.value(bin_op.rhs)) |v| v.isUndef() else false;
1608 if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
15151609 if (src_val_is_undefined)
1516 return try airStoreUndefined(f, dest_ptr);
1610 return try airStoreUndefined(f, dest_ptr, lhs_type);
1611
1612 // Don't check this for airStoreUndefined as that will work for arrays already
1613 if (lhs_type.childType().zigTypeTag() == .Array)
1614 return f.fail("TODO: C backend: implement airStore for arrays", .{});
15171615
15181616 const writer = f.object.writer();
15191617 switch (dest_ptr) {
......@@ -1810,6 +1908,33 @@ fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue
18101908 return local;
18111909}
18121910
1911fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
1912 if (f.liveness.isUnused(inst))
1913 return CValue.none;
1914
1915 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1916 const lhs = try f.resolveInst(bin_op.lhs);
1917 const rhs = try f.resolveInst(bin_op.rhs);
1918
1919 const writer = f.object.writer();
1920 const inst_ty = f.air.typeOfIndex(inst);
1921 const local = try f.allocLocal(inst_ty, .Const);
1922
1923 // We must convert to and from integer types to prevent UB if the operation results in a NULL pointer,
1924 // or if LHS is NULL. The operation is only UB if the result is NULL and then dereferenced.
1925 try writer.writeAll(" = (");
1926 try f.renderType(writer, inst_ty);
1927 try writer.writeAll(")(((uintptr_t)");
1928 try f.writeCValue(writer, lhs);
1929 try writer.print("){s}(", .{operator});
1930 try f.writeCValue(writer, rhs);
1931 try writer.writeAll("*sizeof(");
1932 try f.renderType(writer, inst_ty.childType());
1933 try writer.print(")));\n", .{});
1934
1935 return local;
1936}
1937
18131938fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
18141939 if (f.liveness.isUnused(inst)) return CValue.none;
18151940
......@@ -2306,15 +2431,17 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
23062431 const writer = f.object.writer();
23072432 const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data;
23082433 const field_name = struct_obj.fields.keys()[index];
2434 const field_val = struct_obj.fields.values()[index];
2435 const addrof = if (field_val.ty.zigTypeTag() == .Array) "" else "&";
23092436
23102437 const inst_ty = f.air.typeOfIndex(inst);
23112438 const local = try f.allocLocal(inst_ty, .Const);
23122439 switch (struct_ptr) {
23132440 .local_ref => |i| {
2314 try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) });
2441 try writer.print(" = {s}t{d}.{};\n", .{ addrof, i, fmtIdent(field_name) });
23152442 },
23162443 else => {
2317 try writer.writeAll(" = &");
2444 try writer.print(" = {s}", .{addrof});
23182445 try f.writeCValue(writer, struct_ptr);
23192446 try writer.print("->{};\n", .{fmtIdent(field_name)});
23202447 },
......@@ -2529,7 +2656,9 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {
25292656 const writer = f.object.writer();
25302657 const operand = try f.resolveInst(un_op);
25312658
2532 try writer.writeAll(" = ");
2659 try writer.writeAll(" = (");
2660 try f.renderType(writer, inst_ty);
2661 try writer.writeAll(")");
25332662 try f.writeCValue(writer, operand);
25342663 try writer.writeAll(";\n");
25352664 return local;
src/codegen/llvm.zig+26-4
......@@ -1078,7 +1078,7 @@ pub const DeclGen = struct {
10781078 };
10791079 return self.context.constStruct(&fields, fields.len, .False);
10801080 },
1081 .int_u64 => {
1081 .int_u64, .one, .int_big_positive => {
10821082 const llvm_usize = try self.llvmType(Type.usize);
10831083 const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False);
10841084 return llvm_int.constIntToPtr(try self.llvmType(tv.ty));
......@@ -3464,8 +3464,30 @@ pub const FuncGen = struct {
34643464 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
34653465 const dest_ptr = try self.resolveInst(bin_op.lhs);
34663466 const ptr_ty = self.air.typeOf(bin_op.lhs);
3467 const src_operand = try self.resolveInst(bin_op.rhs);
3468 self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);
3467
3468 // TODO Sema should emit a different instruction when the store should
3469 // possibly do the safety 0xaa bytes for undefined.
3470 const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;
3471 if (val_is_undef) {
3472 const elem_ty = ptr_ty.childType();
3473 const target = self.dg.module.getTarget();
3474 const elem_size = elem_ty.abiSize(target);
3475 const u8_llvm_ty = self.context.intType(8);
3476 const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0);
3477 const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, ptr_u8_llvm_ty, "");
3478 const fill_char = u8_llvm_ty.constInt(0xaa, .False);
3479 const dest_ptr_align = ptr_ty.ptrAlignment(target);
3480 const usize_llvm_ty = try self.dg.llvmType(Type.usize);
3481 const len = usize_llvm_ty.constInt(elem_size, .False);
3482 _ = self.builder.buildMemSet(dest_ptr_u8, fill_char, len, dest_ptr_align, ptr_ty.isVolatilePtr());
3483 if (self.dg.module.comp.bin_file.options.valgrind) {
3484 // TODO generate valgrind client request to mark byte range as undefined
3485 // see gen_valgrind_undef() in codegen.cpp
3486 }
3487 } else {
3488 const src_operand = try self.resolveInst(bin_op.rhs);
3489 self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);
3490 }
34693491 return null;
34703492 }
34713493
......@@ -3651,7 +3673,7 @@ pub const FuncGen = struct {
36513673 const dest_ptr = try self.resolveInst(pl_op.operand);
36523674 const ptr_ty = self.air.typeOf(pl_op.operand);
36533675 const value = try self.resolveInst(extra.lhs);
3654 const val_is_undef = if (self.air.value(extra.lhs)) |val| val.isUndef() else false;
3676 const val_is_undef = if (self.air.value(extra.lhs)) |val| val.isUndefDeep() else false;
36553677 const len = try self.resolveInst(extra.rhs);
36563678 const u8_llvm_ty = self.context.intType(8);
36573679 const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0);
src/value.zig+7
......@@ -1802,6 +1802,13 @@ pub const Value = extern union {
18021802 return self.tag() == .undef;
18031803 }
18041804
1805 /// TODO: check for cases such as array that is not marked undef but all the element
1806 /// values are marked undef, or struct that is not marked undef but all fields are marked
1807 /// undef, etc.
1808 pub fn isUndefDeep(self: Value) bool {
1809 return self.isUndef();
1810 }
1811
18051812 /// Asserts the value is not undefined and not unreachable.
18061813 /// Integer value 0 is considered null because of C pointers.
18071814 pub fn isNull(self: Value) bool {
test/behavior.zig+6-5
......@@ -19,16 +19,18 @@ test {
1919 _ = @import("behavior/bugs/4769_b.zig");
2020 _ = @import("behavior/bugs/6850.zig");
2121 _ = @import("behavior/call.zig");
22 _ = @import("behavior/cast.zig");
2223 _ = @import("behavior/defer.zig");
2324 _ = @import("behavior/enum.zig");
2425 _ = @import("behavior/hasdecl.zig");
2526 _ = @import("behavior/hasfield.zig");
2627 _ = @import("behavior/if.zig");
27 _ = @import("behavior/struct.zig");
28 _ = @import("behavior/truncate.zig");
28 _ = @import("behavior/int128.zig");
2929 _ = @import("behavior/null.zig");
30 _ = @import("behavior/pointers.zig");
3031 _ = @import("behavior/ptrcast.zig");
3132 _ = @import("behavior/pub_enum.zig");
33 _ = @import("behavior/struct.zig");
3234 _ = @import("behavior/truncate.zig");
3335 _ = @import("behavior/underscore.zig");
3436 _ = @import("behavior/usingnamespace.zig");
......@@ -36,6 +38,7 @@ test {
3638 _ = @import("behavior/this.zig");
3739 _ = @import("behavior/member_func.zig");
3840 _ = @import("behavior/translate_c_macros.zig");
41 _ = @import("behavior/generics.zig");
3942
4043 if (builtin.object_format != .c) {
4144 // Tests that pass for stage1 and stage2 but not the C backend.
......@@ -49,18 +52,16 @@ test {
4952 _ = @import("behavior/bugs/1741.zig");
5053 _ = @import("behavior/bugs/2006.zig");
5154 _ = @import("behavior/bugs/3112.zig");
52 _ = @import("behavior/cast.zig");
55 _ = @import("behavior/cast_llvm.zig");
5356 _ = @import("behavior/error.zig");
5457 _ = @import("behavior/eval.zig");
5558 _ = @import("behavior/floatop.zig");
5659 _ = @import("behavior/fn.zig");
5760 _ = @import("behavior/for.zig");
58 _ = @import("behavior/generics.zig");
5961 _ = @import("behavior/math.zig");
6062 _ = @import("behavior/maximum_minimum.zig");
6163 _ = @import("behavior/null_llvm.zig");
6264 _ = @import("behavior/optional.zig");
63 _ = @import("behavior/pointers.zig");
6465 _ = @import("behavior/popcount.zig");
6566 _ = @import("behavior/saturating_arithmetic.zig");
6667 _ = @import("behavior/sizeof_and_typeof.zig");
test/behavior/cast.zig+19-52
......@@ -2,8 +2,6 @@ const std = @import("std");
22const expect = std.testing.expect;
33const mem = std.mem;
44const maxInt = std.math.maxInt;
5const Vector = std.meta.Vector;
6const native_endian = @import("builtin").target.cpu.arch.endian();
75
86test "int to ptr cast" {
97 const x = @as(usize, 13);
......@@ -66,18 +64,6 @@ test "implicit cast comptime_int to comptime_float" {
6664 try expect(2 == 2.0);
6765}
6866
69test "pointer reinterpret const float to int" {
70 // The hex representation is 0x3fe3333333333303.
71 const float: f64 = 5.99999999999994648725e-01;
72 const float_ptr = &float;
73 const int_ptr = @ptrCast(*const i32, float_ptr);
74 const int_val = int_ptr.*;
75 if (native_endian == .Little)
76 try expect(int_val == 0x33333303)
77 else
78 try expect(int_val == 0x3fe33333);
79}
80
8167test "comptime_int @intToFloat" {
8268 {
8369 const result = @intToFloat(f16, 1234);
......@@ -117,9 +103,6 @@ fn testFloatToInts() !void {
117103 try expect(x == 10000);
118104 const y = @floatToInt(i32, @as(f32, 1e4));
119105 try expect(y == 10000);
120 try expectFloatToInt(f16, 255.1, u8, 255);
121 try expectFloatToInt(f16, 127.2, i8, 127);
122 try expectFloatToInt(f16, -128.2, i8, -128);
123106 try expectFloatToInt(f32, 255.1, u8, 255);
124107 try expectFloatToInt(f32, 127.2, i8, 127);
125108 try expectFloatToInt(f32, -128.2, i8, -128);
......@@ -129,20 +112,6 @@ fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {
129112 try expect(@floatToInt(I, f) == i);
130113}
131114
132test "implicit cast from [*]T to ?*c_void" {
133 var a = [_]u8{ 3, 2, 1 };
134 var runtime_zero: usize = 0;
135 incrementVoidPtrArray(a[runtime_zero..].ptr, 3);
136 try expect(std.mem.eql(u8, &a, &[_]u8{ 4, 3, 2 }));
137}
138
139fn incrementVoidPtrArray(array: ?*c_void, len: usize) void {
140 var n: usize = 0;
141 while (n < len) : (n += 1) {
142 @ptrCast([*]u8, array.?)[n] += 1;
143 }
144}
145
146115test "implicitly cast indirect pointer to maybe-indirect pointer" {
147116 const S = struct {
148117 const Self = @This();
......@@ -232,27 +201,6 @@ test "*usize to *void" {
232201 v.* = {};
233202}
234203
235test "compile time int to ptr of function" {
236 try foobar(FUNCTION_CONSTANT);
237}
238
239pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize));
240pub const PFN_void = fn (*c_void) callconv(.C) void;
241
242fn foobar(func: PFN_void) !void {
243 try std.testing.expect(@ptrToInt(func) == maxInt(usize));
244}
245
246test "implicit ptr to *c_void" {
247 var a: u32 = 1;
248 var ptr: *align(@alignOf(u32)) c_void = &a;
249 var b: *u32 = @ptrCast(*u32, ptr);
250 try expect(b.* == 1);
251 var ptr2: ?*align(@alignOf(u32)) c_void = &a;
252 var c: *u32 = @ptrCast(*u32, ptr2.?);
253 try expect(c.* == 1);
254}
255
256204test "@intToEnum passed a comptime_int to an enum with one item" {
257205 const E = enum { A };
258206 const x = @intToEnum(E, 0);
......@@ -299,3 +247,22 @@ test "*const ?[*]const T to [*c]const [*c]const T" {
299247 try expect(b.*[0] == 'o');
300248 try expect(b[0][1] == 'k');
301249}
250
251test "array coersion to undefined at runtime" {
252 @setRuntimeSafety(true);
253
254 // TODO implement @setRuntimeSafety in stage2
255 if (@import("builtin").zig_is_stage2 and
256 @import("builtin").mode != .Debug and
257 @import("builtin").mode != .ReleaseSafe)
258 {
259 return error.SkipZigTest;
260 }
261
262 var array = [4]u8{ 3, 4, 5, 6 };
263 var undefined_val = [4]u8{ 0xAA, 0xAA, 0xAA, 0xAA };
264
265 try expect(std.mem.eql(u8, &array, &array));
266 array = undefined;
267 try expect(std.mem.eql(u8, &array, &undefined_val));
268}
test/behavior/cast_llvm.zig created+67
......@@ -0,0 +1,67 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const mem = std.mem;
4const maxInt = std.math.maxInt;
5const native_endian = @import("builtin").target.cpu.arch.endian();
6
7test "pointer reinterpret const float to int" {
8 // The hex representation is 0x3fe3333333333303.
9 const float: f64 = 5.99999999999994648725e-01;
10 const float_ptr = &float;
11 const int_ptr = @ptrCast(*const i32, float_ptr);
12 const int_val = int_ptr.*;
13 if (native_endian == .Little)
14 try expect(int_val == 0x33333303)
15 else
16 try expect(int_val == 0x3fe33333);
17}
18
19test "@floatToInt" {
20 try testFloatToInts();
21 comptime try testFloatToInts();
22}
23
24fn testFloatToInts() !void {
25 try expectFloatToInt(f16, 255.1, u8, 255);
26 try expectFloatToInt(f16, 127.2, i8, 127);
27 try expectFloatToInt(f16, -128.2, i8, -128);
28}
29
30fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {
31 try expect(@floatToInt(I, f) == i);
32}
33
34test "implicit cast from [*]T to ?*c_void" {
35 var a = [_]u8{ 3, 2, 1 };
36 var runtime_zero: usize = 0;
37 incrementVoidPtrArray(a[runtime_zero..].ptr, 3);
38 try expect(std.mem.eql(u8, &a, &[_]u8{ 4, 3, 2 }));
39}
40
41fn incrementVoidPtrArray(array: ?*c_void, len: usize) void {
42 var n: usize = 0;
43 while (n < len) : (n += 1) {
44 @ptrCast([*]u8, array.?)[n] += 1;
45 }
46}
47
48test "compile time int to ptr of function" {
49 try foobar(FUNCTION_CONSTANT);
50}
51
52pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize));
53pub const PFN_void = fn (*c_void) callconv(.C) void;
54
55fn foobar(func: PFN_void) !void {
56 try std.testing.expect(@ptrToInt(func) == maxInt(usize));
57}
58
59test "implicit ptr to *c_void" {
60 var a: u32 = 1;
61 var ptr: *align(@alignOf(u32)) c_void = &a;
62 var b: *u32 = @ptrCast(*u32, ptr);
63 try expect(b.* == 1);
64 var ptr2: ?*align(@alignOf(u32)) c_void = &a;
65 var c: *u32 = @ptrCast(*u32, ptr2.?);
66 try expect(c.* == 1);
67}
test/behavior/int128.zig created+51
......@@ -0,0 +1,51 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const maxInt = std.math.maxInt;
4const minInt = std.math.minInt;
5
6test "uint128" {
7 var buff: u128 = maxInt(u128);
8 try expect(buff == maxInt(u128));
9
10 const magic_const = 0x12341234123412341234123412341234;
11 buff = magic_const;
12
13 try expect(buff == magic_const);
14 try expect(magic_const == 0x12341234123412341234123412341234);
15
16 buff = 0;
17 try expect(buff == @as(u128, 0));
18}
19
20test "undefined 128 bit int" {
21 @setRuntimeSafety(true);
22
23 // TODO implement @setRuntimeSafety in stage2
24 if (@import("builtin").zig_is_stage2 and
25 @import("builtin").mode != .Debug and
26 @import("builtin").mode != .ReleaseSafe)
27 {
28 return error.SkipZigTest;
29 }
30
31 var undef: u128 = undefined;
32 var undef_signed: i128 = undefined;
33 try expect(undef == 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa and @bitCast(u128, undef_signed) == undef);
34}
35
36test "int128" {
37 var buff: i128 = -1;
38 try expect(buff < 0 and (buff + 1) == 0);
39 try expect(@intCast(i8, buff) == @as(i8, -1));
40
41 buff = minInt(i128);
42 try expect(buff < 0);
43
44 buff = -0x12341234123412341234123412341234;
45 try expect(-buff == 0x12341234123412341234123412341234);
46}
47
48test "truncate int128" {
49 var buff: u128 = maxInt(u128);
50 try expect(@truncate(u64, buff) == maxInt(u64));
51}
test/behavior/pointers.zig+8-4
......@@ -61,12 +61,16 @@ test "initialize const optional C pointer to null" {
6161
6262test "assigning integer to C pointer" {
6363 var x: i32 = 0;
64 var y: i32 = 1;
6465 var ptr: [*c]u8 = 0;
6566 var ptr2: [*c]u8 = x;
66 if (false) {
67 ptr;
68 ptr2;
69 }
67 var ptr3: [*c]u8 = 1;
68 var ptr4: [*c]u8 = y;
69
70 try expect(ptr == ptr2);
71 try expect(ptr3 == ptr4);
72 try expect(ptr3 > ptr and ptr4 > ptr2 and y > x);
73 try expect(1 > ptr and y > ptr2 and 0 < ptr3 and x < ptr4);
7074}
7175
7276test "C pointer comparison and arithmetic" {