@@ -717,13 +717,13 @@ pub const Compilation = struct {
717
717
}
718
718
719
719
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 );
723
721
722
+ while (true ) {
723
+ const link_result = if (build_result ) self .maybeLink () else | err | err ;
724
724
// this makes a handy error return trace and stack trace in debug mode
725
725
if (std .debug .runtime_safety ) {
726
- build_result catch unreachable ;
726
+ link_result catch unreachable ;
727
727
}
728
728
729
729
const compile_errors = blk : {
@@ -732,7 +732,7 @@ pub const Compilation = struct {
732
732
break :blk held .value .toOwnedSlice ();
733
733
};
734
734
735
- if (build_result ) | _ | {
735
+ if (link_result ) | _ | {
736
736
if (compile_errors .len == 0 ) {
737
737
await (async self .events .put (Event .Ok ) catch unreachable );
738
738
} else {
@@ -745,108 +745,158 @@ pub const Compilation = struct {
745
745
await (async self .events .put (Event { .Error = err }) catch unreachable );
746
746
}
747
747
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 );
750
753
}
751
754
}
752
755
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 );
758
764
return err ;
759
765
};
760
- const root_scope = blk : {
761
- errdefer self .gpa ().free (root_src_real_path );
766
+ errdefer self .gpa ().free (source_code );
762
767
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
+ }
772
774
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 );
779
778
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 () ;
784
783
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
+ }
789
789
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 ();
795
792
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 ( );
798
795
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
+ );
802
803
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
+ }
809
806
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
823
826
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
824
852
const fn_decl = try self .gpa ().create (Decl.Fn {
825
853
.base = Decl {
826
854
.id = Decl .Id .Fn ,
827
855
.name = name ,
828
- .visib = parseVisibToken (tree , fn_proto .visib_token ),
856
+ .visib = parseVisibToken (tree_scope . tree , fn_proto .visib_token ),
829
857
.resolution = event .Future (BuildError ! void ).init (self .loop ),
830
- .parent_scope = & decls .base ,
858
+ .parent_scope = & decl_scope .base ,
831
859
},
832
860
.value = Decl.Fn.Val { .Unresolved = {} },
833
861
.fn_proto = fn_proto ,
834
862
});
835
863
errdefer self .gpa ().destroy (fn_decl );
836
864
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 ,
842
870
}
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 );
845
890
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 );
848
896
}
897
+ }
849
898
899
+ async fn maybeLink (self : * Compilation ) ! void {
850
900
(await (async self .prelink_group .wait () catch unreachable )) catch | err | switch (err ) {
851
901
error .SemanticAnalysisFailed = > {},
852
902
else = > return err ,
@@ -920,28 +970,20 @@ pub const Compilation = struct {
920
970
analyzed_code .destroy (comp .gpa ());
921
971
}
922
972
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 {
924
978
const tree = decl .findRootScope ().tree ;
925
979
const is_export = decl .isExported (tree );
926
980
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
-
931
981
if (is_export ) {
932
982
try self .prelink_group .call (verifyUniqueSymbol , self , decl );
933
983
try self .prelink_group .call (resolveDecl , self , decl );
934
984
}
935
985
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 | {
945
987
try self .addCompileError (decls .base .findRoot (), decl .getSpan (), "redefinition of '{}'" , decl .name );
946
988
// TODO note: other definition here
947
989
}
0 commit comments