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(...@@ -18279,146 +18279,126 @@ fn resolvePeerTypes(
18279 },18279 },
18280 },18280 },
18281 .Pointer => {18281 .Pointer => {
18282 if (candidate_ty.ptrSize() == .C) {18282 const cand_info = candidate_ty.ptrInfo().data;
18283 // *T to [*c]T18283 switch (chosen_ty_tag) {
18284 if (chosen_ty_tag == .Pointer) {18284 .Pointer => {
18285 const chosen_elem_ty = chosen_ty.childType();18285 const chosen_info = chosen_ty.ptrInfo().data;
18286 const candidate_elem_ty = candidate_ty.childType();18286
18287 if ((try sema.coerceInMemoryAllowed(block, chosen_elem_ty, candidate_elem_ty, false, target, src, src)) == .ok) {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;
18288 chosen = candidate;18297 chosen = candidate;
18289 chosen_i = candidate_i + 1;18298 chosen_i = candidate_i + 1;
18290 continue;18299 continue;
18291 }18300 }
18292 }18301 if (cand_info.size == .One and
1829318302 cand_info.pointee_type.zigTypeTag() == .Array and
18294 if (chosen_ty_tag == .Int or chosen_ty_tag == .ComptimeInt) {18303 (chosen_info.size == .Many or chosen_info.size == .Slice))
18295 chosen = candidate;18304 {
18296 chosen_i = candidate_i + 1;18305 // In case we see i.e.: `*[1]T`, `*[2]T`, `[*]T`
18297 continue;18306 convert_to_slice = false;
18298 }18307 continue;
18299 if (chosen_ty_tag == .Pointer and chosen_ty.ptrSize() != .Slice) {18308 }
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 }
1836318309
18364 // *[N]T to []T (current is slice)18310 // *[N]T and *[M]T
18365 if (chosen_ty_tag == .Pointer and18311 // Verify both are single-pointers to arrays.
18366 chosen_ty.ptrSize() == .One and18312 // Keep the one whose element type can be coerced into.
18367 chosen_ty.childType().zigTypeTag() == .Array and18313 if (chosen_info.size == .One and
18368 candidate_ty.isSlice())18314 cand_info.size == .One and
18369 {18315 chosen_info.pointee_type.zigTypeTag() == .Array and
18370 const chosen_child_ty = chosen_ty.childType();18316 cand_info.pointee_type.zigTypeTag() == .Array)
18371 const chosen_elem_ty = chosen_child_ty.elemType2();18317 {
18372 const candidate_elem_ty = candidate_ty.elemType2();18318 const chosen_elem_ty = chosen_info.pointee_type.childType();
18373 if ((try sema.coerceInMemoryAllowed(block, candidate_elem_ty, chosen_elem_ty, false, target, src, src)) == .ok) {18319 const cand_elem_ty = cand_info.pointee_type.childType();
18374 chosen = candidate;
18375 chosen_i = candidate_i + 1;
1837618320
18377 convert_to_slice = false; // it already is a slice18321 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 const18327 const cand_ok = .ok == try sema.coerceInMemoryAllowed(block, cand_elem_ty, chosen_elem_ty, cand_info.mutable, target, src, src);
18380 if (chosen_ty.isConstPtr())18328 if (cand_ok) {
18381 seen_const = true;18329 convert_to_slice = true;
18330 chosen = candidate;
18331 chosen_i = candidate_i + 1;
18332 continue;
18333 }
1838218334
18383 continue;18335 // They're both bad. Report error.
18384 }18336 // In the future we probably want to use the
18385 }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]T18342 // [*c]T and any other pointer size
18388 // verify both are pointers to known lengths18343 // Whichever element type can coerce to the other one, is
18389 if (chosen_ty_tag == .Pointer and18344 // the one we will keep. If they're both OK then we keep the
18390 chosen_ty.ptrSize() == .One and18345 // C pointer since it matches both single and many pointers.
18391 candidate_ty.ptrSize() == .One)18346 if (cand_info.size == .C or chosen_info.size == .C) {
18392 {18347 const cand_ok = .ok == try sema.coerceInMemoryAllowed(block, cand_info.pointee_type, chosen_info.pointee_type, cand_info.mutable, target, src, src);
18393 // verify both pointers are two arrays18348 const chosen_ok = .ok == try sema.coerceInMemoryAllowed(block, chosen_info.pointee_type, cand_info.pointee_type, chosen_info.mutable, target, src, src);
18394 const chosen_child_ty = chosen_ty.childType();18349
18395 const candidate_child_ty = candidate_ty.childType();18350 if (cand_ok) {
18396 if (chosen_child_ty.zigTypeTag() == .Array and candidate_child_ty.zigTypeTag() == .Array) {18351 if (chosen_ok) {
18397 // If we can cerce the element types, then we can do this.18352 if (chosen_info.size == .C) {
18398 const chosen_elem_ty = chosen_child_ty.elemType2();18353 continue;
18399 const candidate_elem_ty = candidate_child_ty.elemType2();18354 } else {
18400 if ((try sema.coerceInMemoryAllowed(block, candidate_elem_ty, chosen_elem_ty, false, target, src, src)) == .ok) {18355 chosen = candidate;
18401 // If there is a sentinel, it must match18356 chosen_i = candidate_i + 1;
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))
18405 continue;18357 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 }
18407 }18374 }
1840818375 }
18376 },
18377 .Int, .ComptimeInt => {
18378 if (cand_info.size == .C) {
18409 chosen = candidate;18379 chosen = candidate;
18410 chosen_i = candidate_i + 1;18380 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
18419 continue;18381 continue;
18420 }18382 }
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 => {},
18422 }18402 }
18423 },18403 },
18424 .Optional => {18404 .Optional => {
...@@ -18512,20 +18492,13 @@ fn resolvePeerTypes(...@@ -18512,20 +18492,13 @@ fn resolvePeerTypes(
1851218492
18513 const chosen_ty = sema.typeOf(chosen);18493 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
18522 if (convert_to_slice) {18495 if (convert_to_slice) {
18523 // turn *[N]T => []T18496 // turn *[N]T => []T
18524 const chosen_child_ty = chosen_ty.childType();18497 const chosen_child_ty = chosen_ty.childType();
18525 var info = chosen_ty.ptrInfo();18498 var info = chosen_ty.ptrInfo();
18526 info.data.sentinel = chosen_child_ty.sentinel();18499 info.data.sentinel = chosen_child_ty.sentinel();
18527 info.data.size = .Slice;18500 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());
18529 info.data.pointee_type = switch (chosen_child_ty.tag()) {18502 info.data.pointee_type = switch (chosen_child_ty.tag()) {
18530 .array => chosen_child_ty.elemType2(),18503 .array => chosen_child_ty.elemType2(),
18531 .array_u8, .array_u8_sentinel_0 => Type.initTag(.u8),18504 .array_u8, .array_u8_sentinel_0 => Type.initTag(.u8),
...@@ -18533,8 +18506,12 @@ fn resolvePeerTypes(...@@ -18533,8 +18506,12 @@ fn resolvePeerTypes(
18533 };18506 };
1853418507
18535 const new_ptr_ty = try Type.ptr(sema.arena, target, info.data);18508 const new_ptr_ty = try Type.ptr(sema.arena, target, info.data);
18536 const set_ty = err_set_ty orelse return new_ptr_ty;18509 const opt_ptr_ty = if (any_are_null)
18537 return try Module.errorUnionType(sema.arena, set_ty, new_ptr_ty);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);
18538 }18515 }
1853918516
18540 if (seen_const) {18517 if (seen_const) {
...@@ -18545,20 +18522,37 @@ fn resolvePeerTypes(...@@ -18545,20 +18522,37 @@ fn resolvePeerTypes(
18545 var info = ptr_ty.ptrInfo();18522 var info = ptr_ty.ptrInfo();
18546 info.data.mutable = false;18523 info.data.mutable = false;
18547 const new_ptr_ty = try Type.ptr(sema.arena, target, info.data);18524 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;
18548 const set_ty = err_set_ty orelse chosen_ty.errorUnionSet();18529 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);
18550 },18531 },
18551 .Pointer => {18532 .Pointer => {
18552 var info = chosen_ty.ptrInfo();18533 var info = chosen_ty.ptrInfo();
18553 info.data.mutable = false;18534 info.data.mutable = false;
18554 const new_ptr_ty = try Type.ptr(sema.arena, target, info.data);18535 const new_ptr_ty = try Type.ptr(sema.arena, target, info.data);
18555 const set_ty = err_set_ty orelse return new_ptr_ty;18536 const opt_ptr_ty = if (any_are_null)
18556 return try Module.errorUnionType(sema.arena, set_ty, new_ptr_ty);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);
18557 },18542 },
18558 else => return chosen_ty,18543 else => return chosen_ty,
18559 }18544 }
18560 }18545 }
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
18562 if (err_set_ty) |ty| switch (chosen_ty.zigTypeTag()) {18556 if (err_set_ty) |ty| switch (chosen_ty.zigTypeTag()) {
18563 .ErrorSet => return ty,18557 .ErrorSet => return ty,
18564 .ErrorUnion => {18558 .ErrorUnion => {
test/behavior/pointers.zig+4-16
...@@ -128,12 +128,6 @@ fn testDerefPtrOneVal() !void {...@@ -128,12 +128,6 @@ fn testDerefPtrOneVal() !void {
128}128}
129129
130test "peer type resolution with C pointers" {130test "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
137 var ptr_one: *u8 = undefined;131 var ptr_one: *u8 = undefined;
138 var ptr_many: [*]u8 = undefined;132 var ptr_many: [*]u8 = undefined;
139 var ptr_c: [*c]u8 = undefined;133 var ptr_c: [*c]u8 = undefined;
...@@ -163,11 +157,11 @@ test "implicit casting between C pointer and optional non-C pointer" {...@@ -163,11 +157,11 @@ test "implicit casting between C pointer and optional non-C pointer" {
163}157}
164158
165test "implicit cast error unions with non-optional to optional pointer" {159test "implicit cast error unions with non-optional to optional pointer" {
166 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
167 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO160 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
168 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;161 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
169 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;162 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
170 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;163 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
164 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
171165
172 const S = struct {166 const S = struct {
173 fn doTheTest() !void {167 fn doTheTest() !void {
...@@ -384,12 +378,6 @@ test "pointer arithmetic affects the alignment" {...@@ -384,12 +378,6 @@ test "pointer arithmetic affects the alignment" {
384}378}
385379
386test "@ptrToInt on null optional at comptime" {380test "@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
393 {381 {
394 const pointer = @intToPtr(?*u8, 0x000);382 const pointer = @intToPtr(?*u8, 0x000);
395 const x = @ptrToInt(pointer);383 const x = @ptrToInt(pointer);