authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-08 19:05:05-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-08 19:05:05-07:00
log482b995a4963e045860c7fb1a4e11c48cf4de880
treee00079980426580bbefd547e879d890dab842511
parentb9e508c410cd077d704a73418281f6d7839df241

stage2: blaze the trail for std lib integration

This branch adds "builtin" and "std" to the import table when using the self-hosted backend. "builtin" gains one additional item: ``` pub const zig_is_stage2 = true; // false when using stage1 backend ``` This allows the std lib to do conditional compilation based on detecting which backend is being used. This will be removed from builtin as soon as self-hosted catches up to feature parity with stage1. Keep a sharp eye out - people are going to be tempted to abuse this. The general rule of thumb is do not use `builtin.zig_is_stage2`. However this commit breaks the rule so that we can gain limited start.zig support as we incrementally improve the self-hosted compiler. This commit also implements `fullyQualifiedNameHash` and related functionality, which effectively puts all Decls in their proper namespaces. `fullyQualifiedName` is not yet implemented. Stop printing "todo" log messages for test decls unless we are in test mode. Add "previous definition here" error notes for Decl name collisions. This commit does not bring us yet to a newly passing test case. Here's what I'm working towards: ```zig const std = @import("std"); export fn main() c_int { const a = std.fs.base64_alphabet[0]; return a - 'A'; } ``` Current output: ``` $ ./zig-cache/bin/zig build-exe test.zig test.zig:3:1: error: TODO implement more analyze elemptr zig-cache/lib/zig/std/start.zig:38:46: error: TODO implement structInitExpr ty ``` So the next steps are clear: * Sema: improve elemptr * AstGen: implement structInitExpr

10 files changed, 274 insertions(+), 151 deletions(-)

lib/std/start.zig+89-27
...@@ -7,7 +7,7 @@...@@ -7,7 +7,7 @@
77
8const root = @import("root");8const root = @import("root");
9const std = @import("std.zig");9const std = @import("std.zig");
10const builtin = std.builtin;10const builtin = @import("builtin");
11const assert = std.debug.assert;11const assert = std.debug.assert;
12const uefi = std.os.uefi;12const uefi = std.os.uefi;
13const tlcsprng = @import("crypto/tlcsprng.zig");13const tlcsprng = @import("crypto/tlcsprng.zig");
...@@ -17,39 +17,101 @@ var argc_argv_ptr: [*]usize = undefined;...@@ -17,39 +17,101 @@ var argc_argv_ptr: [*]usize = undefined;
17const start_sym_name = if (builtin.arch.isMIPS()) "__start" else "_start";17const start_sym_name = if (builtin.arch.isMIPS()) "__start" else "_start";
1818
19comptime {19comptime {
20 if (builtin.output_mode == .Lib and builtin.link_mode == .Dynamic) {20 // The self-hosted compiler is not fully capable of handling all of this start.zig file.
21 if (builtin.os.tag == .windows and !@hasDecl(root, "_DllMainCRTStartup")) {21 // Until then, we have simplified logic here for self-hosted. TODO remove this once
22 @export(_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" });22 // self-hosted is capable enough to handle all of the real start.zig logic.
23 if (builtin.zig_is_stage2) {
24 if (builtin.output_mode == .Exe) {
25 if (builtin.link_libc or builtin.object_format == .c) {
26 if (!@hasDecl(root, "main")) {
27 @export(main2, "main");
28 }
29 } else {
30 if (!@hasDecl(root, "_start")) {
31 @export(_start2, "_start");
32 }
33 }
23 }34 }
24 } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) {35 } else {
25 if (builtin.link_libc and @hasDecl(root, "main")) {36 if (builtin.output_mode == .Lib and builtin.link_mode == .Dynamic) {
26 if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {37 if (builtin.os.tag == .windows and !@hasDecl(root, "_DllMainCRTStartup")) {
27 @export(main, .{ .name = "main", .linkage = .Weak });38 @export(_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" });
28 }39 }
29 } else if (builtin.os.tag == .windows) {40 } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) {
30 if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and41 if (builtin.link_libc and @hasDecl(root, "main")) {
31 !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup"))42 if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) {
32 {43 @export(main, .{ .name = "main", .linkage = .Weak });
33 @export(WinStartup, .{ .name = "wWinMainCRTStartup" });44 }
34 } else if (@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and45 } else if (builtin.os.tag == .windows) {
35 !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup"))46 if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and
36 {47 !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup"))
37 @compileError("WinMain not supported; declare wWinMain or main instead");48 {
38 } else if (@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup") and49 @export(WinStartup, .{ .name = "wWinMainCRTStartup" });
39 !@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup"))50 } else if (@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and
40 {51 !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup"))
41 @export(wWinMainCRTStartup, .{ .name = "wWinMainCRTStartup" });52 {
53 @compileError("WinMain not supported; declare wWinMain or main instead");
54 } else if (@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup") and
55 !@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup"))
56 {
57 @export(wWinMainCRTStartup, .{ .name = "wWinMainCRTStartup" });
58 }
59 } else if (builtin.os.tag == .uefi) {
60 if (!@hasDecl(root, "EfiMain")) @export(EfiMain, .{ .name = "EfiMain" });
61 } else if (builtin.arch.isWasm() and builtin.os.tag == .freestanding) {
62 if (!@hasDecl(root, start_sym_name)) @export(wasm_freestanding_start, .{ .name = start_sym_name });
63 } else if (builtin.os.tag != .other and builtin.os.tag != .freestanding) {
64 if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name });
42 }65 }
43 } else if (builtin.os.tag == .uefi) {
44 if (!@hasDecl(root, "EfiMain")) @export(EfiMain, .{ .name = "EfiMain" });
45 } else if (builtin.arch.isWasm() and builtin.os.tag == .freestanding) {
46 if (!@hasDecl(root, start_sym_name)) @export(wasm_freestanding_start, .{ .name = start_sym_name });
47 } else if (builtin.os.tag != .other and builtin.os.tag != .freestanding) {
48 if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name });
49 }66 }
50 }67 }
51}68}
5269
70// Simplified start code for stage2 until it supports more language features ///
71
72fn main2() callconv(.C) c_int {
73 root.main();
74 return 0;
75}
76
77fn _start2() callconv(.Naked) noreturn {
78 root.main();
79 exit2(0);
80}
81
82fn exit2(code: u8) noreturn {
83 switch (builtin.arch) {
84 .x86_64 => {
85 asm volatile ("syscall"
86 :
87 : [number] "{rax}" (231),
88 [arg1] "{rdi}" (code)
89 : "rcx", "r11", "memory"
90 );
91 },
92 .arm => {
93 asm volatile ("svc #0"
94 :
95 : [number] "{r7}" (1),
96 [arg1] "{r0}" (code)
97 : "memory"
98 );
99 },
100 .aarch64 => {
101 asm volatile ("svc #0"
102 :
103 : [number] "{x8}" (93),
104 [arg1] "{x0}" (code)
105 : "memory", "cc"
106 );
107 },
108 else => @compileError("TODO"),
109 }
110 unreachable;
111}
112
113////////////////////////////////////////////////////////////////////////////////
114
53fn _DllMainCRTStartup(115fn _DllMainCRTStartup(
54 hinstDLL: std.os.windows.HINSTANCE,116 hinstDLL: std.os.windows.HINSTANCE,
55 fdwReason: std.os.windows.DWORD,117 fdwReason: std.os.windows.DWORD,
lib/std/start2.zig deleted-58
...@@ -1,58 +0,0 @@
1const root = @import("root");
2const builtin = @import("builtin");
3
4comptime {
5 if (builtin.output_mode == 0) { // OutputMode.Exe
6 if (builtin.link_libc or builtin.object_format == 5) { // ObjectFormat.c
7 if (!@hasDecl(root, "main")) {
8 @export(otherMain, "main");
9 }
10 } else {
11 if (!@hasDecl(root, "_start")) {
12 @export(otherStart, "_start");
13 }
14 }
15 }
16}
17
18// FIXME: Cannot call this function `main`, because `fully qualified names`
19// have not been implemented yet.
20fn otherMain() callconv(.C) c_int {
21 root.zigMain();
22 return 0;
23}
24
25// FIXME: Cannot call this function `_start`, because `fully qualified names`
26// have not been implemented yet.
27fn otherStart() callconv(.Naked) noreturn {
28 root.zigMain();
29 otherExit();
30}
31
32// FIXME: Cannot call this function `exit`, because `fully qualified names`
33// have not been implemented yet.
34fn otherExit() noreturn {
35 if (builtin.arch == 31) { // x86_64
36 asm volatile ("syscall"
37 :
38 : [number] "{rax}" (231),
39 [arg1] "{rdi}" (0)
40 : "rcx", "r11", "memory"
41 );
42 } else if (builtin.arch == 0) { // arm
43 asm volatile ("svc #0"
44 :
45 : [number] "{r7}" (1),
46 [arg1] "{r0}" (0)
47 : "memory"
48 );
49 } else if (builtin.arch == 2) { // aarch64
50 asm volatile ("svc #0"
51 :
52 : [number] "{x8}" (93),
53 [arg1] "{x0}" (0)
54 : "memory", "cc"
55 );
56 } else @compileError("not yet supported!");
57 unreachable;
58}
lib/std/std.zig+1-1
...@@ -92,7 +92,7 @@ pub const zig = @import("zig.zig");...@@ -92,7 +92,7 @@ pub const zig = @import("zig.zig");
92pub const start = @import("start.zig");92pub const start = @import("start.zig");
9393
94// This forces the start.zig file to be imported, and the comptime logic inside that94// This forces the start.zig file to be imported, and the comptime logic inside that
95// file decides whether to export any appropriate start symbols.95// file decides whether to export any appropriate start symbols, and call main.
96comptime {96comptime {
97 _ = start;97 _ = start;
98}98}
lib/std/zig.zig+11-8
...@@ -18,16 +18,19 @@ pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;...@@ -18,16 +18,19 @@ pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;
1818
19pub const SrcHash = [16]u8;19pub const SrcHash = [16]u8;
2020
21/// If the source is small enough, it is used directly as the hash.
22/// If it is long, blake3 hash is computed.
23pub fn hashSrc(src: []const u8) SrcHash {21pub fn hashSrc(src: []const u8) SrcHash {
24 var out: SrcHash = undefined;22 var out: SrcHash = undefined;
25 if (src.len <= @typeInfo(SrcHash).Array.len) {23 std.crypto.hash.Blake3.hash(src, &out, .{});
26 std.mem.copy(u8, &out, src);24 return out;
27 std.mem.set(u8, out[src.len..], 0);25}
28 } else {26
29 std.crypto.hash.Blake3.hash(src, &out, .{});27pub fn hashName(parent_hash: SrcHash, sep: []const u8, name: []const u8) SrcHash {
30 }28 var out: SrcHash = undefined;
29 var hasher = std.crypto.hash.Blake3.init(.{});
30 hasher.update(&parent_hash);
31 hasher.update(sep);
32 hasher.update(name);
33 hasher.final(&out);
31 return out;34 return out;
32}35}
3336
src/Compilation.zig+59-30
...@@ -906,38 +906,61 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -906,38 +906,61 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
906 artifact_sub_dir,906 artifact_sub_dir,
907 };907 };
908908
909 // TODO when we implement serialization and deserialization of incremental compilation metadata,909 // If we rely on stage1, we must not redundantly add these packages.
910 // this is where we would load it. We have open a handle to the directory where910 const use_stage1 = build_options.is_stage1 and use_llvm;
911 // the output either already is, or will be.911 if (!use_stage1) {
912 const builtin_pkg = try Package.createWithDir(
913 gpa,
914 zig_cache_artifact_directory,
915 null,
916 "builtin.zig",
917 );
918 errdefer builtin_pkg.destroy(gpa);
919
920 const std_pkg = try Package.createWithDir(
921 gpa,
922 options.zig_lib_directory,
923 "std",
924 "std.zig",
925 );
926 errdefer std_pkg.destroy(gpa);
927
928 try root_pkg.addAndAdopt(gpa, "builtin", builtin_pkg);
929 try root_pkg.add(gpa, "root", root_pkg);
930 try root_pkg.addAndAdopt(gpa, "std", std_pkg);
931
932 try std_pkg.add(gpa, "builtin", builtin_pkg);
933 try std_pkg.add(gpa, "root", root_pkg);
934 }
935
936 // TODO when we implement serialization and deserialization of incremental
937 // compilation metadata, this is where we would load it. We have open a handle
938 // to the directory where the output either already is, or will be.
912 // However we currently do not have serialization of such metadata, so for now939 // However we currently do not have serialization of such metadata, so for now
913 // we set up an empty Module that does the entire compilation fresh.940 // we set up an empty Module that does the entire compilation fresh.
914941
915 const root_scope = rs: {942 // TODO remove CLI support for .zir files and then we can remove this error
916 if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) {943 // handling and assertion.
917 const root_scope = try gpa.create(Module.Scope.File);944 if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) return error.ZirFilesUnsupported;
918 const struct_ty = try Type.Tag.empty_struct.create(945 assert(mem.endsWith(u8, root_pkg.root_src_path, ".zig"));
919 gpa,946
920 &root_scope.root_container,947 const root_scope = try gpa.create(Module.Scope.File);
921 );948 errdefer gpa.destroy(root_scope);
922 root_scope.* = .{949
923 // TODO this is duped so it can be freed in Container.deinit950 const struct_ty = try Type.Tag.empty_struct.create(gpa, &root_scope.root_container);
924 .sub_file_path = try gpa.dupe(u8, root_pkg.root_src_path),951 root_scope.* = .{
925 .source = .{ .unloaded = {} },952 // TODO this is duped so it can be freed in Container.deinit
926 .tree = undefined,953 .sub_file_path = try gpa.dupe(u8, root_pkg.root_src_path),
927 .status = .never_loaded,954 .source = .{ .unloaded = {} },
928 .pkg = root_pkg,955 .tree = undefined,
929 .root_container = .{956 .status = .never_loaded,
930 .file_scope = root_scope,957 .pkg = root_pkg,
931 .decls = .{},958 .root_container = .{
932 .ty = struct_ty,959 .file_scope = root_scope,
933 },960 .decls = .{},
934 };961 .ty = struct_ty,
935 break :rs root_scope;962 .parent_name_hash = root_pkg.namespace_hash,
936 } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) {963 },
937 return error.ZirFilesUnsupported;
938 } else {
939 unreachable;
940 }
941 };964 };
942965
943 const module = try arena.create(Module);966 const module = try arena.create(Module);
...@@ -1339,7 +1362,8 @@ pub fn update(self: *Compilation) !void {...@@ -1339,7 +1362,8 @@ pub fn update(self: *Compilation) !void {
1339 self.c_object_work_queue.writeItemAssumeCapacity(entry.key);1362 self.c_object_work_queue.writeItemAssumeCapacity(entry.key);
1340 }1363 }
13411364
1342 const use_stage1 = build_options.omit_stage2 or build_options.is_stage1 and self.bin_file.options.use_llvm;1365 const use_stage1 = build_options.omit_stage2 or
1366 (build_options.is_stage1 and self.bin_file.options.use_llvm);
1343 if (!use_stage1) {1367 if (!use_stage1) {
1344 if (self.bin_file.options.module) |module| {1368 if (self.bin_file.options.module) |module| {
1345 module.compile_log_text.shrinkAndFree(module.gpa, 0);1369 module.compile_log_text.shrinkAndFree(module.gpa, 0);
...@@ -2840,6 +2864,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8...@@ -2840,6 +2864,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8
28402864
2841 const target = comp.getTarget();2865 const target = comp.getTarget();
2842 const generic_arch_name = target.cpu.arch.genericName();2866 const generic_arch_name = target.cpu.arch.genericName();
2867 const use_stage1 = build_options.omit_stage2 or
2868 (build_options.is_stage1 and comp.bin_file.options.use_llvm);
28432869
2844 @setEvalBranchQuota(4000);2870 @setEvalBranchQuota(4000);
2845 try buffer.writer().print(2871 try buffer.writer().print(
...@@ -2852,6 +2878,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8...@@ -2852,6 +2878,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8
2852 \\/// Zig version. When writing code that supports multiple versions of Zig, prefer2878 \\/// Zig version. When writing code that supports multiple versions of Zig, prefer
2853 \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.2879 \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.
2854 \\pub const zig_version = try @import("std").SemanticVersion.parse("{s}");2880 \\pub const zig_version = try @import("std").SemanticVersion.parse("{s}");
2881 \\pub const zig_is_stage2 = {};
2855 \\2882 \\
2856 \\pub const output_mode = OutputMode.{};2883 \\pub const output_mode = OutputMode.{};
2857 \\pub const link_mode = LinkMode.{};2884 \\pub const link_mode = LinkMode.{};
...@@ -2865,6 +2892,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8...@@ -2865,6 +2892,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8
2865 \\2892 \\
2866 , .{2893 , .{
2867 build_options.version,2894 build_options.version,
2895 !use_stage1,
2868 std.zig.fmtId(@tagName(comp.bin_file.options.output_mode)),2896 std.zig.fmtId(@tagName(comp.bin_file.options.output_mode)),
2869 std.zig.fmtId(@tagName(comp.bin_file.options.link_mode)),2897 std.zig.fmtId(@tagName(comp.bin_file.options.link_mode)),
2870 comp.bin_file.options.is_test,2898 comp.bin_file.options.is_test,
...@@ -3074,6 +3102,7 @@ fn buildOutputFromZig(...@@ -3074,6 +3102,7 @@ fn buildOutputFromZig(
3074 .handle = special_dir,3102 .handle = special_dir,
3075 },3103 },
3076 .root_src_path = src_basename,3104 .root_src_path = src_basename,
3105 .namespace_hash = Package.root_namespace_hash,
3077 };3106 };
3078 const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len];3107 const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len];
3079 const target = comp.getTarget();3108 const target = comp.getTarget();
src/Module.zig+11-6
...@@ -678,6 +678,7 @@ pub const Scope = struct {...@@ -678,6 +678,7 @@ pub const Scope = struct {
678 base: Scope = Scope{ .tag = base_tag },678 base: Scope = Scope{ .tag = base_tag },
679679
680 file_scope: *Scope.File,680 file_scope: *Scope.File,
681 parent_name_hash: NameHash,
681682
682 /// Direct children of the file.683 /// Direct children of the file.
683 decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},684 decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},
...@@ -696,8 +697,7 @@ pub const Scope = struct {...@@ -696,8 +697,7 @@ pub const Scope = struct {
696 }697 }
697698
698 pub fn fullyQualifiedNameHash(cont: *Container, name: []const u8) NameHash {699 pub fn fullyQualifiedNameHash(cont: *Container, name: []const u8) NameHash {
699 // TODO container scope qualified names.700 return std.zig.hashName(cont.parent_name_hash, ".", name);
700 return std.zig.hashSrc(name);
701 }701 }
702702
703 pub fn renderFullyQualifiedName(cont: Container, name: []const u8, writer: anytype) !void {703 pub fn renderFullyQualifiedName(cont: Container, name: []const u8, writer: anytype) !void {
...@@ -2513,6 +2513,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {...@@ -2513,6 +2513,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {
25132513
2514 const block_expr = node_datas[decl_node].lhs;2514 const block_expr = node_datas[decl_node].lhs;
2515 _ = try AstGen.comptimeExpr(&gen_scope, &gen_scope.base, .none, block_expr);2515 _ = try AstGen.comptimeExpr(&gen_scope, &gen_scope.base, .none, block_expr);
2516 _ = try gen_scope.addBreak(.break_inline, 0, .void_value);
25162517
2517 const code = try gen_scope.finish();2518 const code = try gen_scope.finish();
2518 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {2519 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
...@@ -3468,7 +3469,9 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {...@@ -3468,7 +3469,9 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
3468 ),3469 ),
34693470
3470 .test_decl => {3471 .test_decl => {
3471 log.err("TODO: analyze test decl", .{});3472 if (mod.comp.bin_file.options.is_test) {
3473 log.err("TODO: analyze test decl", .{});
3474 }
3472 },3475 },
3473 .@"usingnamespace" => {3476 .@"usingnamespace" => {
3474 log.err("TODO: analyze usingnamespace decl", .{});3477 log.err("TODO: analyze usingnamespace decl", .{});
...@@ -3532,6 +3535,7 @@ fn semaContainerFn(...@@ -3532,6 +3535,7 @@ fn semaContainerFn(
3532 .lazy = .{ .token_abs = name_tok },3535 .lazy = .{ .token_abs = name_tok },
3533 }, "redefinition of '{s}'", .{decl.name});3536 }, "redefinition of '{s}'", .{decl.name});
3534 errdefer msg.destroy(mod.gpa);3537 errdefer msg.destroy(mod.gpa);
3538 try mod.errNoteNonLazy(decl.srcLoc(), msg, "previous definition here", .{});
3535 try mod.failed_decls.putNoClobber(mod.gpa, decl, msg);3539 try mod.failed_decls.putNoClobber(mod.gpa, decl, msg);
3536 } else {3540 } else {
3537 if (!srcHashEql(decl.contents_hash, contents_hash)) {3541 if (!srcHashEql(decl.contents_hash, contents_hash)) {
...@@ -3589,12 +3593,13 @@ fn semaContainerVar(...@@ -3589,12 +3593,13 @@ fn semaContainerVar(
3589 decl.src_index = decl_i;3593 decl.src_index = decl_i;
3590 if (deleted_decls.swapRemove(decl) == null) {3594 if (deleted_decls.swapRemove(decl) == null) {
3591 decl.analysis = .sema_failure;3595 decl.analysis = .sema_failure;
3592 const err_msg = try ErrorMsg.create(mod.gpa, .{3596 const msg = try ErrorMsg.create(mod.gpa, .{
3593 .container = .{ .file_scope = container_scope.file_scope },3597 .container = .{ .file_scope = container_scope.file_scope },
3594 .lazy = .{ .token_abs = name_token },3598 .lazy = .{ .token_abs = name_token },
3595 }, "redefinition of '{s}'", .{decl.name});3599 }, "redefinition of '{s}'", .{decl.name});
3596 errdefer err_msg.destroy(mod.gpa);3600 errdefer msg.destroy(mod.gpa);
3597 try mod.failed_decls.putNoClobber(mod.gpa, decl, err_msg);3601 try mod.errNoteNonLazy(decl.srcLoc(), msg, "previous definition here", .{});
3602 try mod.failed_decls.putNoClobber(mod.gpa, decl, msg);
3598 } else if (!srcHashEql(decl.contents_hash, contents_hash)) {3603 } else if (!srcHashEql(decl.contents_hash, contents_hash)) {
3599 try outdated_decls.put(decl, {});3604 try outdated_decls.put(decl, {});
3600 decl.contents_hash = contents_hash;3605 decl.contents_hash = contents_hash;
src/Package.zig+68-8
...@@ -4,18 +4,29 @@ const std = @import("std");...@@ -4,18 +4,29 @@ const std = @import("std");
4const fs = std.fs;4const fs = std.fs;
5const mem = std.mem;5const mem = std.mem;
6const Allocator = mem.Allocator;6const Allocator = mem.Allocator;
7const assert = std.debug.assert;
78
8const Compilation = @import("Compilation.zig");9const Compilation = @import("Compilation.zig");
10const Module = @import("Module.zig");
911
10pub const Table = std.StringHashMapUnmanaged(*Package);12pub const Table = std.StringHashMapUnmanaged(*Package);
1113
14pub const root_namespace_hash: Module.Scope.NameHash = .{
15 0, 0, 6, 6, 6, 0, 0, 0,
16 6, 9, 0, 0, 0, 4, 2, 0,
17};
18
12root_src_directory: Compilation.Directory,19root_src_directory: Compilation.Directory,
13/// Relative to `root_src_directory`. May contain path separators.20/// Relative to `root_src_directory`. May contain path separators.
14root_src_path: []const u8,21root_src_path: []const u8,
15table: Table = .{},22table: Table = .{},
16parent: ?*Package = null,23parent: ?*Package = null,
24namespace_hash: Module.Scope.NameHash,
25/// Whether to free `root_src_directory` on `destroy`.
26root_src_directory_owned: bool = false,
1727
18/// Allocate a Package. No references to the slices passed are kept.28/// Allocate a Package. No references to the slices passed are kept.
29/// Don't forget to set `namespace_hash` later.
19pub fn create(30pub fn create(
20 gpa: *Allocator,31 gpa: *Allocator,
21 /// Null indicates the current working directory32 /// Null indicates the current working directory
...@@ -38,27 +49,69 @@ pub fn create(...@@ -38,27 +49,69 @@ pub fn create(
38 .handle = if (owned_dir_path) |p| try fs.cwd().openDir(p, .{}) else fs.cwd(),49 .handle = if (owned_dir_path) |p| try fs.cwd().openDir(p, .{}) else fs.cwd(),
39 },50 },
40 .root_src_path = owned_src_path,51 .root_src_path = owned_src_path,
52 .root_src_directory_owned = true,
53 .namespace_hash = undefined,
41 };54 };
4255
43 return ptr;56 return ptr;
44}57}
4558
46/// Free all memory associated with this package and recursively call destroy59pub fn createWithDir(
47/// on all packages in its table60 gpa: *Allocator,
61 directory: Compilation.Directory,
62 /// Relative to `directory`. If null, means `directory` is the root src dir
63 /// and is owned externally.
64 root_src_dir_path: ?[]const u8,
65 /// Relative to root_src_dir_path
66 root_src_path: []const u8,
67) !*Package {
68 const ptr = try gpa.create(Package);
69 errdefer gpa.destroy(ptr);
70
71 const owned_src_path = try gpa.dupe(u8, root_src_path);
72 errdefer gpa.free(owned_src_path);
73
74 if (root_src_dir_path) |p| {
75 const owned_dir_path = try directory.join(gpa, &[1][]const u8{p});
76 errdefer gpa.free(owned_dir_path);
77
78 ptr.* = .{
79 .root_src_directory = .{
80 .path = owned_dir_path,
81 .handle = try directory.handle.openDir(p, .{}),
82 },
83 .root_src_directory_owned = true,
84 .root_src_path = owned_src_path,
85 .namespace_hash = undefined,
86 };
87 } else {
88 ptr.* = .{
89 .root_src_directory = directory,
90 .root_src_directory_owned = false,
91 .root_src_path = owned_src_path,
92 .namespace_hash = undefined,
93 };
94 }
95 return ptr;
96}
97
98/// Free all memory associated with this package. It does not destroy any packages
99/// inside its table; the caller is responsible for calling destroy() on them.
48pub fn destroy(pkg: *Package, gpa: *Allocator) void {100pub fn destroy(pkg: *Package, gpa: *Allocator) void {
49 gpa.free(pkg.root_src_path);101 gpa.free(pkg.root_src_path);
50102
51 // If root_src_directory.path is null then the handle is the cwd()103 if (pkg.root_src_directory_owned) {
52 // which shouldn't be closed.104 // If root_src_directory.path is null then the handle is the cwd()
53 if (pkg.root_src_directory.path) |p| {105 // which shouldn't be closed.
54 gpa.free(p);106 if (pkg.root_src_directory.path) |p| {
55 pkg.root_src_directory.handle.close();107 gpa.free(p);
108 pkg.root_src_directory.handle.close();
109 }
56 }110 }
57111
58 {112 {
59 var it = pkg.table.iterator();113 var it = pkg.table.iterator();
60 while (it.next()) |kv| {114 while (it.next()) |kv| {
61 kv.value.destroy(gpa);
62 gpa.free(kv.key);115 gpa.free(kv.key);
63 }116 }
64 }117 }
...@@ -72,3 +125,10 @@ pub fn add(pkg: *Package, gpa: *Allocator, name: []const u8, package: *Package)...@@ -72,3 +125,10 @@ pub fn add(pkg: *Package, gpa: *Allocator, name: []const u8, package: *Package)
72 const name_dupe = try mem.dupe(gpa, u8, name);125 const name_dupe = try mem.dupe(gpa, u8, name);
73 pkg.table.putAssumeCapacityNoClobber(name_dupe, package);126 pkg.table.putAssumeCapacityNoClobber(name_dupe, package);
74}127}
128
129pub fn addAndAdopt(parent: *Package, gpa: *Allocator, name: []const u8, child: *Package) !void {
130 assert(child.parent == null); // make up your mind, who is the parent??
131 child.parent = parent;
132 child.namespace_hash = std.zig.hashName(parent.namespace_hash, ":", name);
133 return parent.add(gpa, name, child);
134}
src/Sema.zig+13-6
...@@ -598,6 +598,10 @@ fn zirStructDecl(...@@ -598,6 +598,10 @@ fn zirStructDecl(
598 const struct_obj = try new_decl_arena.allocator.create(Module.Struct);598 const struct_obj = try new_decl_arena.allocator.create(Module.Struct);
599 const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj);599 const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj);
600 const struct_val = try Value.Tag.ty.create(&new_decl_arena.allocator, struct_ty);600 const struct_val = try Value.Tag.ty.create(&new_decl_arena.allocator, struct_ty);
601 const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{
602 .ty = Type.initTag(.type),
603 .val = struct_val,
604 });
601 struct_obj.* = .{605 struct_obj.* = .{
602 .owner_decl = sema.owner_decl,606 .owner_decl = sema.owner_decl,
603 .fields = fields_map,607 .fields = fields_map,
...@@ -605,12 +609,9 @@ fn zirStructDecl(...@@ -605,12 +609,9 @@ fn zirStructDecl(
605 .container = .{609 .container = .{
606 .ty = struct_ty,610 .ty = struct_ty,
607 .file_scope = block.getFileScope(),611 .file_scope = block.getFileScope(),
612 .parent_name_hash = new_decl.fullyQualifiedNameHash(),
608 },613 },
609 };614 };
610 const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{
611 .ty = Type.initTag(.type),
612 .val = struct_val,
613 });
614 return sema.analyzeDeclVal(block, src, new_decl);615 return sema.analyzeDeclVal(block, src, new_decl);
615}616}
616617
...@@ -5298,9 +5299,9 @@ fn analyzeImport(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, target_strin...@@ -5298,9 +5299,9 @@ fn analyzeImport(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, target_strin
5298 try std.fs.path.resolve(sema.gpa, &[_][]const u8{ cur_pkg_dir_path, target_string });5299 try std.fs.path.resolve(sema.gpa, &[_][]const u8{ cur_pkg_dir_path, target_string });
5299 errdefer sema.gpa.free(resolved_path);5300 errdefer sema.gpa.free(resolved_path);
53005301
5301 if (sema.mod.import_table.get(resolved_path)) |some| {5302 if (sema.mod.import_table.get(resolved_path)) |cached_import| {
5302 sema.gpa.free(resolved_path);5303 sema.gpa.free(resolved_path);
5303 return some;5304 return cached_import;
5304 }5305 }
53055306
5306 if (found_pkg == null) {5307 if (found_pkg == null) {
...@@ -5318,6 +5319,11 @@ fn analyzeImport(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, target_strin...@@ -5318,6 +5319,11 @@ fn analyzeImport(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, target_strin
5318 const struct_ty = try Type.Tag.empty_struct.create(sema.gpa, &file_scope.root_container);5319 const struct_ty = try Type.Tag.empty_struct.create(sema.gpa, &file_scope.root_container);
5319 errdefer sema.gpa.destroy(struct_ty.castTag(.empty_struct).?);5320 errdefer sema.gpa.destroy(struct_ty.castTag(.empty_struct).?);
53205321
5322 const container_name_hash: Scope.NameHash = if (found_pkg) |pkg|
5323 pkg.namespace_hash
5324 else
5325 std.zig.hashName(cur_pkg.namespace_hash, "/", resolved_path);
5326
5321 file_scope.* = .{5327 file_scope.* = .{
5322 .sub_file_path = resolved_path,5328 .sub_file_path = resolved_path,
5323 .source = .{ .unloaded = {} },5329 .source = .{ .unloaded = {} },
...@@ -5328,6 +5334,7 @@ fn analyzeImport(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, target_strin...@@ -5328,6 +5334,7 @@ fn analyzeImport(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, target_strin
5328 .file_scope = file_scope,5334 .file_scope = file_scope,
5329 .decls = .{},5335 .decls = .{},
5330 .ty = struct_ty,5336 .ty = struct_ty,
5337 .parent_name_hash = container_name_hash,
5331 },5338 },
5332 };5339 };
5333 sema.mod.analyzeContainer(&file_scope.root_container) catch |err| switch (err) {5340 sema.mod.analyzeContainer(&file_scope.root_container) catch |err| switch (err) {
src/main.zig+21-7
...@@ -599,15 +599,15 @@ fn buildOutputType(...@@ -599,15 +599,15 @@ fn buildOutputType(
599 var test_exec_args = std.ArrayList(?[]const u8).init(gpa);599 var test_exec_args = std.ArrayList(?[]const u8).init(gpa);
600 defer test_exec_args.deinit();600 defer test_exec_args.deinit();
601601
602 const pkg_tree_root = try gpa.create(Package);
603 // This package only exists to clean up the code parsing --pkg-begin and602 // This package only exists to clean up the code parsing --pkg-begin and
604 // --pkg-end flags. Use dummy values that are safe for the destroy call.603 // --pkg-end flags. Use dummy values that are safe for the destroy call.
605 pkg_tree_root.* = .{604 var pkg_tree_root: Package = .{
606 .root_src_directory = .{ .path = null, .handle = fs.cwd() },605 .root_src_directory = .{ .path = null, .handle = fs.cwd() },
607 .root_src_path = &[0]u8{},606 .root_src_path = &[0]u8{},
607 .namespace_hash = Package.root_namespace_hash,
608 };608 };
609 defer pkg_tree_root.destroy(gpa);609 defer freePkgTree(gpa, &pkg_tree_root, false);
610 var cur_pkg: *Package = pkg_tree_root;610 var cur_pkg: *Package = &pkg_tree_root;
611611
612 switch (arg_mode) {612 switch (arg_mode) {
613 .build, .translate_c, .zig_test, .run => {613 .build, .translate_c, .zig_test, .run => {
...@@ -658,8 +658,7 @@ fn buildOutputType(...@@ -658,8 +658,7 @@ fn buildOutputType(
658 ) catch |err| {658 ) catch |err| {
659 fatal("Failed to add package at path {s}: {s}", .{ pkg_path, @errorName(err) });659 fatal("Failed to add package at path {s}: {s}", .{ pkg_path, @errorName(err) });
660 };660 };
661 new_cur_pkg.parent = cur_pkg;661 try cur_pkg.addAndAdopt(gpa, pkg_name, new_cur_pkg);
662 try cur_pkg.add(gpa, pkg_name, new_cur_pkg);
663 cur_pkg = new_cur_pkg;662 cur_pkg = new_cur_pkg;
664 } else if (mem.eql(u8, arg, "--pkg-end")) {663 } else if (mem.eql(u8, arg, "--pkg-end")) {
665 cur_pkg = cur_pkg.parent orelse664 cur_pkg = cur_pkg.parent orelse
...@@ -1747,6 +1746,7 @@ fn buildOutputType(...@@ -1747,6 +1746,7 @@ fn buildOutputType(
1747 if (root_pkg) |pkg| {1746 if (root_pkg) |pkg| {
1748 pkg.table = pkg_tree_root.table;1747 pkg.table = pkg_tree_root.table;
1749 pkg_tree_root.table = .{};1748 pkg_tree_root.table = .{};
1749 pkg.namespace_hash = pkg_tree_root.namespace_hash;
1750 }1750 }
17511751
1752 const self_exe_path = try fs.selfExePathAlloc(arena);1752 const self_exe_path = try fs.selfExePathAlloc(arena);
...@@ -2151,6 +2151,18 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi...@@ -2151,6 +2151,18 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi
2151 }2151 }
2152}2152}
21532153
2154fn freePkgTree(gpa: *Allocator, pkg: *Package, free_parent: bool) void {
2155 {
2156 var it = pkg.table.iterator();
2157 while (it.next()) |kv| {
2158 freePkgTree(gpa, kv.value, true);
2159 }
2160 }
2161 if (free_parent) {
2162 pkg.destroy(gpa);
2163 }
2164}
2165
2154fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !void {2166fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !void {
2155 if (!build_options.have_llvm)2167 if (!build_options.have_llvm)
2156 fatal("cannot translate-c: compiler built without LLVM extensions", .{});2168 fatal("cannot translate-c: compiler built without LLVM extensions", .{});
...@@ -2505,6 +2517,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v...@@ -2505,6 +2517,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
2505 .handle = try zig_lib_directory.handle.openDir(std_special, .{}),2517 .handle = try zig_lib_directory.handle.openDir(std_special, .{}),
2506 },2518 },
2507 .root_src_path = "build_runner.zig",2519 .root_src_path = "build_runner.zig",
2520 .namespace_hash = Package.root_namespace_hash,
2508 };2521 };
2509 defer root_pkg.root_src_directory.handle.close();2522 defer root_pkg.root_src_directory.handle.close();
25102523
...@@ -2550,8 +2563,9 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v...@@ -2550,8 +2563,9 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
2550 var build_pkg: Package = .{2563 var build_pkg: Package = .{
2551 .root_src_directory = build_directory,2564 .root_src_directory = build_directory,
2552 .root_src_path = build_zig_basename,2565 .root_src_path = build_zig_basename,
2566 .namespace_hash = undefined,
2553 };2567 };
2554 try root_pkg.table.put(arena, "@build", &build_pkg);2568 try root_pkg.addAndAdopt(arena, "@build", &build_pkg);
25552569
2556 var global_cache_directory: Compilation.Directory = l: {2570 var global_cache_directory: Compilation.Directory = l: {
2557 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);2571 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
src/stage1/codegen.cpp+1
...@@ -9137,6 +9137,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {...@@ -9137,6 +9137,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
9137 buf_appendf(contents, "pub const position_independent_executable = %s;\n", bool_to_str(g->have_pie));9137 buf_appendf(contents, "pub const position_independent_executable = %s;\n", bool_to_str(g->have_pie));
9138 buf_appendf(contents, "pub const strip_debug_info = %s;\n", bool_to_str(g->strip_debug_symbols));9138 buf_appendf(contents, "pub const strip_debug_info = %s;\n", bool_to_str(g->strip_debug_symbols));
9139 buf_appendf(contents, "pub const code_model = CodeModel.default;\n");9139 buf_appendf(contents, "pub const code_model = CodeModel.default;\n");
9140 buf_appendf(contents, "pub const zig_is_stage2 = false;\n");
91409141
9141 {9142 {
9142 TargetSubsystem detected_subsystem = detect_subsystem(g);9143 TargetSubsystem detected_subsystem = detect_subsystem(g);