| author | |
| committer | |
| log | c6b3d06535f4227541c13fe75da347a485abdb4f |
| tree | 8f0a730e1dc7619168a81d2cd4521db1e7a1b4b6 |
| parent | 6df26a37d13d21be061a1cccd39dd17e46a81322 |
* 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 | 9125 | .pointee_type = elem_type, |
| 9126 | 9126 | .@"addrspace" = .generic, |
| 9127 | 9127 | .mutable = inst_data.is_mutable, |
| 9128 | .@"allowzero" = inst_data.is_allowzero, | |
| 9128 | .@"allowzero" = inst_data.is_allowzero or inst_data.size == .C, | |
| 9129 | 9129 | .@"volatile" = inst_data.is_volatile, |
| 9130 | 9130 | .size = inst_data.size, |
| 9131 | 9131 | }); |
| ... | ... | @@ -9185,7 +9185,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 9185 | 9185 | .bit_offset = bit_start, |
| 9186 | 9186 | .host_size = bit_end, |
| 9187 | 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 | 9189 | .@"volatile" = inst_data.flags.is_volatile, |
| 9190 | 9190 | .size = inst_data.size, |
| 9191 | 9191 | }); |
| ... | ... | @@ -12102,6 +12102,21 @@ fn coerce( |
| 12102 | 12102 | } |
| 12103 | 12103 | } |
| 12104 | 12104 | |
| 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 | 12120 | // coercion to C pointer |
| 12106 | 12121 | if (dest_info.size == .C) { |
| 12107 | 12122 | switch (inst_ty.zigTypeTag()) { |
| ... | ... | @@ -12262,84 +12277,107 @@ const InMemoryCoercionResult = enum { |
| 12262 | 12277 | /// * sentinel-terminated pointers can coerce into `[*]` |
| 12263 | 12278 | /// TODO improve this function to report recursive compile errors like it does in stage1. |
| 12264 | 12279 | /// look at the function types_match_const_cast_only |
| 12265 | fn coerceInMemoryAllowed(dest_ty: Type, src_type: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult { | |
| 12266 | if (dest_ty.eql(src_type)) | |
| 12280 | fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult { | |
| 12281 | if (dest_ty.eql(src_ty)) | |
| 12267 | 12282 | return .ok; |
| 12268 | 12283 | |
| 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); | |
| 12278 | 12290 | } |
| 12291 | } | |
| 12279 | 12292 | |
| 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 | } | |
| 12283 | 12297 | |
| 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 | |
| 12290 | 12304 | |
| 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 | } | |
| 12296 | 12307 | |
| 12297 | const ok_cv_qualifiers = | |
| 12298 | (src_info.mutable or !dest_info.mutable) and | |
| 12299 | (!src_info.@"volatile" or dest_info.@"volatile"); | |
| 12308 | fn 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; | |
| 12300 | 12318 | |
| 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 | } | |
| 12304 | 12323 | |
| 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 | } | |
| 12311 | 12327 | |
| 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 | } | |
| 12315 | 12334 | |
| 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 | } | |
| 12321 | 12340 | |
| 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"); | |
| 12331 | 12344 | |
| 12332 | if (dest_align > src_align) { | |
| 12333 | return .no_match; | |
| 12334 | } | |
| 12335 | } | |
| 12345 | if (!ok_cv_qualifiers) { | |
| 12346 | return .no_match; | |
| 12347 | } | |
| 12336 | 12348 | |
| 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 | } |
| 12339 | 12358 | |
| 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 | } | |
| 12341 | 12364 | |
| 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 | } |
| 12344 | 12382 | |
| 12345 | 12383 | fn coerceNum( |
| ... | ... | @@ -13297,6 +13335,7 @@ fn analyzeSlice( |
| 13297 | 13335 | const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); |
| 13298 | 13336 | |
| 13299 | 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; | |
| 13300 | 13339 | |
| 13301 | 13340 | if (opt_new_len_val) |new_len_val| { |
| 13302 | 13341 | const new_len_int = new_len_val.toUnsignedInt(); |
| ... | ... | @@ -13312,7 +13351,7 @@ fn analyzeSlice( |
| 13312 | 13351 | .@"align" = new_ptr_ty_info.@"align", |
| 13313 | 13352 | .@"addrspace" = new_ptr_ty_info.@"addrspace", |
| 13314 | 13353 | .mutable = new_ptr_ty_info.mutable, |
| 13315 | .@"allowzero" = new_ptr_ty_info.@"allowzero", | |
| 13354 | .@"allowzero" = new_allowzero, | |
| 13316 | 13355 | .@"volatile" = new_ptr_ty_info.@"volatile", |
| 13317 | 13356 | .size = .One, |
| 13318 | 13357 | }); |
| ... | ... | @@ -13340,7 +13379,7 @@ fn analyzeSlice( |
| 13340 | 13379 | .@"align" = new_ptr_ty_info.@"align", |
| 13341 | 13380 | .@"addrspace" = new_ptr_ty_info.@"addrspace", |
| 13342 | 13381 | .mutable = new_ptr_ty_info.mutable, |
| 13343 | .@"allowzero" = new_ptr_ty_info.@"allowzero", | |
| 13382 | .@"allowzero" = new_allowzero, | |
| 13344 | 13383 | .@"volatile" = new_ptr_ty_info.@"volatile", |
| 13345 | 13384 | .size = .Slice, |
| 13346 | 13385 | }); |
src/codegen/llvm.zig+2| ... | ... | @@ -1184,6 +1184,8 @@ pub const DeclGen = struct { |
| 1184 | 1184 | if (tv.ty.isPtrLikeOptional()) { |
| 1185 | 1185 | if (tv.val.castTag(.opt_payload)) |payload| { |
| 1186 | 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 | 1189 | } else { |
| 1188 | 1190 | const llvm_ty = try self.llvmType(tv.ty); |
| 1189 | 1191 | return llvm_ty.constNull(); |
src/type.zig+72-15| ... | ... | @@ -390,7 +390,7 @@ pub const Type = extern union { |
| 390 | 390 | .@"addrspace" = .generic, |
| 391 | 391 | .bit_offset = 0, |
| 392 | 392 | .host_size = 0, |
| 393 | .@"allowzero" = false, | |
| 393 | .@"allowzero" = true, | |
| 394 | 394 | .mutable = false, |
| 395 | 395 | .@"volatile" = false, |
| 396 | 396 | .size = .C, |
| ... | ... | @@ -402,7 +402,7 @@ pub const Type = extern union { |
| 402 | 402 | .@"addrspace" = .generic, |
| 403 | 403 | .bit_offset = 0, |
| 404 | 404 | .host_size = 0, |
| 405 | .@"allowzero" = false, | |
| 405 | .@"allowzero" = true, | |
| 406 | 406 | .mutable = true, |
| 407 | 407 | .@"volatile" = false, |
| 408 | 408 | .size = .C, |
| ... | ... | @@ -1153,7 +1153,7 @@ pub const Type = extern union { |
| 1153 | 1153 | } |
| 1154 | 1154 | if (!payload.mutable) try writer.writeAll("const "); |
| 1155 | 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 "); | |
| 1157 | 1157 | |
| 1158 | 1158 | ty = payload.pointee_type; |
| 1159 | 1159 | continue; |
| ... | ... | @@ -2347,7 +2347,48 @@ pub const Type = extern union { |
| 2347 | 2347 | } |
| 2348 | 2348 | } |
| 2349 | 2349 | |
| 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 | 2392 | pub fn isPtrLikeOptional(self: Type) bool { |
| 2352 | 2393 | switch (self.tag()) { |
| 2353 | 2394 | .optional_single_const_pointer, |
| ... | ... | @@ -2371,7 +2412,8 @@ pub const Type = extern union { |
| 2371 | 2412 | }, |
| 2372 | 2413 | |
| 2373 | 2414 | .pointer => return self.castTag(.pointer).?.data.size == .C, |
| 2374 | else => unreachable, | |
| 2415 | ||
| 2416 | else => return false, | |
| 2375 | 2417 | } |
| 2376 | 2418 | } |
| 2377 | 2419 | |
| ... | ... | @@ -2532,38 +2574,50 @@ pub const Type = extern union { |
| 2532 | 2574 | |
| 2533 | 2575 | /// Asserts that the type is an optional. |
| 2534 | 2576 | /// 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, | |
| 2538 | 2581 | .optional_single_mut_pointer => { |
| 2539 | 2582 | buf.* = .{ |
| 2540 | 2583 | .base = .{ .tag = .single_mut_pointer }, |
| 2541 | .data = self.castPointer().?.data, | |
| 2584 | .data = ty.castPointer().?.data, | |
| 2542 | 2585 | }; |
| 2543 | 2586 | return Type.initPayload(&buf.base); |
| 2544 | 2587 | }, |
| 2545 | 2588 | .optional_single_const_pointer => { |
| 2546 | 2589 | buf.* = .{ |
| 2547 | 2590 | .base = .{ .tag = .single_const_pointer }, |
| 2548 | .data = self.castPointer().?.data, | |
| 2591 | .data = ty.castPointer().?.data, | |
| 2549 | 2592 | }; |
| 2550 | 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 | 2601 | else => unreachable, |
| 2553 | 2602 | }; |
| 2554 | 2603 | } |
| 2555 | 2604 | |
| 2556 | 2605 | /// Asserts that the type is an optional. |
| 2557 | 2606 | /// 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, | |
| 2561 | 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 | 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 | 2621 | else => unreachable, |
| 2568 | 2622 | } |
| 2569 | 2623 | } |
| ... | ... | @@ -4050,6 +4104,9 @@ pub const Type = extern union { |
| 4050 | 4104 | if (d.sentinel != null or d.@"align" != 0 or d.@"addrspace" != .generic or |
| 4051 | 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 | 4110 | return Type.Tag.pointer.create(arena, d); |
| 4054 | 4111 | } |
| 4055 | 4112 |
src/value.zig+2-1| ... | ... | @@ -1819,7 +1819,8 @@ pub const Value = extern union { |
| 1819 | 1819 | .unreachable_value => unreachable, |
| 1820 | 1820 | .inferred_alloc => unreachable, |
| 1821 | 1821 | .inferred_alloc_comptime => unreachable, |
| 1822 | else => unreachable, | |
| 1822 | ||
| 1823 | else => false, | |
| 1823 | 1824 | }; |
| 1824 | 1825 | } |
| 1825 | 1826 |
test/behavior/cast.zig+157| ... | ... | @@ -120,3 +120,160 @@ fn incrementVoidPtrArray(array: ?*c_void, len: usize) void { |
| 120 | 120 | @ptrCast([*]u8, array.?)[n] += 1; |
| 121 | 121 | } |
| 122 | 122 | } |
| 123 | ||
| 124 | test "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 | ||
| 151 | test "@intCast comptime_int" { | |
| 152 | const result = @intCast(i32, 1234); | |
| 153 | try expect(@TypeOf(result) == i32); | |
| 154 | try expect(result == 1234); | |
| 155 | } | |
| 156 | ||
| 157 | test "@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 | ||
| 180 | test "coerce undefined to optional" { | |
| 181 | try expect(MakeType(void).getNull() == null); | |
| 182 | try expect(MakeType(void).getNonNull() != null); | |
| 183 | } | |
| 184 | ||
| 185 | fn 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 | ||
| 197 | test "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 | ||
| 207 | test "*usize to *void" { | |
| 208 | var i = @as(usize, 0); | |
| 209 | var v = @ptrCast(*void, &i); | |
| 210 | v.* = {}; | |
| 211 | } | |
| 212 | ||
| 213 | test "compile time int to ptr of function" { | |
| 214 | try foobar(FUNCTION_CONSTANT); | |
| 215 | } | |
| 216 | ||
| 217 | pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize)); | |
| 218 | pub const PFN_void = fn (*c_void) callconv(.C) void; | |
| 219 | ||
| 220 | fn foobar(func: PFN_void) !void { | |
| 221 | try std.testing.expect(@ptrToInt(func) == maxInt(usize)); | |
| 222 | } | |
| 223 | ||
| 224 | test "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 | ||
| 234 | test "@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 | ||
| 240 | test "@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 | ||
| 251 | test "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 | ||
| 272 | test "*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 | 5 | const Vector = std.meta.Vector; |
| 6 | 6 | const native_endian = @import("builtin").target.cpu.arch.endian(); |
| 7 | 7 | |
| 8 | test "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 | ||
| 35 | 8 | test "explicit cast from integer to error type" { |
| 36 | 9 | try testCastIntToErr(error.ItBroke); |
| 37 | 10 | comptime try testCastIntToErr(error.ItBroke); |
| ... | ... | @@ -175,7 +148,7 @@ fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { |
| 175 | 148 | return slice[0..1]; |
| 176 | 149 | } |
| 177 | 150 | |
| 178 | test "implicit cast from &const [N]T to []const T" { | |
| 151 | test "implicit cast from *const [N]T to []const T" { | |
| 179 | 152 | try testCastConstArrayRefToConstSlice(); |
| 180 | 153 | comptime try testCastConstArrayRefToConstSlice(); |
| 181 | 154 | } |
| ... | ... | @@ -258,7 +231,7 @@ fn cast128Float(x: u128) f128 { |
| 258 | 231 | return @bitCast(f128, x); |
| 259 | 232 | } |
| 260 | 233 | |
| 261 | test "single-item pointer of array to slice and to unknown length pointer" { | |
| 234 | test "single-item pointer of array to slice to unknown length pointer" { | |
| 262 | 235 | try testCastPtrOfArrayToSliceAndPtr(); |
| 263 | 236 | comptime try testCastPtrOfArrayToSliceAndPtr(); |
| 264 | 237 | } |
| ... | ... | @@ -290,35 +263,6 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" { |
| 290 | 263 | try expect(mem.eql(u8, std.mem.spanZ(@ptrCast([*:0]const u8, x[0].?)), "window name")); |
| 291 | 264 | } |
| 292 | 265 | |
| 293 | test "@intCast comptime_int" { | |
| 294 | const result = @intCast(i32, 1234); | |
| 295 | try expect(@TypeOf(result) == i32); | |
| 296 | try expect(result == 1234); | |
| 297 | } | |
| 298 | ||
| 299 | test "@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 | ||
| 322 | 266 | test "vector casts" { |
| 323 | 267 | const S = struct { |
| 324 | 268 | fn doTheTest() !void { |
| ... | ... | @@ -369,23 +313,6 @@ test "@floatCast cast down" { |
| 369 | 313 | } |
| 370 | 314 | } |
| 371 | 315 | |
| 372 | test "implicit cast undefined to optional" { | |
| 373 | try expect(MakeType(void).getNull() == null); | |
| 374 | try expect(MakeType(void).getNonNull() != null); | |
| 375 | } | |
| 376 | ||
| 377 | fn 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 | ||
| 389 | 316 | test "implicit cast from *[N]T to ?[*]T" { |
| 390 | 317 | var x: ?[*]u16 = null; |
| 391 | 318 | var y: [4]u16 = [4]u16{ 0, 1, 2, 3 }; |
| ... | ... | @@ -397,16 +324,6 @@ test "implicit cast from *[N]T to ?[*]T" { |
| 397 | 324 | try expect(std.mem.eql(u16, x.?[0..4], y[0..4])); |
| 398 | 325 | } |
| 399 | 326 | |
| 400 | test "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 | ||
| 410 | 327 | test "implicit cast from *T to ?*c_void" { |
| 411 | 328 | var a: u8 = 1; |
| 412 | 329 | incrementVoidPtrValue(&a); |
| ... | ... | @@ -417,50 +334,6 @@ fn incrementVoidPtrValue(value: ?*c_void) void { |
| 417 | 334 | @ptrCast(*u8, value.?).* += 1; |
| 418 | 335 | } |
| 419 | 336 | |
| 420 | test "*usize to *void" { | |
| 421 | var i = @as(usize, 0); | |
| 422 | var v = @ptrCast(*void, &i); | |
| 423 | v.* = {}; | |
| 424 | } | |
| 425 | ||
| 426 | test "compile time int to ptr of function" { | |
| 427 | try foobar(FUNCTION_CONSTANT); | |
| 428 | } | |
| 429 | ||
| 430 | pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize)); | |
| 431 | pub const PFN_void = fn (*c_void) callconv(.C) void; | |
| 432 | ||
| 433 | fn foobar(func: PFN_void) !void { | |
| 434 | try std.testing.expect(@ptrToInt(func) == maxInt(usize)); | |
| 435 | } | |
| 436 | ||
| 437 | test "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 | ||
| 447 | test "@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 | ||
| 453 | test "@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 | ||
| 464 | 337 | test "peer type resolution: unreachable, null, slice" { |
| 465 | 338 | const S = struct { |
| 466 | 339 | fn doTheTest(num: usize, word: []const u8) !void { |
| ... | ... | @@ -639,27 +512,6 @@ test "return u8 coercing into ?u32 return type" { |
| 639 | 512 | comptime try S.doTheTest(); |
| 640 | 513 | } |
| 641 | 514 | |
| 642 | test "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 | ||
| 663 | 515 | test "peer type resolution implicit cast to return type" { |
| 664 | 516 | const S = struct { |
| 665 | 517 | fn doTheTest() !void { |