authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-26 13:46:27-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-26 13:46:27-07:00
logc6b3d06535f4227541c13fe75da347a485abdb4f
tree8f0a730e1dc7619168a81d2cd4521db1e7a1b4b6
parent6df26a37d13d21be061a1cccd39dd17e46a81322

Sema: improved C pointers and casting

* C pointer types always have allowzero set to true but they omit the word allowzero when printed. * Implement coercion from C pointers to other pointers. * Implement in-memory coercion for slices and pointer-like optionals. * Make slicing a C pointer drop the allowzero bit. * Value representation for pointer-like optionals is now allowed to use pointer tag values in addition to the `opt_payload` tag.

6 files changed, 339 insertions(+), 231 deletions(-)

src/Sema.zig+104-65
......@@ -9125,7 +9125,7 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
91259125 .pointee_type = elem_type,
91269126 .@"addrspace" = .generic,
91279127 .mutable = inst_data.is_mutable,
9128 .@"allowzero" = inst_data.is_allowzero,
9128 .@"allowzero" = inst_data.is_allowzero or inst_data.size == .C,
91299129 .@"volatile" = inst_data.is_volatile,
91309130 .size = inst_data.size,
91319131 });
......@@ -9185,7 +9185,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
91859185 .bit_offset = bit_start,
91869186 .host_size = bit_end,
91879187 .mutable = inst_data.flags.is_mutable,
9188 .@"allowzero" = inst_data.flags.is_allowzero,
9188 .@"allowzero" = inst_data.flags.is_allowzero or inst_data.size == .C,
91899189 .@"volatile" = inst_data.flags.is_volatile,
91909190 .size = inst_data.size,
91919191 });
......@@ -12102,6 +12102,21 @@ fn coerce(
1210212102 }
1210312103 }
1210412104
12105 // coercion from C pointer
12106 if (inst_ty.isCPtr()) src_c_ptr: {
12107 // In this case we must add a safety check because the C pointer
12108 // could be null.
12109 const src_elem_ty = inst_ty.childType();
12110 const dest_is_mut = dest_info.mutable;
12111 const dst_elem_type = dest_info.pointee_type;
12112 switch (coerceInMemoryAllowed(dst_elem_type, src_elem_ty, dest_is_mut, target)) {
12113 .ok => {},
12114 .no_match => break :src_c_ptr,
12115 }
12116 // TODO add safety check for null pointer
12117 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
12118 }
12119
1210512120 // coercion to C pointer
1210612121 if (dest_info.size == .C) {
1210712122 switch (inst_ty.zigTypeTag()) {
......@@ -12262,84 +12277,107 @@ const InMemoryCoercionResult = enum {
1226212277/// * sentinel-terminated pointers can coerce into `[*]`
1226312278/// TODO improve this function to report recursive compile errors like it does in stage1.
1226412279/// look at the function types_match_const_cast_only
12265fn coerceInMemoryAllowed(dest_ty: Type, src_type: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult {
12266 if (dest_ty.eql(src_type))
12280fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult {
12281 if (dest_ty.eql(src_ty))
1226712282 return .ok;
1226812283
12269 if (dest_ty.zigTypeTag() == .Pointer and
12270 src_type.zigTypeTag() == .Pointer)
12271 {
12272 const dest_info = dest_ty.ptrInfo().data;
12273 const src_info = src_type.ptrInfo().data;
12274
12275 const child = coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target);
12276 if (child == .no_match) {
12277 return child;
12284 // Pointers / Pointer-like Optionals
12285 var dest_buf: Type.Payload.ElemType = undefined;
12286 var src_buf: Type.Payload.ElemType = undefined;
12287 if (dest_ty.ptrOrOptionalPtrTy(&dest_buf)) |dest_ptr_ty| {
12288 if (src_ty.ptrOrOptionalPtrTy(&src_buf)) |src_ptr_ty| {
12289 return coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target);
1227812290 }
12291 }
1227912292
12280 if (dest_info.@"addrspace" != src_info.@"addrspace") {
12281 return .no_match;
12282 }
12293 // Slices
12294 if (dest_ty.isSlice() and src_ty.isSlice()) {
12295 return coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target);
12296 }
1228312297
12284 const ok_sent = dest_info.sentinel == null or src_info.size == .C or
12285 (src_info.sentinel != null and
12286 dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type));
12287 if (!ok_sent) {
12288 return .no_match;
12289 }
12298 // TODO: arrays
12299 // TODO: non-pointer-like optionals
12300 // TODO: error unions
12301 // TODO: error sets
12302 // TODO: functions
12303 // TODO: vectors
1229012304
12291 const ok_ptr_size = src_info.size == dest_info.size or
12292 src_info.size == .C or dest_info.size == .C;
12293 if (!ok_ptr_size) {
12294 return .no_match;
12295 }
12305 return .no_match;
12306}
1229612307
12297 const ok_cv_qualifiers =
12298 (src_info.mutable or !dest_info.mutable) and
12299 (!src_info.@"volatile" or dest_info.@"volatile");
12308fn coerceInMemoryAllowedPtrs(
12309 dest_ty: Type,
12310 src_ty: Type,
12311 dest_ptr_ty: Type,
12312 src_ptr_ty: Type,
12313 dest_is_mut: bool,
12314 target: std.Target,
12315) InMemoryCoercionResult {
12316 const dest_info = dest_ptr_ty.ptrInfo().data;
12317 const src_info = src_ptr_ty.ptrInfo().data;
1230012318
12301 if (!ok_cv_qualifiers) {
12302 return .no_match;
12303 }
12319 const child = coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target);
12320 if (child == .no_match) {
12321 return child;
12322 }
1230412323
12305 const ok_allows_zero = (dest_info.@"allowzero" and
12306 (src_info.@"allowzero" or !dest_is_mut)) or
12307 (!dest_info.@"allowzero" and !src_info.@"allowzero");
12308 if (!ok_allows_zero) {
12309 return .no_match;
12310 }
12324 if (dest_info.@"addrspace" != src_info.@"addrspace") {
12325 return .no_match;
12326 }
1231112327
12312 if (dest_ty.hasCodeGenBits() != src_type.hasCodeGenBits()) {
12313 return .no_match;
12314 }
12328 const ok_sent = dest_info.sentinel == null or src_info.size == .C or
12329 (src_info.sentinel != null and
12330 dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type));
12331 if (!ok_sent) {
12332 return .no_match;
12333 }
1231512334
12316 if (src_info.host_size != dest_info.host_size or
12317 src_info.bit_offset != dest_info.bit_offset)
12318 {
12319 return .no_match;
12320 }
12335 const ok_ptr_size = src_info.size == dest_info.size or
12336 src_info.size == .C or dest_info.size == .C;
12337 if (!ok_ptr_size) {
12338 return .no_match;
12339 }
1232112340
12322 // If both pointers have alignment 0, it means they both want ABI alignment.
12323 // In this case, if they share the same child type, no need to resolve
12324 // pointee type alignment. Otherwise both pointee types must have their alignment
12325 // resolved and we compare the alignment numerically.
12326 if (src_info.@"align" != 0 or dest_info.@"align" != 0 or
12327 !dest_info.pointee_type.eql(src_info.pointee_type))
12328 {
12329 const src_align = src_type.ptrAlignment(target);
12330 const dest_align = dest_ty.ptrAlignment(target);
12341 const ok_cv_qualifiers =
12342 (src_info.mutable or !dest_info.mutable) and
12343 (!src_info.@"volatile" or dest_info.@"volatile");
1233112344
12332 if (dest_align > src_align) {
12333 return .no_match;
12334 }
12335 }
12345 if (!ok_cv_qualifiers) {
12346 return .no_match;
12347 }
1233612348
12337 return .ok;
12349 const dest_allow_zero = dest_ty.ptrAllowsZero();
12350 const src_allow_zero = src_ty.ptrAllowsZero();
12351
12352 const ok_allows_zero = (dest_allow_zero and
12353 (src_allow_zero or !dest_is_mut)) or
12354 (!dest_allow_zero and !src_allow_zero);
12355 if (!ok_allows_zero) {
12356 return .no_match;
1233812357 }
1233912358
12340 // TODO: implement more of this function
12359 if (src_info.host_size != dest_info.host_size or
12360 src_info.bit_offset != dest_info.bit_offset)
12361 {
12362 return .no_match;
12363 }
1234112364
12342 return .no_match;
12365 // If both pointers have alignment 0, it means they both want ABI alignment.
12366 // In this case, if they share the same child type, no need to resolve
12367 // pointee type alignment. Otherwise both pointee types must have their alignment
12368 // resolved and we compare the alignment numerically.
12369 if (src_info.@"align" != 0 or dest_info.@"align" != 0 or
12370 !dest_info.pointee_type.eql(src_info.pointee_type))
12371 {
12372 const src_align = src_info.@"align";
12373 const dest_align = dest_info.@"align";
12374
12375 if (dest_align > src_align) {
12376 return .no_match;
12377 }
12378 }
12379
12380 return .ok;
1234312381}
1234412382
1234512383fn coerceNum(
......@@ -13297,6 +13335,7 @@ fn analyzeSlice(
1329713335 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
1329813336
1329913337 const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;
13338 const new_allowzero = new_ptr_ty_info.@"allowzero" and sema.typeOf(ptr).ptrSize() != .C;
1330013339
1330113340 if (opt_new_len_val) |new_len_val| {
1330213341 const new_len_int = new_len_val.toUnsignedInt();
......@@ -13312,7 +13351,7 @@ fn analyzeSlice(
1331213351 .@"align" = new_ptr_ty_info.@"align",
1331313352 .@"addrspace" = new_ptr_ty_info.@"addrspace",
1331413353 .mutable = new_ptr_ty_info.mutable,
13315 .@"allowzero" = new_ptr_ty_info.@"allowzero",
13354 .@"allowzero" = new_allowzero,
1331613355 .@"volatile" = new_ptr_ty_info.@"volatile",
1331713356 .size = .One,
1331813357 });
......@@ -13340,7 +13379,7 @@ fn analyzeSlice(
1334013379 .@"align" = new_ptr_ty_info.@"align",
1334113380 .@"addrspace" = new_ptr_ty_info.@"addrspace",
1334213381 .mutable = new_ptr_ty_info.mutable,
13343 .@"allowzero" = new_ptr_ty_info.@"allowzero",
13382 .@"allowzero" = new_allowzero,
1334413383 .@"volatile" = new_ptr_ty_info.@"volatile",
1334513384 .size = .Slice,
1334613385 });
src/codegen/llvm.zig+2
......@@ -1184,6 +1184,8 @@ pub const DeclGen = struct {
11841184 if (tv.ty.isPtrLikeOptional()) {
11851185 if (tv.val.castTag(.opt_payload)) |payload| {
11861186 return self.genTypedValue(.{ .ty = payload_ty, .val = payload.data });
1187 } else if (is_pl) {
1188 return self.genTypedValue(.{ .ty = payload_ty, .val = tv.val });
11871189 } else {
11881190 const llvm_ty = try self.llvmType(tv.ty);
11891191 return llvm_ty.constNull();
src/type.zig+72-15
......@@ -390,7 +390,7 @@ pub const Type = extern union {
390390 .@"addrspace" = .generic,
391391 .bit_offset = 0,
392392 .host_size = 0,
393 .@"allowzero" = false,
393 .@"allowzero" = true,
394394 .mutable = false,
395395 .@"volatile" = false,
396396 .size = .C,
......@@ -402,7 +402,7 @@ pub const Type = extern union {
402402 .@"addrspace" = .generic,
403403 .bit_offset = 0,
404404 .host_size = 0,
405 .@"allowzero" = false,
405 .@"allowzero" = true,
406406 .mutable = true,
407407 .@"volatile" = false,
408408 .size = .C,
......@@ -1153,7 +1153,7 @@ pub const Type = extern union {
11531153 }
11541154 if (!payload.mutable) try writer.writeAll("const ");
11551155 if (payload.@"volatile") try writer.writeAll("volatile ");
1156 if (payload.@"allowzero") try writer.writeAll("allowzero ");
1156 if (payload.@"allowzero" and payload.size != .C) try writer.writeAll("allowzero ");
11571157
11581158 ty = payload.pointee_type;
11591159 continue;
......@@ -2347,7 +2347,48 @@ pub const Type = extern union {
23472347 }
23482348 }
23492349
2350 /// Asserts that the type is an optional or a pointer that can be null.
2350 /// For pointer-like optionals, returns true, otherwise returns the allowzero property
2351 /// of pointers.
2352 pub fn ptrAllowsZero(ty: Type) bool {
2353 if (ty.isPtrLikeOptional()) {
2354 return true;
2355 }
2356 return ty.ptrInfo().data.@"allowzero";
2357 }
2358
2359 /// For pointer-like optionals, it returns the pointer type. For pointers,
2360 /// the type is returned unmodified.
2361 pub fn ptrOrOptionalPtrTy(ty: Type, buf: *Payload.ElemType) ?Type {
2362 if (isPtrLikeOptional(ty)) return ty.optionalChild(buf);
2363 switch (ty.tag()) {
2364 .c_const_pointer,
2365 .c_mut_pointer,
2366 .single_const_pointer_to_comptime_int,
2367 .single_const_pointer,
2368 .single_mut_pointer,
2369 .many_const_pointer,
2370 .many_mut_pointer,
2371 .manyptr_u8,
2372 .manyptr_const_u8,
2373 => return ty,
2374
2375 .pointer => {
2376 if (ty.ptrSize() == .Slice) {
2377 return null;
2378 } else {
2379 return ty;
2380 }
2381 },
2382
2383 .inferred_alloc_const => unreachable,
2384 .inferred_alloc_mut => unreachable,
2385
2386 else => return null,
2387 }
2388 }
2389
2390 /// Returns true if the type is optional and would be lowered to a single pointer
2391 /// address value, using 0 for null. Note that this returns true for C pointers.
23512392 pub fn isPtrLikeOptional(self: Type) bool {
23522393 switch (self.tag()) {
23532394 .optional_single_const_pointer,
......@@ -2371,7 +2412,8 @@ pub const Type = extern union {
23712412 },
23722413
23732414 .pointer => return self.castTag(.pointer).?.data.size == .C,
2374 else => unreachable,
2415
2416 else => return false,
23752417 }
23762418 }
23772419
......@@ -2532,38 +2574,50 @@ pub const Type = extern union {
25322574
25332575 /// Asserts that the type is an optional.
25342576 /// Resulting `Type` will have inner memory referencing `buf`.
2535 pub fn optionalChild(self: Type, buf: *Payload.ElemType) Type {
2536 return switch (self.tag()) {
2537 .optional => self.castTag(.optional).?.data,
2577 /// Note that for C pointers this returns the type unmodified.
2578 pub fn optionalChild(ty: Type, buf: *Payload.ElemType) Type {
2579 return switch (ty.tag()) {
2580 .optional => ty.castTag(.optional).?.data,
25382581 .optional_single_mut_pointer => {
25392582 buf.* = .{
25402583 .base = .{ .tag = .single_mut_pointer },
2541 .data = self.castPointer().?.data,
2584 .data = ty.castPointer().?.data,
25422585 };
25432586 return Type.initPayload(&buf.base);
25442587 },
25452588 .optional_single_const_pointer => {
25462589 buf.* = .{
25472590 .base = .{ .tag = .single_const_pointer },
2548 .data = self.castPointer().?.data,
2591 .data = ty.castPointer().?.data,
25492592 };
25502593 return Type.initPayload(&buf.base);
25512594 },
2595
2596 .pointer, // here we assume it is a C pointer
2597 .c_const_pointer,
2598 .c_mut_pointer,
2599 => return ty,
2600
25522601 else => unreachable,
25532602 };
25542603 }
25552604
25562605 /// Asserts that the type is an optional.
25572606 /// Same as `optionalChild` but allocates the buffer if needed.
2558 pub fn optionalChildAlloc(self: Type, allocator: *Allocator) !Type {
2559 switch (self.tag()) {
2560 .optional => return self.castTag(.optional).?.data,
2607 pub fn optionalChildAlloc(ty: Type, allocator: *Allocator) !Type {
2608 switch (ty.tag()) {
2609 .optional => return ty.castTag(.optional).?.data,
25612610 .optional_single_mut_pointer => {
2562 return Tag.single_mut_pointer.create(allocator, self.castPointer().?.data);
2611 return Tag.single_mut_pointer.create(allocator, ty.castPointer().?.data);
25632612 },
25642613 .optional_single_const_pointer => {
2565 return Tag.single_const_pointer.create(allocator, self.castPointer().?.data);
2614 return Tag.single_const_pointer.create(allocator, ty.castPointer().?.data);
25662615 },
2616 .pointer, // here we assume it is a C pointer
2617 .c_const_pointer,
2618 .c_mut_pointer,
2619 => return ty,
2620
25672621 else => unreachable,
25682622 }
25692623 }
......@@ -4050,6 +4104,9 @@ pub const Type = extern union {
40504104 if (d.sentinel != null or d.@"align" != 0 or d.@"addrspace" != .generic or
40514105 d.bit_offset != 0 or d.host_size != 0 or d.@"allowzero" or d.@"volatile")
40524106 {
4107 if (d.size == .C) {
4108 assert(d.@"allowzero"); // All C pointers must set allowzero to true.
4109 }
40534110 return Type.Tag.pointer.create(arena, d);
40544111 }
40554112
src/value.zig+2-1
......@@ -1819,7 +1819,8 @@ pub const Value = extern union {
18191819 .unreachable_value => unreachable,
18201820 .inferred_alloc => unreachable,
18211821 .inferred_alloc_comptime => unreachable,
1822 else => unreachable,
1822
1823 else => false,
18231824 };
18241825 }
18251826
test/behavior/cast.zig+157
......@@ -120,3 +120,160 @@ fn incrementVoidPtrArray(array: ?*c_void, len: usize) void {
120120 @ptrCast([*]u8, array.?)[n] += 1;
121121 }
122122}
123
124test "implicitly cast indirect pointer to maybe-indirect pointer" {
125 const S = struct {
126 const Self = @This();
127 x: u8,
128 fn constConst(p: *const *const Self) u8 {
129 return p.*.x;
130 }
131 fn maybeConstConst(p: ?*const *const Self) u8 {
132 return p.?.*.x;
133 }
134 fn constConstConst(p: *const *const *const Self) u8 {
135 return p.*.*.x;
136 }
137 fn maybeConstConstConst(p: ?*const *const *const Self) u8 {
138 return p.?.*.*.x;
139 }
140 };
141 const s = S{ .x = 42 };
142 const p = &s;
143 const q = &p;
144 const r = &q;
145 try expect(42 == S.constConst(q));
146 try expect(42 == S.maybeConstConst(q));
147 try expect(42 == S.constConstConst(r));
148 try expect(42 == S.maybeConstConstConst(r));
149}
150
151test "@intCast comptime_int" {
152 const result = @intCast(i32, 1234);
153 try expect(@TypeOf(result) == i32);
154 try expect(result == 1234);
155}
156
157test "@floatCast comptime_int and comptime_float" {
158 {
159 const result = @floatCast(f16, 1234);
160 try expect(@TypeOf(result) == f16);
161 try expect(result == 1234.0);
162 }
163 {
164 const result = @floatCast(f16, 1234.0);
165 try expect(@TypeOf(result) == f16);
166 try expect(result == 1234.0);
167 }
168 {
169 const result = @floatCast(f32, 1234);
170 try expect(@TypeOf(result) == f32);
171 try expect(result == 1234.0);
172 }
173 {
174 const result = @floatCast(f32, 1234.0);
175 try expect(@TypeOf(result) == f32);
176 try expect(result == 1234.0);
177 }
178}
179
180test "coerce undefined to optional" {
181 try expect(MakeType(void).getNull() == null);
182 try expect(MakeType(void).getNonNull() != null);
183}
184
185fn MakeType(comptime T: type) type {
186 return struct {
187 fn getNull() ?T {
188 return null;
189 }
190
191 fn getNonNull() ?T {
192 return @as(T, undefined);
193 }
194 };
195}
196
197test "implicit cast from *[N]T to [*c]T" {
198 var x: [4]u16 = [4]u16{ 0, 1, 2, 3 };
199 var y: [*c]u16 = &x;
200
201 try expect(std.mem.eql(u16, x[0..4], y[0..4]));
202 x[0] = 8;
203 y[3] = 6;
204 try expect(std.mem.eql(u16, x[0..4], y[0..4]));
205}
206
207test "*usize to *void" {
208 var i = @as(usize, 0);
209 var v = @ptrCast(*void, &i);
210 v.* = {};
211}
212
213test "compile time int to ptr of function" {
214 try foobar(FUNCTION_CONSTANT);
215}
216
217pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize));
218pub const PFN_void = fn (*c_void) callconv(.C) void;
219
220fn foobar(func: PFN_void) !void {
221 try std.testing.expect(@ptrToInt(func) == maxInt(usize));
222}
223
224test "implicit ptr to *c_void" {
225 var a: u32 = 1;
226 var ptr: *align(@alignOf(u32)) c_void = &a;
227 var b: *u32 = @ptrCast(*u32, ptr);
228 try expect(b.* == 1);
229 var ptr2: ?*align(@alignOf(u32)) c_void = &a;
230 var c: *u32 = @ptrCast(*u32, ptr2.?);
231 try expect(c.* == 1);
232}
233
234test "@intToEnum passed a comptime_int to an enum with one item" {
235 const E = enum { A };
236 const x = @intToEnum(E, 0);
237 try expect(x == E.A);
238}
239
240test "@intCast to u0 and use the result" {
241 const S = struct {
242 fn doTheTest(zero: u1, one: u1, bigzero: i32) !void {
243 try expect((one << @intCast(u0, bigzero)) == 1);
244 try expect((zero << @intCast(u0, bigzero)) == 0);
245 }
246 };
247 try S.doTheTest(0, 1, 0);
248 comptime try S.doTheTest(0, 1, 0);
249}
250
251test "peer result null and comptime_int" {
252 const S = struct {
253 fn blah(n: i32) ?i32 {
254 if (n == 0) {
255 return null;
256 } else if (n < 0) {
257 return -1;
258 } else {
259 return 1;
260 }
261 }
262 };
263
264 try expect(S.blah(0) == null);
265 comptime try expect(S.blah(0) == null);
266 try expect(S.blah(10).? == 1);
267 comptime try expect(S.blah(10).? == 1);
268 try expect(S.blah(-10).? == -1);
269 comptime try expect(S.blah(-10).? == -1);
270}
271
272test "*const ?[*]const T to [*c]const [*c]const T" {
273 var array = [_]u8{ 'o', 'k' };
274 const opt_array_ptr: ?[*]const u8 = &array;
275 const a: *const ?[*]const u8 = &opt_array_ptr;
276 const b: [*c]const [*c]const u8 = a;
277 try expect(b.*[0] == 'o');
278 try expect(b[0][1] == 'k');
279}
test/behavior/cast_stage1.zig+2-150
......@@ -5,33 +5,6 @@ const maxInt = std.math.maxInt;
55const Vector = std.meta.Vector;
66const native_endian = @import("builtin").target.cpu.arch.endian();
77
8test "implicitly cast indirect pointer to maybe-indirect pointer" {
9 const S = struct {
10 const Self = @This();
11 x: u8,
12 fn constConst(p: *const *const Self) u8 {
13 return p.*.x;
14 }
15 fn maybeConstConst(p: ?*const *const Self) u8 {
16 return p.?.*.x;
17 }
18 fn constConstConst(p: *const *const *const Self) u8 {
19 return p.*.*.x;
20 }
21 fn maybeConstConstConst(p: ?*const *const *const Self) u8 {
22 return p.?.*.*.x;
23 }
24 };
25 const s = S{ .x = 42 };
26 const p = &s;
27 const q = &p;
28 const r = &q;
29 try expect(42 == S.constConst(q));
30 try expect(42 == S.maybeConstConst(q));
31 try expect(42 == S.constConstConst(r));
32 try expect(42 == S.maybeConstConstConst(r));
33}
34
358test "explicit cast from integer to error type" {
369 try testCastIntToErr(error.ItBroke);
3710 comptime try testCastIntToErr(error.ItBroke);
......@@ -175,7 +148,7 @@ fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 {
175148 return slice[0..1];
176149}
177150
178test "implicit cast from &const [N]T to []const T" {
151test "implicit cast from *const [N]T to []const T" {
179152 try testCastConstArrayRefToConstSlice();
180153 comptime try testCastConstArrayRefToConstSlice();
181154}
......@@ -258,7 +231,7 @@ fn cast128Float(x: u128) f128 {
258231 return @bitCast(f128, x);
259232}
260233
261test "single-item pointer of array to slice and to unknown length pointer" {
234test "single-item pointer of array to slice to unknown length pointer" {
262235 try testCastPtrOfArrayToSliceAndPtr();
263236 comptime try testCastPtrOfArrayToSliceAndPtr();
264237}
......@@ -290,35 +263,6 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
290263 try expect(mem.eql(u8, std.mem.spanZ(@ptrCast([*:0]const u8, x[0].?)), "window name"));
291264}
292265
293test "@intCast comptime_int" {
294 const result = @intCast(i32, 1234);
295 try expect(@TypeOf(result) == i32);
296 try expect(result == 1234);
297}
298
299test "@floatCast comptime_int and comptime_float" {
300 {
301 const result = @floatCast(f16, 1234);
302 try expect(@TypeOf(result) == f16);
303 try expect(result == 1234.0);
304 }
305 {
306 const result = @floatCast(f16, 1234.0);
307 try expect(@TypeOf(result) == f16);
308 try expect(result == 1234.0);
309 }
310 {
311 const result = @floatCast(f32, 1234);
312 try expect(@TypeOf(result) == f32);
313 try expect(result == 1234.0);
314 }
315 {
316 const result = @floatCast(f32, 1234.0);
317 try expect(@TypeOf(result) == f32);
318 try expect(result == 1234.0);
319 }
320}
321
322266test "vector casts" {
323267 const S = struct {
324268 fn doTheTest() !void {
......@@ -369,23 +313,6 @@ test "@floatCast cast down" {
369313 }
370314}
371315
372test "implicit cast undefined to optional" {
373 try expect(MakeType(void).getNull() == null);
374 try expect(MakeType(void).getNonNull() != null);
375}
376
377fn MakeType(comptime T: type) type {
378 return struct {
379 fn getNull() ?T {
380 return null;
381 }
382
383 fn getNonNull() ?T {
384 return @as(T, undefined);
385 }
386 };
387}
388
389316test "implicit cast from *[N]T to ?[*]T" {
390317 var x: ?[*]u16 = null;
391318 var y: [4]u16 = [4]u16{ 0, 1, 2, 3 };
......@@ -397,16 +324,6 @@ test "implicit cast from *[N]T to ?[*]T" {
397324 try expect(std.mem.eql(u16, x.?[0..4], y[0..4]));
398325}
399326
400test "implicit cast from *[N]T to [*c]T" {
401 var x: [4]u16 = [4]u16{ 0, 1, 2, 3 };
402 var y: [*c]u16 = &x;
403
404 try expect(std.mem.eql(u16, x[0..4], y[0..4]));
405 x[0] = 8;
406 y[3] = 6;
407 try expect(std.mem.eql(u16, x[0..4], y[0..4]));
408}
409
410327test "implicit cast from *T to ?*c_void" {
411328 var a: u8 = 1;
412329 incrementVoidPtrValue(&a);
......@@ -417,50 +334,6 @@ fn incrementVoidPtrValue(value: ?*c_void) void {
417334 @ptrCast(*u8, value.?).* += 1;
418335}
419336
420test "*usize to *void" {
421 var i = @as(usize, 0);
422 var v = @ptrCast(*void, &i);
423 v.* = {};
424}
425
426test "compile time int to ptr of function" {
427 try foobar(FUNCTION_CONSTANT);
428}
429
430pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize));
431pub const PFN_void = fn (*c_void) callconv(.C) void;
432
433fn foobar(func: PFN_void) !void {
434 try std.testing.expect(@ptrToInt(func) == maxInt(usize));
435}
436
437test "implicit ptr to *c_void" {
438 var a: u32 = 1;
439 var ptr: *align(@alignOf(u32)) c_void = &a;
440 var b: *u32 = @ptrCast(*u32, ptr);
441 try expect(b.* == 1);
442 var ptr2: ?*align(@alignOf(u32)) c_void = &a;
443 var c: *u32 = @ptrCast(*u32, ptr2.?);
444 try expect(c.* == 1);
445}
446
447test "@intToEnum passed a comptime_int to an enum with one item" {
448 const E = enum { A };
449 const x = @intToEnum(E, 0);
450 try expect(x == E.A);
451}
452
453test "@intCast to u0 and use the result" {
454 const S = struct {
455 fn doTheTest(zero: u1, one: u1, bigzero: i32) !void {
456 try expect((one << @intCast(u0, bigzero)) == 1);
457 try expect((zero << @intCast(u0, bigzero)) == 0);
458 }
459 };
460 try S.doTheTest(0, 1, 0);
461 comptime try S.doTheTest(0, 1, 0);
462}
463
464337test "peer type resolution: unreachable, null, slice" {
465338 const S = struct {
466339 fn doTheTest(num: usize, word: []const u8) !void {
......@@ -639,27 +512,6 @@ test "return u8 coercing into ?u32 return type" {
639512 comptime try S.doTheTest();
640513}
641514
642test "peer result null and comptime_int" {
643 const S = struct {
644 fn blah(n: i32) ?i32 {
645 if (n == 0) {
646 return null;
647 } else if (n < 0) {
648 return -1;
649 } else {
650 return 1;
651 }
652 }
653 };
654
655 try expect(S.blah(0) == null);
656 comptime try expect(S.blah(0) == null);
657 try expect(S.blah(10).? == 1);
658 comptime try expect(S.blah(10).? == 1);
659 try expect(S.blah(-10).? == -1);
660 comptime try expect(S.blah(-10).? == -1);
661}
662
663515test "peer type resolution implicit cast to return type" {
664516 const S = struct {
665517 fn doTheTest() !void {