authorgravatar for kappaloris@gmail.comLoris Cro <kappaloris@gmail.com> 2022-03-21 18:10:20+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-19 19:10:11-07:00
log40a2844c304ebe35fdbb2798499b67ca943c0259
tree9bda2c4398f58ecd8096a33ee89600b1676061eb
parent63be9e65ed151a75595ffeac287a6897067ae41f

autodoc: decl paths become ref paths

originally I thought `foo.bar.baz` was a path of decls, but turns out other language constructs require to make this model more general. originally a decl path was an array of decl indexes, now it's an array of `WalkResult`s

1 files changed, 349 insertions(+), 390 deletions(-)

src/Autodoc.zig+349-390
...@@ -22,22 +22,22 @@ comptime_exprs: std.ArrayListUnmanaged(DocData.ComptimeExpr) = .{},...@@ -22,22 +22,22 @@ comptime_exprs: std.ArrayListUnmanaged(DocData.ComptimeExpr) = .{},
2222
23// These fields hold temporary state of the analysis process23// These fields hold temporary state of the analysis process
24// and are mainly used by the decl path resolving algorithm.24// and are mainly used by the decl path resolving algorithm.
25pending_decl_paths: std.AutoHashMapUnmanaged(25pending_ref_paths: std.AutoHashMapUnmanaged(
26 *usize, // pointer to declpath head (ie `&decl_path[0]`)26 *DocData.WalkResult, // pointer to declpath tail end (ie `&decl_path[decl_path.len - 1]`)
27 std.ArrayListUnmanaged(DeclPathResumeInfo),27 std.ArrayListUnmanaged(RefPathResumeInfo),
28) = .{},28) = .{},
29decl_paths_pending_on_decls: std.AutoHashMapUnmanaged(29ref_paths_pending_on_decls: std.AutoHashMapUnmanaged(
30 usize,30 usize,
31 std.ArrayListUnmanaged(DeclPathResumeInfo),31 std.ArrayListUnmanaged(RefPathResumeInfo),
32) = .{},32) = .{},
33decl_paths_pending_on_types: std.AutoHashMapUnmanaged(33ref_paths_pending_on_types: std.AutoHashMapUnmanaged(
34 usize,34 usize,
35 std.ArrayListUnmanaged(DeclPathResumeInfo),35 std.ArrayListUnmanaged(RefPathResumeInfo),
36) = .{},36) = .{},
3737
38const DeclPathResumeInfo = struct {38const RefPathResumeInfo = struct {
39 file: *File,39 file: *File,
40 decl_path: DocData.DeclPath,40 ref_path: []DocData.WalkResult,
41};41};
4242
43var arena_allocator: std.heap.ArenaAllocator = undefined;43var arena_allocator: std.heap.ArenaAllocator = undefined;
...@@ -79,6 +79,8 @@ pub fn generateZirData(self: *Autodoc) !void {...@@ -79,6 +79,8 @@ pub fn generateZirData(self: *Autodoc) !void {
79 try self.types.append(self.arena, .{79 try self.types.append(self.arena, .{
80 .ComptimeExpr = .{ .name = "ComptimeExpr" },80 .ComptimeExpr = .{ .name = "ComptimeExpr" },
81 });81 });
82
83 var tr = DocData.WalkResult{ .type = @enumToInt(Ref.usize_type) };
82 // this skipts Ref.none but it's ok becuse we replaced it with ComptimeExpr84 // this skipts Ref.none but it's ok becuse we replaced it with ComptimeExpr
83 var i: u32 = 1;85 var i: u32 = 1;
84 while (i <= @enumToInt(Ref.anyerror_void_error_union_type)) : (i += 1) {86 while (i <= @enumToInt(Ref.anyerror_void_error_union_type)) : (i += 1) {
...@@ -94,9 +96,7 @@ pub fn generateZirData(self: *Autodoc) !void {...@@ -94,9 +96,7 @@ pub fn generateZirData(self: *Autodoc) !void {
94 .Array = .{96 .Array = .{
95 .len = .{97 .len = .{
96 .int = .{98 .int = .{
97 .typeRef = .{99 .typeRef = &tr,
98 .type = @enumToInt(Ref.usize_type),
99 },
100 .value = 1,100 .value = 1,
101 .negated = false,101 .negated = false,
102 },102 },
...@@ -165,15 +165,15 @@ pub fn generateZirData(self: *Autodoc) !void {...@@ -165,15 +165,15 @@ pub fn generateZirData(self: *Autodoc) !void {
165 try self.files.put(self.arena, file, main_type_index);165 try self.files.put(self.arena, file, main_type_index);
166 _ = try self.walkInstruction(file, &root_scope, Zir.main_struct_inst);166 _ = try self.walkInstruction(file, &root_scope, Zir.main_struct_inst);
167167
168 if (self.decl_paths_pending_on_decls.count() > 0) {168 if (self.ref_paths_pending_on_decls.count() > 0) {
169 @panic("some decl paths were never fully analized (pending on decls)");169 @panic("some decl paths were never fully analized (pending on decls)");
170 }170 }
171171
172 if (self.decl_paths_pending_on_types.count() > 0) {172 if (self.ref_paths_pending_on_types.count() > 0) {
173 @panic("some decl paths were never fully analized (pending on types)");173 @panic("some decl paths were never fully analized (pending on types)");
174 }174 }
175175
176 if (self.pending_decl_paths.count() > 0) {176 if (self.pending_ref_paths.count() > 0) {
177 @panic("some decl paths were never fully analized");177 @panic("some decl paths were never fully analized");
178 }178 }
179179
...@@ -304,7 +304,7 @@ const DocData = struct {...@@ -304,7 +304,7 @@ const DocData = struct {
304 decls: []Decl,304 decls: []Decl,
305 comptimeExprs: []ComptimeExpr,305 comptimeExprs: []ComptimeExpr,
306 const Call = struct {306 const Call = struct {
307 func: TypeRef,307 func: WalkResult,
308 args: []WalkResult,308 args: []WalkResult,
309 ret: WalkResult,309 ret: WalkResult,
310 };310 };
...@@ -337,7 +337,7 @@ const DocData = struct {...@@ -337,7 +337,7 @@ const DocData = struct {
337337
338 const ComptimeExpr = struct {338 const ComptimeExpr = struct {
339 code: []const u8,339 code: []const u8,
340 typeRef: TypeRef,340 typeRef: WalkResult,
341 };341 };
342 const Package = struct {342 const Package = struct {
343 name: []const u8 = "root",343 name: []const u8 = "root",
...@@ -379,18 +379,18 @@ const DocData = struct {...@@ -379,18 +379,18 @@ const DocData = struct {
379 Float: struct { name: []const u8 },379 Float: struct { name: []const u8 },
380 Pointer: struct {380 Pointer: struct {
381 size: std.builtin.TypeInfo.Pointer.Size,381 size: std.builtin.TypeInfo.Pointer.Size,
382 child: TypeRef,382 child: WalkResult,
383 },383 },
384 Array: struct {384 Array: struct {
385 len: WalkResult,385 len: WalkResult,
386 child: TypeRef,386 child: WalkResult,
387 },387 },
388 Struct: struct {388 Struct: struct {
389 name: []const u8,389 name: []const u8,
390 src: ?usize = null, // index into astNodes390 src: usize, // index into astNodes
391 privDecls: []usize = &.{}, // index into decls391 privDecls: []usize = &.{}, // index into decls
392 pubDecls: []usize = &.{}, // index into decls392 pubDecls: []usize = &.{}, // index into decls
393 fields: ?[]TypeRef = null, // (use src->fields to find names)393 fields: ?[]WalkResult = null, // (use src->fields to find names)
394 },394 },
395 ComptimeExpr: struct { name: []const u8 },395 ComptimeExpr: struct { name: []const u8 },
396 ComptimeFloat: struct { name: []const u8 },396 ComptimeFloat: struct { name: []const u8 },
...@@ -399,7 +399,7 @@ const DocData = struct {...@@ -399,7 +399,7 @@ const DocData = struct {
399 Null: struct { name: []const u8 },399 Null: struct { name: []const u8 },
400 Optional: struct {400 Optional: struct {
401 name: []const u8,401 name: []const u8,
402 child: TypeRef,402 child: WalkResult,
403 },403 },
404 ErrorUnion: struct { name: []const u8 },404 ErrorUnion: struct { name: []const u8 },
405 ErrorSet: struct {405 ErrorSet: struct {
...@@ -418,13 +418,13 @@ const DocData = struct {...@@ -418,13 +418,13 @@ const DocData = struct {
418 src: ?usize = null, // index into astNodes418 src: ?usize = null, // index into astNodes
419 privDecls: ?[]usize = null, // index into decls419 privDecls: ?[]usize = null, // index into decls
420 pubDecls: ?[]usize = null, // index into decls420 pubDecls: ?[]usize = null, // index into decls
421 fields: ?[]TypeRef = null, // (use src->fields to find names)421 fields: ?[]WalkResult = null, // (use src->fields to find names)
422 },422 },
423 Fn: struct {423 Fn: struct {
424 name: []const u8,424 name: []const u8,
425 src: ?usize = null, // index into astNodes425 src: ?usize = null, // index into astNodes
426 ret: TypeRef,426 ret: WalkResult,
427 params: ?[]TypeRef = null, // (use src->fields to find names)427 params: ?[]WalkResult = null, // (use src->fields to find names)
428 },428 },
429 BoundFn: struct { name: []const u8 },429 BoundFn: struct { name: []const u8 },
430 Opaque: struct { name: []const u8 },430 Opaque: struct { name: []const u8 },
...@@ -511,66 +511,6 @@ const DocData = struct {...@@ -511,66 +511,6 @@ const DocData = struct {
511 }511 }
512 };512 };
513513
514 /// A DeclPath represents an expression such as `foo.bar.baz` where each
515 /// component has been resolved to a corresponding index in `self.decls`.
516 /// If a DeclPath has a component that can't be fully solved (eg the
517 /// function call in `foo.bar().baz`), then it will be solved up until the
518 /// unresolved component, leaving the remaining part unresolved.
519 ///
520 /// Note that DeclPaths are currently stored in inverse order: the innermost
521 /// component is at index 0.
522 const DeclPath = struct {
523 path: []usize, // indexes in `decls`
524 hasCte: bool = false, // a prefix of this path could not be resolved
525 // TODO: make hasCte return the actual index where the cte is!
526 };
527
528 /// A TypeRef is a subset of WalkResult that refers a type in a direct or
529 /// indirect manner.
530 ///
531 /// An example of directness is `const foo = struct {...};`.
532 /// An example of indidirectness is `const bar = foo;`.
533 const TypeRef = union(enum) {
534 unspecified,
535 @"anytype",
536 declPath: DeclPath,
537 type: usize, // index in `types`
538 comptimeExpr: usize, // index in `comptimeExprs`
539 // TODO: maybe we should not consider calls to be typerefs and instread
540 // directly refer to their return value. The problem at the moment
541 // is that we can't analyze function calls at all.
542 call: usize, // index in `calls`
543 typeOf: *WalkResult,
544
545 pub fn jsonStringify(
546 self: TypeRef,
547 options: std.json.StringifyOptions,
548 w: anytype,
549 ) !void {
550 switch (self) {
551 .typeOf => |v| try std.json.stringify(v, options, w),
552 .unspecified, .@"anytype" => {
553 try w.print(
554 \\{{ "{s}":{{}} }}
555 , .{@tagName(self)});
556 },
557
558 .type, .comptimeExpr, .call => |v| {
559 try w.print(
560 \\{{ "{s}":{} }}
561 , .{ @tagName(self), v });
562 },
563 .declPath => |v| {
564 try w.print("{{ \"hasCte\": {}, \"declPath\": [", .{v.hasCte});
565 for (v.path) |d, i| {
566 const comma = if (i == v.path.len - 1) "]}" else ",";
567 try w.print("{d}{s}", .{ d, comma });
568 }
569 },
570 }
571 }
572 };
573
574 /// A WalkResult represents the result of the analysis process done to a514 /// A WalkResult represents the result of the analysis process done to a
575 /// declaration. This includes: decls, fields, etc.515 /// declaration. This includes: decls, fields, etc.
576 ///516 ///
...@@ -581,20 +521,23 @@ const DocData = struct {...@@ -581,20 +521,23 @@ const DocData = struct {
581 comptimeExpr: usize, // index in `comptimeExprs`521 comptimeExpr: usize, // index in `comptimeExprs`
582 void,522 void,
583 @"unreachable",523 @"unreachable",
584 @"null": TypeRef,524 @"null": *WalkResult,
585 @"undefined": TypeRef,525 @"undefined": *WalkResult,
586 @"struct": Struct,526 @"struct": Struct,
587 bool: bool,527 bool: bool,
588 @"anytype",528 @"anytype",
589 type: usize, // index in `types`529 type: usize, // index in `types`
590 declPath: DeclPath,530 this: usize, // index in `types`
531 declRef: usize, // index in `decls`
532 fieldRef: FieldRef,
533 refPath: []WalkResult,
591 int: struct {534 int: struct {
592 typeRef: TypeRef,535 typeRef: *WalkResult,
593 value: usize, // direct value536 value: usize, // direct value
594 negated: bool = false,537 negated: bool = false,
595 },538 },
596 float: struct {539 float: struct {
597 typeRef: TypeRef,540 typeRef: *WalkResult,
598 value: f64, // direct value541 value: f64, // direct value
599 negated: bool = false,542 negated: bool = false,
600 },543 },
...@@ -606,8 +549,13 @@ const DocData = struct {...@@ -606,8 +549,13 @@ const DocData = struct {
606 compileError: []const u8,549 compileError: []const u8,
607 string: []const u8,550 string: []const u8,
608551
552 const FieldRef = struct {
553 type: usize, // index in `types`
554 index: usize, // index in type.fields
555 };
556
609 const Struct = struct {557 const Struct = struct {
610 typeRef: TypeRef,558 typeRef: *WalkResult,
611 fieldVals: []FieldVal,559 fieldVals: []FieldVal,
612560
613 const FieldVal = struct {561 const FieldVal = struct {
...@@ -616,7 +564,7 @@ const DocData = struct {...@@ -616,7 +564,7 @@ const DocData = struct {
616 };564 };
617 };565 };
618 const Array = struct {566 const Array = struct {
619 typeRef: TypeRef,567 typeRef: *WalkResult,
620 data: []WalkResult,568 data: []WalkResult,
621 };569 };
622570
...@@ -624,14 +572,14 @@ const DocData = struct {...@@ -624,14 +572,14 @@ const DocData = struct {
624 self: WalkResult,572 self: WalkResult,
625 options: std.json.StringifyOptions,573 options: std.json.StringifyOptions,
626 w: anytype,574 w: anytype,
627 ) !void {575 ) std.os.WriteError!void {
628 switch (self) {576 switch (self) {
629 .void, .@"unreachable", .@"anytype" => {577 .void, .@"unreachable", .@"anytype" => {
630 try w.print(578 try w.print(
631 \\{{ "{s}":{{}} }}579 \\{{ "{s}":{{}} }}
632 , .{@tagName(self)});580 , .{@tagName(self)});
633 },581 },
634 .type, .comptimeExpr, .call => |v| {582 .type, .comptimeExpr, .call, .this, .declRef => |v| {
635 try w.print(583 try w.print(
636 \\{{ "{s}":{} }}584 \\{{ "{s}":{} }}
637 , .{ @tagName(self), v });585 , .{ @tagName(self), v });
...@@ -666,16 +614,22 @@ const DocData = struct {...@@ -666,16 +614,22 @@ const DocData = struct {
666 .typeOf, .sizeOf => |v| try std.json.stringify(v, options, w),614 .typeOf, .sizeOf => |v| try std.json.stringify(v, options, w),
667 .compileError => |v| try std.json.stringify(v, options, w),615 .compileError => |v| try std.json.stringify(v, options, w),
668 .string => |v| try std.json.stringify(v, options, w),616 .string => |v| try std.json.stringify(v, options, w),
617 .fieldRef => |v| try std.json.stringify(
618 struct { fieldRef: FieldRef }{ .fieldRef = v },
619 options,
620 w,
621 ),
669 .@"struct" => |v| try std.json.stringify(622 .@"struct" => |v| try std.json.stringify(
670 struct { @"struct": Struct }{ .@"struct" = v },623 struct { @"struct": Struct }{ .@"struct" = v },
671 options,624 options,
672 w,625 w,
673 ),626 ),
674 .declPath => |v| {627 .refPath => |v| {
675 try w.print("{{ \"hasCte\": {}, \"declPath\": [", .{v.hasCte});628 try w.print("{{ \"refPath\": [", .{});
676 for (v.path) |d, i| {629 for (v) |c, i| {
677 const comma = if (i == v.path.len - 1) "]}" else ",";630 const comma = if (i == v.len - 1) "]}" else ",\n";
678 try w.print("{d}{s}", .{ d, comma });631 try c.jsonStringify(options, w);
632 try w.print("{s}", .{comma});
679 }633 }
680 },634 },
681 .array => |v| try std.json.stringify(635 .array => |v| try std.json.stringify(
...@@ -758,6 +712,7 @@ fn walkInstruction(...@@ -758,6 +712,7 @@ fn walkInstruction(
758 .comptimeExpr = cte_slot_index,712 .comptimeExpr = cte_slot_index,
759 };713 };
760 }714 }
715
761 const new_file = self.module.importFile(file, path) catch unreachable;716 const new_file = self.module.importFile(file, path) catch unreachable;
762 const result = try self.files.getOrPut(self.arena, new_file.file);717 const result = try self.files.getOrPut(self.arena, new_file.file);
763 if (result.found_existing) {718 if (result.found_existing) {
...@@ -794,37 +749,24 @@ fn walkInstruction(...@@ -794,37 +749,24 @@ fn walkInstruction(
794749
795 return DocData.WalkResult{ .compileError = operand.string };750 return DocData.WalkResult{ .compileError = operand.string };
796 },751 },
797 .switch_block => {752 .switch_block => return self.cteTodo("[switch]"),
798 const cte_slot_index = self.comptime_exprs.items.len;
799 try self.comptime_exprs.append(self.arena, .{
800 .code = "switch",
801 .typeRef = .{
802 .type = @enumToInt(DocData.DocTypeKinds.ComptimeExpr),
803 },
804 });
805
806 return DocData.WalkResult{ .comptimeExpr = cte_slot_index };
807 },
808 .enum_literal => {753 .enum_literal => {
809 const str_tok = data[inst_index].str_tok;754 const str_tok = data[inst_index].str_tok;
810 const literal = file.zir.nullTerminatedString(str_tok.start);755 const literal = file.zir.nullTerminatedString(str_tok.start);
811 return DocData.WalkResult{ .enumLiteral = literal };756 return DocData.WalkResult{ .enumLiteral = literal };
812 },757 },
813 .div_exact, .div => {758 .div_exact, .div => return self.cteTodo("@div(...)"),
814 const cte_slot_index = self.comptime_exprs.items.len;759 .mul => return self.cteTodo("@mul(...)"),
815 try self.comptime_exprs.append(self.arena, .{760 .array_mul => return self.cteTodo("a ** b"),
816 .code = "@div*(...)",761 .bool_br_and, .bool_br_or => return self.cteTodo("bool op"),
817 .typeRef = .{ .type = @enumToInt(DocData.DocTypeKinds.ComptimeExpr) },762 .cmp_eq => return self.cteTodo("bool op"),
818 });
819 return DocData.WalkResult{ .comptimeExpr = cte_slot_index };
820 },
821 .int => {763 .int => {
822 const int = data[inst_index].int;764 const int = data[inst_index].int;
765 const t = try self.arena.create(DocData.WalkResult);
766 t.* = .{ .type = @enumToInt(Ref.comptime_int_type) };
823 return DocData.WalkResult{767 return DocData.WalkResult{
824 .int = .{768 .int = .{
825 .typeRef = .{769 .typeRef = t,
826 .type = @enumToInt(Ref.comptime_int_type),
827 },
828 .value = int,770 .value = int,
829 },771 },
830 };772 };
...@@ -843,7 +785,7 @@ fn walkInstruction(...@@ -843,7 +785,7 @@ fn walkInstruction(
843 try self.types.append(self.arena, .{785 try self.types.append(self.arena, .{
844 .Pointer = .{786 .Pointer = .{
845 .size = ptr.size,787 .size = ptr.size,
846 .child = walkResultToTypeRef(elem_type_ref),788 .child = elem_type_ref,
847 },789 },
848 });790 });
849791
...@@ -862,7 +804,7 @@ fn walkInstruction(...@@ -862,7 +804,7 @@ fn walkInstruction(
862 try self.types.append(self.arena, .{804 try self.types.append(self.arena, .{
863 .Pointer = .{805 .Pointer = .{
864 .size = ptr.size,806 .size = ptr.size,
865 .child = walkResultToTypeRef(elem_type_ref),807 .child = elem_type_ref,
866 },808 },
867 });809 });
868810
...@@ -871,7 +813,7 @@ fn walkInstruction(...@@ -871,7 +813,7 @@ fn walkInstruction(
871 .array_type => {813 .array_type => {
872 const bin = data[inst_index].bin;814 const bin = data[inst_index].bin;
873 const len = try self.walkRef(file, parent_scope, bin.lhs);815 const len = try self.walkRef(file, parent_scope, bin.lhs);
874 const child = walkResultToTypeRef(try self.walkRef(file, parent_scope, bin.rhs));816 const child = try self.walkRef(file, parent_scope, bin.rhs);
875817
876 const type_slot_index = self.types.items.len;818 const type_slot_index = self.types.items.len;
877 try self.types.append(self.arena, .{819 try self.types.append(self.arena, .{
...@@ -891,12 +833,15 @@ fn walkInstruction(...@@ -891,12 +833,15 @@ fn walkInstruction(
891 array_data[idx] = try self.walkRef(file, parent_scope, op);833 array_data[idx] = try self.walkRef(file, parent_scope, op);
892 }834 }
893835
836 const at = try self.arena.create(DocData.WalkResult);
837 at.* = .{ .type = @enumToInt(Ref.usize_type) };
838
894 const type_slot_index = self.types.items.len;839 const type_slot_index = self.types.items.len;
895 try self.types.append(self.arena, .{840 try self.types.append(self.arena, .{
896 .Array = .{841 .Array = .{
897 .len = .{842 .len = .{
898 .int = .{843 .int = .{
899 .typeRef = .{ .type = @enumToInt(Ref.usize_type) },844 .typeRef = at,
900 .value = operands.len,845 .value = operands.len,
901 .negated = false,846 .negated = false,
902 },847 },
...@@ -905,18 +850,22 @@ fn walkInstruction(...@@ -905,18 +850,22 @@ fn walkInstruction(
905 },850 },
906 });851 });
907852
853 const t = try self.arena.create(DocData.WalkResult);
854 t.* = .{ .type = type_slot_index };
908 return DocData.WalkResult{ .array = .{855 return DocData.WalkResult{ .array = .{
909 .typeRef = .{ .type = type_slot_index },856 .typeRef = t,
910 .data = array_data,857 .data = array_data,
911 } };858 } };
912 },859 },
913 .float => {860 .float => {
914 const float = data[inst_index].float;861 const float = data[inst_index].float;
862
863 const t = try self.arena.create(DocData.WalkResult);
864 t.* = .{ .type = @enumToInt(Ref.comptime_float_type) };
865
915 return DocData.WalkResult{866 return DocData.WalkResult{
916 .float = .{867 .float = .{
917 .typeRef = .{868 .typeRef = t,
918 .type = @enumToInt(Ref.comptime_float_type),
919 },
920 .value = float,869 .value = float,
921 },870 },
922 };871 };
...@@ -956,7 +905,7 @@ fn walkInstruction(...@@ -956,7 +905,7 @@ fn walkInstruction(
956 const pl_node = data[inst_index].pl_node;905 const pl_node = data[inst_index].pl_node;
957 const extra = file.zir.extraData(Zir.Inst.As, pl_node.payload_index);906 const extra = file.zir.extraData(Zir.Inst.As, pl_node.payload_index);
958 const dest_type_walk = try self.walkRef(file, parent_scope, extra.data.dest_type);907 const dest_type_walk = try self.walkRef(file, parent_scope, extra.data.dest_type);
959 const dest_type_ref = walkResultToTypeRef(dest_type_walk);908 const dest_type_ref = dest_type_walk;
960909
961 var operand = try self.walkRef(file, parent_scope, extra.data.operand);910 var operand = try self.walkRef(file, parent_scope, extra.data.operand);
962911
...@@ -967,7 +916,7 @@ fn walkInstruction(...@@ -967,7 +916,7 @@ fn walkInstruction(
967 "TODO: handle {s} in `walkInstruction.as_node`\n",916 "TODO: handle {s} in `walkInstruction.as_node`\n",
968 .{@tagName(operand)},917 .{@tagName(operand)},
969 ),918 ),
970 .declPath, .type, .string => {},919 .refPath, .type, .string, .call, .enumLiteral => {},
971 // we don't do anything because up until now,920 // we don't do anything because up until now,
972 // I've only seen this used as such:921 // I've only seen this used as such:
973 // @as(@as(type, Baz), .{})922 // @as(@as(type, Baz), .{})
...@@ -979,9 +928,9 @@ fn walkInstruction(...@@ -979,9 +928,9 @@ fn walkInstruction(
979 .comptimeExpr => {928 .comptimeExpr => {
980 self.comptime_exprs.items[operand.comptimeExpr].typeRef = dest_type_ref;929 self.comptime_exprs.items[operand.comptimeExpr].typeRef = dest_type_ref;
981 },930 },
982 .int => operand.int.typeRef = dest_type_ref,931 .int => operand.int.typeRef.* = dest_type_ref,
983 .@"struct" => operand.@"struct".typeRef = dest_type_ref,932 .@"struct" => operand.@"struct".typeRef.* = dest_type_ref,
984 .@"undefined" => operand.@"undefined" = dest_type_ref,933 .@"undefined" => operand.@"undefined".* = dest_type_ref,
985 }934 }
986935
987 return operand;936 return operand;
...@@ -993,7 +942,7 @@ fn walkInstruction(...@@ -993,7 +942,7 @@ fn walkInstruction(
993 parent_scope,942 parent_scope,
994 un_node.operand,943 un_node.operand,
995 );944 );
996 const type_ref = walkResultToTypeRef(operand);945 const type_ref = operand;
997 const res = DocData.WalkResult{ .type = self.types.items.len };946 const res = DocData.WalkResult{ .type = self.types.items.len };
998 try self.types.append(self.arena, .{947 try self.types.append(self.arena, .{
999 .Optional = .{ .name = "?TODO", .child = type_ref },948 .Optional = .{ .name = "?TODO", .child = type_ref },
...@@ -1003,9 +952,7 @@ fn walkInstruction(...@@ -1003,9 +952,7 @@ fn walkInstruction(
1003 .decl_val, .decl_ref => {952 .decl_val, .decl_ref => {
1004 const str_tok = data[inst_index].str_tok;953 const str_tok = data[inst_index].str_tok;
1005 const decls_slot_index = parent_scope.resolveDeclName(str_tok.start);954 const decls_slot_index = parent_scope.resolveDeclName(str_tok.start);
1006 var path = try self.arena.alloc(usize, 1);955 return DocData.WalkResult{ .declRef = decls_slot_index };
1007 path[0] = decls_slot_index;
1008 return DocData.WalkResult{ .declPath = .{ .path = path } };
1009 },956 },
1010 .field_val, .field_call_bind, .field_ptr, .field_type => {957 .field_val, .field_call_bind, .field_ptr, .field_type => {
1011 // TODO: field type uses Zir.Inst.FieldType, it just happens to have the958 // TODO: field type uses Zir.Inst.FieldType, it just happens to have the
...@@ -1013,101 +960,51 @@ fn walkInstruction(...@@ -1013,101 +960,51 @@ fn walkInstruction(
1013 const pl_node = data[inst_index].pl_node;960 const pl_node = data[inst_index].pl_node;
1014 const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index);961 const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index);
1015962
1016 var path: std.ArrayListUnmanaged(usize) = .{};963 var path: std.ArrayListUnmanaged(DocData.WalkResult) = .{};
1017 var lhs = @enumToInt(extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs964 var lhs = @enumToInt(extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs
1018965
1019 try path.append(self.arena, extra.data.field_name_start);966 try path.append(self.arena, .{
967 .string = file.zir.nullTerminatedString(extra.data.field_name_start),
968 });
1020 // Put inside path the starting index of each decl name that969 // Put inside path the starting index of each decl name that
1021 // we encounter as we navigate through all the field_vals970 // we encounter as we navigate through all the field_vals
1022 while (tags[lhs] == .field_val or971 while (tags[lhs] == .field_val or
1023 tags[lhs] == .field_call_bind or972 tags[lhs] == .field_call_bind or
1024 tags[lhs] == .field_ptr)973 tags[lhs] == .field_ptr or
974 tags[lhs] == .field_type)
1025 {975 {
1026 const lhs_extra = file.zir.extraData(976 const lhs_extra = file.zir.extraData(
1027 Zir.Inst.Field,977 Zir.Inst.Field,
1028 data[lhs].pl_node.payload_index,978 data[lhs].pl_node.payload_index,
1029 );979 );
1030980
1031 try path.append(self.arena, lhs_extra.data.field_name_start);981 try path.append(self.arena, .{
982 .string = file.zir.nullTerminatedString(lhs_extra.data.field_name_start),
983 });
1032 lhs = @enumToInt(lhs_extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs984 lhs = @enumToInt(lhs_extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs
1033 }985 }
1034986
1035 switch (tags[lhs]) {987 const wr = try self.walkInstruction(file, parent_scope, lhs);
1036 else => panicWithContext(988 try path.append(self.arena, wr);
1037 file,989
1038 inst_index,990 // This way the data in `path` has the same ordering that the ref
1039 "TODO: handle `{s}` in walkInstruction.field_val",991 // path has in the text: most general component first.
1040 .{@tagName(tags[lhs])},992 std.mem.reverse(DocData.WalkResult, path.items);
1041 ),993
1042 .call => {994 // Righ now, every element of `path` is a string except its first
1043 const walk_result = try self.walkInstruction(file, parent_scope, lhs);995 // element (at index 0). We're now going to attempt to resolve each
1044 const ast_node_index = idx: {996 // string. If one or more components in this path are not yet fully
1045 const idx = self.ast_nodes.items.len;997 // analyzed, the path will only be solved partially, but we expect
1046 try self.ast_nodes.append(self.arena, .{998 // to eventually solve it fully(or give up in case of a
1047 .file = 0,999 // comptimeExpr). This means that:
1048 .line = 0,1000 // - (1) Paths can be not fully analyzed temporarily, so any code
1049 .col = 0,1001 // that requires to know where a ref path leads to, neeeds to
1050 .docs = "",1002 // implement support for lazyness (see self.pending_ref_paths)
1051 .fields = null,1003 // - (2) Paths can sometimes never resolve fully. This means that
1052 });1004 // any value that depends on that will have to become a
1053 break :idx idx;1005 // comptimeExpr.
1054 };1006 try self.tryResolveRefPath(file, lhs, path.items);
10551007 return DocData.WalkResult{ .refPath = path.items };
1056 const decls_slot_index = self.decls.items.len;
1057 try self.decls.append(self.arena, .{
1058 ._analyzed = true,
1059 .name = "call()",
1060 .src = ast_node_index,
1061 .value = walk_result,
1062 .kind = "const",
1063 });
1064 try path.append(self.arena, decls_slot_index);
1065 },
1066 .import => {
1067 const walk_result = try self.walkInstruction(file, parent_scope, lhs);
1068
1069 // astnode
1070 const ast_node_index = idx: {
1071 const idx = self.ast_nodes.items.len;
1072 try self.ast_nodes.append(self.arena, .{
1073 .file = 0,
1074 .line = 0,
1075 .col = 0,
1076 .docs = "",
1077 .fields = null,
1078 });
1079 break :idx idx;
1080 };
1081 const str_tok = data[lhs].str_tok;
1082 const file_path = str_tok.get(file.zir);
1083
1084 const name = try std.fmt.allocPrint(self.arena, "@import({s})", .{file_path});
1085 const decls_slot_index = self.decls.items.len;
1086 try self.decls.append(self.arena, .{
1087 ._analyzed = true,
1088 .name = name,
1089 .src = ast_node_index,
1090 // .typeRef = decl_type_ref,
1091 .value = walk_result,
1092 .kind = "const", // find where this information can be found
1093 });
1094 try path.append(self.arena, decls_slot_index);
1095 },
1096 .decl_val, .decl_ref => {
1097 const str_tok = data[lhs].str_tok;
1098 const decls_slot_index = parent_scope.resolveDeclName(str_tok.start);
1099 try path.append(self.arena, decls_slot_index);
1100 },
1101 }
1102
1103 // Righ now, every element of `path` is the first index of a
1104 // decl name except for the final element, which instead points to
1105 // the analyzed data corresponding to the top-most decl of this path.
1106 // We are now going to reverse loop over `path` to resolve each name
1107 // to its corresponding index in `decls`.
1108 var decl_path: DocData.DeclPath = .{ .path = path.items };
1109 try self.tryResolveDeclPath(file, &decl_path);
1110 return DocData.WalkResult{ .declPath = decl_path };
1111 },1008 },
1112 .int_type => {1009 .int_type => {
1113 const int_type = data[inst_index].int_type;1010 const int_type = data[inst_index].int_type;
...@@ -1139,7 +1036,7 @@ fn walkInstruction(...@@ -1139,7 +1036,7 @@ fn walkInstruction(
1139 extra.data.fields_len,1036 extra.data.fields_len,
1140 );1037 );
11411038
1142 var type_ref: DocData.TypeRef = undefined;1039 const type_ref = try self.arena.create(DocData.WalkResult);
1143 var idx = extra.end;1040 var idx = extra.end;
1144 for (field_vals) |*fv| {1041 for (field_vals) |*fv| {
1145 const init_extra = file.zir.extraData(Zir.Inst.StructInit.Item, idx);1042 const init_extra = file.zir.extraData(Zir.Inst.StructInit.Item, idx);
...@@ -1161,11 +1058,10 @@ fn walkInstruction(...@@ -1161,11 +1058,10 @@ fn walkInstruction(
1161 parent_scope,1058 parent_scope,
1162 field_extra.data.container_type,1059 field_extra.data.container_type,
1163 );1060 );
1164 type_ref = walkResultToTypeRef(wr);1061 type_ref.* = wr;
1165 }1062 }
1166 break :blk file.zir.nullTerminatedString(field_extra.data.name_start);1063 break :blk file.zir.nullTerminatedString(field_extra.data.name_start);
1167 };1064 };
1168
1169 const value = try self.walkRef(file, parent_scope, init_extra.data.init);1065 const value = try self.walkRef(file, parent_scope, init_extra.data.init);
1170 fv.* = .{ .name = field_name, .val = value };1066 fv.* = .{ .name = field_name, .val = value };
1171 }1067 }
...@@ -1236,9 +1132,7 @@ fn walkInstruction(...@@ -1236,9 +1132,7 @@ fn walkInstruction(
1236 const pl_node = data[inst_index].pl_node;1132 const pl_node = data[inst_index].pl_node;
1237 const extra = file.zir.extraData(Zir.Inst.Call, pl_node.payload_index);1133 const extra = file.zir.extraData(Zir.Inst.Call, pl_node.payload_index);
12381134
1239 const callee = walkResultToTypeRef(1135 const callee = try self.walkRef(file, parent_scope, extra.data.callee);
1240 try self.walkRef(file, parent_scope, extra.data.callee),
1241 );
12421136
1243 const args_len = extra.data.flags.args_len;1137 const args_len = extra.data.flags.args_len;
1244 var args = try self.arena.alloc(DocData.WalkResult, args_len);1138 var args = try self.arena.alloc(DocData.WalkResult, args_len);
...@@ -1267,16 +1161,20 @@ fn walkInstruction(...@@ -1267,16 +1161,20 @@ fn walkInstruction(
1267 return DocData.WalkResult{ .call = call_slot_index };1161 return DocData.WalkResult{ .call = call_slot_index };
1268 },1162 },
1269 .func, .func_inferred => {1163 .func, .func_inferred => {
1164 const type_slot_index = self.types.items.len;
1165 try self.types.append(self.arena, .{ .Unanalyzed = {} });
1166
1270 return self.analyzeFunction(1167 return self.analyzeFunction(
1271 file,1168 file,
1272 parent_scope,1169 parent_scope,
1273 inst_index,1170 inst_index,
1274 self_ast_node_index,1171 self_ast_node_index,
1172 type_slot_index,
1275 );1173 );
1276 },1174 },
1277 .extended => {1175 .extended => {
1278 // NOTE: this code + the subsequent defer block are working towards1176 // NOTE: this code + the subsequent defer block are working towards
1279 // solving pending decl paths that depend on a type to be analyzed.1177 // solving pending decl paths that depend on completing the analysis of a type.
1280 // When we don't find a type, the defer will run anyway but shouldn't1178 // When we don't find a type, the defer will run anyway but shouldn't
1281 // ever be able to find a match inside `decl_paths_pending_on_types`1179 // ever be able to find a match inside `decl_paths_pending_on_types`
1282 // TODO: extract this logic into a function and only call it when appropriate.1180 // TODO: extract this logic into a function and only call it when appropriate.
...@@ -1284,14 +1182,18 @@ fn walkInstruction(...@@ -1284,14 +1182,18 @@ fn walkInstruction(
1284 try self.types.append(self.arena, .{ .Unanalyzed = {} });1182 try self.types.append(self.arena, .{ .Unanalyzed = {} });
12851183
1286 defer {1184 defer {
1287 if (self.decl_paths_pending_on_types.get(type_slot_index)) |paths| {1185 if (self.ref_paths_pending_on_types.get(type_slot_index)) |paths| {
1288 for (paths.items) |*resume_info| {1186 for (paths.items) |resume_info| {
1289 self.tryResolveDeclPath(resume_info.file, &resume_info.decl_path) catch {1187 self.tryResolveRefPath(
1188 resume_info.file,
1189 inst_index,
1190 resume_info.ref_path,
1191 ) catch {
1290 @panic("Out of memory");1192 @panic("Out of memory");
1291 };1193 };
1292 }1194 }
12931195
1294 _ = self.decl_paths_pending_on_types.remove(type_slot_index);1196 _ = self.ref_paths_pending_on_types.remove(type_slot_index);
1295 // TODO: we should deallocate the arraylist that holds all the1197 // TODO: we should deallocate the arraylist that holds all the
1296 // decl paths. not doing it now since it's arena-allocated1198 // decl paths. not doing it now since it's arena-allocated
1297 // anyway, but maybe we should put it elsewhere.1199 // anyway, but maybe we should put it elsewhere.
...@@ -1308,12 +1210,15 @@ fn walkInstruction(...@@ -1308,12 +1210,15 @@ fn walkInstruction(
1308 .{@tagName(extended.opcode)},1210 .{@tagName(extended.opcode)},
1309 );1211 );
1310 },1212 },
1213
1214 .opaque_decl => return self.cteTodo("opaque {...}"),
1311 .func => {1215 .func => {
1312 return try self.analyzeFunction(1216 return try self.analyzeFunction(
1313 file,1217 file,
1314 parent_scope,1218 parent_scope,
1315 inst_index,1219 inst_index,
1316 self_ast_node_index,1220 self_ast_node_index,
1221 type_slot_index,
1317 );1222 );
1318 },1223 },
1319 .variable => {1224 .variable => {
...@@ -1403,7 +1308,7 @@ fn walkInstruction(...@@ -1403,7 +1308,7 @@ fn walkInstruction(
1403 // const body = file.zir.extra[extra_index..][0..body_len];1308 // const body = file.zir.extra[extra_index..][0..body_len];
1404 extra_index += body_len;1309 extra_index += body_len;
14051310
1406 var field_type_refs = try std.ArrayListUnmanaged(DocData.TypeRef).initCapacity(1311 var field_type_refs = try std.ArrayListUnmanaged(DocData.WalkResult).initCapacity(
1407 self.arena,1312 self.arena,
1408 fields_len,1313 fields_len,
1409 );1314 );
...@@ -1633,7 +1538,7 @@ fn walkInstruction(...@@ -1633,7 +1538,7 @@ fn walkInstruction(
1633 // const body = file.zir.extra[extra_index..][0..body_len];1538 // const body = file.zir.extra[extra_index..][0..body_len];
1634 extra_index += body_len;1539 extra_index += body_len;
16351540
1636 var field_type_refs: std.ArrayListUnmanaged(DocData.TypeRef) = .{};1541 var field_type_refs: std.ArrayListUnmanaged(DocData.WalkResult) = .{};
1637 var field_name_indexes: std.ArrayListUnmanaged(usize) = .{};1542 var field_name_indexes: std.ArrayListUnmanaged(usize) = .{};
1638 try self.collectStructFieldInfo(1543 try self.collectStructFieldInfo(
1639 file,1544 file,
...@@ -1659,22 +1564,7 @@ fn walkInstruction(...@@ -1659,22 +1564,7 @@ fn walkInstruction(
1659 return DocData.WalkResult{ .type = type_slot_index };1564 return DocData.WalkResult{ .type = type_slot_index };
1660 },1565 },
1661 .this => {1566 .this => {
1662 // TODO: consider if we should reuse an existing decl1567 return DocData.WalkResult{ .this = parent_scope.enclosing_type };
1663 // that points to this type (if present).
1664 const decl_slot_index = self.decls.items.len;
1665 try self.decls.append(self.arena, .{
1666 .name = "@This()",
1667 .value = .{ .type = parent_scope.enclosing_type },
1668 .src = 0,
1669 .kind = "const",
1670 ._analyzed = false,
1671 });
1672 const dpath = try self.arena.alloc(usize, 1);
1673 dpath[0] = decl_slot_index;
1674 return DocData.WalkResult{ .declPath = .{
1675 .hasCte = false,
1676 .path = dpath,
1677 } };
1678 },1568 },
1679 }1569 }
1680 },1570 },
...@@ -1900,14 +1790,18 @@ fn walkDecls(...@@ -1900,14 +1790,18 @@ fn walkDecls(
1900 };1790 };
19011791
1902 // Unblock any pending decl path that was waiting for this decl.1792 // Unblock any pending decl path that was waiting for this decl.
1903 if (self.decl_paths_pending_on_decls.get(decls_slot_index)) |paths| {1793 if (self.ref_paths_pending_on_decls.get(decls_slot_index)) |paths| {
1904 for (paths.items) |*resume_info| {1794 for (paths.items) |resume_info| {
1905 try self.tryResolveDeclPath(resume_info.file, &resume_info.decl_path);1795 try self.tryResolveRefPath(
1796 resume_info.file,
1797 decl_index,
1798 resume_info.ref_path,
1799 );
1906 }1800 }
19071801
1908 _ = self.decl_paths_pending_on_decls.remove(decls_slot_index);1802 _ = self.ref_paths_pending_on_decls.remove(decls_slot_index);
1909 // TODO: we should deallocate the arraylist that holds all the1803 // TODO: we should deallocate the arraylist that holds all the
1910 // decl paths. not doing it now since it's arena-allocated1804 // ref paths. not doing it now since it's arena-allocated
1911 // anyway, but maybe we should put it elsewhere.1805 // anyway, but maybe we should put it elsewhere.
1912 }1806 }
1913 }1807 }
...@@ -1915,109 +1809,139 @@ fn walkDecls(...@@ -1915,109 +1809,139 @@ fn walkDecls(
1915 return extra_index;1809 return extra_index;
1916}1810}
19171811
1918/// An unresolved path has a decl index at its end, while every other element1812/// An unresolved path has a non-string WalkResult at its beginnig, while every
1919/// is an index into the string table. Resolving means iteratively map each1813/// other element is a string WalkResult. Resolving means iteratively map each
1920/// string to a decl_index.1814/// string to a Decl / Type / Call / etc.
1921///1815///
1922/// If we encounter an unanalyzed decl during the process, we append the1816/// If we encounter an unanalyzed decl during the process, we append the
1923/// unsolved sub-path to `self.decl_paths_pending_on_decls` and bail out.1817/// unsolved sub-path to `self.ref_paths_pending_on_decls` and bail out.
1924/// Same happens when a decl holds a type definition that hasn't been fully1818/// Same happens when a decl holds a type definition that hasn't been fully
1925/// analyzed yet (except that we append to `self.decl_paths_pending_on_types`.1819/// analyzed yet (except that we append to `self.ref_paths_pending_on_types`.
1926///1820///
1927/// When a decl or a type is fully analyzed if will then check if there's any1821/// When walkDecls / walkInstruction finishes analyzing a decl / type, it will
1928/// pending decl path blocked on it and, if any, will progress their resolution1822/// then check if there's any pending ref path blocked on it and, if any, it
1929/// by calling tryResolveDeclPath again.1823/// will progress their resolution by calling tryResolveRefPath again.
1930///1824///
1931/// Decl paths can also depend on other decl paths. See1825/// Ref paths can also depend on other ref paths. See
1932/// `self.pending_decl_paths` for more info.1826/// `self.pending_ref_paths` for more info.
1933///1827///
1934/// A decl path that has a component that resolves into a comptimeExpr will1828/// A ref path that has a component that resolves into a comptimeExpr will
1935/// give up its resolution process entirely.1829/// give up its resolution process entirely, leaving the remaining components
1936///1830/// as strings.
1937/// TODO: when giving up, translate remaining string indexes into data that1831fn tryResolveRefPath(
1938/// can be used by the frontend. Requires implementing a frontend string
1939/// table.
1940fn tryResolveDeclPath(
1941 self: *Autodoc,1832 self: *Autodoc,
1942 /// File from which the decl path originates.1833 /// File from which the decl path originates.
1943 file: *File,1834 file: *File,
1944 decl_path: *DocData.DeclPath,1835 inst_index: usize, // used only for panicWithContext
1836 path: []DocData.WalkResult,
1945) error{OutOfMemory}!void {1837) error{OutOfMemory}!void {
1946 const path: []usize = decl_path.path;1838 var i: usize = 0;
1839 outer: while (i < path.len - 1) : (i += 1) {
1840 const parent = path[i];
1841 const child_string = path[i + 1].string; // we expect to find a string union case
1842
1843 var resolved_parent = parent;
1844 var j: usize = 0;
1845 while (j < 10_000) : (j += 1) {
1846 switch (resolved_parent) {
1847 else => break,
1848 .declRef => |decl_index| {
1849 const decl = self.decls.items[decl_index];
1850 if (decl._analyzed) {
1851 resolved_parent = decl.value;
1852 continue;
1853 }
19471854
1948 var i: usize = path.len;1855 // This decl path is pending completion
1949 outer: while (i > 1) {1856 {
1950 i -= 1;1857 const res = try self.pending_ref_paths.getOrPut(
1951 const decl_index = path[i];1858 self.arena,
1952 const string_index = path[i - 1];1859 &path[path.len - 1],
1860 );
1861 if (!res.found_existing) res.value_ptr.* = .{};
1862 }
19531863
1954 const parent = self.decls.items[decl_index];1864 const res = try self.ref_paths_pending_on_decls.getOrPut(
1955 if (!parent._analyzed) {1865 self.arena,
1956 // This decl path is pending completion1866 decl_index,
1957 {1867 );
1958 const res = try self.pending_decl_paths.getOrPut(self.arena, &path[0]);1868 if (!res.found_existing) res.value_ptr.* = .{};
1959 if (!res.found_existing) res.value_ptr.* = .{};1869 try res.value_ptr.*.append(self.arena, .{
1960 }1870 .file = file,
1871 .ref_path = path[i..path.len],
1872 });
19611873
1962 const res = try self.decl_paths_pending_on_decls.getOrPut(self.arena, decl_index);1874 // We return instead doing `break :outer` to prevent the
1963 if (!res.found_existing) res.value_ptr.* = .{};1875 // code after the :outer while loop to run, as it assumes
1964 try res.value_ptr.*.append(self.arena, .{1876 // that the path will have been fully analyzed (or we
1965 .file = file,1877 // have given up because of a comptimeExpr).
1966 .decl_path = .{ .path = path[0 .. i + 1] },1878 return;
1967 });1879 },
1880 .refPath => |rp| {
1881 if (self.pending_ref_paths.getPtr(&rp[rp.len - 1])) |waiter_list| {
1882 try waiter_list.append(self.arena, .{
1883 .file = file,
1884 .ref_path = path[i..path.len],
1885 });
19681886
1969 return;1887 // This decl path is pending completion
1888 {
1889 const res = try self.pending_ref_paths.getOrPut(
1890 self.arena,
1891 &path[path.len - 1],
1892 );
1893 if (!res.found_existing) res.value_ptr.* = .{};
1894 }
1895
1896 return;
1897 }
1898
1899 // If the last element is a string or a CTE, then we give up,
1900 // otherwise we resovle the parent to it and loop again.
1901 // NOTE: we assume that if we find a string, it's because of
1902 // a CTE component somewhere in the path. We know that the path
1903 // is not pending futher evaluation because we just checked!
1904 const last = rp[rp.len - 1];
1905 switch (last) {
1906 .comptimeExpr, .string => break :outer,
1907 else => {
1908 resolved_parent = last;
1909 continue;
1910 },
1911 }
1912 },
1913 }
1914 } else {
1915 panicWithContext(
1916 file,
1917 inst_index,
1918 "exhausted eval quota for `{}`in tryResolveDecl\n",
1919 .{resolved_parent},
1920 );
1970 }1921 }
19711922
1972 const child_decl_name = file.zir.nullTerminatedString(string_index);1923 switch (resolved_parent) {
1973 switch (parent.value) {
1974 else => {1924 else => {
1975 std.debug.panic(1925 // NOTE: indirect references to types / decls should be handled
1976 "TODO: handle `{s}`in tryResolveDecl\n \"{s}\":{}",1926 // in the switch above this one!
1977 .{ @tagName(parent.value), parent.name, parent.value },1927 panicWithContext(
1928 file,
1929 inst_index,
1930 "TODO: handle `{s}`in tryResolveRefPath\nInfo: {}",
1931 .{ @tagName(resolved_parent), resolved_parent },
1978 );1932 );
1979 },1933 },
1980 .comptimeExpr, .call => {1934 .comptimeExpr, .call => {
1981 // Since we hit a cte, we leave the remaining strings unresolved1935 // Since we hit a cte, we leave the remaining strings unresolved
1982 // and completely give up on resolving this decl path.1936 // and completely give up on resolving this decl path.
1983 decl_path.hasCte = true;1937 //decl_path.hasCte = true;
1984 break :outer;1938 break :outer;
1985 },1939 },
1986 .declPath => |dp| {
1987 if (dp.hasCte) {
1988 decl_path.hasCte = true;
1989 break :outer;
1990 }
1991 if (self.pending_decl_paths.getPtr(&dp.path[0])) |waiter_list| {
1992 try waiter_list.append(self.arena, .{
1993 .file = file,
1994 .decl_path = .{ .path = path[0 .. i + 1] },
1995 });
1996
1997 // This decl path is pending completion
1998 {
1999 const res = try self.pending_decl_paths.getOrPut(self.arena, &path[0]);
2000 if (!res.found_existing) res.value_ptr.* = .{};
2001 }
2002
2003 return;
2004 }
2005
2006 const final_decl_index = dp.path[0];
2007 // For the purpose of being able to call tryResolveDeclPath again,
2008 // we momentarily replace the decl index present in `path[i]`
2009 // with the final decl in `dp`.
2010 // We then write the original value back as soon as we're done with the
2011 // recoursive call. This will work out correctly even if the path
2012 // will not get fully resolved (also in the case that final_decl is
2013 // not resolved yet).
2014 path[i] = final_decl_index;
2015 try self.tryResolveDeclPath(file, decl_path);
2016 path[i] = decl_index;
2017 },
2018 .type => |t_index| switch (self.types.items[t_index]) {1940 .type => |t_index| switch (self.types.items[t_index]) {
2019 else => {1941 else => {
2020 std.debug.panic(1942 panicWithContext(
1943 file,
1944 inst_index,
2021 "TODO: handle `{s}` in tryResolveDeclPath.type\n",1945 "TODO: handle `{s}` in tryResolveDeclPath.type\n",
2022 .{@tagName(self.types.items[t_index])},1946 .{@tagName(self.types.items[t_index])},
2023 );1947 );
...@@ -2025,53 +1949,87 @@ fn tryResolveDeclPath(...@@ -2025,53 +1949,87 @@ fn tryResolveDeclPath(
2025 .Unanalyzed => {1949 .Unanalyzed => {
2026 // This decl path is pending completion1950 // This decl path is pending completion
2027 {1951 {
2028 const res = try self.pending_decl_paths.getOrPut(self.arena, &path[0]);1952 const res = try self.pending_ref_paths.getOrPut(
1953 self.arena,
1954 &path[path.len - 1],
1955 );
2029 if (!res.found_existing) res.value_ptr.* = .{};1956 if (!res.found_existing) res.value_ptr.* = .{};
2030 }1957 }
20311958
2032 const res = try self.decl_paths_pending_on_types.getOrPut(1959 const res = try self.ref_paths_pending_on_types.getOrPut(
2033 self.arena,1960 self.arena,
2034 t_index,1961 t_index,
2035 );1962 );
2036 if (!res.found_existing) res.value_ptr.* = .{};1963 if (!res.found_existing) res.value_ptr.* = .{};
2037 try res.value_ptr.*.append(self.arena, .{1964 try res.value_ptr.*.append(self.arena, .{
2038 .file = file,1965 .file = file,
2039 .decl_path = .{ .path = path[0 .. i + 1] },1966 .ref_path = path[i..path.len],
2040 });1967 });
20411968
2042 return;1969 return;
2043 },1970 },
2044 .Struct => |t_struct| {1971 .Struct => |t_struct| {
1972 std.debug.print("search: {s}\n", .{child_string});
2045 for (t_struct.pubDecls) |d| {1973 for (t_struct.pubDecls) |d| {
2046 // TODO: this could be improved a lot1974 // TODO: this could be improved a lot
2047 // by having our own string table!1975 // by having our own string table!
2048 const decl = self.decls.items[d];1976 const decl = self.decls.items[d];
2049 if (std.mem.eql(u8, decl.name, child_decl_name)) {1977 std.debug.print("pub decl `{s}`\n", .{decl.name});
2050 path[i - 1] = d;1978 if (std.mem.eql(u8, decl.name, child_string)) {
2051 continue;1979 std.debug.print("match!\n", .{});
1980 path[i + 1] = .{ .declRef = d };
1981 continue :outer;
2052 }1982 }
2053 }1983 }
2054 for (t_struct.privDecls) |d| {1984 for (t_struct.privDecls) |d| {
2055 // TODO: this could be improved a lot1985 // TODO: this could be improved a lot
2056 // by having our own string table!1986 // by having our own string table!
2057 const decl = self.decls.items[d];1987 const decl = self.decls.items[d];
2058 if (std.mem.eql(u8, decl.name, child_decl_name)) {1988 std.debug.print("priv decl `{s}`\n", .{decl.name});
2059 path[i - 1] = d;1989 if (std.mem.eql(u8, decl.name, child_string)) {
2060 continue;1990 std.debug.print("match!\n", .{});
1991 path[i + 1] = .{ .declRef = d };
1992 continue :outer;
1993 }
1994 }
1995
1996 for (self.ast_nodes.items[t_struct.src].fields.?) |ast_node, idx| {
1997 const name = self.ast_nodes.items[ast_node].name.?;
1998 std.debug.print("field `{s}`\n", .{name});
1999 if (std.mem.eql(u8, name, child_string)) {
2000 std.debug.print("match!\n", .{});
2001 // TODO: should we really create an artificial
2002 // decl for this type? Probably not.
2003
2004 path[i + 1] = .{
2005 .fieldRef = .{
2006 .type = t_index,
2007 .index = idx,
2008 },
2009 };
2010 continue :outer;
2061 }2011 }
2062 }2012 }
2013
2014 // if we got here, our search failed
2015 panicWithContext(
2016 file,
2017 inst_index,
2018 "failed to match `{s}`",
2019 .{child_string},
2020 );
2063 },2021 },
2064 },2022 },
2065 }2023 }
2066 }2024 }
20672025
2068 if (self.pending_decl_paths.get(&path[0])) |waiter_list| {2026 if (self.pending_ref_paths.get(&path[path.len - 1])) |waiter_list| {
2069 // It's important to de-register oureslves as pending before2027 // It's important to de-register oureslves as pending before
2070 // attempting to resolve any other decl.2028 // attempting to resolve any other decl.
2071 _ = self.pending_decl_paths.remove(&path[0]);2029 _ = self.pending_ref_paths.remove(&path[path.len - 1]);
20722030
2073 for (waiter_list.items) |*resume_info| {2031 for (waiter_list.items) |resume_info| {
2074 try self.tryResolveDeclPath(resume_info.file, &resume_info.decl_path);2032 try self.tryResolveRefPath(resume_info.file, inst_index, resume_info.ref_path);
2075 }2033 }
2076 // TODO: this is where we should free waiter_list, but its in the arena2034 // TODO: this is where we should free waiter_list, but its in the arena
2077 // that said, we might want to store it elsewhere and reclaim memory asap2035 // that said, we might want to store it elsewhere and reclaim memory asap
...@@ -2084,13 +2042,14 @@ fn analyzeFunction(...@@ -2084,13 +2042,14 @@ fn analyzeFunction(
2084 scope: *Scope,2042 scope: *Scope,
2085 inst_index: usize,2043 inst_index: usize,
2086 self_ast_node_index: usize,2044 self_ast_node_index: usize,
2045 type_slot_index: usize,
2087) error{OutOfMemory}!DocData.WalkResult {2046) error{OutOfMemory}!DocData.WalkResult {
2088 const tags = file.zir.instructions.items(.tag);2047 const tags = file.zir.instructions.items(.tag);
2089 const data = file.zir.instructions.items(.data);2048 const data = file.zir.instructions.items(.data);
2090
2091 const fn_info = file.zir.getFnInfo(@intCast(u32, inst_index));2049 const fn_info = file.zir.getFnInfo(@intCast(u32, inst_index));
2050
2092 try self.ast_nodes.ensureUnusedCapacity(self.arena, fn_info.total_params_len);2051 try self.ast_nodes.ensureUnusedCapacity(self.arena, fn_info.total_params_len);
2093 var param_type_refs = try std.ArrayListUnmanaged(DocData.TypeRef).initCapacity(2052 var param_type_refs = try std.ArrayListUnmanaged(DocData.WalkResult).initCapacity(
2094 self.arena,2053 self.arena,
2095 fn_info.total_params_len,2054 fn_info.total_params_len,
2096 );2055 );
...@@ -2098,6 +2057,7 @@ fn analyzeFunction(...@@ -2098,6 +2057,7 @@ fn analyzeFunction(
2098 self.arena,2057 self.arena,
2099 fn_info.total_params_len,2058 fn_info.total_params_len,
2100 );2059 );
2060
2101 // TODO: handle scope rules for fn parameters2061 // TODO: handle scope rules for fn parameters
2102 for (fn_info.param_body[0..fn_info.total_params_len]) |param_index| {2062 for (fn_info.param_body[0..fn_info.total_params_len]) |param_index| {
2103 switch (tags[param_index]) {2063 switch (tags[param_index]) {
...@@ -2121,7 +2081,7 @@ fn analyzeFunction(...@@ -2121,7 +2081,7 @@ fn analyzeFunction(
2121 });2081 });
21222082
2123 param_type_refs.appendAssumeCapacity(2083 param_type_refs.appendAssumeCapacity(
2124 DocData.TypeRef{ .@"anytype" = {} },2084 DocData.WalkResult{ .@"anytype" = {} },
2125 );2085 );
2126 },2086 },
2127 .param, .param_comptime => {2087 .param, .param_comptime => {
...@@ -2144,9 +2104,7 @@ fn analyzeFunction(...@@ -2144,9 +2104,7 @@ fn analyzeFunction(
2144 const break_operand = data[break_index].@"break".operand;2104 const break_operand = data[break_index].@"break".operand;
2145 const param_type_ref = try self.walkRef(file, scope, break_operand);2105 const param_type_ref = try self.walkRef(file, scope, break_operand);
21462106
2147 param_type_refs.appendAssumeCapacity(2107 param_type_refs.appendAssumeCapacity(param_type_ref);
2148 walkResultToTypeRef(param_type_ref),
2149 );
2150 },2108 },
2151 }2109 }
2152 }2110 }
...@@ -2156,18 +2114,18 @@ fn analyzeFunction(...@@ -2156,18 +2114,18 @@ fn analyzeFunction(
2156 const last_instr_index = fn_info.ret_ty_body[fn_info.ret_ty_body.len - 1];2114 const last_instr_index = fn_info.ret_ty_body[fn_info.ret_ty_body.len - 1];
2157 const break_operand = data[last_instr_index].@"break".operand;2115 const break_operand = data[last_instr_index].@"break".operand;
2158 const wr = try self.walkRef(file, scope, break_operand);2116 const wr = try self.walkRef(file, scope, break_operand);
2159 break :blk walkResultToTypeRef(wr);2117 break :blk wr;
2160 };2118 };
21612119
2162 self.ast_nodes.items[self_ast_node_index].fields = param_ast_indexes.items;2120 self.ast_nodes.items[self_ast_node_index].fields = param_ast_indexes.items;
2163 try self.types.append(self.arena, .{2121 self.types.items[type_slot_index] = .{
2164 .Fn = .{2122 .Fn = .{
2165 .name = "todo_name func",2123 .name = "todo_name func",
2166 .src = self_ast_node_index,2124 .src = self_ast_node_index,
2167 .params = param_type_refs.items,2125 .params = param_type_refs.items,
2168 .ret = ret_type_ref,2126 .ret = ret_type_ref,
2169 },2127 },
2170 });2128 };
2171 return DocData.WalkResult{ .type = self.types.items.len - 1 };2129 return DocData.WalkResult{ .type = self.types.items.len - 1 };
2172}2130}
21732131
...@@ -2176,7 +2134,7 @@ fn collectUnionFieldInfo(...@@ -2176,7 +2134,7 @@ fn collectUnionFieldInfo(
2176 file: *File,2134 file: *File,
2177 scope: *Scope,2135 scope: *Scope,
2178 fields_len: usize,2136 fields_len: usize,
2179 field_type_refs: *std.ArrayListUnmanaged(DocData.TypeRef),2137 field_type_refs: *std.ArrayListUnmanaged(DocData.WalkResult),
2180 field_name_indexes: *std.ArrayListUnmanaged(usize),2138 field_name_indexes: *std.ArrayListUnmanaged(usize),
2181 ei: usize,2139 ei: usize,
2182) !void {2140) !void {
...@@ -2222,10 +2180,7 @@ fn collectUnionFieldInfo(...@@ -2222,10 +2180,7 @@ fn collectUnionFieldInfo(
2222 // type2180 // type
2223 {2181 {
2224 const walk_result = try self.walkRef(file, scope, field_type);2182 const walk_result = try self.walkRef(file, scope, field_type);
2225 try field_type_refs.append(2183 try field_type_refs.append(self.arena, walk_result);
2226 self.arena,
2227 walkResultToTypeRef(walk_result),
2228 );
2229 }2184 }
22302185
2231 // ast node2186 // ast node
...@@ -2248,7 +2203,7 @@ fn collectStructFieldInfo(...@@ -2248,7 +2203,7 @@ fn collectStructFieldInfo(
2248 file: *File,2203 file: *File,
2249 scope: *Scope,2204 scope: *Scope,
2250 fields_len: usize,2205 fields_len: usize,
2251 field_type_refs: *std.ArrayListUnmanaged(DocData.TypeRef),2206 field_type_refs: *std.ArrayListUnmanaged(DocData.WalkResult),
2252 field_name_indexes: *std.ArrayListUnmanaged(usize),2207 field_name_indexes: *std.ArrayListUnmanaged(usize),
2253 ei: usize,2208 ei: usize,
2254) !void {2209) !void {
...@@ -2291,10 +2246,7 @@ fn collectStructFieldInfo(...@@ -2291,10 +2246,7 @@ fn collectStructFieldInfo(
2291 // type2246 // type
2292 {2247 {
2293 const walk_result = try self.walkRef(file, scope, field_type);2248 const walk_result = try self.walkRef(file, scope, field_type);
2294 try field_type_refs.append(2249 try field_type_refs.append(self.arena, walk_result);
2295 self.arena,
2296 walkResultToTypeRef(walk_result),
2297 );
2298 }2250 }
22992251
2300 // ast node2252 // ast node
...@@ -2334,17 +2286,24 @@ fn walkRef(...@@ -2334,17 +2286,24 @@ fn walkRef(
2334 });2286 });
2335 },2287 },
2336 .undef => {2288 .undef => {
2337 return DocData.WalkResult{ .@"undefined" = .unspecified };2289 var t = try self.arena.create(DocData.WalkResult);
2290 t.* = .void;
2291
2292 return DocData.WalkResult{ .@"undefined" = t };
2338 },2293 },
2339 .zero => {2294 .zero => {
2295 var t = try self.arena.create(DocData.WalkResult);
2296 t.* = .{ .type = @enumToInt(Ref.comptime_int_type) };
2340 return DocData.WalkResult{ .int = .{2297 return DocData.WalkResult{ .int = .{
2341 .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) },2298 .typeRef = t,
2342 .value = 0,2299 .value = 0,
2343 } };2300 } };
2344 },2301 },
2345 .one => {2302 .one => {
2303 var t = try self.arena.create(DocData.WalkResult);
2304 t.* = .{ .type = @enumToInt(Ref.comptime_int_type) };
2346 return DocData.WalkResult{ .int = .{2305 return DocData.WalkResult{ .int = .{
2347 .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) },2306 .typeRef = t,
2348 .value = 1,2307 .value = 1,
2349 } };2308 } };
2350 },2309 },
...@@ -2356,7 +2315,9 @@ fn walkRef(...@@ -2356,7 +2315,9 @@ fn walkRef(
2356 return DocData.WalkResult{ .@"unreachable" = {} };2315 return DocData.WalkResult{ .@"unreachable" = {} };
2357 },2316 },
2358 .null_value => {2317 .null_value => {
2359 return DocData.WalkResult{ .@"null" = .unspecified };2318 var t = try self.arena.create(DocData.WalkResult);
2319 t.* = .void;
2320 return DocData.WalkResult{ .@"null" = t };
2360 },2321 },
2361 .bool_true => {2322 .bool_true => {
2362 return DocData.WalkResult{ .bool = true };2323 return DocData.WalkResult{ .bool = true };
...@@ -2365,20 +2326,27 @@ fn walkRef(...@@ -2365,20 +2326,27 @@ fn walkRef(
2365 return DocData.WalkResult{ .bool = false };2326 return DocData.WalkResult{ .bool = false };
2366 },2327 },
2367 .empty_struct => {2328 .empty_struct => {
2329 var t = try self.arena.create(DocData.WalkResult);
2330 t.* = .void;
2331
2368 return DocData.WalkResult{ .@"struct" = .{2332 return DocData.WalkResult{ .@"struct" = .{
2369 .typeRef = .unspecified,2333 .typeRef = t,
2370 .fieldVals = &.{},2334 .fieldVals = &.{},
2371 } };2335 } };
2372 },2336 },
2373 .zero_usize => {2337 .zero_usize => {
2338 var t = try self.arena.create(DocData.WalkResult);
2339 t.* = .{ .type = @enumToInt(Ref.usize_type) };
2374 return DocData.WalkResult{ .int = .{2340 return DocData.WalkResult{ .int = .{
2375 .typeRef = .{ .type = @enumToInt(Ref.usize_type) },2341 .typeRef = t,
2376 .value = 0,2342 .value = 0,
2377 } };2343 } };
2378 },2344 },
2379 .one_usize => {2345 .one_usize => {
2346 var t = try self.arena.create(DocData.WalkResult);
2347 t.* = .{ .type = @enumToInt(Ref.usize_type) };
2380 return DocData.WalkResult{ .int = .{2348 return DocData.WalkResult{ .int = .{
2381 .typeRef = .{ .type = @enumToInt(Ref.usize_type) },2349 .typeRef = t,
2382 .value = 1,2350 .value = 1,
2383 } };2351 } };
2384 },2352 },
...@@ -2408,37 +2376,19 @@ fn walkRef(...@@ -2408,37 +2376,19 @@ fn walkRef(
2408 }2376 }
2409}2377}
24102378
2411/// Maps some `DocData.WalkResult` cases to `DocData.TypeRef`.
2412/// Correct code should never cause this function to fail but
2413/// incorrect code might (eg: `const foo: 5 = undefined;`)
2414fn walkResultToTypeRef(wr: DocData.WalkResult) DocData.TypeRef {
2415 return switch (wr) {
2416 else => std.debug.panic(
2417 "TODO: handle `{s}` in `walkResultToTypeRef`\n",
2418 .{@tagName(wr)},
2419 ),
2420
2421 .typeOf => |v| .{ .typeOf = v },
2422 .comptimeExpr => |v| .{ .comptimeExpr = v },
2423 .declPath => |v| .{ .declPath = v },
2424 .type => |v| .{ .type = v },
2425 .call => |v| .{ .call = v },
2426 };
2427}
2428
2429/// Given a WalkResult, tries to find its type.2379/// Given a WalkResult, tries to find its type.
2430/// Used to analyze instructions like `array_init`, which require us to2380/// Used to analyze instructions like `array_init`, which require us to
2431/// inspect its first element to find out the array type.2381/// inspect its first element to find out the array type.
2432fn typeOfWalkResult(wr: DocData.WalkResult) DocData.TypeRef {2382fn typeOfWalkResult(wr: DocData.WalkResult) DocData.WalkResult {
2433 return switch (wr) {2383 return switch (wr) {
2434 else => std.debug.panic(2384 else => std.debug.panic(
2435 "TODO: handle `{s}` in typeOfWalkResult\n",2385 "TODO: handle `{s}` in typeOfWalkResult\n",
2436 .{@tagName(wr)},2386 .{@tagName(wr)},
2437 ),2387 ),
2438 .type => .{ .type = @enumToInt(DocData.DocTypeKinds.Type) },2388 .type => .{ .type = @enumToInt(DocData.DocTypeKinds.Type) },
2439 .int => |v| v.typeRef,2389 .int => |v| v.typeRef.*,
2440 .float => |v| v.typeRef,2390 .float => |v| v.typeRef.*,
2441 .array => |v| v.typeRef,2391 .array => |v| v.typeRef.*,
2442 };2392 };
2443}2393}
24442394
...@@ -2454,3 +2404,12 @@ fn panicWithContext(file: *File, inst: usize, comptime fmt: []const u8, args: an...@@ -2454,3 +2404,12 @@ fn panicWithContext(file: *File, inst: usize, comptime fmt: []const u8, args: an
2454 std.debug.print("Context [{s}] % {}\n", .{ file.sub_file_path, inst });2404 std.debug.print("Context [{s}] % {}\n", .{ file.sub_file_path, inst });
2455 std.debug.panic(fmt, args);2405 std.debug.panic(fmt, args);
2456}2406}
2407
2408fn cteTodo(self: *Autodoc, msg: []const u8) error{OutOfMemory}!DocData.WalkResult {
2409 const cte_slot_index = self.comptime_exprs.items.len;
2410 try self.comptime_exprs.append(self.arena, .{
2411 .code = msg,
2412 .typeRef = .{ .type = @enumToInt(DocData.DocTypeKinds.ComptimeExpr) },
2413 });
2414 return DocData.WalkResult{ .comptimeExpr = cte_slot_index };
2415}