authorgravatar for reserveblue@protonmail.comdrew <reserveblue@protonmail.com> 2021-11-14 18:28:44-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-16 16:51:31-07:00
log9bf1681990fe87a6b2e5fc644a89f1aece304579
tree45617b8500c741e8de902852c0495b5c20279dbf
parent952d865bd231834adad30905c469edc5a46d000a

C backend: basic big ints, fix airPtrToInt, array references, pointer arithmetic UB with NULL, implement airPtrElemPtr/Val, fix redundant indirection/references with arrays

-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 type

6 files changed, 443 insertions(+), 270 deletions(-)

src/codegen/c.zig+138-28
......@@ -226,6 +226,36 @@ pub const DeclGen = struct {
226226 try dg.renderDeclName(decl, writer);
227227 }
228228
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
229259 fn renderValue(
230260 dg: *DeclGen,
231261 writer: anytype,
......@@ -240,18 +270,18 @@ pub const DeclGen = struct {
240270 const c_bits = toCIntBits(ty.intInfo(dg.module.getTarget()).bits) orelse
241271 return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
242272 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"),
273 8 => return writer.writeAll("0xaau"),
274 16 => return writer.writeAll("0xaaaau"),
275 32 => return writer.writeAll("0xaaaaaaaau"),
276 64 => return writer.writeAll("0xaaaaaaaaaaaaaaaau"),
277 128 => return renderBigInt(writer, @as(u128, 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)),
248278 else => unreachable,
249279 }
250280 },
251281 .Float => {
252282 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)"),
283 32 => return writer.writeAll("zig_bitcast_f32_u32(0xaaaaaaaau)"),
284 64 => return writer.writeAll("zig_bitcast_f64_u64(0xaaaaaaaaaaaaaaaau)"),
255285 else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}),
256286 }
257287 },
......@@ -265,10 +295,18 @@ pub const DeclGen = struct {
265295 }
266296 }
267297 switch (ty.zigTypeTag()) {
268 .Int => {
269 if (ty.isSignedInt())
270 return writer.print("{d}", .{val.toSignedInt()});
271 return writer.print("{d}", .{val.toUnsignedInt()});
298 .Int => switch (val.tag()) {
299 .int_big_positive => try renderBigInt(writer, val.castTag(.int_big_positive).?.asBigInt().to(u128) catch {
300 return dg.fail("TODO implement integer constants larger than 128 bits", .{});
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 }
272310 },
273311 .Float => {
274312 if (ty.floatBits(dg.module.getTarget()) <= 64) {
......@@ -286,8 +324,17 @@ pub const DeclGen = struct {
286324 return dg.fail("TODO: C backend: implement lowering large float values", .{});
287325 },
288326 .Pointer => switch (val.tag()) {
289 .null_value, .zero => try writer.writeAll("NULL"),
290 .one => try writer.writeAll("1"),
327 .null_value => try writer.writeAll("NULL"),
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 },
291338 .decl_ref => {
292339 const decl = val.castTag(.decl_ref).?.data;
293340 return dg.renderDeclValue(writer, ty, val, decl);
......@@ -316,6 +363,11 @@ pub const DeclGen = struct {
316363 const decl = val.castTag(.extern_fn).?.data;
317364 try dg.renderDeclName(decl, writer);
318365 },
366 .int_u64 => {
367 try writer.writeAll("((");
368 try dg.renderType(writer, ty);
369 try writer.print(")0x{x}u)", .{val.toUnsignedInt()});
370 },
319371 else => unreachable,
320372 },
321373 .Array => {
......@@ -728,6 +780,8 @@ pub const DeclGen = struct {
728780 .i32 => try w.writeAll("int32_t"),
729781 .u64 => try w.writeAll("uint64_t"),
730782 .i64 => try w.writeAll("int64_t"),
783 .u128 => try w.writeAll("uint128_t"),
784 .i128 => try w.writeAll("int128_t"),
731785 .usize => try w.writeAll("uintptr_t"),
732786 .isize => try w.writeAll("intptr_t"),
733787 .c_short => try w.writeAll("short"),
......@@ -787,8 +841,9 @@ pub const DeclGen = struct {
787841 },
788842 .Array => {
789843 // We are referencing the array so it will decay to a C pointer.
790 try dg.renderType(w, t.elemType());
791 return w.writeAll(" *");
844 // NB: arrays are not really types in C so they are either specified in the declaration
845 // or are already pointed to; our only job is to render the element's type.
846 return dg.renderType(w, t.elemType());
792847 },
793848 .Optional => {
794849 var opt_buf: Type.Payload.ElemType = undefined;
......@@ -1068,12 +1123,15 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
10681123 .unreach => try airUnreach(f),
10691124 .fence => try airFence(f, inst),
10701125
1126 .ptr_add => try airPtrAddSub (f, inst, " + "),
1127 .ptr_sub => try airPtrAddSub (f, inst, " - "),
1128
10711129 // TODO use a different strategy for add that communicates to the optimizer
10721130 // that wrapping is UB.
1073 .add, .ptr_add => try airBinOp (f, inst, " + "),
1131 .add => try airBinOp (f, inst, " + "),
10741132 // TODO use a different strategy for sub that communicates to the optimizer
10751133 // that wrapping is UB.
1076 .sub, .ptr_sub => try airBinOp (f, inst, " - "),
1134 .sub => try airBinOp (f, inst, " - "),
10771135 // TODO use a different strategy for mul that communicates to the optimizer
10781136 // that wrapping is UB.
10791137 .mul => try airBinOp (f, inst, " * "),
......@@ -1187,7 +1245,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
11871245 .ptr_slice_len_ptr => try airPtrSliceFieldPtr(f, inst, ".len;\n"),
11881246 .ptr_slice_ptr_ptr => try airPtrSliceFieldPtr(f, inst, ".ptr;\n"),
11891247
1190 .ptr_elem_val => try airPtrElemVal(f, inst, "["),
1248 .ptr_elem_val => try airPtrElemVal(f, inst),
11911249 .ptr_elem_ptr => try airPtrElemPtr(f, inst),
11921250 .slice_elem_val => try airSliceElemVal(f, inst),
11931251 .slice_elem_ptr => try airSliceElemPtr(f, inst),
......@@ -1240,20 +1298,39 @@ fn airPtrSliceFieldPtr(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !
12401298 return f.fail("TODO: C backend: airPtrSliceFieldPtr", .{});
12411299}
12421300
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;
1301fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
1302 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1303 const slice_ty = f.air.typeOf(bin_op.lhs);
1304 if (!slice_ty.isVolatilePtr() and f.liveness.isUnused(inst)) return CValue.none;
12471305
1248 _ = prefix;
1249 return f.fail("TODO: C backend: airPtrElemVal", .{});
1306 const arr = try f.resolveInst(bin_op.lhs);
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;
12501316}
12511317
12521318fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
1253 if (f.liveness.isUnused(inst))
1254 return CValue.none;
1319 if (f.liveness.isUnused(inst)) return CValue.none;
12551320
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;
12571334}
12581335
12591336fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
......@@ -1317,6 +1394,10 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
13171394 const local = try f.allocLocal(elem_type, mutability);
13181395 try writer.writeAll(";\n");
13191396
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
13201401 return CValue{ .local_ref = local.local };
13211402}
13221403
......@@ -1810,6 +1891,33 @@ fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue
18101891 return local;
18111892}
18121893
1894fn 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
18131921fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
18141922 if (f.liveness.isUnused(inst)) return CValue.none;
18151923
......@@ -2529,7 +2637,9 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {
25292637 const writer = f.object.writer();
25302638 const operand = try f.resolveInst(un_op);
25312639
2532 try writer.writeAll(" = ");
2640 try writer.writeAll(" = (");
2641 try f.renderType(writer, inst_ty);
2642 try writer.writeAll(")");
25332643 try f.writeCValue(writer, operand);
25342644 try writer.writeAll(";\n");
25352645 return local;
test/behavior.zig+5-4
......@@ -2,6 +2,7 @@ const builtin = @import("builtin");
22
33test {
44 // Tests that pass for stage1, stage2, and the C backend.
5 _ = @import("behavior/align.zig");
56 _ = @import("behavior/basic.zig");
67 _ = @import("behavior/bitcast.zig");
78 _ = @import("behavior/bool.zig");
......@@ -19,16 +20,18 @@ test {
1920 _ = @import("behavior/bugs/4769_b.zig");
2021 _ = @import("behavior/bugs/6850.zig");
2122 _ = @import("behavior/call.zig");
23 _ = @import("behavior/cast_c.zig");
2224 _ = @import("behavior/defer.zig");
2325 _ = @import("behavior/enum.zig");
2426 _ = @import("behavior/hasdecl.zig");
2527 _ = @import("behavior/hasfield.zig");
2628 _ = @import("behavior/if.zig");
27 _ = @import("behavior/struct.zig");
28 _ = @import("behavior/truncate.zig");
29 _ = @import("behavior/int128.zig");
2930 _ = @import("behavior/null.zig");
31 _ = @import("behavior/pointers.zig");
3032 _ = @import("behavior/ptrcast.zig");
3133 _ = @import("behavior/pub_enum.zig");
34 _ = @import("behavior/struct.zig");
3235 _ = @import("behavior/truncate.zig");
3336 _ = @import("behavior/underscore.zig");
3437 _ = @import("behavior/usingnamespace.zig");
......@@ -39,7 +42,6 @@ test {
3942
4043 if (builtin.object_format != .c) {
4144 // Tests that pass for stage1 and stage2 but not the C backend.
42 _ = @import("behavior/align.zig");
4345 _ = @import("behavior/array.zig");
4446 _ = @import("behavior/atomics.zig");
4547 _ = @import("behavior/basic_llvm.zig");
......@@ -60,7 +62,6 @@ test {
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-234
......@@ -2,70 +2,8 @@ const std = @import("std");
22const expect = std.testing.expect;
33const mem = std.mem;
44const maxInt = std.math.maxInt;
5const Vector = std.meta.Vector;
65const native_endian = @import("builtin").target.cpu.arch.endian();
76
8test "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
15test "integer literal to pointer cast" {
16 const vga_mem = @intToPtr(*u16, 0xB8000);
17 try expect(@ptrToInt(vga_mem) == 0xB8000);
18}
19
20test "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}
28fn 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
36test "resolve undefined with integer" {
37 try testResolveUndefWithInt(true, 1234);
38 comptime try testResolveUndefWithInt(true, 1234);
39}
40fn 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
47test "@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
54test "@intCast to comptime_int" {
55 try expect(@intCast(comptime_int, 0) == 0);
56}
57
58test "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
64test "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
697test "pointer reinterpret const float to int" {
708 // The hex representation is 0x3fe3333333333303.
719 const float: f64 = 5.99999999999994648725e-01;
......@@ -78,51 +16,15 @@ test "pointer reinterpret const float to int" {
7816 try expect(int_val == 0x3fe33333);
7917}
8018
81test "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
11019test "@floatToInt" {
11120 try testFloatToInts();
11221 comptime try testFloatToInts();
11322}
11423
11524fn 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);
12025 try expectFloatToInt(f16, 255.1, u8, 255);
12126 try expectFloatToInt(f16, 127.2, i8, 127);
12227 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);
12628}
12729
12830fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {
......@@ -143,95 +45,6 @@ fn incrementVoidPtrArray(array: ?*c_void, len: usize) void {
14345 }
14446}
14547
146test "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
173test "@intCast comptime_int" {
174 const result = @intCast(i32, 1234);
175 try expect(@TypeOf(result) == i32);
176 try expect(result == 1234);
177}
178
179test "@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
202test "coerce undefined to optional" {
203 try expect(MakeType(void).getNull() == null);
204 try expect(MakeType(void).getNonNull() != null);
205}
206
207fn 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
219test "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
229test "*usize to *void" {
230 var i = @as(usize, 0);
231 var v = @ptrCast(*void, &i);
232 v.* = {};
233}
234
23548test "compile time int to ptr of function" {
23649 try foobar(FUNCTION_CONSTANT);
23750}
......@@ -252,50 +65,3 @@ test "implicit ptr to *c_void" {
25265 var c: *u32 = @ptrCast(*u32, ptr2.?);
25366 try expect(c.* == 1);
25467}
255
256test "@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
262test "@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
273test "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
294test "*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 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const mem = std.mem;
4const maxInt = std.math.maxInt;
5
6test "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
13test "integer literal to pointer cast" {
14 const vga_mem = @intToPtr(*u16, 0xB8000);
15 try expect(@ptrToInt(vga_mem) == 0xB8000);
16}
17
18test "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}
26fn 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
34test "resolve undefined with integer" {
35 try testResolveUndefWithInt(true, 1234);
36 comptime try testResolveUndefWithInt(true, 1234);
37}
38fn 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
45test "@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
52test "@intCast to comptime_int" {
53 try expect(@intCast(comptime_int, 0) == 0);
54}
55
56test "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
62test "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
67test "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
96test "@floatToInt" {
97 try testFloatToInts();
98 comptime try testFloatToInts();
99}
100
101fn 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
111fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {
112 try expect(@floatToInt(I, f) == i);
113}
114
115test "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
142test "@intCast comptime_int" {
143 const result = @intCast(i32, 1234);
144 try expect(@TypeOf(result) == i32);
145 try expect(result == 1234);
146}
147
148test "@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
171test "coerce undefined to optional" {
172 try expect(MakeType(void).getNull() == null);
173 try expect(MakeType(void).getNonNull() != null);
174}
175
176fn 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
188test "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
198test "*usize to *void" {
199 var i = @as(usize, 0);
200 var v = @ptrCast(*void, &i);
201 v.* = {};
202}
203
204test "@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
210test "@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
221test "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
242test "*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 @@
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 var undef: u128 = undefined;
24 var undef_signed: i128 = undefined;
25 try expect(undef == 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa and @bitCast(u128, undef_signed) == undef);
26}
27
28test "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
40test "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" {
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" {