authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-03 23:12:18-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-03 23:12:18-07:00
log63c5c510b1a083fbbca4145b1b53d39da9b0fd81
treefc00c36767b6c15a444ec5c57f69778517ac740b
parent26be5bb8b1e1c05ceab4b7620efa2a058a174886

Sema: rework peer type logic for pointers

Now it's centered around a switch on the chosen type tag which gives us easy access to pointer data. The logic is simplied and in some cases logic is removed when it is sufficient to choose the type that is a better coercion target without knowing whether such coercion will succeed ahead of time. A bug is fixed at the bottom of the function; we were doing the opposite of what we were supposed to with `seen_const`. Also the bottom of the function has a more complete handling of the possible combinations of `any_are_null`, `convert_to_slice`, and `err_set_ty`. In the behavior tests, not as many backends needed to be skipped.

2 files changed, 136 insertions(+), 154 deletions(-)

src/Sema.zig+132-138
......@@ -18279,146 +18279,126 @@ fn resolvePeerTypes(
1827918279 },
1828018280 },
1828118281 .Pointer => {
18282 if (candidate_ty.ptrSize() == .C) {
18283 // *T to [*c]T
18284 if (chosen_ty_tag == .Pointer) {
18285 const chosen_elem_ty = chosen_ty.childType();
18286 const candidate_elem_ty = candidate_ty.childType();
18287 if ((try sema.coerceInMemoryAllowed(block, chosen_elem_ty, candidate_elem_ty, false, target, src, src)) == .ok) {
18282 const cand_info = candidate_ty.ptrInfo().data;
18283 switch (chosen_ty_tag) {
18284 .Pointer => {
18285 const chosen_info = chosen_ty.ptrInfo().data;
18286
18287 seen_const = seen_const or !chosen_info.mutable or !cand_info.mutable;
18288
18289 // *[N]T to [*]T
18290 // *[N]T to []T
18291 if ((cand_info.size == .Many or cand_info.size == .Slice) and
18292 chosen_info.size == .One and
18293 chosen_info.pointee_type.zigTypeTag() == .Array)
18294 {
18295 // In case we see i.e.: `*[1]T`, `*[2]T`, `[*]T`
18296 convert_to_slice = false;
1828818297 chosen = candidate;
1828918298 chosen_i = candidate_i + 1;
1829018299 continue;
1829118300 }
18292 }
18293
18294 if (chosen_ty_tag == .Int or chosen_ty_tag == .ComptimeInt) {
18295 chosen = candidate;
18296 chosen_i = candidate_i + 1;
18297 continue;
18298 }
18299 if (chosen_ty_tag == .Pointer and chosen_ty.ptrSize() != .Slice) {
18300 continue;
18301 }
18302 }
18303
18304 // [*c]T and *T
18305 if (chosen_ty_tag == .Pointer and chosen_ty.ptrSize() == .C) {
18306 const chosen_elem_ty = chosen_ty.childType();
18307 const candidate_elem_ty = candidate_ty.childType();
18308 if ((try sema.coerceInMemoryAllowed(block, chosen_elem_ty, candidate_elem_ty, false, target, src, src)) == .ok) {
18309 continue;
18310 }
18311 }
18312
18313 // *[N]T to [*]T
18314 if (candidate_ty.ptrSize() == .Many and
18315 chosen_ty_tag == .Pointer and
18316 chosen_ty.ptrSize() == .One and
18317 chosen_ty.childType().zigTypeTag() == .Array)
18318 {
18319 chosen = candidate;
18320 chosen_i = candidate_i + 1;
18321
18322 convert_to_slice = false;
18323
18324 if (chosen_ty.isConstPtr() and !candidate_ty.isConstPtr())
18325 seen_const = true;
18326
18327 continue;
18328 }
18329
18330 // *[N]T to [*]T (prev is many pointer)
18331 if (candidate_ty.ptrSize() == .One and
18332 candidate_ty.childType().zigTypeTag() == .Array and
18333 chosen_ty_tag == .Pointer and
18334 chosen_ty.ptrSize() == .Many)
18335 {
18336 if (candidate_ty.isConstPtr() and !chosen_ty.isConstPtr())
18337 seen_const = true;
18338
18339 continue;
18340 }
18341
18342 // *[N]T to []T (prev is slice)
18343 // *[N]T to E![]T
18344 if ((chosen_ty.isSlice() or (chosen_ty_tag == .ErrorUnion and chosen_ty.errorUnionPayload().isSlice())) and
18345 candidate_ty.ptrSize() == .One and
18346 candidate_ty.childType().zigTypeTag() == .Array)
18347 {
18348 const chosen_elem_ty = switch (chosen_ty_tag) {
18349 .ErrorUnion => chosen_ty.errorUnionPayload().elemType2(),
18350 else => chosen_ty.elemType2(),
18351 };
18352 const candidate_elem_ty = candidate_ty.childType().elemType2();
18353 if ((try sema.coerceInMemoryAllowed(block, candidate_elem_ty, chosen_elem_ty, false, target, src, src)) == .ok) {
18354 convert_to_slice = false; // it already is a slice
18355
18356 // If the pointer is const then we need to const
18357 if (candidate_ty.isConstPtr())
18358 seen_const = true;
18359
18360 continue;
18361 }
18362 }
18301 if (cand_info.size == .One and
18302 cand_info.pointee_type.zigTypeTag() == .Array and
18303 (chosen_info.size == .Many or chosen_info.size == .Slice))
18304 {
18305 // In case we see i.e.: `*[1]T`, `*[2]T`, `[*]T`
18306 convert_to_slice = false;
18307 continue;
18308 }
1836318309
18364 // *[N]T to []T (current is slice)
18365 if (chosen_ty_tag == .Pointer and
18366 chosen_ty.ptrSize() == .One and
18367 chosen_ty.childType().zigTypeTag() == .Array and
18368 candidate_ty.isSlice())
18369 {
18370 const chosen_child_ty = chosen_ty.childType();
18371 const chosen_elem_ty = chosen_child_ty.elemType2();
18372 const candidate_elem_ty = candidate_ty.elemType2();
18373 if ((try sema.coerceInMemoryAllowed(block, candidate_elem_ty, chosen_elem_ty, false, target, src, src)) == .ok) {
18374 chosen = candidate;
18375 chosen_i = candidate_i + 1;
18310 // *[N]T and *[M]T
18311 // Verify both are single-pointers to arrays.
18312 // Keep the one whose element type can be coerced into.
18313 if (chosen_info.size == .One and
18314 cand_info.size == .One and
18315 chosen_info.pointee_type.zigTypeTag() == .Array and
18316 cand_info.pointee_type.zigTypeTag() == .Array)
18317 {
18318 const chosen_elem_ty = chosen_info.pointee_type.childType();
18319 const cand_elem_ty = cand_info.pointee_type.childType();
1837618320
18377 convert_to_slice = false; // it already is a slice
18321 const chosen_ok = .ok == try sema.coerceInMemoryAllowed(block, chosen_elem_ty, cand_elem_ty, chosen_info.mutable, target, src, src);
18322 if (chosen_ok) {
18323 convert_to_slice = true;
18324 continue;
18325 }
1837818326
18379 // If the prev pointer is const then we need to const
18380 if (chosen_ty.isConstPtr())
18381 seen_const = true;
18327 const cand_ok = .ok == try sema.coerceInMemoryAllowed(block, cand_elem_ty, chosen_elem_ty, cand_info.mutable, target, src, src);
18328 if (cand_ok) {
18329 convert_to_slice = true;
18330 chosen = candidate;
18331 chosen_i = candidate_i + 1;
18332 continue;
18333 }
1838218334
18383 continue;
18384 }
18385 }
18335 // They're both bad. Report error.
18336 // In the future we probably want to use the
18337 // coerceInMemoryAllowed error reporting mechanism,
18338 // however, for now we just fall through for the
18339 // "incompatible types" error below.
18340 }
1838618341
18387 // *[N]T and *[M]T
18388 // verify both are pointers to known lengths
18389 if (chosen_ty_tag == .Pointer and
18390 chosen_ty.ptrSize() == .One and
18391 candidate_ty.ptrSize() == .One)
18392 {
18393 // verify both pointers are two arrays
18394 const chosen_child_ty = chosen_ty.childType();
18395 const candidate_child_ty = candidate_ty.childType();
18396 if (chosen_child_ty.zigTypeTag() == .Array and candidate_child_ty.zigTypeTag() == .Array) {
18397 // If we can cerce the element types, then we can do this.
18398 const chosen_elem_ty = chosen_child_ty.elemType2();
18399 const candidate_elem_ty = candidate_child_ty.elemType2();
18400 if ((try sema.coerceInMemoryAllowed(block, candidate_elem_ty, chosen_elem_ty, false, target, src, src)) == .ok) {
18401 // If there is a sentinel, it must match
18402 if (chosen_child_ty.sentinel()) |chosen_sentinel| {
18403 if (candidate_child_ty.sentinel()) |candidate_sentinel| {
18404 if (!chosen_sentinel.eql(candidate_sentinel, chosen_elem_ty))
18342 // [*c]T and any other pointer size
18343 // Whichever element type can coerce to the other one, is
18344 // the one we will keep. If they're both OK then we keep the
18345 // C pointer since it matches both single and many pointers.
18346 if (cand_info.size == .C or chosen_info.size == .C) {
18347 const cand_ok = .ok == try sema.coerceInMemoryAllowed(block, cand_info.pointee_type, chosen_info.pointee_type, cand_info.mutable, target, src, src);
18348 const chosen_ok = .ok == try sema.coerceInMemoryAllowed(block, chosen_info.pointee_type, cand_info.pointee_type, chosen_info.mutable, target, src, src);
18349
18350 if (cand_ok) {
18351 if (chosen_ok) {
18352 if (chosen_info.size == .C) {
18353 continue;
18354 } else {
18355 chosen = candidate;
18356 chosen_i = candidate_i + 1;
1840518357 continue;
18406 } else continue;
18358 }
18359 } else {
18360 chosen = candidate;
18361 chosen_i = candidate_i + 1;
18362 continue;
18363 }
18364 } else {
18365 if (chosen_ok) {
18366 continue;
18367 } else {
18368 // They're both bad. Report error.
18369 // In the future we probably want to use the
18370 // coerceInMemoryAllowed error reporting mechanism,
18371 // however, for now we just fall through for the
18372 // "incompatible types" error below.
18373 }
1840718374 }
18408
18375 }
18376 },
18377 .Int, .ComptimeInt => {
18378 if (cand_info.size == .C) {
1840918379 chosen = candidate;
1841018380 chosen_i = candidate_i + 1;
18411
18412 convert_to_slice = true;
18413
18414 // If one of the pointers is to const data, the slice
18415 // must also be const.
18416 if (candidate_child_ty.isConstPtr() or chosen_child_ty.isConstPtr())
18417 seen_const = true;
18418
1841918381 continue;
1842018382 }
18421 }
18383 },
18384 .ErrorUnion => {
18385 const chosen_ptr_ty = chosen_ty.errorUnionPayload();
18386 if (chosen_ptr_ty.zigTypeTag() == .Pointer) {
18387 const chosen_info = chosen_ptr_ty.ptrInfo().data;
18388
18389 seen_const = seen_const or !chosen_info.mutable or !cand_info.mutable;
18390
18391 // *[N]T to E![*]T
18392 // *[N]T to E![]T
18393 if (cand_info.size == .One and
18394 cand_info.pointee_type.zigTypeTag() == .Array and
18395 (chosen_info.size == .Many or chosen_info.size == .Slice))
18396 {
18397 continue;
18398 }
18399 }
18400 },
18401 else => {},
1842218402 }
1842318403 },
1842418404 .Optional => {
......@@ -18512,20 +18492,13 @@ fn resolvePeerTypes(
1851218492
1851318493 const chosen_ty = sema.typeOf(chosen);
1851418494
18515 if (any_are_null) {
18516 switch (chosen_ty.zigTypeTag()) {
18517 .Null, .Optional => return chosen_ty,
18518 else => return Type.optional(sema.arena, chosen_ty),
18519 }
18520 }
18521
1852218495 if (convert_to_slice) {
1852318496 // turn *[N]T => []T
1852418497 const chosen_child_ty = chosen_ty.childType();
1852518498 var info = chosen_ty.ptrInfo();
1852618499 info.data.sentinel = chosen_child_ty.sentinel();
1852718500 info.data.size = .Slice;
18528 info.data.mutable = seen_const or chosen_child_ty.isConstPtr();
18501 info.data.mutable = !(seen_const or chosen_child_ty.isConstPtr());
1852918502 info.data.pointee_type = switch (chosen_child_ty.tag()) {
1853018503 .array => chosen_child_ty.elemType2(),
1853118504 .array_u8, .array_u8_sentinel_0 => Type.initTag(.u8),
......@@ -18533,8 +18506,12 @@ fn resolvePeerTypes(
1853318506 };
1853418507
1853518508 const new_ptr_ty = try Type.ptr(sema.arena, target, info.data);
18536 const set_ty = err_set_ty orelse return new_ptr_ty;
18537 return try Module.errorUnionType(sema.arena, set_ty, new_ptr_ty);
18509 const opt_ptr_ty = if (any_are_null)
18510 try Type.optional(sema.arena, new_ptr_ty)
18511 else
18512 new_ptr_ty;
18513 const set_ty = err_set_ty orelse return opt_ptr_ty;
18514 return try Module.errorUnionType(sema.arena, set_ty, opt_ptr_ty);
1853818515 }
1853918516
1854018517 if (seen_const) {
......@@ -18545,20 +18522,37 @@ fn resolvePeerTypes(
1854518522 var info = ptr_ty.ptrInfo();
1854618523 info.data.mutable = false;
1854718524 const new_ptr_ty = try Type.ptr(sema.arena, target, info.data);
18525 const opt_ptr_ty = if (any_are_null)
18526 try Type.optional(sema.arena, new_ptr_ty)
18527 else
18528 new_ptr_ty;
1854818529 const set_ty = err_set_ty orelse chosen_ty.errorUnionSet();
18549 return try Module.errorUnionType(sema.arena, set_ty, new_ptr_ty);
18530 return try Module.errorUnionType(sema.arena, set_ty, opt_ptr_ty);
1855018531 },
1855118532 .Pointer => {
1855218533 var info = chosen_ty.ptrInfo();
1855318534 info.data.mutable = false;
1855418535 const new_ptr_ty = try Type.ptr(sema.arena, target, info.data);
18555 const set_ty = err_set_ty orelse return new_ptr_ty;
18556 return try Module.errorUnionType(sema.arena, set_ty, new_ptr_ty);
18536 const opt_ptr_ty = if (any_are_null)
18537 try Type.optional(sema.arena, new_ptr_ty)
18538 else
18539 new_ptr_ty;
18540 const set_ty = err_set_ty orelse return opt_ptr_ty;
18541 return try Module.errorUnionType(sema.arena, set_ty, opt_ptr_ty);
1855718542 },
1855818543 else => return chosen_ty,
1855918544 }
1856018545 }
1856118546
18547 if (any_are_null) {
18548 const opt_ty = switch (chosen_ty.zigTypeTag()) {
18549 .Null, .Optional => chosen_ty,
18550 else => try Type.optional(sema.arena, chosen_ty),
18551 };
18552 const set_ty = err_set_ty orelse return opt_ty;
18553 return try Module.errorUnionType(sema.arena, set_ty, opt_ty);
18554 }
18555
1856218556 if (err_set_ty) |ty| switch (chosen_ty.zigTypeTag()) {
1856318557 .ErrorSet => return ty,
1856418558 .ErrorUnion => {
test/behavior/pointers.zig+4-16
......@@ -128,12 +128,6 @@ fn testDerefPtrOneVal() !void {
128128}
129129
130130test "peer type resolution with C pointers" {
131 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
132 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
133 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
134 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
135 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
136
137131 var ptr_one: *u8 = undefined;
138132 var ptr_many: [*]u8 = undefined;
139133 var ptr_c: [*c]u8 = undefined;
......@@ -163,11 +157,11 @@ test "implicit casting between C pointer and optional non-C pointer" {
163157}
164158
165159test "implicit cast error unions with non-optional to optional pointer" {
166 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
167160 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
168 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
169 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
170 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
161 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
162 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
163 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
164 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
171165
172166 const S = struct {
173167 fn doTheTest() !void {
......@@ -384,12 +378,6 @@ test "pointer arithmetic affects the alignment" {
384378}
385379
386380test "@ptrToInt on null optional at comptime" {
387 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
388 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
389 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
390 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
391 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
392
393381 {
394382 const pointer = @intToPtr(?*u8, 0x000);
395383 const x = @ptrToInt(pointer);