self-hosted: watch files and trigger a rebuild
This commit is contained in:
parent
7f6e97cb26
commit
5dfcd09e49
@ -230,6 +230,8 @@ pub const Compilation = struct {
|
|||||||
|
|
||||||
c_int_types: [CInt.list.len]*Type.Int,
|
c_int_types: [CInt.list.len]*Type.Int,
|
||||||
|
|
||||||
|
fs_watch: *fs.Watch(*Scope.Root),
|
||||||
|
|
||||||
const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
|
const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
|
||||||
const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
|
const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
|
||||||
const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
|
const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
|
||||||
@ -285,6 +287,7 @@ pub const Compilation = struct {
|
|||||||
LibCMissingDynamicLinker,
|
LibCMissingDynamicLinker,
|
||||||
InvalidDarwinVersionString,
|
InvalidDarwinVersionString,
|
||||||
UnsupportedLinkArchitecture,
|
UnsupportedLinkArchitecture,
|
||||||
|
UserResourceLimitReached,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Event = union(enum) {
|
pub const Event = union(enum) {
|
||||||
@ -331,7 +334,8 @@ pub const Compilation = struct {
|
|||||||
zig_lib_dir: []const u8,
|
zig_lib_dir: []const u8,
|
||||||
) !*Compilation {
|
) !*Compilation {
|
||||||
const loop = event_loop_local.loop;
|
const loop = event_loop_local.loop;
|
||||||
const comp = try event_loop_local.loop.allocator.create(Compilation{
|
const comp = try event_loop_local.loop.allocator.createOne(Compilation);
|
||||||
|
comp.* = Compilation{
|
||||||
.loop = loop,
|
.loop = loop,
|
||||||
.arena_allocator = std.heap.ArenaAllocator.init(loop.allocator),
|
.arena_allocator = std.heap.ArenaAllocator.init(loop.allocator),
|
||||||
.event_loop_local = event_loop_local,
|
.event_loop_local = event_loop_local,
|
||||||
@ -376,7 +380,7 @@ pub const Compilation = struct {
|
|||||||
.fn_link_set = event.Locked(FnLinkSet).init(loop, FnLinkSet.init()),
|
.fn_link_set = event.Locked(FnLinkSet).init(loop, FnLinkSet.init()),
|
||||||
.windows_subsystem_windows = false,
|
.windows_subsystem_windows = false,
|
||||||
.windows_subsystem_console = false,
|
.windows_subsystem_console = false,
|
||||||
.link_libs_list = undefined,
|
.link_libs_list = ArrayList(*LinkLib).init(comp.arena()),
|
||||||
.libc_link_lib = null,
|
.libc_link_lib = null,
|
||||||
.err_color = errmsg.Color.Auto,
|
.err_color = errmsg.Color.Auto,
|
||||||
.darwin_frameworks = [][]const u8{},
|
.darwin_frameworks = [][]const u8{},
|
||||||
@ -417,8 +421,10 @@ pub const Compilation = struct {
|
|||||||
.override_libc = null,
|
.override_libc = null,
|
||||||
.destroy_handle = undefined,
|
.destroy_handle = undefined,
|
||||||
.have_err_ret_tracing = false,
|
.have_err_ret_tracing = false,
|
||||||
.primitive_type_table = undefined,
|
.primitive_type_table = TypeTable.init(comp.arena()),
|
||||||
});
|
|
||||||
|
.fs_watch = undefined,
|
||||||
|
};
|
||||||
errdefer {
|
errdefer {
|
||||||
comp.int_type_table.private_data.deinit();
|
comp.int_type_table.private_data.deinit();
|
||||||
comp.array_type_table.private_data.deinit();
|
comp.array_type_table.private_data.deinit();
|
||||||
@ -431,9 +437,7 @@ pub const Compilation = struct {
|
|||||||
comp.name = try Buffer.init(comp.arena(), name);
|
comp.name = try Buffer.init(comp.arena(), name);
|
||||||
comp.llvm_triple = try target.getTriple(comp.arena());
|
comp.llvm_triple = try target.getTriple(comp.arena());
|
||||||
comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);
|
comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);
|
||||||
comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
|
|
||||||
comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std");
|
comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std");
|
||||||
comp.primitive_type_table = TypeTable.init(comp.arena());
|
|
||||||
|
|
||||||
const opt_level = switch (build_mode) {
|
const opt_level = switch (build_mode) {
|
||||||
builtin.Mode.Debug => llvm.CodeGenLevelNone,
|
builtin.Mode.Debug => llvm.CodeGenLevelNone,
|
||||||
@ -485,6 +489,9 @@ pub const Compilation = struct {
|
|||||||
comp.root_package = try Package.create(comp.arena(), ".", "");
|
comp.root_package = try Package.create(comp.arena(), ".", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
comp.fs_watch = try fs.Watch(*Scope.Root).create(loop, 16);
|
||||||
|
errdefer comp.fs_watch.destroy();
|
||||||
|
|
||||||
try comp.initTypes();
|
try comp.initTypes();
|
||||||
|
|
||||||
comp.destroy_handle = try async<loop.allocator> comp.internalDeinit();
|
comp.destroy_handle = try async<loop.allocator> comp.internalDeinit();
|
||||||
@ -686,6 +693,7 @@ pub const Compilation = struct {
|
|||||||
os.deleteTree(self.arena(), tmp_dir) catch {};
|
os.deleteTree(self.arena(), tmp_dir) catch {};
|
||||||
} else |_| {};
|
} else |_| {};
|
||||||
|
|
||||||
|
self.fs_watch.destroy();
|
||||||
self.events.destroy();
|
self.events.destroy();
|
||||||
|
|
||||||
llvm.DisposeMessage(self.target_layout_str);
|
llvm.DisposeMessage(self.target_layout_str);
|
||||||
@ -720,7 +728,9 @@ pub const Compilation = struct {
|
|||||||
var build_result = await (async self.initialCompile() catch unreachable);
|
var build_result = await (async self.initialCompile() catch unreachable);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const link_result = if (build_result) self.maybeLink() else |err| err;
|
const link_result = if (build_result) blk: {
|
||||||
|
break :blk await (async self.maybeLink() catch unreachable);
|
||||||
|
} else |err| err;
|
||||||
// this makes a handy error return trace and stack trace in debug mode
|
// this makes a handy error return trace and stack trace in debug mode
|
||||||
if (std.debug.runtime_safety) {
|
if (std.debug.runtime_safety) {
|
||||||
link_result catch unreachable;
|
link_result catch unreachable;
|
||||||
@ -745,9 +755,35 @@ pub const Compilation = struct {
|
|||||||
await (async self.events.put(Event{ .Error = err }) catch unreachable);
|
await (async self.events.put(Event{ .Error = err }) catch unreachable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// First, get an item from the watch channel, waiting on the channel.
|
||||||
var group = event.Group(BuildError!void).init(self.loop);
|
var group = event.Group(BuildError!void).init(self.loop);
|
||||||
while (self.fs_watch.channel.getOrNull()) |root_scope| {
|
{
|
||||||
try group.call(rebuildFile, self, root_scope);
|
const ev = await (async self.fs_watch.channel.get() catch unreachable);
|
||||||
|
const root_scope = switch (ev) {
|
||||||
|
fs.Watch(*Scope.Root).Event.CloseWrite => |x| x,
|
||||||
|
fs.Watch(*Scope.Root).Event.Err => |err| {
|
||||||
|
build_result = err;
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
group.call(rebuildFile, self, root_scope) catch |err| {
|
||||||
|
build_result = err;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Next, get all the items from the channel that are buffered up.
|
||||||
|
while (await (async self.fs_watch.channel.getOrNull() catch unreachable)) |ev| {
|
||||||
|
const root_scope = switch (ev) {
|
||||||
|
fs.Watch(*Scope.Root).Event.CloseWrite => |x| x,
|
||||||
|
fs.Watch(*Scope.Root).Event.Err => |err| {
|
||||||
|
build_result = err;
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
group.call(rebuildFile, self, root_scope) catch |err| {
|
||||||
|
build_result = err;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
build_result = await (async group.wait() catch unreachable);
|
build_result = await (async group.wait() catch unreachable);
|
||||||
}
|
}
|
||||||
@ -757,11 +793,11 @@ pub const Compilation = struct {
|
|||||||
const tree_scope = blk: {
|
const tree_scope = blk: {
|
||||||
const source_code = (await (async fs.readFile(
|
const source_code = (await (async fs.readFile(
|
||||||
self.loop,
|
self.loop,
|
||||||
root_src_real_path,
|
root_scope.realpath,
|
||||||
max_src_size,
|
max_src_size,
|
||||||
) catch unreachable)) catch |err| {
|
) catch unreachable)) catch |err| {
|
||||||
try printError("unable to open '{}': {}", root_src_real_path, err);
|
try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", @errorName(err));
|
||||||
return err;
|
return;
|
||||||
};
|
};
|
||||||
errdefer self.gpa().free(source_code);
|
errdefer self.gpa().free(source_code);
|
||||||
|
|
||||||
@ -793,9 +829,9 @@ pub const Compilation = struct {
|
|||||||
var decl_group = event.Group(BuildError!void).init(self.loop);
|
var decl_group = event.Group(BuildError!void).init(self.loop);
|
||||||
defer decl_group.deinit();
|
defer decl_group.deinit();
|
||||||
|
|
||||||
try self.rebuildChangedDecls(
|
try await try async self.rebuildChangedDecls(
|
||||||
&decl_group,
|
&decl_group,
|
||||||
locked_table,
|
locked_table.value,
|
||||||
root_scope.decls,
|
root_scope.decls,
|
||||||
&tree_scope.tree.root_node.decls,
|
&tree_scope.tree.root_node.decls,
|
||||||
tree_scope,
|
tree_scope,
|
||||||
@ -809,7 +845,7 @@ pub const Compilation = struct {
|
|||||||
group: *event.Group(BuildError!void),
|
group: *event.Group(BuildError!void),
|
||||||
locked_table: *Decl.Table,
|
locked_table: *Decl.Table,
|
||||||
decl_scope: *Scope.Decls,
|
decl_scope: *Scope.Decls,
|
||||||
ast_decls: &ast.Node.Root.DeclList,
|
ast_decls: *ast.Node.Root.DeclList,
|
||||||
tree_scope: *Scope.AstTree,
|
tree_scope: *Scope.AstTree,
|
||||||
) !void {
|
) !void {
|
||||||
var existing_decls = try locked_table.clone();
|
var existing_decls = try locked_table.clone();
|
||||||
@ -824,14 +860,14 @@ pub const Compilation = struct {
|
|||||||
|
|
||||||
// TODO connect existing comptime decls to updated source files
|
// TODO connect existing comptime decls to updated source files
|
||||||
|
|
||||||
try self.prelink_group.call(addCompTimeBlock, self, &decl_scope.base, comptime_node);
|
try self.prelink_group.call(addCompTimeBlock, self, tree_scope, &decl_scope.base, comptime_node);
|
||||||
},
|
},
|
||||||
ast.Node.Id.VarDecl => @panic("TODO"),
|
ast.Node.Id.VarDecl => @panic("TODO"),
|
||||||
ast.Node.Id.FnProto => {
|
ast.Node.Id.FnProto => {
|
||||||
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
|
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
|
||||||
|
|
||||||
const name = if (fn_proto.name_token) |name_token| tree_scope.tree.tokenSlice(name_token) else {
|
const name = if (fn_proto.name_token) |name_token| tree_scope.tree.tokenSlice(name_token) else {
|
||||||
try self.addCompileError(root_scope, Span{
|
try self.addCompileError(tree_scope, Span{
|
||||||
.first = fn_proto.fn_token,
|
.first = fn_proto.fn_token,
|
||||||
.last = fn_proto.fn_token + 1,
|
.last = fn_proto.fn_token + 1,
|
||||||
}, "missing function name");
|
}, "missing function name");
|
||||||
@ -856,10 +892,12 @@ pub const Compilation = struct {
|
|||||||
.visib = parseVisibToken(tree_scope.tree, fn_proto.visib_token),
|
.visib = parseVisibToken(tree_scope.tree, fn_proto.visib_token),
|
||||||
.resolution = event.Future(BuildError!void).init(self.loop),
|
.resolution = event.Future(BuildError!void).init(self.loop),
|
||||||
.parent_scope = &decl_scope.base,
|
.parent_scope = &decl_scope.base,
|
||||||
|
.tree_scope = tree_scope,
|
||||||
},
|
},
|
||||||
.value = Decl.Fn.Val{ .Unresolved = {} },
|
.value = Decl.Fn.Val{ .Unresolved = {} },
|
||||||
.fn_proto = fn_proto,
|
.fn_proto = fn_proto,
|
||||||
});
|
});
|
||||||
|
tree_scope.base.ref();
|
||||||
errdefer self.gpa().destroy(fn_decl);
|
errdefer self.gpa().destroy(fn_decl);
|
||||||
|
|
||||||
try group.call(addTopLevelDecl, self, &fn_decl.base, locked_table);
|
try group.call(addTopLevelDecl, self, &fn_decl.base, locked_table);
|
||||||
@ -883,8 +921,8 @@ pub const Compilation = struct {
|
|||||||
const root_scope = blk: {
|
const root_scope = blk: {
|
||||||
// TODO async/await os.path.real
|
// TODO async/await os.path.real
|
||||||
const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
|
const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
|
||||||
try printError("unable to get real path '{}': {}", root_src_path, err);
|
try self.addCompileErrorCli(root_src_path, "unable to open: {}", @errorName(err));
|
||||||
return err;
|
return;
|
||||||
};
|
};
|
||||||
errdefer self.gpa().free(root_src_real_path);
|
errdefer self.gpa().free(root_src_real_path);
|
||||||
|
|
||||||
@ -892,7 +930,8 @@ pub const Compilation = struct {
|
|||||||
};
|
};
|
||||||
defer root_scope.base.deref(self);
|
defer root_scope.base.deref(self);
|
||||||
|
|
||||||
try self.rebuildFile(root_scope);
|
assert((try await try async self.fs_watch.addFile(root_scope.realpath, root_scope)) == null);
|
||||||
|
try await try async self.rebuildFile(root_scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -917,6 +956,7 @@ pub const Compilation = struct {
|
|||||||
/// caller takes ownership of resulting Code
|
/// caller takes ownership of resulting Code
|
||||||
async fn genAndAnalyzeCode(
|
async fn genAndAnalyzeCode(
|
||||||
comp: *Compilation,
|
comp: *Compilation,
|
||||||
|
tree_scope: *Scope.AstTree,
|
||||||
scope: *Scope,
|
scope: *Scope,
|
||||||
node: *ast.Node,
|
node: *ast.Node,
|
||||||
expected_type: ?*Type,
|
expected_type: ?*Type,
|
||||||
@ -924,6 +964,7 @@ pub const Compilation = struct {
|
|||||||
const unanalyzed_code = try await (async ir.gen(
|
const unanalyzed_code = try await (async ir.gen(
|
||||||
comp,
|
comp,
|
||||||
node,
|
node,
|
||||||
|
tree_scope,
|
||||||
scope,
|
scope,
|
||||||
) catch unreachable);
|
) catch unreachable);
|
||||||
defer unanalyzed_code.destroy(comp.gpa());
|
defer unanalyzed_code.destroy(comp.gpa());
|
||||||
@ -950,6 +991,7 @@ pub const Compilation = struct {
|
|||||||
|
|
||||||
async fn addCompTimeBlock(
|
async fn addCompTimeBlock(
|
||||||
comp: *Compilation,
|
comp: *Compilation,
|
||||||
|
tree_scope: *Scope.AstTree,
|
||||||
scope: *Scope,
|
scope: *Scope,
|
||||||
comptime_node: *ast.Node.Comptime,
|
comptime_node: *ast.Node.Comptime,
|
||||||
) !void {
|
) !void {
|
||||||
@ -958,6 +1000,7 @@ pub const Compilation = struct {
|
|||||||
|
|
||||||
const analyzed_code = (await (async genAndAnalyzeCode(
|
const analyzed_code = (await (async genAndAnalyzeCode(
|
||||||
comp,
|
comp,
|
||||||
|
tree_scope,
|
||||||
scope,
|
scope,
|
||||||
comptime_node.expr,
|
comptime_node.expr,
|
||||||
&void_type.base,
|
&void_type.base,
|
||||||
@ -975,25 +1018,37 @@ pub const Compilation = struct {
|
|||||||
decl: *Decl,
|
decl: *Decl,
|
||||||
locked_table: *Decl.Table,
|
locked_table: *Decl.Table,
|
||||||
) !void {
|
) !void {
|
||||||
const tree = decl.findRootScope().tree;
|
const is_export = decl.isExported(decl.tree_scope.tree);
|
||||||
const is_export = decl.isExported(tree);
|
|
||||||
|
|
||||||
if (is_export) {
|
if (is_export) {
|
||||||
try self.prelink_group.call(verifyUniqueSymbol, self, decl);
|
try self.prelink_group.call(verifyUniqueSymbol, self, decl);
|
||||||
try self.prelink_group.call(resolveDecl, self, decl);
|
try self.prelink_group.call(resolveDecl, self, decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (try locked_table.put(decl.name, decl)) |other_decl| {
|
const gop = try locked_table.getOrPut(decl.name);
|
||||||
try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name);
|
if (gop.found_existing) {
|
||||||
|
try self.addCompileError(decl.tree_scope, decl.getSpan(), "redefinition of '{}'", decl.name);
|
||||||
// TODO note: other definition here
|
// TODO note: other definition here
|
||||||
|
} else {
|
||||||
|
gop.kv.value = decl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addCompileError(self: *Compilation, root: *Scope.Root, span: Span, comptime fmt: []const u8, args: ...) !void {
|
fn addCompileError(self: *Compilation, tree_scope: *Scope.AstTree, span: Span, comptime fmt: []const u8, args: ...) !void {
|
||||||
const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
|
const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
|
||||||
errdefer self.gpa().free(text);
|
errdefer self.gpa().free(text);
|
||||||
|
|
||||||
const msg = try Msg.createFromScope(self, root, span, text);
|
const msg = try Msg.createFromScope(self, tree_scope, span, text);
|
||||||
|
errdefer msg.destroy();
|
||||||
|
|
||||||
|
try self.prelink_group.call(addCompileErrorAsync, self, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addCompileErrorCli(self: *Compilation, realpath: []const u8, comptime fmt: []const u8, args: ...) !void {
|
||||||
|
const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
|
||||||
|
errdefer self.gpa().free(text);
|
||||||
|
|
||||||
|
const msg = try Msg.createFromCli(self, realpath, text);
|
||||||
errdefer msg.destroy();
|
errdefer msg.destroy();
|
||||||
|
|
||||||
try self.prelink_group.call(addCompileErrorAsync, self, msg);
|
try self.prelink_group.call(addCompileErrorAsync, self, msg);
|
||||||
@ -1017,7 +1072,7 @@ pub const Compilation = struct {
|
|||||||
|
|
||||||
if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
|
if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
|
||||||
try self.addCompileError(
|
try self.addCompileError(
|
||||||
decl.findRootScope(),
|
decl.tree_scope,
|
||||||
decl.getSpan(),
|
decl.getSpan(),
|
||||||
"exported symbol collision: '{}'",
|
"exported symbol collision: '{}'",
|
||||||
decl.name,
|
decl.name,
|
||||||
@ -1141,18 +1196,24 @@ pub const Compilation = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a value which has been ref()'d once
|
/// Returns a value which has been ref()'d once
|
||||||
async fn analyzeConstValue(comp: *Compilation, scope: *Scope, node: *ast.Node, expected_type: *Type) !*Value {
|
async fn analyzeConstValue(
|
||||||
const analyzed_code = try await (async comp.genAndAnalyzeCode(scope, node, expected_type) catch unreachable);
|
comp: *Compilation,
|
||||||
|
tree_scope: *Scope.AstTree,
|
||||||
|
scope: *Scope,
|
||||||
|
node: *ast.Node,
|
||||||
|
expected_type: *Type,
|
||||||
|
) !*Value {
|
||||||
|
const analyzed_code = try await (async comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type) catch unreachable);
|
||||||
defer analyzed_code.destroy(comp.gpa());
|
defer analyzed_code.destroy(comp.gpa());
|
||||||
|
|
||||||
return analyzed_code.getCompTimeResult(comp);
|
return analyzed_code.getCompTimeResult(comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn analyzeTypeExpr(comp: *Compilation, scope: *Scope, node: *ast.Node) !*Type {
|
async fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type {
|
||||||
const meta_type = &Type.MetaType.get(comp).base;
|
const meta_type = &Type.MetaType.get(comp).base;
|
||||||
defer meta_type.base.deref(comp);
|
defer meta_type.base.deref(comp);
|
||||||
|
|
||||||
const result_val = try await (async comp.analyzeConstValue(scope, node, meta_type) catch unreachable);
|
const result_val = try await (async comp.analyzeConstValue(tree_scope, scope, node, meta_type) catch unreachable);
|
||||||
errdefer result_val.base.deref(comp);
|
errdefer result_val.base.deref(comp);
|
||||||
|
|
||||||
return result_val.cast(Type).?;
|
return result_val.cast(Type).?;
|
||||||
@ -1168,13 +1229,6 @@ pub const Compilation = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn printError(comptime format: []const u8, args: ...) !void {
|
|
||||||
var stderr_file = try std.io.getStdErr();
|
|
||||||
var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
|
|
||||||
const out_stream = &stderr_file_out_stream.stream;
|
|
||||||
try out_stream.print(format, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {
|
fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {
|
||||||
if (optional_token_index) |token_index| {
|
if (optional_token_index) |token_index| {
|
||||||
const token = tree.tokens.at(token_index);
|
const token = tree.tokens.at(token_index);
|
||||||
@ -1198,12 +1252,14 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
||||||
|
const tree_scope = fn_decl.base.tree_scope;
|
||||||
|
|
||||||
const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable);
|
const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable);
|
||||||
|
|
||||||
const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
|
const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
|
||||||
defer fndef_scope.base.deref(comp);
|
defer fndef_scope.base.deref(comp);
|
||||||
|
|
||||||
const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
|
const fn_type = try await (async analyzeFnType(comp, tree_scope, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
|
||||||
defer fn_type.base.base.deref(comp);
|
defer fn_type.base.base.deref(comp);
|
||||||
|
|
||||||
var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
|
var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
|
||||||
@ -1216,18 +1272,17 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
|||||||
symbol_name_consumed = true;
|
symbol_name_consumed = true;
|
||||||
|
|
||||||
// Define local parameter variables
|
// Define local parameter variables
|
||||||
const root_scope = fn_decl.base.findRootScope();
|
|
||||||
for (fn_type.key.data.Normal.params) |param, i| {
|
for (fn_type.key.data.Normal.params) |param, i| {
|
||||||
//AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
|
//AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
|
||||||
const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*);
|
const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*);
|
||||||
const name_token = param_decl.name_token orelse {
|
const name_token = param_decl.name_token orelse {
|
||||||
try comp.addCompileError(root_scope, Span{
|
try comp.addCompileError(tree_scope, Span{
|
||||||
.first = param_decl.firstToken(),
|
.first = param_decl.firstToken(),
|
||||||
.last = param_decl.type_node.firstToken(),
|
.last = param_decl.type_node.firstToken(),
|
||||||
}, "missing parameter name");
|
}, "missing parameter name");
|
||||||
return error.SemanticAnalysisFailed;
|
return error.SemanticAnalysisFailed;
|
||||||
};
|
};
|
||||||
const param_name = root_scope.tree.tokenSlice(name_token);
|
const param_name = tree_scope.tree.tokenSlice(name_token);
|
||||||
|
|
||||||
// if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
|
// if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
|
||||||
// add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
|
// add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
|
||||||
@ -1249,6 +1304,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const analyzed_code = try await (async comp.genAndAnalyzeCode(
|
const analyzed_code = try await (async comp.genAndAnalyzeCode(
|
||||||
|
tree_scope,
|
||||||
fn_val.child_scope,
|
fn_val.child_scope,
|
||||||
body_node,
|
body_node,
|
||||||
fn_type.key.data.Normal.return_type,
|
fn_type.key.data.Normal.return_type,
|
||||||
@ -1279,12 +1335,17 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 {
|
|||||||
return os.getAppDataDir(allocator, "zig");
|
return os.getAppDataDir(allocator, "zig");
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.FnProto) !*Type.Fn {
|
async fn analyzeFnType(
|
||||||
|
comp: *Compilation,
|
||||||
|
tree_scope: *Scope.AstTree,
|
||||||
|
scope: *Scope,
|
||||||
|
fn_proto: *ast.Node.FnProto,
|
||||||
|
) !*Type.Fn {
|
||||||
const return_type_node = switch (fn_proto.return_type) {
|
const return_type_node = switch (fn_proto.return_type) {
|
||||||
ast.Node.FnProto.ReturnType.Explicit => |n| n,
|
ast.Node.FnProto.ReturnType.Explicit => |n| n,
|
||||||
ast.Node.FnProto.ReturnType.InferErrorSet => |n| n,
|
ast.Node.FnProto.ReturnType.InferErrorSet => |n| n,
|
||||||
};
|
};
|
||||||
const return_type = try await (async comp.analyzeTypeExpr(scope, return_type_node) catch unreachable);
|
const return_type = try await (async comp.analyzeTypeExpr(tree_scope, scope, return_type_node) catch unreachable);
|
||||||
return_type.base.deref(comp);
|
return_type.base.deref(comp);
|
||||||
|
|
||||||
var params = ArrayList(Type.Fn.Param).init(comp.gpa());
|
var params = ArrayList(Type.Fn.Param).init(comp.gpa());
|
||||||
@ -1300,7 +1361,7 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
|
|||||||
var it = fn_proto.params.iterator(0);
|
var it = fn_proto.params.iterator(0);
|
||||||
while (it.next()) |param_node_ptr| {
|
while (it.next()) |param_node_ptr| {
|
||||||
const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?;
|
const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?;
|
||||||
const param_type = try await (async comp.analyzeTypeExpr(scope, param_node.type_node) catch unreachable);
|
const param_type = try await (async comp.analyzeTypeExpr(tree_scope, scope, param_node.type_node) catch unreachable);
|
||||||
errdefer param_type.base.deref(comp);
|
errdefer param_type.base.deref(comp);
|
||||||
try params.append(Type.Fn.Param{
|
try params.append(Type.Fn.Param{
|
||||||
.typ = param_type,
|
.typ = param_type,
|
||||||
@ -1337,7 +1398,12 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
||||||
const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
|
const fn_type = try await (async analyzeFnType(
|
||||||
|
comp,
|
||||||
|
fn_decl.base.tree_scope,
|
||||||
|
fn_decl.base.parent_scope,
|
||||||
|
fn_decl.fn_proto,
|
||||||
|
) catch unreachable);
|
||||||
defer fn_type.base.base.deref(comp);
|
defer fn_type.base.base.deref(comp);
|
||||||
|
|
||||||
var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
|
var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
|
||||||
|
@ -16,6 +16,8 @@ pub const Decl = struct {
|
|||||||
visib: Visib,
|
visib: Visib,
|
||||||
resolution: event.Future(Compilation.BuildError!void),
|
resolution: event.Future(Compilation.BuildError!void),
|
||||||
parent_scope: *Scope,
|
parent_scope: *Scope,
|
||||||
|
|
||||||
|
// TODO when we destroy the decl, deref the tree scope
|
||||||
tree_scope: *Scope.AstTree,
|
tree_scope: *Scope.AstTree,
|
||||||
|
|
||||||
pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
|
pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
|
||||||
|
@ -33,35 +33,48 @@ pub const Span = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const Msg = struct {
|
pub const Msg = struct {
|
||||||
span: Span,
|
|
||||||
text: []u8,
|
text: []u8,
|
||||||
|
realpath: []u8,
|
||||||
data: Data,
|
data: Data,
|
||||||
|
|
||||||
const Data = union(enum) {
|
const Data = union(enum) {
|
||||||
|
Cli: Cli,
|
||||||
PathAndTree: PathAndTree,
|
PathAndTree: PathAndTree,
|
||||||
ScopeAndComp: ScopeAndComp,
|
ScopeAndComp: ScopeAndComp,
|
||||||
};
|
};
|
||||||
|
|
||||||
const PathAndTree = struct {
|
const PathAndTree = struct {
|
||||||
realpath: []const u8,
|
span: Span,
|
||||||
tree: *ast.Tree,
|
tree: *ast.Tree,
|
||||||
allocator: *mem.Allocator,
|
allocator: *mem.Allocator,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ScopeAndComp = struct {
|
const ScopeAndComp = struct {
|
||||||
|
span: Span,
|
||||||
tree_scope: *Scope.AstTree,
|
tree_scope: *Scope.AstTree,
|
||||||
compilation: *Compilation,
|
compilation: *Compilation,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Cli = struct {
|
||||||
|
allocator: *mem.Allocator,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn destroy(self: *Msg) void {
|
pub fn destroy(self: *Msg) void {
|
||||||
switch (self.data) {
|
switch (self.data) {
|
||||||
|
Data.Cli => |cli| {
|
||||||
|
cli.allocator.free(self.text);
|
||||||
|
cli.allocator.free(self.realpath);
|
||||||
|
cli.allocator.destroy(self);
|
||||||
|
},
|
||||||
Data.PathAndTree => |path_and_tree| {
|
Data.PathAndTree => |path_and_tree| {
|
||||||
path_and_tree.allocator.free(self.text);
|
path_and_tree.allocator.free(self.text);
|
||||||
|
path_and_tree.allocator.free(self.realpath);
|
||||||
path_and_tree.allocator.destroy(self);
|
path_and_tree.allocator.destroy(self);
|
||||||
},
|
},
|
||||||
Data.ScopeAndComp => |scope_and_comp| {
|
Data.ScopeAndComp => |scope_and_comp| {
|
||||||
scope_and_comp.tree_scope.base.deref(scope_and_comp.compilation);
|
scope_and_comp.tree_scope.base.deref(scope_and_comp.compilation);
|
||||||
scope_and_comp.compilation.gpa().free(self.text);
|
scope_and_comp.compilation.gpa().free(self.text);
|
||||||
|
scope_and_comp.compilation.gpa().free(self.realpath);
|
||||||
scope_and_comp.compilation.gpa().destroy(self);
|
scope_and_comp.compilation.gpa().destroy(self);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -69,6 +82,7 @@ pub const Msg = struct {
|
|||||||
|
|
||||||
fn getAllocator(self: *const Msg) *mem.Allocator {
|
fn getAllocator(self: *const Msg) *mem.Allocator {
|
||||||
switch (self.data) {
|
switch (self.data) {
|
||||||
|
Data.Cli => |cli| return cli.allocator,
|
||||||
Data.PathAndTree => |path_and_tree| {
|
Data.PathAndTree => |path_and_tree| {
|
||||||
return path_and_tree.allocator;
|
return path_and_tree.allocator;
|
||||||
},
|
},
|
||||||
@ -78,19 +92,9 @@ pub const Msg = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getRealPath(self: *const Msg) []const u8 {
|
|
||||||
switch (self.data) {
|
|
||||||
Data.PathAndTree => |path_and_tree| {
|
|
||||||
return path_and_tree.realpath;
|
|
||||||
},
|
|
||||||
Data.ScopeAndComp => |scope_and_comp| {
|
|
||||||
return scope_and_comp.tree_scope.root().realpath;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getTree(self: *const Msg) *ast.Tree {
|
pub fn getTree(self: *const Msg) *ast.Tree {
|
||||||
switch (self.data) {
|
switch (self.data) {
|
||||||
|
Data.Cli => unreachable,
|
||||||
Data.PathAndTree => |path_and_tree| {
|
Data.PathAndTree => |path_and_tree| {
|
||||||
return path_and_tree.tree;
|
return path_and_tree.tree;
|
||||||
},
|
},
|
||||||
@ -100,16 +104,28 @@ pub const Msg = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getSpan(self: *const Msg) Span {
|
||||||
|
return switch (self.data) {
|
||||||
|
Data.Cli => unreachable,
|
||||||
|
Data.PathAndTree => |path_and_tree| path_and_tree.span,
|
||||||
|
Data.ScopeAndComp => |scope_and_comp| scope_and_comp.span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Takes ownership of text
|
/// Takes ownership of text
|
||||||
/// References tree_scope, and derefs when the msg is freed
|
/// References tree_scope, and derefs when the msg is freed
|
||||||
pub fn createFromScope(comp: *Compilation, tree_scope: *Scope.AstTree, span: Span, text: []u8) !*Msg {
|
pub fn createFromScope(comp: *Compilation, tree_scope: *Scope.AstTree, span: Span, text: []u8) !*Msg {
|
||||||
|
const realpath = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
|
||||||
|
errdefer comp.gpa().free(realpath);
|
||||||
|
|
||||||
const msg = try comp.gpa().create(Msg{
|
const msg = try comp.gpa().create(Msg{
|
||||||
.text = text,
|
.text = text,
|
||||||
.span = span,
|
.realpath = realpath,
|
||||||
.data = Data{
|
.data = Data{
|
||||||
.ScopeAndComp = ScopeAndComp{
|
.ScopeAndComp = ScopeAndComp{
|
||||||
.tree_scope = tree_scope,
|
.tree_scope = tree_scope,
|
||||||
.compilation = comp,
|
.compilation = comp,
|
||||||
|
.span = span,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -117,6 +133,22 @@ pub const Msg = struct {
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Caller owns returned Msg and must free with `allocator`
|
||||||
|
/// allocator will additionally be used for printing messages later.
|
||||||
|
pub fn createFromCli(comp: *Compilation, realpath: []const u8, text: []u8) !*Msg {
|
||||||
|
const realpath_copy = try mem.dupe(comp.gpa(), u8, realpath);
|
||||||
|
errdefer comp.gpa().free(realpath_copy);
|
||||||
|
|
||||||
|
const msg = try comp.gpa().create(Msg{
|
||||||
|
.text = text,
|
||||||
|
.realpath = realpath_copy,
|
||||||
|
.data = Data{
|
||||||
|
.Cli = Cli{ .allocator = comp.gpa() },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn createFromParseErrorAndScope(
|
pub fn createFromParseErrorAndScope(
|
||||||
comp: *Compilation,
|
comp: *Compilation,
|
||||||
tree_scope: *Scope.AstTree,
|
tree_scope: *Scope.AstTree,
|
||||||
@ -126,19 +158,23 @@ pub const Msg = struct {
|
|||||||
var text_buf = try std.Buffer.initSize(comp.gpa(), 0);
|
var text_buf = try std.Buffer.initSize(comp.gpa(), 0);
|
||||||
defer text_buf.deinit();
|
defer text_buf.deinit();
|
||||||
|
|
||||||
|
const realpath_copy = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
|
||||||
|
errdefer comp.gpa().free(realpath_copy);
|
||||||
|
|
||||||
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
|
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
|
||||||
try parse_error.render(&tree_scope.tree.tokens, out_stream);
|
try parse_error.render(&tree_scope.tree.tokens, out_stream);
|
||||||
|
|
||||||
const msg = try comp.gpa().create(Msg{
|
const msg = try comp.gpa().create(Msg{
|
||||||
.text = undefined,
|
.text = undefined,
|
||||||
.span = Span{
|
.realpath = realpath_copy,
|
||||||
.first = loc_token,
|
|
||||||
.last = loc_token,
|
|
||||||
},
|
|
||||||
.data = Data{
|
.data = Data{
|
||||||
.ScopeAndComp = ScopeAndComp{
|
.ScopeAndComp = ScopeAndComp{
|
||||||
.tree_scope = tree_scope,
|
.tree_scope = tree_scope,
|
||||||
.compilation = comp,
|
.compilation = comp,
|
||||||
|
.span = Span{
|
||||||
|
.first = loc_token,
|
||||||
|
.last = loc_token,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -161,22 +197,25 @@ pub const Msg = struct {
|
|||||||
var text_buf = try std.Buffer.initSize(allocator, 0);
|
var text_buf = try std.Buffer.initSize(allocator, 0);
|
||||||
defer text_buf.deinit();
|
defer text_buf.deinit();
|
||||||
|
|
||||||
|
const realpath_copy = try mem.dupe(allocator, u8, realpath);
|
||||||
|
errdefer allocator.free(realpath_copy);
|
||||||
|
|
||||||
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
|
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
|
||||||
try parse_error.render(&tree.tokens, out_stream);
|
try parse_error.render(&tree.tokens, out_stream);
|
||||||
|
|
||||||
const msg = try allocator.create(Msg{
|
const msg = try allocator.create(Msg{
|
||||||
.text = undefined,
|
.text = undefined,
|
||||||
|
.realpath = realpath_copy,
|
||||||
.data = Data{
|
.data = Data{
|
||||||
.PathAndTree = PathAndTree{
|
.PathAndTree = PathAndTree{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.realpath = realpath,
|
|
||||||
.tree = tree,
|
.tree = tree,
|
||||||
|
.span = Span{
|
||||||
|
.first = loc_token,
|
||||||
|
.last = loc_token,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.span = Span{
|
|
||||||
.first = loc_token,
|
|
||||||
.last = loc_token,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
msg.text = text_buf.toOwnedSlice();
|
msg.text = text_buf.toOwnedSlice();
|
||||||
errdefer allocator.destroy(msg);
|
errdefer allocator.destroy(msg);
|
||||||
@ -185,20 +224,28 @@ pub const Msg = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {
|
pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {
|
||||||
|
switch (msg.data) {
|
||||||
|
Data.Cli => {
|
||||||
|
try stream.print("{}:-:-: error: {}\n", msg.realpath, msg.text);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
|
||||||
const allocator = msg.getAllocator();
|
const allocator = msg.getAllocator();
|
||||||
const realpath = msg.getRealPath();
|
|
||||||
const tree = msg.getTree();
|
const tree = msg.getTree();
|
||||||
|
|
||||||
const cwd = try os.getCwd(allocator);
|
const cwd = try os.getCwd(allocator);
|
||||||
defer allocator.free(cwd);
|
defer allocator.free(cwd);
|
||||||
|
|
||||||
const relpath = try os.path.relative(allocator, cwd, realpath);
|
const relpath = try os.path.relative(allocator, cwd, msg.realpath);
|
||||||
defer allocator.free(relpath);
|
defer allocator.free(relpath);
|
||||||
|
|
||||||
const path = if (relpath.len < realpath.len) relpath else realpath;
|
const path = if (relpath.len < msg.realpath.len) relpath else msg.realpath;
|
||||||
|
const span = msg.getSpan();
|
||||||
|
|
||||||
const first_token = tree.tokens.at(msg.span.first);
|
const first_token = tree.tokens.at(span.first);
|
||||||
const last_token = tree.tokens.at(msg.span.last);
|
const last_token = tree.tokens.at(span.last);
|
||||||
const start_loc = tree.tokenLocationPtr(0, first_token);
|
const start_loc = tree.tokenLocationPtr(0, first_token);
|
||||||
const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
|
const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
|
||||||
if (!color_on) {
|
if (!color_on) {
|
||||||
|
@ -961,6 +961,7 @@ pub const Code = struct {
|
|||||||
basic_block_list: std.ArrayList(*BasicBlock),
|
basic_block_list: std.ArrayList(*BasicBlock),
|
||||||
arena: std.heap.ArenaAllocator,
|
arena: std.heap.ArenaAllocator,
|
||||||
return_type: ?*Type,
|
return_type: ?*Type,
|
||||||
|
tree_scope: *Scope.AstTree,
|
||||||
|
|
||||||
/// allocator is comp.gpa()
|
/// allocator is comp.gpa()
|
||||||
pub fn destroy(self: *Code, allocator: *Allocator) void {
|
pub fn destroy(self: *Code, allocator: *Allocator) void {
|
||||||
@ -990,14 +991,14 @@ pub const Code = struct {
|
|||||||
return ret_value.val.KnownValue.getRef();
|
return ret_value.val.KnownValue.getRef();
|
||||||
}
|
}
|
||||||
try comp.addCompileError(
|
try comp.addCompileError(
|
||||||
ret_value.scope.findRoot(),
|
self.tree_scope,
|
||||||
ret_value.span,
|
ret_value.span,
|
||||||
"unable to evaluate constant expression",
|
"unable to evaluate constant expression",
|
||||||
);
|
);
|
||||||
return error.SemanticAnalysisFailed;
|
return error.SemanticAnalysisFailed;
|
||||||
} else if (inst.hasSideEffects()) {
|
} else if (inst.hasSideEffects()) {
|
||||||
try comp.addCompileError(
|
try comp.addCompileError(
|
||||||
inst.scope.findRoot(),
|
self.tree_scope,
|
||||||
inst.span,
|
inst.span,
|
||||||
"unable to evaluate constant expression",
|
"unable to evaluate constant expression",
|
||||||
);
|
);
|
||||||
@ -1013,25 +1014,24 @@ pub const Builder = struct {
|
|||||||
code: *Code,
|
code: *Code,
|
||||||
current_basic_block: *BasicBlock,
|
current_basic_block: *BasicBlock,
|
||||||
next_debug_id: usize,
|
next_debug_id: usize,
|
||||||
root_scope: *Scope.Root,
|
|
||||||
is_comptime: bool,
|
is_comptime: bool,
|
||||||
is_async: bool,
|
is_async: bool,
|
||||||
begin_scope: ?*Scope,
|
begin_scope: ?*Scope,
|
||||||
|
|
||||||
pub const Error = Analyze.Error;
|
pub const Error = Analyze.Error;
|
||||||
|
|
||||||
pub fn init(comp: *Compilation, root_scope: *Scope.Root, begin_scope: ?*Scope) !Builder {
|
pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, begin_scope: ?*Scope) !Builder {
|
||||||
const code = try comp.gpa().create(Code{
|
const code = try comp.gpa().create(Code{
|
||||||
.basic_block_list = undefined,
|
.basic_block_list = undefined,
|
||||||
.arena = std.heap.ArenaAllocator.init(comp.gpa()),
|
.arena = std.heap.ArenaAllocator.init(comp.gpa()),
|
||||||
.return_type = null,
|
.return_type = null,
|
||||||
|
.tree_scope = tree_scope,
|
||||||
});
|
});
|
||||||
code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
|
code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
|
||||||
errdefer code.destroy(comp.gpa());
|
errdefer code.destroy(comp.gpa());
|
||||||
|
|
||||||
return Builder{
|
return Builder{
|
||||||
.comp = comp,
|
.comp = comp,
|
||||||
.root_scope = root_scope,
|
|
||||||
.current_basic_block = undefined,
|
.current_basic_block = undefined,
|
||||||
.code = code,
|
.code = code,
|
||||||
.next_debug_id = 0,
|
.next_debug_id = 0,
|
||||||
@ -1292,6 +1292,7 @@ pub const Builder = struct {
|
|||||||
Scope.Id.FnDef => return false,
|
Scope.Id.FnDef => return false,
|
||||||
Scope.Id.Decls => unreachable,
|
Scope.Id.Decls => unreachable,
|
||||||
Scope.Id.Root => unreachable,
|
Scope.Id.Root => unreachable,
|
||||||
|
Scope.Id.AstTree => unreachable,
|
||||||
Scope.Id.Block,
|
Scope.Id.Block,
|
||||||
Scope.Id.Defer,
|
Scope.Id.Defer,
|
||||||
Scope.Id.DeferExpr,
|
Scope.Id.DeferExpr,
|
||||||
@ -1302,7 +1303,7 @@ pub const Builder = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst {
|
pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst {
|
||||||
const int_token = irb.root_scope.tree.tokenSlice(int_lit.token);
|
const int_token = irb.code.tree_scope.tree.tokenSlice(int_lit.token);
|
||||||
|
|
||||||
var base: u8 = undefined;
|
var base: u8 = undefined;
|
||||||
var rest: []const u8 = undefined;
|
var rest: []const u8 = undefined;
|
||||||
@ -1341,7 +1342,7 @@ pub const Builder = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {
|
pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {
|
||||||
const str_token = irb.root_scope.tree.tokenSlice(str_lit.token);
|
const str_token = irb.code.tree_scope.tree.tokenSlice(str_lit.token);
|
||||||
const src_span = Span.token(str_lit.token);
|
const src_span = Span.token(str_lit.token);
|
||||||
|
|
||||||
var bad_index: usize = undefined;
|
var bad_index: usize = undefined;
|
||||||
@ -1349,7 +1350,7 @@ pub const Builder = struct {
|
|||||||
error.OutOfMemory => return error.OutOfMemory,
|
error.OutOfMemory => return error.OutOfMemory,
|
||||||
error.InvalidCharacter => {
|
error.InvalidCharacter => {
|
||||||
try irb.comp.addCompileError(
|
try irb.comp.addCompileError(
|
||||||
irb.root_scope,
|
irb.code.tree_scope,
|
||||||
src_span,
|
src_span,
|
||||||
"invalid character in string literal: '{c}'",
|
"invalid character in string literal: '{c}'",
|
||||||
str_token[bad_index],
|
str_token[bad_index],
|
||||||
@ -1427,7 +1428,7 @@ pub const Builder = struct {
|
|||||||
|
|
||||||
if (statement_node.cast(ast.Node.Defer)) |defer_node| {
|
if (statement_node.cast(ast.Node.Defer)) |defer_node| {
|
||||||
// defer starts a new scope
|
// defer starts a new scope
|
||||||
const defer_token = irb.root_scope.tree.tokens.at(defer_node.defer_token);
|
const defer_token = irb.code.tree_scope.tree.tokens.at(defer_node.defer_token);
|
||||||
const kind = switch (defer_token.id) {
|
const kind = switch (defer_token.id) {
|
||||||
Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,
|
Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,
|
||||||
Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,
|
Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,
|
||||||
@ -1513,7 +1514,7 @@ pub const Builder = struct {
|
|||||||
const src_span = Span.token(control_flow_expr.ltoken);
|
const src_span = Span.token(control_flow_expr.ltoken);
|
||||||
if (scope.findFnDef() == null) {
|
if (scope.findFnDef() == null) {
|
||||||
try irb.comp.addCompileError(
|
try irb.comp.addCompileError(
|
||||||
irb.root_scope,
|
irb.code.tree_scope,
|
||||||
src_span,
|
src_span,
|
||||||
"return expression outside function definition",
|
"return expression outside function definition",
|
||||||
);
|
);
|
||||||
@ -1523,7 +1524,7 @@ pub const Builder = struct {
|
|||||||
if (scope.findDeferExpr()) |scope_defer_expr| {
|
if (scope.findDeferExpr()) |scope_defer_expr| {
|
||||||
if (!scope_defer_expr.reported_err) {
|
if (!scope_defer_expr.reported_err) {
|
||||||
try irb.comp.addCompileError(
|
try irb.comp.addCompileError(
|
||||||
irb.root_scope,
|
irb.code.tree_scope,
|
||||||
src_span,
|
src_span,
|
||||||
"cannot return from defer expression",
|
"cannot return from defer expression",
|
||||||
);
|
);
|
||||||
@ -1599,7 +1600,7 @@ pub const Builder = struct {
|
|||||||
|
|
||||||
pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
|
pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
|
||||||
const src_span = Span.token(identifier.token);
|
const src_span = Span.token(identifier.token);
|
||||||
const name = irb.root_scope.tree.tokenSlice(identifier.token);
|
const name = irb.code.tree_scope.tree.tokenSlice(identifier.token);
|
||||||
|
|
||||||
//if (buf_eql_str(variable_name, "_") && lval == LValPtr) {
|
//if (buf_eql_str(variable_name, "_") && lval == LValPtr) {
|
||||||
// IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);
|
// IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);
|
||||||
@ -1622,7 +1623,7 @@ pub const Builder = struct {
|
|||||||
}
|
}
|
||||||
} else |err| switch (err) {
|
} else |err| switch (err) {
|
||||||
error.Overflow => {
|
error.Overflow => {
|
||||||
try irb.comp.addCompileError(irb.root_scope, src_span, "integer too large");
|
try irb.comp.addCompileError(irb.code.tree_scope, src_span, "integer too large");
|
||||||
return error.SemanticAnalysisFailed;
|
return error.SemanticAnalysisFailed;
|
||||||
},
|
},
|
||||||
error.OutOfMemory => return error.OutOfMemory,
|
error.OutOfMemory => return error.OutOfMemory,
|
||||||
@ -1656,7 +1657,7 @@ pub const Builder = struct {
|
|||||||
// TODO put a variable of same name with invalid type in global scope
|
// TODO put a variable of same name with invalid type in global scope
|
||||||
// so that future references to this same name will find a variable with an invalid type
|
// so that future references to this same name will find a variable with an invalid type
|
||||||
|
|
||||||
try irb.comp.addCompileError(irb.root_scope, src_span, "unknown identifier '{}'", name);
|
try irb.comp.addCompileError(irb.code.tree_scope, src_span, "unknown identifier '{}'", name);
|
||||||
return error.SemanticAnalysisFailed;
|
return error.SemanticAnalysisFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1689,6 +1690,7 @@ pub const Builder = struct {
|
|||||||
=> scope = scope.parent orelse break,
|
=> scope = scope.parent orelse break,
|
||||||
|
|
||||||
Scope.Id.DeferExpr => unreachable,
|
Scope.Id.DeferExpr => unreachable,
|
||||||
|
Scope.Id.AstTree => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -1740,6 +1742,7 @@ pub const Builder = struct {
|
|||||||
=> scope = scope.parent orelse return is_noreturn,
|
=> scope = scope.parent orelse return is_noreturn,
|
||||||
|
|
||||||
Scope.Id.DeferExpr => unreachable,
|
Scope.Id.DeferExpr => unreachable,
|
||||||
|
Scope.Id.AstTree => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1968,8 +1971,8 @@ const Analyze = struct {
|
|||||||
OutOfMemory,
|
OutOfMemory,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(comp: *Compilation, root_scope: *Scope.Root, explicit_return_type: ?*Type) !Analyze {
|
pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, explicit_return_type: ?*Type) !Analyze {
|
||||||
var irb = try Builder.init(comp, root_scope, null);
|
var irb = try Builder.init(comp, tree_scope, null);
|
||||||
errdefer irb.abort();
|
errdefer irb.abort();
|
||||||
|
|
||||||
return Analyze{
|
return Analyze{
|
||||||
@ -2047,7 +2050,7 @@ const Analyze = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void {
|
fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void {
|
||||||
return self.irb.comp.addCompileError(self.irb.root_scope, span, fmt, args);
|
return self.irb.comp.addCompileError(self.irb.code.tree_scope, span, fmt, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type {
|
fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type {
|
||||||
@ -2535,9 +2538,10 @@ const Analyze = struct {
|
|||||||
pub async fn gen(
|
pub async fn gen(
|
||||||
comp: *Compilation,
|
comp: *Compilation,
|
||||||
body_node: *ast.Node,
|
body_node: *ast.Node,
|
||||||
|
tree_scope: *Scope.AstTree,
|
||||||
scope: *Scope,
|
scope: *Scope,
|
||||||
) !*Code {
|
) !*Code {
|
||||||
var irb = try Builder.init(comp, scope.findRoot(), scope);
|
var irb = try Builder.init(comp, tree_scope, scope);
|
||||||
errdefer irb.abort();
|
errdefer irb.abort();
|
||||||
|
|
||||||
const entry_block = try irb.createBasicBlock(scope, c"Entry");
|
const entry_block = try irb.createBasicBlock(scope, c"Entry");
|
||||||
@ -2555,9 +2559,8 @@ pub async fn gen(
|
|||||||
|
|
||||||
pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
|
pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
|
||||||
const old_entry_bb = old_code.basic_block_list.at(0);
|
const old_entry_bb = old_code.basic_block_list.at(0);
|
||||||
const root_scope = old_entry_bb.scope.findRoot();
|
|
||||||
|
|
||||||
var ira = try Analyze.init(comp, root_scope, expected_type);
|
var ira = try Analyze.init(comp, old_code.tree_scope, expected_type);
|
||||||
errdefer ira.abort();
|
errdefer ira.abort();
|
||||||
|
|
||||||
const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);
|
const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);
|
||||||
|
@ -24,6 +24,8 @@ var stderr_file: os.File = undefined;
|
|||||||
var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;
|
var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;
|
||||||
var stdout: *io.OutStream(io.FileOutStream.Error) = undefined;
|
var stdout: *io.OutStream(io.FileOutStream.Error) = undefined;
|
||||||
|
|
||||||
|
const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
|
||||||
|
|
||||||
const usage =
|
const usage =
|
||||||
\\usage: zig [command] [options]
|
\\usage: zig [command] [options]
|
||||||
\\
|
\\
|
||||||
@ -71,26 +73,26 @@ pub fn main() !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const commands = []Command{
|
const commands = []Command{
|
||||||
//Command{
|
Command{
|
||||||
// .name = "build-exe",
|
.name = "build-exe",
|
||||||
// .exec = cmdBuildExe,
|
.exec = cmdBuildExe,
|
||||||
//},
|
},
|
||||||
//Command{
|
Command{
|
||||||
// .name = "build-lib",
|
.name = "build-lib",
|
||||||
// .exec = cmdBuildLib,
|
.exec = cmdBuildLib,
|
||||||
//},
|
},
|
||||||
//Command{
|
Command{
|
||||||
// .name = "build-obj",
|
.name = "build-obj",
|
||||||
// .exec = cmdBuildObj,
|
.exec = cmdBuildObj,
|
||||||
//},
|
},
|
||||||
Command{
|
Command{
|
||||||
.name = "fmt",
|
.name = "fmt",
|
||||||
.exec = cmdFmt,
|
.exec = cmdFmt,
|
||||||
},
|
},
|
||||||
//Command{
|
Command{
|
||||||
// .name = "libc",
|
.name = "libc",
|
||||||
// .exec = cmdLibC,
|
.exec = cmdLibC,
|
||||||
//},
|
},
|
||||||
Command{
|
Command{
|
||||||
.name = "targets",
|
.name = "targets",
|
||||||
.exec = cmdTargets,
|
.exec = cmdTargets,
|
||||||
@ -472,16 +474,21 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
|
async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
|
||||||
|
var count: usize = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
// TODO directly awaiting async should guarantee memory allocation elision
|
// TODO directly awaiting async should guarantee memory allocation elision
|
||||||
const build_event = await (async comp.events.get() catch unreachable);
|
const build_event = await (async comp.events.get() catch unreachable);
|
||||||
|
count += 1;
|
||||||
|
|
||||||
switch (build_event) {
|
switch (build_event) {
|
||||||
Compilation.Event.Ok => {},
|
Compilation.Event.Ok => {
|
||||||
|
stderr.print("Build {} succeeded\n", count) catch os.exit(1);
|
||||||
|
},
|
||||||
Compilation.Event.Error => |err| {
|
Compilation.Event.Error => |err| {
|
||||||
stderr.print("build failed: {}\n", @errorName(err)) catch os.exit(1);
|
stderr.print("Build {} failed: {}\n", count, @errorName(err)) catch os.exit(1);
|
||||||
},
|
},
|
||||||
Compilation.Event.Fail => |msgs| {
|
Compilation.Event.Fail => |msgs| {
|
||||||
|
stderr.print("Build {} compile errors:\n", count) catch os.exit(1);
|
||||||
for (msgs) |msg| {
|
for (msgs) |msg| {
|
||||||
defer msg.destroy();
|
defer msg.destroy();
|
||||||
msg.printToFile(&stderr_file, color) catch os.exit(1);
|
msg.printToFile(&stderr_file, color) catch os.exit(1);
|
||||||
@ -614,7 +621,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
|
|||||||
var stdin_file = try io.getStdIn();
|
var stdin_file = try io.getStdIn();
|
||||||
var stdin = io.FileInStream.init(&stdin_file);
|
var stdin = io.FileInStream.init(&stdin_file);
|
||||||
|
|
||||||
const source_code = try stdin.stream.readAllAlloc(allocator, @maxValue(usize));
|
const source_code = try stdin.stream.readAllAlloc(allocator, max_src_size);
|
||||||
defer allocator.free(source_code);
|
defer allocator.free(source_code);
|
||||||
|
|
||||||
var tree = std.zig.parse(allocator, source_code) catch |err| {
|
var tree = std.zig.parse(allocator, source_code) catch |err| {
|
||||||
@ -697,12 +704,6 @@ async fn asyncFmtMain(
|
|||||||
suspend {
|
suspend {
|
||||||
resume @handle();
|
resume @handle();
|
||||||
}
|
}
|
||||||
// Things we need to make event-based:
|
|
||||||
// * opening the file in the first place - the open()
|
|
||||||
// * read()
|
|
||||||
// * readdir()
|
|
||||||
// * the actual parsing and rendering
|
|
||||||
// * rename()
|
|
||||||
var fmt = Fmt{
|
var fmt = Fmt{
|
||||||
.seen = event.Locked(Fmt.SeenMap).init(loop, Fmt.SeenMap.init(loop.allocator)),
|
.seen = event.Locked(Fmt.SeenMap).init(loop, Fmt.SeenMap.init(loop.allocator)),
|
||||||
.any_error = false,
|
.any_error = false,
|
||||||
@ -714,7 +715,10 @@ async fn asyncFmtMain(
|
|||||||
for (flags.positionals.toSliceConst()) |file_path| {
|
for (flags.positionals.toSliceConst()) |file_path| {
|
||||||
try group.call(fmtPath, &fmt, file_path);
|
try group.call(fmtPath, &fmt, file_path);
|
||||||
}
|
}
|
||||||
return await (async group.wait() catch unreachable);
|
try await (async group.wait() catch unreachable);
|
||||||
|
if (fmt.any_error) {
|
||||||
|
os.exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
|
async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
|
||||||
@ -731,9 +735,10 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
|
|||||||
const source_code = (await try async event.fs.readFile(
|
const source_code = (await try async event.fs.readFile(
|
||||||
fmt.loop,
|
fmt.loop,
|
||||||
file_path,
|
file_path,
|
||||||
2 * 1024 * 1024 * 1024,
|
max_src_size,
|
||||||
)) catch |err| switch (err) {
|
)) catch |err| switch (err) {
|
||||||
error.IsDir => {
|
error.IsDir => {
|
||||||
|
// TODO make event based (and dir.next())
|
||||||
var dir = try std.os.Dir.open(fmt.loop.allocator, file_path);
|
var dir = try std.os.Dir.open(fmt.loop.allocator, file_path);
|
||||||
defer dir.close();
|
defer dir.close();
|
||||||
|
|
||||||
@ -774,6 +779,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO make this evented
|
||||||
const baf = try io.BufferedAtomicFile.create(fmt.loop.allocator, file_path);
|
const baf = try io.BufferedAtomicFile.create(fmt.loop.allocator, file_path);
|
||||||
defer baf.destroy();
|
defer baf.destroy();
|
||||||
|
|
||||||
|
@ -63,6 +63,8 @@ pub const Scope = struct {
|
|||||||
Id.CompTime,
|
Id.CompTime,
|
||||||
Id.Var,
|
Id.Var,
|
||||||
=> scope = scope.parent.?,
|
=> scope = scope.parent.?,
|
||||||
|
|
||||||
|
Id.AstTree => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,6 +85,8 @@ pub const Scope = struct {
|
|||||||
Id.Root,
|
Id.Root,
|
||||||
Id.Var,
|
Id.Var,
|
||||||
=> scope = scope.parent orelse return null,
|
=> scope = scope.parent orelse return null,
|
||||||
|
|
||||||
|
Id.AstTree => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,6 +136,7 @@ pub const Scope = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy(self: *Root, comp: *Compilation) void {
|
pub fn destroy(self: *Root, comp: *Compilation) void {
|
||||||
|
// TODO comp.fs_watch.removeFile(self.realpath);
|
||||||
self.decls.base.deref(comp);
|
self.decls.base.deref(comp);
|
||||||
comp.gpa().free(self.realpath);
|
comp.gpa().free(self.realpath);
|
||||||
comp.gpa().destroy(self);
|
comp.gpa().destroy(self);
|
||||||
@ -144,13 +149,13 @@ pub const Scope = struct {
|
|||||||
|
|
||||||
/// Creates a scope with 1 reference
|
/// Creates a scope with 1 reference
|
||||||
/// Takes ownership of tree, will deinit and destroy when done.
|
/// Takes ownership of tree, will deinit and destroy when done.
|
||||||
pub fn create(comp: *Compilation, tree: *ast.Tree, root: *Root) !*AstTree {
|
pub fn create(comp: *Compilation, tree: *ast.Tree, root_scope: *Root) !*AstTree {
|
||||||
const self = try comp.gpa().createOne(Root);
|
const self = try comp.gpa().createOne(AstTree);
|
||||||
self.* = AstTree{
|
self.* = AstTree{
|
||||||
.base = undefined,
|
.base = undefined,
|
||||||
.tree = tree,
|
.tree = tree,
|
||||||
};
|
};
|
||||||
self.base.init(Id.AstTree, &root.base);
|
self.base.init(Id.AstTree, &root_scope.base);
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@ -181,7 +186,6 @@ pub const Scope = struct {
|
|||||||
self.* = Decls{
|
self.* = Decls{
|
||||||
.base = undefined,
|
.base = undefined,
|
||||||
.table = event.RwLocked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
|
.table = event.RwLocked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
|
||||||
.name_future = event.Future(void).init(comp.loop),
|
|
||||||
};
|
};
|
||||||
self.base.init(Id.Decls, parent);
|
self.base.init(Id.Decls, parent);
|
||||||
return self;
|
return self;
|
||||||
|
@ -212,9 +212,10 @@ pub const TestContext = struct {
|
|||||||
Compilation.Event.Fail => |msgs| {
|
Compilation.Event.Fail => |msgs| {
|
||||||
assertOrPanic(msgs.len != 0);
|
assertOrPanic(msgs.len != 0);
|
||||||
for (msgs) |msg| {
|
for (msgs) |msg| {
|
||||||
if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) {
|
if (mem.endsWith(u8, msg.realpath, path) and mem.eql(u8, msg.text, text)) {
|
||||||
const first_token = msg.getTree().tokens.at(msg.span.first);
|
const span = msg.getSpan();
|
||||||
const last_token = msg.getTree().tokens.at(msg.span.first);
|
const first_token = msg.getTree().tokens.at(span.first);
|
||||||
|
const last_token = msg.getTree().tokens.at(span.first);
|
||||||
const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
|
const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
|
||||||
if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
|
if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
|
||||||
return;
|
return;
|
||||||
|
11
src/ir.cpp
11
src/ir.cpp
@ -9614,6 +9614,9 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un
|
|||||||
case ConstValSpecialStatic:
|
case ConstValSpecialStatic:
|
||||||
return &value->value;
|
return &value->value;
|
||||||
case ConstValSpecialRuntime:
|
case ConstValSpecialRuntime:
|
||||||
|
if (!type_has_bits(value->value.type)) {
|
||||||
|
return &value->value;
|
||||||
|
}
|
||||||
ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression"));
|
ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression"));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
case ConstValSpecialUndef:
|
case ConstValSpecialUndef:
|
||||||
@ -16115,8 +16118,14 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir
|
|||||||
if (casted_field_value == ira->codegen->invalid_instruction)
|
if (casted_field_value == ira->codegen->invalid_instruction)
|
||||||
return ira->codegen->builtin_types.entry_invalid;
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
|
||||||
|
type_ensure_zero_bits_known(ira->codegen, casted_field_value->value.type);
|
||||||
|
if (type_is_invalid(casted_field_value->value.type))
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
|
||||||
bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope);
|
bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope);
|
||||||
if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime) {
|
if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime ||
|
||||||
|
!type_has_bits(casted_field_value->value.type))
|
||||||
|
{
|
||||||
ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk);
|
ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk);
|
||||||
if (!field_val)
|
if (!field_val)
|
||||||
return ira->codegen->builtin_types.entry_invalid;
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
109
std/build.zig
109
std/build.zig
@ -424,60 +424,69 @@ pub const Builder = struct {
|
|||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) bool {
|
pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool {
|
||||||
if (self.user_input_options.put(name, UserInputOption{
|
const gop = try self.user_input_options.getOrPut(name);
|
||||||
.name = name,
|
if (!gop.found_existing) {
|
||||||
.value = UserValue{ .Scalar = value },
|
gop.kv.value = UserInputOption{
|
||||||
.used = false,
|
.name = name,
|
||||||
}) catch unreachable) |*prev_value| {
|
.value = UserValue{ .Scalar = value },
|
||||||
// option already exists
|
.used = false,
|
||||||
switch (prev_value.value) {
|
};
|
||||||
UserValue.Scalar => |s| {
|
return false;
|
||||||
// turn it into a list
|
}
|
||||||
var list = ArrayList([]const u8).init(self.allocator);
|
|
||||||
list.append(s) catch unreachable;
|
// option already exists
|
||||||
list.append(value) catch unreachable;
|
switch (gop.kv.value.value) {
|
||||||
_ = self.user_input_options.put(name, UserInputOption{
|
UserValue.Scalar => |s| {
|
||||||
.name = name,
|
// turn it into a list
|
||||||
.value = UserValue{ .List = list },
|
var list = ArrayList([]const u8).init(self.allocator);
|
||||||
.used = false,
|
list.append(s) catch unreachable;
|
||||||
}) catch unreachable;
|
list.append(value) catch unreachable;
|
||||||
},
|
_ = self.user_input_options.put(name, UserInputOption{
|
||||||
UserValue.List => |*list| {
|
.name = name,
|
||||||
// append to the list
|
.value = UserValue{ .List = list },
|
||||||
list.append(value) catch unreachable;
|
.used = false,
|
||||||
_ = self.user_input_options.put(name, UserInputOption{
|
}) catch unreachable;
|
||||||
.name = name,
|
},
|
||||||
.value = UserValue{ .List = list.* },
|
UserValue.List => |*list| {
|
||||||
.used = false,
|
// append to the list
|
||||||
}) catch unreachable;
|
list.append(value) catch unreachable;
|
||||||
},
|
_ = self.user_input_options.put(name, UserInputOption{
|
||||||
UserValue.Flag => {
|
.name = name,
|
||||||
warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name);
|
.value = UserValue{ .List = list.* },
|
||||||
return true;
|
.used = false,
|
||||||
},
|
}) catch unreachable;
|
||||||
}
|
},
|
||||||
|
UserValue.Flag => {
|
||||||
|
warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn addUserInputFlag(self: *Builder, name: []const u8) bool {
|
pub fn addUserInputFlag(self: *Builder, name: []const u8) !bool {
|
||||||
if (self.user_input_options.put(name, UserInputOption{
|
const gop = try self.user_input_options.getOrPut(name);
|
||||||
.name = name,
|
if (!gop.found_existing) {
|
||||||
.value = UserValue{ .Flag = {} },
|
gop.kv.value = UserInputOption{
|
||||||
.used = false,
|
.name = name,
|
||||||
}) catch unreachable) |*prev_value| {
|
.value = UserValue{ .Flag = {} },
|
||||||
switch (prev_value.value) {
|
.used = false,
|
||||||
UserValue.Scalar => |s| {
|
};
|
||||||
warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s);
|
return false;
|
||||||
return true;
|
}
|
||||||
},
|
|
||||||
UserValue.List => {
|
// option already exists
|
||||||
warn("Flag '-D{}' conflicts with multiple options of the same name.\n", name);
|
switch (gop.kv.value.value) {
|
||||||
return true;
|
UserValue.Scalar => |s| {
|
||||||
},
|
warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s);
|
||||||
UserValue.Flag => {},
|
return true;
|
||||||
}
|
},
|
||||||
|
UserValue.List => {
|
||||||
|
warn("Flag '-D{}' conflicts with multiple options of the same name.\n", name);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
UserValue.Flag => {},
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
264
std/event/fs.zig
264
std/event/fs.zig
@ -367,109 +367,193 @@ pub async fn readFile(loop: *event.Loop, file_path: []const u8, max_size: usize)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Watch = struct {
|
pub fn Watch(comptime V: type) type {
|
||||||
channel: *event.Channel(Event),
|
return struct {
|
||||||
putter: promise,
|
channel: *event.Channel(Event),
|
||||||
|
putter: promise,
|
||||||
|
wd_table: WdTable,
|
||||||
|
table_lock: event.Lock,
|
||||||
|
inotify_fd: i32,
|
||||||
|
|
||||||
pub const Event = union(enum) {
|
const WdTable = std.AutoHashMap(i32, Dir);
|
||||||
CloseWrite,
|
const FileTable = std.AutoHashMap([]const u8, V);
|
||||||
Err: Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Error = error{
|
const Self = this;
|
||||||
UserResourceLimitReached,
|
|
||||||
SystemResources,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn destroy(self: *Watch) void {
|
const Dir = struct {
|
||||||
// TODO https://github.com/ziglang/zig/issues/1261
|
dirname: []const u8,
|
||||||
cancel self.putter;
|
file_table: FileTable,
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
pub fn watchFile(loop: *event.Loop, file_path: []const u8) !*Watch {
|
pub const Event = union(enum) {
|
||||||
const path_with_null = try std.cstr.addNullByte(loop.allocator, file_path);
|
CloseWrite: V,
|
||||||
defer loop.allocator.free(path_with_null);
|
Err: Error,
|
||||||
|
|
||||||
const inotify_fd = try os.linuxINotifyInit1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);
|
pub const Error = error{
|
||||||
errdefer os.close(inotify_fd);
|
UserResourceLimitReached,
|
||||||
|
SystemResources,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const wd = try os.linuxINotifyAddWatchC(inotify_fd, path_with_null.ptr, os.linux.IN_CLOSE_WRITE);
|
pub fn create(loop: *event.Loop, event_buf_count: usize) !*Self {
|
||||||
errdefer os.close(wd);
|
const inotify_fd = try os.linuxINotifyInit1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);
|
||||||
|
errdefer os.close(inotify_fd);
|
||||||
|
|
||||||
const channel = try event.Channel(Watch.Event).create(loop, 0);
|
const channel = try event.Channel(Self.Event).create(loop, event_buf_count);
|
||||||
errdefer channel.destroy();
|
errdefer channel.destroy();
|
||||||
|
|
||||||
var result: *Watch = undefined;
|
var result: *Self = undefined;
|
||||||
_ = try async<loop.allocator> watchEventPutter(inotify_fd, wd, channel, &result);
|
_ = try async<loop.allocator> eventPutter(inotify_fd, channel, &result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn watchEventPutter(inotify_fd: i32, wd: i32, channel: *event.Channel(Watch.Event), out_watch: **Watch) void {
|
pub fn destroy(self: *Self) void {
|
||||||
// TODO https://github.com/ziglang/zig/issues/1194
|
cancel self.putter;
|
||||||
suspend {
|
}
|
||||||
resume @handle();
|
|
||||||
}
|
|
||||||
|
|
||||||
var watch = Watch{
|
pub async fn addFile(self: *Self, file_path: []const u8, value: V) !?V {
|
||||||
.putter = @handle(),
|
const dirname = os.path.dirname(file_path) orelse ".";
|
||||||
.channel = channel,
|
const dirname_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, dirname);
|
||||||
};
|
var dirname_with_null_consumed = false;
|
||||||
out_watch.* = &watch;
|
defer if (!dirname_with_null_consumed) self.channel.loop.allocator.free(dirname_with_null);
|
||||||
|
|
||||||
const loop = channel.loop;
|
const basename = os.path.basename(file_path);
|
||||||
loop.beginOneEvent();
|
const basename_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, basename);
|
||||||
|
var basename_with_null_consumed = false;
|
||||||
|
defer if (!basename_with_null_consumed) self.channel.loop.allocator.free(basename_with_null);
|
||||||
|
|
||||||
defer {
|
const wd = try os.linuxINotifyAddWatchC(
|
||||||
channel.destroy();
|
self.inotify_fd,
|
||||||
os.close(wd);
|
dirname_with_null.ptr,
|
||||||
os.close(inotify_fd);
|
os.linux.IN_CLOSE_WRITE | os.linux.IN_ONLYDIR | os.linux.IN_EXCL_UNLINK,
|
||||||
loop.finishOneEvent();
|
);
|
||||||
}
|
// wd is either a newly created watch or an existing one.
|
||||||
|
|
||||||
var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined;
|
const held = await (async self.table_lock.acquire() catch unreachable);
|
||||||
|
defer held.release();
|
||||||
|
|
||||||
while (true) {
|
const gop = try self.wd_table.getOrPut(wd);
|
||||||
const rc = os.linux.read(inotify_fd, &event_buf, event_buf.len);
|
if (!gop.found_existing) {
|
||||||
const errno = os.linux.getErrno(rc);
|
gop.kv.value = Dir{
|
||||||
switch (errno) {
|
.dirname = dirname_with_null,
|
||||||
0 => {
|
.file_table = FileTable.init(self.channel.loop.allocator),
|
||||||
// can't use @bytesToSlice because of the special variable length name field
|
};
|
||||||
var ptr = event_buf[0..].ptr;
|
dirname_with_null_consumed = true;
|
||||||
const end_ptr = ptr + event_buf.len;
|
}
|
||||||
var ev: *os.linux.inotify_event = undefined;
|
const dir = &gop.kv.value;
|
||||||
while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += @sizeOf(os.linux.inotify_event) + ev.len) {
|
|
||||||
ev = @ptrCast(*os.linux.inotify_event, ptr);
|
const file_table_gop = try dir.file_table.getOrPut(basename_with_null);
|
||||||
if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {
|
if (file_table_gop.found_existing) {
|
||||||
await (async channel.put(Watch.Event.CloseWrite) catch unreachable);
|
const prev_value = file_table_gop.kv.value;
|
||||||
|
file_table_gop.kv.value = value;
|
||||||
|
return prev_value;
|
||||||
|
} else {
|
||||||
|
file_table_gop.kv.value = value;
|
||||||
|
basename_with_null_consumed = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn removeFile(self: *Self, file_path: []const u8) ?V {
|
||||||
|
@panic("TODO");
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn eventPutter(inotify_fd: i32, channel: *event.Channel(Event), out_watch: **Self) void {
|
||||||
|
// TODO https://github.com/ziglang/zig/issues/1194
|
||||||
|
suspend {
|
||||||
|
resume @handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
const loop = channel.loop;
|
||||||
|
|
||||||
|
var watch = Self{
|
||||||
|
.putter = @handle(),
|
||||||
|
.channel = channel,
|
||||||
|
.wd_table = WdTable.init(loop.allocator),
|
||||||
|
.table_lock = event.Lock.init(loop),
|
||||||
|
.inotify_fd = inotify_fd,
|
||||||
|
};
|
||||||
|
out_watch.* = &watch;
|
||||||
|
|
||||||
|
loop.beginOneEvent();
|
||||||
|
|
||||||
|
defer {
|
||||||
|
watch.table_lock.deinit();
|
||||||
|
{
|
||||||
|
var wd_it = watch.wd_table.iterator();
|
||||||
|
while (wd_it.next()) |wd_entry| {
|
||||||
|
var file_it = wd_entry.value.file_table.iterator();
|
||||||
|
while (file_it.next()) |file_entry| {
|
||||||
|
loop.allocator.free(file_entry.key);
|
||||||
|
}
|
||||||
|
loop.allocator.free(wd_entry.value.dirname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
loop.finishOneEvent();
|
||||||
os.linux.EINTR => continue,
|
os.close(inotify_fd);
|
||||||
os.linux.EINVAL => unreachable,
|
channel.destroy();
|
||||||
os.linux.EFAULT => unreachable,
|
}
|
||||||
os.linux.EAGAIN => {
|
|
||||||
(await (async loop.linuxWaitFd(
|
var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined;
|
||||||
inotify_fd,
|
|
||||||
os.linux.EPOLLET | os.linux.EPOLLIN,
|
while (true) {
|
||||||
) catch unreachable)) catch |err| {
|
const rc = os.linux.read(inotify_fd, &event_buf, event_buf.len);
|
||||||
const transformed_err = switch (err) {
|
const errno = os.linux.getErrno(rc);
|
||||||
error.InvalidFileDescriptor => unreachable,
|
switch (errno) {
|
||||||
error.FileDescriptorAlreadyPresentInSet => unreachable,
|
0 => {
|
||||||
error.InvalidSyscall => unreachable,
|
// can't use @bytesToSlice because of the special variable length name field
|
||||||
error.OperationCausesCircularLoop => unreachable,
|
var ptr = event_buf[0..].ptr;
|
||||||
error.FileDescriptorNotRegistered => unreachable,
|
const end_ptr = ptr + event_buf.len;
|
||||||
error.SystemResources => error.SystemResources,
|
var ev: *os.linux.inotify_event = undefined;
|
||||||
error.UserResourceLimitReached => error.UserResourceLimitReached,
|
while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += @sizeOf(os.linux.inotify_event) + ev.len) {
|
||||||
error.FileDescriptorIncompatibleWithEpoll => unreachable,
|
ev = @ptrCast(*os.linux.inotify_event, ptr);
|
||||||
error.Unexpected => unreachable,
|
if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {
|
||||||
};
|
const basename_ptr = ptr + @sizeOf(os.linux.inotify_event);
|
||||||
await (async channel.put(Watch.Event{ .Err = transformed_err }) catch unreachable);
|
const basename_with_null = basename_ptr[0 .. std.cstr.len(basename_ptr) + 1];
|
||||||
};
|
const user_value = blk: {
|
||||||
},
|
const held = await (async watch.table_lock.acquire() catch unreachable);
|
||||||
else => unreachable,
|
defer held.release();
|
||||||
|
|
||||||
|
const dir = &watch.wd_table.get(ev.wd).?.value;
|
||||||
|
if (dir.file_table.get(basename_with_null)) |entry| {
|
||||||
|
break :blk entry.value;
|
||||||
|
} else {
|
||||||
|
break :blk null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (user_value) |v| {
|
||||||
|
await (async channel.put(Self.Event{ .CloseWrite = v }) catch unreachable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
os.linux.EINTR => continue,
|
||||||
|
os.linux.EINVAL => unreachable,
|
||||||
|
os.linux.EFAULT => unreachable,
|
||||||
|
os.linux.EAGAIN => {
|
||||||
|
(await (async loop.linuxWaitFd(
|
||||||
|
inotify_fd,
|
||||||
|
os.linux.EPOLLET | os.linux.EPOLLIN,
|
||||||
|
) catch unreachable)) catch |err| {
|
||||||
|
const transformed_err = switch (err) {
|
||||||
|
error.InvalidFileDescriptor => unreachable,
|
||||||
|
error.FileDescriptorAlreadyPresentInSet => unreachable,
|
||||||
|
error.InvalidSyscall => unreachable,
|
||||||
|
error.OperationCausesCircularLoop => unreachable,
|
||||||
|
error.FileDescriptorNotRegistered => unreachable,
|
||||||
|
error.SystemResources => error.SystemResources,
|
||||||
|
error.UserResourceLimitReached => error.UserResourceLimitReached,
|
||||||
|
error.FileDescriptorIncompatibleWithEpoll => unreachable,
|
||||||
|
error.Unexpected => unreachable,
|
||||||
|
};
|
||||||
|
await (async channel.put(Self.Event{ .Err = transformed_err }) catch unreachable);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const test_tmp_dir = "std_event_fs_test";
|
const test_tmp_dir = "std_event_fs_test";
|
||||||
@ -517,9 +601,11 @@ async fn testFsWatch(loop: *event.Loop) !void {
|
|||||||
assert(mem.eql(u8, read_contents, contents));
|
assert(mem.eql(u8, read_contents, contents));
|
||||||
|
|
||||||
// now watch the file
|
// now watch the file
|
||||||
var watch = try watchFile(loop, file_path);
|
var watch = try Watch(void).create(loop, 0);
|
||||||
defer watch.destroy();
|
defer watch.destroy();
|
||||||
|
|
||||||
|
assert((try await try async watch.addFile(file_path, {})) == null);
|
||||||
|
|
||||||
const ev = try async watch.channel.get();
|
const ev = try async watch.channel.get();
|
||||||
var ev_consumed = false;
|
var ev_consumed = false;
|
||||||
defer if (!ev_consumed) cancel ev;
|
defer if (!ev_consumed) cancel ev;
|
||||||
@ -534,8 +620,8 @@ async fn testFsWatch(loop: *event.Loop) !void {
|
|||||||
|
|
||||||
ev_consumed = true;
|
ev_consumed = true;
|
||||||
switch (await ev) {
|
switch (await ev) {
|
||||||
Watch.Event.CloseWrite => {},
|
Watch(void).Event.CloseWrite => {},
|
||||||
Watch.Event.Err => |err| return err,
|
Watch(void).Event.Err => |err| return err,
|
||||||
}
|
}
|
||||||
|
|
||||||
const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024);
|
const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024);
|
||||||
|
@ -10,6 +10,8 @@ const Loop = std.event.Loop;
|
|||||||
/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
|
/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
|
||||||
/// are resumed when the lock is released, in order.
|
/// are resumed when the lock is released, in order.
|
||||||
/// Many readers can hold the lock at the same time; however locking for writing is exclusive.
|
/// Many readers can hold the lock at the same time; however locking for writing is exclusive.
|
||||||
|
/// When a read lock is held, it will not be released until the reader queue is empty.
|
||||||
|
/// When a write lock is held, it will not be released until the writer queue is empty.
|
||||||
pub const RwLock = struct {
|
pub const RwLock = struct {
|
||||||
loop: *Loop,
|
loop: *Loop,
|
||||||
shared_state: u8, // TODO make this an enum
|
shared_state: u8, // TODO make this an enum
|
||||||
|
300
std/hash_map.zig
300
std/hash_map.zig
@ -9,6 +9,10 @@ const builtin = @import("builtin");
|
|||||||
const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast;
|
const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast;
|
||||||
const debug_u32 = if (want_modification_safety) u32 else void;
|
const debug_u32 = if (want_modification_safety) u32 else void;
|
||||||
|
|
||||||
|
pub fn AutoHashMap(comptime K: type, comptime V: type) type {
|
||||||
|
return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u32, comptime eql: fn (a: K, b: K) bool) type {
|
pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u32, comptime eql: fn (a: K, b: K) bool) type {
|
||||||
return struct {
|
return struct {
|
||||||
entries: []Entry,
|
entries: []Entry,
|
||||||
@ -20,13 +24,22 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
|
|||||||
|
|
||||||
const Self = this;
|
const Self = this;
|
||||||
|
|
||||||
pub const Entry = struct {
|
pub const KV = struct {
|
||||||
used: bool,
|
|
||||||
distance_from_start_index: usize,
|
|
||||||
key: K,
|
key: K,
|
||||||
value: V,
|
value: V,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Entry = struct {
|
||||||
|
used: bool,
|
||||||
|
distance_from_start_index: usize,
|
||||||
|
kv: KV,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const GetOrPutResult = struct {
|
||||||
|
kv: *KV,
|
||||||
|
found_existing: bool,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Iterator = struct {
|
pub const Iterator = struct {
|
||||||
hm: *const Self,
|
hm: *const Self,
|
||||||
// how many items have we returned
|
// how many items have we returned
|
||||||
@ -36,7 +49,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
|
|||||||
// used to detect concurrent modification
|
// used to detect concurrent modification
|
||||||
initial_modification_count: debug_u32,
|
initial_modification_count: debug_u32,
|
||||||
|
|
||||||
pub fn next(it: *Iterator) ?*Entry {
|
pub fn next(it: *Iterator) ?*KV {
|
||||||
if (want_modification_safety) {
|
if (want_modification_safety) {
|
||||||
assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification
|
assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification
|
||||||
}
|
}
|
||||||
@ -46,7 +59,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
|
|||||||
if (entry.used) {
|
if (entry.used) {
|
||||||
it.index += 1;
|
it.index += 1;
|
||||||
it.count += 1;
|
it.count += 1;
|
||||||
return entry;
|
return &entry.kv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unreachable; // no next item
|
unreachable; // no next item
|
||||||
@ -71,7 +84,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(hm: *const Self) void {
|
pub fn deinit(hm: Self) void {
|
||||||
hm.allocator.free(hm.entries);
|
hm.allocator.free(hm.entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,34 +97,65 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
|
|||||||
hm.incrementModificationCount();
|
hm.incrementModificationCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count(hm: *const Self) usize {
|
pub fn count(self: Self) usize {
|
||||||
return hm.size;
|
return self.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the value that was already there.
|
/// If key exists this function cannot fail.
|
||||||
pub fn put(hm: *Self, key: K, value: *const V) !?V {
|
/// If there is an existing item with `key`, then the result
|
||||||
if (hm.entries.len == 0) {
|
/// kv pointer points to it, and found_existing is true.
|
||||||
try hm.initCapacity(16);
|
/// Otherwise, puts a new item with undefined value, and
|
||||||
|
/// the kv pointer points to it. Caller should then initialize
|
||||||
|
/// the data.
|
||||||
|
pub fn getOrPut(self: *Self, key: K) !GetOrPutResult {
|
||||||
|
// TODO this implementation can be improved - we should only
|
||||||
|
// have to hash once and find the entry once.
|
||||||
|
if (self.get(key)) |kv| {
|
||||||
|
return GetOrPutResult{
|
||||||
|
.kv = kv,
|
||||||
|
.found_existing = true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
self.incrementModificationCount();
|
||||||
|
try self.ensureCapacity();
|
||||||
|
const put_result = self.internalPut(key);
|
||||||
|
assert(put_result.old_kv == null);
|
||||||
|
return GetOrPutResult{
|
||||||
|
.kv = &put_result.new_entry.kv,
|
||||||
|
.found_existing = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensureCapacity(self: *Self) !void {
|
||||||
|
if (self.entries.len == 0) {
|
||||||
|
return self.initCapacity(16);
|
||||||
}
|
}
|
||||||
hm.incrementModificationCount();
|
|
||||||
|
|
||||||
// if we get too full (60%), double the capacity
|
// if we get too full (60%), double the capacity
|
||||||
if (hm.size * 5 >= hm.entries.len * 3) {
|
if (self.size * 5 >= self.entries.len * 3) {
|
||||||
const old_entries = hm.entries;
|
const old_entries = self.entries;
|
||||||
try hm.initCapacity(hm.entries.len * 2);
|
try self.initCapacity(self.entries.len * 2);
|
||||||
// dump all of the old elements into the new table
|
// dump all of the old elements into the new table
|
||||||
for (old_entries) |*old_entry| {
|
for (old_entries) |*old_entry| {
|
||||||
if (old_entry.used) {
|
if (old_entry.used) {
|
||||||
_ = hm.internalPut(old_entry.key, old_entry.value);
|
self.internalPut(old_entry.kv.key).new_entry.kv.value = old_entry.kv.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hm.allocator.free(old_entries);
|
self.allocator.free(old_entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hm.internalPut(key, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(hm: *const Self, key: K) ?*Entry {
|
/// Returns the kv pair that was already there.
|
||||||
|
pub fn put(self: *Self, key: K, value: V) !?KV {
|
||||||
|
self.incrementModificationCount();
|
||||||
|
try self.ensureCapacity();
|
||||||
|
|
||||||
|
const put_result = self.internalPut(key);
|
||||||
|
put_result.new_entry.kv.value = value;
|
||||||
|
return put_result.old_kv;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(hm: *const Self, key: K) ?*KV {
|
||||||
if (hm.entries.len == 0) {
|
if (hm.entries.len == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -122,7 +166,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
|
|||||||
return hm.get(key) != null;
|
return hm.get(key) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(hm: *Self, key: K) ?*Entry {
|
pub fn remove(hm: *Self, key: K) ?*KV {
|
||||||
if (hm.entries.len == 0) return null;
|
if (hm.entries.len == 0) return null;
|
||||||
hm.incrementModificationCount();
|
hm.incrementModificationCount();
|
||||||
const start_index = hm.keyToIndex(key);
|
const start_index = hm.keyToIndex(key);
|
||||||
@ -134,7 +178,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
|
|||||||
|
|
||||||
if (!entry.used) return null;
|
if (!entry.used) return null;
|
||||||
|
|
||||||
if (!eql(entry.key, key)) continue;
|
if (!eql(entry.kv.key, key)) continue;
|
||||||
|
|
||||||
while (roll_over < hm.entries.len) : (roll_over += 1) {
|
while (roll_over < hm.entries.len) : (roll_over += 1) {
|
||||||
const next_index = (start_index + roll_over + 1) % hm.entries.len;
|
const next_index = (start_index + roll_over + 1) % hm.entries.len;
|
||||||
@ -142,7 +186,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
|
|||||||
if (!next_entry.used or next_entry.distance_from_start_index == 0) {
|
if (!next_entry.used or next_entry.distance_from_start_index == 0) {
|
||||||
entry.used = false;
|
entry.used = false;
|
||||||
hm.size -= 1;
|
hm.size -= 1;
|
||||||
return entry;
|
return &entry.kv;
|
||||||
}
|
}
|
||||||
entry.* = next_entry.*;
|
entry.* = next_entry.*;
|
||||||
entry.distance_from_start_index -= 1;
|
entry.distance_from_start_index -= 1;
|
||||||
@ -168,7 +212,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
|
|||||||
try other.initCapacity(self.entries.len);
|
try other.initCapacity(self.entries.len);
|
||||||
var it = self.iterator();
|
var it = self.iterator();
|
||||||
while (it.next()) |entry| {
|
while (it.next()) |entry| {
|
||||||
try other.put(entry.key, entry.value);
|
assert((try other.put(entry.key, entry.value)) == null);
|
||||||
}
|
}
|
||||||
return other;
|
return other;
|
||||||
}
|
}
|
||||||
@ -188,60 +232,81 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the value that was already there.
|
const InternalPutResult = struct {
|
||||||
fn internalPut(hm: *Self, orig_key: K, orig_value: *const V) ?V {
|
new_entry: *Entry,
|
||||||
|
old_kv: ?KV,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Returns a pointer to the new entry.
|
||||||
|
/// Asserts that there is enough space for the new item.
|
||||||
|
fn internalPut(self: *Self, orig_key: K) InternalPutResult {
|
||||||
var key = orig_key;
|
var key = orig_key;
|
||||||
var value = orig_value.*;
|
var value: V = undefined;
|
||||||
const start_index = hm.keyToIndex(key);
|
const start_index = self.keyToIndex(key);
|
||||||
var roll_over: usize = 0;
|
var roll_over: usize = 0;
|
||||||
var distance_from_start_index: usize = 0;
|
var distance_from_start_index: usize = 0;
|
||||||
while (roll_over < hm.entries.len) : ({
|
var got_result_entry = false;
|
||||||
|
var result = InternalPutResult{
|
||||||
|
.new_entry = undefined,
|
||||||
|
.old_kv = null,
|
||||||
|
};
|
||||||
|
while (roll_over < self.entries.len) : ({
|
||||||
roll_over += 1;
|
roll_over += 1;
|
||||||
distance_from_start_index += 1;
|
distance_from_start_index += 1;
|
||||||
}) {
|
}) {
|
||||||
const index = (start_index + roll_over) % hm.entries.len;
|
const index = (start_index + roll_over) % self.entries.len;
|
||||||
const entry = &hm.entries[index];
|
const entry = &self.entries[index];
|
||||||
|
|
||||||
if (entry.used and !eql(entry.key, key)) {
|
if (entry.used and !eql(entry.kv.key, key)) {
|
||||||
if (entry.distance_from_start_index < distance_from_start_index) {
|
if (entry.distance_from_start_index < distance_from_start_index) {
|
||||||
// robin hood to the rescue
|
// robin hood to the rescue
|
||||||
const tmp = entry.*;
|
const tmp = entry.*;
|
||||||
hm.max_distance_from_start_index = math.max(hm.max_distance_from_start_index, distance_from_start_index);
|
self.max_distance_from_start_index = math.max(self.max_distance_from_start_index, distance_from_start_index);
|
||||||
|
if (!got_result_entry) {
|
||||||
|
got_result_entry = true;
|
||||||
|
result.new_entry = entry;
|
||||||
|
}
|
||||||
entry.* = Entry{
|
entry.* = Entry{
|
||||||
.used = true,
|
.used = true,
|
||||||
.distance_from_start_index = distance_from_start_index,
|
.distance_from_start_index = distance_from_start_index,
|
||||||
.key = key,
|
.kv = KV{
|
||||||
.value = value,
|
.key = key,
|
||||||
|
.value = value,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
key = tmp.key;
|
key = tmp.kv.key;
|
||||||
value = tmp.value;
|
value = tmp.kv.value;
|
||||||
distance_from_start_index = tmp.distance_from_start_index;
|
distance_from_start_index = tmp.distance_from_start_index;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var result: ?V = null;
|
|
||||||
if (entry.used) {
|
if (entry.used) {
|
||||||
result = entry.value;
|
result.old_kv = entry.kv;
|
||||||
} else {
|
} else {
|
||||||
// adding an entry. otherwise overwriting old value with
|
// adding an entry. otherwise overwriting old value with
|
||||||
// same key
|
// same key
|
||||||
hm.size += 1;
|
self.size += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
hm.max_distance_from_start_index = math.max(distance_from_start_index, hm.max_distance_from_start_index);
|
self.max_distance_from_start_index = math.max(distance_from_start_index, self.max_distance_from_start_index);
|
||||||
|
if (!got_result_entry) {
|
||||||
|
result.new_entry = entry;
|
||||||
|
}
|
||||||
entry.* = Entry{
|
entry.* = Entry{
|
||||||
.used = true,
|
.used = true,
|
||||||
.distance_from_start_index = distance_from_start_index,
|
.distance_from_start_index = distance_from_start_index,
|
||||||
.key = key,
|
.kv = KV{
|
||||||
.value = value,
|
.key = key,
|
||||||
|
.value = value,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
unreachable; // put into a full map
|
unreachable; // put into a full map
|
||||||
}
|
}
|
||||||
|
|
||||||
fn internalGet(hm: *const Self, key: K) ?*Entry {
|
fn internalGet(hm: Self, key: K) ?*KV {
|
||||||
const start_index = hm.keyToIndex(key);
|
const start_index = hm.keyToIndex(key);
|
||||||
{
|
{
|
||||||
var roll_over: usize = 0;
|
var roll_over: usize = 0;
|
||||||
@ -250,13 +315,13 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
|
|||||||
const entry = &hm.entries[index];
|
const entry = &hm.entries[index];
|
||||||
|
|
||||||
if (!entry.used) return null;
|
if (!entry.used) return null;
|
||||||
if (eql(entry.key, key)) return entry;
|
if (eql(entry.kv.key, key)) return &entry.kv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keyToIndex(hm: *const Self, key: K) usize {
|
fn keyToIndex(hm: Self, key: K) usize {
|
||||||
return usize(hash(key)) % hm.entries.len;
|
return usize(hash(key)) % hm.entries.len;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -266,7 +331,7 @@ test "basic hash map usage" {
|
|||||||
var direct_allocator = std.heap.DirectAllocator.init();
|
var direct_allocator = std.heap.DirectAllocator.init();
|
||||||
defer direct_allocator.deinit();
|
defer direct_allocator.deinit();
|
||||||
|
|
||||||
var map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);
|
var map = AutoHashMap(i32, i32).init(&direct_allocator.allocator);
|
||||||
defer map.deinit();
|
defer map.deinit();
|
||||||
|
|
||||||
assert((try map.put(1, 11)) == null);
|
assert((try map.put(1, 11)) == null);
|
||||||
@ -275,8 +340,19 @@ test "basic hash map usage" {
|
|||||||
assert((try map.put(4, 44)) == null);
|
assert((try map.put(4, 44)) == null);
|
||||||
assert((try map.put(5, 55)) == null);
|
assert((try map.put(5, 55)) == null);
|
||||||
|
|
||||||
assert((try map.put(5, 66)).? == 55);
|
assert((try map.put(5, 66)).?.value == 55);
|
||||||
assert((try map.put(5, 55)).? == 66);
|
assert((try map.put(5, 55)).?.value == 66);
|
||||||
|
|
||||||
|
const gop1 = try map.getOrPut(5);
|
||||||
|
assert(gop1.found_existing == true);
|
||||||
|
assert(gop1.kv.value == 55);
|
||||||
|
gop1.kv.value = 77;
|
||||||
|
assert(map.get(5).?.value == 77);
|
||||||
|
|
||||||
|
const gop2 = try map.getOrPut(99);
|
||||||
|
assert(gop2.found_existing == false);
|
||||||
|
gop2.kv.value = 42;
|
||||||
|
assert(map.get(99).?.value == 42);
|
||||||
|
|
||||||
assert(map.contains(2));
|
assert(map.contains(2));
|
||||||
assert(map.get(2).?.value == 22);
|
assert(map.get(2).?.value == 22);
|
||||||
@ -289,7 +365,7 @@ test "iterator hash map" {
|
|||||||
var direct_allocator = std.heap.DirectAllocator.init();
|
var direct_allocator = std.heap.DirectAllocator.init();
|
||||||
defer direct_allocator.deinit();
|
defer direct_allocator.deinit();
|
||||||
|
|
||||||
var reset_map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);
|
var reset_map = AutoHashMap(i32, i32).init(&direct_allocator.allocator);
|
||||||
defer reset_map.deinit();
|
defer reset_map.deinit();
|
||||||
|
|
||||||
assert((try reset_map.put(1, 11)) == null);
|
assert((try reset_map.put(1, 11)) == null);
|
||||||
@ -332,10 +408,124 @@ test "iterator hash map" {
|
|||||||
assert(entry.value == values[0]);
|
assert(entry.value == values[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_i32(x: i32) u32 {
|
pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
|
||||||
return @bitCast(u32, x);
|
return struct {
|
||||||
|
fn hash(key: K) u32 {
|
||||||
|
comptime var rng = comptime std.rand.DefaultPrng.init(0);
|
||||||
|
return autoHash(key, &rng.random, u32);
|
||||||
|
}
|
||||||
|
}.hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eql_i32(a: i32, b: i32) bool {
|
pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {
|
||||||
return a == b;
|
return struct {
|
||||||
|
fn eql(a: K, b: K) bool {
|
||||||
|
return autoEql(a, b);
|
||||||
|
}
|
||||||
|
}.eql;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO improve these hash functions
|
||||||
|
pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type) HashInt {
|
||||||
|
switch (@typeInfo(@typeOf(key))) {
|
||||||
|
builtin.TypeId.NoReturn,
|
||||||
|
builtin.TypeId.Opaque,
|
||||||
|
builtin.TypeId.Undefined,
|
||||||
|
builtin.TypeId.ArgTuple,
|
||||||
|
=> @compileError("cannot hash this type"),
|
||||||
|
|
||||||
|
builtin.TypeId.Void,
|
||||||
|
builtin.TypeId.Null,
|
||||||
|
=> return 0,
|
||||||
|
|
||||||
|
builtin.TypeId.Int => |info| {
|
||||||
|
const unsigned_x = @bitCast(@IntType(false, info.bits), key);
|
||||||
|
if (info.bits <= HashInt.bit_count) {
|
||||||
|
return HashInt(unsigned_x) *% comptime rng.scalar(HashInt);
|
||||||
|
} else {
|
||||||
|
return @truncate(HashInt, unsigned_x *% comptime rng.scalar(@typeOf(unsigned_x)));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
builtin.TypeId.Float => |info| {
|
||||||
|
return autoHash(@bitCast(@IntType(false, info.bits), key), rng);
|
||||||
|
},
|
||||||
|
builtin.TypeId.Bool => return autoHash(@boolToInt(key), rng),
|
||||||
|
builtin.TypeId.Enum => return autoHash(@enumToInt(key), rng),
|
||||||
|
builtin.TypeId.ErrorSet => return autoHash(@errorToInt(key), rng),
|
||||||
|
builtin.TypeId.Promise, builtin.TypeId.Fn => return autoHash(@ptrToInt(key), rng),
|
||||||
|
|
||||||
|
builtin.TypeId.Namespace,
|
||||||
|
builtin.TypeId.Block,
|
||||||
|
builtin.TypeId.BoundFn,
|
||||||
|
builtin.TypeId.ComptimeFloat,
|
||||||
|
builtin.TypeId.ComptimeInt,
|
||||||
|
builtin.TypeId.Type,
|
||||||
|
=> return 0,
|
||||||
|
|
||||||
|
builtin.TypeId.Pointer => |info| switch (info.size) {
|
||||||
|
builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto hash for single item pointers"),
|
||||||
|
builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto hash for many item pointers"),
|
||||||
|
builtin.TypeInfo.Pointer.Size.Slice => {
|
||||||
|
const interval = std.math.max(1, key.len / 256);
|
||||||
|
var i: usize = 0;
|
||||||
|
var h = comptime rng.scalar(HashInt);
|
||||||
|
while (i < key.len) : (i += interval) {
|
||||||
|
h ^= autoHash(key[i], rng, HashInt);
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
builtin.TypeId.Optional => @compileError("TODO auto hash for optionals"),
|
||||||
|
builtin.TypeId.Array => @compileError("TODO auto hash for arrays"),
|
||||||
|
builtin.TypeId.Struct => @compileError("TODO auto hash for structs"),
|
||||||
|
builtin.TypeId.Union => @compileError("TODO auto hash for unions"),
|
||||||
|
builtin.TypeId.ErrorUnion => @compileError("TODO auto hash for unions"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn autoEql(a: var, b: @typeOf(a)) bool {
|
||||||
|
switch (@typeInfo(@typeOf(a))) {
|
||||||
|
builtin.TypeId.NoReturn,
|
||||||
|
builtin.TypeId.Opaque,
|
||||||
|
builtin.TypeId.Undefined,
|
||||||
|
builtin.TypeId.ArgTuple,
|
||||||
|
=> @compileError("cannot test equality of this type"),
|
||||||
|
builtin.TypeId.Void,
|
||||||
|
builtin.TypeId.Null,
|
||||||
|
=> return true,
|
||||||
|
builtin.TypeId.Bool,
|
||||||
|
builtin.TypeId.Int,
|
||||||
|
builtin.TypeId.Float,
|
||||||
|
builtin.TypeId.ComptimeFloat,
|
||||||
|
builtin.TypeId.ComptimeInt,
|
||||||
|
builtin.TypeId.Namespace,
|
||||||
|
builtin.TypeId.Block,
|
||||||
|
builtin.TypeId.Promise,
|
||||||
|
builtin.TypeId.Enum,
|
||||||
|
builtin.TypeId.BoundFn,
|
||||||
|
builtin.TypeId.Fn,
|
||||||
|
builtin.TypeId.ErrorSet,
|
||||||
|
builtin.TypeId.Type,
|
||||||
|
=> return a == b,
|
||||||
|
|
||||||
|
builtin.TypeId.Pointer => |info| switch (info.size) {
|
||||||
|
builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto eql for single item pointers"),
|
||||||
|
builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto eql for many item pointers"),
|
||||||
|
builtin.TypeInfo.Pointer.Size.Slice => {
|
||||||
|
if (a.len != b.len) return false;
|
||||||
|
for (a) |a_item, i| {
|
||||||
|
if (!autoEql(a_item, b[i])) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
builtin.TypeId.Optional => @compileError("TODO auto eql for optionals"),
|
||||||
|
builtin.TypeId.Array => @compileError("TODO auto eql for arrays"),
|
||||||
|
builtin.TypeId.Struct => @compileError("TODO auto eql for structs"),
|
||||||
|
builtin.TypeId.Union => @compileError("TODO auto eql for unions"),
|
||||||
|
builtin.TypeId.ErrorUnion => @compileError("TODO auto eql for unions"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ pub const BufSet = @import("buf_set.zig").BufSet;
|
|||||||
pub const Buffer = @import("buffer.zig").Buffer;
|
pub const Buffer = @import("buffer.zig").Buffer;
|
||||||
pub const BufferOutStream = @import("buffer.zig").BufferOutStream;
|
pub const BufferOutStream = @import("buffer.zig").BufferOutStream;
|
||||||
pub const HashMap = @import("hash_map.zig").HashMap;
|
pub const HashMap = @import("hash_map.zig").HashMap;
|
||||||
|
pub const AutoHashMap = @import("hash_map.zig").AutoHashMap;
|
||||||
pub const LinkedList = @import("linked_list.zig").LinkedList;
|
pub const LinkedList = @import("linked_list.zig").LinkedList;
|
||||||
pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
|
pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
|
||||||
pub const DynLib = @import("dynamic_library.zig").DynLib;
|
pub const DynLib = @import("dynamic_library.zig").DynLib;
|
||||||
|
@ -1318,7 +1318,7 @@ pub const Parser = struct {
|
|||||||
_ = p.stack.pop();
|
_ = p.stack.pop();
|
||||||
|
|
||||||
var object = &p.stack.items[p.stack.len - 1].Object;
|
var object = &p.stack.items[p.stack.len - 1].Object;
|
||||||
_ = try object.put(key, value);
|
_ = try object.put(key, value.*);
|
||||||
p.state = State.ObjectKey;
|
p.state = State.ObjectKey;
|
||||||
},
|
},
|
||||||
// Array Parent -> [ ..., <array>, value ]
|
// Array Parent -> [ ..., <array>, value ]
|
||||||
|
@ -72,10 +72,10 @@ pub fn main() !void {
|
|||||||
if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {
|
if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {
|
||||||
const option_name = option_contents[0..name_end];
|
const option_name = option_contents[0..name_end];
|
||||||
const option_value = option_contents[name_end + 1 ..];
|
const option_value = option_contents[name_end + 1 ..];
|
||||||
if (builder.addUserInputOption(option_name, option_value))
|
if (try builder.addUserInputOption(option_name, option_value))
|
||||||
return usageAndErr(&builder, false, try stderr_stream);
|
return usageAndErr(&builder, false, try stderr_stream);
|
||||||
} else {
|
} else {
|
||||||
if (builder.addUserInputFlag(option_contents))
|
if (try builder.addUserInputFlag(option_contents))
|
||||||
return usageAndErr(&builder, false, try stderr_stream);
|
return usageAndErr(&builder, false, try stderr_stream);
|
||||||
}
|
}
|
||||||
} else if (mem.startsWith(u8, arg, "-")) {
|
} else if (mem.startsWith(u8, arg, "-")) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user