| ... | @@ -15629,10 +15629,10 @@ fn zirCmpEq( | ... | @@ -15629,10 +15629,10 @@ fn zirCmpEq( |
| 15629 | | 15629 | |
| 15630 | // comparing null with optionals | 15630 | // comparing null with optionals |
| 15631 | if (lhs_ty_tag == .null and (rhs_ty_tag == .optional or rhs_ty.isCPtr(zcu))) { | 15631 | if (lhs_ty_tag == .null and (rhs_ty_tag == .optional or rhs_ty.isCPtr(zcu))) { |
| 15632 | return sema.analyzeIsNull(block, rhs, op == .neq); | 15632 | return sema.analyzeIsNull(block, src, rhs, op == .neq); |
| 15633 | } | 15633 | } |
| 15634 | if (rhs_ty_tag == .null and (lhs_ty_tag == .optional or lhs_ty.isCPtr(zcu))) { | 15634 | if (rhs_ty_tag == .null and (lhs_ty_tag == .optional or lhs_ty.isCPtr(zcu))) { |
| 15635 | return sema.analyzeIsNull(block, lhs, op == .neq); | 15635 | return sema.analyzeIsNull(block, src, lhs, op == .neq); |
| 15636 | } | 15636 | } |
| 15637 | | 15637 | |
| 15638 | if (lhs_ty_tag == .null or rhs_ty_tag == .null) { | 15638 | if (lhs_ty_tag == .null or rhs_ty_tag == .null) { |
| ... | @@ -17375,7 +17375,7 @@ fn zirIsNonNull( | ... | @@ -17375,7 +17375,7 @@ fn zirIsNonNull( |
| 17375 | const src = block.nodeOffset(inst_data.src_node); | 17375 | const src = block.nodeOffset(inst_data.src_node); |
| 17376 | const operand = sema.resolveInst(inst_data.operand); | 17376 | const operand = sema.resolveInst(inst_data.operand); |
| 17377 | try sema.checkNullableType(block, src, sema.typeOf(operand)); | 17377 | try sema.checkNullableType(block, src, sema.typeOf(operand)); |
| 17378 | return sema.analyzeIsNull(block, operand, true); | 17378 | return sema.analyzeIsNull(block, src, operand, true); |
| 17379 | } | 17379 | } |
| 17380 | | 17380 | |
| 17381 | fn zirIsNonNullPtr( | 17381 | fn zirIsNonNullPtr( |
| ... | @@ -17394,15 +17394,19 @@ fn zirIsNonNullPtr( | ... | @@ -17394,15 +17394,19 @@ fn zirIsNonNullPtr( |
| 17394 | const ptr_ty = sema.typeOf(ptr); | 17394 | const ptr_ty = sema.typeOf(ptr); |
| 17395 | assert(ptr_ty.zigTypeTag(zcu) == .pointer); | 17395 | assert(ptr_ty.zigTypeTag(zcu) == .pointer); |
| 17396 | const nullable_ty = ptr_ty.childType(zcu); | 17396 | const nullable_ty = ptr_ty.childType(zcu); |
| | 17397 | |
| 17397 | try sema.checkNullableType(block, src, nullable_ty); | 17398 | try sema.checkNullableType(block, src, nullable_ty); |
| | 17399 | |
| | 17400 | if (try sema.resolveIsNullFromType(block, src, nullable_ty)) |is_null| { |
| | 17401 | return .fromValue(.makeBool(!is_null)); |
| | 17402 | } |
| | 17403 | |
| 17398 | if (sema.resolveValue(ptr)) |ptr_val| { | 17404 | if (sema.resolveValue(ptr)) |ptr_val| { |
| 17399 | if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |nullable_val| { | 17405 | if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |nullable_val| { |
| 17400 | return sema.analyzeIsNull(block, .fromValue(nullable_val), true); | 17406 | return sema.analyzeIsNull(block, src, .fromValue(nullable_val), true); |
| 17401 | } | 17407 | } |
| 17402 | } | 17408 | } |
| 17403 | if (nullable_ty.isNullFromType(zcu)) |is_null| { | 17409 | |
| 17404 | return if (is_null) .bool_false else .bool_true; | | |
| 17405 | } | | |
| 17406 | return block.addUnOp(.is_non_null_ptr, ptr); | 17410 | return block.addUnOp(.is_non_null_ptr, ptr); |
| 17407 | } | 17411 | } |
| 17408 | | 17412 | |
| ... | @@ -30147,25 +30151,25 @@ fn analyzeSliceLen( | ... | @@ -30147,25 +30151,25 @@ fn analyzeSliceLen( |
| 30147 | fn analyzeIsNull( | 30151 | fn analyzeIsNull( |
| 30148 | sema: *Sema, | 30152 | sema: *Sema, |
| 30149 | block: *Block, | 30153 | block: *Block, |
| | 30154 | src: LazySrcLoc, |
| 30150 | operand: Air.Inst.Ref, | 30155 | operand: Air.Inst.Ref, |
| 30151 | invert_logic: bool, | 30156 | invert_logic: bool, |
| 30152 | ) CompileError!Air.Inst.Ref { | 30157 | ) CompileError!Air.Inst.Ref { |
| 30153 | const pt = sema.pt; | 30158 | const pt = sema.pt; |
| 30154 | const zcu = pt.zcu; | 30159 | const zcu = pt.zcu; |
| 30155 | const result_ty: Type = .bool; | 30160 | |
| | 30161 | if (try sema.resolveIsNullFromType(block, src, sema.typeOf(operand))) |is_null| { |
| | 30162 | return .fromValue(.makeBool(is_null != invert_logic)); // XOR |
| | 30163 | } |
| | 30164 | |
| 30156 | if (sema.resolveValue(operand)) |opt_val| { | 30165 | if (sema.resolveValue(operand)) |opt_val| { |
| 30157 | if (opt_val.isUndef(zcu)) { | 30166 | if (opt_val.isUndef(zcu)) { |
| 30158 | return pt.undefRef(result_ty); | 30167 | return pt.undefRef(.bool); |
| 30159 | } | 30168 | } |
| 30160 | const is_null = opt_val.isNull(zcu); | 30169 | const is_null = opt_val.isNull(zcu); |
| 30161 | const bool_value = if (invert_logic) !is_null else is_null; | 30170 | return .fromValue(.makeBool(is_null != invert_logic)); // XOR |
| 30162 | return if (bool_value) .bool_true else .bool_false; | | |
| 30163 | } | 30171 | } |
| 30164 | | 30172 | |
| 30165 | if (sema.typeOf(operand).isNullFromType(zcu)) |is_null| { | | |
| 30166 | const result = is_null != invert_logic; | | |
| 30167 | return if (result) .bool_true else .bool_false; | | |
| 30168 | } | | |
| 30169 | const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null; | 30173 | const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null; |
| 30170 | return block.addUnOp(air_tag, operand); | 30174 | return block.addUnOp(air_tag, operand); |
| 30171 | } | 30175 | } |
| ... | @@ -30218,6 +30222,35 @@ fn resolveIsNonErrVal( | ... | @@ -30218,6 +30222,35 @@ fn resolveIsNonErrVal( |
| 30218 | return null; | 30222 | return null; |
| 30219 | } | 30223 | } |
| 30220 | | 30224 | |
| | 30225 | fn resolveIsNullFromType( |
| | 30226 | sema: *Sema, |
| | 30227 | block: *Block, |
| | 30228 | src: LazySrcLoc, |
| | 30229 | ty: Type, |
| | 30230 | ) CompileError!?bool { |
| | 30231 | const zcu = sema.pt.zcu; |
| | 30232 | return switch (ty.zigTypeTag(zcu)) { |
| | 30233 | else => false, |
| | 30234 | .null => true, |
| | 30235 | .pointer => switch (ty.ptrSize(zcu)) { |
| | 30236 | .c => null, |
| | 30237 | else => false, |
| | 30238 | }, |
| | 30239 | .optional => { |
| | 30240 | const payload_ty = ty.optionalChild(zcu); |
| | 30241 | if (payload_ty.classify(zcu) == .no_possible_value) { |
| | 30242 | return true; // e.g. `?noreturn` |
| | 30243 | } |
| | 30244 | if (payload_ty.zigTypeTag(zcu) == .error_set and |
| | 30245 | try sema.resolveErrSetIsEmpty(block, src, payload_ty)) |
| | 30246 | { |
| | 30247 | return true; // e.g. `?error{}` |
| | 30248 | } |
| | 30249 | return null; |
| | 30250 | }, |
| | 30251 | }; |
| | 30252 | } |
| | 30253 | |
| 30221 | fn resolveIsNonErrFromType( | 30254 | fn resolveIsNonErrFromType( |
| 30222 | sema: *Sema, | 30255 | sema: *Sema, |
| 30223 | block: *Block, | 30256 | block: *Block, |
| ... | @@ -30226,7 +30259,6 @@ fn resolveIsNonErrFromType( | ... | @@ -30226,7 +30259,6 @@ fn resolveIsNonErrFromType( |
| 30226 | ) CompileError!?Value { | 30259 | ) CompileError!?Value { |
| 30227 | const pt = sema.pt; | 30260 | const pt = sema.pt; |
| 30228 | const zcu = pt.zcu; | 30261 | const zcu = pt.zcu; |
| 30229 | const ip = &zcu.intern_pool; | | |
| 30230 | const ot = operand_ty.zigTypeTag(zcu); | 30262 | const ot = operand_ty.zigTypeTag(zcu); |
| 30231 | if (ot != .error_set and ot != .error_union) return .true; | 30263 | if (ot != .error_set and ot != .error_union) return .true; |
| 30232 | if (ot == .error_set) return .false; | 30264 | if (ot == .error_set) return .false; |
| ... | @@ -30236,29 +30268,54 @@ fn resolveIsNonErrFromType( | ... | @@ -30236,29 +30268,54 @@ fn resolveIsNonErrFromType( |
| 30236 | if (payload_ty.classify(zcu) == .no_possible_value) { | 30268 | if (payload_ty.classify(zcu) == .no_possible_value) { |
| 30237 | return .false; | 30269 | return .false; |
| 30238 | } | 30270 | } |
| | 30271 | if (try sema.resolveErrSetIsEmpty(block, src, operand_ty.errorUnionSet(zcu))) { |
| | 30272 | return .true; |
| | 30273 | } |
| | 30274 | return null; |
| | 30275 | } |
| 30239 | | 30276 | |
| 30240 | // exception if the error union error set is known to be empty, | 30277 | /// Returns `true` iff the error set type `orig_err_set_ty` contains no errors. |
| 30241 | // we allow the comparison but always make it comptime-known. | 30278 | /// |
| 30242 | return err_set: switch (ip.errorUnionSet(operand_ty.toIntern())) { | 30279 | /// This is used to give comptime answers for whether `error{}!T` is an error or a payload, as well |
| 30243 | .anyerror_type => null, | 30280 | /// as whether `?error{}` is null. The type `error{}` cannot be NPV, as it has runtime bits, but the |
| | 30281 | /// only value of that type which can exist is `undefined`; semantically it has no "legal" value. |
| | 30282 | /// TODO: this runs into some unsolved language design questions about such types. Performing a |
| | 30283 | /// coercion from `@as(E, undefined)` to `E!T` needs to semantically result in an `undefined` error |
| | 30284 | /// union if our implementation is to be legal, and likewise for coercing `@as(E, undefined)` to |
| | 30285 | /// `?E` (for an error set `E`) because our implementation uses the zero error value at runtime to |
| | 30286 | /// represent `null`. The unsolved problem is the exact rules for `undefined` propagation through |
| | 30287 | /// these types: for instance, what if `@as(u32, undfined)` is coerced to `?u32`? What about error |
| | 30288 | /// union *payloads*, i.e. `@as(u32, undefined)` to `E!u32`? That one is analagous to the optional |
| | 30289 | /// example in some ways, but right now I believe there is code which relies on that coercion giving |
| | 30290 | /// a well-defined error union with an `undefined` payload. |
| | 30291 | /// Relevant issues/discussions: |
| | 30292 | /// * https://github.com/ziglang/zig/issues/1831 |
| | 30293 | /// * https://github.com/ziglang/zig/issues/6762 |
| | 30294 | /// * https://github.com/ziglang/zig/issues/1831#issuecomment-722129239 |
| | 30295 | fn resolveErrSetIsEmpty( |
| | 30296 | sema: *Sema, |
| | 30297 | block: *Block, |
| | 30298 | src: LazySrcLoc, |
| | 30299 | orig_err_set_ty: Type, |
| | 30300 | ) CompileError!bool { |
| | 30301 | const ip = &sema.pt.zcu.intern_pool; |
| | 30302 | err_set: switch (orig_err_set_ty.toIntern()) { |
| | 30303 | .anyerror_type => return false, |
| 30244 | .adhoc_inferred_error_set_type => { | 30304 | .adhoc_inferred_error_set_type => { |
| 30245 | // This is *our* error set; that is, we're currently analyzing the function | 30305 | // This is *our* error set; that is, we're currently analyzing the function |
| 30246 | // which owns it. Trying to resolve it now would cause a dependency loop. | 30306 | // which owns it. Trying to resolve it now would cause a dependency loop. |
| 30247 | // Instead, accept that we don't know. | 30307 | // Instead, accept that we don't know. |
| 30248 | return null; | 30308 | return false; |
| 30249 | }, | 30309 | }, |
| 30250 | else => |set_ty| switch (ip.indexToKey(set_ty)) { | 30310 | else => |err_set_ty| switch (ip.indexToKey(err_set_ty)) { |
| 30251 | .error_set_type => |error_set_type| switch (error_set_type.names.len) { | 30311 | .error_set_type => |es| return es.names.len == 0, |
| 30252 | 0 => .true, | | |
| 30253 | else => null, | | |
| 30254 | }, | | |
| 30255 | .inferred_error_set_type => |func_index| { | 30312 | .inferred_error_set_type => |func_index| { |
| 30256 | if (sema.fn_ret_ty_ies) |ies| { | 30313 | if (sema.fn_ret_ty_ies) |ies| { |
| 30257 | if (ies.func == func_index) { | 30314 | if (ies.func == func_index) { |
| 30258 | // This is *our* error set; that is, we're currently analyzing the function | 30315 | // This is *our* error set; that is, we're currently analyzing the function |
| 30259 | // which owns it. Trying to resolve it now would cause a dependency loop. | 30316 | // which owns it. Trying to resolve it now would cause a dependency loop. |
| 30260 | // Instead, accept that we don't know. | 30317 | // Instead, accept that we don't know. |
| 30261 | return null; | 30318 | return false; |
| 30262 | } | 30319 | } |
| 30263 | } | 30320 | } |
| 30264 | try sema.ensureFuncIesResolved(block, src, func_index); | 30321 | try sema.ensureFuncIesResolved(block, src, func_index); |
| ... | @@ -30266,7 +30323,7 @@ fn resolveIsNonErrFromType( | ... | @@ -30266,7 +30323,7 @@ fn resolveIsNonErrFromType( |
| 30266 | }, | 30323 | }, |
| 30267 | else => unreachable, | 30324 | else => unreachable, |
| 30268 | }, | 30325 | }, |
| 30269 | }; | 30326 | } |
| 30270 | } | 30327 | } |
| 30271 | | 30328 | |
| 30272 | fn analyzeIsNonErr( | 30329 | fn analyzeIsNonErr( |
| ... | @@ -30732,7 +30789,7 @@ fn analyzeSlice( | ... | @@ -30732,7 +30789,7 @@ fn analyzeSlice( |
| 30732 | if (block.wantSafety()) { | 30789 | if (block.wantSafety()) { |
| 30733 | // requirement: slicing C ptr is non-null | 30790 | // requirement: slicing C ptr is non-null |
| 30734 | if (ptr_ptr_child_ty.isCPtr(zcu)) { | 30791 | if (ptr_ptr_child_ty.isCPtr(zcu)) { |
| 30735 | const is_non_null = try sema.analyzeIsNull(block, ptr, true); | 30792 | const is_non_null = try block.addUnOp(.is_non_null, ptr); |
| 30736 | try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); | 30793 | try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); |
| 30737 | } | 30794 | } |
| 30738 | | 30795 | |
| ... | @@ -30792,7 +30849,7 @@ fn analyzeSlice( | ... | @@ -30792,7 +30849,7 @@ fn analyzeSlice( |
| 30792 | if (block.wantSafety()) { | 30849 | if (block.wantSafety()) { |
| 30793 | // requirement: slicing C ptr is non-null | 30850 | // requirement: slicing C ptr is non-null |
| 30794 | if (ptr_ptr_child_ty.isCPtr(zcu)) { | 30851 | if (ptr_ptr_child_ty.isCPtr(zcu)) { |
| 30795 | const is_non_null = try sema.analyzeIsNull(block, ptr, true); | 30852 | const is_non_null = try block.addUnOp(.is_non_null, ptr); |
| 30796 | try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); | 30853 | try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); |
| 30797 | } | 30854 | } |
| 30798 | | 30855 | |