authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-01-20 13:34:47+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-01-21 00:33:32+00:00
log8bcb578507908e17e2081bb03f8c51eb508d51db
tree1e9caba23ae65432f8aa5abaa55f070e2b5220eb
parentb9198b708f8accb41fdb3f11bf635d63fbff461d

Sema: fix `is_non_null_ptr` handling for runtime-known pointers

We can still often determine a comptime result based on the type, even if the pointer is runtime-known. Also, we previously used load -> is non null instead of AIR `is_non_null_ptr` if the pointer is comptime-known, but that's a bad heuristic. Instead, we should check for the pointer to be comptime-known, *and* for the load to be comptime-known, and only in that case should we call `Sema.analyzeIsNonNull`. Resolves: #22556

4 files changed, 42 insertions(+), 21 deletions(-)

src/Sema.zig+17-18
......@@ -17477,10 +17477,10 @@ fn zirCmpEq(
1747717477
1747817478 // comparing null with optionals
1747917479 if (lhs_ty_tag == .null and (rhs_ty_tag == .optional or rhs_ty.isCPtr(zcu))) {
17480 return sema.analyzeIsNull(block, src, rhs, op == .neq);
17480 return sema.analyzeIsNull(block, rhs, op == .neq);
1748117481 }
1748217482 if (rhs_ty_tag == .null and (lhs_ty_tag == .optional or lhs_ty.isCPtr(zcu))) {
17483 return sema.analyzeIsNull(block, src, lhs, op == .neq);
17483 return sema.analyzeIsNull(block, lhs, op == .neq);
1748417484 }
1748517485
1748617486 if (lhs_ty_tag == .null or rhs_ty_tag == .null) {
......@@ -19326,7 +19326,7 @@ fn zirIsNonNull(
1932619326 const src = block.nodeOffset(inst_data.src_node);
1932719327 const operand = try sema.resolveInst(inst_data.operand);
1932819328 try sema.checkNullableType(block, src, sema.typeOf(operand));
19329 return sema.analyzeIsNull(block, src, operand, true);
19329 return sema.analyzeIsNull(block, operand, true);
1933019330}
1933119331
1933219332fn zirIsNonNullPtr(
......@@ -19342,12 +19342,17 @@ fn zirIsNonNullPtr(
1934219342 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
1934319343 const src = block.nodeOffset(inst_data.src_node);
1934419344 const ptr = try sema.resolveInst(inst_data.operand);
19345 const ptr_ty = sema.typeOf(ptr);
1934519346 try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2(zcu));
19346 if ((try sema.resolveValue(ptr)) == null) {
19347 return block.addUnOp(.is_non_null_ptr, ptr);
19347 if (try sema.resolveValue(ptr)) |ptr_val| {
19348 if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |loaded_val| {
19349 return sema.analyzeIsNull(block, Air.internedToRef(loaded_val.toIntern()), true);
19350 }
1934819351 }
19349 const loaded = try sema.analyzeLoad(block, src, ptr, src);
19350 return sema.analyzeIsNull(block, src, loaded, true);
19352 if (ptr_ty.childType(zcu).isNullFromType(zcu)) |is_null| {
19353 return if (is_null) .bool_false else .bool_true;
19354 }
19355 return block.addUnOp(.is_non_null_ptr, ptr);
1935119356}
1935219357
1935319358fn checkErrorType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
......@@ -32405,7 +32410,6 @@ fn analyzeSliceLen(
3240532410fn analyzeIsNull(
3240632411 sema: *Sema,
3240732412 block: *Block,
32408 src: LazySrcLoc,
3240932413 operand: Air.Inst.Ref,
3241032414 invert_logic: bool,
3241132415) CompileError!Air.Inst.Ref {
......@@ -32421,15 +32425,10 @@ fn analyzeIsNull(
3242132425 return if (bool_value) .bool_true else .bool_false;
3242232426 }
3242332427
32424 const inverted_non_null_res: Air.Inst.Ref = if (invert_logic) .bool_true else .bool_false;
32425 const operand_ty = sema.typeOf(operand);
32426 if (operand_ty.zigTypeTag(zcu) == .optional and operand_ty.optionalChild(zcu).zigTypeTag(zcu) == .noreturn) {
32427 return inverted_non_null_res;
32428 if (sema.typeOf(operand).isNullFromType(zcu)) |is_null| {
32429 const result = is_null != invert_logic;
32430 return if (result) .bool_true else .bool_false;
3242832431 }
32429 if (operand_ty.zigTypeTag(zcu) != .optional and !operand_ty.isPtrLikeOptional(zcu)) {
32430 return inverted_non_null_res;
32431 }
32432 try sema.requireRuntimeBlock(block, src, null);
3243332432 const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null;
3243432433 return block.addUnOp(air_tag, operand);
3243532434}
......@@ -33007,7 +33006,7 @@ fn analyzeSlice(
3300733006 if (block.wantSafety()) {
3300833007 // requirement: slicing C ptr is non-null
3300933008 if (ptr_ptr_child_ty.isCPtr(zcu)) {
33010 const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true);
33009 const is_non_null = try sema.analyzeIsNull(block, ptr, true);
3301133010 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
3301233011 }
3301333012
......@@ -33065,7 +33064,7 @@ fn analyzeSlice(
3306533064 if (block.wantSafety()) {
3306633065 // requirement: slicing C ptr is non-null
3306733066 if (ptr_ptr_child_ty.isCPtr(zcu)) {
33068 const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true);
33067 const is_non_null = try sema.analyzeIsNull(block, ptr, true);
3306933068 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
3307033069 }
3307133070
src/Type.zig+10
......@@ -4114,6 +4114,16 @@ pub fn containerTypeName(ty: Type, ip: *const InternPool) InternPool.NullTermina
41144114 };
41154115}
41164116
4117/// Returns `true` if a value of this type is always `null`.
4118/// Returns `false` if a value of this type is neve `null`.
4119/// Returns `null` otherwise.
4120pub fn isNullFromType(ty: Type, zcu: *const Zcu) ?bool {
4121 if (ty.zigTypeTag(zcu) != .optional and !ty.isCPtr(zcu)) return false;
4122 const child = ty.optionalChild(zcu);
4123 if (child.zigTypeTag(zcu) == .noreturn) return true; // `?noreturn` is always null
4124 return null;
4125}
4126
41174127pub const @"u1": Type = .{ .ip_index = .u1_type };
41184128pub const @"u8": Type = .{ .ip_index = .u8_type };
41194129pub const @"u16": Type = .{ .ip_index = .u16_type };
test/behavior/optional.zig+14
......@@ -498,6 +498,20 @@ test "optional of noreturn used with orelse" {
498498 try expect(val == 123);
499499}
500500
501test "mutable optional of noreturn" {
502 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
503
504 var a: ?noreturn = null;
505 if (a) |*ptr| {
506 _ = ptr;
507 @compileError("bad");
508 } else {
509 // this is what we expect to hit
510 return;
511 }
512 @compileError("bad");
513}
514
501515test "orelse on C pointer" {
502516
503517 // TODO https://github.com/ziglang/zig/issues/6597
test/cases/compile_errors/branch_in_comptime_only_scope_uses_condbr_inline.zig+1-3
......@@ -15,11 +15,9 @@ pub export fn entry2() void {
1515}
1616
1717// error
18// backend=stage2
19// target=native
2018//
2119// :5:15: error: unable to evaluate comptime expression
2220// :5:13: note: operation is runtime due to this operand
2321// :4:72: note: '@shuffle' mask must be comptime-known
24// :13:11: error: unable to evaluate comptime expression
22// :13:11: error: unable to resolve comptime value
2523// :12:72: note: '@shuffle' mask must be comptime-known