| ... | @@ -15,6 +15,10 @@ types: std.ArrayListUnmanaged(DocData.Type) = .{}, | ... | @@ -15,6 +15,10 @@ types: std.ArrayListUnmanaged(DocData.Type) = .{}, |
| 15 | decls: std.ArrayListUnmanaged(DocData.Decl) = .{}, | 15 | decls: std.ArrayListUnmanaged(DocData.Decl) = .{}, |
| 16 | ast_nodes: std.ArrayListUnmanaged(DocData.AstNode) = .{}, | 16 | ast_nodes: std.ArrayListUnmanaged(DocData.AstNode) = .{}, |
| 17 | comptime_exprs: std.ArrayListUnmanaged(DocData.ComptimeExpr) = .{}, | 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 | decl_paths_pending_on_decls: std.AutoHashMapUnmanaged( | 22 | decl_paths_pending_on_decls: std.AutoHashMapUnmanaged( |
| 19 | usize, | 23 | usize, |
| 20 | std.ArrayListUnmanaged(DeclPathResumeInfo), | 24 | std.ArrayListUnmanaged(DeclPathResumeInfo), |
| ... | @@ -154,6 +158,10 @@ pub fn generateZirData(self: *Autodoc) !void { | ... | @@ -154,6 +158,10 @@ pub fn generateZirData(self: *Autodoc) !void { |
| 154 | @panic("some decl paths were never fully analized (pending on types)"); | 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 | var data = DocData{ | 165 | var data = DocData{ |
| 158 | .files = .{ .data = self.files }, | 166 | .files = .{ .data = self.files }, |
| 159 | .calls = self.calls.items, | 167 | .calls = self.calls.items, |
| ... | @@ -733,15 +741,49 @@ fn walkInstruction( | ... | @@ -733,15 +741,49 @@ fn walkInstruction( |
| 733 | lhs = @enumToInt(lhs_extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs | 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) { | 744 | switch (tags[lhs]) { |
| 737 | std.debug.panic( | 745 | else => { |
| 738 | "TODO: handle `{s}` endings in walkInstruction.field_val", | 746 | std.debug.panic( |
| 739 | .{@tagName(tags[lhs])}, | 747 | "TODO: handle `{s}` endings in walkInstruction.field_val", |
| 740 | ); | 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 | // Righ now, every element of `path` is the first index of a | 788 | // Righ now, every element of `path` is the first index of a |
| 747 | // decl name except for the final element, which instead points to | 789 | // decl name except for the final element, which instead points to |
| ... | @@ -1458,7 +1500,7 @@ fn tryResolveDeclPath( | ... | @@ -1458,7 +1500,7 @@ fn tryResolveDeclPath( |
| 1458 | /// File from which the decl path originates. | 1500 | /// File from which the decl path originates. |
| 1459 | file: *File, | 1501 | file: *File, |
| 1460 | path: []usize, | 1502 | path: []usize, |
| 1461 | ) !void { | 1503 | ) error{OutOfMemory}!void { |
| 1462 | var i: usize = path.len; | 1504 | var i: usize = path.len; |
| 1463 | while (i > 1) { | 1505 | while (i > 1) { |
| 1464 | i -= 1; | 1506 | i -= 1; |
| ... | @@ -1467,12 +1509,19 @@ fn tryResolveDeclPath( | ... | @@ -1467,12 +1509,19 @@ fn tryResolveDeclPath( |
| 1467 | | 1509 | |
| 1468 | const parent = self.decls.items[decl_index]; | 1510 | const parent = self.decls.items[decl_index]; |
| 1469 | if (!parent._analyzed) { | 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 | const res = try self.decl_paths_pending_on_decls.getOrPut(self.arena, decl_index); | 1518 | const res = try self.decl_paths_pending_on_decls.getOrPut(self.arena, decl_index); |
| 1471 | if (!res.found_existing) res.value_ptr.* = .{}; | 1519 | if (!res.found_existing) res.value_ptr.* = .{}; |
| 1472 | try res.value_ptr.*.append(self.arena, .{ | 1520 | try res.value_ptr.*.append(self.arena, .{ |
| 1473 | .file = file, | 1521 | .file = file, |
| 1474 | .path = path[0 .. i + 1], | 1522 | .path = path[0 .. i + 1], |
| 1475 | }); | 1523 | }); |
| | 1524 | |
| 1476 | return; | 1525 | return; |
| 1477 | } | 1526 | } |
| 1478 | | 1527 | |
| ... | @@ -1480,10 +1529,37 @@ fn tryResolveDeclPath( | ... | @@ -1480,10 +1529,37 @@ fn tryResolveDeclPath( |
| 1480 | switch (parent.value) { | 1529 | switch (parent.value) { |
| 1481 | else => { | 1530 | else => { |
| 1482 | std.debug.panic( | 1531 | std.debug.panic( |
| 1483 | "TODO: handle `{s}`in walkInstruction.field_val\n", | 1532 | "TODO: handle `{s}`in walkInstruction.field_val\n \"{s}\":{}", |
| 1484 | .{@tagName(parent.value)}, | 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 | .type => |t_index| switch (self.types.items[t_index]) { | 1563 | .type => |t_index| switch (self.types.items[t_index]) { |
| 1488 | else => { | 1564 | else => { |
| 1489 | std.debug.panic( | 1565 | std.debug.panic( |
| ... | @@ -1492,6 +1568,12 @@ fn tryResolveDeclPath( | ... | @@ -1492,6 +1568,12 @@ fn tryResolveDeclPath( |
| 1492 | ); | 1568 | ); |
| 1493 | }, | 1569 | }, |
| 1494 | .Unanalyzed => { | 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 | const res = try self.decl_paths_pending_on_types.getOrPut( | 1577 | const res = try self.decl_paths_pending_on_types.getOrPut( |
| 1496 | self.arena, | 1578 | self.arena, |
| 1497 | t_index, | 1579 | t_index, |
| ... | @@ -1501,6 +1583,7 @@ fn tryResolveDeclPath( | ... | @@ -1501,6 +1583,7 @@ fn tryResolveDeclPath( |
| 1501 | .file = file, | 1583 | .file = file, |
| 1502 | .path = path[0 .. i + 1], | 1584 | .path = path[0 .. i + 1], |
| 1503 | }); | 1585 | }); |
| | 1586 | |
| 1504 | return; | 1587 | return; |
| 1505 | }, | 1588 | }, |
| 1506 | .Struct => |t_struct| { | 1589 | .Struct => |t_struct| { |
| ... | @@ -1526,6 +1609,18 @@ fn tryResolveDeclPath( | ... | @@ -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 | fn collectUnionFieldInfo( | 1626 | fn collectUnionFieldInfo( |