Skip to content

Commit 821805a

Browse files
committed
WIP: Channel.getOrNull
1 parent e3ae2cf commit 821805a

17 files changed

+493
-321
lines changed

src-self-hosted/compilation.zig

Lines changed: 133 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -717,13 +717,13 @@ pub const Compilation = struct {
717717
}
718718

719719
async fn buildAsync(self: *Compilation) void {
720-
while (true) {
721-
// TODO directly awaiting async should guarantee memory allocation elision
722-
const build_result = await (async self.compileAndLink() catch unreachable);
720+
var build_result = await (async self.initialCompile() catch unreachable);
723721

722+
while (true) {
723+
const link_result = if (build_result) self.maybeLink() else |err| err;
724724
// this makes a handy error return trace and stack trace in debug mode
725725
if (std.debug.runtime_safety) {
726-
build_result catch unreachable;
726+
link_result catch unreachable;
727727
}
728728

729729
const compile_errors = blk: {
@@ -732,7 +732,7 @@ pub const Compilation = struct {
732732
break :blk held.value.toOwnedSlice();
733733
};
734734

735-
if (build_result) |_| {
735+
if (link_result) |_| {
736736
if (compile_errors.len == 0) {
737737
await (async self.events.put(Event.Ok) catch unreachable);
738738
} else {
@@ -745,108 +745,158 @@ pub const Compilation = struct {
745745
await (async self.events.put(Event{ .Error = err }) catch unreachable);
746746
}
747747

748-
// for now we stop after 1
749-
return;
748+
var group = event.Group(BuildError!void).init(self.loop);
749+
while (self.fs_watch.channel.getOrNull()) |root_scope| {
750+
try group.call(rebuildFile, self, root_scope);
751+
}
752+
build_result = await (async group.wait() catch unreachable);
750753
}
751754
}
752755

753-
async fn compileAndLink(self: *Compilation) !void {
754-
if (self.root_src_path) |root_src_path| {
755-
// TODO async/await os.path.real
756-
const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
757-
try printError("unable to get real path '{}': {}", root_src_path, err);
756+
async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) !void {
757+
const tree_scope = blk: {
758+
const source_code = (await (async fs.readFile(
759+
self.loop,
760+
root_src_real_path,
761+
max_src_size,
762+
) catch unreachable)) catch |err| {
763+
try printError("unable to open '{}': {}", root_src_real_path, err);
758764
return err;
759765
};
760-
const root_scope = blk: {
761-
errdefer self.gpa().free(root_src_real_path);
766+
errdefer self.gpa().free(source_code);
762767

763-
const source_code = (await (async fs.readFile(
764-
self.loop,
765-
root_src_real_path,
766-
max_src_size,
767-
) catch unreachable)) catch |err| {
768-
try printError("unable to open '{}': {}", root_src_real_path, err);
769-
return err;
770-
};
771-
errdefer self.gpa().free(source_code);
768+
const tree = try self.gpa().createOne(ast.Tree);
769+
tree.* = try std.zig.parse(self.gpa(), source_code);
770+
errdefer {
771+
tree.deinit();
772+
self.gpa().destroy(tree);
773+
}
772774

773-
const tree = try self.gpa().createOne(ast.Tree);
774-
tree.* = try std.zig.parse(self.gpa(), source_code);
775-
errdefer {
776-
tree.deinit();
777-
self.gpa().destroy(tree);
778-
}
775+
break :blk try Scope.AstTree.create(self, tree, root_scope);
776+
};
777+
defer tree_scope.base.deref(self);
779778

780-
break :blk try Scope.Root.create(self, tree, root_src_real_path);
781-
};
782-
defer root_scope.base.deref(self);
783-
const tree = root_scope.tree;
779+
var error_it = tree_scope.tree.errors.iterator(0);
780+
while (error_it.next()) |parse_error| {
781+
const msg = try Msg.createFromParseErrorAndScope(self, tree_scope, parse_error);
782+
errdefer msg.destroy();
784783

785-
var error_it = tree.errors.iterator(0);
786-
while (error_it.next()) |parse_error| {
787-
const msg = try Msg.createFromParseErrorAndScope(self, root_scope, parse_error);
788-
errdefer msg.destroy();
784+
try await (async self.addCompileErrorAsync(msg) catch unreachable);
785+
}
786+
if (tree_scope.tree.errors.len != 0) {
787+
return;
788+
}
789789

790-
try await (async self.addCompileErrorAsync(msg) catch unreachable);
791-
}
792-
if (tree.errors.len != 0) {
793-
return;
794-
}
790+
const locked_table = await (async root_scope.decls.table.acquireWrite() catch unreachable);
791+
defer locked_table.release();
795792

796-
const decls = try Scope.Decls.create(self, &root_scope.base);
797-
defer decls.base.deref(self);
793+
var decl_group = event.Group(BuildError!void).init(self.loop);
794+
defer decl_group.deinit();
798795

799-
var decl_group = event.Group(BuildError!void).init(self.loop);
800-
var decl_group_consumed = false;
801-
errdefer if (!decl_group_consumed) decl_group.cancelAll();
796+
try self.rebuildChangedDecls(
797+
&decl_group,
798+
locked_table,
799+
root_scope.decls,
800+
&tree_scope.tree.root_node.decls,
801+
tree_scope,
802+
);
802803

803-
var it = tree.root_node.decls.iterator(0);
804-
while (it.next()) |decl_ptr| {
805-
const decl = decl_ptr.*;
806-
switch (decl.id) {
807-
ast.Node.Id.Comptime => {
808-
const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
804+
try await (async decl_group.wait() catch unreachable);
805+
}
809806

810-
try self.prelink_group.call(addCompTimeBlock, self, &decls.base, comptime_node);
811-
},
812-
ast.Node.Id.VarDecl => @panic("TODO"),
813-
ast.Node.Id.FnProto => {
814-
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
815-
816-
const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
817-
try self.addCompileError(root_scope, Span{
818-
.first = fn_proto.fn_token,
819-
.last = fn_proto.fn_token + 1,
820-
}, "missing function name");
821-
continue;
822-
};
807+
async fn rebuildChangedDecls(
808+
self: *Compilation,
809+
group: *event.Group(BuildError!void),
810+
locked_table: *Decl.Table,
811+
decl_scope: *Scope.Decls,
812+
ast_decls: &ast.Node.Root.DeclList,
813+
tree_scope: *Scope.AstTree,
814+
) !void {
815+
var existing_decls = try locked_table.clone();
816+
defer existing_decls.deinit();
817+
818+
var ast_it = ast_decls.iterator(0);
819+
while (ast_it.next()) |decl_ptr| {
820+
const decl = decl_ptr.*;
821+
switch (decl.id) {
822+
ast.Node.Id.Comptime => {
823+
const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
824+
825+
// TODO connect existing comptime decls to updated source files
823826

827+
try self.prelink_group.call(addCompTimeBlock, self, &decl_scope.base, comptime_node);
828+
},
829+
ast.Node.Id.VarDecl => @panic("TODO"),
830+
ast.Node.Id.FnProto => {
831+
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
832+
833+
const name = if (fn_proto.name_token) |name_token| tree_scope.tree.tokenSlice(name_token) else {
834+
try self.addCompileError(root_scope, Span{
835+
.first = fn_proto.fn_token,
836+
.last = fn_proto.fn_token + 1,
837+
}, "missing function name");
838+
continue;
839+
};
840+
841+
if (existing_decls.remove(name)) |entry| {
842+
// compare new code to existing
843+
const existing_decl = entry.value;
844+
// Just compare the old bytes to the new bytes of the top level decl.
845+
// Even if the AST is technically the same, we want error messages to display
846+
// from the most recent source.
847+
@panic("TODO handle decl comparison");
848+
// Add the new thing before dereferencing the old thing. This way we don't end
849+
// up pointlessly re-creating things we end up using in the new thing.
850+
} else {
851+
// add new decl
824852
const fn_decl = try self.gpa().create(Decl.Fn{
825853
.base = Decl{
826854
.id = Decl.Id.Fn,
827855
.name = name,
828-
.visib = parseVisibToken(tree, fn_proto.visib_token),
856+
.visib = parseVisibToken(tree_scope.tree, fn_proto.visib_token),
829857
.resolution = event.Future(BuildError!void).init(self.loop),
830-
.parent_scope = &decls.base,
858+
.parent_scope = &decl_scope.base,
831859
},
832860
.value = Decl.Fn.Val{ .Unresolved = {} },
833861
.fn_proto = fn_proto,
834862
});
835863
errdefer self.gpa().destroy(fn_decl);
836864

837-
try decl_group.call(addTopLevelDecl, self, decls, &fn_decl.base);
838-
},
839-
ast.Node.Id.TestDecl => @panic("TODO"),
840-
else => unreachable,
841-
}
865+
try group.call(addTopLevelDecl, self, &fn_decl.base, locked_table);
866+
}
867+
},
868+
ast.Node.Id.TestDecl => @panic("TODO"),
869+
else => unreachable,
842870
}
843-
decl_group_consumed = true;
844-
try await (async decl_group.wait() catch unreachable);
871+
}
872+
873+
var existing_decl_it = existing_decls.iterator();
874+
while (existing_decl_it.next()) |entry| {
875+
// this decl was deleted
876+
const existing_decl = entry.value;
877+
@panic("TODO handle decl deletion");
878+
}
879+
}
880+
881+
async fn initialCompile(self: *Compilation) !void {
882+
if (self.root_src_path) |root_src_path| {
883+
const root_scope = blk: {
884+
// TODO async/await os.path.real
885+
const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
886+
try printError("unable to get real path '{}': {}", root_src_path, err);
887+
return err;
888+
};
889+
errdefer self.gpa().free(root_src_real_path);
845890

846-
// Now other code can rely on the decls scope having a complete list of names.
847-
decls.name_future.resolve();
891+
break :blk try Scope.Root.create(self, root_src_real_path);
892+
};
893+
defer root_scope.base.deref(self);
894+
895+
try self.rebuildFile(root_scope);
848896
}
897+
}
849898

899+
async fn maybeLink(self: *Compilation) !void {
850900
(await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) {
851901
error.SemanticAnalysisFailed => {},
852902
else => return err,
@@ -920,28 +970,20 @@ pub const Compilation = struct {
920970
analyzed_code.destroy(comp.gpa());
921971
}
922972

923-
async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
973+
async fn addTopLevelDecl(
974+
self: *Compilation,
975+
decl: *Decl,
976+
locked_table: *Decl.Table,
977+
) !void {
924978
const tree = decl.findRootScope().tree;
925979
const is_export = decl.isExported(tree);
926980

927-
var add_to_table_resolved = false;
928-
const add_to_table = async self.addDeclToTable(decls, decl) catch unreachable;
929-
errdefer if (!add_to_table_resolved) cancel add_to_table; // TODO https://github.com/ziglang/zig/issues/1261
930-
931981
if (is_export) {
932982
try self.prelink_group.call(verifyUniqueSymbol, self, decl);
933983
try self.prelink_group.call(resolveDecl, self, decl);
934984
}
935985

936-
add_to_table_resolved = true;
937-
try await add_to_table;
938-
}
939-
940-
async fn addDeclToTable(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
941-
const held = await (async decls.table.acquire() catch unreachable);
942-
defer held.release();
943-
944-
if (try held.value.put(decl.name, decl)) |other_decl| {
986+
if (try locked_table.put(decl.name, decl)) |other_decl| {
945987
try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name);
946988
// TODO note: other definition here
947989
}

src-self-hosted/decl.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub const Decl = struct {
1616
visib: Visib,
1717
resolution: event.Future(Compilation.BuildError!void),
1818
parent_scope: *Scope,
19+
tree_scope: *Scope.AstTree,
1920

2021
pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
2122

src-self-hosted/errmsg.zig

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub const Msg = struct {
4949
};
5050

5151
const ScopeAndComp = struct {
52-
root_scope: *Scope.Root,
52+
tree_scope: *Scope.AstTree,
5353
compilation: *Compilation,
5454
};
5555

@@ -60,7 +60,7 @@ pub const Msg = struct {
6060
path_and_tree.allocator.destroy(self);
6161
},
6262
Data.ScopeAndComp => |scope_and_comp| {
63-
scope_and_comp.root_scope.base.deref(scope_and_comp.compilation);
63+
scope_and_comp.tree_scope.base.deref(scope_and_comp.compilation);
6464
scope_and_comp.compilation.gpa().free(self.text);
6565
scope_and_comp.compilation.gpa().destroy(self);
6666
},
@@ -84,7 +84,7 @@ pub const Msg = struct {
8484
return path_and_tree.realpath;
8585
},
8686
Data.ScopeAndComp => |scope_and_comp| {
87-
return scope_and_comp.root_scope.realpath;
87+
return scope_and_comp.tree_scope.root().realpath;
8888
},
8989
}
9090
}
@@ -95,39 +95,39 @@ pub const Msg = struct {
9595
return path_and_tree.tree;
9696
},
9797
Data.ScopeAndComp => |scope_and_comp| {
98-
return scope_and_comp.root_scope.tree;
98+
return scope_and_comp.tree_scope.tree;
9999
},
100100
}
101101
}
102102

103103
/// Takes ownership of text
104-
/// References root_scope, and derefs when the msg is freed
105-
pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg {
104+
/// References tree_scope, and derefs when the msg is freed
105+
pub fn createFromScope(comp: *Compilation, tree_scope: *Scope.AstTree, span: Span, text: []u8) !*Msg {
106106
const msg = try comp.gpa().create(Msg{
107107
.text = text,
108108
.span = span,
109109
.data = Data{
110110
.ScopeAndComp = ScopeAndComp{
111-
.root_scope = root_scope,
111+
.tree_scope = tree_scope,
112112
.compilation = comp,
113113
},
114114
},
115115
});
116-
root_scope.base.ref();
116+
tree_scope.base.ref();
117117
return msg;
118118
}
119119

120120
pub fn createFromParseErrorAndScope(
121121
comp: *Compilation,
122-
root_scope: *Scope.Root,
122+
tree_scope: *Scope.AstTree,
123123
parse_error: *const ast.Error,
124124
) !*Msg {
125125
const loc_token = parse_error.loc();
126126
var text_buf = try std.Buffer.initSize(comp.gpa(), 0);
127127
defer text_buf.deinit();
128128

129129
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
130-
try parse_error.render(&root_scope.tree.tokens, out_stream);
130+
try parse_error.render(&tree_scope.tree.tokens, out_stream);
131131

132132
const msg = try comp.gpa().create(Msg{
133133
.text = undefined,
@@ -137,12 +137,12 @@ pub const Msg = struct {
137137
},
138138
.data = Data{
139139
.ScopeAndComp = ScopeAndComp{
140-
.root_scope = root_scope,
140+
.tree_scope = tree_scope,
141141
.compilation = comp,
142142
},
143143
},
144144
});
145-
root_scope.base.ref();
145+
tree_scope.base.ref();
146146
msg.text = text_buf.toOwnedSlice();
147147
return msg;
148148
}

0 commit comments

Comments
 (0)