| ... | ... | @@ -2392,6 +2392,34 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { |
| 2392 | 2392 | return error.AnalysisFail; |
| 2393 | 2393 | } |
| 2394 | 2394 | |
| 2395 | /// Given an ErrorMsg, modify its message and source location to the given values, turning the |
| 2396 | /// original message into a note. Notes on the original message are preserved as further notes. |
| 2397 | /// Reference trace is preserved. |
| 2398 | fn reparentOwnedErrorMsg( |
| 2399 | sema: *Sema, |
| 2400 | block: *Block, |
| 2401 | src: LazySrcLoc, |
| 2402 | msg: *Module.ErrorMsg, |
| 2403 | comptime format: []const u8, |
| 2404 | args: anytype, |
| 2405 | ) !void { |
| 2406 | const mod = sema.mod; |
| 2407 | const src_decl = mod.declPtr(block.src_decl); |
| 2408 | const resolved_src = src.toSrcLoc(src_decl, mod); |
| 2409 | const msg_str = try std.fmt.allocPrint(mod.gpa, format, args); |
| 2410 | |
| 2411 | const orig_notes = msg.notes.len; |
| 2412 | msg.notes = try sema.gpa.realloc(msg.notes, orig_notes + 1); |
| 2413 | std.mem.copyBackwards(Module.ErrorMsg, msg.notes[1..], msg.notes[0..orig_notes]); |
| 2414 | msg.notes[0] = .{ |
| 2415 | .src_loc = msg.src_loc, |
| 2416 | .msg = msg.msg, |
| 2417 | }; |
| 2418 | |
| 2419 | msg.src_loc = resolved_src; |
| 2420 | msg.msg = msg_str; |
| 2421 | } |
| 2422 | |
| 2395 | 2423 | const align_ty = Type.u29; |
| 2396 | 2424 | |
| 2397 | 2425 | fn analyzeAsAlign( |
| ... | ... | @@ -10082,6 +10110,8 @@ const SwitchProngAnalysis = struct { |
| 10082 | 10110 | operand: Air.Inst.Ref, |
| 10083 | 10111 | /// May be `undefined` if no prong has a by-ref capture. |
| 10084 | 10112 | operand_ptr: Air.Inst.Ref, |
| 10113 | /// The switch condition value. For unions, `operand` is the union and `cond` is its tag. |
| 10114 | cond: Air.Inst.Ref, |
| 10085 | 10115 | /// If this switch is on an error set, this is the type to assign to the |
| 10086 | 10116 | /// `else` prong. If `null`, the prong should be unreachable. |
| 10087 | 10117 | else_error_ty: ?Type, |
| ... | ... | @@ -10315,61 +10345,245 @@ const SwitchProngAnalysis = struct { |
| 10315 | 10345 | const first_field_index = @intCast(u32, operand_ty.unionTagFieldIndex(first_item_val, mod).?); |
| 10316 | 10346 | const first_field = union_obj.fields.values()[first_field_index]; |
| 10317 | 10347 | |
| 10318 | | for (case_vals[1..], 0..) |item, i| { |
| 10348 | const field_tys = try sema.arena.alloc(Type, case_vals.len); |
| 10349 | for (case_vals, field_tys) |item, *field_ty| { |
| 10319 | 10350 | const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable; |
| 10351 | const field_idx = @intCast(u32, operand_ty.unionTagFieldIndex(item_val, sema.mod).?); |
| 10352 | field_ty.* = union_obj.fields.values()[field_idx].ty; |
| 10353 | } |
| 10320 | 10354 | |
| 10321 | | const field_index = operand_ty.unionTagFieldIndex(item_val, mod).?; |
| 10322 | | const field = union_obj.fields.values()[field_index]; |
| 10323 | | if (!field.ty.eql(first_field.ty, mod)) { |
| 10324 | | const msg = msg: { |
| 10325 | | const capture_src = raw_capture_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, .none); |
| 10355 | // Fast path: if all the operands are the same type already, we don't need to hit |
| 10356 | // PTR! This will also allow us to emit simpler code. |
| 10357 | const same_types = for (field_tys[1..]) |field_ty| { |
| 10358 | if (!field_ty.eql(field_tys[0], sema.mod)) break false; |
| 10359 | } else true; |
| 10326 | 10360 | |
| 10327 | | const msg = try sema.errMsg(block, capture_src, "capture group with incompatible types", .{}); |
| 10328 | | errdefer msg.destroy(sema.gpa); |
| 10361 | const capture_ty = if (same_types) field_tys[0] else capture_ty: { |
| 10362 | // We need values to run PTR on, so make a bunch of undef constants. |
| 10363 | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len); |
| 10364 | for (dummy_captures, field_tys) |*dummy, field_ty| { |
| 10365 | dummy.* = try sema.addConstUndef(field_ty); |
| 10366 | } |
| 10367 | |
| 10368 | const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); |
| 10369 | @memset(case_srcs, .unneeded); |
| 10329 | 10370 | |
| 10371 | break :capture_ty sema.resolvePeerTypes(block, .unneeded, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) { |
| 10372 | error.NeededSourceLocation => { |
| 10330 | 10373 | // This must be a multi-prong so this must be a `multi_capture` src |
| 10331 | 10374 | const multi_idx = raw_capture_src.multi_capture; |
| 10375 | const src_decl_ptr = sema.mod.declPtr(block.src_decl); |
| 10376 | for (case_srcs, 0..) |*case_src, i| { |
| 10377 | const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(u32, i) } }; |
| 10378 | case_src.* = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); |
| 10379 | } |
| 10380 | const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); |
| 10381 | _ = sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err1| switch (err1) { |
| 10382 | error.AnalysisFail => { |
| 10383 | const msg = sema.err orelse return error.AnalysisFail; |
| 10384 | try sema.reparentOwnedErrorMsg(block, capture_src, msg, "capture group with incompatible types", .{}); |
| 10385 | return error.AnalysisFail; |
| 10386 | }, |
| 10387 | else => |e| return e, |
| 10388 | }; |
| 10389 | unreachable; |
| 10390 | }, |
| 10391 | else => |e| return e, |
| 10392 | }; |
| 10393 | }; |
| 10332 | 10394 | |
| 10333 | | const raw_first_item_src = Module.SwitchProngSrc{ .multi = .{ .prong = multi_idx, .item = 0 } }; |
| 10334 | | const first_item_src = raw_first_item_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, .first); |
| 10335 | | const raw_item_src = Module.SwitchProngSrc{ .multi = .{ .prong = multi_idx, .item = 1 + @intCast(u32, i) } }; |
| 10336 | | const item_src = raw_item_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, .first); |
| 10337 | | try sema.errNote(block, first_item_src, msg, "type '{}' here", .{first_field.ty.fmt(mod)}); |
| 10338 | | try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(mod)}); |
| 10339 | | break :msg msg; |
| 10340 | | }; |
| 10341 | | return sema.failWithOwnedErrorMsg(msg); |
| 10342 | | } |
| 10343 | | } |
| 10344 | | |
| 10395 | // By-reference captures have some further restrictions which make them easier to emit |
| 10345 | 10396 | if (capture_byref) { |
| 10346 | | const field_ty_ptr = try Type.ptr(sema.arena, mod, .{ |
| 10347 | | .pointee_type = first_field.ty, |
| 10348 | | .@"addrspace" = .generic, |
| 10349 | | .mutable = operand_ptr_ty.ptrIsMutable(mod), |
| 10397 | const operand_ptr_info = operand_ptr_ty.ptrInfo(mod); |
| 10398 | const capture_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ |
| 10399 | .pointee_type = capture_ty, |
| 10400 | .@"addrspace" = operand_ptr_info.@"addrspace", |
| 10401 | .mutable = operand_ptr_info.mutable, |
| 10402 | .@"volatile" = operand_ptr_info.@"volatile", |
| 10403 | // TODO: alignment! |
| 10350 | 10404 | }); |
| 10351 | 10405 | |
| 10406 | // By-ref captures of hetereogeneous types are only allowed if each field |
| 10407 | // pointer type is in-memory coercible to the capture pointer type. |
| 10408 | if (!same_types) { |
| 10409 | for (field_tys, 0..) |field_ty, i| { |
| 10410 | const field_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ |
| 10411 | .pointee_type = field_ty, |
| 10412 | .@"addrspace" = operand_ptr_info.@"addrspace", |
| 10413 | .mutable = operand_ptr_info.mutable, |
| 10414 | .@"volatile" = operand_ptr_info.@"volatile", |
| 10415 | // TODO: alignment! |
| 10416 | }); |
| 10417 | if (.ok != try sema.coerceInMemoryAllowed(block, capture_ptr_ty, field_ptr_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) { |
| 10418 | const multi_idx = raw_capture_src.multi_capture; |
| 10419 | const src_decl_ptr = sema.mod.declPtr(block.src_decl); |
| 10420 | const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); |
| 10421 | const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(u32, i) } }; |
| 10422 | const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); |
| 10423 | const msg = msg: { |
| 10424 | const msg = try sema.errMsg(block, capture_src, "capture group with incompatible types", .{}); |
| 10425 | errdefer msg.destroy(sema.gpa); |
| 10426 | try sema.errNote(block, case_src, msg, "pointer type child '{}' cannot cast into resolved pointer type child '{}'", .{ |
| 10427 | field_ty.fmt(sema.mod), |
| 10428 | capture_ty.fmt(sema.mod), |
| 10429 | }); |
| 10430 | try sema.errNote(block, capture_src, msg, "this coercion is only possible when capturing by value", .{}); |
| 10431 | break :msg msg; |
| 10432 | }; |
| 10433 | return sema.failWithOwnedErrorMsg(msg); |
| 10434 | } |
| 10435 | } |
| 10436 | } |
| 10437 | |
| 10352 | 10438 | if (try sema.resolveDefinedValue(block, operand_src, spa.operand_ptr)) |op_ptr_val| { |
| 10353 | | return sema.addConstant(field_ty_ptr, (try mod.intern(.{ .ptr = .{ |
| 10354 | | .ty = field_ty_ptr.toIntern(), |
| 10355 | | .addr = .{ .field = .{ |
| 10356 | | .base = op_ptr_val.toIntern(), |
| 10357 | | .index = first_field_index, |
| 10358 | | } }, |
| 10359 | | } })).toValue()); |
| 10439 | if (op_ptr_val.isUndef(mod)) return sema.addConstUndef(capture_ptr_ty); |
| 10440 | return sema.addConstant( |
| 10441 | capture_ptr_ty, |
| 10442 | (try mod.intern(.{ .ptr = .{ |
| 10443 | .ty = capture_ptr_ty.toIntern(), |
| 10444 | .addr = .{ .field = .{ |
| 10445 | .base = op_ptr_val.toIntern(), |
| 10446 | .index = first_field_index, |
| 10447 | } }, |
| 10448 | } })).toValue(), |
| 10449 | ); |
| 10360 | 10450 | } |
| 10451 | |
| 10361 | 10452 | try sema.requireRuntimeBlock(block, operand_src, null); |
| 10362 | | return block.addStructFieldPtr(spa.operand_ptr, first_field_index, field_ty_ptr); |
| 10453 | return block.addStructFieldPtr(spa.operand_ptr, first_field_index, capture_ptr_ty); |
| 10363 | 10454 | } |
| 10364 | 10455 | |
| 10365 | 10456 | if (try sema.resolveDefinedValue(block, operand_src, spa.operand)) |operand_val| { |
| 10366 | | return sema.addConstant( |
| 10367 | | first_field.ty, |
| 10368 | | mod.intern_pool.indexToKey(operand_val.toIntern()).un.val.toValue(), |
| 10369 | | ); |
| 10457 | if (operand_val.isUndef(mod)) return sema.addConstUndef(capture_ty); |
| 10458 | const union_val = mod.intern_pool.indexToKey(operand_val.toIntern()).un; |
| 10459 | if (union_val.tag.toValue().isUndef(mod)) return sema.addConstUndef(capture_ty); |
| 10460 | const active_field_idx = @intCast(u32, operand_ty.unionTagFieldIndex(union_val.tag.toValue(), sema.mod).?); |
| 10461 | const field_ty = union_obj.fields.values()[active_field_idx].ty; |
| 10462 | const uncoerced = try sema.addConstant(field_ty, union_val.val.toValue()); |
| 10463 | return sema.coerce(block, capture_ty, uncoerced, operand_src); |
| 10370 | 10464 | } |
| 10465 | |
| 10371 | 10466 | try sema.requireRuntimeBlock(block, operand_src, null); |
| 10372 | | return block.addStructFieldVal(spa.operand, first_field_index, first_field.ty); |
| 10467 | |
| 10468 | if (same_types) { |
| 10469 | return block.addStructFieldVal(spa.operand, first_field_index, capture_ty); |
| 10470 | } |
| 10471 | |
| 10472 | // We may have to emit a switch block which coerces the operand to the capture type. |
| 10473 | // If we can, try to avoid that using in-memory coercions. |
| 10474 | const first_non_imc = in_mem: { |
| 10475 | for (field_tys, 0..) |field_ty, i| { |
| 10476 | if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) { |
| 10477 | break :in_mem i; |
| 10478 | } |
| 10479 | } |
| 10480 | // All fields are in-memory coercible to the resolved type! |
| 10481 | // Just take the first field and bitcast the result. |
| 10482 | const uncoerced = try block.addStructFieldVal(spa.operand, first_field_index, first_field.ty); |
| 10483 | return block.addBitCast(capture_ty, uncoerced); |
| 10484 | }; |
| 10485 | |
| 10486 | // By-val capture with heterogeneous types which are not all in-memory coercible to |
| 10487 | // the resolved capture type. We finally have to fall back to the ugly method. |
| 10488 | |
| 10489 | // However, let's first track which operands are in-memory coercible. There may well |
| 10490 | // be several, and we can squash all of these cases into the same switch prong using |
| 10491 | // a simple bitcast. We'll make this the 'else' prong. |
| 10492 | |
| 10493 | var in_mem_coercible = try std.DynamicBitSet.initFull(sema.arena, field_tys.len); |
| 10494 | in_mem_coercible.unset(first_non_imc); |
| 10495 | { |
| 10496 | const next = first_non_imc + 1; |
| 10497 | for (field_tys[next..], next..) |field_ty, i| { |
| 10498 | if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) { |
| 10499 | in_mem_coercible.unset(i); |
| 10500 | } |
| 10501 | } |
| 10502 | } |
| 10503 | |
| 10504 | const capture_block_inst = try block.addInstAsIndex(.{ |
| 10505 | .tag = .block, |
| 10506 | .data = .{ |
| 10507 | .ty_pl = .{ |
| 10508 | .ty = try sema.addType(capture_ty), |
| 10509 | .payload = undefined, // updated below |
| 10510 | }, |
| 10511 | }, |
| 10512 | }); |
| 10513 | |
| 10514 | const prong_count = field_tys.len - in_mem_coercible.count(); |
| 10515 | |
| 10516 | const estimated_extra = prong_count * 6; // 2 for Case, 1 item, probably 3 insts |
| 10517 | var cases_extra = try std.ArrayList(u32).initCapacity(sema.gpa, estimated_extra); |
| 10518 | defer cases_extra.deinit(); |
| 10519 | |
| 10520 | { |
| 10521 | // Non-bitcast cases |
| 10522 | var it = in_mem_coercible.iterator(.{ .kind = .unset }); |
| 10523 | while (it.next()) |idx| { |
| 10524 | var coerce_block = block.makeSubBlock(); |
| 10525 | defer coerce_block.instructions.deinit(sema.gpa); |
| 10526 | |
| 10527 | const uncoerced = try coerce_block.addStructFieldVal(spa.operand, @intCast(u32, idx), field_tys[idx]); |
| 10528 | const coerced = sema.coerce(&coerce_block, capture_ty, uncoerced, .unneeded) catch |err| switch (err) { |
| 10529 | error.NeededSourceLocation => { |
| 10530 | const multi_idx = raw_capture_src.multi_capture; |
| 10531 | const src_decl_ptr = sema.mod.declPtr(block.src_decl); |
| 10532 | const raw_case_src: Module.SwitchProngSrc = .{ .multi = .{ .prong = multi_idx, .item = @intCast(u32, idx) } }; |
| 10533 | const case_src = raw_case_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); |
| 10534 | _ = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); |
| 10535 | unreachable; |
| 10536 | }, |
| 10537 | else => |e| return e, |
| 10538 | }; |
| 10539 | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 10540 | |
| 10541 | try cases_extra.ensureUnusedCapacity(3 + coerce_block.instructions.items.len); |
| 10542 | cases_extra.appendAssumeCapacity(1); // items_len |
| 10543 | cases_extra.appendAssumeCapacity(@intCast(u32, coerce_block.instructions.items.len)); // body_len |
| 10544 | cases_extra.appendAssumeCapacity(@enumToInt(case_vals[idx])); // item |
| 10545 | cases_extra.appendSliceAssumeCapacity(coerce_block.instructions.items); // body |
| 10546 | } |
| 10547 | } |
| 10548 | const else_body_len = len: { |
| 10549 | // 'else' prong uses a bitcast |
| 10550 | var coerce_block = block.makeSubBlock(); |
| 10551 | defer coerce_block.instructions.deinit(sema.gpa); |
| 10552 | |
| 10553 | const first_imc = in_mem_coercible.findFirstSet().?; |
| 10554 | const uncoerced = try coerce_block.addStructFieldVal(spa.operand, @intCast(u32, first_imc), field_tys[first_imc]); |
| 10555 | const coerced = try coerce_block.addBitCast(capture_ty, uncoerced); |
| 10556 | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 10557 | |
| 10558 | try cases_extra.appendSlice(coerce_block.instructions.items); |
| 10559 | break :len coerce_block.instructions.items.len; |
| 10560 | }; |
| 10561 | |
| 10562 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).Struct.fields.len + |
| 10563 | cases_extra.items.len + |
| 10564 | @typeInfo(Air.Block).Struct.fields.len + |
| 10565 | 1); |
| 10566 | |
| 10567 | const switch_br_inst = @intCast(u32, sema.air_instructions.len); |
| 10568 | try sema.air_instructions.append(sema.gpa, .{ |
| 10569 | .tag = .switch_br, |
| 10570 | .data = .{ .pl_op = .{ |
| 10571 | .operand = spa.cond, |
| 10572 | .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ |
| 10573 | .cases_len = @intCast(u32, prong_count), |
| 10574 | .else_body_len = @intCast(u32, else_body_len), |
| 10575 | }), |
| 10576 | } }, |
| 10577 | }); |
| 10578 | sema.air_extra.appendSliceAssumeCapacity(cases_extra.items); |
| 10579 | |
| 10580 | // Set up block body |
| 10581 | sema.air_instructions.items(.data)[capture_block_inst].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ |
| 10582 | .body_len = 1, |
| 10583 | }); |
| 10584 | sema.air_extra.appendAssumeCapacity(switch_br_inst); |
| 10585 | |
| 10586 | return Air.indexToRef(capture_block_inst); |
| 10373 | 10587 | }, |
| 10374 | 10588 | .ErrorSet => { |
| 10375 | 10589 | if (capture_byref) { |
| ... | ... | @@ -11099,6 +11313,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r |
| 11099 | 11313 | .parent_block = block, |
| 11100 | 11314 | .operand = raw_operand.val, |
| 11101 | 11315 | .operand_ptr = raw_operand.ptr, |
| 11316 | .cond = operand, |
| 11102 | 11317 | .else_error_ty = else_error_ty, |
| 11103 | 11318 | .switch_block_inst = inst, |
| 11104 | 11319 | .tag_capture_inst = tag_capture_inst, |