| ... | ... | @@ -23175,11 +23175,12 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData |
| 23175 | 23175 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; |
| 23176 | 23176 | const src = block.nodeOffset(extra.node); |
| 23177 | 23177 | const operand_src = block.builtinCallArgSrc(extra.node, 0); |
| 23178 | | const base_dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast"); |
| 23178 | const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast"); |
| 23179 | 23179 | const operand = try sema.resolveInst(extra.rhs); |
| 23180 | | const base_operand_ty = sema.typeOf(operand); |
| 23181 | | const dest_tag = base_dest_ty.zigTypeTag(zcu); |
| 23182 | | const operand_tag = base_operand_ty.zigTypeTag(zcu); |
| 23180 | const operand_ty = sema.typeOf(operand); |
| 23181 | |
| 23182 | const dest_tag = dest_ty.zigTypeTag(zcu); |
| 23183 | const operand_tag = operand_ty.zigTypeTag(zcu); |
| 23183 | 23184 | |
| 23184 | 23185 | if (dest_tag != .error_set and dest_tag != .error_union) { |
| 23185 | 23186 | return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(dest_tag)}); |
| ... | ... | @@ -23191,107 +23192,133 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData |
| 23191 | 23192 | return sema.fail(block, src, "cannot cast an error union type to error set", .{}); |
| 23192 | 23193 | } |
| 23193 | 23194 | if (dest_tag == .error_union and operand_tag == .error_union and |
| 23194 | | base_dest_ty.errorUnionPayload(zcu).toIntern() != base_operand_ty.errorUnionPayload(zcu).toIntern()) |
| 23195 | dest_ty.errorUnionPayload(zcu).toIntern() != operand_ty.errorUnionPayload(zcu).toIntern()) |
| 23195 | 23196 | { |
| 23196 | 23197 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 23197 | 23198 | const msg = try sema.errMsg(src, "payload types of error unions must match", .{}); |
| 23198 | 23199 | errdefer msg.destroy(sema.gpa); |
| 23199 | | const dest_ty = base_dest_ty.errorUnionPayload(zcu); |
| 23200 | | const operand_ty = base_operand_ty.errorUnionPayload(zcu); |
| 23201 | | try sema.errNote(src, msg, "destination payload is '{}'", .{dest_ty.fmt(pt)}); |
| 23202 | | try sema.errNote(src, msg, "operand payload is '{}'", .{operand_ty.fmt(pt)}); |
| 23200 | const dest_payload_ty = dest_ty.errorUnionPayload(zcu); |
| 23201 | const operand_payload_ty = operand_ty.errorUnionPayload(zcu); |
| 23202 | try sema.errNote(src, msg, "destination payload is '{}'", .{dest_payload_ty.fmt(pt)}); |
| 23203 | try sema.errNote(src, msg, "operand payload is '{}'", .{operand_payload_ty.fmt(pt)}); |
| 23203 | 23204 | try addDeclaredHereNote(sema, msg, dest_ty); |
| 23204 | 23205 | try addDeclaredHereNote(sema, msg, operand_ty); |
| 23205 | 23206 | break :msg msg; |
| 23206 | 23207 | }); |
| 23207 | 23208 | } |
| 23208 | | const dest_ty = if (dest_tag == .error_union) base_dest_ty.errorUnionSet(zcu) else base_dest_ty; |
| 23209 | | const operand_ty = if (operand_tag == .error_union) base_operand_ty.errorUnionSet(zcu) else base_operand_ty; |
| 23210 | | |
| 23211 | | // operand must be defined since it can be an invalid error value |
| 23212 | | const maybe_operand_val = try sema.resolveDefinedValue(block, operand_src, operand); |
| 23209 | const dest_err_ty = switch (dest_tag) { |
| 23210 | .error_union => dest_ty.errorUnionSet(zcu), |
| 23211 | .error_set => dest_ty, |
| 23212 | else => unreachable, |
| 23213 | }; |
| 23214 | const operand_err_ty = switch (operand_tag) { |
| 23215 | .error_union => operand_ty.errorUnionSet(zcu), |
| 23216 | .error_set => operand_ty, |
| 23217 | else => unreachable, |
| 23218 | }; |
| 23213 | 23219 | |
| 23214 | 23220 | const disjoint = disjoint: { |
| 23215 | 23221 | // Try avoiding resolving inferred error sets if we can |
| 23216 | | if (!dest_ty.isAnyError(zcu) and dest_ty.errorSetIsEmpty(zcu)) break :disjoint true; |
| 23217 | | if (!operand_ty.isAnyError(zcu) and operand_ty.errorSetIsEmpty(zcu)) break :disjoint true; |
| 23218 | | if (dest_ty.isAnyError(zcu)) break :disjoint false; |
| 23219 | | if (operand_ty.isAnyError(zcu)) break :disjoint false; |
| 23220 | | const dest_err_names = dest_ty.errorSetNames(zcu); |
| 23222 | if (!dest_err_ty.isAnyError(zcu) and dest_err_ty.errorSetIsEmpty(zcu)) break :disjoint true; |
| 23223 | if (!operand_err_ty.isAnyError(zcu) and operand_err_ty.errorSetIsEmpty(zcu)) break :disjoint true; |
| 23224 | if (dest_err_ty.isAnyError(zcu)) break :disjoint false; |
| 23225 | if (operand_err_ty.isAnyError(zcu)) break :disjoint false; |
| 23226 | const dest_err_names = dest_err_ty.errorSetNames(zcu); |
| 23221 | 23227 | for (0..dest_err_names.len) |dest_err_index| { |
| 23222 | | if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_names.get(ip)[dest_err_index])) |
| 23228 | if (Type.errorSetHasFieldIp(ip, operand_err_ty.toIntern(), dest_err_names.get(ip)[dest_err_index])) |
| 23223 | 23229 | break :disjoint false; |
| 23224 | 23230 | } |
| 23225 | 23231 | |
| 23226 | | if (!ip.isInferredErrorSetType(dest_ty.toIntern()) and |
| 23227 | | !ip.isInferredErrorSetType(operand_ty.toIntern())) |
| 23232 | if (!ip.isInferredErrorSetType(dest_err_ty.toIntern()) and |
| 23233 | !ip.isInferredErrorSetType(operand_err_ty.toIntern())) |
| 23228 | 23234 | { |
| 23229 | 23235 | break :disjoint true; |
| 23230 | 23236 | } |
| 23231 | 23237 | |
| 23232 | | _ = try sema.resolveInferredErrorSetTy(block, src, dest_ty.toIntern()); |
| 23233 | | _ = try sema.resolveInferredErrorSetTy(block, operand_src, operand_ty.toIntern()); |
| 23238 | _ = try sema.resolveInferredErrorSetTy(block, src, dest_err_ty.toIntern()); |
| 23239 | _ = try sema.resolveInferredErrorSetTy(block, operand_src, operand_err_ty.toIntern()); |
| 23234 | 23240 | for (0..dest_err_names.len) |dest_err_index| { |
| 23235 | | if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_names.get(ip)[dest_err_index])) |
| 23241 | if (Type.errorSetHasFieldIp(ip, operand_err_ty.toIntern(), dest_err_names.get(ip)[dest_err_index])) |
| 23236 | 23242 | break :disjoint false; |
| 23237 | 23243 | } |
| 23238 | 23244 | |
| 23239 | 23245 | break :disjoint true; |
| 23240 | 23246 | }; |
| 23241 | | if (disjoint and dest_tag != .error_union) { |
| 23247 | if (disjoint and !(operand_tag == .error_union and dest_tag == .error_union)) { |
| 23242 | 23248 | return sema.fail(block, src, "error sets '{}' and '{}' have no common errors", .{ |
| 23243 | | operand_ty.fmt(pt), dest_ty.fmt(pt), |
| 23249 | operand_err_ty.fmt(pt), dest_err_ty.fmt(pt), |
| 23244 | 23250 | }); |
| 23245 | 23251 | } |
| 23246 | 23252 | |
| 23247 | | if (maybe_operand_val) |val| { |
| 23248 | | if (!dest_ty.isAnyError(zcu)) check: { |
| 23249 | | const operand_val = zcu.intern_pool.indexToKey(val.toIntern()); |
| 23250 | | var error_name: InternPool.NullTerminatedString = undefined; |
| 23251 | | if (operand_tag == .error_union) { |
| 23252 | | if (operand_val.error_union.val != .err_name) break :check; |
| 23253 | | error_name = operand_val.error_union.val.err_name; |
| 23254 | | } else { |
| 23255 | | error_name = operand_val.err.name; |
| 23256 | | } |
| 23257 | | if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), error_name)) { |
| 23258 | | return sema.fail(block, src, "'error.{}' not a member of error set '{}'", .{ |
| 23259 | | error_name.fmt(ip), dest_ty.fmt(pt), |
| 23260 | | }); |
| 23261 | | } |
| 23253 | // operand must be defined since it can be an invalid error value |
| 23254 | if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| { |
| 23255 | const err_name: InternPool.NullTerminatedString = switch (operand_tag) { |
| 23256 | .error_set => ip.indexToKey(operand_val.toIntern()).err.name, |
| 23257 | .error_union => switch (ip.indexToKey(operand_val.toIntern()).error_union.val) { |
| 23258 | .err_name => |name| name, |
| 23259 | .payload => |payload_val| { |
| 23260 | assert(dest_tag == .error_union); // should be guaranteed from the type checks above |
| 23261 | return sema.coerce(block, dest_ty, Air.internedToRef(payload_val), operand_src); |
| 23262 | }, |
| 23263 | }, |
| 23264 | else => unreachable, |
| 23265 | }; |
| 23266 | |
| 23267 | if (!dest_err_ty.isAnyError(zcu) and !Type.errorSetHasFieldIp(ip, dest_err_ty.toIntern(), err_name)) { |
| 23268 | return sema.fail(block, src, "'error.{}' not a member of error set '{}'", .{ |
| 23269 | err_name.fmt(ip), dest_err_ty.fmt(pt), |
| 23270 | }); |
| 23262 | 23271 | } |
| 23263 | 23272 | |
| 23264 | | return Air.internedToRef((try pt.getCoerced(val, base_dest_ty)).toIntern()); |
| 23273 | return Air.internedToRef(try pt.intern(switch (dest_tag) { |
| 23274 | .error_set => .{ .err = .{ |
| 23275 | .ty = dest_ty.toIntern(), |
| 23276 | .name = err_name, |
| 23277 | } }, |
| 23278 | .error_union => .{ .error_union = .{ |
| 23279 | .ty = dest_ty.toIntern(), |
| 23280 | .val = .{ .err_name = err_name }, |
| 23281 | } }, |
| 23282 | else => unreachable, |
| 23283 | })); |
| 23265 | 23284 | } |
| 23266 | 23285 | |
| 23267 | | try sema.requireRuntimeBlock(block, src, operand_src); |
| 23268 | 23286 | const err_int_ty = try pt.errorIntType(); |
| 23269 | | if (block.wantSafety() and !dest_ty.isAnyError(zcu) and |
| 23270 | | dest_ty.toIntern() != .adhoc_inferred_error_set_type and |
| 23287 | if (block.wantSafety() and !dest_err_ty.isAnyError(zcu) and |
| 23288 | dest_err_ty.toIntern() != .adhoc_inferred_error_set_type and |
| 23271 | 23289 | zcu.backendSupportsFeature(.error_set_has_value)) |
| 23272 | 23290 | { |
| 23273 | | if (dest_tag == .error_union) { |
| 23274 | | const err_code = try block.addTyOp(.unwrap_errunion_err, operand_ty, operand); |
| 23275 | | const err_int = try block.addBitCast(err_int_ty, err_code); |
| 23276 | | const zero_err = try pt.intRef(try pt.errorIntType(), 0); |
| 23291 | const err_code_inst = switch (operand_tag) { |
| 23292 | .error_set => operand, |
| 23293 | .error_union => try block.addTyOp(.unwrap_errunion_err, operand_err_ty, operand), |
| 23294 | else => unreachable, |
| 23295 | }; |
| 23296 | const err_int_inst = try block.addBitCast(err_int_ty, err_code_inst); |
| 23277 | 23297 | |
| 23278 | | const is_zero = try block.addBinOp(.cmp_eq, err_int, zero_err); |
| 23298 | if (dest_tag == .error_union) { |
| 23299 | const zero_err = try pt.intRef(err_int_ty, 0); |
| 23300 | const is_zero = try block.addBinOp(.cmp_eq, err_int_inst, zero_err); |
| 23279 | 23301 | if (disjoint) { |
| 23280 | 23302 | // Error must be zero. |
| 23281 | 23303 | try sema.addSafetyCheck(block, src, is_zero, .invalid_error_code); |
| 23282 | 23304 | } else { |
| 23283 | 23305 | // Error must be in destination set or zero. |
| 23284 | | const has_value = try block.addTyOp(.error_set_has_value, dest_ty, err_code); |
| 23306 | const has_value = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst); |
| 23285 | 23307 | const ok = try block.addBinOp(.bool_or, has_value, is_zero); |
| 23286 | 23308 | try sema.addSafetyCheck(block, src, ok, .invalid_error_code); |
| 23287 | 23309 | } |
| 23288 | 23310 | } else { |
| 23289 | | const err_int_inst = try block.addBitCast(err_int_ty, operand); |
| 23290 | | const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst); |
| 23311 | const ok = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst); |
| 23291 | 23312 | try sema.addSafetyCheck(block, src, ok, .invalid_error_code); |
| 23292 | 23313 | } |
| 23293 | 23314 | } |
| 23294 | | return block.addBitCast(base_dest_ty, operand); |
| 23315 | |
| 23316 | if (operand_tag == .error_set and dest_tag == .error_union) { |
| 23317 | const err_val = try block.addBitCast(dest_err_ty, operand); |
| 23318 | return block.addTyOp(.wrap_errunion_err, dest_ty, err_val); |
| 23319 | } else { |
| 23320 | return block.addBitCast(dest_ty, operand); |
| 23321 | } |
| 23295 | 23322 | } |
| 23296 | 23323 | |
| 23297 | 23324 | fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |