authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-02-11 23:29:55-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-02-11 23:29:55-07:00
log3d0f4b90305bc1815ccc86613cb3da715e9b62c0
treebcba43fe405142aa248b0ada3446e43f91477dbd
parent288e18059815f8c23f850dcc5cb3b2880f029ae9

stage2: start reworking Module/astgen for memory layout changes

This commit does not reach any particular milestone, it is work-in-progress towards getting things to build. There's a `@panic("TODO")` in translate-c that should be removed when working on translate-c stuff.

9 files changed, 1146 insertions(+), 711 deletions(-)

lib/std/zig.zig-1
...@@ -12,7 +12,6 @@ pub const fmtId = @import("zig/fmt.zig").fmtId;...@@ -12,7 +12,6 @@ pub const fmtId = @import("zig/fmt.zig").fmtId;
12pub const fmtEscapes = @import("zig/fmt.zig").fmtEscapes;12pub const fmtEscapes = @import("zig/fmt.zig").fmtEscapes;
13pub const parse = @import("zig/parse.zig").parse;13pub const parse = @import("zig/parse.zig").parse;
14pub const parseStringLiteral = @import("zig/string_literal.zig").parse;14pub const parseStringLiteral = @import("zig/string_literal.zig").parse;
15pub const render = @import("zig/render.zig").render;
16pub const ast = @import("zig/ast.zig");15pub const ast = @import("zig/ast.zig");
17pub const system = @import("zig/system.zig");16pub const system = @import("zig/system.zig");
18pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;17pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;
lib/std/zig/ast.zig+195
...@@ -45,6 +45,28 @@ pub const Tree = struct {...@@ -45,6 +45,28 @@ pub const Tree = struct {
45 tree.* = undefined;45 tree.* = undefined;
46 }46 }
4747
48 pub const RenderError = error{
49 /// Ran out of memory allocating call stack frames to complete rendering, or
50 /// ran out of memory allocating space in the output buffer.
51 OutOfMemory,
52 };
53
54 /// `gpa` is used for allocating the resulting formatted source code, as well as
55 /// for allocating extra stack memory if needed, because this function utilizes recursion.
56 /// Note: that's not actually true yet, see https://github.com/ziglang/zig/issues/1006.
57 /// Caller owns the returned slice of bytes, allocated with `gpa`.
58 pub fn render(tree: Tree, gpa: *mem.Allocator) RenderError![]u8 {
59 var buffer = std.ArrayList(u8).init(gpa);
60 defer buffer.deinit();
61
62 try tree.renderToArrayList(&buffer);
63 return buffer.toOwnedSlice();
64 }
65
66 pub fn renderToArrayList(tree: Tree, buffer: *std.ArrayList(u8)) RenderError!void {
67 return @import("./render.zig").renderTree(buffer, tree);
68 }
69
48 pub fn tokenLocation(self: Tree, start_offset: ByteOffset, token_index: TokenIndex) Location {70 pub fn tokenLocation(self: Tree, start_offset: ByteOffset, token_index: TokenIndex) Location {
49 var loc = Location{71 var loc = Location{
50 .line = 0,72 .line = 0,
...@@ -72,6 +94,27 @@ pub const Tree = struct {...@@ -72,6 +94,27 @@ pub const Tree = struct {
72 return loc;94 return loc;
73 }95 }
7496
97 pub fn tokenSlice(tree: Tree, token_index: TokenIndex) []const u8 {
98 const token_starts = tree.tokens.items(.start);
99 const token_tags = tree.tokens.items(.tag);
100 const token_tag = token_tags[token_index];
101
102 // Many tokens can be determined entirely by their tag.
103 if (token_tag.lexeme()) |lexeme| {
104 return lexeme;
105 }
106
107 // For some tokens, re-tokenization is needed to find the end.
108 var tokenizer: std.zig.Tokenizer = .{
109 .buffer = tree.source,
110 .index = token_starts[token_index],
111 .pending_invalid_token = null,
112 };
113 const token = tokenizer.next();
114 assert(token.tag == token_tag);
115 return tree.source[token.loc.start..token.loc.end];
116 }
117
75 pub fn extraData(tree: Tree, index: usize, comptime T: type) T {118 pub fn extraData(tree: Tree, index: usize, comptime T: type) T {
76 const fields = std.meta.fields(T);119 const fields = std.meta.fields(T);
77 var result: T = undefined;120 var result: T = undefined;
...@@ -82,6 +125,12 @@ pub const Tree = struct {...@@ -82,6 +125,12 @@ pub const Tree = struct {
82 return result;125 return result;
83 }126 }
84127
128 pub fn rootDecls(tree: Tree) []const Node.Index {
129 // Root is always index 0.
130 const nodes_data = tree.nodes.items(.data);
131 return tree.extra_data[nodes_data[0].lhs..nodes_data[0].rhs];
132 }
133
85 pub fn renderError(tree: Tree, parse_error: Error, stream: anytype) !void {134 pub fn renderError(tree: Tree, parse_error: Error, stream: anytype) !void {
86 const tokens = tree.tokens.items(.tag);135 const tokens = tree.tokens.items(.tag);
87 switch (parse_error) {136 switch (parse_error) {
...@@ -966,6 +1015,15 @@ pub const Tree = struct {...@@ -966,6 +1015,15 @@ pub const Tree = struct {
966 return mem.indexOfScalar(u8, source, '\n') == null;1015 return mem.indexOfScalar(u8, source, '\n') == null;
967 }1016 }
9681017
1018 pub fn getNodeSource(tree: Tree, node: Node.Index) []const u8 {
1019 const token_starts = tree.tokens.items(.start);
1020 const first_token = tree.firstToken(node);
1021 const last_token = tree.lastToken(node);
1022 const start = token_starts[first_token];
1023 const len = tree.tokenSlice(last_token).len;
1024 return tree.source[start..][0..len];
1025 }
1026
969 pub fn globalVarDecl(tree: Tree, node: Node.Index) full.VarDecl {1027 pub fn globalVarDecl(tree: Tree, node: Node.Index) full.VarDecl {
970 assert(tree.nodes.items(.tag)[node] == .global_var_decl);1028 assert(tree.nodes.items(.tag)[node] == .global_var_decl);
971 const data = tree.nodes.items(.data)[node];1029 const data = tree.nodes.items(.data)[node];
...@@ -1653,7 +1711,31 @@ pub const Tree = struct {...@@ -1653,7 +1711,31 @@ pub const Tree = struct {
1653 const token_tags = tree.tokens.items(.tag);1711 const token_tags = tree.tokens.items(.tag);
1654 var result: full.FnProto = .{1712 var result: full.FnProto = .{
1655 .ast = info,1713 .ast = info,
1714 .visib_token = null,
1715 .extern_export_token = null,
1716 .lib_name = null,
1717 .name_token = null,
1718 .lparen = undefined,
1656 };1719 };
1720 var i = info.fn_token;
1721 while (i > 0) {
1722 i -= 1;
1723 switch (token_tags[i]) {
1724 .keyword_extern, .keyword_export => result.extern_export_token = i,
1725 .keyword_pub => result.visib_token = i,
1726 .string_literal => result.lib_name = i,
1727 else => break,
1728 }
1729 }
1730 const after_fn_token = info.fn_token + 1;
1731 if (token_tags[after_fn_token] == .identifier) {
1732 result.name_token = after_fn_token;
1733 result.lparen = after_fn_token + 1;
1734 } else {
1735 result.lparen = after_fn_token;
1736 }
1737 assert(token_tags[result.lparen] == .l_paren);
1738
1657 return result;1739 return result;
1658 }1740 }
16591741
...@@ -1924,6 +2006,11 @@ pub const full = struct {...@@ -1924,6 +2006,11 @@ pub const full = struct {
1924 };2006 };
19252007
1926 pub const FnProto = struct {2008 pub const FnProto = struct {
2009 visib_token: ?TokenIndex,
2010 extern_export_token: ?TokenIndex,
2011 lib_name: ?TokenIndex,
2012 name_token: ?TokenIndex,
2013 lparen: TokenIndex,
1927 ast: Ast,2014 ast: Ast,
19282015
1929 pub const Ast = struct {2016 pub const Ast = struct {
...@@ -1934,6 +2021,114 @@ pub const full = struct {...@@ -1934,6 +2021,114 @@ pub const full = struct {
1934 section_expr: Node.Index,2021 section_expr: Node.Index,
1935 callconv_expr: Node.Index,2022 callconv_expr: Node.Index,
1936 };2023 };
2024
2025 pub const Param = struct {
2026 first_doc_comment: ?TokenIndex,
2027 name_token: ?TokenIndex,
2028 comptime_noalias: ?TokenIndex,
2029 anytype_ellipsis3: ?TokenIndex,
2030 type_expr: Node.Index,
2031 };
2032
2033 /// Abstracts over the fact that anytype and ... are not included
2034 /// in the params slice, since they are simple identifiers and
2035 /// not sub-expressions.
2036 pub const Iterator = struct {
2037 tree: *const Tree,
2038 fn_proto: *const FnProto,
2039 param_i: usize,
2040 tok_i: TokenIndex,
2041 tok_flag: bool,
2042
2043 pub fn next(it: *Iterator) ?Param {
2044 const token_tags = it.tree.tokens.items(.tag);
2045 while (true) {
2046 var first_doc_comment: ?TokenIndex = null;
2047 var comptime_noalias: ?TokenIndex = null;
2048 var name_token: ?TokenIndex = null;
2049 if (!it.tok_flag) {
2050 if (it.param_i >= it.fn_proto.ast.params.len) {
2051 return null;
2052 }
2053 const param_type = it.fn_proto.ast.params[it.param_i];
2054 var tok_i = tree.firstToken(param_type) - 1;
2055 while (true) : (tok_i -= 1) switch (token_tags[tok_i]) {
2056 .colon => continue,
2057 .identifier => name_token = tok_i,
2058 .doc_comment => first_doc_comment = tok_i,
2059 .keyword_comptime, .keyword_noalias => comptime_noalias = tok_i,
2060 else => break,
2061 };
2062 it.param_i += 1;
2063 it.tok_i = tree.lastToken(param_type) + 1;
2064 it.tok_flag = true;
2065 return Param{
2066 .first_doc_comment = first_doc_comment,
2067 .comptime_noalias = comptime_noalias,
2068 .name_token = name_token,
2069 .anytype_ellipsis3 = null,
2070 .type_expr = param_type,
2071 };
2072 }
2073 // Look for anytype and ... params afterwards.
2074 if (token_tags[it.tok_i] == .comma) {
2075 it.tok_i += 1;
2076 } else {
2077 return null;
2078 }
2079 if (token_tags[it.tok_i] == .doc_comment) {
2080 first_doc_comment = it.tok_i;
2081 while (token_tags[it.tok_i] == .doc_comment) {
2082 it.tok_i += 1;
2083 }
2084 }
2085 switch (token_tags[it.tok_i]) {
2086 .ellipsis3 => {
2087 it.tok_flag = false; // Next iteration should return null.
2088 return Param{
2089 .first_doc_comment = first_doc_comment,
2090 .comptime_noalias = null,
2091 .name_token = null,
2092 .anytype_ellipsis3 = it.tok_i,
2093 .type_expr = 0,
2094 };
2095 },
2096 .keyword_noalias, .keyword_comptime => {
2097 comptime_noalias = it.tok_i;
2098 it.tok_i += 1;
2099 },
2100 else => {},
2101 }
2102 if (token_tags[it.tok_i] == .identifier and
2103 token_tags[it.tok_i + 1] == .colon)
2104 {
2105 name_token = it.tok_i;
2106 it.tok_i += 2;
2107 }
2108 if (token_tags[it.tok_i] == .keyword_anytype) {
2109 it.tok_i += 1;
2110 return Param{
2111 .first_doc_comment = first_doc_comment,
2112 .comptime_noalias = comptime_noalias,
2113 .name_token = name_token,
2114 .anytype_ellipsis3 = it.tok_i - 1,
2115 .type_expr = param_type,
2116 };
2117 }
2118 it.tok_flag = false;
2119 }
2120 }
2121 };
2122
2123 pub fn iterate(fn_proto: FnProto, tree: Tree) Iterator {
2124 return .{
2125 .tree = &tree,
2126 .fn_proto = &fn_proto,
2127 .param_i = 0,
2128 .tok_i = undefined,
2129 .tok_flag = false,
2130 };
2131 }
1937 };2132 };
19382133
1939 pub const StructInit = struct {2134 pub const StructInit = struct {
lib/std/zig/parser_test.zig+1-1
...@@ -4223,7 +4223,7 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b...@@ -4223,7 +4223,7 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
4223 return error.ParseError;4223 return error.ParseError;
4224 }4224 }
42254225
4226 const formatted = try std.zig.render(allocator, tree);4226 const formatted = try tree.render(allocator);
4227 anything_changed.* = !mem.eql(u8, formatted, source);4227 anything_changed.* = !mem.eql(u8, formatted, source);
4228 return formatted;4228 return formatted;
4229}4229}
lib/std/zig/render.zig+12-41
...@@ -13,28 +13,24 @@ const Token = std.zig.Token;...@@ -13,28 +13,24 @@ const Token = std.zig.Token;
13const indent_delta = 4;13const indent_delta = 4;
14const asm_indent_delta = 2;14const asm_indent_delta = 2;
1515
16pub const Error = error{16pub const Error = ast.Tree.RenderError;
17 /// Ran out of memory allocating call stack frames to complete rendering, or
18 /// ran out of memory allocating space in the output buffer.
19 OutOfMemory,
20};
2117
22const Writer = std.ArrayList(u8).Writer;18const Writer = std.ArrayList(u8).Writer;
23const Ais = std.io.AutoIndentingStream(Writer);19const Ais = std.io.AutoIndentingStream(Writer);
2420
25/// `gpa` is used for allocating the resulting formatted source code, as well as21pub fn renderTree(buffer: *std.ArrayList(u8), tree: ast.Tree) Error!void {
26/// for allocating extra stack memory if needed, because this function utilizes recursion.
27/// Note: that's not actually true yet, see https://github.com/ziglang/zig/issues/1006.
28/// Caller owns the returned slice of bytes, allocated with `gpa`.
29pub fn render(gpa: *mem.Allocator, tree: ast.Tree) Error![]u8 {
30 assert(tree.errors.len == 0); // Cannot render an invalid tree.22 assert(tree.errors.len == 0); // Cannot render an invalid tree.
23 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, buffer.writer());
24 const ais = &auto_indenting_stream;
3125
32 var buffer = std.ArrayList(u8).init(gpa);26 // Render all the line comments at the beginning of the file.
33 defer buffer.deinit();27 const src_start: usize = if (mem.startsWith(u8, tree.source, "\xEF\xBB\xBF")) 3 else 0;
28 const comment_end_loc: usize = tree.tokens.items(.start)[0];
29 _ = try renderCommentsAndNewlines(ais, tree, src_start, comment_end_loc);
3430
35 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, buffer.writer());31 for (tree.rootDecls()) |decl| {
36 try renderRoot(&auto_indenting_stream, tree);32 try renderMember(ais, tree, decl, .newline);
37 return buffer.toOwnedSlice();33 }
38}34}
3935
40/// Assumes that start is the first byte past the previous token and36/// Assumes that start is the first byte past the previous token and
...@@ -75,21 +71,6 @@ fn renderCommentsAndNewlines(ais: *Ais, tree: ast.Tree, start: usize, end: usize...@@ -75,21 +71,6 @@ fn renderCommentsAndNewlines(ais: *Ais, tree: ast.Tree, start: usize, end: usize
75 return index != start;71 return index != start;
76}72}
7773
78fn renderRoot(ais: *Ais, tree: ast.Tree) Error!void {
79 // Render all the line comments at the beginning of the file.
80 const src_start: usize = if (mem.startsWith(u8, tree.source, "\xEF\xBB\xBF")) 3 else 0;
81 const comment_end_loc: usize = tree.tokens.items(.start)[0];
82 _ = try renderCommentsAndNewlines(ais, tree, src_start, comment_end_loc);
83
84 // Root is always index 0.
85 const nodes_data = tree.nodes.items(.data);
86 const root_decls = tree.extra_data[nodes_data[0].lhs..nodes_data[0].rhs];
87
88 for (root_decls) |decl| {
89 try renderMember(ais, tree, decl, .newline);
90 }
91}
92
93fn renderMember(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) Error!void {74fn renderMember(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) Error!void {
94 const token_tags = tree.tokens.items(.tag);75 const token_tags = tree.tokens.items(.tag);
95 const main_tokens = tree.nodes.items(.main_token);76 const main_tokens = tree.nodes.items(.main_token);
...@@ -1944,17 +1925,7 @@ fn renderToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex, space: Sp...@@ -1944,17 +1925,7 @@ fn renderToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex, space: Sp
1944 const token_starts = tree.tokens.items(.start);1925 const token_starts = tree.tokens.items(.start);
19451926
1946 const token_start = token_starts[token_index];1927 const token_start = token_starts[token_index];
1947 const token_tag = token_tags[token_index];1928 const lexeme = tree.tokenSlice(token_index);
1948 const lexeme = token_tag.lexeme() orelse lexeme: {
1949 var tokenizer: std.zig.Tokenizer = .{
1950 .buffer = tree.source,
1951 .index = token_start,
1952 .pending_invalid_token = null,
1953 };
1954 const token = tokenizer.next();
1955 assert(token.tag == token_tag);
1956 break :lexeme tree.source[token.loc.start..token.loc.end];
1957 };
1958 try ais.writer().writeAll(lexeme);1929 try ais.writer().writeAll(lexeme);
19591930
1960 switch (space) {1931 switch (space) {
src/Compilation.zig+7-6
...@@ -921,7 +921,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -921,7 +921,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
921 // TODO this is duped so it can be freed in Container.deinit921 // TODO this is duped so it can be freed in Container.deinit
922 .sub_file_path = try gpa.dupe(u8, root_pkg.root_src_path),922 .sub_file_path = try gpa.dupe(u8, root_pkg.root_src_path),
923 .source = .{ .unloaded = {} },923 .source = .{ .unloaded = {} },
924 .contents = .{ .not_available = {} },924 .tree = undefined,
925 .status = .never_loaded,925 .status = .never_loaded,
926 .pkg = root_pkg,926 .pkg = root_pkg,
927 .root_container = .{927 .root_container = .{
...@@ -1882,7 +1882,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {...@@ -1882,7 +1882,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
1882 const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});1882 const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});
1883 const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);1883 const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);
1884 var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};1884 var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};
1885 const tree = translate_c.translate(1885 var tree = translate_c.translate(
1886 comp.gpa,1886 comp.gpa,
1887 new_argv.ptr,1887 new_argv.ptr,
1888 new_argv.ptr + new_argv.len,1888 new_argv.ptr + new_argv.len,
...@@ -1901,7 +1901,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {...@@ -1901,7 +1901,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
1901 };1901 };
1902 },1902 },
1903 };1903 };
1904 defer tree.deinit();1904 defer tree.deinit(comp.gpa);
19051905
1906 if (comp.verbose_cimport) {1906 if (comp.verbose_cimport) {
1907 log.info("C import .d file: {s}", .{out_dep_path});1907 log.info("C import .d file: {s}", .{out_dep_path});
...@@ -1919,9 +1919,10 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {...@@ -1919,9 +1919,10 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
1919 var out_zig_file = try o_dir.createFile(cimport_zig_basename, .{});1919 var out_zig_file = try o_dir.createFile(cimport_zig_basename, .{});
1920 defer out_zig_file.close();1920 defer out_zig_file.close();
19211921
1922 var bos = std.io.bufferedWriter(out_zig_file.writer());1922 const formatted = try tree.render(comp.gpa);
1923 _ = try std.zig.render(comp.gpa, bos.writer(), tree);1923 defer comp.gpa.free(formatted);
1924 try bos.flush();1924
1925 try out_zig_file.writeAll(formatted);
19251926
1926 man.writeManifest() catch |err| {1927 man.writeManifest() catch |err| {
1927 log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)});1928 log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)});
src/Module.zig+876-608
...@@ -244,9 +244,9 @@ pub const Decl = struct {...@@ -244,9 +244,9 @@ pub const Decl = struct {
244 }244 }
245245
246 pub fn src(self: Decl) usize {246 pub fn src(self: Decl) usize {
247 const tree = self.container.file_scope.contents.tree;247 const tree = &self.container.file_scope.tree;
248 const decl_node = tree.root_node.decls()[self.src_index];248 const decl_node = tree.rootDecls()[self.src_index];
249 return tree.token_locs[decl_node.firstToken()].start;249 return tree.tokens.items(.start)[tree.firstToken(decl_node)];
250 }250 }
251251
252 pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash {252 pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash {
...@@ -536,6 +536,12 @@ pub const Scope = struct {...@@ -536,6 +536,12 @@ pub const Scope = struct {
536 pub const File = struct {536 pub const File = struct {
537 pub const base_tag: Tag = .file;537 pub const base_tag: Tag = .file;
538 base: Scope = Scope{ .tag = base_tag },538 base: Scope = Scope{ .tag = base_tag },
539 status: enum {
540 never_loaded,
541 unloaded_success,
542 unloaded_parse_failure,
543 loaded_success,
544 },
539545
540 /// Relative to the owning package's root_src_dir.546 /// Relative to the owning package's root_src_dir.
541 /// Reference to external memory, not owned by File.547 /// Reference to external memory, not owned by File.
...@@ -544,16 +550,8 @@ pub const Scope = struct {...@@ -544,16 +550,8 @@ pub const Scope = struct {
544 unloaded: void,550 unloaded: void,
545 bytes: [:0]const u8,551 bytes: [:0]const u8,
546 },552 },
547 contents: union {553 /// Whether this is populated or not depends on `status`.
548 not_available: void,554 tree: ast.Tree,
549 tree: *ast.Tree,
550 },
551 status: enum {
552 never_loaded,
553 unloaded_success,
554 unloaded_parse_failure,
555 loaded_success,
556 },
557 /// Package that this file is a part of, managed externally.555 /// Package that this file is a part of, managed externally.
558 pkg: *Package,556 pkg: *Package,
559557
...@@ -567,7 +565,7 @@ pub const Scope = struct {...@@ -567,7 +565,7 @@ pub const Scope = struct {
567 => {},565 => {},
568566
569 .loaded_success => {567 .loaded_success => {
570 self.contents.tree.deinit();568 self.tree.deinit(gpa);
571 self.status = .unloaded_success;569 self.status = .unloaded_success;
572 },570 },
573 }571 }
...@@ -905,7 +903,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {...@@ -905,7 +903,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {
905 .unreferenced => false,903 .unreferenced => false,
906 };904 };
907905
908 const type_changed = mod.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {906 const type_changed = mod.astgenAndSemaDecl(decl) catch |err| switch (err) {
909 error.OutOfMemory => return error.OutOfMemory,907 error.OutOfMemory => return error.OutOfMemory,
910 error.AnalysisFail => return error.AnalysisFail,908 error.AnalysisFail => return error.AnalysisFail,
911 else => {909 else => {
...@@ -947,129 +945,69 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {...@@ -947,129 +945,69 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {
947 }945 }
948}946}
949947
950fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {948/// Returns `true` if the Decl type changed.
949/// Returns `true` if this is the first time analyzing the Decl.
950/// Returns `false` otherwise.
951fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {
951 const tracy = trace(@src());952 const tracy = trace(@src());
952 defer tracy.end();953 defer tracy.end();
953954
954 const tree = try self.getAstTree(decl.container.file_scope);955 const tree = try mod.getAstTree(decl.container.file_scope);
955 const ast_node = tree.root_node.decls()[decl.src_index];956 const node_tags = tree.nodes.items(.tag);
956 switch (ast_node.tag) {957 const node_datas = tree.nodes.items(.data);
957 .FnProto => {958 const decl_node = tree.rootDecls()[decl.src_index];
958 const fn_proto = ast_node.castTag(.FnProto).?;959 switch (node_tags[decl_node]) {
960 .fn_decl => {
961 const fn_proto = node_datas[decl_node].lhs;
962 const body = node_datas[decl_node].rhs;
963 switch (node_tags[fn_proto]) {
964 .fn_proto_simple => {
965 var params: [1]ast.Node.Index = undefined;
966 return mod.astgenAndSemaFn(decl, tree, body, tree.fnProtoSimple(&params, fn_proto));
967 },
968 .fn_proto_multi => return mod.astgenAndSemaFn(decl, tree, body, tree.fnProtoMulti(fn_proto)),
969 .fn_proto_one => {
970 var params: [1]ast.Node.Index = undefined;
971 return mod.astgenAndSemaFn(decl, tree, body, tree.fnProtoOne(&params, fn_proto));
972 },
973 .fn_proto => return mod.astgenAndSemaFn(decl, tree, body, tree.fnProto(fn_proto)),
974 else => unreachable,
975 }
976 },
977 .fn_proto_simple => {
978 var params: [1]ast.Node.Index = undefined;
979 return mod.astgenAndSemaFn(decl, tree, null, tree.fnProtoSimple(&params, decl_node));
980 },
981 .fn_proto_multi => return mod.astgenAndSemaFn(decl, tree, null, tree.fnProtoMulti(decl_node)),
982 .fn_proto_one => {
983 var params: [1]ast.Node.Index = undefined;
984 return mod.astgenAndSemaFn(decl, tree, null, tree.fnProtoOne(&params, decl_node));
985 },
986 .fn_proto => return mod.astgenAndSemaFn(decl, tree, null, tree.fnProto(decl_node)),
959987
988 .global_var_decl => return mod.astgenAndSemaVarDecl(decl, tree, tree.globalVarDecl(decl_node)),
989 .local_var_decl => return mod.astgenAndSemaVarDecl(decl, tree, tree.localVarDecl(decl_node)),
990 .simple_var_decl => return mod.astgenAndSemaVarDecl(decl, tree, tree.simpleVarDecl(decl_node)),
991 .aligned_var_decl => return mod.astgenAndSemaVarDecl(decl, tree, tree.alignedVarDecl(decl_node)),
992
993 .@"comptime" => {
960 decl.analysis = .in_progress;994 decl.analysis = .in_progress;
961995
962 // This arena allocator's memory is discarded at the end of this function. It is used996 // A comptime decl does not store any value so we can just deinit this arena after analysis is done.
963 // to determine the type of the function, and hence the type of the decl, which is needed997 var analysis_arena = std.heap.ArenaAllocator.init(self.gpa);
964 // to complete the Decl analysis.998 defer analysis_arena.deinit();
965 var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);999 var gen_scope: Scope.GenZIR = .{
966 defer fn_type_scope_arena.deinit();
967 var fn_type_scope: Scope.GenZIR = .{
968 .decl = decl,1000 .decl = decl,
969 .arena = &fn_type_scope_arena.allocator,1001 .arena = &analysis_arena.allocator,
970 .parent = &decl.container.base,1002 .parent = &decl.container.base,
971 };1003 };
972 defer fn_type_scope.instructions.deinit(self.gpa);1004 defer gen_scope.instructions.deinit(self.gpa);
973
974 decl.is_pub = fn_proto.getVisibToken() != null;
975
976 const param_decls = fn_proto.params();
977 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);
978
979 const fn_src = tree.token_locs[fn_proto.fn_token].start;
980 const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{
981 .ty = Type.initTag(.type),
982 .val = Value.initTag(.type_type),
983 });
984 const type_type_rl: astgen.ResultLoc = .{ .ty = type_type };
985 for (param_decls) |param_decl, i| {
986 const param_type_node = switch (param_decl.param_type) {
987 .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}),
988 .type_expr => |node| node,
989 };
990 param_types[i] = try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node);
991 }
992 if (fn_proto.getVarArgsToken()) |var_args_token| {
993 return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{});
994 }
995 if (fn_proto.getLibName()) |lib_name| blk: {
996 const lib_name_str = mem.trim(u8, tree.tokenSlice(lib_name.firstToken()), "\""); // TODO: call identifierTokenString
997 log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str});
998 const target = self.comp.getTarget();
999 if (target_util.is_libc_lib_name(target, lib_name_str)) {
1000 if (!self.comp.bin_file.options.link_libc) {
1001 return self.failNode(
1002 &fn_type_scope.base,
1003 lib_name,
1004 "dependency on libc must be explicitly specified in the build command",
1005 .{},
1006 );
1007 }
1008 break :blk;
1009 }
1010 if (target_util.is_libcpp_lib_name(target, lib_name_str)) {
1011 if (!self.comp.bin_file.options.link_libcpp) {
1012 return self.failNode(
1013 &fn_type_scope.base,
1014 lib_name,
1015 "dependency on libc++ must be explicitly specified in the build command",
1016 .{},
1017 );
1018 }
1019 break :blk;
1020 }
1021 if (!target.isWasm() and !self.comp.bin_file.options.pic) {
1022 return self.failNode(
1023 &fn_type_scope.base,
1024 lib_name,
1025 "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.",
1026 .{ lib_name, lib_name },
1027 );
1028 }
1029 self.comp.stage1AddLinkLib(lib_name_str) catch |err| {
1030 return self.failNode(
1031 &fn_type_scope.base,
1032 lib_name,
1033 "unable to add link lib '{s}': {s}",
1034 .{ lib_name, @errorName(err) },
1035 );
1036 };
1037 }
1038 if (fn_proto.getAlignExpr()) |align_expr| {
1039 return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{});
1040 }
1041 if (fn_proto.getSectionExpr()) |sect_expr| {
1042 return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{});
1043 }
1044 if (fn_proto.getCallconvExpr()) |callconv_expr| {
1045 return self.failNode(
1046 &fn_type_scope.base,
1047 callconv_expr,
1048 "TODO implement function calling convention expression",
1049 .{},
1050 );
1051 }
1052 const return_type_expr = switch (fn_proto.return_type) {
1053 .Explicit => |node| node,
1054 .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}),
1055 .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}),
1056 };
1057
1058 const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr);
1059 const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{
1060 .return_type = return_type_inst,
1061 .param_types = param_types,
1062 }, .{});
10631005
1006 _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr);
1064 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {1007 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
1065 zir.dumpZir(self.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {};1008 zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {};
1066 }1009 }
10671010
1068 // We need the memory for the Type to go into the arena for the Decl
1069 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
1070 errdefer decl_arena.deinit();
1071 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
1072
1073 var inst_table = Scope.Block.InstTable.init(self.gpa);1011 var inst_table = Scope.Block.InstTable.init(self.gpa);
1074 defer inst_table.deinit();1012 defer inst_table.deinit();
10751013
...@@ -1082,426 +1020,561 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1082,426 +1020,561 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1082 .owner_decl = decl,1020 .owner_decl = decl,
1083 .src_decl = decl,1021 .src_decl = decl,
1084 .instructions = .{},1022 .instructions = .{},
1085 .arena = &decl_arena.allocator,1023 .arena = &analysis_arena.allocator,
1086 .inlining = null,1024 .inlining = null,
1087 .is_comptime = false,1025 .is_comptime = true,
1088 .branch_quota = &branch_quota,1026 .branch_quota = &branch_quota,
1089 };1027 };
1090 defer block_scope.instructions.deinit(self.gpa);1028 defer block_scope.instructions.deinit(self.gpa);
10911029
1092 const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{1030 _ = try zir_sema.analyzeBody(self, &block_scope, .{
1093 .instructions = fn_type_scope.instructions.items,1031 .instructions = gen_scope.instructions.items,
1094 });1032 });
1095 const body_node = fn_proto.getBodyNode() orelse {
1096 // Extern function.
1097 var type_changed = true;
1098 if (decl.typedValueManaged()) |tvm| {
1099 type_changed = !tvm.typed_value.ty.eql(fn_type);
11001033
1101 tvm.deinit(self.gpa);1034 decl.analysis = .complete;
1102 }1035 decl.generation = self.generation;
1103 const fn_val = try Value.Tag.extern_fn.create(&decl_arena.allocator, decl);1036 return true;
1037 },
1038 .UsingNamespace => @panic("TODO usingnamespace decl"),
1039 else => unreachable,
1040 }
1041}
11041042
1105 decl_arena_state.* = decl_arena.state;1043fn astgenAndSemaFn(
1106 decl.typed_value = .{1044 mod: *Module,
1107 .most_recent = .{1045 decl: *Decl,
1108 .typed_value = .{ .ty = fn_type, .val = fn_val },1046 tree: ast.Tree,
1109 .arena = decl_arena_state,1047 body_node: ast.Node.Index,
1110 },1048 fn_proto: ast.full.FnProto,
1111 };1049) !bool {
1112 decl.analysis = .complete;1050 const tracy = trace(@src());
1113 decl.generation = self.generation;1051 defer tracy.end();
11141052
1115 try self.comp.bin_file.allocateDeclIndexes(decl);1053 decl.analysis = .in_progress;
1116 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
11171054
1118 if (type_changed and self.emit_h != null) {1055 const token_starts = tree.tokens.items(.start);
1119 try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
1120 }
11211056
1122 return type_changed;1057 // This arena allocator's memory is discarded at the end of this function. It is used
1123 };1058 // to determine the type of the function, and hence the type of the decl, which is needed
1059 // to complete the Decl analysis.
1060 var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1061 defer fn_type_scope_arena.deinit();
1062 var fn_type_scope: Scope.GenZIR = .{
1063 .decl = decl,
1064 .arena = &fn_type_scope_arena.allocator,
1065 .parent = &decl.container.base,
1066 };
1067 defer fn_type_scope.instructions.deinit(self.gpa);
11241068
1125 const new_func = try decl_arena.allocator.create(Fn);1069 decl.is_pub = fn_proto.visib_token != null;
1126 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
11271070
1128 const fn_zir: zir.Body = blk: {1071 // The AST params array does not contain anytype and ... parameters.
1129 // We put the ZIR inside the Decl arena.1072 // We must iterate to count how many param types to allocate.
1130 var gen_scope: Scope.GenZIR = .{1073 const param_count = blk: {
1131 .decl = decl,1074 var count: usize = 0;
1132 .arena = &decl_arena.allocator,1075 var it = fn_proto.iterate(tree);
1133 .parent = &decl.container.base,1076 while (it.next()) |_| {
1134 };1077 count += 1;
1135 defer gen_scope.instructions.deinit(self.gpa);1078 }
11361079 break :blk count;
1137 // We need an instruction for each parameter, and they must be first in the body.1080 };
1138 try gen_scope.instructions.resize(self.gpa, fn_proto.params_len);1081 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_count);
1139 var params_scope = &gen_scope.base;1082 const fn_src = token_starts[fn_proto.ast.fn_token];
1140 for (fn_proto.params()) |param, i| {1083 const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{
1141 const name_token = param.name_token.?;1084 .ty = Type.initTag(.type),
1142 const src = tree.token_locs[name_token].start;1085 .val = Value.initTag(.type_type),
1143 const param_name = try self.identifierTokenString(&gen_scope.base, name_token);1086 });
1144 const arg = try decl_arena.allocator.create(zir.Inst.Arg);1087 const type_type_rl: astgen.ResultLoc = .{ .ty = type_type };
1145 arg.* = .{1088
1146 .base = .{1089 {
1147 .tag = .arg,1090 var param_type_i: usize = 0;
1148 .src = src,1091 var it = fn_proto.iterate(tree);
1149 },1092 while (it.next()) |param| : (param_type_i += 1) {
1150 .positionals = .{1093 if (param.anytype_ellipsis3) |token| {
1151 .name = param_name,1094 switch (token_tags[token]) {
1152 },1095 .keyword_anytype => return self.failTok(
1153 .kw_args = .{},1096 &fn_type_scope.base,
1154 };1097 tok_i,
1155 gen_scope.instructions.items[i] = &arg.base;1098 "TODO implement anytype parameter",
1156 const sub_scope = try decl_arena.allocator.create(Scope.LocalVal);1099 .{},
1157 sub_scope.* = .{1100 ),
1158 .parent = params_scope,1101 .ellipsis3 => return self.failTok(
1159 .gen_zir = &gen_scope,1102 &fn_type_scope.base,
1160 .name = param_name,1103 token,
1161 .inst = &arg.base,1104 "TODO implement var args",
1162 };1105 .{},
1163 params_scope = &sub_scope.base;1106 ),
1107 else => unreachable,
1164 }1108 }
1109 }
1110 const param_type_node = param.type_expr;
1111 assert(param_type_node != 0);
1112 param_types[param_type_i] =
1113 try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node);
1114 }
1115 assert(param_type_i == param_count);
1116 }
1117 if (fn_proto.lib_name) |lib_name| blk: {
1118 // TODO call std.zig.parseStringLiteral
1119 const lib_name_str = mem.trim(u8, tree.tokenSlice(lib_name), "\"");
1120 log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str});
1121 const target = self.comp.getTarget();
1122 if (target_util.is_libc_lib_name(target, lib_name_str)) {
1123 if (!self.comp.bin_file.options.link_libc) {
1124 return self.failTok(
1125 &fn_type_scope.base,
1126 lib_name,
1127 "dependency on libc must be explicitly specified in the build command",
1128 .{},
1129 );
1130 }
1131 break :blk;
1132 }
1133 if (target_util.is_libcpp_lib_name(target, lib_name_str)) {
1134 if (!self.comp.bin_file.options.link_libcpp) {
1135 return self.failTok(
1136 &fn_type_scope.base,
1137 lib_name,
1138 "dependency on libc++ must be explicitly specified in the build command",
1139 .{},
1140 );
1141 }
1142 break :blk;
1143 }
1144 if (!target.isWasm() and !self.comp.bin_file.options.pic) {
1145 return self.failTok(
1146 &fn_type_scope.base,
1147 lib_name,
1148 "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.",
1149 .{ lib_name, lib_name },
1150 );
1151 }
1152 self.comp.stage1AddLinkLib(lib_name_str) catch |err| {
1153 return self.failTok(
1154 &fn_type_scope.base,
1155 lib_name,
1156 "unable to add link lib '{s}': {s}",
1157 .{ lib_name, @errorName(err) },
1158 );
1159 };
1160 }
1161 if (fn_proto.ast.align_expr) |align_expr| {
1162 return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{});
1163 }
1164 if (fn_proto.ast.section_expr) |sect_expr| {
1165 return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{});
1166 }
1167 if (fn_proto.ast.callconv_expr) |callconv_expr| {
1168 return self.failNode(
1169 &fn_type_scope.base,
1170 callconv_expr,
1171 "TODO implement function calling convention expression",
1172 .{},
1173 );
1174 }
1175 const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
1176 if (token_tags[maybe_bang] == .bang) {
1177 return self.failTok(&fn_type_scope.base, maybe_bang, "TODO implement inferred error sets", .{});
1178 }
1179 const return_type_inst = try astgen.expr(
1180 self,
1181 &fn_type_scope.base,
1182 type_type_rl,
1183 fn_proto.ast.return_type,
1184 );
1185 const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{
1186 .return_type = return_type_inst,
1187 .param_types = param_types,
1188 }, .{});
11651189
1166 const body_block = body_node.cast(ast.Node.Block).?;1190 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
1191 zir.dumpZir(self.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {};
1192 }
11671193
1168 try astgen.blockExpr(self, params_scope, body_block);1194 // We need the memory for the Type to go into the arena for the Decl
1195 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
1196 errdefer decl_arena.deinit();
1197 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
11691198
1170 if (gen_scope.instructions.items.len == 0 or1199 var inst_table = Scope.Block.InstTable.init(self.gpa);
1171 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())1200 defer inst_table.deinit();
1172 {
1173 const src = tree.token_locs[body_block.rbrace].start;
1174 _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);
1175 }
11761201
1177 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {1202 var branch_quota: u32 = default_eval_branch_quota;
1178 zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {};
1179 }
11801203
1181 break :blk .{1204 var block_scope: Scope.Block = .{
1182 .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),1205 .parent = null,
1183 };1206 .inst_table = &inst_table,
1184 };1207 .func = null,
1208 .owner_decl = decl,
1209 .src_decl = decl,
1210 .instructions = .{},
1211 .arena = &decl_arena.allocator,
1212 .inlining = null,
1213 .is_comptime = false,
1214 .branch_quota = &branch_quota,
1215 };
1216 defer block_scope.instructions.deinit(self.gpa);
11851217
1186 const is_inline = blk: {1218 const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{
1187 if (fn_proto.getExternExportInlineToken()) |maybe_inline_token| {1219 .instructions = fn_type_scope.instructions.items,
1188 if (tree.token_ids[maybe_inline_token] == .Keyword_inline) {1220 });
1189 break :blk true;1221 if (body_node == 0) {
1190 }1222 // Extern function.
1191 }1223 var type_changed = true;
1192 break :blk false;1224 if (decl.typedValueManaged()) |tvm| {
1193 };1225 type_changed = !tvm.typed_value.ty.eql(fn_type);
1194 const anal_state = ([2]Fn.Analysis{ .queued, .inline_only })[@boolToInt(is_inline)];
11951226
1196 new_func.* = .{1227 tvm.deinit(self.gpa);
1197 .state = anal_state,1228 }
1198 .zir = fn_zir,1229 const fn_val = try Value.Tag.extern_fn.create(&decl_arena.allocator, decl);
1199 .body = undefined,
1200 .owner_decl = decl,
1201 };
1202 fn_payload.* = .{
1203 .base = .{ .tag = .function },
1204 .data = new_func,
1205 };
12061230
1207 var prev_type_has_bits = false;1231 decl_arena_state.* = decl_arena.state;
1208 var prev_is_inline = false;1232 decl.typed_value = .{
1209 var type_changed = true;1233 .most_recent = .{
1234 .typed_value = .{ .ty = fn_type, .val = fn_val },
1235 .arena = decl_arena_state,
1236 },
1237 };
1238 decl.analysis = .complete;
1239 decl.generation = self.generation;
12101240
1211 if (decl.typedValueManaged()) |tvm| {1241 try self.comp.bin_file.allocateDeclIndexes(decl);
1212 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();1242 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
1213 type_changed = !tvm.typed_value.ty.eql(fn_type);
1214 if (tvm.typed_value.val.castTag(.function)) |payload| {
1215 const prev_func = payload.data;
1216 prev_is_inline = prev_func.state == .inline_only;
1217 }
12181243
1219 tvm.deinit(self.gpa);1244 if (type_changed and self.emit_h != null) {
1220 }1245 try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
1246 }
12211247
1222 decl_arena_state.* = decl_arena.state;1248 return type_changed;
1223 decl.typed_value = .{1249 }
1224 .most_recent = .{1250
1225 .typed_value = .{1251 const new_func = try decl_arena.allocator.create(Fn);
1226 .ty = fn_type,1252 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
1227 .val = Value.initPayload(&fn_payload.base),1253
1228 },1254 const fn_zir: zir.Body = blk: {
1229 .arena = decl_arena_state,1255 // We put the ZIR inside the Decl arena.
1256 var gen_scope: Scope.GenZIR = .{
1257 .decl = decl,
1258 .arena = &decl_arena.allocator,
1259 .parent = &decl.container.base,
1260 };
1261 defer gen_scope.instructions.deinit(self.gpa);
1262
1263 // We need an instruction for each parameter, and they must be first in the body.
1264 try gen_scope.instructions.resize(self.gpa, param_count);
1265 var params_scope = &gen_scope.base;
1266 var i: usize = 0;
1267 var it = fn_proto.iterate(tree);
1268 while (it.next()) |param| : (i += 1) {
1269 const name_token = param.name_token.?;
1270 const src = token_starts[name_token];
1271 const param_name = try self.identifierTokenString(&gen_scope.base, name_token);
1272 const arg = try decl_arena.allocator.create(zir.Inst.NoOp);
1273 arg.* = .{
1274 .base = .{
1275 .tag = .arg,
1276 .src = src,
1230 },1277 },
1278 .positionals = .{},
1279 .kw_args = .{},
1231 };1280 };
1232 decl.analysis = .complete;1281 gen_scope.instructions.items[i] = &arg.base;
1233 decl.generation = self.generation;1282 const sub_scope = try decl_arena.allocator.create(Scope.LocalVal);
1283 sub_scope.* = .{
1284 .parent = params_scope,
1285 .gen_zir = &gen_scope,
1286 .name = param_name,
1287 .inst = &arg.base,
1288 };
1289 params_scope = &sub_scope.base;
1290 }
12341291
1235 if (!is_inline and fn_type.hasCodeGenBits()) {1292 try astgen.blockExpr(self, params_scope, body_node);
1236 // We don't fully codegen the decl until later, but we do need to reserve a global
1237 // offset table index for it. This allows us to codegen decls out of dependency order,
1238 // increasing how many computations can be done in parallel.
1239 try self.comp.bin_file.allocateDeclIndexes(decl);
1240 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
1241 if (type_changed and self.emit_h != null) {
1242 try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
1243 }
1244 } else if (!prev_is_inline and prev_type_has_bits) {
1245 self.comp.bin_file.freeDecl(decl);
1246 }
12471293
1248 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {1294 if (gen_scope.instructions.items.len == 0 or
1249 if (tree.token_ids[maybe_export_token] == .Keyword_export) {1295 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())
1250 if (is_inline) {1296 {
1251 return self.failTok(1297 const src = token_starts[tree.lastToken(body_node)];
1252 &block_scope.base,1298 _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);
1253 maybe_export_token,1299 }
1254 "export of inline function",
1255 .{},
1256 );
1257 }
1258 const export_src = tree.token_locs[maybe_export_token].start;
1259 const name_loc = tree.token_locs[fn_proto.getNameToken().?];
1260 const name = tree.tokenSliceLoc(name_loc);
1261 // The scope needs to have the decl in it.
1262 try self.analyzeExport(&block_scope.base, export_src, name, decl);
1263 }
1264 }
1265 return type_changed or is_inline != prev_is_inline;
1266 },
1267 .VarDecl => {
1268 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node);
12691300
1270 decl.analysis = .in_progress;1301 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
1302 zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {};
1303 }
12711304
1272 // We need the memory for the Type to go into the arena for the Decl1305 break :blk .{
1273 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);1306 .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
1274 errdefer decl_arena.deinit();1307 };
1275 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);1308 };
12761309
1277 var decl_inst_table = Scope.Block.InstTable.init(self.gpa);1310 const is_inline = fn_type.fnCallingConvention() == .Inline;
1278 defer decl_inst_table.deinit();1311 const anal_state: Fn.Analysis = if (is_inline) .inline_only else .queued;
12791312
1280 var branch_quota: u32 = default_eval_branch_quota;1313 new_func.* = .{
1314 .state = anal_state,
1315 .zir = fn_zir,
1316 .body = undefined,
1317 .owner_decl = decl,
1318 };
1319 fn_payload.* = .{
1320 .base = .{ .tag = .function },
1321 .data = new_func,
1322 };
12811323
1282 var block_scope: Scope.Block = .{1324 var prev_type_has_bits = false;
1283 .parent = null,1325 var prev_is_inline = false;
1284 .inst_table = &decl_inst_table,1326 var type_changed = true;
1285 .func = null,
1286 .owner_decl = decl,
1287 .src_decl = decl,
1288 .instructions = .{},
1289 .arena = &decl_arena.allocator,
1290 .inlining = null,
1291 .is_comptime = true,
1292 .branch_quota = &branch_quota,
1293 };
1294 defer block_scope.instructions.deinit(self.gpa);
12951327
1296 decl.is_pub = var_decl.getVisibToken() != null;1328 if (decl.typedValueManaged()) |tvm| {
1297 const is_extern = blk: {1329 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
1298 const maybe_extern_token = var_decl.getExternExportToken() orelse1330 type_changed = !tvm.typed_value.ty.eql(fn_type);
1299 break :blk false;1331 if (tvm.typed_value.val.castTag(.function)) |payload| {
1300 if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false;1332 const prev_func = payload.data;
1301 if (var_decl.getInitNode()) |some| {1333 prev_is_inline = prev_func.state == .inline_only;
1302 return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{});1334 }
1303 }
1304 break :blk true;
1305 };
1306 if (var_decl.getLibName()) |lib_name| {
1307 assert(is_extern);
1308 return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{});
1309 }
1310 const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var;
1311 const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: {
1312 if (!is_mutable) {
1313 return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{});
1314 }
1315 break :blk true;
1316 } else false;
1317 assert(var_decl.getComptimeToken() == null);
1318 if (var_decl.getAlignNode()) |align_expr| {
1319 return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{});
1320 }
1321 if (var_decl.getSectionNode()) |sect_expr| {
1322 return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{});
1323 }
13241335
1325 const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: {1336 tvm.deinit(self.gpa);
1326 var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);1337 }
1327 defer gen_scope_arena.deinit();
1328 var gen_scope: Scope.GenZIR = .{
1329 .decl = decl,
1330 .arena = &gen_scope_arena.allocator,
1331 .parent = &decl.container.base,
1332 };
1333 defer gen_scope.instructions.deinit(self.gpa);
13341338
1335 const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: {1339 decl_arena_state.* = decl_arena.state;
1336 const src = tree.token_locs[type_node.firstToken()].start;1340 decl.typed_value = .{
1337 const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{1341 .most_recent = .{
1338 .ty = Type.initTag(.type),1342 .typed_value = .{
1339 .val = Value.initTag(.type_type),1343 .ty = fn_type,
1340 });1344 .val = Value.initPayload(&fn_payload.base),
1341 const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node);1345 },
1342 break :rl .{ .ty = var_type };1346 .arena = decl_arena_state,
1343 } else .none;1347 },
1348 };
1349 decl.analysis = .complete;
1350 decl.generation = self.generation;
1351
1352 if (!is_inline and fn_type.hasCodeGenBits()) {
1353 // We don't fully codegen the decl until later, but we do need to reserve a global
1354 // offset table index for it. This allows us to codegen decls out of dependency order,
1355 // increasing how many computations can be done in parallel.
1356 try self.comp.bin_file.allocateDeclIndexes(decl);
1357 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
1358 if (type_changed and self.emit_h != null) {
1359 try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
1360 }
1361 } else if (!prev_is_inline and prev_type_has_bits) {
1362 self.comp.bin_file.freeDecl(decl);
1363 }
13441364
1345 const init_inst = try astgen.comptimeExpr(self, &gen_scope.base, init_result_loc, init_node);1365 if (fn_proto.extern_export_token) |maybe_export_token| {
1346 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {1366 if (token_tags[maybe_export_token] == .Keyword_export) {
1347 zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {};1367 if (is_inline) {
1348 }1368 return self.failTok(
1369 &block_scope.base,
1370 maybe_export_token,
1371 "export of inline function",
1372 .{},
1373 );
1374 }
1375 const export_src = token_starts[maybe_export_token];
1376 const name = tree.tokenSlice(fn_proto.name_token.?); // TODO identifierTokenString
1377 // The scope needs to have the decl in it.
1378 try self.analyzeExport(&block_scope.base, export_src, name, decl);
1379 }
1380 }
1381 return type_changed or is_inline != prev_is_inline;
1382}
13491383
1350 var var_inst_table = Scope.Block.InstTable.init(self.gpa);1384fn astgenAndSemaVarDecl(
1351 defer var_inst_table.deinit();1385 mod: *Module,
13521386 decl: *Decl,
1353 var branch_quota_vi: u32 = default_eval_branch_quota;1387 tree: ast.Tree,
1354 var inner_block: Scope.Block = .{1388 var_decl: ast.full.VarDecl,
1355 .parent = null,1389) !bool {
1356 .inst_table = &var_inst_table,1390 const tracy = trace(@src());
1357 .func = null,1391 defer tracy.end();
1358 .owner_decl = decl,
1359 .src_decl = decl,
1360 .instructions = .{},
1361 .arena = &gen_scope_arena.allocator,
1362 .inlining = null,
1363 .is_comptime = true,
1364 .branch_quota = &branch_quota_vi,
1365 };
1366 defer inner_block.instructions.deinit(self.gpa);
1367 try zir_sema.analyzeBody(self, &inner_block, .{
1368 .instructions = gen_scope.instructions.items,
1369 });
13701392
1371 // The result location guarantees the type coercion.1393 decl.analysis = .in_progress;
1372 const analyzed_init_inst = var_inst_table.get(init_inst).?;
1373 // The is_comptime in the Scope.Block guarantees the result is comptime-known.
1374 const val = analyzed_init_inst.value().?;
13751394
1376 const ty = try analyzed_init_inst.ty.copy(block_scope.arena);1395 const token_starts = tree.tokens.items(.start);
1377 break :vi .{
1378 .ty = ty,
1379 .val = try val.copy(block_scope.arena),
1380 };
1381 } else if (!is_extern) {
1382 return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{});
1383 } else if (var_decl.getTypeNode()) |type_node| vi: {
1384 // Temporary arena for the zir instructions.
1385 var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1386 defer type_scope_arena.deinit();
1387 var type_scope: Scope.GenZIR = .{
1388 .decl = decl,
1389 .arena = &type_scope_arena.allocator,
1390 .parent = &decl.container.base,
1391 };
1392 defer type_scope.instructions.deinit(self.gpa);
13931396
1394 const var_type = try astgen.typeExpr(self, &type_scope.base, type_node);1397 // We need the memory for the Type to go into the arena for the Decl
1395 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {1398 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
1396 zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {};1399 errdefer decl_arena.deinit();
1397 }1400 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
13981401
1399 const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{1402 var decl_inst_table = Scope.Block.InstTable.init(self.gpa);
1400 .instructions = type_scope.instructions.items,1403 defer decl_inst_table.deinit();
1401 });
1402 break :vi .{
1403 .ty = ty,
1404 .val = null,
1405 };
1406 } else {
1407 return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{});
1408 };
14091404
1410 if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {1405 var branch_quota: u32 = default_eval_branch_quota;
1411 return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty});
1412 }
14131406
1414 var type_changed = true;1407 var block_scope: Scope.Block = .{
1415 if (decl.typedValueManaged()) |tvm| {1408 .parent = null,
1416 type_changed = !tvm.typed_value.ty.eql(var_info.ty);1409 .inst_table = &decl_inst_table,
1410 .func = null,
1411 .owner_decl = decl,
1412 .src_decl = decl,
1413 .instructions = .{},
1414 .arena = &decl_arena.allocator,
1415 .inlining = null,
1416 .is_comptime = true,
1417 .branch_quota = &branch_quota,
1418 };
1419 defer block_scope.instructions.deinit(self.gpa);
1420
1421 decl.is_pub = var_decl.getVisibToken() != null;
1422 const is_extern = blk: {
1423 const maybe_extern_token = var_decl.getExternExportToken() orelse
1424 break :blk false;
1425 if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false;
1426 if (var_decl.getInitNode()) |some| {
1427 return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{});
1428 }
1429 break :blk true;
1430 };
1431 if (var_decl.getLibName()) |lib_name| {
1432 assert(is_extern);
1433 return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{});
1434 }
1435 const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var;
1436 const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: {
1437 if (!is_mutable) {
1438 return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{});
1439 }
1440 break :blk true;
1441 } else false;
1442 assert(var_decl.getComptimeToken() == null);
1443 if (var_decl.getAlignNode()) |align_expr| {
1444 return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{});
1445 }
1446 if (var_decl.getSectionNode()) |sect_expr| {
1447 return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{});
1448 }
1449
1450 const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: {
1451 var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1452 defer gen_scope_arena.deinit();
1453 var gen_scope: Scope.GenZIR = .{
1454 .decl = decl,
1455 .arena = &gen_scope_arena.allocator,
1456 .parent = &decl.container.base,
1457 };
1458 defer gen_scope.instructions.deinit(self.gpa);
14171459
1418 tvm.deinit(self.gpa);1460 const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: {
1419 }1461 const src = token_starts[type_node.firstToken()];
1462 const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{
1463 .ty = Type.initTag(.type),
1464 .val = Value.initTag(.type_type),
1465 });
1466 const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node);
1467 break :rl .{ .ty = var_type };
1468 } else .none;
14201469
1421 const new_variable = try decl_arena.allocator.create(Var);1470 const init_inst = try astgen.comptimeExpr(self, &gen_scope.base, init_result_loc, init_node);
1422 new_variable.* = .{1471 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
1423 .owner_decl = decl,1472 zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {};
1424 .init = var_info.val orelse undefined,1473 }
1425 .is_extern = is_extern,
1426 .is_mutable = is_mutable,
1427 .is_threadlocal = is_threadlocal,
1428 };
1429 const var_val = try Value.Tag.variable.create(&decl_arena.allocator, new_variable);
1430
1431 decl_arena_state.* = decl_arena.state;
1432 decl.typed_value = .{
1433 .most_recent = .{
1434 .typed_value = .{
1435 .ty = var_info.ty,
1436 .val = var_val,
1437 },
1438 .arena = decl_arena_state,
1439 },
1440 };
1441 decl.analysis = .complete;
1442 decl.generation = self.generation;
14431474
1444 if (var_decl.getExternExportToken()) |maybe_export_token| {1475 var var_inst_table = Scope.Block.InstTable.init(self.gpa);
1445 if (tree.token_ids[maybe_export_token] == .Keyword_export) {1476 defer var_inst_table.deinit();
1446 const export_src = tree.token_locs[maybe_export_token].start;1477
1447 const name_loc = tree.token_locs[var_decl.name_token];1478 var branch_quota_vi: u32 = default_eval_branch_quota;
1448 const name = tree.tokenSliceLoc(name_loc);1479 var inner_block: Scope.Block = .{
1449 // The scope needs to have the decl in it.1480 .parent = null,
1450 try self.analyzeExport(&block_scope.base, export_src, name, decl);1481 .inst_table = &var_inst_table,
1451 }1482 .func = null,
1452 }1483 .owner_decl = decl,
1453 return type_changed;1484 .src_decl = decl,
1454 },1485 .instructions = .{},
1455 .Comptime => {1486 .arena = &gen_scope_arena.allocator,
1456 const comptime_decl = @fieldParentPtr(ast.Node.Comptime, "base", ast_node);1487 .inlining = null,
1488 .is_comptime = true,
1489 .branch_quota = &branch_quota_vi,
1490 };
1491 defer inner_block.instructions.deinit(self.gpa);
1492 try zir_sema.analyzeBody(self, &inner_block, .{
1493 .instructions = gen_scope.instructions.items,
1494 });
14571495
1458 decl.analysis = .in_progress;1496 // The result location guarantees the type coercion.
1497 const analyzed_init_inst = var_inst_table.get(init_inst).?;
1498 // The is_comptime in the Scope.Block guarantees the result is comptime-known.
1499 const val = analyzed_init_inst.value().?;
14591500
1460 // A comptime decl does not store any value so we can just deinit this arena after analysis is done.1501 const ty = try analyzed_init_inst.ty.copy(block_scope.arena);
1461 var analysis_arena = std.heap.ArenaAllocator.init(self.gpa);1502 break :vi .{
1462 defer analysis_arena.deinit();1503 .ty = ty,
1463 var gen_scope: Scope.GenZIR = .{1504 .val = try val.copy(block_scope.arena),
1464 .decl = decl,1505 };
1465 .arena = &analysis_arena.allocator,1506 } else if (!is_extern) {
1466 .parent = &decl.container.base,1507 return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{});
1467 };1508 } else if (var_decl.getTypeNode()) |type_node| vi: {
1468 defer gen_scope.instructions.deinit(self.gpa);1509 // Temporary arena for the zir instructions.
1510 var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1511 defer type_scope_arena.deinit();
1512 var type_scope: Scope.GenZIR = .{
1513 .decl = decl,
1514 .arena = &type_scope_arena.allocator,
1515 .parent = &decl.container.base,
1516 };
1517 defer type_scope.instructions.deinit(self.gpa);
14691518
1470 _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr);1519 const var_type = try astgen.typeExpr(self, &type_scope.base, type_node);
1471 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {1520 if (std.builtin.mode == .Debug and self.comp.verbose_ir) {
1472 zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {};1521 zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {};
1473 }1522 }
14741523
1475 var inst_table = Scope.Block.InstTable.init(self.gpa);1524 const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{
1476 defer inst_table.deinit();1525 .instructions = type_scope.instructions.items,
1526 });
1527 break :vi .{
1528 .ty = ty,
1529 .val = null,
1530 };
1531 } else {
1532 return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{});
1533 };
14771534
1478 var branch_quota: u32 = default_eval_branch_quota;1535 if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
1536 return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty});
1537 }
14791538
1480 var block_scope: Scope.Block = .{1539 var type_changed = true;
1481 .parent = null,1540 if (decl.typedValueManaged()) |tvm| {
1482 .inst_table = &inst_table,1541 type_changed = !tvm.typed_value.ty.eql(var_info.ty);
1483 .func = null,
1484 .owner_decl = decl,
1485 .src_decl = decl,
1486 .instructions = .{},
1487 .arena = &analysis_arena.allocator,
1488 .inlining = null,
1489 .is_comptime = true,
1490 .branch_quota = &branch_quota,
1491 };
1492 defer block_scope.instructions.deinit(self.gpa);
14931542
1494 _ = try zir_sema.analyzeBody(self, &block_scope, .{1543 tvm.deinit(self.gpa);
1495 .instructions = gen_scope.instructions.items,1544 }
1496 });
14971545
1498 decl.analysis = .complete;1546 const new_variable = try decl_arena.allocator.create(Var);
1499 decl.generation = self.generation;1547 new_variable.* = .{
1500 return true;1548 .owner_decl = decl,
1549 .init = var_info.val orelse undefined,
1550 .is_extern = is_extern,
1551 .is_mutable = is_mutable,
1552 .is_threadlocal = is_threadlocal,
1553 };
1554 const var_val = try Value.Tag.variable.create(&decl_arena.allocator, new_variable);
1555
1556 decl_arena_state.* = decl_arena.state;
1557 decl.typed_value = .{
1558 .most_recent = .{
1559 .typed_value = .{
1560 .ty = var_info.ty,
1561 .val = var_val,
1562 },
1563 .arena = decl_arena_state,
1501 },1564 },
1502 .Use => @panic("TODO usingnamespace decl"),1565 };
1503 else => unreachable,1566 decl.analysis = .complete;
1567 decl.generation = self.generation;
1568
1569 if (var_decl.getExternExportToken()) |maybe_export_token| {
1570 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1571 const export_src = token_starts[maybe_export_token];
1572 const name = tree.tokenSlice(var_decl.name_token); // TODO identifierTokenString
1573 // The scope needs to have the decl in it.
1574 try self.analyzeExport(&block_scope.base, export_src, name, decl);
1575 }
1504 }1576 }
1577 return type_changed;
1505}1578}
15061579
1507fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {1580fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
...@@ -1512,7 +1585,7 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void...@@ -1512,7 +1585,7 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void
1512 dependee.dependants.putAssumeCapacity(depender, {});1585 dependee.dependants.putAssumeCapacity(depender, {});
1513}1586}
15141587
1515pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {1588pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*const ast.Tree {
1516 const tracy = trace(@src());1589 const tracy = trace(@src());
1517 defer tracy.end();1590 defer tracy.end();
15181591
...@@ -1523,8 +1596,10 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {...@@ -1523,8 +1596,10 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
1523 const source = try root_scope.getSource(self);1596 const source = try root_scope.getSource(self);
15241597
1525 var keep_tree = false;1598 var keep_tree = false;
1526 const tree = try std.zig.parse(self.gpa, source);1599 root_scope.tree = try std.zig.parse(self.gpa, source);
1527 defer if (!keep_tree) tree.deinit();1600 defer if (!keep_tree) root_scope.tree.deinit(self.gpa);
1601
1602 const tree = &root_scope.tree;
15281603
1529 if (tree.errors.len != 0) {1604 if (tree.errors.len != 0) {
1530 const parse_err = tree.errors[0];1605 const parse_err = tree.errors[0];
...@@ -1532,12 +1607,12 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {...@@ -1532,12 +1607,12 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
1532 var msg = std.ArrayList(u8).init(self.gpa);1607 var msg = std.ArrayList(u8).init(self.gpa);
1533 defer msg.deinit();1608 defer msg.deinit();
15341609
1535 try parse_err.render(tree.token_ids, msg.writer());1610 try tree.renderError(parse_err, msg.writer());
1536 const err_msg = try self.gpa.create(ErrorMsg);1611 const err_msg = try self.gpa.create(ErrorMsg);
1537 err_msg.* = .{1612 err_msg.* = .{
1538 .src_loc = .{1613 .src_loc = .{
1539 .file_scope = root_scope,1614 .file_scope = root_scope,
1540 .byte_offset = tree.token_locs[parse_err.loc()].start,1615 .byte_offset = tree.tokens.items(.start)[parse_err.loc()],
1541 },1616 },
1542 .msg = msg.toOwnedSlice(),1617 .msg = msg.toOwnedSlice(),
1543 };1618 };
...@@ -1548,7 +1623,6 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {...@@ -1548,7 +1623,6 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
1548 }1623 }
15491624
1550 root_scope.status = .loaded_success;1625 root_scope.status = .loaded_success;
1551 root_scope.contents = .{ .tree = tree };
1552 keep_tree = true;1626 keep_tree = true;
15531627
1554 return tree;1628 return tree;
...@@ -1556,144 +1630,336 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {...@@ -1556,144 +1630,336 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
15561630
1557 .unloaded_parse_failure => return error.AnalysisFail,1631 .unloaded_parse_failure => return error.AnalysisFail,
15581632
1559 .loaded_success => return root_scope.contents.tree,1633 .loaded_success => return &root_scope.tree,
1560 }1634 }
1561}1635}
15621636
1563pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void {1637pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
1564 const tracy = trace(@src());1638 const tracy = trace(@src());
1565 defer tracy.end();1639 defer tracy.end();
15661640
1567 // We may be analyzing it for the first time, or this may be1641 // We may be analyzing it for the first time, or this may be
1568 // an incremental update. This code handles both cases.1642 // an incremental update. This code handles both cases.
1569 const tree = try self.getAstTree(container_scope.file_scope);1643 const tree = try mod.getAstTree(container_scope.file_scope);
1570 const decls = tree.root_node.decls();1644 const node_tags = tree.nodes.items(.tag);
1645 const node_datas = tree.nodes.items(.data);
1646 const decls = tree.rootDecls();
15711647
1572 try self.comp.work_queue.ensureUnusedCapacity(decls.len);1648 try mod.comp.work_queue.ensureUnusedCapacity(decls.len);
1573 try container_scope.decls.ensureCapacity(self.gpa, decls.len);1649 try container_scope.decls.ensureCapacity(mod.gpa, decls.len);
15741650
1575 // Keep track of the decls that we expect to see in this file so that1651 // Keep track of the decls that we expect to see in this file so that
1576 // we know which ones have been deleted.1652 // we know which ones have been deleted.
1577 var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);1653 var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(mod.gpa);
1578 defer deleted_decls.deinit();1654 defer deleted_decls.deinit();
1579 try deleted_decls.ensureCapacity(container_scope.decls.items().len);1655 try deleted_decls.ensureCapacity(container_scope.decls.items().len);
1580 for (container_scope.decls.items()) |entry| {1656 for (container_scope.decls.items()) |entry| {
1581 deleted_decls.putAssumeCapacityNoClobber(entry.key, {});1657 deleted_decls.putAssumeCapacityNoClobber(entry.key, {});
1582 }1658 }
15831659
1584 for (decls) |src_decl, decl_i| {1660 for (decls) |decl_node, decl_i| switch (node_tags[decl_node]) {
1585 if (src_decl.cast(ast.Node.FnProto)) |fn_proto| {1661 .fn_decl => {
1586 // We will create a Decl for it regardless of analysis status.1662 const fn_proto = node_datas[decl_node].lhs;
1587 const name_tok = fn_proto.getNameToken() orelse {1663 const body = node_datas[decl_node].rhs;
1588 @panic("TODO missing function name");1664 switch (node_tags[fn_proto]) {
1589 };1665 .fn_proto_simple => {
15901666 var params: [1]ast.Node.Index = undefined;
1591 const name_loc = tree.token_locs[name_tok];1667 try mod.semaContainerFn(
1592 const name = tree.tokenSliceLoc(name_loc);1668 container_scope,
1593 const name_hash = container_scope.fullyQualifiedNameHash(name);1669 &deleted_decls,
1594 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));1670 decl_node,
1595 if (self.decl_table.get(name_hash)) |decl| {1671 decl_i,
1596 // Update the AST Node index of the decl, even if its contents are unchanged, it may1672 tree.*,
1597 // have been re-ordered.1673 body,
1598 decl.src_index = decl_i;1674 tree.fnProtoSimple(&params, fn_proto),
1599 if (deleted_decls.swapRemove(decl) == null) {1675 );
1600 decl.analysis = .sema_failure;1676 },
1601 const msg = try ErrorMsg.create(self.gpa, .{1677 .fn_proto_multi => try mod.semaContainerFn(
1602 .file_scope = container_scope.file_scope,1678 container_scope,
1603 .byte_offset = tree.token_locs[name_tok].start,1679 &deleted_decls,
1604 }, "redefinition of '{s}'", .{decl.name});1680 decl_node,
1605 errdefer msg.destroy(self.gpa);1681 decl_i,
1606 try self.failed_decls.putNoClobber(self.gpa, decl, msg);1682 tree.*,
1607 } else {1683 body,
1608 if (!srcHashEql(decl.contents_hash, contents_hash)) {1684 tree.fnProtoMulti(fn_proto),
1609 try self.markOutdatedDecl(decl);1685 ),
1610 decl.contents_hash = contents_hash;1686 .fn_proto_one => {
1611 } else switch (self.comp.bin_file.tag) {1687 var params: [1]ast.Node.Index = undefined;
1612 .coff => {1688 try mod.semaContainerFn(
1613 // TODO Implement for COFF1689 container_scope,
1614 },1690 &deleted_decls,
1615 .elf => if (decl.fn_link.elf.len != 0) {1691 decl_node,
1616 // TODO Look into detecting when this would be unnecessary by storing enough state1692 decl_i,
1617 // in `Decl` to notice that the line number did not change.1693 tree.*,
1618 self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });1694 body,
1619 },1695 tree.fnProtoOne(&params, fn_proto),
1620 .macho => if (decl.fn_link.macho.len != 0) {1696 );
1621 // TODO Look into detecting when this would be unnecessary by storing enough state1697 },
1622 // in `Decl` to notice that the line number did not change.1698 .fn_proto => try mod.semaContainerFn(
1623 self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });1699 container_scope,
1624 },1700 &deleted_decls,
1625 .c, .wasm => {},1701 decl_node,
1626 }1702 decl_i,
1627 }1703 tree.*,
1628 } else {1704 body,
1629 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);1705 tree.fnProto(fn_proto),
1630 container_scope.decls.putAssumeCapacity(new_decl, {});1706 ),
1631 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {1707 else => unreachable,
1632 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1633 self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
1634 }
1635 }
1636 }
1637 } else if (src_decl.castTag(.VarDecl)) |var_decl| {
1638 const name_loc = tree.token_locs[var_decl.name_token];
1639 const name = tree.tokenSliceLoc(name_loc);
1640 const name_hash = container_scope.fullyQualifiedNameHash(name);
1641 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
1642 if (self.decl_table.get(name_hash)) |decl| {
1643 // Update the AST Node index of the decl, even if its contents are unchanged, it may
1644 // have been re-ordered.
1645 decl.src_index = decl_i;
1646 if (deleted_decls.swapRemove(decl) == null) {
1647 decl.analysis = .sema_failure;
1648 const err_msg = try ErrorMsg.create(self.gpa, .{
1649 .file_scope = container_scope.file_scope,
1650 .byte_offset = name_loc.start,
1651 }, "redefinition of '{s}'", .{decl.name});
1652 errdefer err_msg.destroy(self.gpa);
1653 try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
1654 } else if (!srcHashEql(decl.contents_hash, contents_hash)) {
1655 try self.markOutdatedDecl(decl);
1656 decl.contents_hash = contents_hash;
1657 }
1658 } else {
1659 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
1660 container_scope.decls.putAssumeCapacity(new_decl, {});
1661 if (var_decl.getExternExportToken()) |maybe_export_token| {
1662 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1663 self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
1664 }
1665 }
1666 }1708 }
1667 } else if (src_decl.castTag(.Comptime)) |comptime_node| {1709 },
1668 const name_index = self.getNextAnonNameIndex();1710 .fn_proto_simple => {
1669 const name = try std.fmt.allocPrint(self.gpa, "__comptime_{d}", .{name_index});1711 var params: [1]ast.Node.Index = undefined;
1670 defer self.gpa.free(name);1712 try mod.semaContainerFn(
1713 container_scope,
1714 &deleted_decls,
1715 decl_node,
1716 decl_i,
1717 tree.*,
1718 null,
1719 tree.fnProtoSimple(&params, decl_node),
1720 );
1721 },
1722 .fn_proto_multi => try mod.semaContainerFn(
1723 container_scope,
1724 &deleted_decls,
1725 decl_node,
1726 decl_i,
1727 tree.*,
1728 null,
1729 tree.fnProtoMulti(decl_node),
1730 ),
1731 .fn_proto_one => {
1732 var params: [1]ast.Node.Index = undefined;
1733 try mod.semaContainerFn(
1734 container_scope,
1735 &deleted_decls,
1736 decl_node,
1737 decl_i,
1738 tree.*,
1739 null,
1740 tree.fnProtoOne(&params, decl_node),
1741 );
1742 },
1743 .fn_proto => try mod.semaContainerFn(
1744 container_scope,
1745 &deleted_decls,
1746 decl_node,
1747 decl_i,
1748 tree.*,
1749 null,
1750 tree.fnProto(decl_node),
1751 ),
1752
1753 .global_var_decl => try mod.semaContainerVar(
1754 container_scope,
1755 &deleted_decls,
1756 decl_node,
1757 decl_i,
1758 tree.*,
1759 tree.globalVarDecl(decl_node),
1760 ),
1761 .local_var_decl => try mod.semaContainerVar(
1762 container_scope,
1763 &deleted_decls,
1764 decl_node,
1765 decl_i,
1766 tree.*,
1767 tree.localVarDecl(decl_node),
1768 ),
1769 .simple_var_decl => try mod.semaContainerVar(
1770 container_scope,
1771 &deleted_decls,
1772 decl_node,
1773 decl_i,
1774 tree.*,
1775 tree.simpleVarDecl(decl_node),
1776 ),
1777 .aligned_var_decl => try mod.semaContainerVar(
1778 container_scope,
1779 &deleted_decls,
1780 decl_node,
1781 decl_i,
1782 tree.*,
1783 tree.alignedVarDecl(decl_node),
1784 ),
1785
1786 .@"comptime" => {
1787 const name_index = mod.getNextAnonNameIndex();
1788 const name = try std.fmt.allocPrint(mod.gpa, "__comptime_{d}", .{name_index});
1789 defer mod.gpa.free(name);
16711790
1672 const name_hash = container_scope.fullyQualifiedNameHash(name);1791 const name_hash = container_scope.fullyQualifiedNameHash(name);
1673 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));1792 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
16741793
1675 const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);1794 const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
1676 container_scope.decls.putAssumeCapacity(new_decl, {});1795 container_scope.decls.putAssumeCapacity(new_decl, {});
1677 self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });1796 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
1678 } else if (src_decl.castTag(.ContainerField)) |container_field| {1797 },
1679 log.err("TODO: analyze container field", .{});1798
1680 } else if (src_decl.castTag(.TestDecl)) |test_decl| {1799 .container_field_init => try mod.semaContainerField(
1800 container_scope,
1801 &deleted_decls,
1802 decl_node,
1803 decl_i,
1804 tree.*,
1805 tree.containerFieldInit(decl),
1806 ),
1807 .container_field_align => try mod.semaContainerField(
1808 container_scope,
1809 &deleted_decls,
1810 decl_node,
1811 decl_i,
1812 tree.*,
1813 tree.containerFieldAlign(decl),
1814 ),
1815 .container_field => try mod.semaContainerField(
1816 container_scope,
1817 &deleted_decls,
1818 decl_node,
1819 decl_i,
1820 tree.*,
1821 tree.containerField(decl),
1822 ),
1823
1824 .test_decl => {
1681 log.err("TODO: analyze test decl", .{});1825 log.err("TODO: analyze test decl", .{});
1682 } else if (src_decl.castTag(.Use)) |use_decl| {1826 },
1827 .@"usingnamespace" => {
1683 log.err("TODO: analyze usingnamespace decl", .{});1828 log.err("TODO: analyze usingnamespace decl", .{});
1684 } else {1829 },
1685 unreachable;1830 else => unreachable,
1686 }1831 };
1687 }
1688 // Handle explicitly deleted decls from the source code. Not to be confused1832 // Handle explicitly deleted decls from the source code. Not to be confused
1689 // with when we delete decls because they are no longer referenced.1833 // with when we delete decls because they are no longer referenced.
1690 for (deleted_decls.items()) |entry| {1834 for (deleted_decls.items()) |entry| {
1691 log.debug("noticed '{s}' deleted from source\n", .{entry.key.name});1835 log.debug("noticed '{s}' deleted from source\n", .{entry.key.name});
1692 try self.deleteDecl(entry.key);1836 try mod.deleteDecl(entry.key);
1837 }
1838}
1839
1840fn semaContainerFn(
1841 mod: *Module,
1842 container_scope: *Scope.Container,
1843 deleted_decls: *std.AutoArrayHashMap(*Decl, void),
1844 decl_node: ast.Node.Index,
1845 decl_i: usize,
1846 tree: ast.Tree,
1847 body_node: ast.Node.Index,
1848 fn_proto: ast.full.FnProto,
1849) !void {
1850 const tracy = trace(@src());
1851 defer tracy.end();
1852
1853 const token_starts = tree.tokens.items(.start);
1854 const token_tags = tree.tokens.items(.tag);
1855
1856 // We will create a Decl for it regardless of analysis status.
1857 const name_tok = fn_proto.name_token orelse {
1858 @panic("TODO missing function name");
1859 };
1860 const name = tree.tokenSlice(name_tok); // TODO use identifierTokenString
1861 const name_hash = container_scope.fullyQualifiedNameHash(name);
1862 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
1863 if (mod.decl_table.get(name_hash)) |decl| {
1864 // Update the AST Node index of the decl, even if its contents are unchanged, it may
1865 // have been re-ordered.
1866 decl.src_index = decl_i;
1867 if (deleted_decls.swapRemove(decl) == null) {
1868 decl.analysis = .sema_failure;
1869 const msg = try ErrorMsg.create(mod.gpa, .{
1870 .file_scope = container_scope.file_scope,
1871 .byte_offset = token_starts[name_tok],
1872 }, "redefinition of '{s}'", .{decl.name});
1873 errdefer msg.destroy(mod.gpa);
1874 try mod.failed_decls.putNoClobber(mod.gpa, decl, msg);
1875 } else {
1876 if (!srcHashEql(decl.contents_hash, contents_hash)) {
1877 try mod.markOutdatedDecl(decl);
1878 decl.contents_hash = contents_hash;
1879 } else switch (mod.comp.bin_file.tag) {
1880 .coff => {
1881 // TODO Implement for COFF
1882 },
1883 .elf => if (decl.fn_link.elf.len != 0) {
1884 // TODO Look into detecting when this would be unnecessary by storing enough state
1885 // in `Decl` to notice that the line number did not change.
1886 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
1887 },
1888 .macho => if (decl.fn_link.macho.len != 0) {
1889 // TODO Look into detecting when this would be unnecessary by storing enough state
1890 // in `Decl` to notice that the line number did not change.
1891 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
1892 },
1893 .c, .wasm => {},
1894 }
1895 }
1896 } else {
1897 const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
1898 container_scope.decls.putAssumeCapacity(new_decl, {});
1899 if (fn_proto.getExternExportInlineToken()) |maybe_export_token| {
1900 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1901 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
1902 }
1903 }
1904 }
1905}
1906
1907fn semaContainerVar(
1908 mod: *Module,
1909 container_scope: *Scope.Container,
1910 deleted_decls: *std.AutoArrayHashMap(*Decl, void),
1911 decl_node: ast.Node.Index,
1912 decl_i: usize,
1913 tree: ast.Tree,
1914 var_decl: ast.full.VarDecl,
1915) !void {
1916 const tracy = trace(@src());
1917 defer tracy.end();
1918
1919 const token_starts = tree.tokens.items(.start);
1920
1921 const name_src = token_starts[var_decl.name_token];
1922 const name = tree.tokenSlice(var_decl.name_token); // TODO identifierTokenString
1923 const name_hash = container_scope.fullyQualifiedNameHash(name);
1924 const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
1925 if (mod.decl_table.get(name_hash)) |decl| {
1926 // Update the AST Node index of the decl, even if its contents are unchanged, it may
1927 // have been re-ordered.
1928 decl.src_index = decl_i;
1929 if (deleted_decls.swapRemove(decl) == null) {
1930 decl.analysis = .sema_failure;
1931 const err_msg = try ErrorMsg.create(mod.gpa, .{
1932 .file_scope = container_scope.file_scope,
1933 .byte_offset = name_src,
1934 }, "redefinition of '{s}'", .{decl.name});
1935 errdefer err_msg.destroy(mod.gpa);
1936 try mod.failed_decls.putNoClobber(mod.gpa, decl, err_msg);
1937 } else if (!srcHashEql(decl.contents_hash, contents_hash)) {
1938 try mod.markOutdatedDecl(decl);
1939 decl.contents_hash = contents_hash;
1940 }
1941 } else {
1942 const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash);
1943 container_scope.decls.putAssumeCapacity(new_decl, {});
1944 if (var_decl.getExternExportToken()) |maybe_export_token| {
1945 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1946 mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
1947 }
1948 }
1693 }1949 }
1694}1950}
16951951
1952fn semaContainerField() void {
1953 const tracy = trace(@src());
1954 defer tracy.end();
1955
1956 log.err("TODO: analyze container field", .{});
1957}
1958
1696pub fn deleteDecl(self: *Module, decl: *Decl) !void {1959pub fn deleteDecl(self: *Module, decl: *Decl) !void {
1960 const tracy = trace(@src());
1961 defer tracy.end();
1962
1697 try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len);1963 try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len);
16981964
1699 // Remove from the namespace it resides in. In the case of an anonymous Decl it will1965 // Remove from the namespace it resides in. In the case of an anonymous Decl it will
...@@ -2338,15 +2604,16 @@ pub fn createContainerDecl(...@@ -2338,15 +2604,16 @@ pub fn createContainerDecl(
2338fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 {2604fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 {
2339 // TODO add namespaces, generic function signatrues2605 // TODO add namespaces, generic function signatrues
2340 const tree = scope.tree();2606 const tree = scope.tree();
2341 const base_name = switch (tree.token_ids[base_token]) {2607 const token_tags = tree.tokens.items(.tag);
2342 .Keyword_struct => "struct",2608 const base_name = switch (token_tags[base_token]) {
2343 .Keyword_enum => "enum",2609 .keyword_struct => "struct",
2344 .Keyword_union => "union",2610 .keyword_enum => "enum",
2345 .Keyword_opaque => "opaque",2611 .keyword_union => "union",
2612 .keyword_opaque => "opaque",
2346 else => unreachable,2613 else => unreachable,
2347 };2614 };
2348 const loc = tree.tokenLocationLoc(0, tree.token_locs[base_token]);2615 const loc = tree.tokenLocation(0, base_token);
2349 return std.fmt.allocPrint(self.gpa, "{}:{}:{}", .{ base_name, loc.line, loc.column });2616 return std.fmt.allocPrint(self.gpa, "{s}:{d}:{d}", .{ base_name, loc.line, loc.column });
2350}2617}
23512618
2352fn getNextAnonNameIndex(self: *Module) usize {2619fn getNextAnonNameIndex(self: *Module) usize {
...@@ -3092,7 +3359,7 @@ pub fn failTok(...@@ -3092,7 +3359,7 @@ pub fn failTok(
3092 comptime format: []const u8,3359 comptime format: []const u8,
3093 args: anytype,3360 args: anytype,
3094) InnerError {3361) InnerError {
3095 const src = scope.tree().token_locs[token_index].start;3362 const src = scope.tree().tokens.items(.start)[token_index];
3096 return self.fail(scope, src, format, args);3363 return self.fail(scope, src, format, args);
3097}3364}
30983365
...@@ -3103,7 +3370,7 @@ pub fn failNode(...@@ -3103,7 +3370,7 @@ pub fn failNode(
3103 comptime format: []const u8,3370 comptime format: []const u8,
3104 args: anytype,3371 args: anytype,
3105) InnerError {3372) InnerError {
3106 const src = scope.tree().token_locs[ast_node.firstToken()].start;3373 const src = scope.tree().tokens.items(.start)[ast_node.firstToken()];
3107 return self.fail(scope, src, format, args);3374 return self.fail(scope, src, format, args);
3108}3375}
31093376
...@@ -3537,6 +3804,7 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void...@@ -3537,6 +3804,7 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void
3537/// Identifier token -> String (allocated in scope.arena())3804/// Identifier token -> String (allocated in scope.arena())
3538pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {3805pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
3539 const tree = scope.tree();3806 const tree = scope.tree();
3807 const token_starts = tree.tokens.items(.start);
35403808
3541 const ident_name = tree.tokenSlice(token);3809 const ident_name = tree.tokenSlice(token);
3542 if (mem.startsWith(u8, ident_name, "@")) {3810 if (mem.startsWith(u8, ident_name, "@")) {
...@@ -3545,7 +3813,7 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex)...@@ -3545,7 +3813,7 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex)
3545 return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {3813 return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {
3546 error.InvalidCharacter => {3814 error.InvalidCharacter => {
3547 const bad_byte = raw_string[bad_index];3815 const bad_byte = raw_string[bad_index];
3548 const src = tree.token_locs[token].start;3816 const src = token_starts[token];
3549 return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});3817 return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
3550 },3818 },
3551 else => |e| return e,3819 else => |e| return e,
src/main.zig+44-40
...@@ -2153,7 +2153,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi...@@ -2153,7 +2153,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi
2153 const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});2153 const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});
2154 const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);2154 const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);
2155 var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};2155 var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};
2156 const tree = translate_c.translate(2156 var tree = translate_c.translate(
2157 comp.gpa,2157 comp.gpa,
2158 new_argv.ptr,2158 new_argv.ptr,
2159 new_argv.ptr + new_argv.len,2159 new_argv.ptr + new_argv.len,
...@@ -2174,7 +2174,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi...@@ -2174,7 +2174,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi
2174 process.exit(1);2174 process.exit(1);
2175 },2175 },
2176 };2176 };
2177 defer tree.deinit();2177 defer tree.deinit(comp.gpa);
21782178
2179 if (out_dep_path) |dep_file_path| {2179 if (out_dep_path) |dep_file_path| {
2180 const dep_basename = std.fs.path.basename(dep_file_path);2180 const dep_basename = std.fs.path.basename(dep_file_path);
...@@ -2188,16 +2188,21 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi...@@ -2188,16 +2188,21 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi
21882188
2189 const digest = man.final();2189 const digest = man.final();
2190 const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest });2190 const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest });
2191
2191 var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{});2192 var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{});
2192 defer o_dir.close();2193 defer o_dir.close();
2194
2193 var zig_file = try o_dir.createFile(translated_zig_basename, .{});2195 var zig_file = try o_dir.createFile(translated_zig_basename, .{});
2194 defer zig_file.close();2196 defer zig_file.close();
21952197
2196 var bw = io.bufferedWriter(zig_file.writer());2198 const formatted = try tree.render(comp.gpa);
2197 _ = try std.zig.render(comp.gpa, bw.writer(), tree);2199 defer comp.gpa.free(formatted);
2198 try bw.flush();
21992200
2200 man.writeManifest() catch |err| warn("failed to write cache manifest: {s}", .{@errorName(err)});2201 try zig_file.writeAll(formatted);
2202
2203 man.writeManifest() catch |err| warn("failed to write cache manifest: {s}", .{
2204 @errorName(err),
2205 });
22012206
2202 break :digest digest;2207 break :digest digest;
2203 };2208 };
...@@ -2684,10 +2689,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -2684,10 +2689,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
2684 const source_code = try stdin.readAllAlloc(gpa, max_src_size);2689 const source_code = try stdin.readAllAlloc(gpa, max_src_size);
2685 defer gpa.free(source_code);2690 defer gpa.free(source_code);
26862691
2687 const tree = std.zig.parse(gpa, source_code) catch |err| {2692 var tree = std.zig.parse(gpa, source_code) catch |err| {
2688 fatal("error parsing stdin: {s}", .{err});2693 fatal("error parsing stdin: {s}", .{err});
2689 };2694 };
2690 defer tree.deinit();2695 defer tree.deinit(gpa);
26912696
2692 for (tree.errors) |parse_error| {2697 for (tree.errors) |parse_error| {
2693 try printErrMsgToFile(gpa, parse_error, tree, "<stdin>", stderr_file, color);2698 try printErrMsgToFile(gpa, parse_error, tree, "<stdin>", stderr_file, color);
...@@ -2695,16 +2700,15 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -2695,16 +2700,15 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
2695 if (tree.errors.len != 0) {2700 if (tree.errors.len != 0) {
2696 process.exit(1);2701 process.exit(1);
2697 }2702 }
2703 const formatted = try tree.render(gpa);
2704 defer gpa.free(formatted);
2705
2698 if (check_flag) {2706 if (check_flag) {
2699 const anything_changed = try std.zig.render(gpa, io.null_writer, tree);2707 const code: u8 = @boolToInt(mem.eql(u8, formatted, source_code));
2700 const code = if (anything_changed) @as(u8, 1) else @as(u8, 0);
2701 process.exit(code);2708 process.exit(code);
2702 }2709 }
27032710
2704 var bw = io.bufferedWriter(io.getStdOut().writer());2711 return io.getStdOut().writeAll(formatted);
2705 _ = try std.zig.render(gpa, bw.writer(), tree);
2706 try bw.flush();
2707 return;
2708 }2712 }
27092713
2710 if (input_files.items.len == 0) {2714 if (input_files.items.len == 0) {
...@@ -2841,8 +2845,8 @@ fn fmtPathFile(...@@ -2841,8 +2845,8 @@ fn fmtPathFile(
2841 // Add to set after no longer possible to get error.IsDir.2845 // Add to set after no longer possible to get error.IsDir.
2842 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;2846 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
28432847
2844 const tree = try std.zig.parse(fmt.gpa, source_code);2848 var tree = try std.zig.parse(fmt.gpa, source_code);
2845 defer tree.deinit();2849 defer tree.deinit(fmt.gpa);
28462850
2847 for (tree.errors) |parse_error| {2851 for (tree.errors) |parse_error| {
2848 try printErrMsgToFile(fmt.gpa, parse_error, tree, file_path, std.io.getStdErr(), fmt.color);2852 try printErrMsgToFile(fmt.gpa, parse_error, tree, file_path, std.io.getStdErr(), fmt.color);
...@@ -2852,22 +2856,20 @@ fn fmtPathFile(...@@ -2852,22 +2856,20 @@ fn fmtPathFile(
2852 return;2856 return;
2853 }2857 }
28542858
2859 // As a heuristic, we make enough capacity for the same as the input source.
2860 fmt.out_buffer.shrinkRetainingCapacity(0);
2861 try fmt.out_buffer.ensureCapacity(source_code.len);
2862
2863 try tree.renderToArrayList(&fmt.out_buffer);
2864 const anything_changed = mem.eql(u8, fmt.out_buffer.items, source_code);
2865 if (!anything_changed)
2866 return;
2867
2855 if (check_mode) {2868 if (check_mode) {
2856 const anything_changed = try std.zig.render(fmt.gpa, io.null_writer, tree);2869 const stdout = io.getStdOut().writer();
2857 if (anything_changed) {2870 try stdout.print("{s}\n", .{file_path});
2858 const stdout = io.getStdOut().writer();2871 fmt.any_error = true;
2859 try stdout.print("{s}\n", .{file_path});
2860 fmt.any_error = true;
2861 }
2862 } else {2872 } else {
2863 // As a heuristic, we make enough capacity for the same as the input source.
2864 try fmt.out_buffer.ensureCapacity(source_code.len);
2865 fmt.out_buffer.items.len = 0;
2866 const writer = fmt.out_buffer.writer();
2867 const anything_changed = try std.zig.render(fmt.gpa, writer, tree);
2868 if (!anything_changed)
2869 return; // Good thing we didn't waste any file system access on this.
2870
2871 var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });2873 var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
2872 defer af.deinit();2874 defer af.deinit();
28732875
...@@ -2881,7 +2883,7 @@ fn fmtPathFile(...@@ -2881,7 +2883,7 @@ fn fmtPathFile(
2881fn printErrMsgToFile(2883fn printErrMsgToFile(
2882 gpa: *mem.Allocator,2884 gpa: *mem.Allocator,
2883 parse_error: ast.Error,2885 parse_error: ast.Error,
2884 tree: *ast.Tree,2886 tree: ast.Tree,
2885 path: []const u8,2887 path: []const u8,
2886 file: fs.File,2888 file: fs.File,
2887 color: Color,2889 color: Color,
...@@ -2892,18 +2894,16 @@ fn printErrMsgToFile(...@@ -2892,18 +2894,16 @@ fn printErrMsgToFile(
2892 .off => false,2894 .off => false,
2893 };2895 };
2894 const lok_token = parse_error.loc();2896 const lok_token = parse_error.loc();
2895 const span_first = lok_token;
2896 const span_last = lok_token;
28972897
2898 const first_token = tree.token_locs[span_first];2898 const token_starts = tree.tokens.items(.start);
2899 const last_token = tree.token_locs[span_last];2899 const token_tags = tree.tokens.items(.tag);
2900 const start_loc = tree.tokenLocationLoc(0, first_token);2900 const first_token_start = token_starts[lok_token];
2901 const end_loc = tree.tokenLocationLoc(first_token.end, last_token);2901 const start_loc = tree.tokenLocation(0, lok_token);
29022902
2903 var text_buf = std.ArrayList(u8).init(gpa);2903 var text_buf = std.ArrayList(u8).init(gpa);
2904 defer text_buf.deinit();2904 defer text_buf.deinit();
2905 const writer = text_buf.writer();2905 const writer = text_buf.writer();
2906 try parse_error.render(tree.token_ids, writer);2906 try tree.renderError(parse_error, writer);
2907 const text = text_buf.items;2907 const text = text_buf.items;
29082908
2909 const stream = file.writer();2909 const stream = file.writer();
...@@ -2920,8 +2920,12 @@ fn printErrMsgToFile(...@@ -2920,8 +2920,12 @@ fn printErrMsgToFile(
2920 }2920 }
2921 try stream.writeByte('\n');2921 try stream.writeByte('\n');
2922 try stream.writeByteNTimes(' ', start_loc.column);2922 try stream.writeByteNTimes(' ', start_loc.column);
2923 try stream.writeByteNTimes('~', last_token.end - first_token.start);2923 if (token_tags[lok_token].lexeme()) |lexeme| {
2924 try stream.writeByte('\n');2924 try stream.writeByteNTimes('~', lexeme.len);
2925 try stream.writeByte('\n');
2926 } else {
2927 try stream.writeAll("^\n");
2928 }
2925}2929}
29262930
2927pub const info_zen =2931pub const info_zen =
src/translate_c.zig+9-1
...@@ -375,7 +375,7 @@ pub fn translate(...@@ -375,7 +375,7 @@ pub fn translate(
375 args_end: [*]?[*]const u8,375 args_end: [*]?[*]const u8,
376 errors: *[]ClangErrMsg,376 errors: *[]ClangErrMsg,
377 resources_path: [*:0]const u8,377 resources_path: [*:0]const u8,
378) !*ast.Tree {378) !ast.Tree {
379 const ast_unit = clang.LoadFromCommandLine(379 const ast_unit = clang.LoadFromCommandLine(
380 args_begin,380 args_begin,
381 args_end,381 args_end,
...@@ -396,6 +396,14 @@ pub fn translate(...@@ -396,6 +396,14 @@ pub fn translate(
396 var arena = std.heap.ArenaAllocator.init(gpa);396 var arena = std.heap.ArenaAllocator.init(gpa);
397 errdefer arena.deinit();397 errdefer arena.deinit();
398398
399 if (true) {
400 var x = false;
401 if (x) {
402 return error.OutOfMemory;
403 }
404 @panic("TODO update translate-c");
405 }
406
399 var context = Context{407 var context = Context{
400 .gpa = gpa,408 .gpa = gpa,
401 .arena = &arena.allocator,409 .arena = &arena.allocator,
src/zir.zig+2-13
...@@ -357,6 +357,7 @@ pub const Inst = struct {...@@ -357,6 +357,7 @@ pub const Inst = struct {
357 .ret_type,357 .ret_type,
358 .unreach_nocheck,358 .unreach_nocheck,
359 .@"unreachable",359 .@"unreachable",
360 .arg,
360 => NoOp,361 => NoOp,
361362
362 .alloc,363 .alloc,
...@@ -449,7 +450,6 @@ pub const Inst = struct {...@@ -449,7 +450,6 @@ pub const Inst = struct {
449 .block_comptime_flat,450 .block_comptime_flat,
450 => Block,451 => Block,
451452
452 .arg => Arg,
453 .array_type_sentinel => ArrayTypeSentinel,453 .array_type_sentinel => ArrayTypeSentinel,
454 .@"break" => Break,454 .@"break" => Break,
455 .breakvoid => BreakVoid,455 .breakvoid => BreakVoid,
...@@ -685,16 +685,6 @@ pub const Inst = struct {...@@ -685,16 +685,6 @@ pub const Inst = struct {
685 kw_args: struct {},685 kw_args: struct {},
686 };686 };
687687
688 pub const Arg = struct {
689 pub const base_tag = Tag.arg;
690 base: Inst,
691
692 positionals: struct {
693 name: []const u8,
694 },
695 kw_args: struct {},
696 };
697
698 pub const Block = struct {688 pub const Block = struct {
699 pub const base_tag = Tag.block;689 pub const base_tag = Tag.block;
700 base: Inst,690 base: Inst,
...@@ -1608,6 +1598,7 @@ const DumpTzir = struct {...@@ -1608,6 +1598,7 @@ const DumpTzir = struct {
1608 .unreach,1598 .unreach,
1609 .breakpoint,1599 .breakpoint,
1610 .dbg_stmt,1600 .dbg_stmt,
1601 .arg,
1611 => {},1602 => {},
16121603
1613 .ref,1604 .ref,
...@@ -1652,8 +1643,6 @@ const DumpTzir = struct {...@@ -1652,8 +1643,6 @@ const DumpTzir = struct {
1652 try dtz.findConst(bin_op.rhs);1643 try dtz.findConst(bin_op.rhs);
1653 },1644 },
16541645
1655 .arg => {},
1656
1657 .br => {1646 .br => {
1658 const br = inst.castTag(.br).?;1647 const br = inst.castTag(.br).?;
1659 try dtz.findConst(&br.block.base);1648 try dtz.findConst(&br.block.base);