| ... | ... | @@ -15,6 +15,10 @@ types: std.ArrayListUnmanaged(DocData.Type) = .{}, |
| 15 | 15 | decls: std.ArrayListUnmanaged(DocData.Decl) = .{}, |
| 16 | 16 | ast_nodes: std.ArrayListUnmanaged(DocData.AstNode) = .{}, |
| 17 | 17 | comptime_exprs: std.ArrayListUnmanaged(DocData.ComptimeExpr) = .{}, |
| 18 | pending_decl_paths: std.AutoHashMapUnmanaged( |
| 19 | *usize, // pointer to declpath head (ie `&decl_path[0]`) |
| 20 | std.ArrayListUnmanaged(DeclPathResumeInfo), |
| 21 | ) = .{}, |
| 18 | 22 | decl_paths_pending_on_decls: std.AutoHashMapUnmanaged( |
| 19 | 23 | usize, |
| 20 | 24 | std.ArrayListUnmanaged(DeclPathResumeInfo), |
| ... | ... | @@ -154,6 +158,10 @@ pub fn generateZirData(self: *Autodoc) !void { |
| 154 | 158 | @panic("some decl paths were never fully analized (pending on types)"); |
| 155 | 159 | } |
| 156 | 160 | |
| 161 | if (self.pending_decl_paths.count() > 0) { |
| 162 | @panic("some decl paths were never fully analized"); |
| 163 | } |
| 164 | |
| 157 | 165 | var data = DocData{ |
| 158 | 166 | .files = .{ .data = self.files }, |
| 159 | 167 | .calls = self.calls.items, |
| ... | ... | @@ -733,15 +741,49 @@ fn walkInstruction( |
| 733 | 741 | lhs = @enumToInt(lhs_extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs |
| 734 | 742 | } |
| 735 | 743 | |
| 736 | | if (tags[lhs] != .decl_val and tags[lhs] != .decl_ref) { |
| 737 | | std.debug.panic( |
| 738 | | "TODO: handle `{s}` endings in walkInstruction.field_val", |
| 739 | | .{@tagName(tags[lhs])}, |
| 740 | | ); |
| 744 | switch (tags[lhs]) { |
| 745 | else => { |
| 746 | std.debug.panic( |
| 747 | "TODO: handle `{s}` endings in walkInstruction.field_val", |
| 748 | .{@tagName(tags[lhs])}, |
| 749 | ); |
| 750 | }, |
| 751 | .import => { |
| 752 | const walk_result = try self.walkInstruction(file, parent_scope, lhs); |
| 753 | |
| 754 | // astnode |
| 755 | const ast_node_index = idx: { |
| 756 | const idx = self.ast_nodes.items.len; |
| 757 | try self.ast_nodes.append(self.arena, .{ |
| 758 | .file = 0, |
| 759 | .line = 0, |
| 760 | .col = 0, |
| 761 | .docs = "", |
| 762 | .fields = null, // walkInstruction will fill `fields` if necessary |
| 763 | }); |
| 764 | break :idx idx; |
| 765 | }; |
| 766 | const str_tok = data[inst_index].str_tok; |
| 767 | const file_path = str_tok.get(file.zir); |
| 768 | |
| 769 | const name = try std.fmt.allocPrint(self.arena, "@import({s})", .{file_path}); |
| 770 | const decls_slot_index = self.decls.items.len; |
| 771 | try self.decls.append(self.arena, .{ |
| 772 | ._analyzed = true, |
| 773 | .name = name, |
| 774 | .src = ast_node_index, |
| 775 | // .typeRef = decl_type_ref, |
| 776 | .value = walk_result, |
| 777 | .kind = "const", // find where this information can be found |
| 778 | }); |
| 779 | try path.append(self.arena, decls_slot_index); |
| 780 | }, |
| 781 | .decl_val, .decl_ref => { |
| 782 | const str_tok = data[lhs].str_tok; |
| 783 | const decls_slot_index = parent_scope.resolveDeclName(str_tok.start); |
| 784 | try path.append(self.arena, decls_slot_index); |
| 785 | }, |
| 741 | 786 | } |
| 742 | | const str_tok = data[lhs].str_tok; |
| 743 | | const decls_slot_index = parent_scope.resolveDeclName(str_tok.start); |
| 744 | | try path.append(self.arena, decls_slot_index); |
| 745 | 787 | |
| 746 | 788 | // Righ now, every element of `path` is the first index of a |
| 747 | 789 | // decl name except for the final element, which instead points to |
| ... | ... | @@ -1458,7 +1500,7 @@ fn tryResolveDeclPath( |
| 1458 | 1500 | /// File from which the decl path originates. |
| 1459 | 1501 | file: *File, |
| 1460 | 1502 | path: []usize, |
| 1461 | | ) !void { |
| 1503 | ) error{OutOfMemory}!void { |
| 1462 | 1504 | var i: usize = path.len; |
| 1463 | 1505 | while (i > 1) { |
| 1464 | 1506 | i -= 1; |
| ... | ... | @@ -1467,12 +1509,19 @@ fn tryResolveDeclPath( |
| 1467 | 1509 | |
| 1468 | 1510 | const parent = self.decls.items[decl_index]; |
| 1469 | 1511 | if (!parent._analyzed) { |
| 1512 | // This decl path is pending completion |
| 1513 | { |
| 1514 | const res = try self.pending_decl_paths.getOrPut(self.arena, &path[0]); |
| 1515 | if (!res.found_existing) res.value_ptr.* = .{}; |
| 1516 | } |
| 1517 | |
| 1470 | 1518 | const res = try self.decl_paths_pending_on_decls.getOrPut(self.arena, decl_index); |
| 1471 | 1519 | if (!res.found_existing) res.value_ptr.* = .{}; |
| 1472 | 1520 | try res.value_ptr.*.append(self.arena, .{ |
| 1473 | 1521 | .file = file, |
| 1474 | 1522 | .path = path[0 .. i + 1], |
| 1475 | 1523 | }); |
| 1524 | |
| 1476 | 1525 | return; |
| 1477 | 1526 | } |
| 1478 | 1527 | |
| ... | ... | @@ -1480,10 +1529,37 @@ fn tryResolveDeclPath( |
| 1480 | 1529 | switch (parent.value) { |
| 1481 | 1530 | else => { |
| 1482 | 1531 | std.debug.panic( |
| 1483 | | "TODO: handle `{s}`in walkInstruction.field_val\n", |
| 1484 | | .{@tagName(parent.value)}, |
| 1532 | "TODO: handle `{s}`in walkInstruction.field_val\n \"{s}\":{}", |
| 1533 | .{ @tagName(parent.value), parent.name, parent.value }, |
| 1485 | 1534 | ); |
| 1486 | 1535 | }, |
| 1536 | .declPath => |dp| { |
| 1537 | if (self.pending_decl_paths.getPtr(&dp[0])) |waiter_list| { |
| 1538 | try waiter_list.append(self.arena, .{ |
| 1539 | .file = file, |
| 1540 | .path = path[0 .. i + 1], |
| 1541 | }); |
| 1542 | |
| 1543 | // This decl path is pending completion |
| 1544 | { |
| 1545 | const res = try self.pending_decl_paths.getOrPut(self.arena, &path[0]); |
| 1546 | if (!res.found_existing) res.value_ptr.* = .{}; |
| 1547 | } |
| 1548 | |
| 1549 | return; |
| 1550 | } |
| 1551 | |
| 1552 | const final_decl_index = dp[0]; |
| 1553 | // For the purpose of being able to call tryResolveDeclPath again, |
| 1554 | // we momentarily replace the decl index present in `path[i]` |
| 1555 | // with the final decl in `dp`. |
| 1556 | // We then write the original value back as soon as we're done with the |
| 1557 | // recoursive call. This will work out correctly even if the path |
| 1558 | // will not get fully resolved. |
| 1559 | path[i] = final_decl_index; |
| 1560 | try self.tryResolveDeclPath(file, path); |
| 1561 | path[i] = decl_index; |
| 1562 | }, |
| 1487 | 1563 | .type => |t_index| switch (self.types.items[t_index]) { |
| 1488 | 1564 | else => { |
| 1489 | 1565 | std.debug.panic( |
| ... | ... | @@ -1492,6 +1568,12 @@ fn tryResolveDeclPath( |
| 1492 | 1568 | ); |
| 1493 | 1569 | }, |
| 1494 | 1570 | .Unanalyzed => { |
| 1571 | // This decl path is pending completion |
| 1572 | { |
| 1573 | const res = try self.pending_decl_paths.getOrPut(self.arena, &path[0]); |
| 1574 | if (!res.found_existing) res.value_ptr.* = .{}; |
| 1575 | } |
| 1576 | |
| 1495 | 1577 | const res = try self.decl_paths_pending_on_types.getOrPut( |
| 1496 | 1578 | self.arena, |
| 1497 | 1579 | t_index, |
| ... | ... | @@ -1501,6 +1583,7 @@ fn tryResolveDeclPath( |
| 1501 | 1583 | .file = file, |
| 1502 | 1584 | .path = path[0 .. i + 1], |
| 1503 | 1585 | }); |
| 1586 | |
| 1504 | 1587 | return; |
| 1505 | 1588 | }, |
| 1506 | 1589 | .Struct => |t_struct| { |
| ... | ... | @@ -1526,6 +1609,18 @@ fn tryResolveDeclPath( |
| 1526 | 1609 | }, |
| 1527 | 1610 | } |
| 1528 | 1611 | } |
| 1612 | |
| 1613 | if (self.pending_decl_paths.get(&path[0])) |waiter_list| { |
| 1614 | // It's important to de-register oureslves as pending before |
| 1615 | // attempting to resolve any other decl. |
| 1616 | _ = self.pending_decl_paths.remove(&path[0]); |
| 1617 | |
| 1618 | for (waiter_list.items) |resume_info| { |
| 1619 | try self.tryResolveDeclPath(resume_info.file, resume_info.path); |
| 1620 | } |
| 1621 | // TODO: this is where we should free waiter_list, but its in the arena |
| 1622 | // that said, we might want to store it elsewhere and reclaim memory asap |
| 1623 | } |
| 1529 | 1624 | } |
| 1530 | 1625 | |
| 1531 | 1626 | fn collectUnionFieldInfo( |