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) = .{},
2222
2323// These fields hold temporary state of the analysis process
2424// and are mainly used by the decl path resolving algorithm.
25pending_decl_paths: std.AutoHashMapUnmanaged(
26 *usize, // pointer to declpath head (ie `&decl_path[0]`)
27 std.ArrayListUnmanaged(DeclPathResumeInfo),
25pending_ref_paths: std.AutoHashMapUnmanaged(
26 *DocData.WalkResult, // pointer to declpath tail end (ie `&decl_path[decl_path.len - 1]`)
27 std.ArrayListUnmanaged(RefPathResumeInfo),
2828) = .{},
29decl_paths_pending_on_decls: std.AutoHashMapUnmanaged(
29ref_paths_pending_on_decls: std.AutoHashMapUnmanaged(
3030 usize,
31 std.ArrayListUnmanaged(DeclPathResumeInfo),
31 std.ArrayListUnmanaged(RefPathResumeInfo),
3232) = .{},
33decl_paths_pending_on_types: std.AutoHashMapUnmanaged(
33ref_paths_pending_on_types: std.AutoHashMapUnmanaged(
3434 usize,
35 std.ArrayListUnmanaged(DeclPathResumeInfo),
35 std.ArrayListUnmanaged(RefPathResumeInfo),
3636) = .{},
3737
38const DeclPathResumeInfo = struct {
38const RefPathResumeInfo = struct {
3939 file: *File,
40 decl_path: DocData.DeclPath,
40 ref_path: []DocData.WalkResult,
4141};
4242
4343var arena_allocator: std.heap.ArenaAllocator = undefined;
......@@ -79,6 +79,8 @@ pub fn generateZirData(self: *Autodoc) !void {
7979 try self.types.append(self.arena, .{
8080 .ComptimeExpr = .{ .name = "ComptimeExpr" },
8181 });
82
83 var tr = DocData.WalkResult{ .type = @enumToInt(Ref.usize_type) };
8284 // this skipts Ref.none but it's ok becuse we replaced it with ComptimeExpr
8385 var i: u32 = 1;
8486 while (i <= @enumToInt(Ref.anyerror_void_error_union_type)) : (i += 1) {
......@@ -94,9 +96,7 @@ pub fn generateZirData(self: *Autodoc) !void {
9496 .Array = .{
9597 .len = .{
9698 .int = .{
97 .typeRef = .{
98 .type = @enumToInt(Ref.usize_type),
99 },
99 .typeRef = &tr,
100100 .value = 1,
101101 .negated = false,
102102 },
......@@ -165,15 +165,15 @@ pub fn generateZirData(self: *Autodoc) !void {
165165 try self.files.put(self.arena, file, main_type_index);
166166 _ = 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) {
169169 @panic("some decl paths were never fully analized (pending on decls)");
170170 }
171171
172 if (self.decl_paths_pending_on_types.count() > 0) {
172 if (self.ref_paths_pending_on_types.count() > 0) {
173173 @panic("some decl paths were never fully analized (pending on types)");
174174 }
175175
176 if (self.pending_decl_paths.count() > 0) {
176 if (self.pending_ref_paths.count() > 0) {
177177 @panic("some decl paths were never fully analized");
178178 }
179179
......@@ -304,7 +304,7 @@ const DocData = struct {
304304 decls: []Decl,
305305 comptimeExprs: []ComptimeExpr,
306306 const Call = struct {
307 func: TypeRef,
307 func: WalkResult,
308308 args: []WalkResult,
309309 ret: WalkResult,
310310 };
......@@ -337,7 +337,7 @@ const DocData = struct {
337337
338338 const ComptimeExpr = struct {
339339 code: []const u8,
340 typeRef: TypeRef,
340 typeRef: WalkResult,
341341 };
342342 const Package = struct {
343343 name: []const u8 = "root",
......@@ -379,18 +379,18 @@ const DocData = struct {
379379 Float: struct { name: []const u8 },
380380 Pointer: struct {
381381 size: std.builtin.TypeInfo.Pointer.Size,
382 child: TypeRef,
382 child: WalkResult,
383383 },
384384 Array: struct {
385385 len: WalkResult,
386 child: TypeRef,
386 child: WalkResult,
387387 },
388388 Struct: struct {
389389 name: []const u8,
390 src: ?usize = null, // index into astNodes
390 src: usize, // index into astNodes
391391 privDecls: []usize = &.{}, // index into decls
392392 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)
394394 },
395395 ComptimeExpr: struct { name: []const u8 },
396396 ComptimeFloat: struct { name: []const u8 },
......@@ -399,7 +399,7 @@ const DocData = struct {
399399 Null: struct { name: []const u8 },
400400 Optional: struct {
401401 name: []const u8,
402 child: TypeRef,
402 child: WalkResult,
403403 },
404404 ErrorUnion: struct { name: []const u8 },
405405 ErrorSet: struct {
......@@ -418,13 +418,13 @@ const DocData = struct {
418418 src: ?usize = null, // index into astNodes
419419 privDecls: ?[]usize = null, // index into decls
420420 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)
422422 },
423423 Fn: struct {
424424 name: []const u8,
425425 src: ?usize = null, // index into astNodes
426 ret: TypeRef,
427 params: ?[]TypeRef = null, // (use src->fields to find names)
426 ret: WalkResult,
427 params: ?[]WalkResult = null, // (use src->fields to find names)
428428 },
429429 BoundFn: struct { name: []const u8 },
430430 Opaque: struct { name: []const u8 },
......@@ -511,66 +511,6 @@ const DocData = struct {
511511 }
512512 };
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
574514 /// A WalkResult represents the result of the analysis process done to a
575515 /// declaration. This includes: decls, fields, etc.
576516 ///
......@@ -581,20 +521,23 @@ const DocData = struct {
581521 comptimeExpr: usize, // index in `comptimeExprs`
582522 void,
583523 @"unreachable",
584 @"null": TypeRef,
585 @"undefined": TypeRef,
524 @"null": *WalkResult,
525 @"undefined": *WalkResult,
586526 @"struct": Struct,
587527 bool: bool,
588528 @"anytype",
589529 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,
591534 int: struct {
592 typeRef: TypeRef,
535 typeRef: *WalkResult,
593536 value: usize, // direct value
594537 negated: bool = false,
595538 },
596539 float: struct {
597 typeRef: TypeRef,
540 typeRef: *WalkResult,
598541 value: f64, // direct value
599542 negated: bool = false,
600543 },
......@@ -606,8 +549,13 @@ const DocData = struct {
606549 compileError: []const u8,
607550 string: []const u8,
608551
552 const FieldRef = struct {
553 type: usize, // index in `types`
554 index: usize, // index in type.fields
555 };
556
609557 const Struct = struct {
610 typeRef: TypeRef,
558 typeRef: *WalkResult,
611559 fieldVals: []FieldVal,
612560
613561 const FieldVal = struct {
......@@ -616,7 +564,7 @@ const DocData = struct {
616564 };
617565 };
618566 const Array = struct {
619 typeRef: TypeRef,
567 typeRef: *WalkResult,
620568 data: []WalkResult,
621569 };
622570
......@@ -624,14 +572,14 @@ const DocData = struct {
624572 self: WalkResult,
625573 options: std.json.StringifyOptions,
626574 w: anytype,
627 ) !void {
575 ) std.os.WriteError!void {
628576 switch (self) {
629577 .void, .@"unreachable", .@"anytype" => {
630578 try w.print(
631579 \\{{ "{s}":{{}} }}
632580 , .{@tagName(self)});
633581 },
634 .type, .comptimeExpr, .call => |v| {
582 .type, .comptimeExpr, .call, .this, .declRef => |v| {
635583 try w.print(
636584 \\{{ "{s}":{} }}
637585 , .{ @tagName(self), v });
......@@ -666,16 +614,22 @@ const DocData = struct {
666614 .typeOf, .sizeOf => |v| try std.json.stringify(v, options, w),
667615 .compileError => |v| try std.json.stringify(v, options, w),
668616 .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 ),
669622 .@"struct" => |v| try std.json.stringify(
670623 struct { @"struct": Struct }{ .@"struct" = v },
671624 options,
672625 w,
673626 ),
674 .declPath => |v| {
675 try w.print("{{ \"hasCte\": {}, \"declPath\": [", .{v.hasCte});
676 for (v.path) |d, i| {
677 const comma = if (i == v.path.len - 1) "]}" else ",";
678 try w.print("{d}{s}", .{ d, comma });
627 .refPath => |v| {
628 try w.print("{{ \"refPath\": [", .{});
629 for (v) |c, i| {
630 const comma = if (i == v.len - 1) "]}" else ",\n";
631 try c.jsonStringify(options, w);
632 try w.print("{s}", .{comma});
679633 }
680634 },
681635 .array => |v| try std.json.stringify(
......@@ -758,6 +712,7 @@ fn walkInstruction(
758712 .comptimeExpr = cte_slot_index,
759713 };
760714 }
715
761716 const new_file = self.module.importFile(file, path) catch unreachable;
762717 const result = try self.files.getOrPut(self.arena, new_file.file);
763718 if (result.found_existing) {
......@@ -794,37 +749,24 @@ fn walkInstruction(
794749
795750 return DocData.WalkResult{ .compileError = operand.string };
796751 },
797 .switch_block => {
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 },
752 .switch_block => return self.cteTodo("[switch]"),
808753 .enum_literal => {
809754 const str_tok = data[inst_index].str_tok;
810755 const literal = file.zir.nullTerminatedString(str_tok.start);
811756 return DocData.WalkResult{ .enumLiteral = literal };
812757 },
813 .div_exact, .div => {
814 const cte_slot_index = self.comptime_exprs.items.len;
815 try self.comptime_exprs.append(self.arena, .{
816 .code = "@div*(...)",
817 .typeRef = .{ .type = @enumToInt(DocData.DocTypeKinds.ComptimeExpr) },
818 });
819 return DocData.WalkResult{ .comptimeExpr = cte_slot_index };
820 },
758 .div_exact, .div => return self.cteTodo("@div(...)"),
759 .mul => return self.cteTodo("@mul(...)"),
760 .array_mul => return self.cteTodo("a ** b"),
761 .bool_br_and, .bool_br_or => return self.cteTodo("bool op"),
762 .cmp_eq => return self.cteTodo("bool op"),
821763 .int => {
822764 const int = data[inst_index].int;
765 const t = try self.arena.create(DocData.WalkResult);
766 t.* = .{ .type = @enumToInt(Ref.comptime_int_type) };
823767 return DocData.WalkResult{
824768 .int = .{
825 .typeRef = .{
826 .type = @enumToInt(Ref.comptime_int_type),
827 },
769 .typeRef = t,
828770 .value = int,
829771 },
830772 };
......@@ -843,7 +785,7 @@ fn walkInstruction(
843785 try self.types.append(self.arena, .{
844786 .Pointer = .{
845787 .size = ptr.size,
846 .child = walkResultToTypeRef(elem_type_ref),
788 .child = elem_type_ref,
847789 },
848790 });
849791
......@@ -862,7 +804,7 @@ fn walkInstruction(
862804 try self.types.append(self.arena, .{
863805 .Pointer = .{
864806 .size = ptr.size,
865 .child = walkResultToTypeRef(elem_type_ref),
807 .child = elem_type_ref,
866808 },
867809 });
868810
......@@ -871,7 +813,7 @@ fn walkInstruction(
871813 .array_type => {
872814 const bin = data[inst_index].bin;
873815 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
876818 const type_slot_index = self.types.items.len;
877819 try self.types.append(self.arena, .{
......@@ -891,12 +833,15 @@ fn walkInstruction(
891833 array_data[idx] = try self.walkRef(file, parent_scope, op);
892834 }
893835
836 const at = try self.arena.create(DocData.WalkResult);
837 at.* = .{ .type = @enumToInt(Ref.usize_type) };
838
894839 const type_slot_index = self.types.items.len;
895840 try self.types.append(self.arena, .{
896841 .Array = .{
897842 .len = .{
898843 .int = .{
899 .typeRef = .{ .type = @enumToInt(Ref.usize_type) },
844 .typeRef = at,
900845 .value = operands.len,
901846 .negated = false,
902847 },
......@@ -905,18 +850,22 @@ fn walkInstruction(
905850 },
906851 });
907852
853 const t = try self.arena.create(DocData.WalkResult);
854 t.* = .{ .type = type_slot_index };
908855 return DocData.WalkResult{ .array = .{
909 .typeRef = .{ .type = type_slot_index },
856 .typeRef = t,
910857 .data = array_data,
911858 } };
912859 },
913860 .float => {
914861 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
915866 return DocData.WalkResult{
916867 .float = .{
917 .typeRef = .{
918 .type = @enumToInt(Ref.comptime_float_type),
919 },
868 .typeRef = t,
920869 .value = float,
921870 },
922871 };
......@@ -956,7 +905,7 @@ fn walkInstruction(
956905 const pl_node = data[inst_index].pl_node;
957906 const extra = file.zir.extraData(Zir.Inst.As, pl_node.payload_index);
958907 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
961910 var operand = try self.walkRef(file, parent_scope, extra.data.operand);
962911
......@@ -967,7 +916,7 @@ fn walkInstruction(
967916 "TODO: handle {s} in `walkInstruction.as_node`\n",
968917 .{@tagName(operand)},
969918 ),
970 .declPath, .type, .string => {},
919 .refPath, .type, .string, .call, .enumLiteral => {},
971920 // we don't do anything because up until now,
972921 // I've only seen this used as such:
973922 // @as(@as(type, Baz), .{})
......@@ -979,9 +928,9 @@ fn walkInstruction(
979928 .comptimeExpr => {
980929 self.comptime_exprs.items[operand.comptimeExpr].typeRef = dest_type_ref;
981930 },
982 .int => operand.int.typeRef = dest_type_ref,
983 .@"struct" => operand.@"struct".typeRef = dest_type_ref,
984 .@"undefined" => operand.@"undefined" = dest_type_ref,
931 .int => operand.int.typeRef.* = dest_type_ref,
932 .@"struct" => operand.@"struct".typeRef.* = dest_type_ref,
933 .@"undefined" => operand.@"undefined".* = dest_type_ref,
985934 }
986935
987936 return operand;
......@@ -993,7 +942,7 @@ fn walkInstruction(
993942 parent_scope,
994943 un_node.operand,
995944 );
996 const type_ref = walkResultToTypeRef(operand);
945 const type_ref = operand;
997946 const res = DocData.WalkResult{ .type = self.types.items.len };
998947 try self.types.append(self.arena, .{
999948 .Optional = .{ .name = "?TODO", .child = type_ref },
......@@ -1003,9 +952,7 @@ fn walkInstruction(
1003952 .decl_val, .decl_ref => {
1004953 const str_tok = data[inst_index].str_tok;
1005954 const decls_slot_index = parent_scope.resolveDeclName(str_tok.start);
1006 var path = try self.arena.alloc(usize, 1);
1007 path[0] = decls_slot_index;
1008 return DocData.WalkResult{ .declPath = .{ .path = path } };
955 return DocData.WalkResult{ .declRef = decls_slot_index };
1009956 },
1010957 .field_val, .field_call_bind, .field_ptr, .field_type => {
1011958 // TODO: field type uses Zir.Inst.FieldType, it just happens to have the
......@@ -1013,101 +960,51 @@ fn walkInstruction(
1013960 const pl_node = data[inst_index].pl_node;
1014961 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) = .{};
1017964 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 });
1020969 // Put inside path the starting index of each decl name that
1021970 // we encounter as we navigate through all the field_vals
1022971 while (tags[lhs] == .field_val or
1023972 tags[lhs] == .field_call_bind or
1024 tags[lhs] == .field_ptr)
973 tags[lhs] == .field_ptr or
974 tags[lhs] == .field_type)
1025975 {
1026976 const lhs_extra = file.zir.extraData(
1027977 Zir.Inst.Field,
1028978 data[lhs].pl_node.payload_index,
1029979 );
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 });
1032984 lhs = @enumToInt(lhs_extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs
1033985 }
1034986
1035 switch (tags[lhs]) {
1036 else => panicWithContext(
1037 file,
1038 inst_index,
1039 "TODO: handle `{s}` in walkInstruction.field_val",
1040 .{@tagName(tags[lhs])},
1041 ),
1042 .call => {
1043 const walk_result = try self.walkInstruction(file, parent_scope, lhs);
1044 const ast_node_index = idx: {
1045 const idx = self.ast_nodes.items.len;
1046 try self.ast_nodes.append(self.arena, .{
1047 .file = 0,
1048 .line = 0,
1049 .col = 0,
1050 .docs = "",
1051 .fields = null,
1052 });
1053 break :idx idx;
1054 };
1055
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 };
987 const wr = try self.walkInstruction(file, parent_scope, lhs);
988 try path.append(self.arena, wr);
989
990 // This way the data in `path` has the same ordering that the ref
991 // path has in the text: most general component first.
992 std.mem.reverse(DocData.WalkResult, path.items);
993
994 // Righ now, every element of `path` is a string except its first
995 // element (at index 0). We're now going to attempt to resolve each
996 // string. If one or more components in this path are not yet fully
997 // analyzed, the path will only be solved partially, but we expect
998 // to eventually solve it fully(or give up in case of a
999 // comptimeExpr). This means that:
1000 // - (1) Paths can be not fully analyzed temporarily, so any code
1001 // that requires to know where a ref path leads to, neeeds to
1002 // implement support for lazyness (see self.pending_ref_paths)
1003 // - (2) Paths can sometimes never resolve fully. This means that
1004 // any value that depends on that will have to become a
1005 // comptimeExpr.
1006 try self.tryResolveRefPath(file, lhs, path.items);
1007 return DocData.WalkResult{ .refPath = path.items };
11111008 },
11121009 .int_type => {
11131010 const int_type = data[inst_index].int_type;
......@@ -1139,7 +1036,7 @@ fn walkInstruction(
11391036 extra.data.fields_len,
11401037 );
11411038
1142 var type_ref: DocData.TypeRef = undefined;
1039 const type_ref = try self.arena.create(DocData.WalkResult);
11431040 var idx = extra.end;
11441041 for (field_vals) |*fv| {
11451042 const init_extra = file.zir.extraData(Zir.Inst.StructInit.Item, idx);
......@@ -1161,11 +1058,10 @@ fn walkInstruction(
11611058 parent_scope,
11621059 field_extra.data.container_type,
11631060 );
1164 type_ref = walkResultToTypeRef(wr);
1061 type_ref.* = wr;
11651062 }
11661063 break :blk file.zir.nullTerminatedString(field_extra.data.name_start);
11671064 };
1168
11691065 const value = try self.walkRef(file, parent_scope, init_extra.data.init);
11701066 fv.* = .{ .name = field_name, .val = value };
11711067 }
......@@ -1236,9 +1132,7 @@ fn walkInstruction(
12361132 const pl_node = data[inst_index].pl_node;
12371133 const extra = file.zir.extraData(Zir.Inst.Call, pl_node.payload_index);
12381134
1239 const callee = walkResultToTypeRef(
1240 try self.walkRef(file, parent_scope, extra.data.callee),
1241 );
1135 const callee = try self.walkRef(file, parent_scope, extra.data.callee);
12421136
12431137 const args_len = extra.data.flags.args_len;
12441138 var args = try self.arena.alloc(DocData.WalkResult, args_len);
......@@ -1267,16 +1161,20 @@ fn walkInstruction(
12671161 return DocData.WalkResult{ .call = call_slot_index };
12681162 },
12691163 .func, .func_inferred => {
1164 const type_slot_index = self.types.items.len;
1165 try self.types.append(self.arena, .{ .Unanalyzed = {} });
1166
12701167 return self.analyzeFunction(
12711168 file,
12721169 parent_scope,
12731170 inst_index,
12741171 self_ast_node_index,
1172 type_slot_index,
12751173 );
12761174 },
12771175 .extended => {
12781176 // 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.
12801178 // When we don't find a type, the defer will run anyway but shouldn't
12811179 // ever be able to find a match inside `decl_paths_pending_on_types`
12821180 // TODO: extract this logic into a function and only call it when appropriate.
......@@ -1284,14 +1182,18 @@ fn walkInstruction(
12841182 try self.types.append(self.arena, .{ .Unanalyzed = {} });
12851183
12861184 defer {
1287 if (self.decl_paths_pending_on_types.get(type_slot_index)) |paths| {
1288 for (paths.items) |*resume_info| {
1289 self.tryResolveDeclPath(resume_info.file, &resume_info.decl_path) catch {
1185 if (self.ref_paths_pending_on_types.get(type_slot_index)) |paths| {
1186 for (paths.items) |resume_info| {
1187 self.tryResolveRefPath(
1188 resume_info.file,
1189 inst_index,
1190 resume_info.ref_path,
1191 ) catch {
12901192 @panic("Out of memory");
12911193 };
12921194 }
12931195
1294 _ = self.decl_paths_pending_on_types.remove(type_slot_index);
1196 _ = self.ref_paths_pending_on_types.remove(type_slot_index);
12951197 // TODO: we should deallocate the arraylist that holds all the
12961198 // decl paths. not doing it now since it's arena-allocated
12971199 // anyway, but maybe we should put it elsewhere.
......@@ -1308,12 +1210,15 @@ fn walkInstruction(
13081210 .{@tagName(extended.opcode)},
13091211 );
13101212 },
1213
1214 .opaque_decl => return self.cteTodo("opaque {...}"),
13111215 .func => {
13121216 return try self.analyzeFunction(
13131217 file,
13141218 parent_scope,
13151219 inst_index,
13161220 self_ast_node_index,
1221 type_slot_index,
13171222 );
13181223 },
13191224 .variable => {
......@@ -1403,7 +1308,7 @@ fn walkInstruction(
14031308 // const body = file.zir.extra[extra_index..][0..body_len];
14041309 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(
14071312 self.arena,
14081313 fields_len,
14091314 );
......@@ -1633,7 +1538,7 @@ fn walkInstruction(
16331538 // const body = file.zir.extra[extra_index..][0..body_len];
16341539 extra_index += body_len;
16351540
1636 var field_type_refs: std.ArrayListUnmanaged(DocData.TypeRef) = .{};
1541 var field_type_refs: std.ArrayListUnmanaged(DocData.WalkResult) = .{};
16371542 var field_name_indexes: std.ArrayListUnmanaged(usize) = .{};
16381543 try self.collectStructFieldInfo(
16391544 file,
......@@ -1659,22 +1564,7 @@ fn walkInstruction(
16591564 return DocData.WalkResult{ .type = type_slot_index };
16601565 },
16611566 .this => {
1662 // TODO: consider if we should reuse an existing decl
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 } };
1567 return DocData.WalkResult{ .this = parent_scope.enclosing_type };
16781568 },
16791569 }
16801570 },
......@@ -1900,14 +1790,18 @@ fn walkDecls(
19001790 };
19011791
19021792 // Unblock any pending decl path that was waiting for this decl.
1903 if (self.decl_paths_pending_on_decls.get(decls_slot_index)) |paths| {
1904 for (paths.items) |*resume_info| {
1905 try self.tryResolveDeclPath(resume_info.file, &resume_info.decl_path);
1793 if (self.ref_paths_pending_on_decls.get(decls_slot_index)) |paths| {
1794 for (paths.items) |resume_info| {
1795 try self.tryResolveRefPath(
1796 resume_info.file,
1797 decl_index,
1798 resume_info.ref_path,
1799 );
19061800 }
19071801
1908 _ = self.decl_paths_pending_on_decls.remove(decls_slot_index);
1802 _ = self.ref_paths_pending_on_decls.remove(decls_slot_index);
19091803 // TODO: we should deallocate the arraylist that holds all the
1910 // decl paths. not doing it now since it's arena-allocated
1804 // ref paths. not doing it now since it's arena-allocated
19111805 // anyway, but maybe we should put it elsewhere.
19121806 }
19131807 }
......@@ -1915,109 +1809,139 @@ fn walkDecls(
19151809 return extra_index;
19161810}
19171811
1918/// An unresolved path has a decl index at its end, while every other element
1919/// is an index into the string table. Resolving means iteratively map each
1920/// string to a decl_index.
1812/// An unresolved path has a non-string WalkResult at its beginnig, while every
1813/// other element is a string WalkResult. Resolving means iteratively map each
1814/// string to a Decl / Type / Call / etc.
19211815///
19221816/// 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.
19241818/// 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`.
19261820///
1927/// When a decl or a type is fully analyzed if will then check if there's any
1928/// pending decl path blocked on it and, if any, will progress their resolution
1929/// by calling tryResolveDeclPath again.
1821/// When walkDecls / walkInstruction finishes analyzing a decl / type, it will
1822/// then check if there's any pending ref path blocked on it and, if any, it
1823/// will progress their resolution by calling tryResolveRefPath again.
19301824///
1931/// Decl paths can also depend on other decl paths. See
1932/// `self.pending_decl_paths` for more info.
1825/// Ref paths can also depend on other ref paths. See
1826/// `self.pending_ref_paths` for more info.
19331827///
1934/// A decl path that has a component that resolves into a comptimeExpr will
1935/// give up its resolution process entirely.
1936///
1937/// TODO: when giving up, translate remaining string indexes into data that
1938/// can be used by the frontend. Requires implementing a frontend string
1939/// table.
1940fn tryResolveDeclPath(
1828/// A ref path that has a component that resolves into a comptimeExpr will
1829/// give up its resolution process entirely, leaving the remaining components
1830/// as strings.
1831fn tryResolveRefPath(
19411832 self: *Autodoc,
19421833 /// File from which the decl path originates.
19431834 file: *File,
1944 decl_path: *DocData.DeclPath,
1835 inst_index: usize, // used only for panicWithContext
1836 path: []DocData.WalkResult,
19451837) 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;
1949 outer: while (i > 1) {
1950 i -= 1;
1951 const decl_index = path[i];
1952 const string_index = path[i - 1];
1855 // This decl path is pending completion
1856 {
1857 const res = try self.pending_ref_paths.getOrPut(
1858 self.arena,
1859 &path[path.len - 1],
1860 );
1861 if (!res.found_existing) res.value_ptr.* = .{};
1862 }
19531863
1954 const parent = self.decls.items[decl_index];
1955 if (!parent._analyzed) {
1956 // This decl path is pending completion
1957 {
1958 const res = try self.pending_decl_paths.getOrPut(self.arena, &path[0]);
1959 if (!res.found_existing) res.value_ptr.* = .{};
1960 }
1864 const res = try self.ref_paths_pending_on_decls.getOrPut(
1865 self.arena,
1866 decl_index,
1867 );
1868 if (!res.found_existing) res.value_ptr.* = .{};
1869 try res.value_ptr.*.append(self.arena, .{
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);
1963 if (!res.found_existing) res.value_ptr.* = .{};
1964 try res.value_ptr.*.append(self.arena, .{
1965 .file = file,
1966 .decl_path = .{ .path = path[0 .. i + 1] },
1967 });
1874 // We return instead doing `break :outer` to prevent the
1875 // code after the :outer while loop to run, as it assumes
1876 // that the path will have been fully analyzed (or we
1877 // have given up because of a comptimeExpr).
1878 return;
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 );
19701921 }
19711922
1972 const child_decl_name = file.zir.nullTerminatedString(string_index);
1973 switch (parent.value) {
1923 switch (resolved_parent) {
19741924 else => {
1975 std.debug.panic(
1976 "TODO: handle `{s}`in tryResolveDecl\n \"{s}\":{}",
1977 .{ @tagName(parent.value), parent.name, parent.value },
1925 // NOTE: indirect references to types / decls should be handled
1926 // in the switch above this one!
1927 panicWithContext(
1928 file,
1929 inst_index,
1930 "TODO: handle `{s}`in tryResolveRefPath\nInfo: {}",
1931 .{ @tagName(resolved_parent), resolved_parent },
19781932 );
19791933 },
19801934 .comptimeExpr, .call => {
19811935 // Since we hit a cte, we leave the remaining strings unresolved
19821936 // and completely give up on resolving this decl path.
1983 decl_path.hasCte = true;
1937 //decl_path.hasCte = true;
19841938 break :outer;
19851939 },
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 },
20181940 .type => |t_index| switch (self.types.items[t_index]) {
20191941 else => {
2020 std.debug.panic(
1942 panicWithContext(
1943 file,
1944 inst_index,
20211945 "TODO: handle `{s}` in tryResolveDeclPath.type\n",
20221946 .{@tagName(self.types.items[t_index])},
20231947 );
......@@ -2025,53 +1949,87 @@ fn tryResolveDeclPath(
20251949 .Unanalyzed => {
20261950 // This decl path is pending completion
20271951 {
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 );
20291956 if (!res.found_existing) res.value_ptr.* = .{};
20301957 }
20311958
2032 const res = try self.decl_paths_pending_on_types.getOrPut(
1959 const res = try self.ref_paths_pending_on_types.getOrPut(
20331960 self.arena,
20341961 t_index,
20351962 );
20361963 if (!res.found_existing) res.value_ptr.* = .{};
20371964 try res.value_ptr.*.append(self.arena, .{
20381965 .file = file,
2039 .decl_path = .{ .path = path[0 .. i + 1] },
1966 .ref_path = path[i..path.len],
20401967 });
20411968
20421969 return;
20431970 },
20441971 .Struct => |t_struct| {
1972 std.debug.print("search: {s}\n", .{child_string});
20451973 for (t_struct.pubDecls) |d| {
20461974 // TODO: this could be improved a lot
20471975 // by having our own string table!
20481976 const decl = self.decls.items[d];
2049 if (std.mem.eql(u8, decl.name, child_decl_name)) {
2050 path[i - 1] = d;
2051 continue;
1977 std.debug.print("pub decl `{s}`\n", .{decl.name});
1978 if (std.mem.eql(u8, decl.name, child_string)) {
1979 std.debug.print("match!\n", .{});
1980 path[i + 1] = .{ .declRef = d };
1981 continue :outer;
20521982 }
20531983 }
20541984 for (t_struct.privDecls) |d| {
20551985 // TODO: this could be improved a lot
20561986 // by having our own string table!
20571987 const decl = self.decls.items[d];
2058 if (std.mem.eql(u8, decl.name, child_decl_name)) {
2059 path[i - 1] = d;
2060 continue;
1988 std.debug.print("priv decl `{s}`\n", .{decl.name});
1989 if (std.mem.eql(u8, decl.name, child_string)) {
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;
20612011 }
20622012 }
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 );
20632021 },
20642022 },
20652023 }
20662024 }
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| {
20692027 // It's important to de-register oureslves as pending before
20702028 // 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| {
2074 try self.tryResolveDeclPath(resume_info.file, &resume_info.decl_path);
2031 for (waiter_list.items) |resume_info| {
2032 try self.tryResolveRefPath(resume_info.file, inst_index, resume_info.ref_path);
20752033 }
20762034 // TODO: this is where we should free waiter_list, but its in the arena
20772035 // that said, we might want to store it elsewhere and reclaim memory asap
......@@ -2084,13 +2042,14 @@ fn analyzeFunction(
20842042 scope: *Scope,
20852043 inst_index: usize,
20862044 self_ast_node_index: usize,
2045 type_slot_index: usize,
20872046) error{OutOfMemory}!DocData.WalkResult {
20882047 const tags = file.zir.instructions.items(.tag);
20892048 const data = file.zir.instructions.items(.data);
2090
20912049 const fn_info = file.zir.getFnInfo(@intCast(u32, inst_index));
2050
20922051 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(
20942053 self.arena,
20952054 fn_info.total_params_len,
20962055 );
......@@ -2098,6 +2057,7 @@ fn analyzeFunction(
20982057 self.arena,
20992058 fn_info.total_params_len,
21002059 );
2060
21012061 // TODO: handle scope rules for fn parameters
21022062 for (fn_info.param_body[0..fn_info.total_params_len]) |param_index| {
21032063 switch (tags[param_index]) {
......@@ -2121,7 +2081,7 @@ fn analyzeFunction(
21212081 });
21222082
21232083 param_type_refs.appendAssumeCapacity(
2124 DocData.TypeRef{ .@"anytype" = {} },
2084 DocData.WalkResult{ .@"anytype" = {} },
21252085 );
21262086 },
21272087 .param, .param_comptime => {
......@@ -2144,9 +2104,7 @@ fn analyzeFunction(
21442104 const break_operand = data[break_index].@"break".operand;
21452105 const param_type_ref = try self.walkRef(file, scope, break_operand);
21462106
2147 param_type_refs.appendAssumeCapacity(
2148 walkResultToTypeRef(param_type_ref),
2149 );
2107 param_type_refs.appendAssumeCapacity(param_type_ref);
21502108 },
21512109 }
21522110 }
......@@ -2156,18 +2114,18 @@ fn analyzeFunction(
21562114 const last_instr_index = fn_info.ret_ty_body[fn_info.ret_ty_body.len - 1];
21572115 const break_operand = data[last_instr_index].@"break".operand;
21582116 const wr = try self.walkRef(file, scope, break_operand);
2159 break :blk walkResultToTypeRef(wr);
2117 break :blk wr;
21602118 };
21612119
21622120 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] = .{
21642122 .Fn = .{
21652123 .name = "todo_name func",
21662124 .src = self_ast_node_index,
21672125 .params = param_type_refs.items,
21682126 .ret = ret_type_ref,
21692127 },
2170 });
2128 };
21712129 return DocData.WalkResult{ .type = self.types.items.len - 1 };
21722130}
21732131
......@@ -2176,7 +2134,7 @@ fn collectUnionFieldInfo(
21762134 file: *File,
21772135 scope: *Scope,
21782136 fields_len: usize,
2179 field_type_refs: *std.ArrayListUnmanaged(DocData.TypeRef),
2137 field_type_refs: *std.ArrayListUnmanaged(DocData.WalkResult),
21802138 field_name_indexes: *std.ArrayListUnmanaged(usize),
21812139 ei: usize,
21822140) !void {
......@@ -2222,10 +2180,7 @@ fn collectUnionFieldInfo(
22222180 // type
22232181 {
22242182 const walk_result = try self.walkRef(file, scope, field_type);
2225 try field_type_refs.append(
2226 self.arena,
2227 walkResultToTypeRef(walk_result),
2228 );
2183 try field_type_refs.append(self.arena, walk_result);
22292184 }
22302185
22312186 // ast node
......@@ -2248,7 +2203,7 @@ fn collectStructFieldInfo(
22482203 file: *File,
22492204 scope: *Scope,
22502205 fields_len: usize,
2251 field_type_refs: *std.ArrayListUnmanaged(DocData.TypeRef),
2206 field_type_refs: *std.ArrayListUnmanaged(DocData.WalkResult),
22522207 field_name_indexes: *std.ArrayListUnmanaged(usize),
22532208 ei: usize,
22542209) !void {
......@@ -2291,10 +2246,7 @@ fn collectStructFieldInfo(
22912246 // type
22922247 {
22932248 const walk_result = try self.walkRef(file, scope, field_type);
2294 try field_type_refs.append(
2295 self.arena,
2296 walkResultToTypeRef(walk_result),
2297 );
2249 try field_type_refs.append(self.arena, walk_result);
22982250 }
22992251
23002252 // ast node
......@@ -2334,17 +2286,24 @@ fn walkRef(
23342286 });
23352287 },
23362288 .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 };
23382293 },
23392294 .zero => {
2295 var t = try self.arena.create(DocData.WalkResult);
2296 t.* = .{ .type = @enumToInt(Ref.comptime_int_type) };
23402297 return DocData.WalkResult{ .int = .{
2341 .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) },
2298 .typeRef = t,
23422299 .value = 0,
23432300 } };
23442301 },
23452302 .one => {
2303 var t = try self.arena.create(DocData.WalkResult);
2304 t.* = .{ .type = @enumToInt(Ref.comptime_int_type) };
23462305 return DocData.WalkResult{ .int = .{
2347 .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) },
2306 .typeRef = t,
23482307 .value = 1,
23492308 } };
23502309 },
......@@ -2356,7 +2315,9 @@ fn walkRef(
23562315 return DocData.WalkResult{ .@"unreachable" = {} };
23572316 },
23582317 .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 };
23602321 },
23612322 .bool_true => {
23622323 return DocData.WalkResult{ .bool = true };
......@@ -2365,20 +2326,27 @@ fn walkRef(
23652326 return DocData.WalkResult{ .bool = false };
23662327 },
23672328 .empty_struct => {
2329 var t = try self.arena.create(DocData.WalkResult);
2330 t.* = .void;
2331
23682332 return DocData.WalkResult{ .@"struct" = .{
2369 .typeRef = .unspecified,
2333 .typeRef = t,
23702334 .fieldVals = &.{},
23712335 } };
23722336 },
23732337 .zero_usize => {
2338 var t = try self.arena.create(DocData.WalkResult);
2339 t.* = .{ .type = @enumToInt(Ref.usize_type) };
23742340 return DocData.WalkResult{ .int = .{
2375 .typeRef = .{ .type = @enumToInt(Ref.usize_type) },
2341 .typeRef = t,
23762342 .value = 0,
23772343 } };
23782344 },
23792345 .one_usize => {
2346 var t = try self.arena.create(DocData.WalkResult);
2347 t.* = .{ .type = @enumToInt(Ref.usize_type) };
23802348 return DocData.WalkResult{ .int = .{
2381 .typeRef = .{ .type = @enumToInt(Ref.usize_type) },
2349 .typeRef = t,
23822350 .value = 1,
23832351 } };
23842352 },
......@@ -2408,37 +2376,19 @@ fn walkRef(
24082376 }
24092377}
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
24292379/// Given a WalkResult, tries to find its type.
24302380/// Used to analyze instructions like `array_init`, which require us to
24312381/// inspect its first element to find out the array type.
2432fn typeOfWalkResult(wr: DocData.WalkResult) DocData.TypeRef {
2382fn typeOfWalkResult(wr: DocData.WalkResult) DocData.WalkResult {
24332383 return switch (wr) {
24342384 else => std.debug.panic(
24352385 "TODO: handle `{s}` in typeOfWalkResult\n",
24362386 .{@tagName(wr)},
24372387 ),
24382388 .type => .{ .type = @enumToInt(DocData.DocTypeKinds.Type) },
2439 .int => |v| v.typeRef,
2440 .float => |v| v.typeRef,
2441 .array => |v| v.typeRef,
2389 .int => |v| v.typeRef.*,
2390 .float => |v| v.typeRef.*,
2391 .array => |v| v.typeRef.*,
24422392 };
24432393}
24442394
......@@ -2454,3 +2404,12 @@ fn panicWithContext(file: *File, inst: usize, comptime fmt: []const u8, args: an
24542404 std.debug.print("Context [{s}] % {}\n", .{ file.sub_file_path, inst });
24552405 std.debug.panic(fmt, args);
24562406}
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}