zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 1014cfdf3b60faf9af5b062d198f4f44976cb1bc (tree)
parent 47dfaf3d175ebea63b169a373b4dec4af6af7001
Author: Andrew Kelley <andrew@ziglang.org>
Date:   Wed, 16 Oct 2019 01:29:16 -0400

generated docs: progress towards generic types being useful

See #3406

Diffstat:
Mlib/std/hash_map.zig | 3++-
Mlib/std/meta.zig | 10++++++++++
Mlib/std/special/docs/index.html | 15++++++++++++++-
Mlib/std/special/docs/main.js | 249+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/dump_analysis.cpp | 162++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mtools/merge_anal_dumps.zig | 173+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 527 insertions(+), 85 deletions(-)

diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig @@ -36,7 +36,8 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 size: usize, max_distance_from_start_index: usize, allocator: *Allocator, - // this is used to detect bugs where a hashtable is edited while an iterator is running. + + /// This is used to detect bugs where a hashtable is edited while an iterator is running. modification_count: debug_u32, const Self = @This(); diff --git a/lib/std/meta.zig b/lib/std/meta.zig @@ -542,3 +542,13 @@ pub fn intToEnum(comptime Tag: type, tag_int: var) IntToEnumError!Tag { } return error.InvalidEnumTag; } + +/// Given a type and a name, return the field index according to source order. +/// Returns `null` if the field is not found. +pub fn fieldIndex(comptime T: type, comptime name: []const u8) ?comptime_int { + inline for (fields(T)) |field, i| { + if (mem.eql(u8, field.name, name)) + return comptime_int(i); + } + return null; +} diff --git a/lib/std/special/docs/index.html b/lib/std/special/docs/index.html @@ -104,6 +104,16 @@ background-color: #FFBB4D; color: #000; } + #listFnExamples { + list-style-type: none; + margin: 0; + padding: 0; + } + #listFnExamples li { + padding: 0.5em 0; + white-space: nowrap; + overflow-x: auto; + } #logo { width: 8em; padding: 0.5em 1em; @@ -289,7 +299,6 @@ <pre id="fnProtoCode"></pre> </div> <h1 id="hdrName" class="hidden"></h1> - <div id="fnExamples" class="hidden"></div> <div id="fnNoExamples" class="hidden"> <p>This function is not tested or referenced.</p> </div> @@ -357,6 +366,10 @@ <ul id="listErrSets"> </ul> </div> + <div id="fnExamples" class="hidden"> + <h2>Examples</h2> + <ul id="listFnExamples"></ul> + </div> </section> <div id="helpDialog" class="hidden"> <h1>Keyboard Shortcuts</h1> diff --git a/lib/std/special/docs/main.js b/lib/std/special/docs/main.js @@ -26,6 +26,7 @@ var domTableFnErrors = document.getElementById("tableFnErrors"); var domFnErrorsAnyError = document.getElementById("fnErrorsAnyError"); var domFnExamples = document.getElementById("fnExamples"); + var domListFnExamples = document.getElementById("listFnExamples"); var domFnNoExamples = document.getElementById("fnNoExamples"); var domDeclNoRef = document.getElementById("declNoRef"); var domSearch = document.getElementById("search"); @@ -64,6 +65,9 @@ declNames: [], // these will be all types, except the last one may be a type or a decl declObjs: [], + + // (a, b, c, d) comptime call; result is the value the docs refer to + callName: null, }; var curNavSearch = ""; var curSearchIndex = -1; @@ -237,20 +241,29 @@ renderErrorSet(errSetType); } - var protoSrcIndex; + var fnObj = zigAnalysis.fns[fnDecl.value]; + var protoSrcIndex = fnObj.src; if (typeIsGenericFn(fnDecl.type)) { - protoSrcIndex = fnDecl.value; - var instantiations = nodesToFnsMap[protoSrcIndex]; var calls = nodesToCallsMap[protoSrcIndex]; if (instantiations == null && calls == null) { domFnNoExamples.classList.remove("hidden"); - } else { - // TODO show examples + } else if (calls != null) { + if (fnObj.combined === undefined) fnObj.combined = allCompTimeFnCallsResult(calls); + if (fnObj.combined != null) renderContainer(fnObj.combined); + + resizeDomList(domListFnExamples, calls.length, '<li></li>'); + + for (var callI = 0; callI < calls.length; callI += 1) { + var liDom = domListFnExamples.children[callI]; + liDom.innerHTML = getCallHtml(fnDecl, calls[callI]); + } + domFnExamples.classList.remove("hidden"); + } else if (instantiations != null) { + // TODO } } else { - protoSrcIndex = zigAnalysis.fns[fnDecl.value].src; domFnExamples.classList.add("hidden"); domFnNoExamples.classList.add("hidden"); @@ -349,13 +362,15 @@ } } - function navLink(pkgNames, declNames) { + function navLink(pkgNames, declNames, callName) { if (pkgNames.length === 0 && declNames.length === 0) { return '#'; - } else if (declNames.length === 0) { + } else if (declNames.length === 0 && callName == null) { return '#' + pkgNames.join('.'); - } else { + } else if (callName == null) { return '#' + pkgNames.join('.') + ';' + declNames.join('.'); + } else { + return '#' + pkgNames.join('.') + ';' + declNames.join('.') + ';' + callName; } } @@ -367,6 +382,22 @@ return navLink(curNav.pkgNames, curNav.declNames.concat([childName])); } + function navLinkCall(callObj) { + var declNamesCopy = curNav.declNames.concat([]); + var callName = declNamesCopy.pop(); + + callName += '('; + for (var arg_i = 0; arg_i < callObj.args.length; arg_i += 1) { + if (arg_i !== 0) callName += ','; + var argObj = callObj.args[arg_i]; + callName += getValueText(argObj.type, argObj.value, false, false); + } + callName += ')'; + + declNamesCopy.push(callName); + return navLink(curNav.pkgNames, declNamesCopy); + } + function resizeDomListDl(dlDom, desiredLen) { // add the missing dom entries var i, ev; @@ -426,6 +457,40 @@ return (typeObj.len == null) ? pointerSizeEnum.One : typeObj.len; } + function getCallHtml(fnDecl, callIndex) { + var callObj = zigAnalysis.calls[callIndex]; + + // TODO make these links work + //var html = '<a href="' + navLinkCall(callObj) + '">' + escapeHtml(fnDecl.name) + '</a>('; + var html = escapeHtml(fnDecl.name) + '('; + for (var arg_i = 0; arg_i < callObj.args.length; arg_i += 1) { + if (arg_i !== 0) html += ', '; + var argObj = callObj.args[arg_i]; + html += getValueText(argObj.type, argObj.value, true, true); + } + html += ')'; + return html; + } + + function getValueText(typeIndex, value, wantHtml, wantLink) { + var typeObj = zigAnalysis.types[typeIndex]; + switch (typeObj.kind) { + case typeKinds.Type: + return typeIndexName(value, wantHtml, wantLink); + case typeKinds.Fn: + var fnObj = zigAnalysis.fns[value]; + return typeIndexName(fnObj.type, wantHtml, wantLink); + case typeKinds.Int: + if (wantHtml) { + return '<span class="tok-number">' + value + '</span>'; + } else { + return value + ""; + } + default: + throw new Error("TODO implement getValueText for this type"); + } + } + function typeName(typeObj, wantHtml, wantSubLink, fnDecl, linkFnNameDecl) { switch (typeObj.kind) { case typeKinds.Array: @@ -544,6 +609,12 @@ } else { return "void"; } + case typeKinds.EnumLiteral: + if (wantHtml) { + return '<span class="tok-type">(enum literal)</span>'; + } else { + return "(enum literal)"; + } case typeKinds.NoReturn: if (wantHtml) { return '<span class="tok-type">noreturn</span>'; @@ -592,6 +663,15 @@ } payloadHtml += '('; if (typeObj.args != null) { + var fields = null; + var isVarArgs = false; + if (fnDecl != null) { + var fnObj = zigAnalysis.fns[fnDecl.value]; + var fnNode = zigAnalysis.astNodes[fnObj.src]; + fields = fnNode.fields; + isVarArgs = fnNode.varArgs; + } + for (var i = 0; i < typeObj.args.length; i += 1) { if (i != 0) { payloadHtml += ', '; @@ -599,10 +679,31 @@ var argTypeIndex = typeObj.args[i]; - if (fnDecl != null && zigAnalysis.astNodes[fnDecl.src].fields != null) { - var paramDeclIndex = zigAnalysis.astNodes[fnDecl.src].fields[i]; - var paramName = zigAnalysis.astNodes[paramDeclIndex].name; + if (fields != null) { + var paramNode = zigAnalysis.astNodes[fields[i]]; + + if (paramNode.varArgs) { + payloadHtml += '...'; + continue; + } + + if (paramNode.noalias) { + if (wantHtml) { + payloadHtml += '<span class="tok-kw">noalias</span> '; + } else { + payloadHtml += 'noalias '; + } + } + + if (paramNode.comptime) { + if (wantHtml) { + payloadHtml += '<span class="tok-kw">comptime</span> '; + } else { + payloadHtml += 'comptime '; + } + } + var paramName = paramNode.name; if (paramName != null) { // skip if it matches the type name if (argTypeIndex == null || !shouldSkipParamName(argTypeIndex, paramName)) { @@ -611,7 +712,9 @@ } } - if (argTypeIndex != null) { + if (isVarArgs && i === typeObj.args.length - 1) { + payloadHtml += '...'; + } else if (argTypeIndex != null) { payloadHtml += typeIndexName(argTypeIndex, wantHtml, wantSubLink); } else if (wantHtml) { payloadHtml += '<span class="tok-kw">var</span>'; @@ -690,7 +793,7 @@ } function allCompTimeFnCallsHaveTypeResult(typeIndex, value) { - var srcIndex = typeIsGenericFn(typeIndex) ? value : zigAnalysis.fns[value].src; + var srcIndex = zigAnalysis.fns[value].src; var calls = nodesToCallsMap[srcIndex]; if (calls == null) return false; for (var i = 0; i < calls.length; i += 1) { @@ -700,6 +803,90 @@ return true; } + function allCompTimeFnCallsResult(calls) { + var firstTypeObj = null; + var containerObj = { + privDecls: [], + }; + for (var callI = 0; callI < calls.length; callI += 1) { + var call = zigAnalysis.calls[calls[callI]]; + if (call.result.type !== typeTypeId) return null; + var typeObj = zigAnalysis.types[call.result.value]; + if (!typeKindIsContainer(typeObj.kind)) return null; + if (firstTypeObj == null) { + firstTypeObj = typeObj; + containerObj.src = typeObj.src; + } else if (firstTypeObj.src !== typeObj.src) { + return null; + } + + if (containerObj.fields == null) { + containerObj.fields = (typeObj.fields || []).concat([]); + } else for (var fieldI = 0; fieldI < typeObj.fields.length; fieldI += 1) { + var prev = containerObj.fields[fieldI]; + var next = typeObj.fields[fieldI]; + if (prev === next) continue; + if (typeof(prev) === 'object') { + if (prev[next] == null) prev[next] = typeObj; + } else { + containerObj.fields[fieldI] = {}; + containerObj.fields[fieldI][prev] = firstTypeObj; + containerObj.fields[fieldI][next] = typeObj; + } + } + + if (containerObj.pubDecls == null) { + containerObj.pubDecls = (typeObj.pubDecls || []).concat([]); + } else for (var declI = 0; declI < typeObj.pubDecls.length; declI += 1) { + var prev = containerObj.pubDecls[declI]; + var next = typeObj.pubDecls[declI]; + if (prev === next) continue; + // TODO instead of showing "examples" as the public declarations, + // do logic like this: + //if (typeof(prev) !== 'object') { + // var newDeclId = zigAnalysis.decls.length; + // prev = clone(zigAnalysis.decls[prev]); + // prev.id = newDeclId; + // zigAnalysis.decls.push(prev); + // containerObj.pubDecls[declI] = prev; + //} + //mergeDecls(prev, next, firstTypeObj, typeObj); + } + } + for (var declI = 0; declI < containerObj.pubDecls.length; declI += 1) { + var decl = containerObj.pubDecls[declI]; + if (typeof(decl) === 'object') { + containerObj.pubDecls[declI] = containerObj.pubDecls[declI].id; + } + } + return containerObj; + } + + function mergeDecls(declObj, nextDeclIndex, firstTypeObj, typeObj) { + var nextDeclObj = zigAnalysis.decls[nextDeclIndex]; + if (declObj.type != null && nextDeclObj.type != null && declObj.type !== nextDeclObj.type) { + if (typeof(declObj.type) !== 'object') { + var prevType = declObj.type; + declObj.type = {}; + declObj.type[prevType] = firstTypeObj; + declObj.value = null; + } + declObj.type[nextDeclObj.type] = typeObj; + } else if (declObj.type == null && nextDeclObj != null) { + declObj.type = nextDeclObj.type; + } + if (declObj.value != null && nextDeclObj.value != null && declObj.value !== nextDeclObj.value) { + if (typeof(declObj.value) !== 'object') { + var prevValue = declObj.value; + declObj.value = {}; + declObj.value[prevValue] = firstTypeObj; + } + declObj.value[nextDeclObj.value] = typeObj; + } else if (declObj.value == null && nextDeclObj.value != null) { + declObj.value = nextDeclObj.value; + } + } + function renderValue(decl) { domFnProtoCode.innerHTML = '<span class="tok-kw">const</span> ' + escapeHtml(decl.name) + ': ' + typeIndexName(decl.type, true, true); @@ -733,13 +920,15 @@ var fnsList = []; var varsList = []; var valsList = []; + for (var i = 0; i < container.pubDecls.length; i += 1) { var decl = zigAnalysis.decls[container.pubDecls[i]]; + if (decl.kind === 'var') { varsList.push(decl); continue; } else if (decl.kind === 'const' && decl.type != null) { - if (decl.type == typeTypeId) { + if (decl.type === typeTypeId) { if (typeIsErrSet(decl.value)) { errSetsList.push(decl); } else if (typeIsStructWithNoFields(decl.value)) { @@ -838,7 +1027,12 @@ if (container.kind === typeKinds.Enum) { html += ' = <span class="tok-number">' + field + '</span>'; } else { - html += ": " + typeIndexName(field, true, true); + html += ": "; + if (typeof(field) === 'object') { + html += '<span class="tok-kw">var</span>'; + } else { + html += typeIndexName(field, true, true); + } } html += ',</pre>'; @@ -1042,13 +1236,16 @@ return list; } - function declCanRepresentTypeKind(typeKind) { - return typeKind === typeKinds.ErrorSet || - typeKind === typeKinds.Struct || + function typeKindIsContainer(typeKind) { + return typeKind === typeKinds.Struct || typeKind === typeKinds.Union || typeKind === typeKinds.Enum; } + function declCanRepresentTypeKind(typeKind) { + return typeKind === typeKinds.ErrorSet || typeKindIsContainer(typeKind); + } + function computeCanonDeclPaths() { var list = new Array(zigAnalysis.decls.length); canonTypeDecls = new Array(zigAnalysis.types.length); @@ -1406,4 +1603,18 @@ function byNameProperty(a, b) { return operatorCompare(a.name, b.name); } + + function clone(obj) { + var res = {}; + for (var key in obj) { + res[key] = obj[key]; + } + return res; + } + + function firstObjectKey(obj) { + for (var key in obj) { + return key; + } + } })(); diff --git a/src/dump_analysis.cpp b/src/dump_analysis.cpp @@ -456,6 +456,10 @@ static uint32_t anal_dump_get_fn_id(AnalDumpCtx *ctx, ZigFn *fn) { auto existing_entry = ctx->fn_map.put_unique(fn, fn_id); if (existing_entry == nullptr) { ctx->fn_list.append(fn); + + // poke the fn + (void)anal_dump_get_type_id(ctx, fn->type_entry); + (void)anal_dump_get_node_id(ctx, fn->proto_node); } else { fn_id = existing_entry->value; } @@ -700,11 +704,7 @@ static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, case ZigTypeIdFn: { if (value->data.x_ptr.special == ConstPtrSpecialFunction) { ZigFn *val_fn = value->data.x_ptr.data.fn.fn_entry; - if (val_fn->type_entry->data.fn.is_generic) { - anal_dump_node_ref(ctx, val_fn->proto_node); - } else { - anal_dump_fn_ref(ctx, val_fn); - } + anal_dump_fn_ref(ctx, val_fn); } else { jw_null(&ctx->jw); } @@ -758,6 +758,7 @@ static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) { switch (ty->id) { case ZigTypeIdMetaType: case ZigTypeIdBool: + case ZigTypeIdEnumLiteral: break; case ZigTypeIdStruct: { if (ty->data.structure.is_slice) { @@ -1072,13 +1073,25 @@ static void anal_dump_node(AnalDumpCtx *ctx, const AstNode *node) { jw_object_field(jw, "col"); jw_int(jw, node->column); - const Buf *doc_comments_buf; + const Buf *doc_comments_buf = nullptr; + const Buf *name_buf = nullptr; + const ZigList<AstNode *> *field_nodes = nullptr; + bool is_var_args = false; + bool is_noalias = false; + bool is_comptime = false; + switch (node->type) { case NodeTypeParamDecl: doc_comments_buf = &node->data.param_decl.doc_comments; + name_buf = node->data.param_decl.name; + is_var_args = node->data.param_decl.is_var_args; + is_noalias = node->data.param_decl.is_noalias; + is_comptime = node->data.param_decl.is_comptime; break; case NodeTypeFnProto: doc_comments_buf = &node->data.fn_proto.doc_comments; + field_nodes = &node->data.fn_proto.params; + is_var_args = node->data.fn_proto.is_var_args; break; case NodeTypeVariableDeclaration: doc_comments_buf = &node->data.variable_declaration.doc_comments; @@ -1088,55 +1101,50 @@ static void anal_dump_node(AnalDumpCtx *ctx, const AstNode *node) { break; case NodeTypeStructField: doc_comments_buf = &node->data.struct_field.doc_comments; + name_buf = node->data.struct_field.name; + break; + case NodeTypeContainerDecl: + field_nodes = &node->data.container_decl.fields; break; default: - doc_comments_buf = nullptr; break; } + if (doc_comments_buf != nullptr && doc_comments_buf->list.length != 0) { jw_object_field(jw, "docs"); jw_string(jw, buf_ptr(doc_comments_buf)); } - const Buf *name_buf; - switch (node->type) { - case NodeTypeStructField: - name_buf = node->data.struct_field.name; - break; - case NodeTypeParamDecl: - name_buf = node->data.param_decl.name; - break; - default: - name_buf = nullptr; - break; - } if (name_buf != nullptr) { jw_object_field(jw, "name"); jw_string(jw, buf_ptr(name_buf)); } - const ZigList<AstNode *> *fieldNodes; - switch (node->type) { - case NodeTypeContainerDecl: - fieldNodes = &node->data.container_decl.fields; - break; - case NodeTypeFnProto: - fieldNodes = &node->data.fn_proto.params; - break; - default: - fieldNodes = nullptr; - break; - } - if (fieldNodes != nullptr) { + if (field_nodes != nullptr) { jw_object_field(jw, "fields"); jw_begin_array(jw); - for (size_t i = 0; i < fieldNodes->length; i += 1) { + for (size_t i = 0; i < field_nodes->length; i += 1) { jw_array_elem(jw); - anal_dump_node_ref(ctx, fieldNodes->at(i)); + anal_dump_node_ref(ctx, field_nodes->at(i)); } jw_end_array(jw); } + if (is_var_args) { + jw_object_field(jw, "varArgs"); + jw_bool(jw, true); + } + + if (is_comptime) { + jw_object_field(jw, "comptime"); + jw_bool(jw, true); + } + + if (is_noalias) { + jw_object_field(jw, "noalias"); + jw_bool(jw, true); + } + jw_end_object(jw); } @@ -1235,59 +1243,76 @@ void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const jw_object_field(jw, "calls"); jw_begin_array(jw); { + ZigList<ZigVar *> var_stack = {}; + auto it = g->memoized_fn_eval_table.entry_iterator(); for (;;) { auto *entry = it.next(); if (!entry) break; - jw_array_elem(jw); - jw_begin_object(jw); - - jw_object_field(jw, "args"); - jw_begin_object(jw); + var_stack.resize(0); + ZigFn *fn = nullptr; Scope *scope = entry->key; while (scope != nullptr) { if (scope->id == ScopeIdVarDecl) { ZigVar *var = reinterpret_cast<ScopeVarDecl *>(scope)->var; - jw_object_field(jw, var->name); - jw_begin_object(jw); - jw_object_field(jw, "type"); - anal_dump_type_ref(&ctx, var->var_type); - jw_object_field(jw, "value"); - anal_dump_value(&ctx, scope->source_node, var->var_type, var->const_value); - jw_end_object(jw); + var_stack.append(var); } else if (scope->id == ScopeIdFnDef) { - jw_end_object(jw); + fn = reinterpret_cast<ScopeFnDef *>(scope)->fn_entry; + break; + } + scope = scope->parent; + } + ConstExprValue *result = entry->value; + + assert(fn != nullptr); + + jw_array_elem(jw); + jw_begin_object(jw); + + jw_object_field(jw, "fn"); + anal_dump_fn_ref(&ctx, fn); + + jw_object_field(jw, "result"); + { + jw_begin_object(jw); + + jw_object_field(jw, "type"); + anal_dump_type_ref(&ctx, result->type); + + jw_object_field(jw, "value"); + anal_dump_value(&ctx, scope->source_node, result->type, result); + + jw_end_object(jw); + } + + if (var_stack.length != 0) { + jw_object_field(jw, "args"); + jw_begin_array(jw); - jw_object_field(jw, "fn"); - ZigFn *fn = reinterpret_cast<ScopeFnDef *>(scope)->fn_entry; - anal_dump_fn_ref(&ctx, fn); + while (var_stack.length != 0) { + ZigVar *var = var_stack.pop(); - ConstExprValue *result = entry->value; - jw_object_field(jw, "result"); + jw_array_elem(jw); jw_begin_object(jw); + jw_object_field(jw, "type"); - anal_dump_type_ref(&ctx, result->type); + anal_dump_type_ref(&ctx, var->var_type); + jw_object_field(jw, "value"); - anal_dump_value(&ctx, scope->source_node, result->type, result); + anal_dump_value(&ctx, scope->source_node, var->var_type, var->const_value); + jw_end_object(jw); - break; } - scope = scope->parent; + jw_end_array(jw); } + jw_end_object(jw); } - } - jw_end_array(jw); - jw_object_field(jw, "fns"); - jw_begin_array(jw); - for (uint32_t i = 0; i < ctx.fn_list.length; i += 1) { - ZigFn *fn = ctx.fn_list.at(i); - jw_array_elem(jw); - anal_dump_fn(&ctx, fn); + var_stack.deinit(); } jw_end_array(jw); @@ -1315,6 +1340,15 @@ void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const } jw_end_array(jw); + jw_object_field(jw, "fns"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ctx.fn_list.length; i += 1) { + ZigFn *fn = ctx.fn_list.at(i); + jw_array_elem(jw); + anal_dump_fn(&ctx, fn); + } + jw_end_array(jw); + jw_object_field(jw, "errors"); jw_begin_array(jw); for (uint32_t i = 0; i < ctx.err_list.length; i += 1) { diff --git a/tools/merge_anal_dumps.zig b/tools/merge_anal_dumps.zig @@ -2,6 +2,8 @@ const builtin = @import("builtin"); const std = @import("std"); const json = std.json; const mem = std.mem; +const fieldIndex = std.meta.fieldIndex; +const TypeId = builtin.TypeId; pub fn main() anyerror!void { var arena = std.heap.ArenaAllocator.init(std.heap.direct_allocator); @@ -61,6 +63,81 @@ const Error = struct { } }; +const simple_types = [_][]const u8{ + "Type", + "Void", + "Bool", + "NoReturn", + "ComptimeFloat", + "ComptimeInt", + "Undefined", + "Null", + "AnyFrame", + "EnumLiteral", +}; + +const Type = union(builtin.TypeId) { + Type, + Void, + Bool, + NoReturn, + ComptimeFloat, + ComptimeInt, + Undefined, + Null, + AnyFrame, + EnumLiteral, + + Int: Int, + Float: usize, // bits + + Vector: Array, + Optional: usize, // payload type index + Pointer: Pointer, + Array: Array, + + Struct, // TODO + ErrorUnion, // TODO + ErrorSet, // TODO + Enum, // TODO + Union, // TODO + Fn, // TODO + BoundFn, // TODO + ArgTuple, // TODO + Opaque, // TODO + Frame, // TODO + + const Int = struct { + bits: usize, + signed: bool, + }; + + const Pointer = struct { + elem: usize, + alignment: usize, + is_const: bool, + is_volatile: bool, + allow_zero: bool, + host_int_bytes: usize, + bit_offset_in_host: usize, + }; + + const Array = struct { + elem: usize, + len: usize, + }; + + fn hash(t: Type) u32 { + var hasher = std.hash.Wyhash.init(0); + std.hash.autoHash(&hasher, builtin.TypeId(t)); + return @truncate(u32, hasher.final()); + } + + fn eql(a: Type, b: Type) bool { + return std.meta.eql(a, b); + } +}; + const Dump = struct { zig_id: ?[]const u8 = null, zig_version: ?[]const u8 = null, @@ -79,6 +156,10 @@ const Dump = struct { error_list: std.ArrayList(Error), error_map: ErrorMap, + const TypeMap = std.HashMap(Type, usize, Type.hash, Type.eql); + type_list: std.ArrayList(Type), + type_map: TypeMap, + fn init(allocator: *mem.Allocator) Dump { return Dump{ .targets = std.ArrayList([]const u8).init(allocator), @@ -88,6 +169,8 @@ const Dump = struct { .node_map = NodeMap.init(allocator), .error_list = std.ArrayList(Error).init(allocator), .error_map = ErrorMap.init(allocator), + .type_list = std.ArrayList(Type).init(allocator), + .type_map = TypeMap.init(allocator), }; } @@ -165,6 +248,66 @@ const Dump = struct { } try other_error_to_mine.putNoClobber(i, gop.kv.value); } + + // Merge types. Now it starts to get advanced. + // First we identify all the simple types and merge those. + // Example: void, type, noreturn + // We can also do integers and floats. + const other_types = root.Object.get("types").?.value.Array.toSliceConst(); + var other_types_to_mine = std.AutoHashMap(usize, usize).init(self.a()); + for (other_types) |other_type_json, i| { + const type_kind = jsonObjInt(other_type_json, "kind"); + switch (type_kind) { + fieldIndex(TypeId, "Int").? => { + var signed: bool = undefined; + var bits: usize = undefined; + if (other_type_json.Object.get("i")) |kv| { + signed = true; + bits = @intCast(usize, kv.value.Integer); + } else if (other_type_json.Object.get("u")) |kv| { + signed = false; + bits = @intCast(usize, kv.value.Integer); + } else { + unreachable; + } + const other_type = Type{ + .Int = Type.Int{ + .bits = bits, + .signed = signed, + }, + }; + try self.mergeOtherType(other_type, i, &other_types_to_mine); + }, + fieldIndex(TypeId, "Float").? => { + const other_type = Type{ + .Float = jsonObjInt(other_type_json, "bits"), + }; + try self.mergeOtherType(other_type, i, &other_types_to_mine); + }, + else => {}, + } + + inline for (simple_types) |simple_type_name| { + if (type_kind == std.meta.fieldIndex(builtin.TypeId, simple_type_name).?) { + const other_type = @unionInit(Type, simple_type_name, {}); + try self.mergeOtherType(other_type, i, &other_types_to_mine); + } + } + } + } + + fn mergeOtherType( + self: *Dump, + other_type: Type, + other_type_index: usize, + other_types_to_mine: *std.AutoHashMap(usize, usize), + ) !void { + const gop = try self.type_map.getOrPut(other_type); + if (!gop.found_existing) { + gop.kv.value = self.type_list.len; + try self.type_list.append(other_type); + } + try other_types_to_mine.putNoClobber(other_type_index, gop.kv.value); } fn render(self: *Dump, stream: var) !void { @@ -204,6 +347,36 @@ const Dump = struct { try jw.endObject(); + try jw.objectField("types"); + try jw.beginArray(); + for (self.type_list.toSliceConst()) |t| { + try jw.arrayElem(); + try jw.beginObject(); + + try jw.objectField("kind"); + try jw.emitNumber(@enumToInt(builtin.TypeId(t))); + + switch (t) { + .Int => |int| { + if (int.signed) { + try jw.objectField("i"); + } else { + try jw.objectField("u"); + } + try jw.emitNumber(int.bits); + }, + .Float => |bits| { + try jw.objectField("bits"); + try jw.emitNumber(bits); + }, + + else => {}, + } + + try jw.endObject(); + } + try jw.endArray(); + try jw.objectField("errors"); try jw.beginArray(); for (self.error_list.toSliceConst()) |zig_error| {