generated docs: progress towards generic types being useful

See #3406
This commit is contained in:
Andrew Kelley 2019-10-16 01:29:16 -04:00
parent 47dfaf3d17
commit 1014cfdf3b
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
6 changed files with 531 additions and 89 deletions

View File

@ -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();

View File

@ -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;
}

View File

@ -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>

View File

@ -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;
}
}
})();

View File

@ -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);
jw_object_field(jw, "fn");
ZigFn *fn = reinterpret_cast<ScopeFnDef *>(scope)->fn_entry;
anal_dump_fn_ref(&ctx, fn);
ConstExprValue *result = entry->value;
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);
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);
while (var_stack.length != 0) {
ZigVar *var = var_stack.pop();
jw_array_elem(jw);
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);
}
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) {

View File

@ -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| {