| author | |
| committer | |
| log | 9bf1681990fe87a6b2e5fc644a89f1aece304579 |
| tree | 45617b8500c741e8de902852c0495b5c20279dbf |
| parent | 952d865bd231834adad30905c469edc5a46d000a |
-add additional test cases that were found to be passing
-add basic int128 test cases which previously did not pass but weren't covered
-most test cases in cast.zig now pass
-i128/u128 or smaller int constants can now be rendered
-unsigned int constants are now always suffixed with 'u' to prevent random compile errors
-pointers with a val tag of 'zero' now just emit a 0 constant which coerces to the pointer type and fixes some warnings with ordered comparisons
-pointers with a val tag of 'one' are now casted back to the pointer type
-support pointers with a u64 val
-fix bug where rendering an array's type will emit more indirection than is needed
-render uint128_t/int128_t manually when needed
-implement ptr_add/sub AIR handlers manually so they manually cast to int types which avoids UB if the result or ptr operand is NULL
-implement airPtrElemVal/Ptr
-airAlloc for arrays will not allocate a ref as the local for the array is already a reference/pointer to the array itself
-fix airPtrToInt by casting to the int type6 files changed, 443 insertions(+), 270 deletions(-)
src/codegen/c.zig+138-28| ... | @@ -226,6 +226,36 @@ pub const DeclGen = struct { | ... | @@ -226,6 +226,36 @@ pub const DeclGen = struct { |
| 226 | try dg.renderDeclName(decl, writer); | 226 | try dg.renderDeclName(decl, writer); |
| 227 | } | 227 | } |
| 228 | 228 | ||
| 229 | /// Assumes that int_val is an int greater than maxInt(u64) and has > 64 and <= 128 bits. | ||
| 230 | fn renderBigInt( | ||
| 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 | |||
| 229 | fn renderValue( | 259 | fn renderValue( |
| 230 | dg: *DeclGen, | 260 | dg: *DeclGen, |
| 231 | writer: anytype, | 261 | writer: anytype, |
| ... | @@ -240,18 +270,18 @@ pub const DeclGen = struct { | ... | @@ -240,18 +270,18 @@ pub const DeclGen = struct { |
| 240 | const c_bits = toCIntBits(ty.intInfo(dg.module.getTarget()).bits) orelse | 270 | const c_bits = toCIntBits(ty.intInfo(dg.module.getTarget()).bits) orelse |
| 241 | return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); | 271 | return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); |
| 242 | switch (c_bits) { | 272 | switch (c_bits) { |
| 243 | 8 => return writer.writeAll("0xaaU"), | 273 | 8 => return writer.writeAll("0xaau"), |
| 244 | 16 => return writer.writeAll("0xaaaaU"), | 274 | 16 => return writer.writeAll("0xaaaau"), |
| 245 | 32 => return writer.writeAll("0xaaaaaaaaU"), | 275 | 32 => return writer.writeAll("0xaaaaaaaau"), |
| 246 | 64 => return writer.writeAll("0xaaaaaaaaaaaaaaaaUL"), | 276 | 64 => return writer.writeAll("0xaaaaaaaaaaaaaaaau"), |
| 247 | 128 => return writer.writeAll("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaULL"), | 277 | 128 => return renderBigInt(writer, @as(u128, 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)), |
| 248 | else => unreachable, | 278 | else => unreachable, |
| 249 | } | 279 | } |
| 250 | }, | 280 | }, |
| 251 | .Float => { | 281 | .Float => { |
| 252 | switch (ty.floatBits(dg.module.getTarget())) { | 282 | switch (ty.floatBits(dg.module.getTarget())) { |
| 253 | 32 => return writer.writeAll("zig_bitcast_f32_u32(0xaaaaaaaa)"), | 283 | 32 => return writer.writeAll("zig_bitcast_f32_u32(0xaaaaaaaau)"), |
| 254 | 64 => return writer.writeAll("zig_bitcast_f64_u64(0xaaaaaaaaaaaaaaaa)"), | 284 | 64 => return writer.writeAll("zig_bitcast_f64_u64(0xaaaaaaaaaaaaaaaau)"), |
| 255 | else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}), | 285 | else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}), |
| 256 | } | 286 | } |
| 257 | }, | 287 | }, |
| ... | @@ -265,10 +295,18 @@ pub const DeclGen = struct { | ... | @@ -265,10 +295,18 @@ pub const DeclGen = struct { |
| 265 | } | 295 | } |
| 266 | } | 296 | } |
| 267 | switch (ty.zigTypeTag()) { | 297 | switch (ty.zigTypeTag()) { |
| 268 | .Int => { | 298 | .Int => switch (val.tag()) { |
| 269 | if (ty.isSignedInt()) | 299 | .int_big_positive => try renderBigInt(writer, val.castTag(.int_big_positive).?.asBigInt().to(u128) catch { |
| 270 | return writer.print("{d}", .{val.toSignedInt()}); | 300 | return dg.fail("TODO implement integer constants larger than 128 bits", .{}); |
| 271 | return writer.print("{d}", .{val.toUnsignedInt()}); | 301 | }), |
| 302 | .int_big_negative => try renderBigInt(writer, val.castTag(.int_big_negative).?.asBigInt().to(i128) catch { | ||
| 303 | return dg.fail("TODO implement integer constants larger than 128 bits", .{}); | ||
| 304 | }), | ||
| 305 | else => { | ||
| 306 | if (ty.isSignedInt()) | ||
| 307 | return writer.print("{d}", .{val.toSignedInt()}); | ||
| 308 | return writer.print("{d}u", .{val.toUnsignedInt()}); | ||
| 309 | } | ||
| 272 | }, | 310 | }, |
| 273 | .Float => { | 311 | .Float => { |
| 274 | if (ty.floatBits(dg.module.getTarget()) <= 64) { | 312 | if (ty.floatBits(dg.module.getTarget()) <= 64) { |
| ... | @@ -286,8 +324,17 @@ pub const DeclGen = struct { | ... | @@ -286,8 +324,17 @@ pub const DeclGen = struct { |
| 286 | return dg.fail("TODO: C backend: implement lowering large float values", .{}); | 324 | return dg.fail("TODO: C backend: implement lowering large float values", .{}); |
| 287 | }, | 325 | }, |
| 288 | .Pointer => switch (val.tag()) { | 326 | .Pointer => switch (val.tag()) { |
| 289 | .null_value, .zero => try writer.writeAll("NULL"), | 327 | .null_value => try writer.writeAll("NULL"), |
| 290 | .one => try writer.writeAll("1"), | 328 | // Technically this should produce NULL but the integer literal 0 will always coerce |
| 329 | // to the assigned pointer type. Note this is just a hack to fix warnings from ordered comparisons (<, >, etc) | ||
| 330 | // between pointers and 0, which is an extension to begin with. | ||
| 331 | .zero => try writer.writeByte('0'), | ||
| 332 | .one => { | ||
| 333 | // int constants like 1 will not cast to the pointer however. | ||
| 334 | try writer.writeAll("(("); | ||
| 335 | try dg.renderType(writer, ty); | ||
| 336 | return writer.writeAll(")1)"); | ||
| 337 | }, | ||
| 291 | .decl_ref => { | 338 | .decl_ref => { |
| 292 | const decl = val.castTag(.decl_ref).?.data; | 339 | const decl = val.castTag(.decl_ref).?.data; |
| 293 | return dg.renderDeclValue(writer, ty, val, decl); | 340 | return dg.renderDeclValue(writer, ty, val, decl); |
| ... | @@ -316,6 +363,11 @@ pub const DeclGen = struct { | ... | @@ -316,6 +363,11 @@ pub const DeclGen = struct { |
| 316 | const decl = val.castTag(.extern_fn).?.data; | 363 | const decl = val.castTag(.extern_fn).?.data; |
| 317 | try dg.renderDeclName(decl, writer); | 364 | try dg.renderDeclName(decl, writer); |
| 318 | }, | 365 | }, |
| 366 | .int_u64 => { | ||
| 367 | try writer.writeAll("(("); | ||
| 368 | try dg.renderType(writer, ty); | ||
| 369 | try writer.print(")0x{x}u)", .{val.toUnsignedInt()}); | ||
| 370 | }, | ||
| 319 | else => unreachable, | 371 | else => unreachable, |
| 320 | }, | 372 | }, |
| 321 | .Array => { | 373 | .Array => { |
| ... | @@ -728,6 +780,8 @@ pub const DeclGen = struct { | ... | @@ -728,6 +780,8 @@ pub const DeclGen = struct { |
| 728 | .i32 => try w.writeAll("int32_t"), | 780 | .i32 => try w.writeAll("int32_t"), |
| 729 | .u64 => try w.writeAll("uint64_t"), | 781 | .u64 => try w.writeAll("uint64_t"), |
| 730 | .i64 => try w.writeAll("int64_t"), | 782 | .i64 => try w.writeAll("int64_t"), |
| 783 | .u128 => try w.writeAll("uint128_t"), | ||
| 784 | .i128 => try w.writeAll("int128_t"), | ||
| 731 | .usize => try w.writeAll("uintptr_t"), | 785 | .usize => try w.writeAll("uintptr_t"), |
| 732 | .isize => try w.writeAll("intptr_t"), | 786 | .isize => try w.writeAll("intptr_t"), |
| 733 | .c_short => try w.writeAll("short"), | 787 | .c_short => try w.writeAll("short"), |
| ... | @@ -787,8 +841,9 @@ pub const DeclGen = struct { | ... | @@ -787,8 +841,9 @@ pub const DeclGen = struct { |
| 787 | }, | 841 | }, |
| 788 | .Array => { | 842 | .Array => { |
| 789 | // We are referencing the array so it will decay to a C pointer. | 843 | // We are referencing the array so it will decay to a C pointer. |
| 790 | try dg.renderType(w, t.elemType()); | 844 | // NB: arrays are not really types in C so they are either specified in the declaration |
| 791 | return w.writeAll(" *"); | 845 | // or are already pointed to; our only job is to render the element's type. |
| 846 | return dg.renderType(w, t.elemType()); | ||
| 792 | }, | 847 | }, |
| 793 | .Optional => { | 848 | .Optional => { |
| 794 | var opt_buf: Type.Payload.ElemType = undefined; | 849 | var opt_buf: Type.Payload.ElemType = undefined; |
| ... | @@ -1068,12 +1123,15 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO | ... | @@ -1068,12 +1123,15 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1068 | .unreach => try airUnreach(f), | 1123 | .unreach => try airUnreach(f), |
| 1069 | .fence => try airFence(f, inst), | 1124 | .fence => try airFence(f, inst), |
| 1070 | 1125 | ||
| 1126 | .ptr_add => try airPtrAddSub (f, inst, " + "), | ||
| 1127 | .ptr_sub => try airPtrAddSub (f, inst, " - "), | ||
| 1128 | |||
| 1071 | // TODO use a different strategy for add that communicates to the optimizer | 1129 | // TODO use a different strategy for add that communicates to the optimizer |
| 1072 | // that wrapping is UB. | 1130 | // that wrapping is UB. |
| 1073 | .add, .ptr_add => try airBinOp (f, inst, " + "), | 1131 | .add => try airBinOp (f, inst, " + "), |
| 1074 | // TODO use a different strategy for sub that communicates to the optimizer | 1132 | // TODO use a different strategy for sub that communicates to the optimizer |
| 1075 | // that wrapping is UB. | 1133 | // that wrapping is UB. |
| 1076 | .sub, .ptr_sub => try airBinOp (f, inst, " - "), | 1134 | .sub => try airBinOp (f, inst, " - "), |
| 1077 | // TODO use a different strategy for mul that communicates to the optimizer | 1135 | // TODO use a different strategy for mul that communicates to the optimizer |
| 1078 | // that wrapping is UB. | 1136 | // that wrapping is UB. |
| 1079 | .mul => try airBinOp (f, inst, " * "), | 1137 | .mul => try airBinOp (f, inst, " * "), |
| ... | @@ -1187,7 +1245,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO | ... | @@ -1187,7 +1245,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1187 | .ptr_slice_len_ptr => try airPtrSliceFieldPtr(f, inst, ".len;\n"), | 1245 | .ptr_slice_len_ptr => try airPtrSliceFieldPtr(f, inst, ".len;\n"), |
| 1188 | .ptr_slice_ptr_ptr => try airPtrSliceFieldPtr(f, inst, ".ptr;\n"), | 1246 | .ptr_slice_ptr_ptr => try airPtrSliceFieldPtr(f, inst, ".ptr;\n"), |
| 1189 | 1247 | ||
| 1190 | .ptr_elem_val => try airPtrElemVal(f, inst, "["), | 1248 | .ptr_elem_val => try airPtrElemVal(f, inst), |
| 1191 | .ptr_elem_ptr => try airPtrElemPtr(f, inst), | 1249 | .ptr_elem_ptr => try airPtrElemPtr(f, inst), |
| 1192 | .slice_elem_val => try airSliceElemVal(f, inst), | 1250 | .slice_elem_val => try airSliceElemVal(f, inst), |
| 1193 | .slice_elem_ptr => try airSliceElemPtr(f, inst), | 1251 | .slice_elem_ptr => try airSliceElemPtr(f, inst), |
| ... | @@ -1240,20 +1298,39 @@ fn airPtrSliceFieldPtr(f: *Function, inst: Air.Inst.Index, suffix: []const u8) ! | ... | @@ -1240,20 +1298,39 @@ fn airPtrSliceFieldPtr(f: *Function, inst: Air.Inst.Index, suffix: []const u8) ! |
| 1240 | return f.fail("TODO: C backend: airPtrSliceFieldPtr", .{}); | 1298 | return f.fail("TODO: C backend: airPtrSliceFieldPtr", .{}); |
| 1241 | } | 1299 | } |
| 1242 | 1300 | ||
| 1243 | fn airPtrElemVal(f: *Function, inst: Air.Inst.Index, prefix: []const u8) !CValue { | 1301 | fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1244 | const is_volatile = false; // TODO | 1302 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; |
| 1245 | if (!is_volatile and f.liveness.isUnused(inst)) | 1303 | const slice_ty = f.air.typeOf(bin_op.lhs); |
| 1246 | return CValue.none; | 1304 | if (!slice_ty.isVolatilePtr() and f.liveness.isUnused(inst)) return CValue.none; |
| 1247 | 1305 | ||
| 1248 | _ = prefix; | 1306 | const arr = try f.resolveInst(bin_op.lhs); |
| 1249 | return f.fail("TODO: C backend: airPtrElemVal", .{}); | 1307 | const index = try f.resolveInst(bin_op.rhs); |
| 1308 | const writer = f.object.writer(); | ||
| 1309 | const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const); | ||
| 1310 | try writer.writeAll(" = "); | ||
| 1311 | try f.writeCValue(writer, arr); | ||
| 1312 | try writer.writeByte('['); | ||
| 1313 | try f.writeCValue(writer, index); | ||
| 1314 | try writer.writeAll("];\n"); | ||
| 1315 | return local; | ||
| 1250 | } | 1316 | } |
| 1251 | 1317 | ||
| 1252 | fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { | 1318 | fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1253 | if (f.liveness.isUnused(inst)) | 1319 | if (f.liveness.isUnused(inst)) return CValue.none; |
| 1254 | return CValue.none; | ||
| 1255 | 1320 | ||
| 1256 | return f.fail("TODO: C backend: airPtrElemPtr", .{}); | 1321 | const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; |
| 1322 | const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; | ||
| 1323 | |||
| 1324 | const arr = try f.resolveInst(bin_op.lhs); | ||
| 1325 | const index = try f.resolveInst(bin_op.rhs); | ||
| 1326 | const writer = f.object.writer(); | ||
| 1327 | const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const); | ||
| 1328 | try writer.writeAll(" = &"); | ||
| 1329 | try f.writeCValue(writer, arr); | ||
| 1330 | try writer.writeByte('['); | ||
| 1331 | try f.writeCValue(writer, index); | ||
| 1332 | try writer.writeAll("];\n"); | ||
| 1333 | return local; | ||
| 1257 | } | 1334 | } |
| 1258 | 1335 | ||
| 1259 | fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { | 1336 | fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| ... | @@ -1317,6 +1394,10 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -1317,6 +1394,10 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1317 | const local = try f.allocLocal(elem_type, mutability); | 1394 | const local = try f.allocLocal(elem_type, mutability); |
| 1318 | try writer.writeAll(";\n"); | 1395 | try writer.writeAll(";\n"); |
| 1319 | 1396 | ||
| 1397 | // Arrays are already pointers so they don't need to be referenced. | ||
| 1398 | if (elem_type.zigTypeTag() == .Array) | ||
| 1399 | return CValue{ .local = local.local }; | ||
| 1400 | |||
| 1320 | return CValue{ .local_ref = local.local }; | 1401 | return CValue{ .local_ref = local.local }; |
| 1321 | } | 1402 | } |
| 1322 | 1403 | ||
| ... | @@ -1810,6 +1891,33 @@ fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue | ... | @@ -1810,6 +1891,33 @@ fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue |
| 1810 | return local; | 1891 | return local; |
| 1811 | } | 1892 | } |
| 1812 | 1893 | ||
| 1894 | fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue { | ||
| 1895 | if (f.liveness.isUnused(inst)) | ||
| 1896 | return CValue.none; | ||
| 1897 | |||
| 1898 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; | ||
| 1899 | const lhs = try f.resolveInst(bin_op.lhs); | ||
| 1900 | const rhs = try f.resolveInst(bin_op.rhs); | ||
| 1901 | |||
| 1902 | const writer = f.object.writer(); | ||
| 1903 | const inst_ty = f.air.typeOfIndex(inst); | ||
| 1904 | const local = try f.allocLocal(inst_ty, .Const); | ||
| 1905 | |||
| 1906 | // We must convert to and from integer types to prevent UB if the operation results in a NULL pointer, | ||
| 1907 | // or if LHS is NULL. The operation is only UB if the result is NULL and then dereferenced. | ||
| 1908 | try writer.writeAll(" = ("); | ||
| 1909 | try f.renderType(writer, inst_ty); | ||
| 1910 | try writer.writeAll(")(((uintptr_t)"); | ||
| 1911 | try f.writeCValue(writer, lhs); | ||
| 1912 | try writer.print("){s}(", .{operator}); | ||
| 1913 | try f.writeCValue(writer, rhs); | ||
| 1914 | try writer.writeAll("*sizeof("); | ||
| 1915 | try f.renderType(writer, inst_ty.childType()); | ||
| 1916 | try writer.print(")));\n", .{}); | ||
| 1917 | |||
| 1918 | return local; | ||
| 1919 | } | ||
| 1920 | |||
| 1813 | fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue { | 1921 | fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue { |
| 1814 | if (f.liveness.isUnused(inst)) return CValue.none; | 1922 | if (f.liveness.isUnused(inst)) return CValue.none; |
| 1815 | 1923 | ||
| ... | @@ -2529,7 +2637,9 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -2529,7 +2637,9 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2529 | const writer = f.object.writer(); | 2637 | const writer = f.object.writer(); |
| 2530 | const operand = try f.resolveInst(un_op); | 2638 | const operand = try f.resolveInst(un_op); |
| 2531 | 2639 | ||
| 2532 | try writer.writeAll(" = "); | 2640 | try writer.writeAll(" = ("); |
| 2641 | try f.renderType(writer, inst_ty); | ||
| 2642 | try writer.writeAll(")"); | ||
| 2533 | try f.writeCValue(writer, operand); | 2643 | try f.writeCValue(writer, operand); |
| 2534 | try writer.writeAll(";\n"); | 2644 | try writer.writeAll(";\n"); |
| 2535 | return local; | 2645 | return local; |
test/behavior.zig+5-4| ... | @@ -2,6 +2,7 @@ const builtin = @import("builtin"); | ... | @@ -2,6 +2,7 @@ const builtin = @import("builtin"); |
| 2 | 2 | ||
| 3 | test { | 3 | test { |
| 4 | // Tests that pass for stage1, stage2, and the C backend. | 4 | // Tests that pass for stage1, stage2, and the C backend. |
| 5 | _ = @import("behavior/align.zig"); | ||
| 5 | _ = @import("behavior/basic.zig"); | 6 | _ = @import("behavior/basic.zig"); |
| 6 | _ = @import("behavior/bitcast.zig"); | 7 | _ = @import("behavior/bitcast.zig"); |
| 7 | _ = @import("behavior/bool.zig"); | 8 | _ = @import("behavior/bool.zig"); |
| ... | @@ -19,16 +20,18 @@ test { | ... | @@ -19,16 +20,18 @@ test { |
| 19 | _ = @import("behavior/bugs/4769_b.zig"); | 20 | _ = @import("behavior/bugs/4769_b.zig"); |
| 20 | _ = @import("behavior/bugs/6850.zig"); | 21 | _ = @import("behavior/bugs/6850.zig"); |
| 21 | _ = @import("behavior/call.zig"); | 22 | _ = @import("behavior/call.zig"); |
| 23 | _ = @import("behavior/cast_c.zig"); | ||
| 22 | _ = @import("behavior/defer.zig"); | 24 | _ = @import("behavior/defer.zig"); |
| 23 | _ = @import("behavior/enum.zig"); | 25 | _ = @import("behavior/enum.zig"); |
| 24 | _ = @import("behavior/hasdecl.zig"); | 26 | _ = @import("behavior/hasdecl.zig"); |
| 25 | _ = @import("behavior/hasfield.zig"); | 27 | _ = @import("behavior/hasfield.zig"); |
| 26 | _ = @import("behavior/if.zig"); | 28 | _ = @import("behavior/if.zig"); |
| 27 | _ = @import("behavior/struct.zig"); | 29 | _ = @import("behavior/int128.zig"); |
| 28 | _ = @import("behavior/truncate.zig"); | ||
| 29 | _ = @import("behavior/null.zig"); | 30 | _ = @import("behavior/null.zig"); |
| 31 | _ = @import("behavior/pointers.zig"); | ||
| 30 | _ = @import("behavior/ptrcast.zig"); | 32 | _ = @import("behavior/ptrcast.zig"); |
| 31 | _ = @import("behavior/pub_enum.zig"); | 33 | _ = @import("behavior/pub_enum.zig"); |
| 34 | _ = @import("behavior/struct.zig"); | ||
| 32 | _ = @import("behavior/truncate.zig"); | 35 | _ = @import("behavior/truncate.zig"); |
| 33 | _ = @import("behavior/underscore.zig"); | 36 | _ = @import("behavior/underscore.zig"); |
| 34 | _ = @import("behavior/usingnamespace.zig"); | 37 | _ = @import("behavior/usingnamespace.zig"); |
| ... | @@ -39,7 +42,6 @@ test { | ... | @@ -39,7 +42,6 @@ test { |
| 39 | 42 | ||
| 40 | if (builtin.object_format != .c) { | 43 | if (builtin.object_format != .c) { |
| 41 | // Tests that pass for stage1 and stage2 but not the C backend. | 44 | // Tests that pass for stage1 and stage2 but not the C backend. |
| 42 | _ = @import("behavior/align.zig"); | ||
| 43 | _ = @import("behavior/array.zig"); | 45 | _ = @import("behavior/array.zig"); |
| 44 | _ = @import("behavior/atomics.zig"); | 46 | _ = @import("behavior/atomics.zig"); |
| 45 | _ = @import("behavior/basic_llvm.zig"); | 47 | _ = @import("behavior/basic_llvm.zig"); |
| ... | @@ -60,7 +62,6 @@ test { | ... | @@ -60,7 +62,6 @@ test { |
| 60 | _ = @import("behavior/maximum_minimum.zig"); | 62 | _ = @import("behavior/maximum_minimum.zig"); |
| 61 | _ = @import("behavior/null_llvm.zig"); | 63 | _ = @import("behavior/null_llvm.zig"); |
| 62 | _ = @import("behavior/optional.zig"); | 64 | _ = @import("behavior/optional.zig"); |
| 63 | _ = @import("behavior/pointers.zig"); | ||
| 64 | _ = @import("behavior/popcount.zig"); | 65 | _ = @import("behavior/popcount.zig"); |
| 65 | _ = @import("behavior/saturating_arithmetic.zig"); | 66 | _ = @import("behavior/saturating_arithmetic.zig"); |
| 66 | _ = @import("behavior/sizeof_and_typeof.zig"); | 67 | _ = @import("behavior/sizeof_and_typeof.zig"); |
test/behavior/cast.zig-234| ... | @@ -2,70 +2,8 @@ const std = @import("std"); | ... | @@ -2,70 +2,8 @@ const std = @import("std"); |
| 2 | const expect = std.testing.expect; | 2 | const expect = std.testing.expect; |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| 4 | const maxInt = std.math.maxInt; | 4 | const maxInt = std.math.maxInt; |
| 5 | const Vector = std.meta.Vector; | ||
| 6 | const native_endian = @import("builtin").target.cpu.arch.endian(); | 5 | const native_endian = @import("builtin").target.cpu.arch.endian(); |
| 7 | 6 | ||
| 8 | test "int to ptr cast" { | ||
| 9 | const x = @as(usize, 13); | ||
| 10 | const y = @intToPtr(*u8, x); | ||
| 11 | const z = @ptrToInt(y); | ||
| 12 | try expect(z == 13); | ||
| 13 | } | ||
| 14 | |||
| 15 | test "integer literal to pointer cast" { | ||
| 16 | const vga_mem = @intToPtr(*u16, 0xB8000); | ||
| 17 | try expect(@ptrToInt(vga_mem) == 0xB8000); | ||
| 18 | } | ||
| 19 | |||
| 20 | test "peer type resolution: ?T and T" { | ||
| 21 | try expect(peerTypeTAndOptionalT(true, false).? == 0); | ||
| 22 | try expect(peerTypeTAndOptionalT(false, false).? == 3); | ||
| 23 | comptime { | ||
| 24 | try expect(peerTypeTAndOptionalT(true, false).? == 0); | ||
| 25 | try expect(peerTypeTAndOptionalT(false, false).? == 3); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { | ||
| 29 | if (c) { | ||
| 30 | return if (b) null else @as(usize, 0); | ||
| 31 | } | ||
| 32 | |||
| 33 | return @as(usize, 3); | ||
| 34 | } | ||
| 35 | |||
| 36 | test "resolve undefined with integer" { | ||
| 37 | try testResolveUndefWithInt(true, 1234); | ||
| 38 | comptime try testResolveUndefWithInt(true, 1234); | ||
| 39 | } | ||
| 40 | fn testResolveUndefWithInt(b: bool, x: i32) !void { | ||
| 41 | const value = if (b) x else undefined; | ||
| 42 | if (b) { | ||
| 43 | try expect(value == x); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | test "@intCast i32 to u7" { | ||
| 48 | var x: u128 = maxInt(u128); | ||
| 49 | var y: i32 = 120; | ||
| 50 | var z = x >> @intCast(u7, y); | ||
| 51 | try expect(z == 0xff); | ||
| 52 | } | ||
| 53 | |||
| 54 | test "@intCast to comptime_int" { | ||
| 55 | try expect(@intCast(comptime_int, 0) == 0); | ||
| 56 | } | ||
| 57 | |||
| 58 | test "implicit cast comptime numbers to any type when the value fits" { | ||
| 59 | const a: u64 = 255; | ||
| 60 | var b: u8 = a; | ||
| 61 | try expect(b == 255); | ||
| 62 | } | ||
| 63 | |||
| 64 | test "implicit cast comptime_int to comptime_float" { | ||
| 65 | comptime try expect(@as(comptime_float, 10) == @as(f32, 10)); | ||
| 66 | try expect(2 == 2.0); | ||
| 67 | } | ||
| 68 | |||
| 69 | test "pointer reinterpret const float to int" { | 7 | test "pointer reinterpret const float to int" { |
| 70 | // The hex representation is 0x3fe3333333333303. | 8 | // The hex representation is 0x3fe3333333333303. |
| 71 | const float: f64 = 5.99999999999994648725e-01; | 9 | const float: f64 = 5.99999999999994648725e-01; |
| ... | @@ -78,51 +16,15 @@ test "pointer reinterpret const float to int" { | ... | @@ -78,51 +16,15 @@ test "pointer reinterpret const float to int" { |
| 78 | try expect(int_val == 0x3fe33333); | 16 | try expect(int_val == 0x3fe33333); |
| 79 | } | 17 | } |
| 80 | 18 | ||
| 81 | test "comptime_int @intToFloat" { | ||
| 82 | { | ||
| 83 | const result = @intToFloat(f16, 1234); | ||
| 84 | try expect(@TypeOf(result) == f16); | ||
| 85 | try expect(result == 1234.0); | ||
| 86 | } | ||
| 87 | { | ||
| 88 | const result = @intToFloat(f32, 1234); | ||
| 89 | try expect(@TypeOf(result) == f32); | ||
| 90 | try expect(result == 1234.0); | ||
| 91 | } | ||
| 92 | { | ||
| 93 | const result = @intToFloat(f64, 1234); | ||
| 94 | try expect(@TypeOf(result) == f64); | ||
| 95 | try expect(result == 1234.0); | ||
| 96 | } | ||
| 97 | { | ||
| 98 | const result = @intToFloat(f128, 1234); | ||
| 99 | try expect(@TypeOf(result) == f128); | ||
| 100 | try expect(result == 1234.0); | ||
| 101 | } | ||
| 102 | // big comptime_int (> 64 bits) to f128 conversion | ||
| 103 | { | ||
| 104 | const result = @intToFloat(f128, 0x1_0000_0000_0000_0000); | ||
| 105 | try expect(@TypeOf(result) == f128); | ||
| 106 | try expect(result == 0x1_0000_0000_0000_0000.0); | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | test "@floatToInt" { | 19 | test "@floatToInt" { |
| 111 | try testFloatToInts(); | 20 | try testFloatToInts(); |
| 112 | comptime try testFloatToInts(); | 21 | comptime try testFloatToInts(); |
| 113 | } | 22 | } |
| 114 | 23 | ||
| 115 | fn testFloatToInts() !void { | 24 | fn testFloatToInts() !void { |
| 116 | const x = @as(i32, 1e4); | ||
| 117 | try expect(x == 10000); | ||
| 118 | const y = @floatToInt(i32, @as(f32, 1e4)); | ||
| 119 | try expect(y == 10000); | ||
| 120 | try expectFloatToInt(f16, 255.1, u8, 255); | 25 | try expectFloatToInt(f16, 255.1, u8, 255); |
| 121 | try expectFloatToInt(f16, 127.2, i8, 127); | 26 | try expectFloatToInt(f16, 127.2, i8, 127); |
| 122 | try expectFloatToInt(f16, -128.2, i8, -128); | 27 | try expectFloatToInt(f16, -128.2, i8, -128); |
| 123 | try expectFloatToInt(f32, 255.1, u8, 255); | ||
| 124 | try expectFloatToInt(f32, 127.2, i8, 127); | ||
| 125 | try expectFloatToInt(f32, -128.2, i8, -128); | ||
| 126 | } | 28 | } |
| 127 | 29 | ||
| 128 | fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void { | 30 | fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void { |
| ... | @@ -143,95 +45,6 @@ fn incrementVoidPtrArray(array: ?*c_void, len: usize) void { | ... | @@ -143,95 +45,6 @@ fn incrementVoidPtrArray(array: ?*c_void, len: usize) void { |
| 143 | } | 45 | } |
| 144 | } | 46 | } |
| 145 | 47 | ||
| 146 | test "implicitly cast indirect pointer to maybe-indirect pointer" { | ||
| 147 | const S = struct { | ||
| 148 | const Self = @This(); | ||
| 149 | x: u8, | ||
| 150 | fn constConst(p: *const *const Self) u8 { | ||
| 151 | return p.*.x; | ||
| 152 | } | ||
| 153 | fn maybeConstConst(p: ?*const *const Self) u8 { | ||
| 154 | return p.?.*.x; | ||
| 155 | } | ||
| 156 | fn constConstConst(p: *const *const *const Self) u8 { | ||
| 157 | return p.*.*.x; | ||
| 158 | } | ||
| 159 | fn maybeConstConstConst(p: ?*const *const *const Self) u8 { | ||
| 160 | return p.?.*.*.x; | ||
| 161 | } | ||
| 162 | }; | ||
| 163 | const s = S{ .x = 42 }; | ||
| 164 | const p = &s; | ||
| 165 | const q = &p; | ||
| 166 | const r = &q; | ||
| 167 | try expect(42 == S.constConst(q)); | ||
| 168 | try expect(42 == S.maybeConstConst(q)); | ||
| 169 | try expect(42 == S.constConstConst(r)); | ||
| 170 | try expect(42 == S.maybeConstConstConst(r)); | ||
| 171 | } | ||
| 172 | |||
| 173 | test "@intCast comptime_int" { | ||
| 174 | const result = @intCast(i32, 1234); | ||
| 175 | try expect(@TypeOf(result) == i32); | ||
| 176 | try expect(result == 1234); | ||
| 177 | } | ||
| 178 | |||
| 179 | test "@floatCast comptime_int and comptime_float" { | ||
| 180 | { | ||
| 181 | const result = @floatCast(f16, 1234); | ||
| 182 | try expect(@TypeOf(result) == f16); | ||
| 183 | try expect(result == 1234.0); | ||
| 184 | } | ||
| 185 | { | ||
| 186 | const result = @floatCast(f16, 1234.0); | ||
| 187 | try expect(@TypeOf(result) == f16); | ||
| 188 | try expect(result == 1234.0); | ||
| 189 | } | ||
| 190 | { | ||
| 191 | const result = @floatCast(f32, 1234); | ||
| 192 | try expect(@TypeOf(result) == f32); | ||
| 193 | try expect(result == 1234.0); | ||
| 194 | } | ||
| 195 | { | ||
| 196 | const result = @floatCast(f32, 1234.0); | ||
| 197 | try expect(@TypeOf(result) == f32); | ||
| 198 | try expect(result == 1234.0); | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | test "coerce undefined to optional" { | ||
| 203 | try expect(MakeType(void).getNull() == null); | ||
| 204 | try expect(MakeType(void).getNonNull() != null); | ||
| 205 | } | ||
| 206 | |||
| 207 | fn MakeType(comptime T: type) type { | ||
| 208 | return struct { | ||
| 209 | fn getNull() ?T { | ||
| 210 | return null; | ||
| 211 | } | ||
| 212 | |||
| 213 | fn getNonNull() ?T { | ||
| 214 | return @as(T, undefined); | ||
| 215 | } | ||
| 216 | }; | ||
| 217 | } | ||
| 218 | |||
| 219 | test "implicit cast from *[N]T to [*c]T" { | ||
| 220 | var x: [4]u16 = [4]u16{ 0, 1, 2, 3 }; | ||
| 221 | var y: [*c]u16 = &x; | ||
| 222 | |||
| 223 | try expect(std.mem.eql(u16, x[0..4], y[0..4])); | ||
| 224 | x[0] = 8; | ||
| 225 | y[3] = 6; | ||
| 226 | try expect(std.mem.eql(u16, x[0..4], y[0..4])); | ||
| 227 | } | ||
| 228 | |||
| 229 | test "*usize to *void" { | ||
| 230 | var i = @as(usize, 0); | ||
| 231 | var v = @ptrCast(*void, &i); | ||
| 232 | v.* = {}; | ||
| 233 | } | ||
| 234 | |||
| 235 | test "compile time int to ptr of function" { | 48 | test "compile time int to ptr of function" { |
| 236 | try foobar(FUNCTION_CONSTANT); | 49 | try foobar(FUNCTION_CONSTANT); |
| 237 | } | 50 | } |
| ... | @@ -252,50 +65,3 @@ test "implicit ptr to *c_void" { | ... | @@ -252,50 +65,3 @@ test "implicit ptr to *c_void" { |
| 252 | var c: *u32 = @ptrCast(*u32, ptr2.?); | 65 | var c: *u32 = @ptrCast(*u32, ptr2.?); |
| 253 | try expect(c.* == 1); | 66 | try expect(c.* == 1); |
| 254 | } | 67 | } |
| 255 | |||
| 256 | test "@intToEnum passed a comptime_int to an enum with one item" { | ||
| 257 | const E = enum { A }; | ||
| 258 | const x = @intToEnum(E, 0); | ||
| 259 | try expect(x == E.A); | ||
| 260 | } | ||
| 261 | |||
| 262 | test "@intCast to u0 and use the result" { | ||
| 263 | const S = struct { | ||
| 264 | fn doTheTest(zero: u1, one: u1, bigzero: i32) !void { | ||
| 265 | try expect((one << @intCast(u0, bigzero)) == 1); | ||
| 266 | try expect((zero << @intCast(u0, bigzero)) == 0); | ||
| 267 | } | ||
| 268 | }; | ||
| 269 | try S.doTheTest(0, 1, 0); | ||
| 270 | comptime try S.doTheTest(0, 1, 0); | ||
| 271 | } | ||
| 272 | |||
| 273 | test "peer result null and comptime_int" { | ||
| 274 | const S = struct { | ||
| 275 | fn blah(n: i32) ?i32 { | ||
| 276 | if (n == 0) { | ||
| 277 | return null; | ||
| 278 | } else if (n < 0) { | ||
| 279 | return -1; | ||
| 280 | } else { | ||
| 281 | return 1; | ||
| 282 | } | ||
| 283 | } | ||
| 284 | }; | ||
| 285 | |||
| 286 | try expect(S.blah(0) == null); | ||
| 287 | comptime try expect(S.blah(0) == null); | ||
| 288 | try expect(S.blah(10).? == 1); | ||
| 289 | comptime try expect(S.blah(10).? == 1); | ||
| 290 | try expect(S.blah(-10).? == -1); | ||
| 291 | comptime try expect(S.blah(-10).? == -1); | ||
| 292 | } | ||
| 293 | |||
| 294 | test "*const ?[*]const T to [*c]const [*c]const T" { | ||
| 295 | var array = [_]u8{ 'o', 'k' }; | ||
| 296 | const opt_array_ptr: ?[*]const u8 = &array; | ||
| 297 | const a: *const ?[*]const u8 = &opt_array_ptr; | ||
| 298 | const b: [*c]const [*c]const u8 = a; | ||
| 299 | try expect(b.*[0] == 'o'); | ||
| 300 | try expect(b[0][1] == 'k'); | ||
| 301 | } |
test/behavior/cast_c.zig created+249| ... | @@ -0,0 +1,249 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const expect = std.testing.expect; | ||
| 3 | const mem = std.mem; | ||
| 4 | const maxInt = std.math.maxInt; | ||
| 5 | |||
| 6 | test "int to ptr cast" { | ||
| 7 | const x = @as(usize, 13); | ||
| 8 | const y = @intToPtr(*u8, x); | ||
| 9 | const z = @ptrToInt(y); | ||
| 10 | try expect(z == 13); | ||
| 11 | } | ||
| 12 | |||
| 13 | test "integer literal to pointer cast" { | ||
| 14 | const vga_mem = @intToPtr(*u16, 0xB8000); | ||
| 15 | try expect(@ptrToInt(vga_mem) == 0xB8000); | ||
| 16 | } | ||
| 17 | |||
| 18 | test "peer type resolution: ?T and T" { | ||
| 19 | try expect(peerTypeTAndOptionalT(true, false).? == 0); | ||
| 20 | try expect(peerTypeTAndOptionalT(false, false).? == 3); | ||
| 21 | comptime { | ||
| 22 | try expect(peerTypeTAndOptionalT(true, false).? == 0); | ||
| 23 | try expect(peerTypeTAndOptionalT(false, false).? == 3); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { | ||
| 27 | if (c) { | ||
| 28 | return if (b) null else @as(usize, 0); | ||
| 29 | } | ||
| 30 | |||
| 31 | return @as(usize, 3); | ||
| 32 | } | ||
| 33 | |||
| 34 | test "resolve undefined with integer" { | ||
| 35 | try testResolveUndefWithInt(true, 1234); | ||
| 36 | comptime try testResolveUndefWithInt(true, 1234); | ||
| 37 | } | ||
| 38 | fn testResolveUndefWithInt(b: bool, x: i32) !void { | ||
| 39 | const value = if (b) x else undefined; | ||
| 40 | if (b) { | ||
| 41 | try expect(value == x); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | test "@intCast i32 to u7" { | ||
| 46 | var x: u128 = maxInt(u128); | ||
| 47 | var y: i32 = 120; | ||
| 48 | var z = x >> @intCast(u7, y); | ||
| 49 | try expect(z == 0xff); | ||
| 50 | } | ||
| 51 | |||
| 52 | test "@intCast to comptime_int" { | ||
| 53 | try expect(@intCast(comptime_int, 0) == 0); | ||
| 54 | } | ||
| 55 | |||
| 56 | test "implicit cast comptime numbers to any type when the value fits" { | ||
| 57 | const a: u64 = 255; | ||
| 58 | var b: u8 = a; | ||
| 59 | try expect(b == 255); | ||
| 60 | } | ||
| 61 | |||
| 62 | test "implicit cast comptime_int to comptime_float" { | ||
| 63 | comptime try expect(@as(comptime_float, 10) == @as(f32, 10)); | ||
| 64 | try expect(2 == 2.0); | ||
| 65 | } | ||
| 66 | |||
| 67 | test "comptime_int @intToFloat" { | ||
| 68 | { | ||
| 69 | const result = @intToFloat(f16, 1234); | ||
| 70 | try expect(@TypeOf(result) == f16); | ||
| 71 | try expect(result == 1234.0); | ||
| 72 | } | ||
| 73 | { | ||
| 74 | const result = @intToFloat(f32, 1234); | ||
| 75 | try expect(@TypeOf(result) == f32); | ||
| 76 | try expect(result == 1234.0); | ||
| 77 | } | ||
| 78 | { | ||
| 79 | const result = @intToFloat(f64, 1234); | ||
| 80 | try expect(@TypeOf(result) == f64); | ||
| 81 | try expect(result == 1234.0); | ||
| 82 | } | ||
| 83 | { | ||
| 84 | const result = @intToFloat(f128, 1234); | ||
| 85 | try expect(@TypeOf(result) == f128); | ||
| 86 | try expect(result == 1234.0); | ||
| 87 | } | ||
| 88 | // big comptime_int (> 64 bits) to f128 conversion | ||
| 89 | { | ||
| 90 | const result = @intToFloat(f128, 0x1_0000_0000_0000_0000); | ||
| 91 | try expect(@TypeOf(result) == f128); | ||
| 92 | try expect(result == 0x1_0000_0000_0000_0000.0); | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | test "@floatToInt" { | ||
| 97 | try testFloatToInts(); | ||
| 98 | comptime try testFloatToInts(); | ||
| 99 | } | ||
| 100 | |||
| 101 | fn testFloatToInts() !void { | ||
| 102 | const x = @as(i32, 1e4); | ||
| 103 | try expect(x == 10000); | ||
| 104 | const y = @floatToInt(i32, @as(f32, 1e4)); | ||
| 105 | try expect(y == 10000); | ||
| 106 | try expectFloatToInt(f32, 255.1, u8, 255); | ||
| 107 | try expectFloatToInt(f32, 127.2, i8, 127); | ||
| 108 | try expectFloatToInt(f32, -128.2, i8, -128); | ||
| 109 | } | ||
| 110 | |||
| 111 | fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void { | ||
| 112 | try expect(@floatToInt(I, f) == i); | ||
| 113 | } | ||
| 114 | |||
| 115 | test "implicitly cast indirect pointer to maybe-indirect pointer" { | ||
| 116 | const S = struct { | ||
| 117 | const Self = @This(); | ||
| 118 | x: u8, | ||
| 119 | fn constConst(p: *const *const Self) u8 { | ||
| 120 | return p.*.x; | ||
| 121 | } | ||
| 122 | fn maybeConstConst(p: ?*const *const Self) u8 { | ||
| 123 | return p.?.*.x; | ||
| 124 | } | ||
| 125 | fn constConstConst(p: *const *const *const Self) u8 { | ||
| 126 | return p.*.*.x; | ||
| 127 | } | ||
| 128 | fn maybeConstConstConst(p: ?*const *const *const Self) u8 { | ||
| 129 | return p.?.*.*.x; | ||
| 130 | } | ||
| 131 | }; | ||
| 132 | const s = S{ .x = 42 }; | ||
| 133 | const p = &s; | ||
| 134 | const q = &p; | ||
| 135 | const r = &q; | ||
| 136 | try expect(42 == S.constConst(q)); | ||
| 137 | try expect(42 == S.maybeConstConst(q)); | ||
| 138 | try expect(42 == S.constConstConst(r)); | ||
| 139 | try expect(42 == S.maybeConstConstConst(r)); | ||
| 140 | } | ||
| 141 | |||
| 142 | test "@intCast comptime_int" { | ||
| 143 | const result = @intCast(i32, 1234); | ||
| 144 | try expect(@TypeOf(result) == i32); | ||
| 145 | try expect(result == 1234); | ||
| 146 | } | ||
| 147 | |||
| 148 | test "@floatCast comptime_int and comptime_float" { | ||
| 149 | { | ||
| 150 | const result = @floatCast(f16, 1234); | ||
| 151 | try expect(@TypeOf(result) == f16); | ||
| 152 | try expect(result == 1234.0); | ||
| 153 | } | ||
| 154 | { | ||
| 155 | const result = @floatCast(f16, 1234.0); | ||
| 156 | try expect(@TypeOf(result) == f16); | ||
| 157 | try expect(result == 1234.0); | ||
| 158 | } | ||
| 159 | { | ||
| 160 | const result = @floatCast(f32, 1234); | ||
| 161 | try expect(@TypeOf(result) == f32); | ||
| 162 | try expect(result == 1234.0); | ||
| 163 | } | ||
| 164 | { | ||
| 165 | const result = @floatCast(f32, 1234.0); | ||
| 166 | try expect(@TypeOf(result) == f32); | ||
| 167 | try expect(result == 1234.0); | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | test "coerce undefined to optional" { | ||
| 172 | try expect(MakeType(void).getNull() == null); | ||
| 173 | try expect(MakeType(void).getNonNull() != null); | ||
| 174 | } | ||
| 175 | |||
| 176 | fn MakeType(comptime T: type) type { | ||
| 177 | return struct { | ||
| 178 | fn getNull() ?T { | ||
| 179 | return null; | ||
| 180 | } | ||
| 181 | |||
| 182 | fn getNonNull() ?T { | ||
| 183 | return @as(T, undefined); | ||
| 184 | } | ||
| 185 | }; | ||
| 186 | } | ||
| 187 | |||
| 188 | test "implicit cast from *[N]T to [*c]T" { | ||
| 189 | var x: [4]u16 = [4]u16{ 0, 1, 2, 3 }; | ||
| 190 | var y: [*c]u16 = &x; | ||
| 191 | |||
| 192 | try expect(std.mem.eql(u16, x[0..4], y[0..4])); | ||
| 193 | x[0] = 8; | ||
| 194 | y[3] = 6; | ||
| 195 | try expect(std.mem.eql(u16, x[0..4], y[0..4])); | ||
| 196 | } | ||
| 197 | |||
| 198 | test "*usize to *void" { | ||
| 199 | var i = @as(usize, 0); | ||
| 200 | var v = @ptrCast(*void, &i); | ||
| 201 | v.* = {}; | ||
| 202 | } | ||
| 203 | |||
| 204 | test "@intToEnum passed a comptime_int to an enum with one item" { | ||
| 205 | const E = enum { A }; | ||
| 206 | const x = @intToEnum(E, 0); | ||
| 207 | try expect(x == E.A); | ||
| 208 | } | ||
| 209 | |||
| 210 | test "@intCast to u0 and use the result" { | ||
| 211 | const S = struct { | ||
| 212 | fn doTheTest(zero: u1, one: u1, bigzero: i32) !void { | ||
| 213 | try expect((one << @intCast(u0, bigzero)) == 1); | ||
| 214 | try expect((zero << @intCast(u0, bigzero)) == 0); | ||
| 215 | } | ||
| 216 | }; | ||
| 217 | try S.doTheTest(0, 1, 0); | ||
| 218 | comptime try S.doTheTest(0, 1, 0); | ||
| 219 | } | ||
| 220 | |||
| 221 | test "peer result null and comptime_int" { | ||
| 222 | const S = struct { | ||
| 223 | fn blah(n: i32) ?i32 { | ||
| 224 | if (n == 0) { | ||
| 225 | return null; | ||
| 226 | } else if (n < 0) { | ||
| 227 | return -1; | ||
| 228 | } else { | ||
| 229 | return 1; | ||
| 230 | } | ||
| 231 | } | ||
| 232 | }; | ||
| 233 | |||
| 234 | try expect(S.blah(0) == null); | ||
| 235 | comptime try expect(S.blah(0) == null); | ||
| 236 | try expect(S.blah(10).? == 1); | ||
| 237 | comptime try expect(S.blah(10).? == 1); | ||
| 238 | try expect(S.blah(-10).? == -1); | ||
| 239 | comptime try expect(S.blah(-10).? == -1); | ||
| 240 | } | ||
| 241 | |||
| 242 | test "*const ?[*]const T to [*c]const [*c]const T" { | ||
| 243 | var array = [_]u8{ 'o', 'k' }; | ||
| 244 | const opt_array_ptr: ?[*]const u8 = &array; | ||
| 245 | const a: *const ?[*]const u8 = &opt_array_ptr; | ||
| 246 | const b: [*c]const [*c]const u8 = a; | ||
| 247 | try expect(b.*[0] == 'o'); | ||
| 248 | try expect(b[0][1] == 'k'); | ||
| 249 | } | ||
test/behavior/int128.zig created+43| ... | @@ -0,0 +1,43 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const expect = std.testing.expect; | ||
| 3 | const maxInt = std.math.maxInt; | ||
| 4 | const minInt = std.math.minInt; | ||
| 5 | |||
| 6 | test "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 | |||
| 20 | test "undefined 128 bit int" { | ||
| 21 | @setRuntimeSafety(true); | ||
| 22 | |||
| 23 | var undef: u128 = undefined; | ||
| 24 | var undef_signed: i128 = undefined; | ||
| 25 | try expect(undef == 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa and @bitCast(u128, undef_signed) == undef); | ||
| 26 | } | ||
| 27 | |||
| 28 | test "int128" { | ||
| 29 | var buff: i128 = -1; | ||
| 30 | try expect(buff < 0 and (buff + 1) == 0); | ||
| 31 | try expect(@intCast(i8, buff) == @as(i8, -1)); | ||
| 32 | |||
| 33 | buff = minInt(i128); | ||
| 34 | try expect(buff < 0); | ||
| 35 | |||
| 36 | // This should be uncommented once wrapping arithmetic is implemented for 128 bit ints: | ||
| 37 | // try expect(buff < 0 and (buff -% 1) > 0) | ||
| 38 | } | ||
| 39 | |||
| 40 | test "truncate int128" { | ||
| 41 | var buff: u128 = maxInt(u128); | ||
| 42 | try expect(@truncate(u64, buff) == maxInt(u64)); | ||
| 43 | } | ||
| \ No newline at end of file | |||
test/behavior/pointers.zig+8-4| ... | @@ -61,12 +61,16 @@ test "initialize const optional C pointer to null" { | ... | @@ -61,12 +61,16 @@ test "initialize const optional C pointer to null" { |
| 61 | 61 | ||
| 62 | test "assigning integer to C pointer" { | 62 | test "assigning integer to C pointer" { |
| 63 | var x: i32 = 0; | 63 | var x: i32 = 0; |
| 64 | var y: i32 = 1; | ||
| 64 | var ptr: [*c]u8 = 0; | 65 | var ptr: [*c]u8 = 0; |
| 65 | var ptr2: [*c]u8 = x; | 66 | var ptr2: [*c]u8 = x; |
| 66 | if (false) { | 67 | var ptr3: [*c]u8 = 1; |
| 67 | ptr; | 68 | var ptr4: [*c]u8 = y; |
| 68 | ptr2; | 69 | |
| 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); | ||
| 70 | } | 74 | } |
| 71 | 75 | ||
| 72 | test "C pointer comparison and arithmetic" { | 76 | test "C pointer comparison and arithmetic" { |