| ... | ... | @@ -805,7 +805,12 @@ pub fn analyzeBody( |
| 805 | 805 | continue; |
| 806 | 806 | }, |
| 807 | 807 | .validate_struct_init => { |
| 808 | | try sema.zirValidateStructInit(block, inst); |
| 808 | try sema.zirValidateStructInit(block, inst, false); |
| 809 | i += 1; |
| 810 | continue; |
| 811 | }, |
| 812 | .validate_struct_init_comptime => { |
| 813 | try sema.zirValidateStructInit(block, inst, true); |
| 809 | 814 | i += 1; |
| 810 | 815 | continue; |
| 811 | 816 | }, |
| ... | ... | @@ -2438,7 +2443,12 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com |
| 2438 | 2443 | } |
| 2439 | 2444 | } |
| 2440 | 2445 | |
| 2441 | | fn zirValidateStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 2446 | fn zirValidateStructInit( |
| 2447 | sema: *Sema, |
| 2448 | block: *Block, |
| 2449 | inst: Zir.Inst.Index, |
| 2450 | is_comptime: bool, |
| 2451 | ) CompileError!void { |
| 2442 | 2452 | const tracy = trace(@src()); |
| 2443 | 2453 | defer tracy.end(); |
| 2444 | 2454 | |
| ... | ... | @@ -2456,6 +2466,7 @@ fn zirValidateStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi |
| 2456 | 2466 | agg_ty.castTag(.@"struct").?.data, |
| 2457 | 2467 | init_src, |
| 2458 | 2468 | instrs, |
| 2469 | is_comptime, |
| 2459 | 2470 | ), |
| 2460 | 2471 | .Union => return sema.validateUnionInit( |
| 2461 | 2472 | block, |
| ... | ... | @@ -2529,6 +2540,7 @@ fn validateStructInit( |
| 2529 | 2540 | struct_obj: *Module.Struct, |
| 2530 | 2541 | init_src: LazySrcLoc, |
| 2531 | 2542 | instrs: []const Zir.Inst.Index, |
| 2543 | is_comptime: bool, |
| 2532 | 2544 | ) CompileError!void { |
| 2533 | 2545 | const gpa = sema.gpa; |
| 2534 | 2546 | |
| ... | ... | @@ -2537,10 +2549,13 @@ fn validateStructInit( |
| 2537 | 2549 | defer gpa.free(found_fields); |
| 2538 | 2550 | mem.set(Zir.Inst.Index, found_fields, 0); |
| 2539 | 2551 | |
| 2552 | var struct_ptr_zir_ref: Zir.Inst.Ref = undefined; |
| 2553 | |
| 2540 | 2554 | for (instrs) |field_ptr| { |
| 2541 | 2555 | const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node; |
| 2542 | 2556 | const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_ptr_data.src_node }; |
| 2543 | 2557 | const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; |
| 2558 | struct_ptr_zir_ref = field_ptr_extra.lhs; |
| 2544 | 2559 | const field_name = sema.code.nullTerminatedString(field_ptr_extra.field_name_start); |
| 2545 | 2560 | const field_index = struct_obj.fields.getIndex(field_name) orelse |
| 2546 | 2561 | return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); |
| ... | ... | @@ -2561,19 +2576,127 @@ fn validateStructInit( |
| 2561 | 2576 | |
| 2562 | 2577 | var root_msg: ?*Module.ErrorMsg = null; |
| 2563 | 2578 | |
| 2564 | | // TODO handle default struct field values |
| 2579 | const fields = struct_obj.fields.values(); |
| 2580 | const struct_ptr = sema.resolveInst(struct_ptr_zir_ref); |
| 2581 | const struct_ty = sema.typeOf(struct_ptr).childType(); |
| 2582 | |
| 2583 | if (is_comptime or block.is_comptime) { |
| 2584 | // In this case the only thing we need to do is evaluate the implicit |
| 2585 | // store instructions for default field values, and report any missing fields. |
| 2586 | // Avoid the cost of the extra machinery for detecting a comptime struct init value. |
| 2587 | for (found_fields) |field_ptr, i| { |
| 2588 | if (field_ptr != 0) continue; |
| 2589 | |
| 2590 | const field = fields[i]; |
| 2591 | const field_name = struct_obj.fields.keys()[i]; |
| 2592 | |
| 2593 | if (field.default_val.tag() == .unreachable_value) { |
| 2594 | const template = "missing struct field: {s}"; |
| 2595 | const args = .{field_name}; |
| 2596 | if (root_msg) |msg| { |
| 2597 | try sema.errNote(block, init_src, msg, template, args); |
| 2598 | } else { |
| 2599 | root_msg = try sema.errMsg(block, init_src, template, args); |
| 2600 | } |
| 2601 | continue; |
| 2602 | } |
| 2603 | |
| 2604 | const default_field_ptr = try sema.structFieldPtr(block, init_src, struct_ptr, field_name, init_src, struct_ty); |
| 2605 | const init = try sema.addConstant(field.ty, field.default_val); |
| 2606 | const field_src = init_src; // TODO better source location |
| 2607 | try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store); |
| 2608 | } |
| 2609 | |
| 2610 | if (root_msg) |msg| { |
| 2611 | const fqn = try struct_obj.getFullyQualifiedName(gpa); |
| 2612 | defer gpa.free(fqn); |
| 2613 | try sema.mod.errNoteNonLazy( |
| 2614 | struct_obj.srcLoc(), |
| 2615 | msg, |
| 2616 | "struct '{s}' declared here", |
| 2617 | .{fqn}, |
| 2618 | ); |
| 2619 | return sema.failWithOwnedErrorMsg(msg); |
| 2620 | } |
| 2621 | |
| 2622 | return; |
| 2623 | } |
| 2624 | |
| 2625 | var struct_is_comptime = true; |
| 2626 | var first_block_index: usize = std.math.maxInt(u32); |
| 2627 | |
| 2628 | const air_tags = sema.air_instructions.items(.tag); |
| 2629 | const air_datas = sema.air_instructions.items(.data); |
| 2630 | |
| 2631 | // We collect the comptime field values in case the struct initialization |
| 2632 | // ends up being comptime-known. |
| 2633 | const field_values = try sema.arena.alloc(Value, fields.len); |
| 2634 | |
| 2565 | 2635 | for (found_fields) |field_ptr, i| { |
| 2566 | | if (field_ptr != 0) continue; |
| 2636 | const field = fields[i]; |
| 2637 | |
| 2638 | if (field_ptr != 0) { |
| 2639 | const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node; |
| 2640 | const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_ptr_data.src_node }; |
| 2641 | |
| 2642 | // Determine whether the value stored to this pointer is comptime-known. |
| 2643 | if (try sema.typeHasOnePossibleValue(block, field_src, field.ty)) |opv| { |
| 2644 | field_values[i] = opv; |
| 2645 | continue; |
| 2646 | } |
| 2647 | |
| 2648 | const field_ptr_air_ref = sema.inst_map.get(field_ptr).?; |
| 2649 | const field_ptr_air_inst = Air.refToIndex(field_ptr_air_ref).?; |
| 2650 | // Find the block index of the field_ptr so that we can look at the next |
| 2651 | // instruction after it within the same block. |
| 2652 | // Possible performance enhancement: save the `block_index` between iterations |
| 2653 | // of the for loop. |
| 2654 | const next_air_inst = inst: { |
| 2655 | var block_index = block.instructions.items.len - 1; |
| 2656 | while (block.instructions.items[block_index] != field_ptr_air_inst) { |
| 2657 | block_index -= 1; |
| 2658 | } |
| 2659 | first_block_index = @minimum(first_block_index, block_index); |
| 2660 | break :inst block.instructions.items[block_index + 1]; |
| 2661 | }; |
| 2662 | |
| 2663 | // If the next instructon is a store with a comptime operand, this field |
| 2664 | // is comptime. |
| 2665 | switch (air_tags[next_air_inst]) { |
| 2666 | .store => { |
| 2667 | const bin_op = air_datas[next_air_inst].bin_op; |
| 2668 | if (bin_op.lhs != field_ptr_air_ref) { |
| 2669 | struct_is_comptime = false; |
| 2670 | continue; |
| 2671 | } |
| 2672 | if (try sema.resolveMaybeUndefValAllowVariables(block, field_src, bin_op.rhs)) |val| { |
| 2673 | field_values[i] = val; |
| 2674 | } else { |
| 2675 | struct_is_comptime = false; |
| 2676 | } |
| 2677 | continue; |
| 2678 | }, |
| 2679 | else => { |
| 2680 | struct_is_comptime = false; |
| 2681 | continue; |
| 2682 | }, |
| 2683 | } |
| 2684 | } |
| 2567 | 2685 | |
| 2568 | 2686 | const field_name = struct_obj.fields.keys()[i]; |
| 2569 | | const template = "missing struct field: {s}"; |
| 2570 | | const args = .{field_name}; |
| 2571 | | if (root_msg) |msg| { |
| 2572 | | try sema.errNote(block, init_src, msg, template, args); |
| 2573 | | } else { |
| 2574 | | root_msg = try sema.errMsg(block, init_src, template, args); |
| 2687 | |
| 2688 | if (field.default_val.tag() == .unreachable_value) { |
| 2689 | const template = "missing struct field: {s}"; |
| 2690 | const args = .{field_name}; |
| 2691 | if (root_msg) |msg| { |
| 2692 | try sema.errNote(block, init_src, msg, template, args); |
| 2693 | } else { |
| 2694 | root_msg = try sema.errMsg(block, init_src, template, args); |
| 2695 | } |
| 2696 | continue; |
| 2575 | 2697 | } |
| 2576 | 2698 | } |
| 2699 | |
| 2577 | 2700 | if (root_msg) |msg| { |
| 2578 | 2701 | const fqn = try struct_obj.getFullyQualifiedName(gpa); |
| 2579 | 2702 | defer gpa.free(fqn); |
| ... | ... | @@ -2585,6 +2708,40 @@ fn validateStructInit( |
| 2585 | 2708 | ); |
| 2586 | 2709 | return sema.failWithOwnedErrorMsg(msg); |
| 2587 | 2710 | } |
| 2711 | |
| 2712 | if (struct_is_comptime) { |
| 2713 | // Our task is to delete all the `field_ptr` and `store` instructions, and insert |
| 2714 | // instead a single `store` to the struct_ptr with a comptime struct value. |
| 2715 | |
| 2716 | block.instructions.shrinkRetainingCapacity(first_block_index); |
| 2717 | |
| 2718 | // The `field_values` array has been populated for all the non-default struct |
| 2719 | // fields. Here we fill in the default field values. |
| 2720 | for (found_fields) |field_ptr, i| { |
| 2721 | if (field_ptr != 0) continue; |
| 2722 | |
| 2723 | field_values[i] = fields[i].default_val; |
| 2724 | } |
| 2725 | |
| 2726 | const struct_val = try Value.Tag.@"struct".create(sema.arena, field_values); |
| 2727 | const struct_init = try sema.addConstant(struct_ty, struct_val); |
| 2728 | try sema.storePtr2(block, init_src, struct_ptr, init_src, struct_init, init_src, .store); |
| 2729 | return; |
| 2730 | } |
| 2731 | |
| 2732 | // Our task is to insert `store` instructions for all the default field values. |
| 2733 | |
| 2734 | for (found_fields) |field_ptr, i| { |
| 2735 | if (field_ptr != 0) continue; |
| 2736 | |
| 2737 | const field = fields[i]; |
| 2738 | const field_name = struct_obj.fields.keys()[i]; |
| 2739 | const default_field_ptr = try sema.structFieldPtr(block, init_src, struct_ptr, field_name, init_src, struct_ty); |
| 2740 | |
| 2741 | const init = try sema.addConstant(field.ty, field.default_val); |
| 2742 | const field_src = init_src; // TODO better source location |
| 2743 | try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store); |
| 2744 | } |
| 2588 | 2745 | } |
| 2589 | 2746 | |
| 2590 | 2747 | fn zirValidateArrayInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |