From 0482e8ba9d63ea009a9e0c2d497371261764d3e1 Mon Sep 17 00:00:00 2001 From: Loris Cro Date: Sun, 21 Aug 2022 18:53:00 +0200 Subject: [PATCH 1/2] autodoc: initial support for struct_init_anon --- lib/docs/main.js | 27 +++++---- src/Autodoc.zig | 152 +++++++++++++++++++++++++++++++++++------------ 2 files changed, 130 insertions(+), 49 deletions(-) diff --git a/lib/docs/main.js b/lib/docs/main.js index 5a3572a629..8f6bbcedd2 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -1430,20 +1430,25 @@ var zigAnalysis; return lhs + "!" + rhs; } case "struct": { - const struct_name = - zigAnalysis.decls[expr.struct[0].val.typeRef.refPath[0].declRef].name; + // const struct_name = + // zigAnalysis.decls[expr.struct[0].val.typeRef.refPath[0].declRef].name; + const struct_name = "."; let struct_body = ""; struct_body += struct_name + "{ "; for (let i = 0; i < expr.struct.length; i++) { - const val = expr.struct[i].name; - const exprArg = zigAnalysis.exprs[expr.struct[i].val.expr.as.exprArg]; - let value_field = exprArg[Object.keys(exprArg)[0]]; - if (value_field instanceof Object) { - value_field = - zigAnalysis.decls[value_field[0].val.typeRef.refPath[0].declRef] - .name; - } - struct_body += "." + val + " = " + value_field; + const fv = expr.struct[i]; + const field_name = fv.name; + const exprArg = zigAnalysis.exprs[fv.val.expr.as.exprArg]; + let field_value = exprName(exprArg, opts); + // TODO: commented out because it seems not needed. if it deals + // with a corner case, please add a comment when re-enabling it. + // let field_value = exprArg[Object.keys(exprArg)[0]]; + // if (field_value instanceof Object) { + // value_field = exprName(value_field) + // zigAnalysis.decls[value_field[0].val.typeRef.refPath[0].declRef] + // .name; + // } + struct_body += "." + field_name + " = " + field_value; if (i !== expr.struct.length - 1) { struct_body += ", "; } else { diff --git a/src/Autodoc.zig b/src/Autodoc.zig index f983bdfdf0..128e09faf2 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -69,6 +69,8 @@ pub fn generateZirData(self: *Autodoc) !void { } } + log.debug("Ref map size: {}", .{Ref.typed_value_map.len}); + const root_src_dir = self.module.main_pkg.root_src_directory; const root_src_path = self.module.main_pkg.root_src_path; const joined_src_path = try root_src_dir.join(self.arena, &.{root_src_path}); @@ -159,6 +161,9 @@ pub fn generateZirData(self: *Autodoc) !void { .void_type => .{ .Void = .{ .name = tmpbuf.toOwnedSlice() }, }, + .type_info_type => .{ + .Unanalyzed = .{}, + }, .type_type => .{ .Type = .{ .name = tmpbuf.toOwnedSlice() }, }, @@ -608,6 +613,7 @@ const DocData = struct { type: usize, // index in `types` this: usize, // index in `types` declRef: usize, // index in `decls` + builtinField: enum { len, ptr }, fieldRef: FieldRef, refPath: []Expr, int: struct { @@ -697,20 +703,26 @@ const DocData = struct { var jsw = std.json.writeStream(w, 15); try jsw.beginObject(); try jsw.objectField(@tagName(active_tag)); - inline for (comptime std.meta.fields(Expr)) |case| { - if (@field(Expr, case.name) == active_tag) { - switch (active_tag) { - .int => { - if (self.int.negated) try w.writeAll("-"); - try jsw.emitNumber(self.int.value); - }, - .int_big => { + switch (self) { + .int => { + if (self.int.negated) try w.writeAll("-"); + try jsw.emitNumber(self.int.value); + }, + .int_big => { - //@panic("TODO: json serialization of big ints!"); - //if (v.negated) try w.writeAll("-"); - //try jsw.emitNumber(v.value); - }, - else => { + //@panic("TODO: json serialization of big ints!"); + //if (v.negated) try w.writeAll("-"); + //try jsw.emitNumber(v.value); + }, + .builtinField => { + try jsw.emitString(@tagName(self.builtinField)); + }, + else => { + inline for (comptime std.meta.fields(Expr)) |case| { + // TODO: this is super ugly, fix once `inline else` is a thing + if (comptime std.mem.eql(u8, case.name, "builtinField")) + continue; + if (@field(Expr, case.name) == active_tag) { try std.json.stringify(@field(self, case.name), opt, w); jsw.state_index -= 1; // TODO: we should not reach into the state of the @@ -719,9 +731,9 @@ const DocData = struct { // would be nice to have a proper integration // between the json writer and the generic // std.json.stringify implementation - }, + } } - } + }, } try jsw.endObject(); } @@ -1905,31 +1917,38 @@ fn walkInstruction( const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index); var path: std.ArrayListUnmanaged(DocData.Expr) = .{}; - var lhs = @enumToInt(extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs - try path.append(self.arena, .{ .string = file.zir.nullTerminatedString(extra.data.field_name_start), }); - // Put inside path the starting index of each decl name that - // we encounter as we navigate through all the field_vals - while (tags[lhs] == .field_val or - tags[lhs] == .field_call_bind or - tags[lhs] == .field_ptr or - tags[lhs] == .field_type) - { - const lhs_extra = file.zir.extraData( - Zir.Inst.Field, - data[lhs].pl_node.payload_index, - ); - try path.append(self.arena, .{ - .string = file.zir.nullTerminatedString(lhs_extra.data.field_name_start), - }); - lhs = @enumToInt(lhs_extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs - } + // Put inside path the starting index of each decl name that + // we encounter as we navigate through all the field_*s + const lhs_ref = blk: { + var lhs_extra = extra; + while (true) { + if (@enumToInt(lhs_extra.data.lhs) < Ref.typed_value_map.len) { + break :blk lhs_extra.data.lhs; + } + + const lhs = @enumToInt(lhs_extra.data.lhs) - Ref.typed_value_map.len; + if (tags[lhs] != .field_val and + tags[lhs] != .field_call_bind and + tags[lhs] != .field_ptr and + tags[lhs] != .field_type) break :blk lhs_extra.data.lhs; + + lhs_extra = file.zir.extraData( + Zir.Inst.Field, + data[lhs].pl_node.payload_index, + ); + + try path.append(self.arena, .{ + .string = file.zir.nullTerminatedString(lhs_extra.data.field_name_start), + }); + } + }; // TODO: double check that we really don't need type info here - const wr = try self.walkInstruction(file, parent_scope, lhs, false); + const wr = try self.walkRef(file, parent_scope, lhs_ref, false); try path.append(self.arena, wr.expr); // This way the data in `path` has the same ordering that the ref @@ -1948,7 +1967,7 @@ fn walkInstruction( // - (2) Paths can sometimes never resolve fully. This means that // any value that depends on that will have to become a // comptimeExpr. - try self.tryResolveRefPath(file, lhs, path.items); + try self.tryResolveRefPath(file, inst_index, path.items); return DocData.WalkResult{ .expr = .{ .refPath = path.items } }; }, .int_type => { @@ -2053,6 +2072,46 @@ fn walkInstruction( ); return self.cteTodo(@tagName(tags[inst_index])); }, + .struct_init_anon => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.StructInitAnon, pl_node.payload_index); + + const field_vals = try self.arena.alloc( + DocData.Expr.FieldVal, + extra.data.fields_len, + ); + + log.debug("number of fields: {}", .{extra.data.fields_len}); + var idx = extra.end; + for (field_vals) |*fv| { + const init_extra = file.zir.extraData(Zir.Inst.StructInitAnon.Item, idx); + const field_name = file.zir.nullTerminatedString(init_extra.data.field_name); + fv.* = .{ + .name = field_name, + .val = DocData.WalkResult{ + .expr = .{ .comptimeExpr = 0 }, + }, + }; + // printWithContext( + // file, + // inst_index, + // "analyzing field [{}] %{} `{s}`", + // .{ i, init_extra.data.init, field_name }, + // ); + // const value = try self.walkRef( + // file, + // parent_scope, + // init_extra.data.init, + // need_type, + // ); + // fv.* = .{ .name = field_name, .val = value }; + // idx = init_extra.end; + } + + return DocData.WalkResult{ + .expr = .{ .@"struct" = field_vals }, + }; + }, .error_set_decl => { const pl_node = data[inst_index].pl_node; const extra = file.zir.extraData(Zir.Inst.ErrorSetDecl, pl_node.payload_index); @@ -3094,6 +3153,20 @@ fn tryResolveRefPath( return; }, + .Array => { + if (std.mem.eql(u8, child_string, "len")) { + path[i + 1] = .{ + .builtinField = .len, + }; + } else { + panicWithContext( + file, + inst_index, + "TODO: handle `{s}` in tryResolveDeclPath.type.Array\nInfo: {}", + .{ child_string, resolved_parent }, + ); + } + }, .Enum => |t_enum| { for (t_enum.pubDecls) |d| { // TODO: this could be improved a lot @@ -3820,9 +3893,12 @@ fn walkRef( } else if (enum_value < Ref.typed_value_map.len) { switch (ref) { else => { - std.debug.panic("TODO: handle {s} in `walkRef`\n", .{ - @tagName(ref), - }); + panicWithContext( + file, + 0, + "TODO: handle {s} in walkRef", + .{@tagName(ref)}, + ); }, .undef => { return DocData.WalkResult{ .expr = .@"undefined" }; From b32e5a14ce20ae2943576de76d1af4c8bcc9dd72 Mon Sep 17 00:00:00 2001 From: Loris Cro Date: Tue, 23 Aug 2022 17:37:06 +0200 Subject: [PATCH 2/2] autodoc: handle self-referential call+field_type instructions --- lib/docs/main.js | 3 +-- src/Autodoc.zig | 59 +++++++++++++++++++++++++++++------------------- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/lib/docs/main.js b/lib/docs/main.js index 8f6bbcedd2..47bbc9591a 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -1438,8 +1438,7 @@ var zigAnalysis; for (let i = 0; i < expr.struct.length; i++) { const fv = expr.struct[i]; const field_name = fv.name; - const exprArg = zigAnalysis.exprs[fv.val.expr.as.exprArg]; - let field_value = exprName(exprArg, opts); + const field_value = exprName(fv.val.expr, opts); // TODO: commented out because it seems not needed. if it deals // with a corner case, please add a comment when re-enabling it. // let field_value = exprArg[Object.keys(exprArg)[0]]; diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 128e09faf2..f8560407fe 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -162,7 +162,7 @@ pub fn generateZirData(self: *Autodoc) !void { .Void = .{ .name = tmpbuf.toOwnedSlice() }, }, .type_info_type => .{ - .Unanalyzed = .{}, + .ComptimeExpr = .{ .name = tmpbuf.toOwnedSlice() }, }, .type_type => .{ .Type = .{ .name = tmpbuf.toOwnedSlice() }, @@ -1947,8 +1947,30 @@ fn walkInstruction( } }; + // If the lhs is a `call` instruction, it means that we're inside + // a function call and we're referring to one of its arguments. + // We can't just blindly analyze the instruction or we will + // start recursing forever. + // TODO: add proper resolution of the container type for `calls` + // TODO: we're like testing lhs as an instruction twice + // (above and below) this todo, maybe a cleaer solution woul + // avoid that. // TODO: double check that we really don't need type info here - const wr = try self.walkRef(file, parent_scope, lhs_ref, false); + + const wr = blk: { + if (@enumToInt(lhs_ref) >= Ref.typed_value_map.len) { + const lhs_inst = @enumToInt(lhs_ref) - Ref.typed_value_map.len; + if (tags[lhs_inst] == .call) { + break :blk DocData.WalkResult{ + .expr = .{ + .comptimeExpr = 0, + }, + }; + } + } + + break :blk try self.walkRef(file, parent_scope, lhs_ref, false); + }; try path.append(self.arena, wr.expr); // This way the data in `path` has the same ordering that the ref @@ -2081,31 +2103,18 @@ fn walkInstruction( extra.data.fields_len, ); - log.debug("number of fields: {}", .{extra.data.fields_len}); var idx = extra.end; for (field_vals) |*fv| { const init_extra = file.zir.extraData(Zir.Inst.StructInitAnon.Item, idx); const field_name = file.zir.nullTerminatedString(init_extra.data.field_name); - fv.* = .{ - .name = field_name, - .val = DocData.WalkResult{ - .expr = .{ .comptimeExpr = 0 }, - }, - }; - // printWithContext( - // file, - // inst_index, - // "analyzing field [{}] %{} `{s}`", - // .{ i, init_extra.data.init, field_name }, - // ); - // const value = try self.walkRef( - // file, - // parent_scope, - // init_extra.data.init, - // need_type, - // ); - // fv.* = .{ .name = field_name, .val = value }; - // idx = init_extra.end; + const value = try self.walkRef( + file, + parent_scope, + init_extra.data.init, + need_type, + ); + fv.* = .{ .name = field_name, .val = value }; + idx = init_extra.end; } return DocData.WalkResult{ @@ -3131,6 +3140,10 @@ fn tryResolveRefPath( .{ @tagName(self.types.items[t_index]), resolved_parent }, ); }, + .ComptimeExpr => { + // Same as the comptimeExpr branch above + break :outer; + }, .Unanalyzed => { // This decl path is pending completion {