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...@@ -9125,7 +9125,7 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
9125 .pointee_type = elem_type,9125 .pointee_type = elem_type,
9126 .@"addrspace" = .generic,9126 .@"addrspace" = .generic,
9127 .mutable = inst_data.is_mutable,9127 .mutable = inst_data.is_mutable,
9128 .@"allowzero" = inst_data.is_allowzero,9128 .@"allowzero" = inst_data.is_allowzero or inst_data.size == .C,
9129 .@"volatile" = inst_data.is_volatile,9129 .@"volatile" = inst_data.is_volatile,
9130 .size = inst_data.size,9130 .size = inst_data.size,
9131 });9131 });
...@@ -9185,7 +9185,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -9185,7 +9185,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
9185 .bit_offset = bit_start,9185 .bit_offset = bit_start,
9186 .host_size = bit_end,9186 .host_size = bit_end,
9187 .mutable = inst_data.flags.is_mutable,9187 .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,
9189 .@"volatile" = inst_data.flags.is_volatile,9189 .@"volatile" = inst_data.flags.is_volatile,
9190 .size = inst_data.size,9190 .size = inst_data.size,
9191 });9191 });
...@@ -12102,6 +12102,21 @@ fn coerce(...@@ -12102,6 +12102,21 @@ fn coerce(
12102 }12102 }
12103 }12103 }
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
12105 // coercion to C pointer12120 // coercion to C pointer
12106 if (dest_info.size == .C) {12121 if (dest_info.size == .C) {
12107 switch (inst_ty.zigTypeTag()) {12122 switch (inst_ty.zigTypeTag()) {
...@@ -12262,84 +12277,107 @@ const InMemoryCoercionResult = enum {...@@ -12262,84 +12277,107 @@ const InMemoryCoercionResult = enum {
12262/// * sentinel-terminated pointers can coerce into `[*]`12277/// * sentinel-terminated pointers can coerce into `[*]`
12263/// TODO improve this function to report recursive compile errors like it does in stage1.12278/// TODO improve this function to report recursive compile errors like it does in stage1.
12264/// look at the function types_match_const_cast_only12279/// 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 {12280fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult {
12266 if (dest_ty.eql(src_type))12281 if (dest_ty.eql(src_ty))
12267 return .ok;12282 return .ok;
1226812283
12269 if (dest_ty.zigTypeTag() == .Pointer and12284 // Pointers / Pointer-like Optionals
12270 src_type.zigTypeTag() == .Pointer)12285 var dest_buf: Type.Payload.ElemType = undefined;
12271 {12286 var src_buf: Type.Payload.ElemType = undefined;
12272 const dest_info = dest_ty.ptrInfo().data;12287 if (dest_ty.ptrOrOptionalPtrTy(&dest_buf)) |dest_ptr_ty| {
12273 const src_info = src_type.ptrInfo().data;12288 if (src_ty.ptrOrOptionalPtrTy(&src_buf)) |src_ptr_ty| {
1227412289 return coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target);
12275 const child = coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target);
12276 if (child == .no_match) {
12277 return child;
12278 }12290 }
12291 }
1227912292
12280 if (dest_info.@"addrspace" != src_info.@"addrspace") {12293 // Slices
12281 return .no_match;12294 if (dest_ty.isSlice() and src_ty.isSlice()) {
12282 }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 or12298 // TODO: arrays
12285 (src_info.sentinel != null and12299 // TODO: non-pointer-like optionals
12286 dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type));12300 // TODO: error unions
12287 if (!ok_sent) {12301 // TODO: error sets
12288 return .no_match;12302 // TODO: functions
12289 }12303 // TODO: vectors
1229012304
12291 const ok_ptr_size = src_info.size == dest_info.size or12305 return .no_match;
12292 src_info.size == .C or dest_info.size == .C;12306}
12293 if (!ok_ptr_size) {
12294 return .no_match;
12295 }
1229612307
12297 const ok_cv_qualifiers =12308fn coerceInMemoryAllowedPtrs(
12298 (src_info.mutable or !dest_info.mutable) and12309 dest_ty: Type,
12299 (!src_info.@"volatile" or dest_info.@"volatile");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) {12319 const child = coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target);
12302 return .no_match;12320 if (child == .no_match) {
12303 }12321 return child;
12322 }
1230412323
12305 const ok_allows_zero = (dest_info.@"allowzero" and12324 if (dest_info.@"addrspace" != src_info.@"addrspace") {
12306 (src_info.@"allowzero" or !dest_is_mut)) or12325 return .no_match;
12307 (!dest_info.@"allowzero" and !src_info.@"allowzero");12326 }
12308 if (!ok_allows_zero) {
12309 return .no_match;
12310 }
1231112327
12312 if (dest_ty.hasCodeGenBits() != src_type.hasCodeGenBits()) {12328 const ok_sent = dest_info.sentinel == null or src_info.size == .C or
12313 return .no_match;12329 (src_info.sentinel != null and
12314 }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 or12335 const ok_ptr_size = src_info.size == dest_info.size or
12317 src_info.bit_offset != dest_info.bit_offset)12336 src_info.size == .C or dest_info.size == .C;
12318 {12337 if (!ok_ptr_size) {
12319 return .no_match;12338 return .no_match;
12320 }12339 }
1232112340
12322 // If both pointers have alignment 0, it means they both want ABI alignment.12341 const ok_cv_qualifiers =
12323 // In this case, if they share the same child type, no need to resolve12342 (src_info.mutable or !dest_info.mutable) and
12324 // pointee type alignment. Otherwise both pointee types must have their alignment12343 (!src_info.@"volatile" or dest_info.@"volatile");
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);
1233112344
12332 if (dest_align > src_align) {12345 if (!ok_cv_qualifiers) {
12333 return .no_match;12346 return .no_match;
12334 }12347 }
12335 }
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;
12338 }12357 }
1233912358
12340 // TODO: implement more of this function12359 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;
12343}12381}
1234412382
12345fn coerceNum(12383fn coerceNum(
...@@ -13297,6 +13335,7 @@ fn analyzeSlice(...@@ -13297,6 +13335,7 @@ fn analyzeSlice(
13297 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);13335 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
1329813336
13299 const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;13337 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
13301 if (opt_new_len_val) |new_len_val| {13340 if (opt_new_len_val) |new_len_val| {
13302 const new_len_int = new_len_val.toUnsignedInt();13341 const new_len_int = new_len_val.toUnsignedInt();
...@@ -13312,7 +13351,7 @@ fn analyzeSlice(...@@ -13312,7 +13351,7 @@ fn analyzeSlice(
13312 .@"align" = new_ptr_ty_info.@"align",13351 .@"align" = new_ptr_ty_info.@"align",
13313 .@"addrspace" = new_ptr_ty_info.@"addrspace",13352 .@"addrspace" = new_ptr_ty_info.@"addrspace",
13314 .mutable = new_ptr_ty_info.mutable,13353 .mutable = new_ptr_ty_info.mutable,
13315 .@"allowzero" = new_ptr_ty_info.@"allowzero",13354 .@"allowzero" = new_allowzero,
13316 .@"volatile" = new_ptr_ty_info.@"volatile",13355 .@"volatile" = new_ptr_ty_info.@"volatile",
13317 .size = .One,13356 .size = .One,
13318 });13357 });
...@@ -13340,7 +13379,7 @@ fn analyzeSlice(...@@ -13340,7 +13379,7 @@ fn analyzeSlice(
13340 .@"align" = new_ptr_ty_info.@"align",13379 .@"align" = new_ptr_ty_info.@"align",
13341 .@"addrspace" = new_ptr_ty_info.@"addrspace",13380 .@"addrspace" = new_ptr_ty_info.@"addrspace",
13342 .mutable = new_ptr_ty_info.mutable,13381 .mutable = new_ptr_ty_info.mutable,
13343 .@"allowzero" = new_ptr_ty_info.@"allowzero",13382 .@"allowzero" = new_allowzero,
13344 .@"volatile" = new_ptr_ty_info.@"volatile",13383 .@"volatile" = new_ptr_ty_info.@"volatile",
13345 .size = .Slice,13384 .size = .Slice,
13346 });13385 });
src/codegen/llvm.zig+2
...@@ -1184,6 +1184,8 @@ pub const DeclGen = struct {...@@ -1184,6 +1184,8 @@ pub const DeclGen = struct {
1184 if (tv.ty.isPtrLikeOptional()) {1184 if (tv.ty.isPtrLikeOptional()) {
1185 if (tv.val.castTag(.opt_payload)) |payload| {1185 if (tv.val.castTag(.opt_payload)) |payload| {
1186 return self.genTypedValue(.{ .ty = payload_ty, .val = payload.data });1186 return self.genTypedValue(.{ .ty = payload_ty, .val = payload.data });
1187 } else if (is_pl) {
1188 return self.genTypedValue(.{ .ty = payload_ty, .val = tv.val });
1187 } else {1189 } else {
1188 const llvm_ty = try self.llvmType(tv.ty);1190 const llvm_ty = try self.llvmType(tv.ty);
1189 return llvm_ty.constNull();1191 return llvm_ty.constNull();
src/type.zig+72-15
...@@ -390,7 +390,7 @@ pub const Type = extern union {...@@ -390,7 +390,7 @@ pub const Type = extern union {
390 .@"addrspace" = .generic,390 .@"addrspace" = .generic,
391 .bit_offset = 0,391 .bit_offset = 0,
392 .host_size = 0,392 .host_size = 0,
393 .@"allowzero" = false,393 .@"allowzero" = true,
394 .mutable = false,394 .mutable = false,
395 .@"volatile" = false,395 .@"volatile" = false,
396 .size = .C,396 .size = .C,
...@@ -402,7 +402,7 @@ pub const Type = extern union {...@@ -402,7 +402,7 @@ pub const Type = extern union {
402 .@"addrspace" = .generic,402 .@"addrspace" = .generic,
403 .bit_offset = 0,403 .bit_offset = 0,
404 .host_size = 0,404 .host_size = 0,
405 .@"allowzero" = false,405 .@"allowzero" = true,
406 .mutable = true,406 .mutable = true,
407 .@"volatile" = false,407 .@"volatile" = false,
408 .size = .C,408 .size = .C,
...@@ -1153,7 +1153,7 @@ pub const Type = extern union {...@@ -1153,7 +1153,7 @@ pub const Type = extern union {
1153 }1153 }
1154 if (!payload.mutable) try writer.writeAll("const ");1154 if (!payload.mutable) try writer.writeAll("const ");
1155 if (payload.@"volatile") try writer.writeAll("volatile ");1155 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
1158 ty = payload.pointee_type;1158 ty = payload.pointee_type;
1159 continue;1159 continue;
...@@ -2347,7 +2347,48 @@ pub const Type = extern union {...@@ -2347,7 +2347,48 @@ pub const Type = extern union {
2347 }2347 }
2348 }2348 }
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.
2351 pub fn isPtrLikeOptional(self: Type) bool {2392 pub fn isPtrLikeOptional(self: Type) bool {
2352 switch (self.tag()) {2393 switch (self.tag()) {
2353 .optional_single_const_pointer,2394 .optional_single_const_pointer,
...@@ -2371,7 +2412,8 @@ pub const Type = extern union {...@@ -2371,7 +2412,8 @@ pub const Type = extern union {
2371 },2412 },
23722413
2373 .pointer => return self.castTag(.pointer).?.data.size == .C,2414 .pointer => return self.castTag(.pointer).?.data.size == .C,
2374 else => unreachable,2415
2416 else => return false,
2375 }2417 }
2376 }2418 }
23772419
...@@ -2532,38 +2574,50 @@ pub const Type = extern union {...@@ -2532,38 +2574,50 @@ pub const Type = extern union {
25322574
2533 /// Asserts that the type is an optional.2575 /// Asserts that the type is an optional.
2534 /// Resulting `Type` will have inner memory referencing `buf`.2576 /// Resulting `Type` will have inner memory referencing `buf`.
2535 pub fn optionalChild(self: Type, buf: *Payload.ElemType) Type {2577 /// Note that for C pointers this returns the type unmodified.
2536 return switch (self.tag()) {2578 pub fn optionalChild(ty: Type, buf: *Payload.ElemType) Type {
2537 .optional => self.castTag(.optional).?.data,2579 return switch (ty.tag()) {
2580 .optional => ty.castTag(.optional).?.data,
2538 .optional_single_mut_pointer => {2581 .optional_single_mut_pointer => {
2539 buf.* = .{2582 buf.* = .{
2540 .base = .{ .tag = .single_mut_pointer },2583 .base = .{ .tag = .single_mut_pointer },
2541 .data = self.castPointer().?.data,2584 .data = ty.castPointer().?.data,
2542 };2585 };
2543 return Type.initPayload(&buf.base);2586 return Type.initPayload(&buf.base);
2544 },2587 },
2545 .optional_single_const_pointer => {2588 .optional_single_const_pointer => {
2546 buf.* = .{2589 buf.* = .{
2547 .base = .{ .tag = .single_const_pointer },2590 .base = .{ .tag = .single_const_pointer },
2548 .data = self.castPointer().?.data,2591 .data = ty.castPointer().?.data,
2549 };2592 };
2550 return Type.initPayload(&buf.base);2593 return Type.initPayload(&buf.base);
2551 },2594 },
2595
2596 .pointer, // here we assume it is a C pointer
2597 .c_const_pointer,
2598 .c_mut_pointer,
2599 => return ty,
2600
2552 else => unreachable,2601 else => unreachable,
2553 };2602 };
2554 }2603 }
25552604
2556 /// Asserts that the type is an optional.2605 /// Asserts that the type is an optional.
2557 /// Same as `optionalChild` but allocates the buffer if needed.2606 /// Same as `optionalChild` but allocates the buffer if needed.
2558 pub fn optionalChildAlloc(self: Type, allocator: *Allocator) !Type {2607 pub fn optionalChildAlloc(ty: Type, allocator: *Allocator) !Type {
2559 switch (self.tag()) {2608 switch (ty.tag()) {
2560 .optional => return self.castTag(.optional).?.data,2609 .optional => return ty.castTag(.optional).?.data,
2561 .optional_single_mut_pointer => {2610 .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);
2563 },2612 },
2564 .optional_single_const_pointer => {2613 .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);
2566 },2615 },
2616 .pointer, // here we assume it is a C pointer
2617 .c_const_pointer,
2618 .c_mut_pointer,
2619 => return ty,
2620
2567 else => unreachable,2621 else => unreachable,
2568 }2622 }
2569 }2623 }
...@@ -4050,6 +4104,9 @@ pub const Type = extern union {...@@ -4050,6 +4104,9 @@ pub const Type = extern union {
4050 if (d.sentinel != null or d.@"align" != 0 or d.@"addrspace" != .generic or4104 if (d.sentinel != null or d.@"align" != 0 or d.@"addrspace" != .generic or
4051 d.bit_offset != 0 or d.host_size != 0 or d.@"allowzero" or d.@"volatile")4105 d.bit_offset != 0 or d.host_size != 0 or d.@"allowzero" or d.@"volatile")
4052 {4106 {
4107 if (d.size == .C) {
4108 assert(d.@"allowzero"); // All C pointers must set allowzero to true.
4109 }
4053 return Type.Tag.pointer.create(arena, d);4110 return Type.Tag.pointer.create(arena, d);
4054 }4111 }
40554112
src/value.zig+2-1
...@@ -1819,7 +1819,8 @@ pub const Value = extern union {...@@ -1819,7 +1819,8 @@ pub const Value = extern union {
1819 .unreachable_value => unreachable,1819 .unreachable_value => unreachable,
1820 .inferred_alloc => unreachable,1820 .inferred_alloc => unreachable,
1821 .inferred_alloc_comptime => unreachable,1821 .inferred_alloc_comptime => unreachable,
1822 else => unreachable,1822
1823 else => false,
1823 };1824 };
1824 }1825 }
18251826
test/behavior/cast.zig+157
...@@ -120,3 +120,160 @@ fn incrementVoidPtrArray(array: ?*c_void, len: usize) void {...@@ -120,3 +120,160 @@ fn incrementVoidPtrArray(array: ?*c_void, len: usize) void {
120 @ptrCast([*]u8, array.?)[n] += 1;120 @ptrCast([*]u8, array.?)[n] += 1;
121 }121 }
122}122}
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;...@@ -5,33 +5,6 @@ const maxInt = std.math.maxInt;
5const Vector = std.meta.Vector;5const Vector = std.meta.Vector;
6const native_endian = @import("builtin").target.cpu.arch.endian();6const 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
35test "explicit cast from integer to error type" {8test "explicit cast from integer to error type" {
36 try testCastIntToErr(error.ItBroke);9 try testCastIntToErr(error.ItBroke);
37 comptime try testCastIntToErr(error.ItBroke);10 comptime try testCastIntToErr(error.ItBroke);
...@@ -175,7 +148,7 @@ fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 {...@@ -175,7 +148,7 @@ fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 {
175 return slice[0..1];148 return slice[0..1];
176}149}
177150
178test "implicit cast from &const [N]T to []const T" {151test "implicit cast from *const [N]T to []const T" {
179 try testCastConstArrayRefToConstSlice();152 try testCastConstArrayRefToConstSlice();
180 comptime try testCastConstArrayRefToConstSlice();153 comptime try testCastConstArrayRefToConstSlice();
181}154}
...@@ -258,7 +231,7 @@ fn cast128Float(x: u128) f128 {...@@ -258,7 +231,7 @@ fn cast128Float(x: u128) f128 {
258 return @bitCast(f128, x);231 return @bitCast(f128, x);
259}232}
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" {
262 try testCastPtrOfArrayToSliceAndPtr();235 try testCastPtrOfArrayToSliceAndPtr();
263 comptime try testCastPtrOfArrayToSliceAndPtr();236 comptime try testCastPtrOfArrayToSliceAndPtr();
264}237}
...@@ -290,35 +263,6 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" {...@@ -290,35 +263,6 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
290 try expect(mem.eql(u8, std.mem.spanZ(@ptrCast([*:0]const u8, x[0].?)), "window name"));263 try expect(mem.eql(u8, std.mem.spanZ(@ptrCast([*:0]const u8, x[0].?)), "window name"));
291}264}
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
322test "vector casts" {266test "vector casts" {
323 const S = struct {267 const S = struct {
324 fn doTheTest() !void {268 fn doTheTest() !void {
...@@ -369,23 +313,6 @@ test "@floatCast cast down" {...@@ -369,23 +313,6 @@ test "@floatCast cast down" {
369 }313 }
370}314}
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
389test "implicit cast from *[N]T to ?[*]T" {316test "implicit cast from *[N]T to ?[*]T" {
390 var x: ?[*]u16 = null;317 var x: ?[*]u16 = null;
391 var y: [4]u16 = [4]u16{ 0, 1, 2, 3 };318 var y: [4]u16 = [4]u16{ 0, 1, 2, 3 };
...@@ -397,16 +324,6 @@ test "implicit cast from *[N]T to ?[*]T" {...@@ -397,16 +324,6 @@ test "implicit cast from *[N]T to ?[*]T" {
397 try expect(std.mem.eql(u16, x.?[0..4], y[0..4]));324 try expect(std.mem.eql(u16, x.?[0..4], y[0..4]));
398}325}
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
410test "implicit cast from *T to ?*c_void" {327test "implicit cast from *T to ?*c_void" {
411 var a: u8 = 1;328 var a: u8 = 1;
412 incrementVoidPtrValue(&a);329 incrementVoidPtrValue(&a);
...@@ -417,50 +334,6 @@ fn incrementVoidPtrValue(value: ?*c_void) void {...@@ -417,50 +334,6 @@ fn incrementVoidPtrValue(value: ?*c_void) void {
417 @ptrCast(*u8, value.?).* += 1;334 @ptrCast(*u8, value.?).* += 1;
418}335}
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
464test "peer type resolution: unreachable, null, slice" {337test "peer type resolution: unreachable, null, slice" {
465 const S = struct {338 const S = struct {
466 fn doTheTest(num: usize, word: []const u8) !void {339 fn doTheTest(num: usize, word: []const u8) !void {
...@@ -639,27 +512,6 @@ test "return u8 coercing into ?u32 return type" {...@@ -639,27 +512,6 @@ test "return u8 coercing into ?u32 return type" {
639 comptime try S.doTheTest();512 comptime try S.doTheTest();
640}513}
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
663test "peer type resolution implicit cast to return type" {515test "peer type resolution implicit cast to return type" {
664 const S = struct {516 const S = struct {
665 fn doTheTest() !void {517 fn doTheTest() !void {