authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-24 00:43:12-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-24 00:43:12-04:00
logdd9728c5a03844267bc378c326c353fd2b0e084e
tree5786bd228312976ee482a58463a798bc426d64af
parent558b0b87913dfb6e6b76f5dbe2c36b920302faab
parent10bdf73a02c90dc375985e49b08b5020cfc20b93

Merge remote-tracking branch 'origin/master' into llvm7


76 files changed, 7872 insertions(+), 1812 deletions(-)

CMakeLists.txt+12-1
......@@ -426,6 +426,7 @@ set(ZIG_SOURCES
426426)
427427set(ZIG_CPP_SOURCES
428428 "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
429 "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp"
429430)
430431
431432set(ZIG_STD_FILES
......@@ -479,6 +480,7 @@ set(ZIG_STD_FILES
479480 "index.zig"
480481 "io.zig"
481482 "json.zig"
483 "lazy_init.zig"
482484 "linked_list.zig"
483485 "macho.zig"
484486 "math/acos.zig"
......@@ -488,6 +490,7 @@ set(ZIG_STD_FILES
488490 "math/atan.zig"
489491 "math/atan2.zig"
490492 "math/atanh.zig"
493 "math/big/index.zig"
491494 "math/big/int.zig"
492495 "math/cbrt.zig"
493496 "math/ceil.zig"
......@@ -553,9 +556,10 @@ set(ZIG_STD_FILES
553556 "net.zig"
554557 "os/child_process.zig"
555558 "os/darwin.zig"
556 "os/darwin_errno.zig"
559 "os/darwin/errno.zig"
557560 "os/epoch.zig"
558561 "os/file.zig"
562 "os/get_app_data_dir.zig"
559563 "os/get_user_id.zig"
560564 "os/index.zig"
561565 "os/linux/errno.zig"
......@@ -564,8 +568,14 @@ set(ZIG_STD_FILES
564568 "os/linux/x86_64.zig"
565569 "os/path.zig"
566570 "os/time.zig"
571 "os/windows/advapi32.zig"
567572 "os/windows/error.zig"
568573 "os/windows/index.zig"
574 "os/windows/kernel32.zig"
575 "os/windows/ole32.zig"
576 "os/windows/shell32.zig"
577 "os/windows/shlwapi.zig"
578 "os/windows/user32.zig"
569579 "os/windows/util.zig"
570580 "os/zen.zig"
571581 "rand/index.zig"
......@@ -614,6 +624,7 @@ set(ZIG_STD_FILES
614624 "zig/ast.zig"
615625 "zig/index.zig"
616626 "zig/parse.zig"
627 "zig/parse_string_literal.zig"
617628 "zig/render.zig"
618629 "zig/tokenizer.zig"
619630)
README.md+5-5
......@@ -21,19 +21,19 @@ clarity.
2121 * Compatible with C libraries with no wrapper necessary. Directly include
2222 C .h files and get access to the functions and symbols therein.
2323 * Provides standard library which competes with the C standard library and is
24 always compiled against statically in source form. Compile units do not
24 always compiled against statically in source form. Zig binaries do not
2525 depend on libc unless explicitly linked.
26 * Nullable type instead of null pointers.
26 * Optional type instead of null pointers.
2727 * Safe unions, tagged unions, and C ABI compatible unions.
2828 * Generics so that one can write efficient data structures that work for any
2929 data type.
3030 * No header files required. Top level declarations are entirely
3131 order-independent.
3232 * Compile-time code execution. Compile-time reflection.
33 * Partial compile-time function evaluation with eliminates the need for
33 * Partial compile-time function evaluation which eliminates the need for
3434 a preprocessor or macros.
3535 * The binaries produced by Zig have complete debugging information so you can,
36 for example, use GDB or MSVC to debug your software.
36 for example, use GDB, MSVC, or LLDB to debug your software.
3737 * Built-in unit tests with `zig test`.
3838 * Friendly toward package maintainers. Reproducible build, bootstrapping
3939 process carefully documented. Issues filed by package maintainers are
......@@ -70,7 +70,7 @@ that counts as "freestanding" for the purposes of this table.
7070
7171## Community
7272
73 * IRC: `#zig` on Freenode.
73 * IRC: `#zig` on Freenode ([Channel Logs](https://irclog.whitequark.org/zig/)).
7474 * Reddit: [/r/zig](https://www.reddit.com/r/zig)
7575 * Email list: [ziglang@googlegroups.com](https://groups.google.com/forum/#!forum/ziglang)
7676
build.zig+1-1
......@@ -92,7 +92,7 @@ pub fn build(b: *Builder) !void {
9292 test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", modes));
9393
9494 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
95 test_step.dependOn(tests.addBuildExampleTests(b, test_filter));
95 test_step.dependOn(tests.addBuildExampleTests(b, test_filter, modes));
9696 test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));
9797 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
9898 test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));
doc/langref.html.in+3-3
......@@ -1087,7 +1087,7 @@ unwrapped == 1234</code></pre>
10871087 </td>
10881088 <td>
10891089 If <code>a</code> is <code>false</code>, returns <code>false</code>
1090 without evaluating <code>b</code>. Otherwise, retuns <code>b</code>.
1090 without evaluating <code>b</code>. Otherwise, returns <code>b</code>.
10911091 </td>
10921092 <td>
10931093 <pre><code class="zig">false and true == false</code></pre>
......@@ -1102,7 +1102,7 @@ unwrapped == 1234</code></pre>
11021102 </td>
11031103 <td>
11041104 If <code>a</code> is <code>true</code>, returns <code>true</code>
1105 without evaluating <code>b</code>. Otherwise, retuns <code>b</code>.
1105 without evaluating <code>b</code>. Otherwise, returns <code>b</code>.
11061106 </td>
11071107 <td>
11081108 <pre><code class="zig">false or true == true</code></pre>
......@@ -1483,7 +1483,7 @@ test "pointer array access" {
14831483}
14841484
14851485test "pointer slicing" {
1486 // In Zig, we prefer using slices over null-terminated pointers.
1486 // In Zig, we prefer slices over pointers to null-terminated arrays.
14871487 // You can turn an array into a slice using slice syntax:
14881488 var array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
14891489 const slice = array[2..4];
src-self-hosted/c.zig+1
......@@ -4,4 +4,5 @@ pub use @cImport({
44 @cInclude("inttypes.h");
55 @cInclude("config.h");
66 @cInclude("zig_llvm.h");
7 @cInclude("windows_sdk.h");
78});
src-self-hosted/c_int.zig created+68
......@@ -0,0 +1,68 @@
1pub const CInt = struct {
2 id: Id,
3 zig_name: []const u8,
4 c_name: []const u8,
5 is_signed: bool,
6
7 pub const Id = enum {
8 Short,
9 UShort,
10 Int,
11 UInt,
12 Long,
13 ULong,
14 LongLong,
15 ULongLong,
16 };
17
18 pub const list = []CInt{
19 CInt{
20 .id = Id.Short,
21 .zig_name = "c_short",
22 .c_name = "short",
23 .is_signed = true,
24 },
25 CInt{
26 .id = Id.UShort,
27 .zig_name = "c_ushort",
28 .c_name = "unsigned short",
29 .is_signed = false,
30 },
31 CInt{
32 .id = Id.Int,
33 .zig_name = "c_int",
34 .c_name = "int",
35 .is_signed = true,
36 },
37 CInt{
38 .id = Id.UInt,
39 .zig_name = "c_uint",
40 .c_name = "unsigned int",
41 .is_signed = false,
42 },
43 CInt{
44 .id = Id.Long,
45 .zig_name = "c_long",
46 .c_name = "long",
47 .is_signed = true,
48 },
49 CInt{
50 .id = Id.ULong,
51 .zig_name = "c_ulong",
52 .c_name = "unsigned long",
53 .is_signed = false,
54 },
55 CInt{
56 .id = Id.LongLong,
57 .zig_name = "c_longlong",
58 .c_name = "long long",
59 .is_signed = true,
60 },
61 CInt{
62 .id = Id.ULongLong,
63 .zig_name = "c_ulonglong",
64 .c_name = "unsigned long long",
65 .is_signed = false,
66 },
67 };
68};
src-self-hosted/codegen.zig+90-10
......@@ -1,19 +1,22 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const Compilation = @import("compilation.zig").Compilation;
3// we go through llvm instead of c for 2 reasons:
4// 1. to avoid accidentally calling the non-thread-safe functions
5// 2. patch up some of the types to remove nullability
64const llvm = @import("llvm.zig");
5const c = @import("c.zig");
76const ir = @import("ir.zig");
87const Value = @import("value.zig").Value;
98const Type = @import("type.zig").Type;
109const event = std.event;
1110const assert = std.debug.assert;
11const DW = std.dwarf;
1212
1313pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void {
1414 fn_val.base.ref();
1515 defer fn_val.base.deref(comp);
16 defer code.destroy(comp.a());
16 defer code.destroy(comp.gpa());
17
18 var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable);
19 errdefer output_path.deinit();
1720
1821 const llvm_handle = try comp.event_loop_local.getAnyLlvmContext();
1922 defer llvm_handle.release(comp.event_loop_local);
......@@ -23,15 +26,59 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
2326 const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory;
2427 defer llvm.DisposeModule(module);
2528
29 llvm.SetTarget(module, comp.llvm_triple.ptr());
30 llvm.SetDataLayout(module, comp.target_layout_str);
31
32 if (comp.target.getObjectFormat() == builtin.ObjectFormat.coff) {
33 llvm.AddModuleCodeViewFlag(module);
34 } else {
35 llvm.AddModuleDebugInfoFlag(module);
36 }
37
2638 const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory;
2739 defer llvm.DisposeBuilder(builder);
2840
41 const dibuilder = llvm.CreateDIBuilder(module, true) orelse return error.OutOfMemory;
42 defer llvm.DisposeDIBuilder(dibuilder);
43
44 // Don't use ZIG_VERSION_STRING here. LLVM misparses it when it includes
45 // the git revision.
46 const producer = try std.Buffer.allocPrint(
47 &code.arena.allocator,
48 "zig {}.{}.{}",
49 u32(c.ZIG_VERSION_MAJOR),
50 u32(c.ZIG_VERSION_MINOR),
51 u32(c.ZIG_VERSION_PATCH),
52 );
53 const flags = c"";
54 const runtime_version = 0;
55 const compile_unit_file = llvm.CreateFile(
56 dibuilder,
57 comp.name.ptr(),
58 comp.root_package.root_src_dir.ptr(),
59 ) orelse return error.OutOfMemory;
60 const is_optimized = comp.build_mode != builtin.Mode.Debug;
61 const compile_unit = llvm.CreateCompileUnit(
62 dibuilder,
63 DW.LANG_C99,
64 compile_unit_file,
65 producer.ptr(),
66 is_optimized,
67 flags,
68 runtime_version,
69 c"",
70 0,
71 !comp.strip,
72 ) orelse return error.OutOfMemory;
73
2974 var ofile = ObjectFile{
3075 .comp = comp,
3176 .module = module,
3277 .builder = builder,
78 .dibuilder = dibuilder,
3379 .context = context,
3480 .lock = event.Lock.init(comp.loop),
81 .arena = &code.arena.allocator,
3582 };
3683
3784 try renderToLlvmModule(&ofile, fn_val, code);
......@@ -41,10 +88,10 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
4188 // LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm));
4289 //}
4390
44 // TODO
45 //ZigLLVMDIBuilderFinalize(g->dbuilder);
91 llvm.DIBuilderFinalize(dibuilder);
4692
4793 if (comp.verbose_llvm_ir) {
94 std.debug.warn("raw module:\n");
4895 llvm.DumpModule(ofile.module);
4996 }
5097
......@@ -53,23 +100,56 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
53100 var error_ptr: ?[*]u8 = null;
54101 _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr);
55102 }
103
104 assert(comp.emit_file_type == Compilation.Emit.Binary); // TODO support other types
105
106 const is_small = comp.build_mode == builtin.Mode.ReleaseSmall;
107 const is_debug = comp.build_mode == builtin.Mode.Debug;
108
109 var err_msg: [*]u8 = undefined;
110 // TODO integrate this with evented I/O
111 if (llvm.TargetMachineEmitToFile(
112 comp.target_machine,
113 module,
114 output_path.ptr(),
115 llvm.EmitBinary,
116 &err_msg,
117 is_debug,
118 is_small,
119 )) {
120 if (std.debug.runtime_safety) {
121 std.debug.panic("unable to write object file {}: {s}\n", output_path.toSliceConst(), err_msg);
122 }
123 return error.WritingObjectFileFailed;
124 }
125 //validate_inline_fns(g); TODO
126 fn_val.containing_object = output_path;
127 if (comp.verbose_llvm_ir) {
128 std.debug.warn("optimized module:\n");
129 llvm.DumpModule(ofile.module);
130 }
131 if (comp.verbose_link) {
132 std.debug.warn("created {}\n", output_path.toSliceConst());
133 }
56134}
57135
58136pub const ObjectFile = struct {
59137 comp: *Compilation,
60138 module: llvm.ModuleRef,
61139 builder: llvm.BuilderRef,
140 dibuilder: *llvm.DIBuilder,
62141 context: llvm.ContextRef,
63142 lock: event.Lock,
143 arena: *std.mem.Allocator,
64144
65 fn a(self: *ObjectFile) *std.mem.Allocator {
66 return self.comp.a();
145 fn gpa(self: *ObjectFile) *std.mem.Allocator {
146 return self.comp.gpa();
67147 }
68148};
69149
70150pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void {
71151 // TODO audit more of codegen.cpp:fn_llvm_value and port more logic
72 const llvm_fn_type = try fn_val.base.typeof.getLlvmType(ofile);
152 const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context);
73153 const llvm_fn = llvm.AddFunction(
74154 ofile.module,
75155 fn_val.symbol_name.ptr(),
......@@ -87,7 +167,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
87167 // try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack);
88168 //}
89169
90 const fn_type = fn_val.base.typeof.cast(Type.Fn).?;
170 const fn_type = fn_val.base.typ.cast(Type.Fn).?;
91171
92172 try addLLVMFnAttr(ofile, llvm_fn, "nounwind");
93173 //add_uwtable_attr(g, fn_table_entry->llvm_value);
src-self-hosted/compilation.zig+719-229
......@@ -21,24 +21,47 @@ const Scope = @import("scope.zig").Scope;
2121const Decl = @import("decl.zig").Decl;
2222const ir = @import("ir.zig");
2323const Visib = @import("visib.zig").Visib;
24const ParsedFile = @import("parsed_file.zig").ParsedFile;
2524const Value = @import("value.zig").Value;
2625const Type = Value.Type;
2726const Span = errmsg.Span;
27const Msg = errmsg.Msg;
2828const codegen = @import("codegen.zig");
29const Package = @import("package.zig").Package;
30const link = @import("link.zig").link;
31const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
32const CInt = @import("c_int.zig").CInt;
2933
3034/// Data that is local to the event loop.
3135pub const EventLoopLocal = struct {
3236 loop: *event.Loop,
3337 llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),
3438
35 fn init(loop: *event.Loop) EventLoopLocal {
39 /// TODO pool these so that it doesn't have to lock
40 prng: event.Locked(std.rand.DefaultPrng),
41
42 native_libc: event.Future(LibCInstallation),
43
44 var lazy_init_targets = std.lazyInit(void);
45
46 fn init(loop: *event.Loop) !EventLoopLocal {
47 lazy_init_targets.get() orelse {
48 Target.initializeAll();
49 lazy_init_targets.resolve();
50 };
51
52 var seed_bytes: [@sizeOf(u64)]u8 = undefined;
53 try std.os.getRandomBytes(seed_bytes[0..]);
54 const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big);
55
3656 return EventLoopLocal{
3757 .loop = loop,
3858 .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
59 .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)),
60 .native_libc = event.Future(LibCInstallation).init(loop),
3961 };
4062 }
4163
64 /// Must be called only after EventLoop.run completes.
4265 fn deinit(self: *EventLoopLocal) void {
4366 while (self.llvm_handle_pool.pop()) |node| {
4467 c.LLVMContextDispose(node.data);
......@@ -62,6 +85,13 @@ pub const EventLoopLocal = struct {
6285
6386 return LlvmHandle{ .node = node };
6487 }
88
89 pub async fn getNativeLibC(self: *EventLoopLocal) !*LibCInstallation {
90 if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr;
91 try await (async self.native_libc.data.findNative(self.loop) catch unreachable);
92 self.native_libc.resolve();
93 return &self.native_libc.data;
94 }
6595};
6696
6797pub const LlvmHandle = struct {
......@@ -76,23 +106,22 @@ pub const Compilation = struct {
76106 event_loop_local: *EventLoopLocal,
77107 loop: *event.Loop,
78108 name: Buffer,
109 llvm_triple: Buffer,
79110 root_src_path: ?[]const u8,
80111 target: Target,
112 llvm_target: llvm.TargetRef,
81113 build_mode: builtin.Mode,
82114 zig_lib_dir: []const u8,
115 zig_std_dir: []const u8,
116
117 /// lazily created when we need it
118 tmp_dir: event.Future(BuildError![]u8),
83119
84120 version_major: u32,
85121 version_minor: u32,
86122 version_patch: u32,
87123
88124 linker_script: ?[]const u8,
89 cache_dir: []const u8,
90 libc_lib_dir: ?[]const u8,
91 libc_static_lib_dir: ?[]const u8,
92 libc_include_dir: ?[]const u8,
93 msvc_lib_dir: ?[]const u8,
94 kernel32_lib_dir: ?[]const u8,
95 dynamic_linker: ?[]const u8,
96125 out_h_path: ?[]const u8,
97126
98127 is_test: bool,
......@@ -106,8 +135,16 @@ pub const Compilation = struct {
106135 lib_dirs: []const []const u8,
107136 rpath_list: []const []const u8,
108137 assembly_files: []const []const u8,
138
139 /// paths that are explicitly provided by the user to link against
109140 link_objects: []const []const u8,
110141
142 /// functions that have their own objects that we need to link
143 /// it uses an optional pointer so that tombstone removals are possible
144 fn_link_set: event.Locked(FnLinkSet),
145
146 pub const FnLinkSet = std.LinkedList(?*Value.Fn);
147
111148 windows_subsystem_windows: bool,
112149 windows_subsystem_console: bool,
113150
......@@ -141,7 +178,7 @@ pub const Compilation = struct {
141178
142179 /// Before code generation starts, must wait on this group to make sure
143180 /// the build is complete.
144 build_group: event.Group(BuildError!void),
181 prelink_group: event.Group(BuildError!void),
145182
146183 compile_errors: event.Locked(CompileErrList),
147184
......@@ -149,13 +186,49 @@ pub const Compilation = struct {
149186 void_type: *Type.Void,
150187 bool_type: *Type.Bool,
151188 noreturn_type: *Type.NoReturn,
189 comptime_int_type: *Type.ComptimeInt,
190 u8_type: *Type.Int,
152191
153192 void_value: *Value.Void,
154193 true_value: *Value.Bool,
155194 false_value: *Value.Bool,
156195 noreturn_value: *Value.NoReturn,
157196
158 const CompileErrList = std.ArrayList(*errmsg.Msg);
197 target_machine: llvm.TargetMachineRef,
198 target_data_ref: llvm.TargetDataRef,
199 target_layout_str: [*]u8,
200 target_ptr_bits: u32,
201
202 /// for allocating things which have the same lifetime as this Compilation
203 arena_allocator: std.heap.ArenaAllocator,
204
205 root_package: *Package,
206 std_package: *Package,
207
208 override_libc: ?*LibCInstallation,
209
210 /// need to wait on this group before deinitializing
211 deinit_group: event.Group(void),
212
213 destroy_handle: promise,
214
215 have_err_ret_tracing: bool,
216
217 /// not locked because it is read-only
218 primitive_type_table: TypeTable,
219
220 int_type_table: event.Locked(IntTypeTable),
221 array_type_table: event.Locked(ArrayTypeTable),
222 ptr_type_table: event.Locked(PtrTypeTable),
223
224 c_int_types: [CInt.list.len]*Type.Int,
225
226 const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
227 const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
228 const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
229 const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8);
230
231 const CompileErrList = std.ArrayList(*Msg);
159232
160233 // TODO handle some of these earlier and report them in a way other than error codes
161234 pub const BuildError = error{
......@@ -195,12 +268,21 @@ pub const Compilation = struct {
195268 BufferTooSmall,
196269 Unimplemented, // TODO remove this one
197270 SemanticAnalysisFailed, // TODO remove this one
271 ReadOnlyFileSystem,
272 LinkQuotaExceeded,
273 EnvironmentVariableNotFound,
274 AppDataDirUnavailable,
275 LinkFailed,
276 LibCRequiredButNotProvidedOrFound,
277 LibCMissingDynamicLinker,
278 InvalidDarwinVersionString,
279 UnsupportedLinkArchitecture,
198280 };
199281
200282 pub const Event = union(enum) {
201283 Ok,
202284 Error: BuildError,
203 Fail: []*errmsg.Msg,
285 Fail: []*Msg,
204286 };
205287
206288 pub const DarwinVersionMin = union(enum) {
......@@ -234,31 +316,29 @@ pub const Compilation = struct {
234316 event_loop_local: *EventLoopLocal,
235317 name: []const u8,
236318 root_src_path: ?[]const u8,
237 target: *const Target,
319 target: Target,
238320 kind: Kind,
239321 build_mode: builtin.Mode,
322 is_static: bool,
240323 zig_lib_dir: []const u8,
241 cache_dir: []const u8,
242324 ) !*Compilation {
243325 const loop = event_loop_local.loop;
244
245 var name_buffer = try Buffer.init(loop.allocator, name);
246 errdefer name_buffer.deinit();
247
248 const events = try event.Channel(Event).create(loop, 0);
249 errdefer events.destroy();
250
251 const comp = try loop.allocator.create(Compilation{
326 const comp = try event_loop_local.loop.allocator.create(Compilation{
252327 .loop = loop,
328 .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator),
253329 .event_loop_local = event_loop_local,
254 .events = events,
255 .name = name_buffer,
330 .events = undefined,
256331 .root_src_path = root_src_path,
257 .target = target.*,
332 .target = target,
333 .llvm_target = undefined,
258334 .kind = kind,
259335 .build_mode = build_mode,
260336 .zig_lib_dir = zig_lib_dir,
261 .cache_dir = cache_dir,
337 .zig_std_dir = undefined,
338 .tmp_dir = event.Future(BuildError![]u8).init(loop),
339
340 .name = undefined,
341 .llvm_triple = undefined,
262342
263343 .version_major = 0,
264344 .version_minor = 0,
......@@ -273,17 +353,11 @@ pub const Compilation = struct {
273353 .verbose_link = false,
274354
275355 .linker_script = null,
276 .libc_lib_dir = null,
277 .libc_static_lib_dir = null,
278 .libc_include_dir = null,
279 .msvc_lib_dir = null,
280 .kernel32_lib_dir = null,
281 .dynamic_linker = null,
282356 .out_h_path = null,
283357 .is_test = false,
284358 .each_lib_rpath = false,
285359 .strip = false,
286 .is_static = false,
360 .is_static = is_static,
287361 .linker_rdynamic = false,
288362 .clang_argv = [][]const u8{},
289363 .llvm_argv = [][]const u8{},
......@@ -291,9 +365,10 @@ pub const Compilation = struct {
291365 .rpath_list = [][]const u8{},
292366 .assembly_files = [][]const u8{},
293367 .link_objects = [][]const u8{},
368 .fn_link_set = event.Locked(FnLinkSet).init(loop, FnLinkSet.init()),
294369 .windows_subsystem_windows = false,
295370 .windows_subsystem_console = false,
296 .link_libs_list = ArrayList(*LinkLib).init(loop.allocator),
371 .link_libs_list = undefined,
297372 .libc_link_lib = null,
298373 .err_color = errmsg.Color.Auto,
299374 .darwin_frameworks = [][]const u8{},
......@@ -303,8 +378,13 @@ pub const Compilation = struct {
303378 .emit_file_type = Emit.Binary,
304379 .link_out_file = null,
305380 .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),
306 .build_group = event.Group(BuildError!void).init(loop),
381 .prelink_group = event.Group(BuildError!void).init(loop),
382 .deinit_group = event.Group(void).init(loop),
307383 .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)),
384 .int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)),
385 .array_type_table = event.Locked(ArrayTypeTable).init(loop, ArrayTypeTable.init(loop.allocator)),
386 .ptr_type_table = event.Locked(PtrTypeTable).init(loop, PtrTypeTable.init(loop.allocator)),
387 .c_int_types = undefined,
308388
309389 .meta_type = undefined,
310390 .void_type = undefined,
......@@ -314,120 +394,307 @@ pub const Compilation = struct {
314394 .false_value = undefined,
315395 .noreturn_type = undefined,
316396 .noreturn_value = undefined,
397 .comptime_int_type = undefined,
398 .u8_type = undefined,
399
400 .target_machine = undefined,
401 .target_data_ref = undefined,
402 .target_layout_str = undefined,
403 .target_ptr_bits = target.getArchPtrBitWidth(),
404
405 .root_package = undefined,
406 .std_package = undefined,
407
408 .override_libc = null,
409 .destroy_handle = undefined,
410 .have_err_ret_tracing = false,
411 .primitive_type_table = undefined,
317412 });
413 errdefer {
414 comp.int_type_table.private_data.deinit();
415 comp.array_type_table.private_data.deinit();
416 comp.ptr_type_table.private_data.deinit();
417 comp.arena_allocator.deinit();
418 comp.loop.allocator.destroy(comp);
419 }
420
421 comp.name = try Buffer.init(comp.arena(), name);
422 comp.llvm_triple = try target.getTriple(comp.arena());
423 comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);
424 comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
425 comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std");
426 comp.primitive_type_table = TypeTable.init(comp.arena());
427
428 const opt_level = switch (build_mode) {
429 builtin.Mode.Debug => llvm.CodeGenLevelNone,
430 else => llvm.CodeGenLevelAggressive,
431 };
432
433 const reloc_mode = if (is_static) llvm.RelocStatic else llvm.RelocPIC;
434
435 // LLVM creates invalid binaries on Windows sometimes.
436 // See https://github.com/ziglang/zig/issues/508
437 // As a workaround we do not use target native features on Windows.
438 var target_specific_cpu_args: ?[*]u8 = null;
439 var target_specific_cpu_features: ?[*]u8 = null;
440 errdefer llvm.DisposeMessage(target_specific_cpu_args);
441 errdefer llvm.DisposeMessage(target_specific_cpu_features);
442 if (target == Target.Native and !target.isWindows()) {
443 target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory;
444 target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory;
445 }
446
447 comp.target_machine = llvm.CreateTargetMachine(
448 comp.llvm_target,
449 comp.llvm_triple.ptr(),
450 target_specific_cpu_args orelse c"",
451 target_specific_cpu_features orelse c"",
452 opt_level,
453 reloc_mode,
454 llvm.CodeModelDefault,
455 ) orelse return error.OutOfMemory;
456 errdefer llvm.DisposeTargetMachine(comp.target_machine);
457
458 comp.target_data_ref = llvm.CreateTargetDataLayout(comp.target_machine) orelse return error.OutOfMemory;
459 errdefer llvm.DisposeTargetData(comp.target_data_ref);
460
461 comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory;
462 errdefer llvm.DisposeMessage(comp.target_layout_str);
463
464 comp.events = try event.Channel(Event).create(comp.loop, 0);
465 errdefer comp.events.destroy();
466
467 if (root_src_path) |root_src| {
468 const dirname = std.os.path.dirname(root_src) orelse ".";
469 const basename = std.os.path.basename(root_src);
470
471 comp.root_package = try Package.create(comp.arena(), dirname, basename);
472 comp.std_package = try Package.create(comp.arena(), comp.zig_std_dir, "index.zig");
473 try comp.root_package.add("std", comp.std_package);
474 } else {
475 comp.root_package = try Package.create(comp.arena(), ".", "");
476 }
477
318478 try comp.initTypes();
479
480 comp.destroy_handle = try async<loop.allocator> comp.internalDeinit();
481
319482 return comp;
320483 }
321484
485 /// it does ref the result because it could be an arbitrary integer size
486 pub async fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type {
487 if (name.len >= 2) {
488 switch (name[0]) {
489 'i', 'u' => blk: {
490 for (name[1..]) |byte|
491 switch (byte) {
492 '0'...'9' => {},
493 else => break :blk,
494 };
495 const is_signed = name[0] == 'i';
496 const bit_count = std.fmt.parseUnsigned(u32, name[1..], 10) catch |err| switch (err) {
497 error.Overflow => return error.Overflow,
498 error.InvalidCharacter => unreachable, // we just checked the characters above
499 };
500 const int_type = try await (async Type.Int.get(comp, Type.Int.Key{
501 .bit_count = bit_count,
502 .is_signed = is_signed,
503 }) catch unreachable);
504 errdefer int_type.base.base.deref();
505 return &int_type.base;
506 },
507 else => {},
508 }
509 }
510
511 if (comp.primitive_type_table.get(name)) |entry| {
512 entry.value.base.ref();
513 return entry.value;
514 }
515
516 return null;
517 }
518
322519 fn initTypes(comp: *Compilation) !void {
323 comp.meta_type = try comp.a().create(Type.MetaType{
520 comp.meta_type = try comp.arena().create(Type.MetaType{
324521 .base = Type{
522 .name = "type",
325523 .base = Value{
326524 .id = Value.Id.Type,
327 .typeof = undefined,
525 .typ = undefined,
328526 .ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice
329527 },
330528 .id = builtin.TypeId.Type,
529 .abi_alignment = Type.AbiAlignment.init(comp.loop),
331530 },
332531 .value = undefined,
333532 });
334533 comp.meta_type.value = &comp.meta_type.base;
335 comp.meta_type.base.base.typeof = &comp.meta_type.base;
336 errdefer comp.a().destroy(comp.meta_type);
534 comp.meta_type.base.base.typ = &comp.meta_type.base;
535 assert((try comp.primitive_type_table.put(comp.meta_type.base.name, &comp.meta_type.base)) == null);
337536
338 comp.void_type = try comp.a().create(Type.Void{
537 comp.void_type = try comp.arena().create(Type.Void{
339538 .base = Type{
539 .name = "void",
340540 .base = Value{
341541 .id = Value.Id.Type,
342 .typeof = &Type.MetaType.get(comp).base,
542 .typ = &Type.MetaType.get(comp).base,
343543 .ref_count = std.atomic.Int(usize).init(1),
344544 },
345545 .id = builtin.TypeId.Void,
546 .abi_alignment = Type.AbiAlignment.init(comp.loop),
346547 },
347548 });
348 errdefer comp.a().destroy(comp.void_type);
549 assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null);
349550
350 comp.noreturn_type = try comp.a().create(Type.NoReturn{
551 comp.noreturn_type = try comp.arena().create(Type.NoReturn{
351552 .base = Type{
553 .name = "noreturn",
352554 .base = Value{
353555 .id = Value.Id.Type,
354 .typeof = &Type.MetaType.get(comp).base,
556 .typ = &Type.MetaType.get(comp).base,
355557 .ref_count = std.atomic.Int(usize).init(1),
356558 },
357559 .id = builtin.TypeId.NoReturn,
560 .abi_alignment = Type.AbiAlignment.init(comp.loop),
561 },
562 });
563 assert((try comp.primitive_type_table.put(comp.noreturn_type.base.name, &comp.noreturn_type.base)) == null);
564
565 comp.comptime_int_type = try comp.arena().create(Type.ComptimeInt{
566 .base = Type{
567 .name = "comptime_int",
568 .base = Value{
569 .id = Value.Id.Type,
570 .typ = &Type.MetaType.get(comp).base,
571 .ref_count = std.atomic.Int(usize).init(1),
572 },
573 .id = builtin.TypeId.ComptimeInt,
574 .abi_alignment = Type.AbiAlignment.init(comp.loop),
358575 },
359576 });
360 errdefer comp.a().destroy(comp.noreturn_type);
577 assert((try comp.primitive_type_table.put(comp.comptime_int_type.base.name, &comp.comptime_int_type.base)) == null);
361578
362 comp.bool_type = try comp.a().create(Type.Bool{
579 comp.bool_type = try comp.arena().create(Type.Bool{
363580 .base = Type{
581 .name = "bool",
364582 .base = Value{
365583 .id = Value.Id.Type,
366 .typeof = &Type.MetaType.get(comp).base,
584 .typ = &Type.MetaType.get(comp).base,
367585 .ref_count = std.atomic.Int(usize).init(1),
368586 },
369587 .id = builtin.TypeId.Bool,
588 .abi_alignment = Type.AbiAlignment.init(comp.loop),
370589 },
371590 });
372 errdefer comp.a().destroy(comp.bool_type);
591 assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null);
373592
374 comp.void_value = try comp.a().create(Value.Void{
593 comp.void_value = try comp.arena().create(Value.Void{
375594 .base = Value{
376595 .id = Value.Id.Void,
377 .typeof = &Type.Void.get(comp).base,
596 .typ = &Type.Void.get(comp).base,
378597 .ref_count = std.atomic.Int(usize).init(1),
379598 },
380599 });
381 errdefer comp.a().destroy(comp.void_value);
382600
383 comp.true_value = try comp.a().create(Value.Bool{
601 comp.true_value = try comp.arena().create(Value.Bool{
384602 .base = Value{
385603 .id = Value.Id.Bool,
386 .typeof = &Type.Bool.get(comp).base,
604 .typ = &Type.Bool.get(comp).base,
387605 .ref_count = std.atomic.Int(usize).init(1),
388606 },
389607 .x = true,
390608 });
391 errdefer comp.a().destroy(comp.true_value);
392609
393 comp.false_value = try comp.a().create(Value.Bool{
610 comp.false_value = try comp.arena().create(Value.Bool{
394611 .base = Value{
395612 .id = Value.Id.Bool,
396 .typeof = &Type.Bool.get(comp).base,
613 .typ = &Type.Bool.get(comp).base,
397614 .ref_count = std.atomic.Int(usize).init(1),
398615 },
399616 .x = false,
400617 });
401 errdefer comp.a().destroy(comp.false_value);
402618
403 comp.noreturn_value = try comp.a().create(Value.NoReturn{
619 comp.noreturn_value = try comp.arena().create(Value.NoReturn{
404620 .base = Value{
405621 .id = Value.Id.NoReturn,
406 .typeof = &Type.NoReturn.get(comp).base,
622 .typ = &Type.NoReturn.get(comp).base,
407623 .ref_count = std.atomic.Int(usize).init(1),
408624 },
409625 });
410 errdefer comp.a().destroy(comp.noreturn_value);
626
627 for (CInt.list) |cint, i| {
628 const c_int_type = try comp.arena().create(Type.Int{
629 .base = Type{
630 .name = cint.zig_name,
631 .base = Value{
632 .id = Value.Id.Type,
633 .typ = &Type.MetaType.get(comp).base,
634 .ref_count = std.atomic.Int(usize).init(1),
635 },
636 .id = builtin.TypeId.Int,
637 .abi_alignment = Type.AbiAlignment.init(comp.loop),
638 },
639 .key = Type.Int.Key{
640 .is_signed = cint.is_signed,
641 .bit_count = comp.target.cIntTypeSizeInBits(cint.id),
642 },
643 .garbage_node = undefined,
644 });
645 comp.c_int_types[i] = c_int_type;
646 assert((try comp.primitive_type_table.put(cint.zig_name, &c_int_type.base)) == null);
647 }
648 comp.u8_type = try comp.arena().create(Type.Int{
649 .base = Type{
650 .name = "u8",
651 .base = Value{
652 .id = Value.Id.Type,
653 .typ = &Type.MetaType.get(comp).base,
654 .ref_count = std.atomic.Int(usize).init(1),
655 },
656 .id = builtin.TypeId.Int,
657 .abi_alignment = Type.AbiAlignment.init(comp.loop),
658 },
659 .key = Type.Int.Key{
660 .is_signed = false,
661 .bit_count = 8,
662 },
663 .garbage_node = undefined,
664 });
665 assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null);
411666 }
412667
413 pub fn destroy(self: *Compilation) void {
414 self.noreturn_value.base.deref(self);
415 self.void_value.base.deref(self);
416 self.false_value.base.deref(self);
417 self.true_value.base.deref(self);
418 self.noreturn_type.base.base.deref(self);
419 self.void_type.base.base.deref(self);
420 self.meta_type.base.base.deref(self);
668 /// This function can safely use async/await, because it manages Compilation's lifetime,
669 /// and EventLoopLocal.deinit will not be called until the event.Loop.run() completes.
670 async fn internalDeinit(self: *Compilation) void {
671 suspend;
672
673 await (async self.deinit_group.wait() catch unreachable);
674 if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| {
675 // TODO evented I/O?
676 os.deleteTree(self.arena(), tmp_dir) catch {};
677 } else |_| {};
421678
422679 self.events.destroy();
423 self.name.deinit();
424680
425 self.a().destroy(self);
681 llvm.DisposeMessage(self.target_layout_str);
682 llvm.DisposeTargetData(self.target_data_ref);
683 llvm.DisposeTargetMachine(self.target_machine);
684
685 self.primitive_type_table.deinit();
686
687 self.arena_allocator.deinit();
688 self.gpa().destroy(self);
689 }
690
691 pub fn destroy(self: *Compilation) void {
692 resume self.destroy_handle;
426693 }
427694
428695 pub fn build(self: *Compilation) !void {
429696 if (self.llvm_argv.len != 0) {
430 var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.a(), [][]const []const u8{
697 var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{
431698 [][]const u8{"zig (LLVM option parsing)"},
432699 self.llvm_argv,
433700 });
......@@ -436,14 +703,13 @@ pub const Compilation = struct {
436703 c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);
437704 }
438705
439 _ = try async<self.a()> self.buildAsync();
706 _ = try async<self.gpa()> self.buildAsync();
440707 }
441708
442709 async fn buildAsync(self: *Compilation) void {
443710 while (true) {
444711 // TODO directly awaiting async should guarantee memory allocation elision
445 // TODO also async before suspending should guarantee memory allocation elision
446 const build_result = await (async self.addRootSrc() catch unreachable);
712 const build_result = await (async self.compileAndLink() catch unreachable);
447713
448714 // this makes a handy error return trace and stack trace in debug mode
449715 if (std.debug.runtime_safety) {
......@@ -464,7 +730,7 @@ pub const Compilation = struct {
464730 }
465731 } else |err| {
466732 // if there's an error then the compile errors have dangling references
467 self.a().free(compile_errors);
733 self.gpa().free(compile_errors);
468734
469735 await (async self.events.put(Event{ .Error = err }) catch unreachable);
470736 }
......@@ -474,111 +740,215 @@ pub const Compilation = struct {
474740 }
475741 }
476742
477 async fn addRootSrc(self: *Compilation) !void {
478 const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");
479 // TODO async/await os.path.real
480 const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| {
481 try printError("unable to get real path '{}': {}", root_src_path, err);
482 return err;
743 async fn compileAndLink(self: *Compilation) !void {
744 if (self.root_src_path) |root_src_path| {
745 // TODO async/await os.path.real
746 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
747 try printError("unable to get real path '{}': {}", root_src_path, err);
748 return err;
749 };
750 const root_scope = blk: {
751 errdefer self.gpa().free(root_src_real_path);
752
753 // TODO async/await readFileAlloc()
754 const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| {
755 try printError("unable to open '{}': {}", root_src_real_path, err);
756 return err;
757 };
758 errdefer self.gpa().free(source_code);
759
760 const tree = try self.gpa().createOne(ast.Tree);
761 tree.* = try std.zig.parse(self.gpa(), source_code);
762 errdefer {
763 tree.deinit();
764 self.gpa().destroy(tree);
765 }
766
767 break :blk try Scope.Root.create(self, tree, root_src_real_path);
768 };
769 defer root_scope.base.deref(self);
770 const tree = root_scope.tree;
771
772 var error_it = tree.errors.iterator(0);
773 while (error_it.next()) |parse_error| {
774 const msg = try Msg.createFromParseErrorAndScope(self, root_scope, parse_error);
775 errdefer msg.destroy();
776
777 try await (async self.addCompileErrorAsync(msg) catch unreachable);
778 }
779 if (tree.errors.len != 0) {
780 return;
781 }
782
783 const decls = try Scope.Decls.create(self, &root_scope.base);
784 defer decls.base.deref(self);
785
786 var decl_group = event.Group(BuildError!void).init(self.loop);
787 var decl_group_consumed = false;
788 errdefer if (!decl_group_consumed) decl_group.cancelAll();
789
790 var it = tree.root_node.decls.iterator(0);
791 while (it.next()) |decl_ptr| {
792 const decl = decl_ptr.*;
793 switch (decl.id) {
794 ast.Node.Id.Comptime => {
795 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
796
797 try self.prelink_group.call(addCompTimeBlock, self, &decls.base, comptime_node);
798 },
799 ast.Node.Id.VarDecl => @panic("TODO"),
800 ast.Node.Id.FnProto => {
801 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
802
803 const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
804 try self.addCompileError(root_scope, Span{
805 .first = fn_proto.fn_token,
806 .last = fn_proto.fn_token + 1,
807 }, "missing function name");
808 continue;
809 };
810
811 const fn_decl = try self.gpa().create(Decl.Fn{
812 .base = Decl{
813 .id = Decl.Id.Fn,
814 .name = name,
815 .visib = parseVisibToken(tree, fn_proto.visib_token),
816 .resolution = event.Future(BuildError!void).init(self.loop),
817 .parent_scope = &decls.base,
818 },
819 .value = Decl.Fn.Val{ .Unresolved = {} },
820 .fn_proto = fn_proto,
821 });
822 errdefer self.gpa().destroy(fn_decl);
823
824 try decl_group.call(addTopLevelDecl, self, decls, &fn_decl.base);
825 },
826 ast.Node.Id.TestDecl => @panic("TODO"),
827 else => unreachable,
828 }
829 }
830 decl_group_consumed = true;
831 try await (async decl_group.wait() catch unreachable);
832
833 // Now other code can rely on the decls scope having a complete list of names.
834 decls.name_future.resolve();
835 }
836
837 (await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) {
838 error.SemanticAnalysisFailed => {},
839 else => return err,
483840 };
484 errdefer self.a().free(root_src_real_path);
485841
486 // TODO async/await readFileAlloc()
487 const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| {
488 try printError("unable to open '{}': {}", root_src_real_path, err);
489 return err;
842 const any_prelink_errors = blk: {
843 const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
844 defer compile_errors.release();
845
846 break :blk compile_errors.value.len != 0;
490847 };
491 errdefer self.a().free(source_code);
492848
493 const parsed_file = try self.a().create(ParsedFile{
494 .tree = undefined,
495 .realpath = root_src_real_path,
496 });
497 errdefer self.a().destroy(parsed_file);
498
499 parsed_file.tree = try std.zig.parse(self.a(), source_code);
500 errdefer parsed_file.tree.deinit();
501
502 const tree = &parsed_file.tree;
503
504 // create empty struct for it
505 const decls = try Scope.Decls.create(self, null);
506 defer decls.base.deref(self);
507
508 var decl_group = event.Group(BuildError!void).init(self.loop);
509 errdefer decl_group.cancelAll();
510
511 var it = tree.root_node.decls.iterator(0);
512 while (it.next()) |decl_ptr| {
513 const decl = decl_ptr.*;
514 switch (decl.id) {
515 ast.Node.Id.Comptime => @panic("TODO"),
516 ast.Node.Id.VarDecl => @panic("TODO"),
517 ast.Node.Id.FnProto => {
518 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
519
520 const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
521 try self.addCompileError(parsed_file, Span{
522 .first = fn_proto.fn_token,
523 .last = fn_proto.fn_token + 1,
524 }, "missing function name");
525 continue;
526 };
849 if (!any_prelink_errors) {
850 try await (async link(self) catch unreachable);
851 }
852 }
527853
528 const fn_decl = try self.a().create(Decl.Fn{
529 .base = Decl{
530 .id = Decl.Id.Fn,
531 .name = name,
532 .visib = parseVisibToken(tree, fn_proto.visib_token),
533 .resolution = event.Future(BuildError!void).init(self.loop),
534 .resolution_in_progress = 0,
535 .parsed_file = parsed_file,
536 .parent_scope = &decls.base,
537 },
538 .value = Decl.Fn.Val{ .Unresolved = {} },
539 .fn_proto = fn_proto,
540 });
541 errdefer self.a().destroy(fn_decl);
542
543 try decl_group.call(addTopLevelDecl, self, &fn_decl.base);
544 },
545 ast.Node.Id.TestDecl => @panic("TODO"),
546 else => unreachable,
547 }
854 /// caller takes ownership of resulting Code
855 async fn genAndAnalyzeCode(
856 comp: *Compilation,
857 scope: *Scope,
858 node: *ast.Node,
859 expected_type: ?*Type,
860 ) !*ir.Code {
861 const unanalyzed_code = try await (async ir.gen(
862 comp,
863 node,
864 scope,
865 ) catch unreachable);
866 defer unanalyzed_code.destroy(comp.gpa());
867
868 if (comp.verbose_ir) {
869 std.debug.warn("unanalyzed:\n");
870 unanalyzed_code.dump();
871 }
872
873 const analyzed_code = try await (async ir.analyze(
874 comp,
875 unanalyzed_code,
876 expected_type,
877 ) catch unreachable);
878 errdefer analyzed_code.destroy(comp.gpa());
879
880 if (comp.verbose_ir) {
881 std.debug.warn("analyzed:\n");
882 analyzed_code.dump();
548883 }
549 try await (async decl_group.wait() catch unreachable);
550 try await (async self.build_group.wait() catch unreachable);
884
885 return analyzed_code;
886 }
887
888 async fn addCompTimeBlock(
889 comp: *Compilation,
890 scope: *Scope,
891 comptime_node: *ast.Node.Comptime,
892 ) !void {
893 const void_type = Type.Void.get(comp);
894 defer void_type.base.base.deref(comp);
895
896 const analyzed_code = (await (async genAndAnalyzeCode(
897 comp,
898 scope,
899 comptime_node.expr,
900 &void_type.base,
901 ) catch unreachable)) catch |err| switch (err) {
902 // This poison value should not cause the errdefers to run. It simply means
903 // that comp.compile_errors is populated.
904 error.SemanticAnalysisFailed => return {},
905 else => return err,
906 };
907 analyzed_code.destroy(comp.gpa());
551908 }
552909
553 async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void {
554 const is_export = decl.isExported(&decl.parsed_file.tree);
910 async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
911 const tree = decl.findRootScope().tree;
912 const is_export = decl.isExported(tree);
913
914 var add_to_table_resolved = false;
915 const add_to_table = async self.addDeclToTable(decls, decl) catch unreachable;
916 errdefer if (!add_to_table_resolved) cancel add_to_table; // TODO https://github.com/ziglang/zig/issues/1261
555917
556918 if (is_export) {
557 try self.build_group.call(verifyUniqueSymbol, self, decl);
558 try self.build_group.call(resolveDecl, self, decl);
919 try self.prelink_group.call(verifyUniqueSymbol, self, decl);
920 try self.prelink_group.call(resolveDecl, self, decl);
921 }
922
923 add_to_table_resolved = true;
924 try await add_to_table;
925 }
926
927 async fn addDeclToTable(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
928 const held = await (async decls.table.acquire() catch unreachable);
929 defer held.release();
930
931 if (try held.value.put(decl.name, decl)) |other_decl| {
932 try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name);
933 // TODO note: other definition here
559934 }
560935 }
561936
562 fn addCompileError(self: *Compilation, parsed_file: *ParsedFile, span: Span, comptime fmt: []const u8, args: ...) !void {
563 const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args);
564 errdefer self.loop.allocator.free(text);
937 fn addCompileError(self: *Compilation, root: *Scope.Root, span: Span, comptime fmt: []const u8, args: ...) !void {
938 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
939 errdefer self.gpa().free(text);
940
941 const msg = try Msg.createFromScope(self, root, span, text);
942 errdefer msg.destroy();
565943
566 try self.build_group.call(addCompileErrorAsync, self, parsed_file, span, text);
944 try self.prelink_group.call(addCompileErrorAsync, self, msg);
567945 }
568946
569947 async fn addCompileErrorAsync(
570948 self: *Compilation,
571 parsed_file: *ParsedFile,
572 span: Span,
573 text: []u8,
949 msg: *Msg,
574950 ) !void {
575 const msg = try self.loop.allocator.create(errmsg.Msg{
576 .path = parsed_file.realpath,
577 .text = text,
578 .span = span,
579 .tree = &parsed_file.tree,
580 });
581 errdefer self.loop.allocator.destroy(msg);
951 errdefer msg.destroy();
582952
583953 const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
584954 defer compile_errors.release();
......@@ -592,7 +962,7 @@ pub const Compilation = struct {
592962
593963 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
594964 try self.addCompileError(
595 decl.parsed_file,
965 decl.findRootScope(),
596966 decl.getSpan(),
597967 "exported symbol collision: '{}'",
598968 decl.name,
......@@ -601,11 +971,6 @@ pub const Compilation = struct {
601971 }
602972 }
603973
604 pub fn link(self: *Compilation, out_file: ?[]const u8) !void {
605 warn("TODO link");
606 return error.Todo;
607 }
608
609974 pub fn haveLibC(self: *Compilation) bool {
610975 return self.libc_link_lib != null;
611976 }
......@@ -625,22 +990,127 @@ pub const Compilation = struct {
625990 }
626991 }
627992
628 const link_lib = try self.a().create(LinkLib{
993 const link_lib = try self.gpa().create(LinkLib{
629994 .name = name,
630995 .path = null,
631996 .provided_explicitly = provided_explicitly,
632 .symbols = ArrayList([]u8).init(self.a()),
997 .symbols = ArrayList([]u8).init(self.gpa()),
633998 });
634999 try self.link_libs_list.append(link_lib);
6351000 if (is_libc) {
6361001 self.libc_link_lib = link_lib;
1002
1003 // get a head start on looking for the native libc
1004 if (self.target == Target.Native and self.override_libc == null) {
1005 try self.deinit_group.call(startFindingNativeLibC, self);
1006 }
6371007 }
6381008 return link_lib;
6391009 }
6401010
641 fn a(self: Compilation) *mem.Allocator {
1011 /// cancels itself so no need to await or cancel the promise.
1012 async fn startFindingNativeLibC(self: *Compilation) void {
1013 await (async self.loop.yield() catch unreachable);
1014 // we don't care if it fails, we're just trying to kick off the future resolution
1015 _ = (await (async self.event_loop_local.getNativeLibC() catch unreachable)) catch return;
1016 }
1017
1018 /// General Purpose Allocator. Must free when done.
1019 fn gpa(self: Compilation) *mem.Allocator {
6421020 return self.loop.allocator;
6431021 }
1022
1023 /// Arena Allocator. Automatically freed when the Compilation is destroyed.
1024 fn arena(self: *Compilation) *mem.Allocator {
1025 return &self.arena_allocator.allocator;
1026 }
1027
1028 /// If the temporary directory for this compilation has not been created, it creates it.
1029 /// Then it creates a random file name in that dir and returns it.
1030 pub async fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer {
1031 const tmp_dir = try await (async self.getTmpDir() catch unreachable);
1032 const file_prefix = await (async self.getRandomFileName() catch unreachable);
1033
1034 const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", file_prefix[0..], suffix);
1035 defer self.gpa().free(file_name);
1036
1037 const full_path = try os.path.join(self.gpa(), tmp_dir, file_name[0..]);
1038 errdefer self.gpa().free(full_path);
1039
1040 return Buffer.fromOwnedSlice(self.gpa(), full_path);
1041 }
1042
1043 /// If the temporary directory for this Compilation has not been created, creates it.
1044 /// Then returns it. The directory is unique to this Compilation and cleaned up when
1045 /// the Compilation deinitializes.
1046 async fn getTmpDir(self: *Compilation) ![]const u8 {
1047 if (await (async self.tmp_dir.start() catch unreachable)) |ptr| return ptr.*;
1048 self.tmp_dir.data = await (async self.getTmpDirImpl() catch unreachable);
1049 self.tmp_dir.resolve();
1050 return self.tmp_dir.data;
1051 }
1052
1053 async fn getTmpDirImpl(self: *Compilation) ![]u8 {
1054 const comp_dir_name = await (async self.getRandomFileName() catch unreachable);
1055 const zig_dir_path = try getZigDir(self.gpa());
1056 defer self.gpa().free(zig_dir_path);
1057
1058 const tmp_dir = try os.path.join(self.arena(), zig_dir_path, comp_dir_name[0..]);
1059 try os.makePath(self.gpa(), tmp_dir);
1060 return tmp_dir;
1061 }
1062
1063 async fn getRandomFileName(self: *Compilation) [12]u8 {
1064 // here we replace the standard +/ with -_ so that it can be used in a file name
1065 const b64_fs_encoder = std.base64.Base64Encoder.init(
1066 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
1067 std.base64.standard_pad_char,
1068 );
1069
1070 var rand_bytes: [9]u8 = undefined;
1071
1072 {
1073 const held = await (async self.event_loop_local.prng.acquire() catch unreachable);
1074 defer held.release();
1075
1076 held.value.random.bytes(rand_bytes[0..]);
1077 }
1078
1079 var result: [12]u8 = undefined;
1080 b64_fs_encoder.encode(result[0..], rand_bytes);
1081 return result;
1082 }
1083
1084 fn registerGarbage(comp: *Compilation, comptime T: type, node: *std.atomic.Stack(*T).Node) void {
1085 // TODO put the garbage somewhere
1086 }
1087
1088 /// Returns a value which has been ref()'d once
1089 async fn analyzeConstValue(comp: *Compilation, scope: *Scope, node: *ast.Node, expected_type: *Type) !*Value {
1090 const analyzed_code = try await (async comp.genAndAnalyzeCode(scope, node, expected_type) catch unreachable);
1091 defer analyzed_code.destroy(comp.gpa());
1092
1093 return analyzed_code.getCompTimeResult(comp);
1094 }
1095
1096 async fn analyzeTypeExpr(comp: *Compilation, scope: *Scope, node: *ast.Node) !*Type {
1097 const meta_type = &Type.MetaType.get(comp).base;
1098 defer meta_type.base.deref(comp);
1099
1100 const result_val = try await (async comp.analyzeConstValue(scope, node, meta_type) catch unreachable);
1101 errdefer result_val.base.deref(comp);
1102
1103 return result_val.cast(Type).?;
1104 }
1105
1106 /// This declaration has been blessed as going into the final code generation.
1107 pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void {
1108 if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*;
1109
1110 decl.resolution.data = try await (async generateDecl(comp, decl) catch unreachable);
1111 decl.resolution.resolve();
1112 return decl.resolution.data;
1113 }
6441114};
6451115
6461116fn printError(comptime format: []const u8, args: ...) !void {
......@@ -660,17 +1130,6 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib
6601130 }
6611131}
6621132
663/// This declaration has been blessed as going into the final code generation.
664pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void {
665 if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {
666 decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable);
667 decl.resolution.resolve();
668 return decl.resolution.data;
669 } else {
670 return (await (async decl.resolution.get() catch unreachable)).*;
671 }
672}
673
6741133/// The function that actually does the generation.
6751134async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
6761135 switch (decl.id) {
......@@ -684,68 +1143,99 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
6841143}
6851144
6861145async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
687 const body_node = fn_decl.fn_proto.body_node orelse @panic("TODO extern fn proto decl");
1146 const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable);
6881147
6891148 const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
6901149 defer fndef_scope.base.deref(comp);
6911150
692 // TODO actually look at the return type of the AST
693 const return_type = &Type.Void.get(comp).base;
694 defer return_type.base.deref(comp);
695
696 const is_var_args = false;
697 const params = ([*]Type.Fn.Param)(undefined)[0..0];
698 const fn_type = try Type.Fn.create(comp, return_type, params, is_var_args);
1151 const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
6991152 defer fn_type.base.base.deref(comp);
7001153
701 var symbol_name = try std.Buffer.init(comp.a(), fn_decl.base.name);
702 errdefer symbol_name.deinit();
1154 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
1155 var symbol_name_consumed = false;
1156 errdefer if (!symbol_name_consumed) symbol_name.deinit();
7031157
1158 // The Decl.Fn owns the initial 1 reference count
7041159 const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
1160 fn_decl.value = Decl.Fn.Val{ .Fn = fn_val };
1161 symbol_name_consumed = true;
1162
1163 const analyzed_code = try await (async comp.genAndAnalyzeCode(
1164 &fndef_scope.base,
1165 body_node,
1166 fn_type.return_type,
1167 ) catch unreachable);
1168 errdefer analyzed_code.destroy(comp.gpa());
1169
1170 // Kick off rendering to LLVM module, but it doesn't block the fn decl
1171 // analysis from being complete.
1172 try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code);
1173 try comp.prelink_group.call(addFnToLinkSet, comp, fn_val);
1174}
1175
1176async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void {
1177 fn_val.base.ref();
7051178 defer fn_val.base.deref(comp);
7061179
707 fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };
1180 fn_val.link_set_node.data = fn_val;
7081181
709 const unanalyzed_code = (await (async ir.gen(
710 comp,
711 body_node,
712 &fndef_scope.base,
713 Span.token(body_node.lastToken()),
714 fn_decl.base.parsed_file,
715 ) catch unreachable)) catch |err| switch (err) {
716 // This poison value should not cause the errdefers to run. It simply means
717 // that self.compile_errors is populated.
718 // TODO https://github.com/ziglang/zig/issues/769
719 error.SemanticAnalysisFailed => return {},
720 else => return err,
1182 const held = await (async comp.fn_link_set.acquire() catch unreachable);
1183 defer held.release();
1184
1185 held.value.append(fn_val.link_set_node);
1186}
1187
1188fn getZigDir(allocator: *mem.Allocator) ![]u8 {
1189 return os.getAppDataDir(allocator, "zig");
1190}
1191
1192async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.FnProto) !*Type.Fn {
1193 const return_type_node = switch (fn_proto.return_type) {
1194 ast.Node.FnProto.ReturnType.Explicit => |n| n,
1195 ast.Node.FnProto.ReturnType.InferErrorSet => |n| n,
7211196 };
722 defer unanalyzed_code.destroy(comp.a());
723
724 if (comp.verbose_ir) {
725 std.debug.warn("unanalyzed:\n");
726 unanalyzed_code.dump();
727 }
728
729 const analyzed_code = (await (async ir.analyze(
730 comp,
731 fn_decl.base.parsed_file,
732 unanalyzed_code,
733 null,
734 ) catch unreachable)) catch |err| switch (err) {
735 // This poison value should not cause the errdefers to run. It simply means
736 // that self.compile_errors is populated.
737 // TODO https://github.com/ziglang/zig/issues/769
738 error.SemanticAnalysisFailed => return {},
739 else => return err,
1197 const return_type = try await (async comp.analyzeTypeExpr(scope, return_type_node) catch unreachable);
1198 return_type.base.deref(comp);
1199
1200 var params = ArrayList(Type.Fn.Param).init(comp.gpa());
1201 var params_consumed = false;
1202 defer if (params_consumed) {
1203 for (params.toSliceConst()) |param| {
1204 param.typ.base.deref(comp);
1205 }
1206 params.deinit();
7401207 };
741 errdefer analyzed_code.destroy(comp.a());
7421208
743 if (comp.verbose_ir) {
744 std.debug.warn("analyzed:\n");
745 analyzed_code.dump();
1209 const is_var_args = false;
1210 {
1211 var it = fn_proto.params.iterator(0);
1212 while (it.next()) |param_node_ptr| {
1213 const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?;
1214 const param_type = try await (async comp.analyzeTypeExpr(scope, param_node.type_node) catch unreachable);
1215 errdefer param_type.base.deref(comp);
1216 try params.append(Type.Fn.Param{
1217 .typ = param_type,
1218 .is_noalias = param_node.noalias_token != null,
1219 });
1220 }
7461221 }
1222 const fn_type = try Type.Fn.create(comp, return_type, params.toOwnedSlice(), is_var_args);
1223 params_consumed = true;
1224 errdefer fn_type.base.base.deref(comp);
7471225
748 // Kick off rendering to LLVM module, but it doesn't block the fn decl
749 // analysis from being complete.
750 try comp.build_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code);
1226 return fn_type;
1227}
1228
1229async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1230 const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
1231 defer fn_type.base.base.deref(comp);
1232
1233 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
1234 var symbol_name_consumed = false;
1235 defer if (!symbol_name_consumed) symbol_name.deinit();
1236
1237 // The Decl.Fn owns the initial 1 reference count
1238 const fn_proto_val = try Value.FnProto.create(comp, fn_type, symbol_name);
1239 fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val };
1240 symbol_name_consumed = true;
7511241}
src-self-hosted/decl.zig+8-6
......@@ -3,7 +3,6 @@ const Allocator = mem.Allocator;
33const mem = std.mem;
44const ast = std.zig.ast;
55const Visib = @import("visib.zig").Visib;
6const ParsedFile = @import("parsed_file.zig").ParsedFile;
76const event = std.event;
87const Value = @import("value.zig").Value;
98const Token = std.zig.Token;
......@@ -16,8 +15,6 @@ pub const Decl = struct {
1615 name: []const u8,
1716 visib: Visib,
1817 resolution: event.Future(Compilation.BuildError!void),
19 resolution_in_progress: u8,
20 parsed_file: *ParsedFile,
2118 parent_scope: *Scope,
2219
2320 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
......@@ -48,6 +45,10 @@ pub const Decl = struct {
4845 }
4946 }
5047
48 pub fn findRootScope(base: *const Decl) *Scope.Root {
49 return base.parent_scope.findRoot();
50 }
51
5152 pub const Id = enum {
5253 Var,
5354 Fn,
......@@ -61,12 +62,13 @@ pub const Decl = struct {
6162 pub const Fn = struct {
6263 base: Decl,
6364 value: Val,
64 fn_proto: *const ast.Node.FnProto,
65 fn_proto: *ast.Node.FnProto,
6566
6667 // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
67 pub const Val = union {
68 pub const Val = union(enum) {
6869 Unresolved: void,
69 Ok: *Value.Fn,
70 Fn: *Value.Fn,
71 FnProto: *Value.FnProto,
7072 };
7173
7274 pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
src-self-hosted/errmsg.zig+203-66
......@@ -4,6 +4,8 @@ const os = std.os;
44const Token = std.zig.Token;
55const ast = std.zig.ast;
66const TokenIndex = std.zig.ast.TokenIndex;
7const Compilation = @import("compilation.zig").Compilation;
8const Scope = @import("scope.zig").Scope;
79
810pub const Color = enum {
911 Auto,
......@@ -16,85 +18,220 @@ pub const Span = struct {
1618 last: ast.TokenIndex,
1719
1820 pub fn token(i: TokenIndex) Span {
19 return Span {
21 return Span{
2022 .first = i,
2123 .last = i,
2224 };
2325 }
26
27 pub fn node(n: *ast.Node) Span {
28 return Span{
29 .first = n.firstToken(),
30 .last = n.lastToken(),
31 };
32 }
2433};
2534
2635pub const Msg = struct {
27 path: []const u8,
28 text: []u8,
2936 span: Span,
30 tree: *ast.Tree,
31};
37 text: []u8,
38 data: Data,
39
40 const Data = union(enum) {
41 PathAndTree: PathAndTree,
42 ScopeAndComp: ScopeAndComp,
43 };
44
45 const PathAndTree = struct {
46 realpath: []const u8,
47 tree: *ast.Tree,
48 allocator: *mem.Allocator,
49 };
50
51 const ScopeAndComp = struct {
52 root_scope: *Scope.Root,
53 compilation: *Compilation,
54 };
55
56 pub fn destroy(self: *Msg) void {
57 switch (self.data) {
58 Data.PathAndTree => |path_and_tree| {
59 path_and_tree.allocator.free(self.text);
60 path_and_tree.allocator.destroy(self);
61 },
62 Data.ScopeAndComp => |scope_and_comp| {
63 scope_and_comp.root_scope.base.deref(scope_and_comp.compilation);
64 scope_and_comp.compilation.gpa().free(self.text);
65 scope_and_comp.compilation.gpa().destroy(self);
66 },
67 }
68 }
69
70 fn getAllocator(self: *const Msg) *mem.Allocator {
71 switch (self.data) {
72 Data.PathAndTree => |path_and_tree| {
73 return path_and_tree.allocator;
74 },
75 Data.ScopeAndComp => |scope_and_comp| {
76 return scope_and_comp.compilation.gpa();
77 },
78 }
79 }
80
81 pub fn getRealPath(self: *const Msg) []const u8 {
82 switch (self.data) {
83 Data.PathAndTree => |path_and_tree| {
84 return path_and_tree.realpath;
85 },
86 Data.ScopeAndComp => |scope_and_comp| {
87 return scope_and_comp.root_scope.realpath;
88 },
89 }
90 }
91
92 pub fn getTree(self: *const Msg) *ast.Tree {
93 switch (self.data) {
94 Data.PathAndTree => |path_and_tree| {
95 return path_and_tree.tree;
96 },
97 Data.ScopeAndComp => |scope_and_comp| {
98 return scope_and_comp.root_scope.tree;
99 },
100 }
101 }
102
103 /// Takes ownership of text
104 /// References root_scope, and derefs when the msg is freed
105 pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg {
106 const msg = try comp.gpa().create(Msg{
107 .text = text,
108 .span = span,
109 .data = Data{
110 .ScopeAndComp = ScopeAndComp{
111 .root_scope = root_scope,
112 .compilation = comp,
113 },
114 },
115 });
116 root_scope.base.ref();
117 return msg;
118 }
119
120 pub fn createFromParseErrorAndScope(
121 comp: *Compilation,
122 root_scope: *Scope.Root,
123 parse_error: *const ast.Error,
124 ) !*Msg {
125 const loc_token = parse_error.loc();
126 var text_buf = try std.Buffer.initSize(comp.gpa(), 0);
127 defer text_buf.deinit();
128
129 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
130 try parse_error.render(&root_scope.tree.tokens, out_stream);
131
132 const msg = try comp.gpa().create(Msg{
133 .text = undefined,
134 .span = Span{
135 .first = loc_token,
136 .last = loc_token,
137 },
138 .data = Data{
139 .ScopeAndComp = ScopeAndComp{
140 .root_scope = root_scope,
141 .compilation = comp,
142 },
143 },
144 });
145 root_scope.base.ref();
146 msg.text = text_buf.toOwnedSlice();
147 return msg;
148 }
149
150 /// `realpath` must outlive the returned Msg
151 /// `tree` must outlive the returned Msg
152 /// Caller owns returned Msg and must free with `allocator`
153 /// allocator will additionally be used for printing messages later.
154 pub fn createFromParseError(
155 allocator: *mem.Allocator,
156 parse_error: *const ast.Error,
157 tree: *ast.Tree,
158 realpath: []const u8,
159 ) !*Msg {
160 const loc_token = parse_error.loc();
161 var text_buf = try std.Buffer.initSize(allocator, 0);
162 defer text_buf.deinit();
163
164 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
165 try parse_error.render(&tree.tokens, out_stream);
166
167 const msg = try allocator.create(Msg{
168 .text = undefined,
169 .data = Data{
170 .PathAndTree = PathAndTree{
171 .allocator = allocator,
172 .realpath = realpath,
173 .tree = tree,
174 },
175 },
176 .span = Span{
177 .first = loc_token,
178 .last = loc_token,
179 },
180 });
181 msg.text = text_buf.toOwnedSlice();
182 errdefer allocator.destroy(msg);
183
184 return msg;
185 }
186
187 pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {
188 const allocator = msg.getAllocator();
189 const realpath = msg.getRealPath();
190 const tree = msg.getTree();
191
192 const cwd = try os.getCwd(allocator);
193 defer allocator.free(cwd);
194
195 const relpath = try os.path.relative(allocator, cwd, realpath);
196 defer allocator.free(relpath);
197
198 const path = if (relpath.len < realpath.len) relpath else realpath;
199
200 const first_token = tree.tokens.at(msg.span.first);
201 const last_token = tree.tokens.at(msg.span.last);
202 const start_loc = tree.tokenLocationPtr(0, first_token);
203 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
204 if (!color_on) {
205 try stream.print(
206 "{}:{}:{}: error: {}\n",
207 path,
208 start_loc.line + 1,
209 start_loc.column + 1,
210 msg.text,
211 );
212 return;
213 }
32214
33/// `path` must outlive the returned Msg
34/// `tree` must outlive the returned Msg
35/// Caller owns returned Msg and must free with `allocator`
36pub fn createFromParseError(
37 allocator: *mem.Allocator,
38 parse_error: *const ast.Error,
39 tree: *ast.Tree,
40 path: []const u8,
41) !*Msg {
42 const loc_token = parse_error.loc();
43 var text_buf = try std.Buffer.initSize(allocator, 0);
44 defer text_buf.deinit();
45
46 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
47 try parse_error.render(&tree.tokens, out_stream);
48
49 const msg = try allocator.create(Msg{
50 .tree = tree,
51 .path = path,
52 .text = text_buf.toOwnedSlice(),
53 .span = Span{
54 .first = loc_token,
55 .last = loc_token,
56 },
57 });
58 errdefer allocator.destroy(msg);
59
60 return msg;
61}
62
63pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void {
64 const first_token = msg.tree.tokens.at(msg.span.first);
65 const last_token = msg.tree.tokens.at(msg.span.last);
66 const start_loc = msg.tree.tokenLocationPtr(0, first_token);
67 const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token);
68 if (!color_on) {
69215 try stream.print(
70 "{}:{}:{}: error: {}\n",
71 msg.path,
216 "{}:{}:{}: error: {}\n{}\n",
217 path,
72218 start_loc.line + 1,
73219 start_loc.column + 1,
74220 msg.text,
221 tree.source[start_loc.line_start..start_loc.line_end],
75222 );
76 return;
223 try stream.writeByteNTimes(' ', start_loc.column);
224 try stream.writeByteNTimes('~', last_token.end - first_token.start);
225 try stream.write("\n");
77226 }
78227
79 try stream.print(
80 "{}:{}:{}: error: {}\n{}\n",
81 msg.path,
82 start_loc.line + 1,
83 start_loc.column + 1,
84 msg.text,
85 msg.tree.source[start_loc.line_start..start_loc.line_end],
86 );
87 try stream.writeByteNTimes(' ', start_loc.column);
88 try stream.writeByteNTimes('~', last_token.end - first_token.start);
89 try stream.write("\n");
90}
91
92pub fn printToFile(file: *os.File, msg: *const Msg, color: Color) !void {
93 const color_on = switch (color) {
94 Color.Auto => file.isTty(),
95 Color.On => true,
96 Color.Off => false,
97 };
98 var stream = &std.io.FileOutStream.init(file).stream;
99 return printToStream(stream, msg, color_on);
100}
228 pub fn printToFile(msg: *const Msg, file: *os.File, color: Color) !void {
229 const color_on = switch (color) {
230 Color.Auto => file.isTty(),
231 Color.On => true,
232 Color.Off => false,
233 };
234 var stream = &std.io.FileOutStream.init(file).stream;
235 return msg.printToStream(stream, color_on);
236 }
237};
src-self-hosted/ir.zig+1566-199
......@@ -8,10 +8,10 @@ const Value = @import("value.zig").Value;
88const Type = Value.Type;
99const assert = std.debug.assert;
1010const Token = std.zig.Token;
11const ParsedFile = @import("parsed_file.zig").ParsedFile;
1211const Span = @import("errmsg.zig").Span;
1312const llvm = @import("llvm.zig");
1413const ObjectFile = @import("codegen.zig").ObjectFile;
14const Decl = @import("decl.zig").Decl;
1515
1616pub const LVal = enum {
1717 None,
......@@ -31,10 +31,10 @@ pub const IrVal = union(enum) {
3131
3232 pub fn dump(self: IrVal) void {
3333 switch (self) {
34 IrVal.Unknown => typeof.dump(),
35 IrVal.KnownType => |typeof| {
34 IrVal.Unknown => std.debug.warn("Unknown"),
35 IrVal.KnownType => |typ| {
3636 std.debug.warn("KnownType(");
37 typeof.dump();
37 typ.dump();
3838 std.debug.warn(")");
3939 },
4040 IrVal.KnownValue => |value| {
......@@ -46,27 +46,28 @@ pub const IrVal = union(enum) {
4646 }
4747};
4848
49pub const Instruction = struct {
49pub const Inst = struct {
5050 id: Id,
5151 scope: *Scope,
5252 debug_id: usize,
5353 val: IrVal,
5454 ref_count: usize,
5555 span: Span,
56 owner_bb: *BasicBlock,
5657
5758 /// true if this instruction was generated by zig and not from user code
5859 is_generated: bool,
5960
6061 /// the instruction that is derived from this one in analysis
61 child: ?*Instruction,
62 child: ?*Inst,
6263
6364 /// the instruction that this one derives from in analysis
64 parent: ?*Instruction,
65 parent: ?*Inst,
6566
6667 /// populated durign codegen
6768 llvm_value: ?llvm.ValueRef,
6869
69 pub fn cast(base: *Instruction, comptime T: type) ?*T {
70 pub fn cast(base: *Inst, comptime T: type) ?*T {
7071 if (base.id == comptime typeToId(T)) {
7172 return @fieldParentPtr(T, "base", base);
7273 }
......@@ -76,18 +77,18 @@ pub const Instruction = struct {
7677 pub fn typeToId(comptime T: type) Id {
7778 comptime var i = 0;
7879 inline while (i < @memberCount(Id)) : (i += 1) {
79 if (T == @field(Instruction, @memberName(Id, i))) {
80 if (T == @field(Inst, @memberName(Id, i))) {
8081 return @field(Id, @memberName(Id, i));
8182 }
8283 }
8384 unreachable;
8485 }
8586
86 pub fn dump(base: *const Instruction) void {
87 pub fn dump(base: *const Inst) void {
8788 comptime var i = 0;
8889 inline while (i < @memberCount(Id)) : (i += 1) {
8990 if (base.id == @field(Id, @memberName(Id, i))) {
90 const T = @field(Instruction, @memberName(Id, i));
91 const T = @field(Inst, @memberName(Id, i));
9192 std.debug.warn("#{} = {}(", base.debug_id, @tagName(base.id));
9293 @fieldParentPtr(T, "base", base).dump();
9394 std.debug.warn(")");
......@@ -97,32 +98,40 @@ pub const Instruction = struct {
9798 unreachable;
9899 }
99100
100 pub fn hasSideEffects(base: *const Instruction) bool {
101 pub fn hasSideEffects(base: *const Inst) bool {
101102 comptime var i = 0;
102103 inline while (i < @memberCount(Id)) : (i += 1) {
103104 if (base.id == @field(Id, @memberName(Id, i))) {
104 const T = @field(Instruction, @memberName(Id, i));
105 const T = @field(Inst, @memberName(Id, i));
105106 return @fieldParentPtr(T, "base", base).hasSideEffects();
106107 }
107108 }
108109 unreachable;
109110 }
110111
111 pub fn analyze(base: *Instruction, ira: *Analyze) Analyze.Error!*Instruction {
112 comptime var i = 0;
113 inline while (i < @memberCount(Id)) : (i += 1) {
114 if (base.id == @field(Id, @memberName(Id, i))) {
115 const T = @field(Instruction, @memberName(Id, i));
116 return @fieldParentPtr(T, "base", base).analyze(ira);
117 }
112 pub async fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst {
113 switch (base.id) {
114 Id.Return => return @fieldParentPtr(Return, "base", base).analyze(ira),
115 Id.Const => return @fieldParentPtr(Const, "base", base).analyze(ira),
116 Id.Call => return @fieldParentPtr(Call, "base", base).analyze(ira),
117 Id.DeclRef => return await (async @fieldParentPtr(DeclRef, "base", base).analyze(ira) catch unreachable),
118 Id.Ref => return await (async @fieldParentPtr(Ref, "base", base).analyze(ira) catch unreachable),
119 Id.DeclVar => return @fieldParentPtr(DeclVar, "base", base).analyze(ira),
120 Id.CheckVoidStmt => return @fieldParentPtr(CheckVoidStmt, "base", base).analyze(ira),
121 Id.Phi => return @fieldParentPtr(Phi, "base", base).analyze(ira),
122 Id.Br => return @fieldParentPtr(Br, "base", base).analyze(ira),
123 Id.AddImplicitReturnType => return @fieldParentPtr(AddImplicitReturnType, "base", base).analyze(ira),
124 Id.PtrType => return await (async @fieldParentPtr(PtrType, "base", base).analyze(ira) catch unreachable),
118125 }
119 unreachable;
120126 }
121127
122 pub fn render(base: *Instruction, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?llvm.ValueRef) {
128 pub fn render(base: *Inst, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?llvm.ValueRef) {
123129 switch (base.id) {
124130 Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val),
125131 Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val),
132 Id.Call => return @fieldParentPtr(Call, "base", base).render(ofile, fn_val),
133 Id.DeclRef => unreachable,
134 Id.PtrType => unreachable,
126135 Id.Ref => @panic("TODO"),
127136 Id.DeclVar => @panic("TODO"),
128137 Id.CheckVoidStmt => @panic("TODO"),
......@@ -132,7 +141,22 @@ pub const Instruction = struct {
132141 }
133142 }
134143
135 fn getAsParam(param: *Instruction) !*Instruction {
144 fn ref(base: *Inst, builder: *Builder) void {
145 base.ref_count += 1;
146 if (base.owner_bb != builder.current_basic_block and !base.isCompTime()) {
147 base.owner_bb.ref(builder);
148 }
149 }
150
151 fn copyVal(base: *Inst, comp: *Compilation) !*Value {
152 if (base.parent.?.ref_count == 0) {
153 return base.val.KnownValue.derefAndCopy(comp);
154 }
155 return base.val.KnownValue.copy(comp);
156 }
157
158 fn getAsParam(param: *Inst) !*Inst {
159 param.ref_count -= 1;
136160 const child = param.child orelse return error.SemanticAnalysisFailed;
137161 switch (child.val) {
138162 IrVal.Unknown => return error.SemanticAnalysisFailed,
......@@ -140,28 +164,72 @@ pub const Instruction = struct {
140164 }
141165 }
142166
167 fn getConstVal(self: *Inst, ira: *Analyze) !*Value {
168 if (self.isCompTime()) {
169 return self.val.KnownValue;
170 } else {
171 try ira.addCompileError(self.span, "unable to evaluate constant expression");
172 return error.SemanticAnalysisFailed;
173 }
174 }
175
176 fn getAsConstType(param: *Inst, ira: *Analyze) !*Type {
177 const meta_type = Type.MetaType.get(ira.irb.comp);
178 meta_type.base.base.deref(ira.irb.comp);
179
180 const inst = try param.getAsParam();
181 const casted = try ira.implicitCast(inst, &meta_type.base);
182 const val = try casted.getConstVal(ira);
183 return val.cast(Value.Type).?;
184 }
185
186 fn getAsConstAlign(param: *Inst, ira: *Analyze) !u32 {
187 return error.Unimplemented;
188 //const align_type = Type.Int.get_align(ira.irb.comp);
189 //align_type.base.base.deref(ira.irb.comp);
190
191 //const inst = try param.getAsParam();
192 //const casted = try ira.implicitCast(inst, align_type);
193 //const val = try casted.getConstVal(ira);
194
195 //uint32_t align_bytes = bigint_as_unsigned(&const_val->data.x_bigint);
196 //if (align_bytes == 0) {
197 // ir_add_error(ira, value, buf_sprintf("alignment must be >= 1"));
198 // return false;
199 //}
200
201 //if (!is_power_of_2(align_bytes)) {
202 // ir_add_error(ira, value, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes));
203 // return false;
204 //}
205 }
206
143207 /// asserts that the type is known
144 fn getKnownType(self: *Instruction) *Type {
208 fn getKnownType(self: *Inst) *Type {
145209 switch (self.val) {
146 IrVal.KnownType => |typeof| return typeof,
147 IrVal.KnownValue => |value| return value.typeof,
210 IrVal.KnownType => |typ| return typ,
211 IrVal.KnownValue => |value| return value.typ,
148212 IrVal.Unknown => unreachable,
149213 }
150214 }
151215
152 pub fn setGenerated(base: *Instruction) void {
216 pub fn setGenerated(base: *Inst) void {
153217 base.is_generated = true;
154218 }
155219
156 pub fn isNoReturn(base: *const Instruction) bool {
220 pub fn isNoReturn(base: *const Inst) bool {
157221 switch (base.val) {
158222 IrVal.Unknown => return false,
159 IrVal.KnownValue => |x| return x.typeof.id == Type.Id.NoReturn,
160 IrVal.KnownType => |typeof| return typeof.id == Type.Id.NoReturn,
223 IrVal.KnownValue => |x| return x.typ.id == Type.Id.NoReturn,
224 IrVal.KnownType => |typ| return typ.id == Type.Id.NoReturn,
161225 }
162226 }
163227
164 pub fn linkToParent(self: *Instruction, parent: *Instruction) void {
228 pub fn isCompTime(base: *const Inst) bool {
229 return base.val == IrVal.KnownValue;
230 }
231
232 pub fn linkToParent(self: *Inst, parent: *Inst) void {
165233 assert(self.parent == null);
166234 assert(parent.child == null);
167235 self.parent = parent;
......@@ -177,10 +245,89 @@ pub const Instruction = struct {
177245 Phi,
178246 Br,
179247 AddImplicitReturnType,
248 Call,
249 DeclRef,
250 PtrType,
251 };
252
253 pub const Call = struct {
254 base: Inst,
255 params: Params,
256
257 const Params = struct {
258 fn_ref: *Inst,
259 args: []*Inst,
260 };
261
262 const ir_val_init = IrVal.Init.Unknown;
263
264 pub fn dump(self: *const Call) void {
265 std.debug.warn("#{}(", self.params.fn_ref.debug_id);
266 for (self.params.args) |arg| {
267 std.debug.warn("#{},", arg.debug_id);
268 }
269 std.debug.warn(")");
270 }
271
272 pub fn hasSideEffects(self: *const Call) bool {
273 return true;
274 }
275
276 pub fn analyze(self: *const Call, ira: *Analyze) !*Inst {
277 const fn_ref = try self.params.fn_ref.getAsParam();
278 const fn_ref_type = fn_ref.getKnownType();
279 const fn_type = fn_ref_type.cast(Type.Fn) orelse {
280 try ira.addCompileError(fn_ref.span, "type '{}' not a function", fn_ref_type.name);
281 return error.SemanticAnalysisFailed;
282 };
283
284 if (fn_type.params.len != self.params.args.len) {
285 try ira.addCompileError(
286 self.base.span,
287 "expected {} arguments, found {}",
288 fn_type.params.len,
289 self.params.args.len,
290 );
291 return error.SemanticAnalysisFailed;
292 }
293
294 const args = try ira.irb.arena().alloc(*Inst, self.params.args.len);
295 for (self.params.args) |arg, i| {
296 args[i] = try arg.getAsParam();
297 }
298 const new_inst = try ira.irb.build(Call, self.base.scope, self.base.span, Params{
299 .fn_ref = fn_ref,
300 .args = args,
301 });
302 new_inst.val = IrVal{ .KnownType = fn_type.return_type };
303 return new_inst;
304 }
305
306 pub fn render(self: *Call, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
307 const fn_ref = self.params.fn_ref.llvm_value.?;
308
309 const args = try ofile.arena.alloc(llvm.ValueRef, self.params.args.len);
310 for (self.params.args) |arg, i| {
311 args[i] = arg.llvm_value.?;
312 }
313
314 const llvm_cc = llvm.CCallConv;
315 const fn_inline = llvm.FnInline.Auto;
316
317 return llvm.BuildCall(
318 ofile.builder,
319 fn_ref,
320 args.ptr,
321 @intCast(c_uint, args.len),
322 llvm_cc,
323 fn_inline,
324 c"",
325 ) orelse error.OutOfMemory;
326 }
180327 };
181328
182329 pub const Const = struct {
183 base: Instruction,
330 base: Inst,
184331 params: Params,
185332
186333 const Params = struct {};
......@@ -197,7 +344,7 @@ pub const Instruction = struct {
197344 return false;
198345 }
199346
200 pub fn analyze(self: *const Const, ira: *Analyze) !*Instruction {
347 pub fn analyze(self: *const Const, ira: *Analyze) !*Inst {
201348 const new_inst = try ira.irb.build(Const, self.base.scope, self.base.span, Params{});
202349 new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() };
203350 return new_inst;
......@@ -209,11 +356,11 @@ pub const Instruction = struct {
209356 };
210357
211358 pub const Return = struct {
212 base: Instruction,
359 base: Inst,
213360 params: Params,
214361
215362 const Params = struct {
216 return_value: *Instruction,
363 return_value: *Inst,
217364 };
218365
219366 const ir_val_init = IrVal.Init.NoReturn;
......@@ -226,7 +373,7 @@ pub const Instruction = struct {
226373 return true;
227374 }
228375
229 pub fn analyze(self: *const Return, ira: *Analyze) !*Instruction {
376 pub fn analyze(self: *const Return, ira: *Analyze) !*Inst {
230377 const value = try self.params.return_value.getAsParam();
231378 const casted_value = try ira.implicitCast(value, ira.explicit_return_type);
232379
......@@ -235,25 +382,25 @@ pub const Instruction = struct {
235382 return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value });
236383 }
237384
238 pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) ?llvm.ValueRef {
385 pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
239386 const value = self.params.return_value.llvm_value;
240387 const return_type = self.params.return_value.getKnownType();
241388
242389 if (return_type.handleIsPtr()) {
243390 @panic("TODO");
244391 } else {
245 _ = llvm.BuildRet(ofile.builder, value);
392 _ = llvm.BuildRet(ofile.builder, value) orelse return error.OutOfMemory;
246393 }
247394 return null;
248395 }
249396 };
250397
251398 pub const Ref = struct {
252 base: Instruction,
399 base: Inst,
253400 params: Params,
254401
255402 const Params = struct {
256 target: *Instruction,
403 target: *Inst,
257404 mut: Type.Pointer.Mut,
258405 volatility: Type.Pointer.Vol,
259406 };
......@@ -266,7 +413,7 @@ pub const Instruction = struct {
266413 return false;
267414 }
268415
269 pub fn analyze(self: *const Ref, ira: *Analyze) !*Instruction {
416 pub async fn analyze(self: *const Ref, ira: *Analyze) !*Inst {
270417 const target = try self.params.target.getAsParam();
271418
272419 if (ira.getCompTimeValOrNullUndefOk(target)) |val| {
......@@ -275,7 +422,6 @@ pub const Instruction = struct {
275422 Value.Ptr.Mut.CompTimeConst,
276423 self.params.mut,
277424 self.params.volatility,
278 val.typeof.getAbiAlignment(ira.irb.comp),
279425 );
280426 }
281427
......@@ -285,14 +431,13 @@ pub const Instruction = struct {
285431 .volatility = self.params.volatility,
286432 });
287433 const elem_type = target.getKnownType();
288 const ptr_type = Type.Pointer.get(
289 ira.irb.comp,
290 elem_type,
291 self.params.mut,
292 self.params.volatility,
293 Type.Pointer.Size.One,
294 elem_type.getAbiAlignment(ira.irb.comp),
295 );
434 const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
435 .child_type = elem_type,
436 .mut = self.params.mut,
437 .vol = self.params.volatility,
438 .size = Type.Pointer.Size.One,
439 .alignment = Type.Pointer.Align.Abi,
440 }) catch unreachable);
296441 // TODO: potentially set the hint that this is a stack pointer. But it might not be - this
297442 // could be a ref of a global, for example
298443 new_inst.val = IrVal{ .KnownType = &ptr_type.base };
......@@ -301,8 +446,99 @@ pub const Instruction = struct {
301446 }
302447 };
303448
449 pub const DeclRef = struct {
450 base: Inst,
451 params: Params,
452
453 const Params = struct {
454 decl: *Decl,
455 lval: LVal,
456 };
457
458 const ir_val_init = IrVal.Init.Unknown;
459
460 pub fn dump(inst: *const DeclRef) void {}
461
462 pub fn hasSideEffects(inst: *const DeclRef) bool {
463 return false;
464 }
465
466 pub async fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst {
467 (await (async ira.irb.comp.resolveDecl(self.params.decl) catch unreachable)) catch |err| switch (err) {
468 error.OutOfMemory => return error.OutOfMemory,
469 else => return error.SemanticAnalysisFailed,
470 };
471 switch (self.params.decl.id) {
472 Decl.Id.CompTime => unreachable,
473 Decl.Id.Var => return error.Unimplemented,
474 Decl.Id.Fn => {
475 const fn_decl = @fieldParentPtr(Decl.Fn, "base", self.params.decl);
476 const decl_val = switch (fn_decl.value) {
477 Decl.Fn.Val.Unresolved => unreachable,
478 Decl.Fn.Val.Fn => |fn_val| &fn_val.base,
479 Decl.Fn.Val.FnProto => |fn_proto| &fn_proto.base,
480 };
481 switch (self.params.lval) {
482 LVal.None => {
483 return ira.irb.buildConstValue(self.base.scope, self.base.span, decl_val);
484 },
485 LVal.Ptr => return error.Unimplemented,
486 }
487 },
488 }
489 }
490 };
491
492 pub const PtrType = struct {
493 base: Inst,
494 params: Params,
495
496 const Params = struct {
497 child_type: *Inst,
498 mut: Type.Pointer.Mut,
499 vol: Type.Pointer.Vol,
500 size: Type.Pointer.Size,
501 alignment: ?*Inst,
502 };
503
504 const ir_val_init = IrVal.Init.Unknown;
505
506 pub fn dump(inst: *const PtrType) void {}
507
508 pub fn hasSideEffects(inst: *const PtrType) bool {
509 return false;
510 }
511
512 pub async fn analyze(self: *const PtrType, ira: *Analyze) !*Inst {
513 const child_type = try self.params.child_type.getAsConstType(ira);
514 // if (child_type->id == TypeTableEntryIdUnreachable) {
515 // ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed"));
516 // return ira->codegen->builtin_types.entry_invalid;
517 // } else if (child_type->id == TypeTableEntryIdOpaque && instruction->ptr_len == PtrLenUnknown) {
518 // ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque"));
519 // return ira->codegen->builtin_types.entry_invalid;
520 // }
521 const alignment = if (self.params.alignment) |align_inst| blk: {
522 const amt = try align_inst.getAsConstAlign(ira);
523 break :blk Type.Pointer.Align{ .Override = amt };
524 } else blk: {
525 break :blk Type.Pointer.Align{ .Abi = {} };
526 };
527 const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
528 .child_type = child_type,
529 .mut = self.params.mut,
530 .vol = self.params.vol,
531 .size = self.params.size,
532 .alignment = alignment,
533 }) catch unreachable);
534 ptr_type.base.base.deref(ira.irb.comp);
535
536 return ira.irb.buildConstValue(self.base.scope, self.base.span, &ptr_type.base.base);
537 }
538 };
539
304540 pub const DeclVar = struct {
305 base: Instruction,
541 base: Inst,
306542 params: Params,
307543
308544 const Params = struct {
......@@ -317,39 +553,46 @@ pub const Instruction = struct {
317553 return true;
318554 }
319555
320 pub fn analyze(self: *const DeclVar, ira: *Analyze) !*Instruction {
556 pub fn analyze(self: *const DeclVar, ira: *Analyze) !*Inst {
321557 return error.Unimplemented; // TODO
322558 }
323559 };
324560
325561 pub const CheckVoidStmt = struct {
326 base: Instruction,
562 base: Inst,
327563 params: Params,
328564
329565 const Params = struct {
330 target: *Instruction,
566 target: *Inst,
331567 };
332568
333569 const ir_val_init = IrVal.Init.Unknown;
334570
335 pub fn dump(inst: *const CheckVoidStmt) void {}
571 pub fn dump(self: *const CheckVoidStmt) void {
572 std.debug.warn("#{}", self.params.target.debug_id);
573 }
336574
337575 pub fn hasSideEffects(inst: *const CheckVoidStmt) bool {
338576 return true;
339577 }
340578
341 pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Instruction {
342 return error.Unimplemented; // TODO
579 pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Inst {
580 const target = try self.params.target.getAsParam();
581 if (target.getKnownType().id != Type.Id.Void) {
582 try ira.addCompileError(self.base.span, "expression value is ignored");
583 return error.SemanticAnalysisFailed;
584 }
585 return ira.irb.buildConstVoid(self.base.scope, self.base.span, true);
343586 }
344587 };
345588
346589 pub const Phi = struct {
347 base: Instruction,
590 base: Inst,
348591 params: Params,
349592
350593 const Params = struct {
351594 incoming_blocks: []*BasicBlock,
352 incoming_values: []*Instruction,
595 incoming_values: []*Inst,
353596 };
354597
355598 const ir_val_init = IrVal.Init.Unknown;
......@@ -360,18 +603,18 @@ pub const Instruction = struct {
360603 return false;
361604 }
362605
363 pub fn analyze(self: *const Phi, ira: *Analyze) !*Instruction {
606 pub fn analyze(self: *const Phi, ira: *Analyze) !*Inst {
364607 return error.Unimplemented; // TODO
365608 }
366609 };
367610
368611 pub const Br = struct {
369 base: Instruction,
612 base: Inst,
370613 params: Params,
371614
372615 const Params = struct {
373616 dest_block: *BasicBlock,
374 is_comptime: *Instruction,
617 is_comptime: *Inst,
375618 };
376619
377620 const ir_val_init = IrVal.Init.NoReturn;
......@@ -382,17 +625,41 @@ pub const Instruction = struct {
382625 return true;
383626 }
384627
385 pub fn analyze(self: *const Br, ira: *Analyze) !*Instruction {
628 pub fn analyze(self: *const Br, ira: *Analyze) !*Inst {
629 return error.Unimplemented; // TODO
630 }
631 };
632
633 pub const CondBr = struct {
634 base: Inst,
635 params: Params,
636
637 const Params = struct {
638 condition: *Inst,
639 then_block: *BasicBlock,
640 else_block: *BasicBlock,
641 is_comptime: *Inst,
642 };
643
644 const ir_val_init = IrVal.Init.NoReturn;
645
646 pub fn dump(inst: *const CondBr) void {}
647
648 pub fn hasSideEffects(inst: *const CondBr) bool {
649 return true;
650 }
651
652 pub fn analyze(self: *const CondBr, ira: *Analyze) !*Inst {
386653 return error.Unimplemented; // TODO
387654 }
388655 };
389656
390657 pub const AddImplicitReturnType = struct {
391 base: Instruction,
658 base: Inst,
392659 params: Params,
393660
394661 pub const Params = struct {
395 target: *Instruction,
662 target: *Inst,
396663 };
397664
398665 const ir_val_init = IrVal.Init.Unknown;
......@@ -405,12 +672,117 @@ pub const Instruction = struct {
405672 return true;
406673 }
407674
408 pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Instruction {
675 pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Inst {
409676 const target = try self.params.target.getAsParam();
410677 try ira.src_implicit_return_type_list.append(target);
411678 return ira.irb.buildConstVoid(self.base.scope, self.base.span, true);
412679 }
413680 };
681
682 pub const TestErr = struct {
683 base: Inst,
684 params: Params,
685
686 pub const Params = struct {
687 target: *Inst,
688 };
689
690 const ir_val_init = IrVal.Init.Unknown;
691
692 pub fn dump(inst: *const TestErr) void {
693 std.debug.warn("#{}", inst.params.target.debug_id);
694 }
695
696 pub fn hasSideEffects(inst: *const TestErr) bool {
697 return false;
698 }
699
700 pub fn analyze(self: *const TestErr, ira: *Analyze) !*Inst {
701 const target = try self.params.target.getAsParam();
702 const target_type = target.getKnownType();
703 switch (target_type.id) {
704 Type.Id.ErrorUnion => {
705 return error.Unimplemented;
706 // if (instr_is_comptime(value)) {
707 // ConstExprValue *err_union_val = ir_resolve_const(ira, value, UndefBad);
708 // if (!err_union_val)
709 // return ira->codegen->builtin_types.entry_invalid;
710
711 // if (err_union_val->special != ConstValSpecialRuntime) {
712 // ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
713 // out_val->data.x_bool = (err_union_val->data.x_err_union.err != nullptr);
714 // return ira->codegen->builtin_types.entry_bool;
715 // }
716 // }
717
718 // TypeTableEntry *err_set_type = type_entry->data.error_union.err_set_type;
719 // if (!resolve_inferred_error_set(ira->codegen, err_set_type, instruction->base.source_node)) {
720 // return ira->codegen->builtin_types.entry_invalid;
721 // }
722 // if (!type_is_global_error_set(err_set_type) &&
723 // err_set_type->data.error_set.err_count == 0)
724 // {
725 // assert(err_set_type->data.error_set.infer_fn == nullptr);
726 // ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
727 // out_val->data.x_bool = false;
728 // return ira->codegen->builtin_types.entry_bool;
729 // }
730
731 // ir_build_test_err_from(&ira->new_irb, &instruction->base, value);
732 // return ira->codegen->builtin_types.entry_bool;
733 },
734 Type.Id.ErrorSet => {
735 return ira.irb.buildConstBool(self.base.scope, self.base.span, true);
736 },
737 else => {
738 return ira.irb.buildConstBool(self.base.scope, self.base.span, false);
739 },
740 }
741 }
742 };
743
744 pub const TestCompTime = struct {
745 base: Inst,
746 params: Params,
747
748 pub const Params = struct {
749 target: *Inst,
750 };
751
752 const ir_val_init = IrVal.Init.Unknown;
753
754 pub fn dump(inst: *const TestCompTime) void {
755 std.debug.warn("#{}", inst.params.target.debug_id);
756 }
757
758 pub fn hasSideEffects(inst: *const TestCompTime) bool {
759 return false;
760 }
761
762 pub fn analyze(self: *const TestCompTime, ira: *Analyze) !*Inst {
763 const target = try self.params.target.getAsParam();
764 return ira.irb.buildConstBool(self.base.scope, self.base.span, target.isCompTime());
765 }
766 };
767
768 pub const SaveErrRetAddr = struct {
769 base: Inst,
770 params: Params,
771
772 const Params = struct {};
773
774 const ir_val_init = IrVal.Init.Unknown;
775
776 pub fn dump(inst: *const SaveErrRetAddr) void {}
777
778 pub fn hasSideEffects(inst: *const SaveErrRetAddr) bool {
779 return true;
780 }
781
782 pub fn analyze(self: *const SaveErrRetAddr, ira: *Analyze) !*Inst {
783 return ira.irb.build(Inst.SaveErrRetAddr, self.base.scope, self.base.span, Params{});
784 }
785 };
414786};
415787
416788pub const Variable = struct {
......@@ -422,8 +794,8 @@ pub const BasicBlock = struct {
422794 name_hint: [*]const u8, // must be a C string literal
423795 debug_id: usize,
424796 scope: *Scope,
425 instruction_list: std.ArrayList(*Instruction),
426 ref_instruction: ?*Instruction,
797 instruction_list: std.ArrayList(*Inst),
798 ref_instruction: ?*Inst,
427799
428800 /// for codegen
429801 llvm_block: llvm.BasicBlockRef,
......@@ -435,7 +807,7 @@ pub const BasicBlock = struct {
435807 /// the basic block that this one derives from in analysis
436808 parent: ?*BasicBlock,
437809
438 pub fn ref(self: *BasicBlock) void {
810 pub fn ref(self: *BasicBlock, builder: *Builder) void {
439811 self.ref_count += 1;
440812 }
441813
......@@ -453,7 +825,7 @@ pub const Code = struct {
453825 arena: std.heap.ArenaAllocator,
454826 return_type: ?*Type,
455827
456 /// allocator is comp.a()
828 /// allocator is comp.gpa()
457829 pub fn destroy(self: *Code, allocator: *Allocator) void {
458830 self.arena.deinit();
459831 allocator.destroy(self);
......@@ -470,6 +842,33 @@ pub const Code = struct {
470842 }
471843 }
472844 }
845
846 /// returns a ref-incremented value, or adds a compile error
847 pub fn getCompTimeResult(self: *Code, comp: *Compilation) !*Value {
848 const bb = self.basic_block_list.at(0);
849 for (bb.instruction_list.toSliceConst()) |inst| {
850 if (inst.cast(Inst.Return)) |ret_inst| {
851 const ret_value = ret_inst.params.return_value;
852 if (ret_value.isCompTime()) {
853 return ret_value.val.KnownValue.getRef();
854 }
855 try comp.addCompileError(
856 ret_value.scope.findRoot(),
857 ret_value.span,
858 "unable to evaluate constant expression",
859 );
860 return error.SemanticAnalysisFailed;
861 } else if (inst.hasSideEffects()) {
862 try comp.addCompileError(
863 inst.scope.findRoot(),
864 inst.span,
865 "unable to evaluate constant expression",
866 );
867 return error.SemanticAnalysisFailed;
868 }
869 }
870 unreachable;
871 }
473872};
474873
475874pub const Builder = struct {
......@@ -477,32 +876,36 @@ pub const Builder = struct {
477876 code: *Code,
478877 current_basic_block: *BasicBlock,
479878 next_debug_id: usize,
480 parsed_file: *ParsedFile,
879 root_scope: *Scope.Root,
481880 is_comptime: bool,
881 is_async: bool,
882 begin_scope: ?*Scope,
482883
483884 pub const Error = Analyze.Error;
484885
485 pub fn init(comp: *Compilation, parsed_file: *ParsedFile) !Builder {
486 const code = try comp.a().create(Code{
886 pub fn init(comp: *Compilation, root_scope: *Scope.Root, begin_scope: ?*Scope) !Builder {
887 const code = try comp.gpa().create(Code{
487888 .basic_block_list = undefined,
488 .arena = std.heap.ArenaAllocator.init(comp.a()),
889 .arena = std.heap.ArenaAllocator.init(comp.gpa()),
489890 .return_type = null,
490891 });
491892 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
492 errdefer code.destroy(comp.a());
893 errdefer code.destroy(comp.gpa());
493894
494895 return Builder{
495896 .comp = comp,
496 .parsed_file = parsed_file,
897 .root_scope = root_scope,
497898 .current_basic_block = undefined,
498899 .code = code,
499900 .next_debug_id = 0,
500901 .is_comptime = false,
902 .is_async = false,
903 .begin_scope = begin_scope,
501904 };
502905 }
503906
504907 pub fn abort(self: *Builder) void {
505 self.code.destroy(self.comp.a());
908 self.code.destroy(self.comp.gpa());
506909 }
507910
508911 /// Call code.destroy() when done
......@@ -517,7 +920,7 @@ pub const Builder = struct {
517920 .name_hint = name_hint,
518921 .debug_id = self.next_debug_id,
519922 .scope = scope,
520 .instruction_list = std.ArrayList(*Instruction).init(self.arena()),
923 .instruction_list = std.ArrayList(*Inst).init(self.arena()),
521924 .child = null,
522925 .parent = null,
523926 .ref_instruction = null,
......@@ -537,67 +940,208 @@ pub const Builder = struct {
537940 self.current_basic_block = basic_block;
538941 }
539942
540 pub fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Instruction {
943 pub async fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst {
541944 switch (node.id) {
542945 ast.Node.Id.Root => unreachable,
543946 ast.Node.Id.Use => unreachable,
544947 ast.Node.Id.TestDecl => unreachable,
545 ast.Node.Id.VarDecl => @panic("TODO"),
546 ast.Node.Id.Defer => @panic("TODO"),
547 ast.Node.Id.InfixOp => @panic("TODO"),
548 ast.Node.Id.PrefixOp => @panic("TODO"),
549 ast.Node.Id.SuffixOp => @panic("TODO"),
550 ast.Node.Id.Switch => @panic("TODO"),
551 ast.Node.Id.While => @panic("TODO"),
552 ast.Node.Id.For => @panic("TODO"),
553 ast.Node.Id.If => @panic("TODO"),
554 ast.Node.Id.ControlFlowExpression => return error.Unimplemented,
555 ast.Node.Id.Suspend => @panic("TODO"),
556 ast.Node.Id.VarType => @panic("TODO"),
557 ast.Node.Id.ErrorType => @panic("TODO"),
558 ast.Node.Id.FnProto => @panic("TODO"),
559 ast.Node.Id.PromiseType => @panic("TODO"),
560 ast.Node.Id.IntegerLiteral => @panic("TODO"),
561 ast.Node.Id.FloatLiteral => @panic("TODO"),
562 ast.Node.Id.StringLiteral => @panic("TODO"),
563 ast.Node.Id.MultilineStringLiteral => @panic("TODO"),
564 ast.Node.Id.CharLiteral => @panic("TODO"),
565 ast.Node.Id.BoolLiteral => @panic("TODO"),
566 ast.Node.Id.NullLiteral => @panic("TODO"),
567 ast.Node.Id.UndefinedLiteral => @panic("TODO"),
568 ast.Node.Id.ThisLiteral => @panic("TODO"),
569 ast.Node.Id.Unreachable => @panic("TODO"),
570 ast.Node.Id.Identifier => @panic("TODO"),
948 ast.Node.Id.VarDecl => return error.Unimplemented,
949 ast.Node.Id.Defer => return error.Unimplemented,
950 ast.Node.Id.InfixOp => return error.Unimplemented,
951 ast.Node.Id.PrefixOp => {
952 const prefix_op = @fieldParentPtr(ast.Node.PrefixOp, "base", node);
953 switch (prefix_op.op) {
954 ast.Node.PrefixOp.Op.AddressOf => return error.Unimplemented,
955 ast.Node.PrefixOp.Op.ArrayType => |n| return error.Unimplemented,
956 ast.Node.PrefixOp.Op.Await => return error.Unimplemented,
957 ast.Node.PrefixOp.Op.BitNot => return error.Unimplemented,
958 ast.Node.PrefixOp.Op.BoolNot => return error.Unimplemented,
959 ast.Node.PrefixOp.Op.Cancel => return error.Unimplemented,
960 ast.Node.PrefixOp.Op.OptionalType => return error.Unimplemented,
961 ast.Node.PrefixOp.Op.Negation => return error.Unimplemented,
962 ast.Node.PrefixOp.Op.NegationWrap => return error.Unimplemented,
963 ast.Node.PrefixOp.Op.Resume => return error.Unimplemented,
964 ast.Node.PrefixOp.Op.PtrType => |ptr_info| {
965 const inst = try await (async irb.genPtrType(prefix_op, ptr_info, scope) catch unreachable);
966 return irb.lvalWrap(scope, inst, lval);
967 },
968 ast.Node.PrefixOp.Op.SliceType => |ptr_info| return error.Unimplemented,
969 ast.Node.PrefixOp.Op.Try => return error.Unimplemented,
970 }
971 },
972 ast.Node.Id.SuffixOp => {
973 const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node);
974 switch (suffix_op.op) {
975 @TagType(ast.Node.SuffixOp.Op).Call => |*call| {
976 const inst = try await (async irb.genCall(suffix_op, call, scope) catch unreachable);
977 return irb.lvalWrap(scope, inst, lval);
978 },
979 @TagType(ast.Node.SuffixOp.Op).ArrayAccess => |n| return error.Unimplemented,
980 @TagType(ast.Node.SuffixOp.Op).Slice => |slice| return error.Unimplemented,
981 @TagType(ast.Node.SuffixOp.Op).ArrayInitializer => |init_list| return error.Unimplemented,
982 @TagType(ast.Node.SuffixOp.Op).StructInitializer => |init_list| return error.Unimplemented,
983 @TagType(ast.Node.SuffixOp.Op).Deref => return error.Unimplemented,
984 @TagType(ast.Node.SuffixOp.Op).UnwrapOptional => return error.Unimplemented,
985 }
986 },
987 ast.Node.Id.Switch => return error.Unimplemented,
988 ast.Node.Id.While => return error.Unimplemented,
989 ast.Node.Id.For => return error.Unimplemented,
990 ast.Node.Id.If => return error.Unimplemented,
991 ast.Node.Id.ControlFlowExpression => {
992 const control_flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", node);
993 return await (async irb.genControlFlowExpr(control_flow_expr, scope, lval) catch unreachable);
994 },
995 ast.Node.Id.Suspend => return error.Unimplemented,
996 ast.Node.Id.VarType => return error.Unimplemented,
997 ast.Node.Id.ErrorType => return error.Unimplemented,
998 ast.Node.Id.FnProto => return error.Unimplemented,
999 ast.Node.Id.PromiseType => return error.Unimplemented,
1000 ast.Node.Id.IntegerLiteral => {
1001 const int_lit = @fieldParentPtr(ast.Node.IntegerLiteral, "base", node);
1002 return irb.lvalWrap(scope, try irb.genIntLit(int_lit, scope), lval);
1003 },
1004 ast.Node.Id.FloatLiteral => return error.Unimplemented,
1005 ast.Node.Id.StringLiteral => {
1006 const str_lit = @fieldParentPtr(ast.Node.StringLiteral, "base", node);
1007 const inst = try await (async irb.genStrLit(str_lit, scope) catch unreachable);
1008 return irb.lvalWrap(scope, inst, lval);
1009 },
1010 ast.Node.Id.MultilineStringLiteral => return error.Unimplemented,
1011 ast.Node.Id.CharLiteral => return error.Unimplemented,
1012 ast.Node.Id.BoolLiteral => return error.Unimplemented,
1013 ast.Node.Id.NullLiteral => return error.Unimplemented,
1014 ast.Node.Id.UndefinedLiteral => return error.Unimplemented,
1015 ast.Node.Id.ThisLiteral => return error.Unimplemented,
1016 ast.Node.Id.Unreachable => return error.Unimplemented,
1017 ast.Node.Id.Identifier => {
1018 const identifier = @fieldParentPtr(ast.Node.Identifier, "base", node);
1019 return await (async irb.genIdentifier(identifier, scope, lval) catch unreachable);
1020 },
5711021 ast.Node.Id.GroupedExpression => {
5721022 const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node);
573 return irb.genNode(grouped_expr.expr, scope, lval);
1023 return await (async irb.genNode(grouped_expr.expr, scope, lval) catch unreachable);
5741024 },
575 ast.Node.Id.BuiltinCall => @panic("TODO"),
576 ast.Node.Id.ErrorSetDecl => @panic("TODO"),
577 ast.Node.Id.ContainerDecl => @panic("TODO"),
578 ast.Node.Id.Asm => @panic("TODO"),
579 ast.Node.Id.Comptime => @panic("TODO"),
1025 ast.Node.Id.BuiltinCall => return error.Unimplemented,
1026 ast.Node.Id.ErrorSetDecl => return error.Unimplemented,
1027 ast.Node.Id.ContainerDecl => return error.Unimplemented,
1028 ast.Node.Id.Asm => return error.Unimplemented,
1029 ast.Node.Id.Comptime => return error.Unimplemented,
5801030 ast.Node.Id.Block => {
5811031 const block = @fieldParentPtr(ast.Node.Block, "base", node);
582 return irb.lvalWrap(scope, try irb.genBlock(block, scope), lval);
1032 const inst = try await (async irb.genBlock(block, scope) catch unreachable);
1033 return irb.lvalWrap(scope, inst, lval);
5831034 },
584 ast.Node.Id.DocComment => @panic("TODO"),
585 ast.Node.Id.SwitchCase => @panic("TODO"),
586 ast.Node.Id.SwitchElse => @panic("TODO"),
587 ast.Node.Id.Else => @panic("TODO"),
588 ast.Node.Id.Payload => @panic("TODO"),
589 ast.Node.Id.PointerPayload => @panic("TODO"),
590 ast.Node.Id.PointerIndexPayload => @panic("TODO"),
591 ast.Node.Id.StructField => @panic("TODO"),
592 ast.Node.Id.UnionTag => @panic("TODO"),
593 ast.Node.Id.EnumTag => @panic("TODO"),
594 ast.Node.Id.ErrorTag => @panic("TODO"),
595 ast.Node.Id.AsmInput => @panic("TODO"),
596 ast.Node.Id.AsmOutput => @panic("TODO"),
597 ast.Node.Id.AsyncAttribute => @panic("TODO"),
598 ast.Node.Id.ParamDecl => @panic("TODO"),
599 ast.Node.Id.FieldInitializer => @panic("TODO"),
1035 ast.Node.Id.DocComment => return error.Unimplemented,
1036 ast.Node.Id.SwitchCase => return error.Unimplemented,
1037 ast.Node.Id.SwitchElse => return error.Unimplemented,
1038 ast.Node.Id.Else => return error.Unimplemented,
1039 ast.Node.Id.Payload => return error.Unimplemented,
1040 ast.Node.Id.PointerPayload => return error.Unimplemented,
1041 ast.Node.Id.PointerIndexPayload => return error.Unimplemented,
1042 ast.Node.Id.StructField => return error.Unimplemented,
1043 ast.Node.Id.UnionTag => return error.Unimplemented,
1044 ast.Node.Id.EnumTag => return error.Unimplemented,
1045 ast.Node.Id.ErrorTag => return error.Unimplemented,
1046 ast.Node.Id.AsmInput => return error.Unimplemented,
1047 ast.Node.Id.AsmOutput => return error.Unimplemented,
1048 ast.Node.Id.AsyncAttribute => return error.Unimplemented,
1049 ast.Node.Id.ParamDecl => return error.Unimplemented,
1050 ast.Node.Id.FieldInitializer => return error.Unimplemented,
1051 }
1052 }
1053
1054 async fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst {
1055 const fn_ref = try await (async irb.genNode(suffix_op.lhs, scope, LVal.None) catch unreachable);
1056
1057 const args = try irb.arena().alloc(*Inst, call.params.len);
1058 var it = call.params.iterator(0);
1059 var i: usize = 0;
1060 while (it.next()) |arg_node_ptr| : (i += 1) {
1061 args[i] = try await (async irb.genNode(arg_node_ptr.*, scope, LVal.None) catch unreachable);
6001062 }
1063
1064 //bool is_async = node->data.fn_call_expr.is_async;
1065 //IrInstruction *async_allocator = nullptr;
1066 //if (is_async) {
1067 // if (node->data.fn_call_expr.async_allocator) {
1068 // async_allocator = ir_gen_node(irb, node->data.fn_call_expr.async_allocator, scope);
1069 // if (async_allocator == irb->codegen->invalid_instruction)
1070 // return async_allocator;
1071 // }
1072 //}
1073
1074 return irb.build(Inst.Call, scope, Span.token(suffix_op.rtoken), Inst.Call.Params{
1075 .fn_ref = fn_ref,
1076 .args = args,
1077 });
1078 //IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr);
1079 //return ir_lval_wrap(irb, scope, fn_call, lval);
1080 }
1081
1082 async fn genPtrType(
1083 irb: *Builder,
1084 prefix_op: *ast.Node.PrefixOp,
1085 ptr_info: ast.Node.PrefixOp.PtrInfo,
1086 scope: *Scope,
1087 ) !*Inst {
1088 // TODO port more logic
1089
1090 //assert(node->type == NodeTypePointerType);
1091 //PtrLen ptr_len = (node->data.pointer_type.star_token->id == TokenIdStar ||
1092 // node->data.pointer_type.star_token->id == TokenIdStarStar) ? PtrLenSingle : PtrLenUnknown;
1093 //bool is_const = node->data.pointer_type.is_const;
1094 //bool is_volatile = node->data.pointer_type.is_volatile;
1095 //AstNode *expr_node = node->data.pointer_type.op_expr;
1096 //AstNode *align_expr = node->data.pointer_type.align_expr;
1097
1098 //IrInstruction *align_value;
1099 //if (align_expr != nullptr) {
1100 // align_value = ir_gen_node(irb, align_expr, scope);
1101 // if (align_value == irb->codegen->invalid_instruction)
1102 // return align_value;
1103 //} else {
1104 // align_value = nullptr;
1105 //}
1106 const child_type = try await (async irb.genNode(prefix_op.rhs, scope, LVal.None) catch unreachable);
1107
1108 //uint32_t bit_offset_start = 0;
1109 //if (node->data.pointer_type.bit_offset_start != nullptr) {
1110 // if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_start, 32, false)) {
1111 // Buf *val_buf = buf_alloc();
1112 // bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_start, 10);
1113 // exec_add_error_node(irb->codegen, irb->exec, node,
1114 // buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf)));
1115 // return irb->codegen->invalid_instruction;
1116 // }
1117 // bit_offset_start = bigint_as_unsigned(node->data.pointer_type.bit_offset_start);
1118 //}
1119
1120 //uint32_t bit_offset_end = 0;
1121 //if (node->data.pointer_type.bit_offset_end != nullptr) {
1122 // if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_end, 32, false)) {
1123 // Buf *val_buf = buf_alloc();
1124 // bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_end, 10);
1125 // exec_add_error_node(irb->codegen, irb->exec, node,
1126 // buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf)));
1127 // return irb->codegen->invalid_instruction;
1128 // }
1129 // bit_offset_end = bigint_as_unsigned(node->data.pointer_type.bit_offset_end);
1130 //}
1131
1132 //if ((bit_offset_start != 0 || bit_offset_end != 0) && bit_offset_start >= bit_offset_end) {
1133 // exec_add_error_node(irb->codegen, irb->exec, node,
1134 // buf_sprintf("bit offset start must be less than bit offset end"));
1135 // return irb->codegen->invalid_instruction;
1136 //}
1137
1138 return irb.build(Inst.PtrType, scope, Span.node(&prefix_op.base), Inst.PtrType.Params{
1139 .child_type = child_type,
1140 .mut = Type.Pointer.Mut.Mut,
1141 .vol = Type.Pointer.Vol.Non,
1142 .size = Type.Pointer.Size.Many,
1143 .alignment = null,
1144 });
6011145 }
6021146
6031147 fn isCompTime(irb: *Builder, target_scope: *Scope) bool {
......@@ -610,15 +1154,105 @@ pub const Builder = struct {
6101154 Scope.Id.CompTime => return true,
6111155 Scope.Id.FnDef => return false,
6121156 Scope.Id.Decls => unreachable,
1157 Scope.Id.Root => unreachable,
6131158 Scope.Id.Block,
6141159 Scope.Id.Defer,
6151160 Scope.Id.DeferExpr,
616 => scope = scope.parent orelse return false,
1161 => scope = scope.parent.?,
6171162 }
6181163 }
6191164 }
6201165
621 pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Instruction {
1166 pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst {
1167 const int_token = irb.root_scope.tree.tokenSlice(int_lit.token);
1168
1169 var base: u8 = undefined;
1170 var rest: []const u8 = undefined;
1171 if (int_token.len >= 3 and int_token[0] == '0') {
1172 base = switch (int_token[1]) {
1173 'b' => u8(2),
1174 'o' => u8(8),
1175 'x' => u8(16),
1176 else => unreachable,
1177 };
1178 rest = int_token[2..];
1179 } else {
1180 base = 10;
1181 rest = int_token;
1182 }
1183
1184 const comptime_int_type = Type.ComptimeInt.get(irb.comp);
1185 defer comptime_int_type.base.base.deref(irb.comp);
1186
1187 const int_val = Value.Int.createFromString(
1188 irb.comp,
1189 &comptime_int_type.base,
1190 base,
1191 rest,
1192 ) catch |err| switch (err) {
1193 error.OutOfMemory => return error.OutOfMemory,
1194 error.InvalidBase => unreachable,
1195 error.InvalidCharForDigit => unreachable,
1196 error.DigitTooLargeForBase => unreachable,
1197 };
1198 errdefer int_val.base.deref(irb.comp);
1199
1200 const inst = try irb.build(Inst.Const, scope, Span.token(int_lit.token), Inst.Const.Params{});
1201 inst.val = IrVal{ .KnownValue = &int_val.base };
1202 return inst;
1203 }
1204
1205 pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {
1206 const str_token = irb.root_scope.tree.tokenSlice(str_lit.token);
1207 const src_span = Span.token(str_lit.token);
1208
1209 var bad_index: usize = undefined;
1210 var buf = std.zig.parseStringLiteral(irb.comp.gpa(), str_token, &bad_index) catch |err| switch (err) {
1211 error.OutOfMemory => return error.OutOfMemory,
1212 error.InvalidCharacter => {
1213 try irb.comp.addCompileError(
1214 irb.root_scope,
1215 src_span,
1216 "invalid character in string literal: '{c}'",
1217 str_token[bad_index],
1218 );
1219 return error.SemanticAnalysisFailed;
1220 },
1221 };
1222 var buf_cleaned = false;
1223 errdefer if (!buf_cleaned) irb.comp.gpa().free(buf);
1224
1225 if (str_token[0] == 'c') {
1226 // first we add a null
1227 buf = try irb.comp.gpa().realloc(u8, buf, buf.len + 1);
1228 buf[buf.len - 1] = 0;
1229
1230 // next make an array value
1231 const array_val = try await (async Value.Array.createOwnedBuffer(irb.comp, buf) catch unreachable);
1232 buf_cleaned = true;
1233 defer array_val.base.deref(irb.comp);
1234
1235 // then make a pointer value pointing at the first element
1236 const ptr_val = try await (async Value.Ptr.createArrayElemPtr(
1237 irb.comp,
1238 array_val,
1239 Type.Pointer.Mut.Const,
1240 Type.Pointer.Size.Many,
1241 0,
1242 ) catch unreachable);
1243 defer ptr_val.base.deref(irb.comp);
1244
1245 return irb.buildConstValue(scope, src_span, &ptr_val.base);
1246 } else {
1247 const array_val = try await (async Value.Array.createOwnedBuffer(irb.comp, buf) catch unreachable);
1248 buf_cleaned = true;
1249 defer array_val.base.deref(irb.comp);
1250
1251 return irb.buildConstValue(scope, src_span, &array_val.base);
1252 }
1253 }
1254
1255 pub async fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst {
6221256 const block_scope = try Scope.Block.create(irb.comp, parent_scope);
6231257
6241258 const outer_block_scope = &block_scope.base;
......@@ -636,7 +1270,7 @@ pub const Builder = struct {
6361270 }
6371271
6381272 if (block.label) |label| {
639 block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena());
1273 block_scope.incoming_values = std.ArrayList(*Inst).init(irb.arena());
6401274 block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena());
6411275 block_scope.end_block = try irb.createBasicBlock(parent_scope, c"BlockEnd");
6421276 block_scope.is_comptime = try irb.buildConstBool(
......@@ -647,7 +1281,7 @@ pub const Builder = struct {
6471281 }
6481282
6491283 var is_continuation_unreachable = false;
650 var noreturn_return_value: ?*Instruction = null;
1284 var noreturn_return_value: ?*Inst = null;
6511285
6521286 var stmt_it = block.statements.iterator(0);
6531287 while (stmt_it.next()) |statement_node_ptr| {
......@@ -655,7 +1289,7 @@ pub const Builder = struct {
6551289
6561290 if (statement_node.cast(ast.Node.Defer)) |defer_node| {
6571291 // defer starts a new scope
658 const defer_token = irb.parsed_file.tree.tokens.at(defer_node.defer_token);
1292 const defer_token = irb.root_scope.tree.tokens.at(defer_node.defer_token);
6591293 const kind = switch (defer_token.id) {
6601294 Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,
6611295 Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,
......@@ -666,7 +1300,7 @@ pub const Builder = struct {
6661300 child_scope = &defer_child_scope.base;
6671301 continue;
6681302 }
669 const statement_value = try irb.genNode(statement_node, child_scope, LVal.None);
1303 const statement_value = try await (async irb.genNode(statement_node, child_scope, LVal.None) catch unreachable);
6701304
6711305 is_continuation_unreachable = statement_value.isNoReturn();
6721306 if (is_continuation_unreachable) {
......@@ -674,16 +1308,19 @@ pub const Builder = struct {
6741308 noreturn_return_value = statement_value;
6751309 }
6761310
677 if (statement_value.cast(Instruction.DeclVar)) |decl_var| {
1311 if (statement_value.cast(Inst.DeclVar)) |decl_var| {
6781312 // variable declarations start a new scope
6791313 child_scope = decl_var.params.variable.child_scope;
6801314 } else if (!is_continuation_unreachable) {
6811315 // this statement's value must be void
6821316 _ = irb.build(
683 Instruction.CheckVoidStmt,
1317 Inst.CheckVoidStmt,
6841318 child_scope,
685 statement_value.span,
686 Instruction.CheckVoidStmt.Params{ .target = statement_value },
1319 Span{
1320 .first = statement_node.firstToken(),
1321 .last = statement_node.lastToken(),
1322 },
1323 Inst.CheckVoidStmt.Params{ .target = statement_value },
6871324 );
6881325 }
6891326 }
......@@ -695,7 +1332,7 @@ pub const Builder = struct {
6951332 }
6961333
6971334 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
698 return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{
1335 return irb.build(Inst.Phi, parent_scope, Span.token(block.rbrace), Inst.Phi.Params{
6991336 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
7001337 .incoming_values = block_scope.incoming_values.toOwnedSlice(),
7011338 });
......@@ -706,26 +1343,216 @@ pub const Builder = struct {
7061343 try block_scope.incoming_values.append(
7071344 try irb.buildConstVoid(parent_scope, Span.token(block.rbrace), true),
7081345 );
709 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit);
1346 _ = try await (async irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit) catch unreachable);
7101347
711 _ = try irb.buildGen(Instruction.Br, parent_scope, Span.token(block.rbrace), Instruction.Br.Params{
1348 _ = try irb.buildGen(Inst.Br, parent_scope, Span.token(block.rbrace), Inst.Br.Params{
7121349 .dest_block = block_scope.end_block,
7131350 .is_comptime = block_scope.is_comptime,
7141351 });
7151352
7161353 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
7171354
718 return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{
1355 return irb.build(Inst.Phi, parent_scope, Span.token(block.rbrace), Inst.Phi.Params{
7191356 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
7201357 .incoming_values = block_scope.incoming_values.toOwnedSlice(),
7211358 });
7221359 }
7231360
724 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit);
1361 _ = try await (async irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit) catch unreachable);
7251362 return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true);
7261363 }
7271364
728 fn genDefersForBlock(
1365 pub async fn genControlFlowExpr(
1366 irb: *Builder,
1367 control_flow_expr: *ast.Node.ControlFlowExpression,
1368 scope: *Scope,
1369 lval: LVal,
1370 ) !*Inst {
1371 switch (control_flow_expr.kind) {
1372 ast.Node.ControlFlowExpression.Kind.Break => |arg| return error.Unimplemented,
1373 ast.Node.ControlFlowExpression.Kind.Continue => |arg| return error.Unimplemented,
1374 ast.Node.ControlFlowExpression.Kind.Return => {
1375 const src_span = Span.token(control_flow_expr.ltoken);
1376 if (scope.findFnDef() == null) {
1377 try irb.comp.addCompileError(
1378 irb.root_scope,
1379 src_span,
1380 "return expression outside function definition",
1381 );
1382 return error.SemanticAnalysisFailed;
1383 }
1384
1385 if (scope.findDeferExpr()) |scope_defer_expr| {
1386 if (!scope_defer_expr.reported_err) {
1387 try irb.comp.addCompileError(
1388 irb.root_scope,
1389 src_span,
1390 "cannot return from defer expression",
1391 );
1392 scope_defer_expr.reported_err = true;
1393 }
1394 return error.SemanticAnalysisFailed;
1395 }
1396
1397 const outer_scope = irb.begin_scope.?;
1398 const return_value = if (control_flow_expr.rhs) |rhs| blk: {
1399 break :blk try await (async irb.genNode(rhs, scope, LVal.None) catch unreachable);
1400 } else blk: {
1401 break :blk try irb.buildConstVoid(scope, src_span, true);
1402 };
1403
1404 const defer_counts = irb.countDefers(scope, outer_scope);
1405 const have_err_defers = defer_counts.error_exit != 0;
1406 if (have_err_defers or irb.comp.have_err_ret_tracing) {
1407 const err_block = try irb.createBasicBlock(scope, c"ErrRetErr");
1408 const ok_block = try irb.createBasicBlock(scope, c"ErrRetOk");
1409 if (!have_err_defers) {
1410 _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit) catch unreachable);
1411 }
1412
1413 const is_err = try irb.build(
1414 Inst.TestErr,
1415 scope,
1416 src_span,
1417 Inst.TestErr.Params{ .target = return_value },
1418 );
1419
1420 const err_is_comptime = try irb.buildTestCompTime(scope, src_span, is_err);
1421
1422 _ = try irb.buildGen(Inst.CondBr, scope, src_span, Inst.CondBr.Params{
1423 .condition = is_err,
1424 .then_block = err_block,
1425 .else_block = ok_block,
1426 .is_comptime = err_is_comptime,
1427 });
1428
1429 const ret_stmt_block = try irb.createBasicBlock(scope, c"RetStmt");
1430
1431 try irb.setCursorAtEndAndAppendBlock(err_block);
1432 if (have_err_defers) {
1433 _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ErrorExit) catch unreachable);
1434 }
1435 if (irb.comp.have_err_ret_tracing and !irb.isCompTime(scope)) {
1436 _ = try irb.build(Inst.SaveErrRetAddr, scope, src_span, Inst.SaveErrRetAddr.Params{});
1437 }
1438 _ = try irb.build(Inst.Br, scope, src_span, Inst.Br.Params{
1439 .dest_block = ret_stmt_block,
1440 .is_comptime = err_is_comptime,
1441 });
1442
1443 try irb.setCursorAtEndAndAppendBlock(ok_block);
1444 if (have_err_defers) {
1445 _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit) catch unreachable);
1446 }
1447 _ = try irb.build(Inst.Br, scope, src_span, Inst.Br.Params{
1448 .dest_block = ret_stmt_block,
1449 .is_comptime = err_is_comptime,
1450 });
1451
1452 try irb.setCursorAtEndAndAppendBlock(ret_stmt_block);
1453 return irb.genAsyncReturn(scope, src_span, return_value, false);
1454 } else {
1455 _ = try await (async irb.genDefersForBlock(scope, outer_scope, Scope.Defer.Kind.ScopeExit) catch unreachable);
1456 return irb.genAsyncReturn(scope, src_span, return_value, false);
1457 }
1458 },
1459 }
1460 }
1461
1462 pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
1463 const src_span = Span.token(identifier.token);
1464 const name = irb.root_scope.tree.tokenSlice(identifier.token);
1465
1466 //if (buf_eql_str(variable_name, "_") && lval == LValPtr) {
1467 // IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);
1468 // const_instruction->base.value.type = get_pointer_to_type(irb->codegen,
1469 // irb->codegen->builtin_types.entry_void, false);
1470 // const_instruction->base.value.special = ConstValSpecialStatic;
1471 // const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialDiscard;
1472 // return &const_instruction->base;
1473 //}
1474
1475 if (await (async irb.comp.getPrimitiveType(name) catch unreachable)) |result| {
1476 if (result) |primitive_type| {
1477 defer primitive_type.base.deref(irb.comp);
1478 switch (lval) {
1479 // if (lval == LValPtr) {
1480 // return ir_build_ref(irb, scope, node, value, false, false);
1481 LVal.Ptr => return error.Unimplemented,
1482 LVal.None => return irb.buildConstValue(scope, src_span, &primitive_type.base),
1483 }
1484 }
1485 } else |err| switch (err) {
1486 error.Overflow => {
1487 try irb.comp.addCompileError(irb.root_scope, src_span, "integer too large");
1488 return error.SemanticAnalysisFailed;
1489 },
1490 error.OutOfMemory => return error.OutOfMemory,
1491 }
1492
1493 //VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name);
1494 //if (var) {
1495 // IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var);
1496 // if (lval == LValPtr)
1497 // return var_ptr;
1498 // else
1499 // return ir_build_load_ptr(irb, scope, node, var_ptr);
1500 //}
1501
1502 if (await (async irb.findDecl(scope, name) catch unreachable)) |decl| {
1503 return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{
1504 .decl = decl,
1505 .lval = lval,
1506 });
1507 }
1508
1509 //if (node->owner->any_imports_failed) {
1510 // // skip the error message since we had a failing import in this file
1511 // // if an import breaks we don't need redundant undeclared identifier errors
1512 // return irb->codegen->invalid_instruction;
1513 //}
1514
1515 // TODO put a variable of same name with invalid type in global scope
1516 // so that future references to this same name will find a variable with an invalid type
1517
1518 try irb.comp.addCompileError(irb.root_scope, src_span, "unknown identifier '{}'", name);
1519 return error.SemanticAnalysisFailed;
1520 }
1521
1522 const DeferCounts = struct {
1523 scope_exit: usize,
1524 error_exit: usize,
1525 };
1526
1527 fn countDefers(irb: *Builder, inner_scope: *Scope, outer_scope: *Scope) DeferCounts {
1528 var result = DeferCounts{ .scope_exit = 0, .error_exit = 0 };
1529
1530 var scope = inner_scope;
1531 while (scope != outer_scope) {
1532 switch (scope.id) {
1533 Scope.Id.Defer => {
1534 const defer_scope = @fieldParentPtr(Scope.Defer, "base", scope);
1535 switch (defer_scope.kind) {
1536 Scope.Defer.Kind.ScopeExit => result.scope_exit += 1,
1537 Scope.Defer.Kind.ErrorExit => result.error_exit += 1,
1538 }
1539 scope = scope.parent orelse break;
1540 },
1541 Scope.Id.FnDef => break,
1542
1543 Scope.Id.CompTime,
1544 Scope.Id.Block,
1545 Scope.Id.Decls,
1546 Scope.Id.Root,
1547 => scope = scope.parent orelse break,
1548
1549 Scope.Id.DeferExpr => unreachable,
1550 }
1551 }
1552 return result;
1553 }
1554
1555 async fn genDefersForBlock(
7291556 irb: *Builder,
7301557 inner_scope: *Scope,
7311558 outer_scope: *Scope,
......@@ -743,25 +1570,26 @@ pub const Builder = struct {
7431570 };
7441571 if (generate) {
7451572 const defer_expr_scope = defer_scope.defer_expr_scope;
746 const instruction = try irb.genNode(
1573 const instruction = try await (async irb.genNode(
7471574 defer_expr_scope.expr_node,
7481575 &defer_expr_scope.base,
7491576 LVal.None,
750 );
1577 ) catch unreachable);
7511578 if (instruction.isNoReturn()) {
7521579 is_noreturn = true;
7531580 } else {
7541581 _ = try irb.build(
755 Instruction.CheckVoidStmt,
1582 Inst.CheckVoidStmt,
7561583 &defer_expr_scope.base,
7571584 Span.token(defer_expr_scope.expr_node.lastToken()),
758 Instruction.CheckVoidStmt.Params{ .target = instruction },
1585 Inst.CheckVoidStmt.Params{ .target = instruction },
7591586 );
7601587 }
7611588 }
7621589 },
7631590 Scope.Id.FnDef,
7641591 Scope.Id.Decls,
1592 Scope.Id.Root,
7651593 => return is_noreturn,
7661594
7671595 Scope.Id.CompTime,
......@@ -773,13 +1601,13 @@ pub const Builder = struct {
7731601 }
7741602 }
7751603
776 pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Instruction, lval: LVal) !*Instruction {
1604 pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Inst, lval: LVal) !*Inst {
7771605 switch (lval) {
7781606 LVal.None => return instruction,
7791607 LVal.Ptr => {
7801608 // We needed a pointer to a value, but we got a value. So we create
7811609 // an instruction which just makes a const pointer of it.
782 return irb.build(Instruction.Ref, scope, instruction.span, Instruction.Ref.Params{
1610 return irb.build(Inst.Ref, scope, instruction.span, Inst.Ref.Params{
7831611 .target = instruction,
7841612 .mut = Type.Pointer.Mut.Const,
7851613 .volatility = Type.Pointer.Vol.Non,
......@@ -799,10 +1627,10 @@ pub const Builder = struct {
7991627 span: Span,
8001628 params: I.Params,
8011629 is_generated: bool,
802 ) !*Instruction {
1630 ) !*Inst {
8031631 const inst = try self.arena().create(I{
804 .base = Instruction{
805 .id = Instruction.typeToId(I),
1632 .base = Inst{
1633 .id = Inst.typeToId(I),
8061634 .is_generated = is_generated,
8071635 .scope = scope,
8081636 .debug_id = self.next_debug_id,
......@@ -816,6 +1644,7 @@ pub const Builder = struct {
8161644 .child = null,
8171645 .parent = null,
8181646 .llvm_value = undefined,
1647 .owner_bb = self.current_basic_block,
8191648 },
8201649 .params = params,
8211650 });
......@@ -825,9 +1654,27 @@ pub const Builder = struct {
8251654 inline while (i < @memberCount(I.Params)) : (i += 1) {
8261655 const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i)));
8271656 switch (FieldType) {
828 *Instruction => @field(inst.params, @memberName(I.Params, i)).ref_count += 1,
829 ?*Instruction => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref_count += 1,
830 else => {},
1657 *Inst => @field(inst.params, @memberName(I.Params, i)).ref(self),
1658 *BasicBlock => @field(inst.params, @memberName(I.Params, i)).ref(self),
1659 ?*Inst => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref(self),
1660 []*Inst => {
1661 // TODO https://github.com/ziglang/zig/issues/1269
1662 for (@field(inst.params, @memberName(I.Params, i))) |other|
1663 other.ref(self);
1664 },
1665 []*BasicBlock => {
1666 // TODO https://github.com/ziglang/zig/issues/1269
1667 for (@field(inst.params, @memberName(I.Params, i))) |other|
1668 other.ref(self);
1669 },
1670 Type.Pointer.Mut,
1671 Type.Pointer.Vol,
1672 Type.Pointer.Size,
1673 LVal,
1674 *Decl,
1675 => {},
1676 // it's ok to add more types here, just make sure any instructions are ref'd appropriately
1677 else => @compileError("unrecognized type in Params: " ++ @typeName(FieldType)),
8311678 }
8321679 }
8331680
......@@ -842,7 +1689,7 @@ pub const Builder = struct {
8421689 scope: *Scope,
8431690 span: Span,
8441691 params: I.Params,
845 ) !*Instruction {
1692 ) !*Inst {
8461693 return self.buildExtra(I, scope, span, params, false);
8471694 }
8481695
......@@ -852,21 +1699,95 @@ pub const Builder = struct {
8521699 scope: *Scope,
8531700 span: Span,
8541701 params: I.Params,
855 ) !*Instruction {
1702 ) !*Inst {
8561703 return self.buildExtra(I, scope, span, params, true);
8571704 }
8581705
859 fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Instruction {
860 const inst = try self.build(Instruction.Const, scope, span, Instruction.Const.Params{});
1706 fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Inst {
1707 const inst = try self.build(Inst.Const, scope, span, Inst.Const.Params{});
8611708 inst.val = IrVal{ .KnownValue = &Value.Bool.get(self.comp, x).base };
8621709 return inst;
8631710 }
8641711
865 fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Instruction {
866 const inst = try self.buildExtra(Instruction.Const, scope, span, Instruction.Const.Params{}, is_generated);
1712 fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Inst {
1713 const inst = try self.buildExtra(Inst.Const, scope, span, Inst.Const.Params{}, is_generated);
8671714 inst.val = IrVal{ .KnownValue = &Value.Void.get(self.comp).base };
8681715 return inst;
8691716 }
1717
1718 fn buildConstValue(self: *Builder, scope: *Scope, span: Span, v: *Value) !*Inst {
1719 const inst = try self.build(Inst.Const, scope, span, Inst.Const.Params{});
1720 inst.val = IrVal{ .KnownValue = v.getRef() };
1721 return inst;
1722 }
1723
1724 /// If the code is explicitly set to be comptime, then builds a const bool,
1725 /// otherwise builds a TestCompTime instruction.
1726 fn buildTestCompTime(self: *Builder, scope: *Scope, span: Span, target: *Inst) !*Inst {
1727 if (self.isCompTime(scope)) {
1728 return self.buildConstBool(scope, span, true);
1729 } else {
1730 return self.build(
1731 Inst.TestCompTime,
1732 scope,
1733 span,
1734 Inst.TestCompTime.Params{ .target = target },
1735 );
1736 }
1737 }
1738
1739 fn genAsyncReturn(irb: *Builder, scope: *Scope, span: Span, result: *Inst, is_gen: bool) !*Inst {
1740 _ = irb.buildGen(
1741 Inst.AddImplicitReturnType,
1742 scope,
1743 span,
1744 Inst.AddImplicitReturnType.Params{ .target = result },
1745 );
1746
1747 if (!irb.is_async) {
1748 return irb.buildExtra(
1749 Inst.Return,
1750 scope,
1751 span,
1752 Inst.Return.Params{ .return_value = result },
1753 is_gen,
1754 );
1755 }
1756 return error.Unimplemented;
1757
1758 //ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value);
1759 //IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node,
1760 // get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
1761 //// TODO replace replacement_value with @intToPtr(?promise, 0x1) when it doesn't crash zig
1762 //IrInstruction *replacement_value = irb->exec->coro_handle;
1763 //IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, scope, node,
1764 // promise_type_val, irb->exec->coro_awaiter_field_ptr, nullptr, replacement_value, nullptr,
1765 // AtomicRmwOp_xchg, AtomicOrderSeqCst);
1766 //ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, maybe_await_handle);
1767 //IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_await_handle);
1768 //IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
1769 //return ir_build_cond_br(irb, scope, node, is_non_null, irb->exec->coro_normal_final, irb->exec->coro_early_final,
1770 // is_comptime);
1771 //// the above blocks are rendered by ir_gen after the rest of codegen
1772 }
1773
1774 async fn findDecl(irb: *Builder, scope: *Scope, name: []const u8) ?*Decl {
1775 var s = scope;
1776 while (true) {
1777 switch (s.id) {
1778 Scope.Id.Decls => {
1779 const decls = @fieldParentPtr(Scope.Decls, "base", s);
1780 const table = await (async decls.getTableReadOnly() catch unreachable);
1781 if (table.get(name)) |entry| {
1782 return entry.value;
1783 }
1784 },
1785 Scope.Id.Root => return null,
1786 else => {},
1787 }
1788 s = s.parent.?;
1789 }
1790 }
8701791};
8711792
8721793const Analyze = struct {
......@@ -875,7 +1796,7 @@ const Analyze = struct {
8751796 const_predecessor_bb: ?*BasicBlock,
8761797 parent_basic_block: *BasicBlock,
8771798 instruction_index: usize,
878 src_implicit_return_type_list: std.ArrayList(*Instruction),
1799 src_implicit_return_type_list: std.ArrayList(*Inst),
8791800 explicit_return_type: ?*Type,
8801801
8811802 pub const Error = error{
......@@ -889,8 +1810,8 @@ const Analyze = struct {
8891810 OutOfMemory,
8901811 };
8911812
892 pub fn init(comp: *Compilation, parsed_file: *ParsedFile, explicit_return_type: ?*Type) !Analyze {
893 var irb = try Builder.init(comp, parsed_file);
1813 pub fn init(comp: *Compilation, root_scope: *Scope.Root, explicit_return_type: ?*Type) !Analyze {
1814 var irb = try Builder.init(comp, root_scope, null);
8941815 errdefer irb.abort();
8951816
8961817 return Analyze{
......@@ -899,7 +1820,7 @@ const Analyze = struct {
8991820 .const_predecessor_bb = null,
9001821 .parent_basic_block = undefined, // initialized with startBasicBlock
9011822 .instruction_index = undefined, // initialized with startBasicBlock
902 .src_implicit_return_type_list = std.ArrayList(*Instruction).init(irb.arena()),
1823 .src_implicit_return_type_list = std.ArrayList(*Inst).init(irb.arena()),
9031824 .explicit_return_type = explicit_return_type,
9041825 };
9051826 }
......@@ -908,7 +1829,7 @@ const Analyze = struct {
9081829 self.irb.abort();
9091830 }
9101831
911 pub fn getNewBasicBlock(self: *Analyze, old_bb: *BasicBlock, ref_old_instruction: ?*Instruction) !*BasicBlock {
1832 pub fn getNewBasicBlock(self: *Analyze, old_bb: *BasicBlock, ref_old_instruction: ?*Inst) !*BasicBlock {
9121833 if (old_bb.child) |child| {
9131834 if (ref_old_instruction == null or child.ref_instruction != ref_old_instruction)
9141835 return child;
......@@ -968,21 +1889,478 @@ const Analyze = struct {
9681889 }
9691890
9701891 fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void {
971 return self.irb.comp.addCompileError(self.irb.parsed_file, span, fmt, args);
1892 return self.irb.comp.addCompileError(self.irb.root_scope, span, fmt, args);
9721893 }
9731894
974 fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Instruction) Analyze.Error!*Type {
1895 fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type {
9751896 // TODO actual implementation
9761897 return &Type.Void.get(self.irb.comp).base;
9771898 }
9781899
979 fn implicitCast(self: *Analyze, target: *Instruction, optional_dest_type: ?*Type) Analyze.Error!*Instruction {
1900 fn implicitCast(self: *Analyze, target: *Inst, optional_dest_type: ?*Type) Analyze.Error!*Inst {
9801901 const dest_type = optional_dest_type orelse return target;
981 @panic("TODO implicitCast");
1902 const from_type = target.getKnownType();
1903 if (from_type == dest_type or from_type.id == Type.Id.NoReturn) return target;
1904 return self.analyzeCast(target, target, dest_type);
9821905 }
9831906
984 fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Instruction) ?*Value {
985 @panic("TODO getCompTimeValOrNullUndefOk");
1907 fn analyzeCast(ira: *Analyze, source_instr: *Inst, target: *Inst, dest_type: *Type) !*Inst {
1908 const from_type = target.getKnownType();
1909
1910 //if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) {
1911 // return ira->codegen->invalid_instruction;
1912 //}
1913
1914 //// perfect match or non-const to const
1915 //ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type,
1916 // source_node, false);
1917 //if (const_cast_result.id == ConstCastResultIdOk) {
1918 // return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false);
1919 //}
1920
1921 //// widening conversion
1922 //if (wanted_type->id == TypeTableEntryIdInt &&
1923 // actual_type->id == TypeTableEntryIdInt &&
1924 // wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed &&
1925 // wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count)
1926 //{
1927 // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
1928 //}
1929
1930 //// small enough unsigned ints can get casted to large enough signed ints
1931 //if (wanted_type->id == TypeTableEntryIdInt && wanted_type->data.integral.is_signed &&
1932 // actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed &&
1933 // wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count)
1934 //{
1935 // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
1936 //}
1937
1938 //// float widening conversion
1939 //if (wanted_type->id == TypeTableEntryIdFloat &&
1940 // actual_type->id == TypeTableEntryIdFloat &&
1941 // wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count)
1942 //{
1943 // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
1944 //}
1945
1946 //// cast from [N]T to []const T
1947 //if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray) {
1948 // TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
1949 // assert(ptr_type->id == TypeTableEntryIdPointer);
1950 // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
1951 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
1952 // source_node, false).id == ConstCastResultIdOk)
1953 // {
1954 // return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
1955 // }
1956 //}
1957
1958 //// cast from *const [N]T to []const T
1959 //if (is_slice(wanted_type) &&
1960 // actual_type->id == TypeTableEntryIdPointer &&
1961 // actual_type->data.pointer.is_const &&
1962 // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray)
1963 //{
1964 // TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
1965 // assert(ptr_type->id == TypeTableEntryIdPointer);
1966
1967 // TypeTableEntry *array_type = actual_type->data.pointer.child_type;
1968
1969 // if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) &&
1970 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type,
1971 // source_node, false).id == ConstCastResultIdOk)
1972 // {
1973 // return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
1974 // }
1975 //}
1976
1977 //// cast from [N]T to *const []const T
1978 //if (wanted_type->id == TypeTableEntryIdPointer &&
1979 // wanted_type->data.pointer.is_const &&
1980 // is_slice(wanted_type->data.pointer.child_type) &&
1981 // actual_type->id == TypeTableEntryIdArray)
1982 //{
1983 // TypeTableEntry *ptr_type =
1984 // wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry;
1985 // assert(ptr_type->id == TypeTableEntryIdPointer);
1986 // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
1987 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
1988 // source_node, false).id == ConstCastResultIdOk)
1989 // {
1990 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);
1991 // if (type_is_invalid(cast1->value.type))
1992 // return ira->codegen->invalid_instruction;
1993
1994 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
1995 // if (type_is_invalid(cast2->value.type))
1996 // return ira->codegen->invalid_instruction;
1997
1998 // return cast2;
1999 // }
2000 //}
2001
2002 //// cast from [N]T to ?[]const T
2003 //if (wanted_type->id == TypeTableEntryIdOptional &&
2004 // is_slice(wanted_type->data.maybe.child_type) &&
2005 // actual_type->id == TypeTableEntryIdArray)
2006 //{
2007 // TypeTableEntry *ptr_type =
2008 // wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;
2009 // assert(ptr_type->id == TypeTableEntryIdPointer);
2010 // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
2011 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
2012 // source_node, false).id == ConstCastResultIdOk)
2013 // {
2014 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value);
2015 // if (type_is_invalid(cast1->value.type))
2016 // return ira->codegen->invalid_instruction;
2017
2018 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2019 // if (type_is_invalid(cast2->value.type))
2020 // return ira->codegen->invalid_instruction;
2021
2022 // return cast2;
2023 // }
2024 //}
2025
2026 //// *[N]T to [*]T
2027 //if (wanted_type->id == TypeTableEntryIdPointer &&
2028 // wanted_type->data.pointer.ptr_len == PtrLenUnknown &&
2029 // actual_type->id == TypeTableEntryIdPointer &&
2030 // actual_type->data.pointer.ptr_len == PtrLenSingle &&
2031 // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray &&
2032 // actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment &&
2033 // types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
2034 // actual_type->data.pointer.child_type->data.array.child_type, source_node,
2035 // !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
2036 //{
2037 // return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type);
2038 //}
2039
2040 //// *[N]T to []T
2041 //if (is_slice(wanted_type) &&
2042 // actual_type->id == TypeTableEntryIdPointer &&
2043 // actual_type->data.pointer.ptr_len == PtrLenSingle &&
2044 // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray)
2045 //{
2046 // TypeTableEntry *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
2047 // assert(slice_ptr_type->id == TypeTableEntryIdPointer);
2048 // if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
2049 // actual_type->data.pointer.child_type->data.array.child_type, source_node,
2050 // !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)
2051 // {
2052 // return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type);
2053 // }
2054 //}
2055
2056 //// cast from T to ?T
2057 //// note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism
2058 //if (wanted_type->id == TypeTableEntryIdOptional) {
2059 // TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type;
2060 // if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node,
2061 // false).id == ConstCastResultIdOk)
2062 // {
2063 // return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
2064 // } else if (actual_type->id == TypeTableEntryIdComptimeInt ||
2065 // actual_type->id == TypeTableEntryIdComptimeFloat)
2066 // {
2067 // if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) {
2068 // return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
2069 // } else {
2070 // return ira->codegen->invalid_instruction;
2071 // }
2072 // } else if (wanted_child_type->id == TypeTableEntryIdPointer &&
2073 // wanted_child_type->data.pointer.is_const &&
2074 // (actual_type->id == TypeTableEntryIdPointer || is_container(actual_type)))
2075 // {
2076 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_child_type, value);
2077 // if (type_is_invalid(cast1->value.type))
2078 // return ira->codegen->invalid_instruction;
2079
2080 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2081 // if (type_is_invalid(cast2->value.type))
2082 // return ira->codegen->invalid_instruction;
2083
2084 // return cast2;
2085 // }
2086 //}
2087
2088 //// cast from null literal to maybe type
2089 //if (wanted_type->id == TypeTableEntryIdOptional &&
2090 // actual_type->id == TypeTableEntryIdNull)
2091 //{
2092 // return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type);
2093 //}
2094
2095 //// cast from child type of error type to error type
2096 //if (wanted_type->id == TypeTableEntryIdErrorUnion) {
2097 // if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type,
2098 // source_node, false).id == ConstCastResultIdOk)
2099 // {
2100 // return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
2101 // } else if (actual_type->id == TypeTableEntryIdComptimeInt ||
2102 // actual_type->id == TypeTableEntryIdComptimeFloat)
2103 // {
2104 // if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) {
2105 // return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
2106 // } else {
2107 // return ira->codegen->invalid_instruction;
2108 // }
2109 // }
2110 //}
2111
2112 //// cast from [N]T to E![]const T
2113 //if (wanted_type->id == TypeTableEntryIdErrorUnion &&
2114 // is_slice(wanted_type->data.error_union.payload_type) &&
2115 // actual_type->id == TypeTableEntryIdArray)
2116 //{
2117 // TypeTableEntry *ptr_type =
2118 // wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry;
2119 // assert(ptr_type->id == TypeTableEntryIdPointer);
2120 // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
2121 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
2122 // source_node, false).id == ConstCastResultIdOk)
2123 // {
2124 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value);
2125 // if (type_is_invalid(cast1->value.type))
2126 // return ira->codegen->invalid_instruction;
2127
2128 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2129 // if (type_is_invalid(cast2->value.type))
2130 // return ira->codegen->invalid_instruction;
2131
2132 // return cast2;
2133 // }
2134 //}
2135
2136 //// cast from error set to error union type
2137 //if (wanted_type->id == TypeTableEntryIdErrorUnion &&
2138 // actual_type->id == TypeTableEntryIdErrorSet)
2139 //{
2140 // return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type);
2141 //}
2142
2143 //// cast from T to E!?T
2144 //if (wanted_type->id == TypeTableEntryIdErrorUnion &&
2145 // wanted_type->data.error_union.payload_type->id == TypeTableEntryIdOptional &&
2146 // actual_type->id != TypeTableEntryIdOptional)
2147 //{
2148 // TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type;
2149 // if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk ||
2150 // actual_type->id == TypeTableEntryIdNull ||
2151 // actual_type->id == TypeTableEntryIdComptimeInt ||
2152 // actual_type->id == TypeTableEntryIdComptimeFloat)
2153 // {
2154 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value);
2155 // if (type_is_invalid(cast1->value.type))
2156 // return ira->codegen->invalid_instruction;
2157
2158 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2159 // if (type_is_invalid(cast2->value.type))
2160 // return ira->codegen->invalid_instruction;
2161
2162 // return cast2;
2163 // }
2164 //}
2165
2166 // cast from comptime-known integer to another integer where the value fits
2167 if (target.isCompTime() and (from_type.id == Type.Id.Int or from_type.id == Type.Id.ComptimeInt)) cast: {
2168 const target_val = target.val.KnownValue;
2169 const from_int = &target_val.cast(Value.Int).?.big_int;
2170 const fits = fits: {
2171 if (dest_type.cast(Type.ComptimeInt)) |ctint| {
2172 break :fits true;
2173 }
2174 if (dest_type.cast(Type.Int)) |int| {
2175 break :fits from_int.fitsInTwosComp(int.key.is_signed, int.key.bit_count);
2176 }
2177 break :cast;
2178 };
2179 if (!fits) {
2180 try ira.addCompileError(
2181 source_instr.span,
2182 "integer value '{}' cannot be stored in type '{}'",
2183 from_int,
2184 dest_type.name,
2185 );
2186 return error.SemanticAnalysisFailed;
2187 }
2188
2189 const new_val = try target.copyVal(ira.irb.comp);
2190 new_val.setType(dest_type, ira.irb.comp);
2191 return ira.irb.buildConstValue(source_instr.scope, source_instr.span, new_val);
2192 }
2193
2194 // cast from number literal to another type
2195 // cast from number literal to *const integer
2196 //if (actual_type->id == TypeTableEntryIdComptimeFloat ||
2197 // actual_type->id == TypeTableEntryIdComptimeInt)
2198 //{
2199 // ensure_complete_type(ira->codegen, wanted_type);
2200 // if (type_is_invalid(wanted_type))
2201 // return ira->codegen->invalid_instruction;
2202 // if (wanted_type->id == TypeTableEntryIdEnum) {
2203 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.enumeration.tag_int_type, value);
2204 // if (type_is_invalid(cast1->value.type))
2205 // return ira->codegen->invalid_instruction;
2206
2207 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2208 // if (type_is_invalid(cast2->value.type))
2209 // return ira->codegen->invalid_instruction;
2210
2211 // return cast2;
2212 // } else if (wanted_type->id == TypeTableEntryIdPointer &&
2213 // wanted_type->data.pointer.is_const)
2214 // {
2215 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);
2216 // if (type_is_invalid(cast1->value.type))
2217 // return ira->codegen->invalid_instruction;
2218
2219 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2220 // if (type_is_invalid(cast2->value.type))
2221 // return ira->codegen->invalid_instruction;
2222
2223 // return cast2;
2224 // } else if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) {
2225 // CastOp op;
2226 // if ((actual_type->id == TypeTableEntryIdComptimeFloat &&
2227 // wanted_type->id == TypeTableEntryIdFloat) ||
2228 // (actual_type->id == TypeTableEntryIdComptimeInt &&
2229 // wanted_type->id == TypeTableEntryIdInt))
2230 // {
2231 // op = CastOpNumLitToConcrete;
2232 // } else if (wanted_type->id == TypeTableEntryIdInt) {
2233 // op = CastOpFloatToInt;
2234 // } else if (wanted_type->id == TypeTableEntryIdFloat) {
2235 // op = CastOpIntToFloat;
2236 // } else {
2237 // zig_unreachable();
2238 // }
2239 // return ir_resolve_cast(ira, source_instr, value, wanted_type, op, false);
2240 // } else {
2241 // return ira->codegen->invalid_instruction;
2242 // }
2243 //}
2244
2245 //// cast from typed number to integer or float literal.
2246 //// works when the number is known at compile time
2247 //if (instr_is_comptime(value) &&
2248 // ((actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdComptimeInt) ||
2249 // (actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdComptimeFloat)))
2250 //{
2251 // return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type);
2252 //}
2253
2254 //// cast from union to the enum type of the union
2255 //if (actual_type->id == TypeTableEntryIdUnion && wanted_type->id == TypeTableEntryIdEnum) {
2256 // type_ensure_zero_bits_known(ira->codegen, actual_type);
2257 // if (type_is_invalid(actual_type))
2258 // return ira->codegen->invalid_instruction;
2259
2260 // if (actual_type->data.unionation.tag_type == wanted_type) {
2261 // return ir_analyze_union_to_tag(ira, source_instr, value, wanted_type);
2262 // }
2263 //}
2264
2265 //// enum to union which has the enum as the tag type
2266 //if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum &&
2267 // (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum ||
2268 // wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
2269 //{
2270 // type_ensure_zero_bits_known(ira->codegen, wanted_type);
2271 // if (wanted_type->data.unionation.tag_type == actual_type) {
2272 // return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type);
2273 // }
2274 //}
2275
2276 //// enum to &const union which has the enum as the tag type
2277 //if (actual_type->id == TypeTableEntryIdEnum && wanted_type->id == TypeTableEntryIdPointer) {
2278 // TypeTableEntry *union_type = wanted_type->data.pointer.child_type;
2279 // if (union_type->data.unionation.decl_node->data.container_decl.auto_enum ||
2280 // union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)
2281 // {
2282 // type_ensure_zero_bits_known(ira->codegen, union_type);
2283 // if (union_type->data.unionation.tag_type == actual_type) {
2284 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, union_type, value);
2285 // if (type_is_invalid(cast1->value.type))
2286 // return ira->codegen->invalid_instruction;
2287
2288 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2289 // if (type_is_invalid(cast2->value.type))
2290 // return ira->codegen->invalid_instruction;
2291
2292 // return cast2;
2293 // }
2294 // }
2295 //}
2296
2297 //// cast from *T to *[1]T
2298 //if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle &&
2299 // actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle)
2300 //{
2301 // TypeTableEntry *array_type = wanted_type->data.pointer.child_type;
2302 // if (array_type->id == TypeTableEntryIdArray && array_type->data.array.len == 1 &&
2303 // types_match_const_cast_only(ira, array_type->data.array.child_type,
2304 // actual_type->data.pointer.child_type, source_node,
2305 // !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
2306 // {
2307 // if (wanted_type->data.pointer.alignment > actual_type->data.pointer.alignment) {
2308 // ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment"));
2309 // add_error_note(ira->codegen, msg, value->source_node,
2310 // buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name),
2311 // actual_type->data.pointer.alignment));
2312 // add_error_note(ira->codegen, msg, source_instr->source_node,
2313 // buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name),
2314 // wanted_type->data.pointer.alignment));
2315 // return ira->codegen->invalid_instruction;
2316 // }
2317 // return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type);
2318 // }
2319 //}
2320
2321 //// cast from T to *T where T is zero bits
2322 //if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle &&
2323 // types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
2324 // actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
2325 //{
2326 // type_ensure_zero_bits_known(ira->codegen, actual_type);
2327 // if (type_is_invalid(actual_type)) {
2328 // return ira->codegen->invalid_instruction;
2329 // }
2330 // if (!type_has_bits(actual_type)) {
2331 // return ir_get_ref(ira, source_instr, value, false, false);
2332 // }
2333 //}
2334
2335 //// cast from undefined to anything
2336 //if (actual_type->id == TypeTableEntryIdUndefined) {
2337 // return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type);
2338 //}
2339
2340 //// cast from something to const pointer of it
2341 //if (!type_requires_comptime(actual_type)) {
2342 // TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);
2343 // if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node, false).id == ConstCastResultIdOk) {
2344 // return ir_analyze_cast_ref(ira, source_instr, value, wanted_type);
2345 // }
2346 //}
2347
2348 try ira.addCompileError(
2349 source_instr.span,
2350 "expected type '{}', found '{}'",
2351 dest_type.name,
2352 from_type.name,
2353 );
2354 //ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node,
2355 // buf_sprintf("expected type '%s', found '%s'",
2356 // buf_ptr(&wanted_type->name),
2357 // buf_ptr(&actual_type->name)));
2358 //report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg);
2359 return error.SemanticAnalysisFailed;
2360 }
2361
2362 fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Inst) ?*Value {
2363 @panic("TODO");
9862364 }
9872365
9882366 fn getCompTimeRef(
......@@ -991,9 +2369,8 @@ const Analyze = struct {
9912369 ptr_mut: Value.Ptr.Mut,
9922370 mut: Type.Pointer.Mut,
9932371 volatility: Type.Pointer.Vol,
994 ptr_align: u32,
995 ) Analyze.Error!*Instruction {
996 @panic("TODO getCompTimeRef");
2372 ) Analyze.Error!*Inst {
2373 return error.Unimplemented;
9972374 }
9982375};
9992376
......@@ -1001,43 +2378,32 @@ pub async fn gen(
10012378 comp: *Compilation,
10022379 body_node: *ast.Node,
10032380 scope: *Scope,
1004 end_span: Span,
1005 parsed_file: *ParsedFile,
10062381) !*Code {
1007 var irb = try Builder.init(comp, parsed_file);
2382 var irb = try Builder.init(comp, scope.findRoot(), scope);
10082383 errdefer irb.abort();
10092384
10102385 const entry_block = try irb.createBasicBlock(scope, c"Entry");
1011 entry_block.ref(); // Entry block gets a reference because we enter it to begin.
2386 entry_block.ref(&irb); // Entry block gets a reference because we enter it to begin.
10122387 try irb.setCursorAtEndAndAppendBlock(entry_block);
10132388
1014 const result = try irb.genNode(body_node, scope, LVal.None);
2389 const result = try await (async irb.genNode(body_node, scope, LVal.None) catch unreachable);
10152390 if (!result.isNoReturn()) {
1016 _ = irb.buildGen(
1017 Instruction.AddImplicitReturnType,
1018 scope,
1019 end_span,
1020 Instruction.AddImplicitReturnType.Params{ .target = result },
1021 );
1022 _ = irb.buildGen(
1023 Instruction.Return,
1024 scope,
1025 end_span,
1026 Instruction.Return.Params{ .return_value = result },
1027 );
2391 // no need for save_err_ret_addr because this cannot return error
2392 _ = try irb.genAsyncReturn(scope, Span.token(body_node.lastToken()), result, true);
10282393 }
10292394
10302395 return irb.finish();
10312396}
10322397
1033pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Code, expected_type: ?*Type) !*Code {
1034 var ira = try Analyze.init(comp, parsed_file, expected_type);
1035 errdefer ira.abort();
1036
2398pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
10372399 const old_entry_bb = old_code.basic_block_list.at(0);
2400 const root_scope = old_entry_bb.scope.findRoot();
2401
2402 var ira = try Analyze.init(comp, root_scope, expected_type);
2403 errdefer ira.abort();
10382404
10392405 const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);
1040 new_entry_bb.ref();
2406 new_entry_bb.ref(&ira.irb);
10412407
10422408 ira.irb.current_basic_block = new_entry_bb;
10432409
......@@ -1051,7 +2417,8 @@ pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Co
10512417 continue;
10522418 }
10532419
1054 const return_inst = try old_instruction.analyze(&ira);
2420 const return_inst = try await (async old_instruction.analyze(&ira) catch unreachable);
2421 assert(return_inst.val != IrVal.Unknown); // at least the type should be known at this point
10552422 return_inst.linkToParent(old_instruction);
10562423 // Note: if we ever modify the above to handle error.CompileError by continuing analysis,
10572424 // then here we want to check if ira.isCompTime() and return early if true
src-self-hosted/libc_installation.zig created+462
......@@ -0,0 +1,462 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const event = std.event;
4const Target = @import("target.zig").Target;
5const c = @import("c.zig");
6
7/// See the render function implementation for documentation of the fields.
8pub const LibCInstallation = struct {
9 include_dir: []const u8,
10 lib_dir: ?[]const u8,
11 static_lib_dir: ?[]const u8,
12 msvc_lib_dir: ?[]const u8,
13 kernel32_lib_dir: ?[]const u8,
14 dynamic_linker_path: ?[]const u8,
15
16 pub const FindError = error{
17 OutOfMemory,
18 FileSystem,
19 UnableToSpawnCCompiler,
20 CCompilerExitCode,
21 CCompilerCrashed,
22 CCompilerCannotFindHeaders,
23 LibCRuntimeNotFound,
24 LibCStdLibHeaderNotFound,
25 LibCKernel32LibNotFound,
26 UnsupportedArchitecture,
27 };
28
29 pub fn parse(
30 self: *LibCInstallation,
31 allocator: *std.mem.Allocator,
32 libc_file: []const u8,
33 stderr: *std.io.OutStream(std.io.FileOutStream.Error),
34 ) !void {
35 self.initEmpty();
36
37 const keys = []const []const u8{
38 "include_dir",
39 "lib_dir",
40 "static_lib_dir",
41 "msvc_lib_dir",
42 "kernel32_lib_dir",
43 "dynamic_linker_path",
44 };
45 const FoundKey = struct {
46 found: bool,
47 allocated: ?[]u8,
48 };
49 var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** keys.len;
50 errdefer {
51 self.initEmpty();
52 for (found_keys) |found_key| {
53 if (found_key.allocated) |s| allocator.free(s);
54 }
55 }
56
57 const contents = try std.io.readFileAlloc(allocator, libc_file);
58 defer allocator.free(contents);
59
60 var it = std.mem.split(contents, "\n");
61 while (it.next()) |line| {
62 if (line.len == 0 or line[0] == '#') continue;
63 var line_it = std.mem.split(line, "=");
64 const name = line_it.next() orelse {
65 try stderr.print("missing equal sign after field name\n");
66 return error.ParseError;
67 };
68 const value = line_it.rest();
69 inline for (keys) |key, i| {
70 if (std.mem.eql(u8, name, key)) {
71 found_keys[i].found = true;
72 switch (@typeInfo(@typeOf(@field(self, key)))) {
73 builtin.TypeId.Optional => {
74 if (value.len == 0) {
75 @field(self, key) = null;
76 } else {
77 found_keys[i].allocated = try std.mem.dupe(allocator, u8, value);
78 @field(self, key) = found_keys[i].allocated;
79 }
80 },
81 else => {
82 if (value.len == 0) {
83 try stderr.print("field cannot be empty: {}\n", key);
84 return error.ParseError;
85 }
86 const dupe = try std.mem.dupe(allocator, u8, value);
87 found_keys[i].allocated = dupe;
88 @field(self, key) = dupe;
89 },
90 }
91 break;
92 }
93 }
94 }
95 for (found_keys) |found_key, i| {
96 if (!found_key.found) {
97 try stderr.print("missing field: {}\n", keys[i]);
98 return error.ParseError;
99 }
100 }
101 }
102
103 pub fn render(self: *const LibCInstallation, out: *std.io.OutStream(std.io.FileOutStream.Error)) !void {
104 @setEvalBranchQuota(4000);
105 try out.print(
106 \\# The directory that contains `stdlib.h`.
107 \\# On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null`
108 \\include_dir={}
109 \\
110 \\# The directory that contains `crt1.o`.
111 \\# On Linux, can be found with `cc -print-file-name=crt1.o`.
112 \\# Not needed when targeting MacOS.
113 \\lib_dir={}
114 \\
115 \\# The directory that contains `crtbegin.o`.
116 \\# On Linux, can be found with `cc -print-file-name=crtbegin.o`.
117 \\# Not needed when targeting MacOS or Windows.
118 \\static_lib_dir={}
119 \\
120 \\# The directory that contains `vcruntime.lib`.
121 \\# Only needed when targeting Windows.
122 \\msvc_lib_dir={}
123 \\
124 \\# The directory that contains `kernel32.lib`.
125 \\# Only needed when targeting Windows.
126 \\kernel32_lib_dir={}
127 \\
128 \\# The full path to the dynamic linker, on the target system.
129 \\# Only needed when targeting Linux.
130 \\dynamic_linker_path={}
131 \\
132 ,
133 self.include_dir,
134 self.lib_dir orelse "",
135 self.static_lib_dir orelse "",
136 self.msvc_lib_dir orelse "",
137 self.kernel32_lib_dir orelse "",
138 self.dynamic_linker_path orelse Target(Target.Native).getDynamicLinkerPath(),
139 );
140 }
141
142 /// Finds the default, native libc.
143 pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void {
144 self.initEmpty();
145 var group = event.Group(FindError!void).init(loop);
146 errdefer group.cancelAll();
147 var windows_sdk: ?*c.ZigWindowsSDK = null;
148 errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk));
149
150 switch (builtin.os) {
151 builtin.Os.windows => {
152 var sdk: *c.ZigWindowsSDK = undefined;
153 switch (c.zig_find_windows_sdk(@ptrCast(?[*]?[*]c.ZigWindowsSDK, &sdk))) {
154 c.ZigFindWindowsSdkError.None => {
155 windows_sdk = sdk;
156
157 if (sdk.msvc_lib_dir_ptr) |ptr| {
158 self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, ptr[0..sdk.msvc_lib_dir_len]);
159 }
160 try group.call(findNativeKernel32LibDir, self, loop, sdk);
161 try group.call(findNativeIncludeDirWindows, self, loop, sdk);
162 try group.call(findNativeLibDirWindows, self, loop, sdk);
163 },
164 c.ZigFindWindowsSdkError.OutOfMemory => return error.OutOfMemory,
165 c.ZigFindWindowsSdkError.NotFound => return error.NotFound,
166 c.ZigFindWindowsSdkError.PathTooLong => return error.NotFound,
167 }
168 },
169 builtin.Os.linux => {
170 try group.call(findNativeIncludeDirLinux, self, loop);
171 try group.call(findNativeLibDirLinux, self, loop);
172 try group.call(findNativeStaticLibDir, self, loop);
173 try group.call(findNativeDynamicLinker, self, loop);
174 },
175 builtin.Os.macosx => {
176 self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include");
177 },
178 else => @compileError("unimplemented: find libc for this OS"),
179 }
180 return await (async group.wait() catch unreachable);
181 }
182
183 async fn findNativeIncludeDirLinux(self: *LibCInstallation, loop: *event.Loop) !void {
184 const cc_exe = std.os.getEnvPosix("CC") orelse "cc";
185 const argv = []const []const u8{
186 cc_exe,
187 "-E",
188 "-Wp,-v",
189 "-xc",
190 "/dev/null",
191 };
192 // TODO make this use event loop
193 const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024);
194 const exec_result = if (std.debug.runtime_safety) blk: {
195 break :blk errorable_result catch unreachable;
196 } else blk: {
197 break :blk errorable_result catch |err| switch (err) {
198 error.OutOfMemory => return error.OutOfMemory,
199 else => return error.UnableToSpawnCCompiler,
200 };
201 };
202 defer {
203 loop.allocator.free(exec_result.stdout);
204 loop.allocator.free(exec_result.stderr);
205 }
206
207 switch (exec_result.term) {
208 std.os.ChildProcess.Term.Exited => |code| {
209 if (code != 0) return error.CCompilerExitCode;
210 },
211 else => {
212 return error.CCompilerCrashed;
213 },
214 }
215
216 var it = std.mem.split(exec_result.stderr, "\n\r");
217 var search_paths = std.ArrayList([]const u8).init(loop.allocator);
218 defer search_paths.deinit();
219 while (it.next()) |line| {
220 if (line.len != 0 and line[0] == ' ') {
221 try search_paths.append(line);
222 }
223 }
224 if (search_paths.len == 0) {
225 return error.CCompilerCannotFindHeaders;
226 }
227
228 // search in reverse order
229 var path_i: usize = 0;
230 while (path_i < search_paths.len) : (path_i += 1) {
231 const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1);
232 const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
233 const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h");
234 defer loop.allocator.free(stdlib_path);
235
236 if (try fileExists(loop.allocator, stdlib_path)) {
237 self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path);
238 return;
239 }
240 }
241
242 return error.LibCStdLibHeaderNotFound;
243 }
244
245 async fn findNativeIncludeDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) !void {
246 var search_buf: [2]Search = undefined;
247 const searches = fillSearch(&search_buf, sdk);
248
249 var result_buf = try std.Buffer.initSize(loop.allocator, 0);
250 defer result_buf.deinit();
251
252 for (searches) |search| {
253 result_buf.shrink(0);
254 const stream = &std.io.BufferOutStream.init(&result_buf).stream;
255 try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version);
256
257 const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h");
258 defer loop.allocator.free(stdlib_path);
259
260 if (try fileExists(loop.allocator, stdlib_path)) {
261 self.include_dir = result_buf.toOwnedSlice();
262 return;
263 }
264 }
265
266 return error.LibCStdLibHeaderNotFound;
267 }
268
269 async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
270 var search_buf: [2]Search = undefined;
271 const searches = fillSearch(&search_buf, sdk);
272
273 var result_buf = try std.Buffer.initSize(loop.allocator, 0);
274 defer result_buf.deinit();
275
276 for (searches) |search| {
277 result_buf.shrink(0);
278 const stream = &std.io.BufferOutStream.init(&result_buf).stream;
279 try stream.print("{}\\Lib\\{}\\ucrt\\", search.path, search.version);
280 switch (builtin.arch) {
281 builtin.Arch.i386 => try stream.write("x86"),
282 builtin.Arch.x86_64 => try stream.write("x64"),
283 builtin.Arch.aarch64 => try stream.write("arm"),
284 else => return error.UnsupportedArchitecture,
285 }
286 const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib");
287 defer loop.allocator.free(ucrt_lib_path);
288 if (try fileExists(loop.allocator, ucrt_lib_path)) {
289 self.lib_dir = result_buf.toOwnedSlice();
290 return;
291 }
292 }
293 return error.LibCRuntimeNotFound;
294 }
295
296 async fn findNativeLibDirLinux(self: *LibCInstallation, loop: *event.Loop) FindError!void {
297 self.lib_dir = try await (async ccPrintFileName(loop, "crt1.o", true) catch unreachable);
298 }
299
300 async fn findNativeStaticLibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void {
301 self.static_lib_dir = try await (async ccPrintFileName(loop, "crtbegin.o", true) catch unreachable);
302 }
303
304 async fn findNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop) FindError!void {
305 var dyn_tests = []DynTest{
306 DynTest{
307 .name = "ld-linux-x86-64.so.2",
308 .result = null,
309 },
310 DynTest{
311 .name = "ld-musl-x86_64.so.1",
312 .result = null,
313 },
314 };
315 var group = event.Group(FindError!void).init(loop);
316 errdefer group.cancelAll();
317 for (dyn_tests) |*dyn_test| {
318 try group.call(testNativeDynamicLinker, self, loop, dyn_test);
319 }
320 try await (async group.wait() catch unreachable);
321 for (dyn_tests) |*dyn_test| {
322 if (dyn_test.result) |result| {
323 self.dynamic_linker_path = result;
324 return;
325 }
326 }
327 }
328
329 const DynTest = struct {
330 name: []const u8,
331 result: ?[]const u8,
332 };
333
334 async fn testNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop, dyn_test: *DynTest) FindError!void {
335 if (await (async ccPrintFileName(loop, dyn_test.name, false) catch unreachable)) |result| {
336 dyn_test.result = result;
337 return;
338 } else |err| switch (err) {
339 error.LibCRuntimeNotFound => return,
340 else => return err,
341 }
342 }
343
344
345 async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
346 var search_buf: [2]Search = undefined;
347 const searches = fillSearch(&search_buf, sdk);
348
349 var result_buf = try std.Buffer.initSize(loop.allocator, 0);
350 defer result_buf.deinit();
351
352 for (searches) |search| {
353 result_buf.shrink(0);
354 const stream = &std.io.BufferOutStream.init(&result_buf).stream;
355 try stream.print("{}\\Lib\\{}\\um\\", search.path, search.version);
356 switch (builtin.arch) {
357 builtin.Arch.i386 => try stream.write("x86\\"),
358 builtin.Arch.x86_64 => try stream.write("x64\\"),
359 builtin.Arch.aarch64 => try stream.write("arm\\"),
360 else => return error.UnsupportedArchitecture,
361 }
362 const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib");
363 defer loop.allocator.free(kernel32_path);
364 if (try fileExists(loop.allocator, kernel32_path)) {
365 self.kernel32_lib_dir = result_buf.toOwnedSlice();
366 return;
367 }
368 }
369 return error.LibCKernel32LibNotFound;
370 }
371
372 fn initEmpty(self: *LibCInstallation) void {
373 self.* = LibCInstallation{
374 .include_dir = ([*]const u8)(undefined)[0..0],
375 .lib_dir = null,
376 .static_lib_dir = null,
377 .msvc_lib_dir = null,
378 .kernel32_lib_dir = null,
379 .dynamic_linker_path = null,
380 };
381 }
382};
383
384/// caller owns returned memory
385async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bool) ![]u8 {
386 const cc_exe = std.os.getEnvPosix("CC") orelse "cc";
387 const arg1 = try std.fmt.allocPrint(loop.allocator, "-print-file-name={}", o_file);
388 defer loop.allocator.free(arg1);
389 const argv = []const []const u8{ cc_exe, arg1 };
390
391 // TODO This simulates evented I/O for the child process exec
392 await (async loop.yield() catch unreachable);
393 const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024);
394 const exec_result = if (std.debug.runtime_safety) blk: {
395 break :blk errorable_result catch unreachable;
396 } else blk: {
397 break :blk errorable_result catch |err| switch (err) {
398 error.OutOfMemory => return error.OutOfMemory,
399 else => return error.UnableToSpawnCCompiler,
400 };
401 };
402 defer {
403 loop.allocator.free(exec_result.stdout);
404 loop.allocator.free(exec_result.stderr);
405 }
406 switch (exec_result.term) {
407 std.os.ChildProcess.Term.Exited => |code| {
408 if (code != 0) return error.CCompilerExitCode;
409 },
410 else => {
411 return error.CCompilerCrashed;
412 },
413 }
414 var it = std.mem.split(exec_result.stdout, "\n\r");
415 const line = it.next() orelse return error.LibCRuntimeNotFound;
416 const dirname = std.os.path.dirname(line) orelse return error.LibCRuntimeNotFound;
417
418 if (want_dirname) {
419 return std.mem.dupe(loop.allocator, u8, dirname);
420 } else {
421 return std.mem.dupe(loop.allocator, u8, line);
422 }
423}
424
425const Search = struct {
426 path: []const u8,
427 version: []const u8,
428};
429
430fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {
431 var search_end: usize = 0;
432 if (sdk.path10_ptr) |path10_ptr| {
433 if (sdk.version10_ptr) |ver10_ptr| {
434 search_buf[search_end] = Search{
435 .path = path10_ptr[0..sdk.path10_len],
436 .version = ver10_ptr[0..sdk.version10_len],
437 };
438 search_end += 1;
439 }
440 }
441 if (sdk.path81_ptr) |path81_ptr| {
442 if (sdk.version81_ptr) |ver81_ptr| {
443 search_buf[search_end] = Search{
444 .path = path81_ptr[0..sdk.path81_len],
445 .version = ver81_ptr[0..sdk.version81_len],
446 };
447 search_end += 1;
448 }
449 }
450 return search_buf[0..search_end];
451}
452
453
454fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool {
455 if (std.os.File.access(allocator, path)) |_| {
456 return true;
457 } else |err| switch (err) {
458 error.NotFound, error.PermissionDenied => return false,
459 error.OutOfMemory => return error.OutOfMemory,
460 else => return error.FileSystem,
461 }
462}
src-self-hosted/link.zig created+724
......@@ -0,0 +1,724 @@
1const std = @import("std");
2const mem = std.mem;
3const c = @import("c.zig");
4const builtin = @import("builtin");
5const ObjectFormat = builtin.ObjectFormat;
6const Compilation = @import("compilation.zig").Compilation;
7const Target = @import("target.zig").Target;
8const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
9const assert = std.debug.assert;
10
11const Context = struct {
12 comp: *Compilation,
13 arena: std.heap.ArenaAllocator,
14 args: std.ArrayList([*]const u8),
15 link_in_crt: bool,
16
17 link_err: error{OutOfMemory}!void,
18 link_msg: std.Buffer,
19
20 libc: *LibCInstallation,
21 out_file_path: std.Buffer,
22};
23
24pub async fn link(comp: *Compilation) !void {
25 var ctx = Context{
26 .comp = comp,
27 .arena = std.heap.ArenaAllocator.init(comp.gpa()),
28 .args = undefined,
29 .link_in_crt = comp.haveLibC() and comp.kind == Compilation.Kind.Exe,
30 .link_err = {},
31 .link_msg = undefined,
32 .libc = undefined,
33 .out_file_path = undefined,
34 };
35 defer ctx.arena.deinit();
36 ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator);
37 ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator);
38
39 if (comp.link_out_file) |out_file| {
40 ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, out_file);
41 } else {
42 ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, comp.name.toSliceConst());
43 switch (comp.kind) {
44 Compilation.Kind.Exe => {
45 try ctx.out_file_path.append(comp.target.exeFileExt());
46 },
47 Compilation.Kind.Lib => {
48 try ctx.out_file_path.append(comp.target.libFileExt(comp.is_static));
49 },
50 Compilation.Kind.Obj => {
51 try ctx.out_file_path.append(comp.target.objFileExt());
52 },
53 }
54 }
55
56 // even though we're calling LLD as a library it thinks the first
57 // argument is its own exe name
58 try ctx.args.append(c"lld");
59
60 if (comp.haveLibC()) {
61 ctx.libc = ctx.comp.override_libc orelse blk: {
62 switch (comp.target) {
63 Target.Native => {
64 break :blk (await (async comp.event_loop_local.getNativeLibC() catch unreachable)) catch return error.LibCRequiredButNotProvidedOrFound;
65 },
66 else => return error.LibCRequiredButNotProvidedOrFound,
67 }
68 };
69 }
70
71 try constructLinkerArgs(&ctx);
72
73 if (comp.verbose_link) {
74 for (ctx.args.toSliceConst()) |arg, i| {
75 const space = if (i == 0) "" else " ";
76 std.debug.warn("{}{s}", space, arg);
77 }
78 std.debug.warn("\n");
79 }
80
81 const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat());
82 const args_slice = ctx.args.toSlice();
83 // Not evented I/O. LLD does its own multithreading internally.
84 if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) {
85 if (!ctx.link_msg.isNull()) {
86 // TODO capture these messages and pass them through the system, reporting them through the
87 // event system instead of printing them directly here.
88 // perhaps try to parse and understand them.
89 std.debug.warn("{}\n", ctx.link_msg.toSliceConst());
90 }
91 return error.LinkFailed;
92 }
93}
94
95extern fn ZigLLDLink(
96 oformat: c.ZigLLVM_ObjectFormatType,
97 args: [*]const [*]const u8,
98 arg_count: usize,
99 append_diagnostic: extern fn (*c_void, [*]const u8, usize) void,
100 context: *c_void,
101) bool;
102
103extern fn linkDiagCallback(context: *c_void, ptr: [*]const u8, len: usize) void {
104 const ctx = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
105 ctx.link_err = linkDiagCallbackErrorable(ctx, ptr[0..len]);
106}
107
108fn linkDiagCallbackErrorable(ctx: *Context, msg: []const u8) !void {
109 if (ctx.link_msg.isNull()) {
110 try ctx.link_msg.resize(0);
111 }
112 try ctx.link_msg.append(msg);
113}
114
115fn toExternObjectFormatType(ofmt: ObjectFormat) c.ZigLLVM_ObjectFormatType {
116 return switch (ofmt) {
117 ObjectFormat.unknown => c.ZigLLVM_UnknownObjectFormat,
118 ObjectFormat.coff => c.ZigLLVM_COFF,
119 ObjectFormat.elf => c.ZigLLVM_ELF,
120 ObjectFormat.macho => c.ZigLLVM_MachO,
121 ObjectFormat.wasm => c.ZigLLVM_Wasm,
122 };
123}
124
125fn constructLinkerArgs(ctx: *Context) !void {
126 switch (ctx.comp.target.getObjectFormat()) {
127 ObjectFormat.unknown => unreachable,
128 ObjectFormat.coff => return constructLinkerArgsCoff(ctx),
129 ObjectFormat.elf => return constructLinkerArgsElf(ctx),
130 ObjectFormat.macho => return constructLinkerArgsMachO(ctx),
131 ObjectFormat.wasm => return constructLinkerArgsWasm(ctx),
132 }
133}
134
135fn constructLinkerArgsElf(ctx: *Context) !void {
136 // TODO commented out code in this function
137 //if (g->linker_script) {
138 // lj->args.append("-T");
139 // lj->args.append(g->linker_script);
140 //}
141
142 //if (g->no_rosegment_workaround) {
143 // lj->args.append("--no-rosegment");
144 //}
145 try ctx.args.append(c"--gc-sections");
146
147 //lj->args.append("-m");
148 //lj->args.append(getLDMOption(&g->zig_target));
149
150 //bool is_lib = g->out_type == OutTypeLib;
151 //bool shared = !g->is_static && is_lib;
152 //Buf *soname = nullptr;
153 if (ctx.comp.is_static) {
154 if (ctx.comp.target.isArmOrThumb()) {
155 try ctx.args.append(c"-Bstatic");
156 } else {
157 try ctx.args.append(c"-static");
158 }
159 }
160 //} else if (shared) {
161 // lj->args.append("-shared");
162
163 // if (buf_len(&lj->out_file) == 0) {
164 // buf_appendf(&lj->out_file, "lib%s.so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize "",
165 // buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
166 // }
167 // soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major);
168 //}
169
170 try ctx.args.append(c"-o");
171 try ctx.args.append(ctx.out_file_path.ptr());
172
173 if (ctx.link_in_crt) {
174 const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o";
175 const crtbegino = if (ctx.comp.is_static) "crtbeginT.o" else "crtbegin.o";
176 try addPathJoin(ctx, ctx.libc.lib_dir.?, crt1o);
177 try addPathJoin(ctx, ctx.libc.lib_dir.?, "crti.o");
178 try addPathJoin(ctx, ctx.libc.static_lib_dir.?, crtbegino);
179 }
180
181 //for (size_t i = 0; i < g->rpath_list.length; i += 1) {
182 // Buf *rpath = g->rpath_list.at(i);
183 // add_rpath(lj, rpath);
184 //}
185 //if (g->each_lib_rpath) {
186 // for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
187 // const char *lib_dir = g->lib_dirs.at(i);
188 // for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
189 // LinkLib *link_lib = g->link_libs_list.at(i);
190 // if (buf_eql_str(link_lib->name, "c")) {
191 // continue;
192 // }
193 // bool does_exist;
194 // Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib->name));
195 // if (os_file_exists(test_path, &does_exist) != ErrorNone) {
196 // zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path));
197 // }
198 // if (does_exist) {
199 // add_rpath(lj, buf_create_from_str(lib_dir));
200 // break;
201 // }
202 // }
203 // }
204 //}
205
206 //for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
207 // const char *lib_dir = g->lib_dirs.at(i);
208 // lj->args.append("-L");
209 // lj->args.append(lib_dir);
210 //}
211
212 if (ctx.comp.haveLibC()) {
213 try ctx.args.append(c"-L");
214 try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr);
215
216 try ctx.args.append(c"-L");
217 try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr);
218
219 if (!ctx.comp.is_static) {
220 const dl = blk: {
221 if (ctx.libc.dynamic_linker_path) |dl| break :blk dl;
222 if (ctx.comp.target.getDynamicLinkerPath()) |dl| break :blk dl;
223 return error.LibCMissingDynamicLinker;
224 };
225 try ctx.args.append(c"-dynamic-linker");
226 try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr);
227 }
228 }
229
230 //if (shared) {
231 // lj->args.append("-soname");
232 // lj->args.append(buf_ptr(soname));
233 //}
234
235 // .o files
236 for (ctx.comp.link_objects) |link_object| {
237 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
238 try ctx.args.append(link_obj_with_null.ptr);
239 }
240 try addFnObjects(ctx);
241
242 //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
243 // if (g->libc_link_lib == nullptr) {
244 // Buf *builtin_o_path = build_o(g, "builtin");
245 // lj->args.append(buf_ptr(builtin_o_path));
246 // }
247
248 // // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage
249 // Buf *compiler_rt_o_path = build_compiler_rt(g);
250 // lj->args.append(buf_ptr(compiler_rt_o_path));
251 //}
252
253 //for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
254 // LinkLib *link_lib = g->link_libs_list.at(i);
255 // if (buf_eql_str(link_lib->name, "c")) {
256 // continue;
257 // }
258 // Buf *arg;
259 // if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") ||
260 // buf_ends_with_str(link_lib->name, ".so"))
261 // {
262 // arg = link_lib->name;
263 // } else {
264 // arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
265 // }
266 // lj->args.append(buf_ptr(arg));
267 //}
268
269 // libc dep
270 if (ctx.comp.haveLibC()) {
271 if (ctx.comp.is_static) {
272 try ctx.args.append(c"--start-group");
273 try ctx.args.append(c"-lgcc");
274 try ctx.args.append(c"-lgcc_eh");
275 try ctx.args.append(c"-lc");
276 try ctx.args.append(c"-lm");
277 try ctx.args.append(c"--end-group");
278 } else {
279 try ctx.args.append(c"-lgcc");
280 try ctx.args.append(c"--as-needed");
281 try ctx.args.append(c"-lgcc_s");
282 try ctx.args.append(c"--no-as-needed");
283 try ctx.args.append(c"-lc");
284 try ctx.args.append(c"-lm");
285 try ctx.args.append(c"-lgcc");
286 try ctx.args.append(c"--as-needed");
287 try ctx.args.append(c"-lgcc_s");
288 try ctx.args.append(c"--no-as-needed");
289 }
290 }
291
292 // crt end
293 if (ctx.link_in_crt) {
294 try addPathJoin(ctx, ctx.libc.static_lib_dir.?, "crtend.o");
295 try addPathJoin(ctx, ctx.libc.lib_dir.?, "crtn.o");
296 }
297
298 if (ctx.comp.target != Target.Native) {
299 try ctx.args.append(c"--allow-shlib-undefined");
300 }
301
302 if (ctx.comp.target.getOs() == builtin.Os.zen) {
303 try ctx.args.append(c"-e");
304 try ctx.args.append(c"_start");
305
306 try ctx.args.append(c"--image-base=0x10000000");
307 }
308}
309
310fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {
311 const full_path = try std.os.path.join(&ctx.arena.allocator, dirname, basename);
312 const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);
313 try ctx.args.append(full_path_with_null.ptr);
314}
315
316fn constructLinkerArgsCoff(ctx: *Context) !void {
317 try ctx.args.append(c"-NOLOGO");
318
319 if (!ctx.comp.strip) {
320 try ctx.args.append(c"-DEBUG");
321 }
322
323 switch (ctx.comp.target.getArch()) {
324 builtin.Arch.i386 => try ctx.args.append(c"-MACHINE:X86"),
325 builtin.Arch.x86_64 => try ctx.args.append(c"-MACHINE:X64"),
326 builtin.Arch.aarch64 => try ctx.args.append(c"-MACHINE:ARM"),
327 else => return error.UnsupportedLinkArchitecture,
328 }
329
330 if (ctx.comp.windows_subsystem_windows) {
331 try ctx.args.append(c"/SUBSYSTEM:windows");
332 } else if (ctx.comp.windows_subsystem_console) {
333 try ctx.args.append(c"/SUBSYSTEM:console");
334 }
335
336 const is_library = ctx.comp.kind == Compilation.Kind.Lib;
337
338 const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", ctx.out_file_path.toSliceConst());
339 try ctx.args.append(out_arg.ptr);
340
341 if (ctx.comp.haveLibC()) {
342 try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr);
343 try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr);
344 try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr);
345 }
346
347 if (ctx.link_in_crt) {
348 const lib_str = if (ctx.comp.is_static) "lib" else "";
349 const d_str = if (ctx.comp.build_mode == builtin.Mode.Debug) "d" else "";
350
351 if (ctx.comp.is_static) {
352 const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", d_str);
353 try ctx.args.append(cmt_lib_name.ptr);
354 } else {
355 const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", d_str);
356 try ctx.args.append(msvcrt_lib_name.ptr);
357 }
358
359 const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", lib_str, d_str);
360 try ctx.args.append(vcruntime_lib_name.ptr);
361
362 const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", lib_str, d_str);
363 try ctx.args.append(crt_lib_name.ptr);
364
365 // Visual C++ 2015 Conformance Changes
366 // https://msdn.microsoft.com/en-us/library/bb531344.aspx
367 try ctx.args.append(c"legacy_stdio_definitions.lib");
368
369 // msvcrt depends on kernel32
370 try ctx.args.append(c"kernel32.lib");
371 } else {
372 try ctx.args.append(c"-NODEFAULTLIB");
373 if (!is_library) {
374 try ctx.args.append(c"-ENTRY:WinMainCRTStartup");
375 // TODO
376 //if (g->have_winmain) {
377 // lj->args.append("-ENTRY:WinMain");
378 //} else {
379 // lj->args.append("-ENTRY:WinMainCRTStartup");
380 //}
381 }
382 }
383
384 if (is_library and !ctx.comp.is_static) {
385 try ctx.args.append(c"-DLL");
386 }
387
388 //for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
389 // const char *lib_dir = g->lib_dirs.at(i);
390 // lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", lib_dir)));
391 //}
392
393 for (ctx.comp.link_objects) |link_object| {
394 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
395 try ctx.args.append(link_obj_with_null.ptr);
396 }
397 try addFnObjects(ctx);
398
399 switch (ctx.comp.kind) {
400 Compilation.Kind.Exe, Compilation.Kind.Lib => {
401 if (!ctx.comp.haveLibC()) {
402 @panic("TODO");
403 //Buf *builtin_o_path = build_o(g, "builtin");
404 //lj->args.append(buf_ptr(builtin_o_path));
405 }
406
407 // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage
408 // TODO
409 //Buf *compiler_rt_o_path = build_compiler_rt(g);
410 //lj->args.append(buf_ptr(compiler_rt_o_path));
411 },
412 Compilation.Kind.Obj => {},
413 }
414
415 //Buf *def_contents = buf_alloc();
416 //ZigList<const char *> gen_lib_args = {0};
417 //for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) {
418 // LinkLib *link_lib = g->link_libs_list.at(lib_i);
419 // if (buf_eql_str(link_lib->name, "c")) {
420 // continue;
421 // }
422 // if (link_lib->provided_explicitly) {
423 // if (lj->codegen->zig_target.env_type == ZigLLVM_GNU) {
424 // Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
425 // lj->args.append(buf_ptr(arg));
426 // }
427 // else {
428 // lj->args.append(buf_ptr(link_lib->name));
429 // }
430 // } else {
431 // buf_resize(def_contents, 0);
432 // buf_appendf(def_contents, "LIBRARY %s\nEXPORTS\n", buf_ptr(link_lib->name));
433 // for (size_t exp_i = 0; exp_i < link_lib->symbols.length; exp_i += 1) {
434 // Buf *symbol_name = link_lib->symbols.at(exp_i);
435 // buf_appendf(def_contents, "%s\n", buf_ptr(symbol_name));
436 // }
437 // buf_appendf(def_contents, "\n");
438
439 // Buf *def_path = buf_alloc();
440 // os_path_join(g->cache_dir, buf_sprintf("%s.def", buf_ptr(link_lib->name)), def_path);
441 // os_write_file(def_path, def_contents);
442
443 // Buf *generated_lib_path = buf_alloc();
444 // os_path_join(g->cache_dir, buf_sprintf("%s.lib", buf_ptr(link_lib->name)), generated_lib_path);
445
446 // gen_lib_args.resize(0);
447 // gen_lib_args.append("link");
448
449 // coff_append_machine_arg(g, &gen_lib_args);
450 // gen_lib_args.append(buf_ptr(buf_sprintf("-DEF:%s", buf_ptr(def_path))));
451 // gen_lib_args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(generated_lib_path))));
452 // Buf diag = BUF_INIT;
453 // if (!zig_lld_link(g->zig_target.oformat, gen_lib_args.items, gen_lib_args.length, &diag)) {
454 // fprintf(stderr, "%s\n", buf_ptr(&diag));
455 // exit(1);
456 // }
457 // lj->args.append(buf_ptr(generated_lib_path));
458 // }
459 //}
460}
461
462fn constructLinkerArgsMachO(ctx: *Context) !void {
463 try ctx.args.append(c"-demangle");
464
465 if (ctx.comp.linker_rdynamic) {
466 try ctx.args.append(c"-export_dynamic");
467 }
468
469 const is_lib = ctx.comp.kind == Compilation.Kind.Lib;
470 const shared = !ctx.comp.is_static and is_lib;
471 if (ctx.comp.is_static) {
472 try ctx.args.append(c"-static");
473 } else {
474 try ctx.args.append(c"-dynamic");
475 }
476
477 //if (is_lib) {
478 // if (!g->is_static) {
479 // lj->args.append("-dylib");
480
481 // Buf *compat_vers = buf_sprintf("%" ZIG_PRI_usize ".0.0", g->version_major);
482 // lj->args.append("-compatibility_version");
483 // lj->args.append(buf_ptr(compat_vers));
484
485 // Buf *cur_vers = buf_sprintf("%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize,
486 // g->version_major, g->version_minor, g->version_patch);
487 // lj->args.append("-current_version");
488 // lj->args.append(buf_ptr(cur_vers));
489
490 // // TODO getting an error when running an executable when doing this rpath thing
491 // //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib",
492 // // buf_ptr(g->root_out_name), g->version_major);
493 // //lj->args.append("-install_name");
494 // //lj->args.append(buf_ptr(dylib_install_name));
495
496 // if (buf_len(&lj->out_file) == 0) {
497 // buf_appendf(&lj->out_file, "lib%s.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib",
498 // buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
499 // }
500 // }
501 //}
502
503 try ctx.args.append(c"-arch");
504 const darwin_arch_str = try std.cstr.addNullByte(
505 &ctx.arena.allocator,
506 ctx.comp.target.getDarwinArchString(),
507 );
508 try ctx.args.append(darwin_arch_str.ptr);
509
510 const platform = try DarwinPlatform.get(ctx.comp);
511 switch (platform.kind) {
512 DarwinPlatform.Kind.MacOS => try ctx.args.append(c"-macosx_version_min"),
513 DarwinPlatform.Kind.IPhoneOS => try ctx.args.append(c"-iphoneos_version_min"),
514 DarwinPlatform.Kind.IPhoneOSSimulator => try ctx.args.append(c"-ios_simulator_version_min"),
515 }
516 const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", platform.major, platform.minor, platform.micro);
517 try ctx.args.append(ver_str.ptr);
518
519 if (ctx.comp.kind == Compilation.Kind.Exe) {
520 if (ctx.comp.is_static) {
521 try ctx.args.append(c"-no_pie");
522 } else {
523 try ctx.args.append(c"-pie");
524 }
525 }
526
527 try ctx.args.append(c"-o");
528 try ctx.args.append(ctx.out_file_path.ptr());
529
530 //for (size_t i = 0; i < g->rpath_list.length; i += 1) {
531 // Buf *rpath = g->rpath_list.at(i);
532 // add_rpath(lj, rpath);
533 //}
534 //add_rpath(lj, &lj->out_file);
535
536 if (shared) {
537 try ctx.args.append(c"-headerpad_max_install_names");
538 } else if (ctx.comp.is_static) {
539 try ctx.args.append(c"-lcrt0.o");
540 } else {
541 switch (platform.kind) {
542 DarwinPlatform.Kind.MacOS => {
543 if (platform.versionLessThan(10, 5)) {
544 try ctx.args.append(c"-lcrt1.o");
545 } else if (platform.versionLessThan(10, 6)) {
546 try ctx.args.append(c"-lcrt1.10.5.o");
547 } else if (platform.versionLessThan(10, 8)) {
548 try ctx.args.append(c"-lcrt1.10.6.o");
549 }
550 },
551 DarwinPlatform.Kind.IPhoneOS => {
552 if (ctx.comp.target.getArch() == builtin.Arch.aarch64) {
553 // iOS does not need any crt1 files for arm64
554 } else if (platform.versionLessThan(3, 1)) {
555 try ctx.args.append(c"-lcrt1.o");
556 } else if (platform.versionLessThan(6, 0)) {
557 try ctx.args.append(c"-lcrt1.3.1.o");
558 }
559 },
560 DarwinPlatform.Kind.IPhoneOSSimulator => {}, // no crt1.o needed
561 }
562 }
563
564 //for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
565 // const char *lib_dir = g->lib_dirs.at(i);
566 // lj->args.append("-L");
567 // lj->args.append(lib_dir);
568 //}
569
570 for (ctx.comp.link_objects) |link_object| {
571 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
572 try ctx.args.append(link_obj_with_null.ptr);
573 }
574 try addFnObjects(ctx);
575
576 //// compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce
577 //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
578 // Buf *compiler_rt_o_path = build_compiler_rt(g);
579 // lj->args.append(buf_ptr(compiler_rt_o_path));
580 //}
581
582 if (ctx.comp.target == Target.Native) {
583 for (ctx.comp.link_libs_list.toSliceConst()) |lib| {
584 if (mem.eql(u8, lib.name, "c")) {
585 // on Darwin, libSystem has libc in it, but also you have to use it
586 // to make syscalls because the syscall numbers are not documented
587 // and change between versions.
588 // so we always link against libSystem
589 try ctx.args.append(c"-lSystem");
590 } else {
591 if (mem.indexOfScalar(u8, lib.name, '/') == null) {
592 const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", lib.name);
593 try ctx.args.append(arg.ptr);
594 } else {
595 const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name);
596 try ctx.args.append(arg.ptr);
597 }
598 }
599 }
600 } else {
601 try ctx.args.append(c"-undefined");
602 try ctx.args.append(c"dynamic_lookup");
603 }
604
605 if (platform.kind == DarwinPlatform.Kind.MacOS) {
606 if (platform.versionLessThan(10, 5)) {
607 try ctx.args.append(c"-lgcc_s.10.4");
608 } else if (platform.versionLessThan(10, 6)) {
609 try ctx.args.append(c"-lgcc_s.10.5");
610 }
611 } else {
612 @panic("TODO");
613 }
614
615 //for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) {
616 // lj->args.append("-framework");
617 // lj->args.append(buf_ptr(g->darwin_frameworks.at(i)));
618 //}
619}
620
621fn constructLinkerArgsWasm(ctx: *Context) void {
622 @panic("TODO");
623}
624
625fn addFnObjects(ctx: *Context) !void {
626 // at this point it's guaranteed nobody else has this lock, so we circumvent it
627 // and avoid having to be a coroutine
628 const fn_link_set = &ctx.comp.fn_link_set.private_data;
629
630 var it = fn_link_set.first;
631 while (it) |node| {
632 const fn_val = node.data orelse {
633 // handle the tombstone. See Value.Fn.destroy.
634 it = node.next;
635 fn_link_set.remove(node);
636 ctx.comp.gpa().destroy(node);
637 continue;
638 };
639 try ctx.args.append(fn_val.containing_object.ptr());
640 it = node.next;
641 }
642}
643
644const DarwinPlatform = struct {
645 kind: Kind,
646 major: u32,
647 minor: u32,
648 micro: u32,
649
650 const Kind = enum {
651 MacOS,
652 IPhoneOS,
653 IPhoneOSSimulator,
654 };
655
656 fn get(comp: *Compilation) !DarwinPlatform {
657 var result: DarwinPlatform = undefined;
658 const ver_str = switch (comp.darwin_version_min) {
659 Compilation.DarwinVersionMin.MacOS => |ver| blk: {
660 result.kind = Kind.MacOS;
661 break :blk ver;
662 },
663 Compilation.DarwinVersionMin.Ios => |ver| blk: {
664 result.kind = Kind.IPhoneOS;
665 break :blk ver;
666 },
667 Compilation.DarwinVersionMin.None => blk: {
668 assert(comp.target.getOs() == builtin.Os.macosx);
669 result.kind = Kind.MacOS;
670 break :blk "10.10";
671 },
672 };
673
674 var had_extra: bool = undefined;
675 try darwinGetReleaseVersion(ver_str, &result.major, &result.minor, &result.micro, &had_extra,);
676 if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) {
677 return error.InvalidDarwinVersionString;
678 }
679
680 if (result.kind == Kind.IPhoneOS) {
681 switch (comp.target.getArch()) {
682 builtin.Arch.i386,
683 builtin.Arch.x86_64,
684 => result.kind = Kind.IPhoneOSSimulator,
685 else => {},
686 }
687 }
688 return result;
689 }
690
691 fn versionLessThan(self: DarwinPlatform, major: u32, minor: u32) bool {
692 if (self.major < major)
693 return true;
694 if (self.major > major)
695 return false;
696 if (self.minor < minor)
697 return true;
698 return false;
699 }
700};
701
702/// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the
703/// grouped values as integers. Numbers which are not provided are set to 0.
704/// return true if the entire string was parsed (9.2), or all groups were
705/// parsed (10.3.5extrastuff).
706fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u32, had_extra: *bool) !void {
707 major.* = 0;
708 minor.* = 0;
709 micro.* = 0;
710 had_extra.* = false;
711
712 if (str.len == 0)
713 return error.InvalidDarwinVersionString;
714
715 var start_pos: usize = 0;
716 for ([]*u32{major, minor, micro}) |v| {
717 const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.');
718 const end_pos = dot_pos orelse str.len;
719 v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString;
720 start_pos = (dot_pos orelse return) + 1;
721 if (start_pos == str.len) return;
722 }
723 had_extra.* = true;
724}
src-self-hosted/llvm.zig+112-3
......@@ -2,6 +2,12 @@ const builtin = @import("builtin");
22const c = @import("c.zig");
33const assert = @import("std").debug.assert;
44
5// we wrap the c module for 3 reasons:
6// 1. to avoid accidentally calling the non-thread-safe functions
7// 2. patch up some of the types to remove nullability
8// 3. some functions have been augmented by zig_llvm.cpp to be more powerful,
9// such as ZigLLVMTargetMachineEmitToFile
10
511pub const AttributeIndex = c_uint;
612pub const Bool = c_int;
713
......@@ -12,25 +18,59 @@ pub const ValueRef = removeNullability(c.LLVMValueRef);
1218pub const TypeRef = removeNullability(c.LLVMTypeRef);
1319pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef);
1420pub const AttributeRef = removeNullability(c.LLVMAttributeRef);
21pub const TargetRef = removeNullability(c.LLVMTargetRef);
22pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef);
23pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef);
24pub const DIBuilder = c.ZigLLVMDIBuilder;
1525
26pub const ABIAlignmentOfType = c.LLVMABIAlignmentOfType;
1627pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex;
1728pub const AddFunction = c.LLVMAddFunction;
29pub const AddGlobal = c.LLVMAddGlobal;
30pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;
31pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;
32pub const ArrayType = c.LLVMArrayType;
1833pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;
34pub const ConstAllOnes = c.LLVMConstAllOnes;
35pub const ConstArray = c.LLVMConstArray;
36pub const ConstBitCast = c.LLVMConstBitCast;
1937pub const ConstInt = c.LLVMConstInt;
38pub const ConstIntOfArbitraryPrecision = c.LLVMConstIntOfArbitraryPrecision;
39pub const ConstNeg = c.LLVMConstNeg;
40pub const ConstNull = c.LLVMConstNull;
2041pub const ConstStringInContext = c.LLVMConstStringInContext;
2142pub const ConstStructInContext = c.LLVMConstStructInContext;
43pub const CopyStringRepOfTargetData = c.LLVMCopyStringRepOfTargetData;
2244pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext;
45pub const CreateCompileUnit = c.ZigLLVMCreateCompileUnit;
46pub const CreateDIBuilder = c.ZigLLVMCreateDIBuilder;
2347pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute;
48pub const CreateFile = c.ZigLLVMCreateFile;
2449pub const CreateStringAttribute = c.LLVMCreateStringAttribute;
50pub const CreateTargetDataLayout = c.LLVMCreateTargetDataLayout;
51pub const CreateTargetMachine = c.LLVMCreateTargetMachine;
52pub const DIBuilderFinalize = c.ZigLLVMDIBuilderFinalize;
2553pub const DisposeBuilder = c.LLVMDisposeBuilder;
54pub const DisposeDIBuilder = c.ZigLLVMDisposeDIBuilder;
55pub const DisposeMessage = c.LLVMDisposeMessage;
2656pub const DisposeModule = c.LLVMDisposeModule;
57pub const DisposeTargetData = c.LLVMDisposeTargetData;
58pub const DisposeTargetMachine = c.LLVMDisposeTargetMachine;
2759pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext;
2860pub const DumpModule = c.LLVMDumpModule;
2961pub const FP128TypeInContext = c.LLVMFP128TypeInContext;
3062pub const FloatTypeInContext = c.LLVMFloatTypeInContext;
3163pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName;
64pub const GetHostCPUName = c.ZigLLVMGetHostCPUName;
3265pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext;
66pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures;
67pub const GetUndef = c.LLVMGetUndef;
3368pub const HalfTypeInContext = c.LLVMHalfTypeInContext;
69pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers;
70pub const InitializeAllAsmPrinters = c.LLVMInitializeAllAsmPrinters;
71pub const InitializeAllTargetInfos = c.LLVMInitializeAllTargetInfos;
72pub const InitializeAllTargetMCs = c.LLVMInitializeAllTargetMCs;
73pub const InitializeAllTargets = c.LLVMInitializeAllTargets;
3474pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext;
3575pub const Int128TypeInContext = c.LLVMInt128TypeInContext;
3676pub const Int16TypeInContext = c.LLVMInt16TypeInContext;
......@@ -47,13 +87,26 @@ pub const MDStringInContext = c.LLVMMDStringInContext;
4787pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext;
4888pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext;
4989pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext;
90pub const PointerType = c.LLVMPointerType;
91pub const SetAlignment = c.LLVMSetAlignment;
92pub const SetDataLayout = c.LLVMSetDataLayout;
93pub const SetGlobalConstant = c.LLVMSetGlobalConstant;
94pub const SetInitializer = c.LLVMSetInitializer;
95pub const SetLinkage = c.LLVMSetLinkage;
96pub const SetTarget = c.LLVMSetTarget;
97pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr;
5098pub const StructTypeInContext = c.LLVMStructTypeInContext;
5199pub const TokenTypeInContext = c.LLVMTokenTypeInContext;
100pub const TypeOf = c.LLVMTypeOf;
52101pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
53102pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;
54103pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;
55pub const ConstAllOnes = c.LLVMConstAllOnes;
56pub const ConstNull = c.LLVMConstNull;
104
105pub const ConstInBoundsGEP = LLVMConstInBoundsGEP;
106pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef;
107
108pub const GetTargetFromTriple = LLVMGetTargetFromTriple;
109extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool;
57110
58111pub const VerifyModule = LLVMVerifyModule;
59112extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool;
......@@ -83,10 +136,66 @@ pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction;
83136pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction;
84137pub const VerifierFailureAction = c.LLVMVerifierFailureAction;
85138
139pub const CodeGenLevelNone = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelNone;
140pub const CodeGenLevelLess = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelLess;
141pub const CodeGenLevelDefault = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelDefault;
142pub const CodeGenLevelAggressive = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelAggressive;
143pub const CodeGenOptLevel = c.LLVMCodeGenOptLevel;
144
145pub const RelocDefault = c.LLVMRelocMode.LLVMRelocDefault;
146pub const RelocStatic = c.LLVMRelocMode.LLVMRelocStatic;
147pub const RelocPIC = c.LLVMRelocMode.LLVMRelocPIC;
148pub const RelocDynamicNoPic = c.LLVMRelocMode.LLVMRelocDynamicNoPic;
149pub const RelocMode = c.LLVMRelocMode;
150
151pub const CodeModelDefault = c.LLVMCodeModel.LLVMCodeModelDefault;
152pub const CodeModelJITDefault = c.LLVMCodeModel.LLVMCodeModelJITDefault;
153pub const CodeModelSmall = c.LLVMCodeModel.LLVMCodeModelSmall;
154pub const CodeModelKernel = c.LLVMCodeModel.LLVMCodeModelKernel;
155pub const CodeModelMedium = c.LLVMCodeModel.LLVMCodeModelMedium;
156pub const CodeModelLarge = c.LLVMCodeModel.LLVMCodeModelLarge;
157pub const CodeModel = c.LLVMCodeModel;
158
159pub const EmitAssembly = EmitOutputType.ZigLLVM_EmitAssembly;
160pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary;
161pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr;
162pub const EmitOutputType = c.ZigLLVM_EmitOutputType;
163
164pub const CCallConv = c.LLVMCCallConv;
165pub const FastCallConv = c.LLVMFastCallConv;
166pub const ColdCallConv = c.LLVMColdCallConv;
167pub const WebKitJSCallConv = c.LLVMWebKitJSCallConv;
168pub const AnyRegCallConv = c.LLVMAnyRegCallConv;
169pub const X86StdcallCallConv = c.LLVMX86StdcallCallConv;
170pub const X86FastcallCallConv = c.LLVMX86FastcallCallConv;
171pub const CallConv = c.LLVMCallConv;
172
173pub const FnInline = extern enum {
174 Auto,
175 Always,
176 Never,
177};
178
86179fn removeNullability(comptime T: type) type {
87180 comptime assert(@typeId(T) == builtin.TypeId.Optional);
88181 return T.Child;
89182}
90183
91184pub const BuildRet = LLVMBuildRet;
92extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ValueRef;
185extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ?ValueRef;
186
187pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile;
188extern fn ZigLLVMTargetMachineEmitToFile(
189 targ_machine_ref: TargetMachineRef,
190 module_ref: ModuleRef,
191 filename: [*]const u8,
192 output_type: EmitOutputType,
193 error_message: *[*]u8,
194 is_debug: bool,
195 is_small: bool,
196) bool;
197
198pub const BuildCall = ZigLLVMBuildCall;
199extern fn ZigLLVMBuildCall(B: BuilderRef, Fn: ValueRef, Args: [*]ValueRef, NumArgs: c_uint, CC: c_uint, fn_inline: FnInline, Name: [*]const u8) ?ValueRef;
200
201pub const PrivateLinkage = c.LLVMLinkage.LLVMPrivateLinkage;
src-self-hosted/main.zig+87-60
......@@ -18,6 +18,7 @@ const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
1818const Compilation = @import("compilation.zig").Compilation;
1919const Target = @import("target.zig").Target;
2020const errmsg = @import("errmsg.zig");
21const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
2122
2223var stderr_file: os.File = undefined;
2324var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;
......@@ -28,13 +29,14 @@ const usage =
2829 \\
2930 \\Commands:
3031 \\
31 \\ build-exe [source] Create executable from source or object files
32 \\ build-lib [source] Create library from source or object files
33 \\ build-obj [source] Create object from source or assembly
34 \\ fmt [source] Parse file and render in canonical zig format
35 \\ targets List available compilation targets
36 \\ version Print version number and exit
37 \\ zen Print zen of zig and exit
32 \\ build-exe [source] Create executable from source or object files
33 \\ build-lib [source] Create library from source or object files
34 \\ build-obj [source] Create object from source or assembly
35 \\ fmt [source] Parse file and render in canonical zig format
36 \\ libc [paths_file] Display native libc paths file or validate one
37 \\ targets List available compilation targets
38 \\ version Print version number and exit
39 \\ zen Print zen of zig and exit
3840 \\
3941 \\
4042;
......@@ -85,6 +87,10 @@ pub fn main() !void {
8587 .name = "fmt",
8688 .exec = cmdFmt,
8789 },
90 Command{
91 .name = "libc",
92 .exec = cmdLibC,
93 },
8894 Command{
8995 .name = "targets",
9096 .exec = cmdTargets,
......@@ -130,11 +136,10 @@ const usage_build_generic =
130136 \\ --color [auto|off|on] Enable or disable colored error messages
131137 \\
132138 \\Compile Options:
139 \\ --libc [file] Provide a file which specifies libc paths
133140 \\ --assembly [source] Add assembly file to build
134 \\ --cache-dir [path] Override the cache directory
135141 \\ --emit [filetype] Emit a specific file format as compilation output
136142 \\ --enable-timing-info Print timing diagnostics
137 \\ --libc-include-dir [path] Directory where libc stdlib.h resides
138143 \\ --name [name] Override output name
139144 \\ --output [file] Override destination path
140145 \\ --output-h [file] Override generated header file path
......@@ -163,12 +168,7 @@ const usage_build_generic =
163168 \\
164169 \\Link Options:
165170 \\ --ar-path [path] Set the path to ar
166 \\ --dynamic-linker [path] Set the path to ld.so
167171 \\ --each-lib-rpath Add rpath for each used dynamic library
168 \\ --libc-lib-dir [path] Directory where libc crt1.o resides
169 \\ --libc-static-lib-dir [path] Directory where libc crtbegin.o resides
170 \\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides
171 \\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides
172172 \\ --library [lib] Link against lib
173173 \\ --forbid-library [lib] Make it an error to link against lib
174174 \\ --library-path [dir] Add a directory to the library search path
......@@ -203,14 +203,13 @@ const args_build_generic = []Flag{
203203 }),
204204
205205 Flag.ArgMergeN("--assembly", 1),
206 Flag.Arg1("--cache-dir"),
207206 Flag.Option("--emit", []const []const u8{
208207 "asm",
209208 "bin",
210209 "llvm-ir",
211210 }),
212211 Flag.Bool("--enable-timing-info"),
213 Flag.Arg1("--libc-include-dir"),
212 Flag.Arg1("--libc"),
214213 Flag.Arg1("--name"),
215214 Flag.Arg1("--output"),
216215 Flag.Arg1("--output-h"),
......@@ -234,12 +233,7 @@ const args_build_generic = []Flag{
234233 Flag.Arg1("-mllvm"),
235234
236235 Flag.Arg1("--ar-path"),
237 Flag.Arg1("--dynamic-linker"),
238236 Flag.Bool("--each-lib-rpath"),
239 Flag.Arg1("--libc-lib-dir"),
240 Flag.Arg1("--libc-static-lib-dir"),
241 Flag.Arg1("--msvc-lib-dir"),
242 Flag.Arg1("--kernel32-lib-dir"),
243237 Flag.ArgMergeN("--library", 1),
244238 Flag.ArgMergeN("--forbid-library", 1),
245239 Flag.ArgMergeN("--library-path", 1),
......@@ -363,6 +357,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
363357 }
364358 };
365359
360 const is_static = flags.present("static");
361
366362 const assembly_files = flags.many("assembly");
367363 const link_objects = flags.many("object");
368364 if (root_source_file == null and link_objects.len == 0 and assembly_files.len == 0) {
......@@ -375,21 +371,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
375371 os.exit(1);
376372 }
377373
378 const rel_cache_dir = flags.single("cache-dir") orelse "zig-cache"[0..];
379 const full_cache_dir = os.path.resolve(allocator, ".", rel_cache_dir) catch {
380 try stderr.print("invalid cache dir: {}\n", rel_cache_dir);
381 os.exit(1);
382 };
383 defer allocator.free(full_cache_dir);
384
385374 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
386375 defer allocator.free(zig_lib_dir);
387376
377 var override_libc: LibCInstallation = undefined;
378
388379 var loop: event.Loop = undefined;
389380 try loop.initMultiThreaded(allocator);
390381 defer loop.deinit();
391382
392 var event_loop_local = EventLoopLocal.init(&loop);
383 var event_loop_local = try EventLoopLocal.init(&loop);
393384 defer event_loop_local.deinit();
394385
395386 var comp = try Compilation.create(
......@@ -399,11 +390,20 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
399390 Target.Native,
400391 out_type,
401392 build_mode,
393 is_static,
402394 zig_lib_dir,
403 full_cache_dir,
404395 );
405396 defer comp.destroy();
406397
398 if (flags.single("libc")) |libc_path| {
399 parseLibcPaths(loop.allocator, &override_libc, libc_path);
400 comp.override_libc = &override_libc;
401 }
402
403 for (flags.many("library")) |lib| {
404 _ = try comp.addLinkLib(lib, true);
405 }
406
407407 comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10);
408408 comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10);
409409 comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10);
......@@ -426,26 +426,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
426426 comp.clang_argv = clang_argv_buf.toSliceConst();
427427
428428 comp.strip = flags.present("strip");
429 comp.is_static = flags.present("static");
430
431 if (flags.single("libc-lib-dir")) |libc_lib_dir| {
432 comp.libc_lib_dir = libc_lib_dir;
433 }
434 if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| {
435 comp.libc_static_lib_dir = libc_static_lib_dir;
436 }
437 if (flags.single("libc-include-dir")) |libc_include_dir| {
438 comp.libc_include_dir = libc_include_dir;
439 }
440 if (flags.single("msvc-lib-dir")) |msvc_lib_dir| {
441 comp.msvc_lib_dir = msvc_lib_dir;
442 }
443 if (flags.single("kernel32-lib-dir")) |kernel32_lib_dir| {
444 comp.kernel32_lib_dir = kernel32_lib_dir;
445 }
446 if (flags.single("dynamic-linker")) |dynamic_linker| {
447 comp.dynamic_linker = dynamic_linker;
448 }
449429
450430 comp.verbose_tokenize = flags.present("verbose-tokenize");
451431 comp.verbose_ast_tree = flags.present("verbose-ast-tree");
......@@ -481,9 +461,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
481461 }
482462
483463 comp.emit_file_type = emit_type;
484 comp.link_objects = link_objects;
485464 comp.assembly_files = assembly_files;
486 comp.link_out_file = flags.single("out-file");
465 comp.link_out_file = flags.single("output");
466 comp.link_objects = link_objects;
487467
488468 try comp.build();
489469 const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color);
......@@ -497,7 +477,6 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
497477
498478 switch (build_event) {
499479 Compilation.Event.Ok => {
500 std.debug.warn("Build succeeded\n");
501480 return;
502481 },
503482 Compilation.Event.Error => |err| {
......@@ -506,7 +485,8 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
506485 },
507486 Compilation.Event.Fail => |msgs| {
508487 for (msgs) |msg| {
509 errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1);
488 defer msg.destroy();
489 msg.printToFile(&stderr_file, color) catch os.exit(1);
510490 }
511491 },
512492 }
......@@ -577,6 +557,53 @@ const Fmt = struct {
577557 }
578558};
579559
560fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
561 libc.parse(allocator, libc_paths_file, stderr) catch |err| {
562 stderr.print(
563 "Unable to parse libc path file '{}': {}.\n" ++
564 "Try running `zig libc` to see an example for the native target.\n",
565 libc_paths_file,
566 @errorName(err),
567 ) catch os.exit(1);
568 os.exit(1);
569 };
570}
571
572fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
573 switch (args.len) {
574 0 => {},
575 1 => {
576 var libc_installation: LibCInstallation = undefined;
577 parseLibcPaths(allocator, &libc_installation, args[0]);
578 return;
579 },
580 else => {
581 try stderr.print("unexpected extra parameter: {}\n", args[1]);
582 os.exit(1);
583 },
584 }
585
586 var loop: event.Loop = undefined;
587 try loop.initMultiThreaded(allocator);
588 defer loop.deinit();
589
590 var event_loop_local = try EventLoopLocal.init(&loop);
591 defer event_loop_local.deinit();
592
593 const handle = try async<loop.allocator> findLibCAsync(&event_loop_local);
594 defer cancel handle;
595
596 loop.run();
597}
598
599async fn findLibCAsync(event_loop_local: *EventLoopLocal) void {
600 const libc = (await (async event_loop_local.getNativeLibC() catch unreachable)) catch |err| {
601 stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1);
602 os.exit(1);
603 };
604 libc.render(stdout) catch os.exit(1);
605}
606
580607fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
581608 var flags = try Args.parse(allocator, args_fmt_spec, args);
582609 defer flags.deinit();
......@@ -620,10 +647,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
620647
621648 var error_it = tree.errors.iterator(0);
622649 while (error_it.next()) |parse_error| {
623 const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
624 defer allocator.destroy(msg);
650 const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
651 defer msg.destroy();
625652
626 try errmsg.printToFile(&stderr_file, msg, color);
653 try msg.printToFile(&stderr_file, color);
627654 }
628655 if (tree.errors.len != 0) {
629656 os.exit(1);
......@@ -676,10 +703,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
676703
677704 var error_it = tree.errors.iterator(0);
678705 while (error_it.next()) |parse_error| {
679 const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, file_path);
680 defer allocator.destroy(msg);
706 const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, file_path);
707 defer msg.destroy();
681708
682 try errmsg.printToFile(&stderr_file, msg, color);
709 try msg.printToFile(&stderr_file, color);
683710 }
684711 if (tree.errors.len != 0) {
685712 fmt.any_error = true;
src-self-hosted/package.zig created+29
......@@ -0,0 +1,29 @@
1const std = @import("std");
2const mem = std.mem;
3const assert = std.debug.assert;
4const Buffer = std.Buffer;
5
6pub const Package = struct {
7 root_src_dir: Buffer,
8 root_src_path: Buffer,
9
10 /// relative to root_src_dir
11 table: Table,
12
13 pub const Table = std.HashMap([]const u8, *Package, mem.hash_slice_u8, mem.eql_slice_u8);
14
15 /// makes internal copies of root_src_dir and root_src_path
16 /// allocator should be an arena allocator because Package never frees anything
17 pub fn create(allocator: *mem.Allocator, root_src_dir: []const u8, root_src_path: []const u8) !*Package {
18 return allocator.create(Package{
19 .root_src_dir = try Buffer.init(allocator, root_src_dir),
20 .root_src_path = try Buffer.init(allocator, root_src_path),
21 .table = Table.init(allocator),
22 });
23 }
24
25 pub fn add(self: *Package, name: []const u8, package: *Package) !void {
26 const entry = try self.table.put(try mem.dupe(self.table.allocator, u8, name), package);
27 assert(entry == null);
28 }
29};
src-self-hosted/parsed_file.zig deleted-6
......@@ -1,6 +0,0 @@
1const ast = @import("std").zig.ast;
2
3pub const ParsedFile = struct {
4 tree: ast.Tree,
5 realpath: []const u8,
6};
src-self-hosted/scope.zig+114-39
......@@ -8,6 +8,8 @@ const ast = std.zig.ast;
88const Value = @import("value.zig").Value;
99const ir = @import("ir.zig");
1010const Span = @import("errmsg.zig").Span;
11const assert = std.debug.assert;
12const event = std.event;
1113
1214pub const Scope = struct {
1315 id: Id,
......@@ -23,7 +25,8 @@ pub const Scope = struct {
2325 if (base.ref_count == 0) {
2426 if (base.parent) |parent| parent.deref(comp);
2527 switch (base.id) {
26 Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(),
28 Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp),
29 Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(comp),
2730 Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp),
2831 Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp),
2932 Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp),
......@@ -33,6 +36,15 @@ pub const Scope = struct {
3336 }
3437 }
3538
39 pub fn findRoot(base: *Scope) *Root {
40 var scope = base;
41 while (scope.parent) |parent| {
42 scope = parent;
43 }
44 assert(scope.id == Id.Root);
45 return @fieldParentPtr(Root, "base", scope);
46 }
47
3648 pub fn findFnDef(base: *Scope) ?*FnDef {
3749 var scope = base;
3850 while (true) {
......@@ -44,12 +56,33 @@ pub const Scope = struct {
4456 Id.Defer,
4557 Id.DeferExpr,
4658 Id.CompTime,
59 Id.Root,
60 => scope = scope.parent orelse return null,
61 }
62 }
63 }
64
65 pub fn findDeferExpr(base: *Scope) ?*DeferExpr {
66 var scope = base;
67 while (true) {
68 switch (scope.id) {
69 Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", base),
70
71 Id.FnDef,
72 Id.Decls,
73 => return null,
74
75 Id.Block,
76 Id.Defer,
77 Id.CompTime,
78 Id.Root,
4779 => scope = scope.parent orelse return null,
4880 }
4981 }
5082 }
5183
5284 pub const Id = enum {
85 Root,
5386 Decls,
5487 Block,
5588 FnDef,
......@@ -58,42 +91,82 @@ pub const Scope = struct {
5891 DeferExpr,
5992 };
6093
94 pub const Root = struct {
95 base: Scope,
96 tree: *ast.Tree,
97 realpath: []const u8,
98
99 /// Creates a Root scope with 1 reference
100 /// Takes ownership of realpath
101 /// Takes ownership of tree, will deinit and destroy when done.
102 pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root {
103 const self = try comp.gpa().create(Root{
104 .base = Scope{
105 .id = Id.Root,
106 .parent = null,
107 .ref_count = 1,
108 },
109 .tree = tree,
110 .realpath = realpath,
111 });
112 errdefer comp.gpa().destroy(self);
113
114 return self;
115 }
116
117 pub fn destroy(self: *Root, comp: *Compilation) void {
118 comp.gpa().free(self.tree.source);
119 self.tree.deinit();
120 comp.gpa().destroy(self.tree);
121 comp.gpa().free(self.realpath);
122 comp.gpa().destroy(self);
123 }
124 };
125
61126 pub const Decls = struct {
62127 base: Scope,
63 table: Decl.Table,
128
129 /// The lock must be respected for writing. However once name_future resolves,
130 /// readers can freely access it.
131 table: event.Locked(Decl.Table),
132
133 /// Once this future is resolved, the table is complete and available for unlocked
134 /// read-only access. It does not mean all the decls are resolved; it means only that
135 /// the table has all the names. Each decl in the table has its own resolution state.
136 name_future: event.Future(void),
64137
65138 /// Creates a Decls scope with 1 reference
66 pub fn create(comp: *Compilation, parent: ?*Scope) !*Decls {
67 const self = try comp.a().create(Decls{
139 pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
140 const self = try comp.gpa().create(Decls{
68141 .base = Scope{
69142 .id = Id.Decls,
70143 .parent = parent,
71144 .ref_count = 1,
72145 },
73 .table = undefined,
146 .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
147 .name_future = event.Future(void).init(comp.loop),
74148 });
75 errdefer comp.a().destroy(self);
76
77 self.table = Decl.Table.init(comp.a());
78 errdefer self.table.deinit();
79
80 if (parent) |p| p.ref();
81
149 parent.ref();
82150 return self;
83151 }
84152
85 pub fn destroy(self: *Decls) void {
153 pub fn destroy(self: *Decls, comp: *Compilation) void {
86154 self.table.deinit();
87 self.table.allocator.destroy(self);
155 comp.gpa().destroy(self);
156 }
157
158 pub async fn getTableReadOnly(self: *Decls) *Decl.Table {
159 _ = await (async self.name_future.get() catch unreachable);
160 return &self.table.private_data;
88161 }
89162 };
90163
91164 pub const Block = struct {
92165 base: Scope,
93 incoming_values: std.ArrayList(*ir.Instruction),
166 incoming_values: std.ArrayList(*ir.Inst),
94167 incoming_blocks: std.ArrayList(*ir.BasicBlock),
95168 end_block: *ir.BasicBlock,
96 is_comptime: *ir.Instruction,
169 is_comptime: *ir.Inst,
97170
98171 safety: Safety,
99172
......@@ -125,8 +198,8 @@ pub const Scope = struct {
125198 };
126199
127200 /// Creates a Block scope with 1 reference
128 pub fn create(comp: *Compilation, parent: ?*Scope) !*Block {
129 const self = try comp.a().create(Block{
201 pub fn create(comp: *Compilation, parent: *Scope) !*Block {
202 const self = try comp.gpa().create(Block{
130203 .base = Scope{
131204 .id = Id.Block,
132205 .parent = parent,
......@@ -138,14 +211,14 @@ pub const Scope = struct {
138211 .is_comptime = undefined,
139212 .safety = Safety.Auto,
140213 });
141 errdefer comp.a().destroy(self);
214 errdefer comp.gpa().destroy(self);
142215
143 if (parent) |p| p.ref();
216 parent.ref();
144217 return self;
145218 }
146219
147220 pub fn destroy(self: *Block, comp: *Compilation) void {
148 comp.a().destroy(self);
221 comp.gpa().destroy(self);
149222 }
150223 };
151224
......@@ -157,8 +230,8 @@ pub const Scope = struct {
157230
158231 /// Creates a FnDef scope with 1 reference
159232 /// Must set the fn_val later
160 pub fn create(comp: *Compilation, parent: ?*Scope) !*FnDef {
161 const self = try comp.a().create(FnDef{
233 pub fn create(comp: *Compilation, parent: *Scope) !*FnDef {
234 const self = try comp.gpa().create(FnDef{
162235 .base = Scope{
163236 .id = Id.FnDef,
164237 .parent = parent,
......@@ -167,13 +240,13 @@ pub const Scope = struct {
167240 .fn_val = undefined,
168241 });
169242
170 if (parent) |p| p.ref();
243 parent.ref();
171244
172245 return self;
173246 }
174247
175248 pub fn destroy(self: *FnDef, comp: *Compilation) void {
176 comp.a().destroy(self);
249 comp.gpa().destroy(self);
177250 }
178251 };
179252
......@@ -181,8 +254,8 @@ pub const Scope = struct {
181254 base: Scope,
182255
183256 /// Creates a CompTime scope with 1 reference
184 pub fn create(comp: *Compilation, parent: ?*Scope) !*CompTime {
185 const self = try comp.a().create(CompTime{
257 pub fn create(comp: *Compilation, parent: *Scope) !*CompTime {
258 const self = try comp.gpa().create(CompTime{
186259 .base = Scope{
187260 .id = Id.CompTime,
188261 .parent = parent,
......@@ -190,12 +263,12 @@ pub const Scope = struct {
190263 },
191264 });
192265
193 if (parent) |p| p.ref();
266 parent.ref();
194267 return self;
195268 }
196269
197270 pub fn destroy(self: *CompTime, comp: *Compilation) void {
198 comp.a().destroy(self);
271 comp.gpa().destroy(self);
199272 }
200273 };
201274
......@@ -212,11 +285,11 @@ pub const Scope = struct {
212285 /// Creates a Defer scope with 1 reference
213286 pub fn create(
214287 comp: *Compilation,
215 parent: ?*Scope,
288 parent: *Scope,
216289 kind: Kind,
217290 defer_expr_scope: *DeferExpr,
218291 ) !*Defer {
219 const self = try comp.a().create(Defer{
292 const self = try comp.gpa().create(Defer{
220293 .base = Scope{
221294 .id = Id.Defer,
222295 .parent = parent,
......@@ -225,42 +298,44 @@ pub const Scope = struct {
225298 .defer_expr_scope = defer_expr_scope,
226299 .kind = kind,
227300 });
228 errdefer comp.a().destroy(self);
301 errdefer comp.gpa().destroy(self);
229302
230303 defer_expr_scope.base.ref();
231304
232 if (parent) |p| p.ref();
305 parent.ref();
233306 return self;
234307 }
235308
236309 pub fn destroy(self: *Defer, comp: *Compilation) void {
237310 self.defer_expr_scope.base.deref(comp);
238 comp.a().destroy(self);
311 comp.gpa().destroy(self);
239312 }
240313 };
241314
242315 pub const DeferExpr = struct {
243316 base: Scope,
244317 expr_node: *ast.Node,
318 reported_err: bool,
245319
246320 /// Creates a DeferExpr scope with 1 reference
247 pub fn create(comp: *Compilation, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr {
248 const self = try comp.a().create(DeferExpr{
321 pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr {
322 const self = try comp.gpa().create(DeferExpr{
249323 .base = Scope{
250324 .id = Id.DeferExpr,
251325 .parent = parent,
252326 .ref_count = 1,
253327 },
254328 .expr_node = expr_node,
329 .reported_err = false,
255330 });
256 errdefer comp.a().destroy(self);
331 errdefer comp.gpa().destroy(self);
257332
258 if (parent) |p| p.ref();
333 parent.ref();
259334 return self;
260335 }
261336
262337 pub fn destroy(self: *DeferExpr, comp: *Compilation) void {
263 comp.a().destroy(self);
338 comp.gpa().destroy(self);
264339 }
265340 };
266341};
src-self-hosted/target.zig+529-27
......@@ -1,60 +1,562 @@
1const std = @import("std");
12const builtin = @import("builtin");
2const c = @import("c.zig");
3const llvm = @import("llvm.zig");
4const CInt = @import("c_int.zig").CInt;
35
4pub const CrossTarget = struct {
5 arch: builtin.Arch,
6 os: builtin.Os,
7 environ: builtin.Environ,
6pub const FloatAbi = enum {
7 Hard,
8 Soft,
9 SoftFp,
810};
911
1012pub const Target = union(enum) {
1113 Native,
12 Cross: CrossTarget,
14 Cross: Cross,
1315
14 pub fn oFileExt(self: *const Target) []const u8 {
15 const environ = switch (self.*) {
16 Target.Native => builtin.environ,
17 Target.Cross => |t| t.environ,
18 };
19 return switch (environ) {
20 builtin.Environ.msvc => ".obj",
16 pub const Cross = struct {
17 arch: builtin.Arch,
18 os: builtin.Os,
19 environ: builtin.Environ,
20 object_format: builtin.ObjectFormat,
21 };
22
23 pub fn objFileExt(self: Target) []const u8 {
24 return switch (self.getObjectFormat()) {
25 builtin.ObjectFormat.coff => ".obj",
2126 else => ".o",
2227 };
2328 }
2429
25 pub fn exeFileExt(self: *const Target) []const u8 {
30 pub fn exeFileExt(self: Target) []const u8 {
2631 return switch (self.getOs()) {
2732 builtin.Os.windows => ".exe",
2833 else => "",
2934 };
3035 }
3136
32 pub fn getOs(self: *const Target) builtin.Os {
33 return switch (self.*) {
37 pub fn libFileExt(self: Target, is_static: bool) []const u8 {
38 return switch (self.getOs()) {
39 builtin.Os.windows => if (is_static) ".lib" else ".dll",
40 else => if (is_static) ".a" else ".so",
41 };
42 }
43
44 pub fn getOs(self: Target) builtin.Os {
45 return switch (self) {
3446 Target.Native => builtin.os,
35 Target.Cross => |t| t.os,
47 @TagType(Target).Cross => |t| t.os,
48 };
49 }
50
51 pub fn getArch(self: Target) builtin.Arch {
52 return switch (self) {
53 Target.Native => builtin.arch,
54 @TagType(Target).Cross => |t| t.arch,
55 };
56 }
57
58 pub fn getEnviron(self: Target) builtin.Environ {
59 return switch (self) {
60 Target.Native => builtin.environ,
61 @TagType(Target).Cross => |t| t.environ,
62 };
63 }
64
65 pub fn getObjectFormat(self: Target) builtin.ObjectFormat {
66 return switch (self) {
67 Target.Native => builtin.object_format,
68 @TagType(Target).Cross => |t| t.object_format,
69 };
70 }
71
72 pub fn isWasm(self: Target) bool {
73 return switch (self.getArch()) {
74 builtin.Arch.wasm32, builtin.Arch.wasm64 => true,
75 else => false,
3676 };
3777 }
3878
39 pub fn isDarwin(self: *const Target) bool {
79 pub fn isDarwin(self: Target) bool {
4080 return switch (self.getOs()) {
4181 builtin.Os.ios, builtin.Os.macosx => true,
4282 else => false,
4383 };
4484 }
4585
46 pub fn isWindows(self: *const Target) bool {
86 pub fn isWindows(self: Target) bool {
4787 return switch (self.getOs()) {
4888 builtin.Os.windows => true,
4989 else => false,
5090 };
5191 }
52};
5392
54pub fn initializeAll() void {
55 c.LLVMInitializeAllTargets();
56 c.LLVMInitializeAllTargetInfos();
57 c.LLVMInitializeAllTargetMCs();
58 c.LLVMInitializeAllAsmPrinters();
59 c.LLVMInitializeAllAsmParsers();
60}
93 /// TODO expose the arch and subarch separately
94 pub fn isArmOrThumb(self: Target) bool {
95 return switch (self.getArch()) {
96 builtin.Arch.armv8_3a,
97 builtin.Arch.armv8_2a,
98 builtin.Arch.armv8_1a,
99 builtin.Arch.armv8,
100 builtin.Arch.armv8r,
101 builtin.Arch.armv8m_baseline,
102 builtin.Arch.armv8m_mainline,
103 builtin.Arch.armv7,
104 builtin.Arch.armv7em,
105 builtin.Arch.armv7m,
106 builtin.Arch.armv7s,
107 builtin.Arch.armv7k,
108 builtin.Arch.armv7ve,
109 builtin.Arch.armv6,
110 builtin.Arch.armv6m,
111 builtin.Arch.armv6k,
112 builtin.Arch.armv6t2,
113 builtin.Arch.armv5,
114 builtin.Arch.armv5te,
115 builtin.Arch.armv4t,
116 builtin.Arch.armebv8_3a,
117 builtin.Arch.armebv8_2a,
118 builtin.Arch.armebv8_1a,
119 builtin.Arch.armebv8,
120 builtin.Arch.armebv8r,
121 builtin.Arch.armebv8m_baseline,
122 builtin.Arch.armebv8m_mainline,
123 builtin.Arch.armebv7,
124 builtin.Arch.armebv7em,
125 builtin.Arch.armebv7m,
126 builtin.Arch.armebv7s,
127 builtin.Arch.armebv7k,
128 builtin.Arch.armebv7ve,
129 builtin.Arch.armebv6,
130 builtin.Arch.armebv6m,
131 builtin.Arch.armebv6k,
132 builtin.Arch.armebv6t2,
133 builtin.Arch.armebv5,
134 builtin.Arch.armebv5te,
135 builtin.Arch.armebv4t,
136 builtin.Arch.thumb,
137 builtin.Arch.thumbeb,
138 => true,
139 else => false,
140 };
141 }
142
143 pub fn initializeAll() void {
144 llvm.InitializeAllTargets();
145 llvm.InitializeAllTargetInfos();
146 llvm.InitializeAllTargetMCs();
147 llvm.InitializeAllAsmPrinters();
148 llvm.InitializeAllAsmParsers();
149 }
150
151 pub fn getTriple(self: Target, allocator: *std.mem.Allocator) !std.Buffer {
152 var result = try std.Buffer.initSize(allocator, 0);
153 errdefer result.deinit();
154
155 // LLVM WebAssembly output support requires the target to be activated at
156 // build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly.
157 //
158 // LLVM determines the output format based on the environment suffix,
159 // defaulting to an object based on the architecture. The default format in
160 // LLVM 6 sets the wasm arch output incorrectly to ELF. We need to
161 // explicitly set this ourself in order for it to work.
162 //
163 // This is fixed in LLVM 7 and you will be able to get wasm output by
164 // using the target triple `wasm32-unknown-unknown-unknown`.
165 const env_name = if (self.isWasm()) "wasm" else @tagName(self.getEnviron());
166
167 var out = &std.io.BufferOutStream.init(&result).stream;
168 try out.print("{}-unknown-{}-{}", @tagName(self.getArch()), @tagName(self.getOs()), env_name);
169
170 return result;
171 }
172
173 pub fn is64bit(self: Target) bool {
174 return self.getArchPtrBitWidth() == 64;
175 }
176
177 pub fn getArchPtrBitWidth(self: Target) u32 {
178 switch (self.getArch()) {
179 builtin.Arch.avr,
180 builtin.Arch.msp430,
181 => return 16,
182
183 builtin.Arch.arc,
184 builtin.Arch.armv8_3a,
185 builtin.Arch.armv8_2a,
186 builtin.Arch.armv8_1a,
187 builtin.Arch.armv8,
188 builtin.Arch.armv8r,
189 builtin.Arch.armv8m_baseline,
190 builtin.Arch.armv8m_mainline,
191 builtin.Arch.armv7,
192 builtin.Arch.armv7em,
193 builtin.Arch.armv7m,
194 builtin.Arch.armv7s,
195 builtin.Arch.armv7k,
196 builtin.Arch.armv7ve,
197 builtin.Arch.armv6,
198 builtin.Arch.armv6m,
199 builtin.Arch.armv6k,
200 builtin.Arch.armv6t2,
201 builtin.Arch.armv5,
202 builtin.Arch.armv5te,
203 builtin.Arch.armv4t,
204 builtin.Arch.armebv8_3a,
205 builtin.Arch.armebv8_2a,
206 builtin.Arch.armebv8_1a,
207 builtin.Arch.armebv8,
208 builtin.Arch.armebv8r,
209 builtin.Arch.armebv8m_baseline,
210 builtin.Arch.armebv8m_mainline,
211 builtin.Arch.armebv7,
212 builtin.Arch.armebv7em,
213 builtin.Arch.armebv7m,
214 builtin.Arch.armebv7s,
215 builtin.Arch.armebv7k,
216 builtin.Arch.armebv7ve,
217 builtin.Arch.armebv6,
218 builtin.Arch.armebv6m,
219 builtin.Arch.armebv6k,
220 builtin.Arch.armebv6t2,
221 builtin.Arch.armebv5,
222 builtin.Arch.armebv5te,
223 builtin.Arch.armebv4t,
224 builtin.Arch.hexagon,
225 builtin.Arch.le32,
226 builtin.Arch.mips,
227 builtin.Arch.mipsel,
228 builtin.Arch.nios2,
229 builtin.Arch.powerpc,
230 builtin.Arch.r600,
231 builtin.Arch.riscv32,
232 builtin.Arch.sparc,
233 builtin.Arch.sparcel,
234 builtin.Arch.tce,
235 builtin.Arch.tcele,
236 builtin.Arch.thumb,
237 builtin.Arch.thumbeb,
238 builtin.Arch.i386,
239 builtin.Arch.xcore,
240 builtin.Arch.nvptx,
241 builtin.Arch.amdil,
242 builtin.Arch.hsail,
243 builtin.Arch.spir,
244 builtin.Arch.kalimbav3,
245 builtin.Arch.kalimbav4,
246 builtin.Arch.kalimbav5,
247 builtin.Arch.shave,
248 builtin.Arch.lanai,
249 builtin.Arch.wasm32,
250 builtin.Arch.renderscript32,
251 => return 32,
252
253 builtin.Arch.aarch64,
254 builtin.Arch.aarch64_be,
255 builtin.Arch.mips64,
256 builtin.Arch.mips64el,
257 builtin.Arch.powerpc64,
258 builtin.Arch.powerpc64le,
259 builtin.Arch.riscv64,
260 builtin.Arch.x86_64,
261 builtin.Arch.nvptx64,
262 builtin.Arch.le64,
263 builtin.Arch.amdil64,
264 builtin.Arch.hsail64,
265 builtin.Arch.spir64,
266 builtin.Arch.wasm64,
267 builtin.Arch.renderscript64,
268 builtin.Arch.amdgcn,
269 builtin.Arch.bpfel,
270 builtin.Arch.bpfeb,
271 builtin.Arch.sparcv9,
272 builtin.Arch.s390x,
273 => return 64,
274 }
275 }
276
277 pub fn getFloatAbi(self: Target) FloatAbi {
278 return switch (self.getEnviron()) {
279 builtin.Environ.gnueabihf,
280 builtin.Environ.eabihf,
281 builtin.Environ.musleabihf,
282 => FloatAbi.Hard,
283 else => FloatAbi.Soft,
284 };
285 }
286
287 pub fn getDynamicLinkerPath(self: Target) ?[]const u8 {
288 const env = self.getEnviron();
289 const arch = self.getArch();
290 switch (env) {
291 builtin.Environ.android => {
292 if (self.is64bit()) {
293 return "/system/bin/linker64";
294 } else {
295 return "/system/bin/linker";
296 }
297 },
298 builtin.Environ.gnux32 => {
299 if (arch == builtin.Arch.x86_64) {
300 return "/libx32/ld-linux-x32.so.2";
301 }
302 },
303 builtin.Environ.musl,
304 builtin.Environ.musleabi,
305 builtin.Environ.musleabihf,
306 => {
307 if (arch == builtin.Arch.x86_64) {
308 return "/lib/ld-musl-x86_64.so.1";
309 }
310 },
311 else => {},
312 }
313 switch (arch) {
314 builtin.Arch.i386,
315 builtin.Arch.sparc,
316 builtin.Arch.sparcel,
317 => return "/lib/ld-linux.so.2",
318
319 builtin.Arch.aarch64 => return "/lib/ld-linux-aarch64.so.1",
320 builtin.Arch.aarch64_be => return "/lib/ld-linux-aarch64_be.so.1",
321
322 builtin.Arch.armv8_3a,
323 builtin.Arch.armv8_2a,
324 builtin.Arch.armv8_1a,
325 builtin.Arch.armv8,
326 builtin.Arch.armv8r,
327 builtin.Arch.armv8m_baseline,
328 builtin.Arch.armv8m_mainline,
329 builtin.Arch.armv7,
330 builtin.Arch.armv7em,
331 builtin.Arch.armv7m,
332 builtin.Arch.armv7s,
333 builtin.Arch.armv7k,
334 builtin.Arch.armv7ve,
335 builtin.Arch.armv6,
336 builtin.Arch.armv6m,
337 builtin.Arch.armv6k,
338 builtin.Arch.armv6t2,
339 builtin.Arch.armv5,
340 builtin.Arch.armv5te,
341 builtin.Arch.armv4t,
342 builtin.Arch.thumb,
343 => return switch (self.getFloatAbi()) {
344 FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3",
345 else => return "/lib/ld-linux.so.3",
346 },
347
348 builtin.Arch.armebv8_3a,
349 builtin.Arch.armebv8_2a,
350 builtin.Arch.armebv8_1a,
351 builtin.Arch.armebv8,
352 builtin.Arch.armebv8r,
353 builtin.Arch.armebv8m_baseline,
354 builtin.Arch.armebv8m_mainline,
355 builtin.Arch.armebv7,
356 builtin.Arch.armebv7em,
357 builtin.Arch.armebv7m,
358 builtin.Arch.armebv7s,
359 builtin.Arch.armebv7k,
360 builtin.Arch.armebv7ve,
361 builtin.Arch.armebv6,
362 builtin.Arch.armebv6m,
363 builtin.Arch.armebv6k,
364 builtin.Arch.armebv6t2,
365 builtin.Arch.armebv5,
366 builtin.Arch.armebv5te,
367 builtin.Arch.armebv4t,
368 builtin.Arch.thumbeb,
369 => return switch (self.getFloatAbi()) {
370 FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3",
371 else => return "/lib/ld-linux.so.3",
372 },
373
374 builtin.Arch.mips,
375 builtin.Arch.mipsel,
376 builtin.Arch.mips64,
377 builtin.Arch.mips64el,
378 => return null,
379
380 builtin.Arch.powerpc => return "/lib/ld.so.1",
381 builtin.Arch.powerpc64 => return "/lib64/ld64.so.2",
382 builtin.Arch.powerpc64le => return "/lib64/ld64.so.2",
383 builtin.Arch.s390x => return "/lib64/ld64.so.1",
384 builtin.Arch.sparcv9 => return "/lib64/ld-linux.so.2",
385 builtin.Arch.x86_64 => return "/lib64/ld-linux-x86-64.so.2",
386
387 builtin.Arch.arc,
388 builtin.Arch.avr,
389 builtin.Arch.bpfel,
390 builtin.Arch.bpfeb,
391 builtin.Arch.hexagon,
392 builtin.Arch.msp430,
393 builtin.Arch.nios2,
394 builtin.Arch.r600,
395 builtin.Arch.amdgcn,
396 builtin.Arch.riscv32,
397 builtin.Arch.riscv64,
398 builtin.Arch.tce,
399 builtin.Arch.tcele,
400 builtin.Arch.xcore,
401 builtin.Arch.nvptx,
402 builtin.Arch.nvptx64,
403 builtin.Arch.le32,
404 builtin.Arch.le64,
405 builtin.Arch.amdil,
406 builtin.Arch.amdil64,
407 builtin.Arch.hsail,
408 builtin.Arch.hsail64,
409 builtin.Arch.spir,
410 builtin.Arch.spir64,
411 builtin.Arch.kalimbav3,
412 builtin.Arch.kalimbav4,
413 builtin.Arch.kalimbav5,
414 builtin.Arch.shave,
415 builtin.Arch.lanai,
416 builtin.Arch.wasm32,
417 builtin.Arch.wasm64,
418 builtin.Arch.renderscript32,
419 builtin.Arch.renderscript64,
420 => return null,
421 }
422 }
423
424 pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef {
425 var result: llvm.TargetRef = undefined;
426 var err_msg: [*]u8 = undefined;
427 if (llvm.GetTargetFromTriple(triple.ptr(), &result, &err_msg) != 0) {
428 std.debug.warn("triple: {s} error: {s}\n", triple.ptr(), err_msg);
429 return error.UnsupportedTarget;
430 }
431 return result;
432 }
433
434 pub fn cIntTypeSizeInBits(self: Target, id: CInt.Id) u32 {
435 const arch = self.getArch();
436 switch (self.getOs()) {
437 builtin.Os.freestanding => switch (self.getArch()) {
438 builtin.Arch.msp430 => switch (id) {
439 CInt.Id.Short,
440 CInt.Id.UShort,
441 CInt.Id.Int,
442 CInt.Id.UInt,
443 => return 16,
444 CInt.Id.Long,
445 CInt.Id.ULong,
446 => return 32,
447 CInt.Id.LongLong,
448 CInt.Id.ULongLong,
449 => return 64,
450 },
451 else => switch (id) {
452 CInt.Id.Short,
453 CInt.Id.UShort,
454 => return 16,
455 CInt.Id.Int,
456 CInt.Id.UInt,
457 => return 32,
458 CInt.Id.Long,
459 CInt.Id.ULong,
460 => return self.getArchPtrBitWidth(),
461 CInt.Id.LongLong,
462 CInt.Id.ULongLong,
463 => return 64,
464 },
465 },
466
467 builtin.Os.linux,
468 builtin.Os.macosx,
469 builtin.Os.openbsd,
470 builtin.Os.zen,
471 => switch (id) {
472 CInt.Id.Short,
473 CInt.Id.UShort,
474 => return 16,
475 CInt.Id.Int,
476 CInt.Id.UInt,
477 => return 32,
478 CInt.Id.Long,
479 CInt.Id.ULong,
480 => return self.getArchPtrBitWidth(),
481 CInt.Id.LongLong,
482 CInt.Id.ULongLong,
483 => return 64,
484 },
485
486 builtin.Os.windows => switch (id) {
487 CInt.Id.Short,
488 CInt.Id.UShort,
489 => return 16,
490 CInt.Id.Int,
491 CInt.Id.UInt,
492 => return 32,
493 CInt.Id.Long,
494 CInt.Id.ULong,
495 CInt.Id.LongLong,
496 CInt.Id.ULongLong,
497 => return 64,
498 },
499
500 builtin.Os.ananas,
501 builtin.Os.cloudabi,
502 builtin.Os.dragonfly,
503 builtin.Os.freebsd,
504 builtin.Os.fuchsia,
505 builtin.Os.ios,
506 builtin.Os.kfreebsd,
507 builtin.Os.lv2,
508 builtin.Os.netbsd,
509 builtin.Os.solaris,
510 builtin.Os.haiku,
511 builtin.Os.minix,
512 builtin.Os.rtems,
513 builtin.Os.nacl,
514 builtin.Os.cnk,
515 builtin.Os.aix,
516 builtin.Os.cuda,
517 builtin.Os.nvcl,
518 builtin.Os.amdhsa,
519 builtin.Os.ps4,
520 builtin.Os.elfiamcu,
521 builtin.Os.tvos,
522 builtin.Os.watchos,
523 builtin.Os.mesa3d,
524 builtin.Os.contiki,
525 builtin.Os.amdpal,
526 => @panic("TODO specify the C integer type sizes for this OS"),
527 }
528 }
529
530 pub fn getDarwinArchString(self: Target) []const u8 {
531 const arch = self.getArch();
532 switch (arch) {
533 builtin.Arch.aarch64 => return "arm64",
534 builtin.Arch.thumb,
535 builtin.Arch.armv8_3a,
536 builtin.Arch.armv8_2a,
537 builtin.Arch.armv8_1a,
538 builtin.Arch.armv8,
539 builtin.Arch.armv8r,
540 builtin.Arch.armv8m_baseline,
541 builtin.Arch.armv8m_mainline,
542 builtin.Arch.armv7,
543 builtin.Arch.armv7em,
544 builtin.Arch.armv7m,
545 builtin.Arch.armv7s,
546 builtin.Arch.armv7k,
547 builtin.Arch.armv7ve,
548 builtin.Arch.armv6,
549 builtin.Arch.armv6m,
550 builtin.Arch.armv6k,
551 builtin.Arch.armv6t2,
552 builtin.Arch.armv5,
553 builtin.Arch.armv5te,
554 builtin.Arch.armv4t,
555 => return "arm",
556 builtin.Arch.powerpc => return "ppc",
557 builtin.Arch.powerpc64 => return "ppc64",
558 builtin.Arch.powerpc64le => return "ppc64le",
559 else => return @tagName(arch),
560 }
561 }
562};
src-self-hosted/test.zig+90-15
......@@ -8,12 +8,14 @@ const assertOrPanic = std.debug.assertOrPanic;
88const errmsg = @import("errmsg.zig");
99const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
1010
11test "compile errors" {
12 var ctx: TestContext = undefined;
11var ctx: TestContext = undefined;
12
13test "stage2" {
1314 try ctx.init();
1415 defer ctx.deinit();
1516
1617 try @import("../test/stage2/compile_errors.zig").addCases(&ctx);
18 try @import("../test/stage2/compare_output.zig").addCases(&ctx);
1719
1820 try ctx.run();
1921}
......@@ -25,7 +27,6 @@ pub const TestContext = struct {
2527 loop: std.event.Loop,
2628 event_loop_local: EventLoopLocal,
2729 zig_lib_dir: []u8,
28 zig_cache_dir: []u8,
2930 file_index: std.atomic.Int(usize),
3031 group: std.event.Group(error!void),
3132 any_err: error!void,
......@@ -38,7 +39,6 @@ pub const TestContext = struct {
3839 .loop = undefined,
3940 .event_loop_local = undefined,
4041 .zig_lib_dir = undefined,
41 .zig_cache_dir = undefined,
4242 .group = undefined,
4343 .file_index = std.atomic.Int(usize).init(0),
4444 };
......@@ -46,7 +46,7 @@ pub const TestContext = struct {
4646 try self.loop.initMultiThreaded(allocator);
4747 errdefer self.loop.deinit();
4848
49 self.event_loop_local = EventLoopLocal.init(&self.loop);
49 self.event_loop_local = try EventLoopLocal.init(&self.loop);
5050 errdefer self.event_loop_local.deinit();
5151
5252 self.group = std.event.Group(error!void).init(&self.loop);
......@@ -55,16 +55,12 @@ pub const TestContext = struct {
5555 self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
5656 errdefer allocator.free(self.zig_lib_dir);
5757
58 self.zig_cache_dir = try introspect.resolveZigCacheDir(allocator);
59 errdefer allocator.free(self.zig_cache_dir);
60
6158 try std.os.makePath(allocator, tmp_dir_name);
6259 errdefer std.os.deleteTree(allocator, tmp_dir_name) catch {};
6360 }
6461
6562 fn deinit(self: *TestContext) void {
6663 std.os.deleteTree(allocator, tmp_dir_name) catch {};
67 allocator.free(self.zig_cache_dir);
6864 allocator.free(self.zig_lib_dir);
6965 self.event_loop_local.deinit();
7066 self.loop.deinit();
......@@ -107,8 +103,8 @@ pub const TestContext = struct {
107103 Target.Native,
108104 Compilation.Kind.Obj,
109105 builtin.Mode.Debug,
106 true, // is_static
110107 self.zig_lib_dir,
111 self.zig_cache_dir,
112108 );
113109 errdefer comp.destroy();
114110
......@@ -117,6 +113,84 @@ pub const TestContext = struct {
117113 try self.group.call(getModuleEvent, comp, source, path, line, column, msg);
118114 }
119115
116 fn testCompareOutputLibC(
117 self: *TestContext,
118 source: []const u8,
119 expected_output: []const u8,
120 ) !void {
121 var file_index_buf: [20]u8 = undefined;
122 const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());
123 const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1);
124
125 const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, Target(Target.Native).exeFileExt());
126 if (std.os.path.dirname(file1_path)) |dirname| {
127 try std.os.makePath(allocator, dirname);
128 }
129
130 // TODO async I/O
131 try std.io.writeFile(allocator, file1_path, source);
132
133 var comp = try Compilation.create(
134 &self.event_loop_local,
135 "test",
136 file1_path,
137 Target.Native,
138 Compilation.Kind.Exe,
139 builtin.Mode.Debug,
140 false,
141 self.zig_lib_dir,
142 );
143 errdefer comp.destroy();
144
145 _ = try comp.addLinkLib("c", true);
146 comp.link_out_file = output_file;
147 try comp.build();
148
149 try self.group.call(getModuleEventSuccess, comp, output_file, expected_output);
150 }
151
152 async fn getModuleEventSuccess(
153 comp: *Compilation,
154 exe_file: []const u8,
155 expected_output: []const u8,
156 ) !void {
157 // TODO this should not be necessary
158 const exe_file_2 = try std.mem.dupe(allocator, u8, exe_file);
159
160 defer comp.destroy();
161 const build_event = await (async comp.events.get() catch unreachable);
162
163 switch (build_event) {
164 Compilation.Event.Ok => {
165 const argv = []const []const u8{exe_file_2};
166 // TODO use event loop
167 const child = try std.os.ChildProcess.exec(allocator, argv, null, null, 1024 * 1024);
168 switch (child.term) {
169 std.os.ChildProcess.Term.Exited => |code| {
170 if (code != 0) {
171 return error.BadReturnCode;
172 }
173 },
174 else => {
175 return error.Crashed;
176 },
177 }
178 if (!mem.eql(u8, child.stdout, expected_output)) {
179 return error.OutputMismatch;
180 }
181 },
182 Compilation.Event.Error => |err| return err,
183 Compilation.Event.Fail => |msgs| {
184 var stderr = try std.io.getStdErr();
185 try stderr.write("build incorrectly failed:\n");
186 for (msgs) |msg| {
187 defer msg.destroy();
188 try msg.printToFile(&stderr, errmsg.Color.Auto);
189 }
190 },
191 }
192 }
193
120194 async fn getModuleEvent(
121195 comp: *Compilation,
122196 source: []const u8,
......@@ -138,10 +212,10 @@ pub const TestContext = struct {
138212 Compilation.Event.Fail => |msgs| {
139213 assertOrPanic(msgs.len != 0);
140214 for (msgs) |msg| {
141 if (mem.endsWith(u8, msg.path, path) and mem.eql(u8, msg.text, text)) {
142 const first_token = msg.tree.tokens.at(msg.span.first);
143 const last_token = msg.tree.tokens.at(msg.span.first);
144 const start_loc = msg.tree.tokenLocationPtr(0, first_token);
215 if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) {
216 const first_token = msg.getTree().tokens.at(msg.span.first);
217 const last_token = msg.getTree().tokens.at(msg.span.first);
218 const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
145219 if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
146220 return;
147221 }
......@@ -158,7 +232,8 @@ pub const TestContext = struct {
158232 std.debug.warn("\n====found:========\n");
159233 var stderr = try std.io.getStdErr();
160234 for (msgs) |msg| {
161 try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto);
235 defer msg.destroy();
236 try msg.printToFile(&stderr, errmsg.Color.Auto);
162237 }
163238 std.debug.warn("============\n");
164239 return error.TestFailed;
src-self-hosted/type.zig+419-92
......@@ -4,11 +4,17 @@ const Scope = @import("scope.zig").Scope;
44const Compilation = @import("compilation.zig").Compilation;
55const Value = @import("value.zig").Value;
66const llvm = @import("llvm.zig");
7const ObjectFile = @import("codegen.zig").ObjectFile;
7const event = std.event;
8const Allocator = std.mem.Allocator;
9const assert = std.debug.assert;
810
911pub const Type = struct {
1012 base: Value,
1113 id: Id,
14 name: []const u8,
15 abi_alignment: AbiAlignment,
16
17 pub const AbiAlignment = event.Future(error{OutOfMemory}!u32);
1218
1319 pub const Id = builtin.TypeId;
1420
......@@ -42,33 +48,37 @@ pub const Type = struct {
4248 }
4349 }
4450
45 pub fn getLlvmType(base: *Type, ofile: *ObjectFile) (error{OutOfMemory}!llvm.TypeRef) {
51 pub fn getLlvmType(
52 base: *Type,
53 allocator: *Allocator,
54 llvm_context: llvm.ContextRef,
55 ) (error{OutOfMemory}!llvm.TypeRef) {
4656 switch (base.id) {
47 Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(ofile),
48 Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(ofile),
57 Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context),
58 Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context),
4959 Id.Type => unreachable,
5060 Id.Void => unreachable,
51 Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(ofile),
61 Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(allocator, llvm_context),
5262 Id.NoReturn => unreachable,
53 Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(ofile),
54 Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(ofile),
55 Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(ofile),
56 Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(ofile),
63 Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(allocator, llvm_context),
64 Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(allocator, llvm_context),
65 Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(allocator, llvm_context),
66 Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(allocator, llvm_context),
5767 Id.ComptimeFloat => unreachable,
5868 Id.ComptimeInt => unreachable,
5969 Id.Undefined => unreachable,
6070 Id.Null => unreachable,
61 Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(ofile),
62 Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(ofile),
63 Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(ofile),
64 Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(ofile),
65 Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(ofile),
71 Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(allocator, llvm_context),
72 Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(allocator, llvm_context),
73 Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(allocator, llvm_context),
74 Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(allocator, llvm_context),
75 Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(allocator, llvm_context),
6676 Id.Namespace => unreachable,
6777 Id.Block => unreachable,
68 Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(ofile),
78 Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(allocator, llvm_context),
6979 Id.ArgTuple => unreachable,
70 Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(ofile),
71 Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(ofile),
80 Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context),
81 Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(allocator, llvm_context),
7282 }
7383 }
7484
......@@ -151,8 +161,49 @@ pub const Type = struct {
151161 std.debug.warn("{}", @tagName(base.id));
152162 }
153163
154 pub fn getAbiAlignment(base: *Type, comp: *Compilation) u32 {
155 @panic("TODO getAbiAlignment");
164 fn init(base: *Type, comp: *Compilation, id: Id, name: []const u8) void {
165 base.* = Type{
166 .base = Value{
167 .id = Value.Id.Type,
168 .typ = &MetaType.get(comp).base,
169 .ref_count = std.atomic.Int(usize).init(1),
170 },
171 .id = id,
172 .name = name,
173 .abi_alignment = AbiAlignment.init(comp.loop),
174 };
175 }
176
177 /// If you happen to have an llvm context handy, use getAbiAlignmentInContext instead.
178 /// Otherwise, this one will grab one from the pool and then release it.
179 pub async fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 {
180 if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
181
182 {
183 const held = try comp.event_loop_local.getAnyLlvmContext();
184 defer held.release(comp.event_loop_local);
185
186 const llvm_context = held.node.data;
187
188 base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable);
189 }
190 base.abi_alignment.resolve();
191 return base.abi_alignment.data;
192 }
193
194 /// If you have an llvm conext handy, you can use it here.
195 pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 {
196 if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
197
198 base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable);
199 base.abi_alignment.resolve();
200 return base.abi_alignment.data;
201 }
202
203 /// Lower level function that does the work. See getAbiAlignment.
204 async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 {
205 const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context);
206 return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type));
156207 }
157208
158209 pub const Struct = struct {
......@@ -160,10 +211,10 @@ pub const Type = struct {
160211 decls: *Scope.Decls,
161212
162213 pub fn destroy(self: *Struct, comp: *Compilation) void {
163 comp.a().destroy(self);
214 comp.gpa().destroy(self);
164215 }
165216
166 pub fn getLlvmType(self: *Struct, ofile: *ObjectFile) llvm.TypeRef {
217 pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
167218 @panic("TODO");
168219 }
169220 };
......@@ -176,28 +227,23 @@ pub const Type = struct {
176227
177228 pub const Param = struct {
178229 is_noalias: bool,
179 typeof: *Type,
230 typ: *Type,
180231 };
181232
182233 pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn {
183 const result = try comp.a().create(Fn{
184 .base = Type{
185 .base = Value{
186 .id = Value.Id.Type,
187 .typeof = &MetaType.get(comp).base,
188 .ref_count = std.atomic.Int(usize).init(1),
189 },
190 .id = builtin.TypeId.Fn,
191 },
234 const result = try comp.gpa().create(Fn{
235 .base = undefined,
192236 .return_type = return_type,
193237 .params = params,
194238 .is_var_args = is_var_args,
195239 });
196 errdefer comp.a().destroy(result);
240 errdefer comp.gpa().destroy(result);
241
242 result.base.init(comp, Id.Fn, "TODO fn type name");
197243
198244 result.return_type.base.ref();
199245 for (result.params) |param| {
200 param.typeof.base.ref();
246 param.typ.base.ref();
201247 }
202248 return result;
203249 }
......@@ -205,20 +251,20 @@ pub const Type = struct {
205251 pub fn destroy(self: *Fn, comp: *Compilation) void {
206252 self.return_type.base.deref(comp);
207253 for (self.params) |param| {
208 param.typeof.base.deref(comp);
254 param.typ.base.deref(comp);
209255 }
210 comp.a().destroy(self);
256 comp.gpa().destroy(self);
211257 }
212258
213 pub fn getLlvmType(self: *Fn, ofile: *ObjectFile) !llvm.TypeRef {
259 pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
214260 const llvm_return_type = switch (self.return_type.id) {
215 Type.Id.Void => llvm.VoidTypeInContext(ofile.context) orelse return error.OutOfMemory,
216 else => try self.return_type.getLlvmType(ofile),
261 Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory,
262 else => try self.return_type.getLlvmType(allocator, llvm_context),
217263 };
218 const llvm_param_types = try ofile.a().alloc(llvm.TypeRef, self.params.len);
219 defer ofile.a().free(llvm_param_types);
264 const llvm_param_types = try allocator.alloc(llvm.TypeRef, self.params.len);
265 defer allocator.free(llvm_param_types);
220266 for (llvm_param_types) |*llvm_param_type, i| {
221 llvm_param_type.* = try self.params[i].typeof.getLlvmType(ofile);
267 llvm_param_type.* = try self.params[i].typ.getLlvmType(allocator, llvm_context);
222268 }
223269
224270 return llvm.FunctionType(
......@@ -241,7 +287,7 @@ pub const Type = struct {
241287 }
242288
243289 pub fn destroy(self: *MetaType, comp: *Compilation) void {
244 comp.a().destroy(self);
290 comp.gpa().destroy(self);
245291 }
246292 };
247293
......@@ -255,7 +301,7 @@ pub const Type = struct {
255301 }
256302
257303 pub fn destroy(self: *Void, comp: *Compilation) void {
258 comp.a().destroy(self);
304 comp.gpa().destroy(self);
259305 }
260306 };
261307
......@@ -269,10 +315,10 @@ pub const Type = struct {
269315 }
270316
271317 pub fn destroy(self: *Bool, comp: *Compilation) void {
272 comp.a().destroy(self);
318 comp.gpa().destroy(self);
273319 }
274320
275 pub fn getLlvmType(self: *Bool, ofile: *ObjectFile) llvm.TypeRef {
321 pub fn getLlvmType(self: *Bool, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
276322 @panic("TODO");
277323 }
278324 };
......@@ -287,19 +333,89 @@ pub const Type = struct {
287333 }
288334
289335 pub fn destroy(self: *NoReturn, comp: *Compilation) void {
290 comp.a().destroy(self);
336 comp.gpa().destroy(self);
291337 }
292338 };
293339
294340 pub const Int = struct {
295341 base: Type,
342 key: Key,
343 garbage_node: std.atomic.Stack(*Int).Node,
344
345 pub const Key = struct {
346 bit_count: u32,
347 is_signed: bool,
348
349 pub fn hash(self: *const Key) u32 {
350 const rands = [2]u32{ 0xa4ba6498, 0x75fc5af7 };
351 return rands[@boolToInt(self.is_signed)] *% self.bit_count;
352 }
353
354 pub fn eql(self: *const Key, other: *const Key) bool {
355 return self.bit_count == other.bit_count and self.is_signed == other.is_signed;
356 }
357 };
358
359 pub fn get_u8(comp: *Compilation) *Int {
360 comp.u8_type.base.base.ref();
361 return comp.u8_type;
362 }
363
364 pub async fn get(comp: *Compilation, key: Key) !*Int {
365 {
366 const held = await (async comp.int_type_table.acquire() catch unreachable);
367 defer held.release();
368
369 if (held.value.get(&key)) |entry| {
370 entry.value.base.base.ref();
371 return entry.value;
372 }
373 }
374
375 const self = try comp.gpa().create(Int{
376 .base = undefined,
377 .key = key,
378 .garbage_node = undefined,
379 });
380 errdefer comp.gpa().destroy(self);
381
382 const u_or_i = "ui"[@boolToInt(key.is_signed)];
383 const name = try std.fmt.allocPrint(comp.gpa(), "{c}{}", u_or_i, key.bit_count);
384 errdefer comp.gpa().free(name);
385
386 self.base.init(comp, Id.Int, name);
387
388 {
389 const held = await (async comp.int_type_table.acquire() catch unreachable);
390 defer held.release();
391
392 _ = try held.value.put(&self.key, self);
393 }
394 return self;
395 }
296396
297397 pub fn destroy(self: *Int, comp: *Compilation) void {
298 comp.a().destroy(self);
398 self.garbage_node = std.atomic.Stack(*Int).Node{
399 .data = self,
400 .next = undefined,
401 };
402 comp.registerGarbage(Int, &self.garbage_node);
299403 }
300404
301 pub fn getLlvmType(self: *Int, ofile: *ObjectFile) llvm.TypeRef {
302 @panic("TODO");
405 pub async fn gcDestroy(self: *Int, comp: *Compilation) void {
406 {
407 const held = await (async comp.int_type_table.acquire() catch unreachable);
408 defer held.release();
409
410 _ = held.value.remove(&self.key).?;
411 }
412 // we allocated the name
413 comp.gpa().free(self.base.name);
414 comp.gpa().destroy(self);
415 }
416
417 pub fn getLlvmType(self: *Int, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
418 return llvm.IntTypeInContext(llvm_context, self.key.bit_count) orelse return error.OutOfMemory;
303419 }
304420 };
305421
......@@ -307,59 +423,239 @@ pub const Type = struct {
307423 base: Type,
308424
309425 pub fn destroy(self: *Float, comp: *Compilation) void {
310 comp.a().destroy(self);
426 comp.gpa().destroy(self);
311427 }
312428
313 pub fn getLlvmType(self: *Float, ofile: *ObjectFile) llvm.TypeRef {
429 pub fn getLlvmType(self: *Float, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
314430 @panic("TODO");
315431 }
316432 };
317433 pub const Pointer = struct {
318434 base: Type,
319 mut: Mut,
320 vol: Vol,
321 size: Size,
322 alignment: u32,
435 key: Key,
436 garbage_node: std.atomic.Stack(*Pointer).Node,
437
438 pub const Key = struct {
439 child_type: *Type,
440 mut: Mut,
441 vol: Vol,
442 size: Size,
443 alignment: Align,
444
445 pub fn hash(self: *const Key) u32 {
446 const align_hash = switch (self.alignment) {
447 Align.Abi => 0xf201c090,
448 Align.Override => |x| x,
449 };
450 return hash_usize(@ptrToInt(self.child_type)) *%
451 hash_enum(self.mut) *%
452 hash_enum(self.vol) *%
453 hash_enum(self.size) *%
454 align_hash;
455 }
456
457 pub fn eql(self: *const Key, other: *const Key) bool {
458 if (self.child_type != other.child_type or
459 self.mut != other.mut or
460 self.vol != other.vol or
461 self.size != other.size or
462 @TagType(Align)(self.alignment) != @TagType(Align)(other.alignment))
463 {
464 return false;
465 }
466 switch (self.alignment) {
467 Align.Abi => return true,
468 Align.Override => |x| return x == other.alignment.Override,
469 }
470 }
471 };
323472
324473 pub const Mut = enum {
325474 Mut,
326475 Const,
327476 };
477
328478 pub const Vol = enum {
329479 Non,
330480 Volatile,
331481 };
482
483 pub const Align = union(enum) {
484 Abi,
485 Override: u32,
486 };
487
332488 pub const Size = builtin.TypeInfo.Pointer.Size;
333489
334490 pub fn destroy(self: *Pointer, comp: *Compilation) void {
335 comp.a().destroy(self);
491 self.garbage_node = std.atomic.Stack(*Pointer).Node{
492 .data = self,
493 .next = undefined,
494 };
495 comp.registerGarbage(Pointer, &self.garbage_node);
336496 }
337497
338 pub fn get(
498 pub async fn gcDestroy(self: *Pointer, comp: *Compilation) void {
499 {
500 const held = await (async comp.ptr_type_table.acquire() catch unreachable);
501 defer held.release();
502
503 _ = held.value.remove(&self.key).?;
504 }
505 self.key.child_type.base.deref(comp);
506 comp.gpa().destroy(self);
507 }
508
509 pub async fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 {
510 switch (self.key.alignment) {
511 Align.Abi => return await (async self.key.child_type.getAbiAlignment(comp) catch unreachable),
512 Align.Override => |alignment| return alignment,
513 }
514 }
515
516 pub async fn get(
339517 comp: *Compilation,
340 elem_type: *Type,
341 mut: Mut,
342 vol: Vol,
343 size: Size,
344 alignment: u32,
345 ) *Pointer {
346 @panic("TODO get pointer");
518 key: Key,
519 ) !*Pointer {
520 var normal_key = key;
521 switch (key.alignment) {
522 Align.Abi => {},
523 Align.Override => |alignment| {
524 const abi_align = try await (async key.child_type.getAbiAlignment(comp) catch unreachable);
525 if (abi_align == alignment) {
526 normal_key.alignment = Align.Abi;
527 }
528 },
529 }
530 {
531 const held = await (async comp.ptr_type_table.acquire() catch unreachable);
532 defer held.release();
533
534 if (held.value.get(&normal_key)) |entry| {
535 entry.value.base.base.ref();
536 return entry.value;
537 }
538 }
539
540 const self = try comp.gpa().create(Pointer{
541 .base = undefined,
542 .key = normal_key,
543 .garbage_node = undefined,
544 });
545 errdefer comp.gpa().destroy(self);
546
547 const size_str = switch (self.key.size) {
548 Size.One => "*",
549 Size.Many => "[*]",
550 Size.Slice => "[]",
551 };
552 const mut_str = switch (self.key.mut) {
553 Mut.Const => "const ",
554 Mut.Mut => "",
555 };
556 const vol_str = switch (self.key.vol) {
557 Vol.Volatile => "volatile ",
558 Vol.Non => "",
559 };
560 const name = switch (self.key.alignment) {
561 Align.Abi => try std.fmt.allocPrint(
562 comp.gpa(),
563 "{}{}{}{}",
564 size_str,
565 mut_str,
566 vol_str,
567 self.key.child_type.name,
568 ),
569 Align.Override => |alignment| try std.fmt.allocPrint(
570 comp.gpa(),
571 "{}align<{}> {}{}{}",
572 size_str,
573 alignment,
574 mut_str,
575 vol_str,
576 self.key.child_type.name,
577 ),
578 };
579 errdefer comp.gpa().free(name);
580
581 self.base.init(comp, Id.Pointer, name);
582
583 {
584 const held = await (async comp.ptr_type_table.acquire() catch unreachable);
585 defer held.release();
586
587 _ = try held.value.put(&self.key, self);
588 }
589 return self;
347590 }
348591
349 pub fn getLlvmType(self: *Pointer, ofile: *ObjectFile) llvm.TypeRef {
350 @panic("TODO");
592 pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
593 const elem_llvm_type = try self.key.child_type.getLlvmType(allocator, llvm_context);
594 return llvm.PointerType(elem_llvm_type, 0) orelse return error.OutOfMemory;
351595 }
352596 };
353597
354598 pub const Array = struct {
355599 base: Type,
600 key: Key,
601 garbage_node: std.atomic.Stack(*Array).Node,
602
603 pub const Key = struct {
604 elem_type: *Type,
605 len: usize,
606
607 pub fn hash(self: *const Key) u32 {
608 return hash_usize(@ptrToInt(self.elem_type)) *% hash_usize(self.len);
609 }
610
611 pub fn eql(self: *const Key, other: *const Key) bool {
612 return self.elem_type == other.elem_type and self.len == other.len;
613 }
614 };
356615
357616 pub fn destroy(self: *Array, comp: *Compilation) void {
358 comp.a().destroy(self);
617 self.key.elem_type.base.deref(comp);
618 comp.gpa().destroy(self);
359619 }
360620
361 pub fn getLlvmType(self: *Array, ofile: *ObjectFile) llvm.TypeRef {
362 @panic("TODO");
621 pub async fn get(comp: *Compilation, key: Key) !*Array {
622 key.elem_type.base.ref();
623 errdefer key.elem_type.base.deref(comp);
624
625 {
626 const held = await (async comp.array_type_table.acquire() catch unreachable);
627 defer held.release();
628
629 if (held.value.get(&key)) |entry| {
630 entry.value.base.base.ref();
631 return entry.value;
632 }
633 }
634
635 const self = try comp.gpa().create(Array{
636 .base = undefined,
637 .key = key,
638 .garbage_node = undefined,
639 });
640 errdefer comp.gpa().destroy(self);
641
642 const name = try std.fmt.allocPrint(comp.gpa(), "[{}]{}", key.len, key.elem_type.name);
643 errdefer comp.gpa().free(name);
644
645 self.base.init(comp, Id.Array, name);
646
647 {
648 const held = await (async comp.array_type_table.acquire() catch unreachable);
649 defer held.release();
650
651 _ = try held.value.put(&self.key, self);
652 }
653 return self;
654 }
655
656 pub fn getLlvmType(self: *Array, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
657 const elem_llvm_type = try self.key.elem_type.getLlvmType(allocator, llvm_context);
658 return llvm.ArrayType(elem_llvm_type, @intCast(c_uint, self.key.len)) orelse return error.OutOfMemory;
363659 }
364660 };
365661
......@@ -367,15 +663,21 @@ pub const Type = struct {
367663 base: Type,
368664
369665 pub fn destroy(self: *ComptimeFloat, comp: *Compilation) void {
370 comp.a().destroy(self);
666 comp.gpa().destroy(self);
371667 }
372668 };
373669
374670 pub const ComptimeInt = struct {
375671 base: Type,
376672
673 /// Adds 1 reference to the resulting type
674 pub fn get(comp: *Compilation) *ComptimeInt {
675 comp.comptime_int_type.base.base.ref();
676 return comp.comptime_int_type;
677 }
678
377679 pub fn destroy(self: *ComptimeInt, comp: *Compilation) void {
378 comp.a().destroy(self);
680 comp.gpa().destroy(self);
379681 }
380682 };
381683
......@@ -383,7 +685,7 @@ pub const Type = struct {
383685 base: Type,
384686
385687 pub fn destroy(self: *Undefined, comp: *Compilation) void {
386 comp.a().destroy(self);
688 comp.gpa().destroy(self);
387689 }
388690 };
389691
......@@ -391,7 +693,7 @@ pub const Type = struct {
391693 base: Type,
392694
393695 pub fn destroy(self: *Null, comp: *Compilation) void {
394 comp.a().destroy(self);
696 comp.gpa().destroy(self);
395697 }
396698 };
397699
......@@ -399,10 +701,10 @@ pub const Type = struct {
399701 base: Type,
400702
401703 pub fn destroy(self: *Optional, comp: *Compilation) void {
402 comp.a().destroy(self);
704 comp.gpa().destroy(self);
403705 }
404706
405 pub fn getLlvmType(self: *Optional, ofile: *ObjectFile) llvm.TypeRef {
707 pub fn getLlvmType(self: *Optional, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
406708 @panic("TODO");
407709 }
408710 };
......@@ -411,10 +713,10 @@ pub const Type = struct {
411713 base: Type,
412714
413715 pub fn destroy(self: *ErrorUnion, comp: *Compilation) void {
414 comp.a().destroy(self);
716 comp.gpa().destroy(self);
415717 }
416718
417 pub fn getLlvmType(self: *ErrorUnion, ofile: *ObjectFile) llvm.TypeRef {
719 pub fn getLlvmType(self: *ErrorUnion, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
418720 @panic("TODO");
419721 }
420722 };
......@@ -423,10 +725,10 @@ pub const Type = struct {
423725 base: Type,
424726
425727 pub fn destroy(self: *ErrorSet, comp: *Compilation) void {
426 comp.a().destroy(self);
728 comp.gpa().destroy(self);
427729 }
428730
429 pub fn getLlvmType(self: *ErrorSet, ofile: *ObjectFile) llvm.TypeRef {
731 pub fn getLlvmType(self: *ErrorSet, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
430732 @panic("TODO");
431733 }
432734 };
......@@ -435,10 +737,10 @@ pub const Type = struct {
435737 base: Type,
436738
437739 pub fn destroy(self: *Enum, comp: *Compilation) void {
438 comp.a().destroy(self);
740 comp.gpa().destroy(self);
439741 }
440742
441 pub fn getLlvmType(self: *Enum, ofile: *ObjectFile) llvm.TypeRef {
743 pub fn getLlvmType(self: *Enum, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
442744 @panic("TODO");
443745 }
444746 };
......@@ -447,10 +749,10 @@ pub const Type = struct {
447749 base: Type,
448750
449751 pub fn destroy(self: *Union, comp: *Compilation) void {
450 comp.a().destroy(self);
752 comp.gpa().destroy(self);
451753 }
452754
453 pub fn getLlvmType(self: *Union, ofile: *ObjectFile) llvm.TypeRef {
755 pub fn getLlvmType(self: *Union, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
454756 @panic("TODO");
455757 }
456758 };
......@@ -459,7 +761,7 @@ pub const Type = struct {
459761 base: Type,
460762
461763 pub fn destroy(self: *Namespace, comp: *Compilation) void {
462 comp.a().destroy(self);
764 comp.gpa().destroy(self);
463765 }
464766 };
465767
......@@ -467,7 +769,7 @@ pub const Type = struct {
467769 base: Type,
468770
469771 pub fn destroy(self: *Block, comp: *Compilation) void {
470 comp.a().destroy(self);
772 comp.gpa().destroy(self);
471773 }
472774 };
473775
......@@ -475,10 +777,10 @@ pub const Type = struct {
475777 base: Type,
476778
477779 pub fn destroy(self: *BoundFn, comp: *Compilation) void {
478 comp.a().destroy(self);
780 comp.gpa().destroy(self);
479781 }
480782
481 pub fn getLlvmType(self: *BoundFn, ofile: *ObjectFile) llvm.TypeRef {
783 pub fn getLlvmType(self: *BoundFn, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
482784 @panic("TODO");
483785 }
484786 };
......@@ -487,7 +789,7 @@ pub const Type = struct {
487789 base: Type,
488790
489791 pub fn destroy(self: *ArgTuple, comp: *Compilation) void {
490 comp.a().destroy(self);
792 comp.gpa().destroy(self);
491793 }
492794 };
493795
......@@ -495,10 +797,10 @@ pub const Type = struct {
495797 base: Type,
496798
497799 pub fn destroy(self: *Opaque, comp: *Compilation) void {
498 comp.a().destroy(self);
800 comp.gpa().destroy(self);
499801 }
500802
501 pub fn getLlvmType(self: *Opaque, ofile: *ObjectFile) llvm.TypeRef {
803 pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
502804 @panic("TODO");
503805 }
504806 };
......@@ -507,11 +809,36 @@ pub const Type = struct {
507809 base: Type,
508810
509811 pub fn destroy(self: *Promise, comp: *Compilation) void {
510 comp.a().destroy(self);
812 comp.gpa().destroy(self);
511813 }
512814
513 pub fn getLlvmType(self: *Promise, ofile: *ObjectFile) llvm.TypeRef {
815 pub fn getLlvmType(self: *Promise, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
514816 @panic("TODO");
515817 }
516818 };
517819};
820
821fn hash_usize(x: usize) u32 {
822 return switch (@sizeOf(usize)) {
823 4 => x,
824 8 => @truncate(u32, x *% 0xad44ee2d8e3fc13d),
825 else => @compileError("implement this hash function"),
826 };
827}
828
829fn hash_enum(x: var) u32 {
830 const rands = []u32{
831 0x85ebf64f,
832 0x3fcb3211,
833 0x240a4e8e,
834 0x40bb0e3c,
835 0x78be45af,
836 0x1ca98e37,
837 0xec56053a,
838 0x906adc48,
839 0xd4fe9763,
840 0x54c80dac,
841 };
842 comptime assert(@memberCount(@typeOf(x)) < rands.len);
843 return rands[@enumToInt(x)];
844}
src-self-hosted/value.zig+401-12
......@@ -4,12 +4,14 @@ const Scope = @import("scope.zig").Scope;
44const Compilation = @import("compilation.zig").Compilation;
55const ObjectFile = @import("codegen.zig").ObjectFile;
66const llvm = @import("llvm.zig");
7const Buffer = std.Buffer;
8const assert = std.debug.assert;
79
810/// Values are ref-counted, heap-allocated, and copy-on-write
911/// If there is only 1 ref then write need not copy
1012pub const Value = struct {
1113 id: Id,
12 typeof: *Type,
14 typ: *Type,
1315 ref_count: std.atomic.Int(usize),
1416
1517 /// Thread-safe
......@@ -20,23 +22,37 @@ pub const Value = struct {
2022 /// Thread-safe
2123 pub fn deref(base: *Value, comp: *Compilation) void {
2224 if (base.ref_count.decr() == 1) {
23 base.typeof.base.deref(comp);
25 base.typ.base.deref(comp);
2426 switch (base.id) {
2527 Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp),
2628 Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp),
29 Id.FnProto => @fieldParentPtr(FnProto, "base", base).destroy(comp),
2730 Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp),
2831 Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp),
2932 Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp),
3033 Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp),
34 Id.Int => @fieldParentPtr(Int, "base", base).destroy(comp),
35 Id.Array => @fieldParentPtr(Array, "base", base).destroy(comp),
3136 }
3237 }
3338 }
3439
40 pub fn setType(base: *Value, new_type: *Type, comp: *Compilation) void {
41 base.typ.base.deref(comp);
42 new_type.base.ref();
43 base.typ = new_type;
44 }
45
3546 pub fn getRef(base: *Value) *Value {
3647 base.ref();
3748 return base;
3849 }
3950
51 pub fn cast(base: *Value, comptime T: type) ?*T {
52 if (base.id != @field(Id, @typeName(T))) return null;
53 return @fieldParentPtr(T, "base", base);
54 }
55
4056 pub fn dump(base: *const Value) void {
4157 std.debug.warn("{}", @tagName(base.id));
4258 }
......@@ -45,30 +61,117 @@ pub const Value = struct {
4561 switch (base.id) {
4662 Id.Type => unreachable,
4763 Id.Fn => @panic("TODO"),
64 Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile),
4865 Id.Void => return null,
4966 Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),
5067 Id.NoReturn => unreachable,
51 Id.Ptr => @panic("TODO"),
68 Id.Ptr => return @fieldParentPtr(Ptr, "base", base).getLlvmConst(ofile),
69 Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmConst(ofile),
70 Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmConst(ofile),
71 }
72 }
73
74 pub fn derefAndCopy(self: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
75 if (self.ref_count.get() == 1) {
76 // ( Í¡° ͜ʖ Í¡°)
77 return self;
78 }
79
80 assert(self.ref_count.decr() != 1);
81 return self.copy(comp);
82 }
83
84 pub fn copy(base: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
85 switch (base.id) {
86 Id.Type => unreachable,
87 Id.Fn => unreachable,
88 Id.FnProto => unreachable,
89 Id.Void => unreachable,
90 Id.Bool => unreachable,
91 Id.NoReturn => unreachable,
92 Id.Ptr => unreachable,
93 Id.Array => unreachable,
94 Id.Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base,
5295 }
5396 }
5497
98 pub const Parent = union(enum) {
99 None,
100 BaseStruct: BaseStruct,
101 BaseArray: BaseArray,
102 BaseUnion: *Value,
103 BaseScalar: *Value,
104
105 pub const BaseStruct = struct {
106 val: *Value,
107 field_index: usize,
108 };
109
110 pub const BaseArray = struct {
111 val: *Value,
112 elem_index: usize,
113 };
114 };
115
55116 pub const Id = enum {
56117 Type,
57118 Fn,
58119 Void,
59120 Bool,
60121 NoReturn,
122 Array,
61123 Ptr,
124 Int,
125 FnProto,
62126 };
63127
64128 pub const Type = @import("type.zig").Type;
65129
130 pub const FnProto = struct {
131 base: Value,
132
133 /// The main external name that is used in the .o file.
134 /// TODO https://github.com/ziglang/zig/issues/265
135 symbol_name: Buffer,
136
137 pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: Buffer) !*FnProto {
138 const self = try comp.gpa().create(FnProto{
139 .base = Value{
140 .id = Value.Id.FnProto,
141 .typ = &fn_type.base,
142 .ref_count = std.atomic.Int(usize).init(1),
143 },
144 .symbol_name = symbol_name,
145 });
146 fn_type.base.base.ref();
147 return self;
148 }
149
150 pub fn destroy(self: *FnProto, comp: *Compilation) void {
151 self.symbol_name.deinit();
152 comp.gpa().destroy(self);
153 }
154
155 pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?llvm.ValueRef {
156 const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
157 const llvm_fn = llvm.AddFunction(
158 ofile.module,
159 self.symbol_name.ptr(),
160 llvm_fn_type,
161 ) orelse return error.OutOfMemory;
162
163 // TODO port more logic from codegen.cpp:fn_llvm_value
164
165 return llvm_fn;
166 }
167 };
168
66169 pub const Fn = struct {
67170 base: Value,
68171
69172 /// The main external name that is used in the .o file.
70173 /// TODO https://github.com/ziglang/zig/issues/265
71 symbol_name: std.Buffer,
174 symbol_name: Buffer,
72175
73176 /// parent should be the top level decls or container decls
74177 fndef_scope: *Scope.FnDef,
......@@ -79,19 +182,33 @@ pub const Value = struct {
79182 /// parent is child_scope
80183 block_scope: *Scope.Block,
81184
185 /// Path to the object file that contains this function
186 containing_object: Buffer,
187
188 link_set_node: *std.LinkedList(?*Value.Fn).Node,
189
82190 /// Creates a Fn value with 1 ref
83191 /// Takes ownership of symbol_name
84 pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: std.Buffer) !*Fn {
85 const self = try comp.a().create(Fn{
192 pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: Buffer) !*Fn {
193 const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node{
194 .data = null,
195 .next = undefined,
196 .prev = undefined,
197 });
198 errdefer comp.gpa().destroy(link_set_node);
199
200 const self = try comp.gpa().create(Fn{
86201 .base = Value{
87202 .id = Value.Id.Fn,
88 .typeof = &fn_type.base,
203 .typ = &fn_type.base,
89204 .ref_count = std.atomic.Int(usize).init(1),
90205 },
91206 .fndef_scope = fndef_scope,
92207 .child_scope = &fndef_scope.base,
93208 .block_scope = undefined,
94209 .symbol_name = symbol_name,
210 .containing_object = Buffer.initNull(comp.gpa()),
211 .link_set_node = link_set_node,
95212 });
96213 fn_type.base.base.ref();
97214 fndef_scope.fn_val = self;
......@@ -100,9 +217,19 @@ pub const Value = struct {
100217 }
101218
102219 pub fn destroy(self: *Fn, comp: *Compilation) void {
220 // remove with a tombstone so that we do not have to grab a lock
221 if (self.link_set_node.data != null) {
222 // it's now the job of the link step to find this tombstone and
223 // deallocate it.
224 self.link_set_node.data = null;
225 } else {
226 comp.gpa().destroy(self.link_set_node);
227 }
228
229 self.containing_object.deinit();
103230 self.fndef_scope.base.deref(comp);
104231 self.symbol_name.deinit();
105 comp.a().destroy(self);
232 comp.gpa().destroy(self);
106233 }
107234 };
108235
......@@ -115,7 +242,7 @@ pub const Value = struct {
115242 }
116243
117244 pub fn destroy(self: *Void, comp: *Compilation) void {
118 comp.a().destroy(self);
245 comp.gpa().destroy(self);
119246 }
120247 };
121248
......@@ -134,7 +261,7 @@ pub const Value = struct {
134261 }
135262
136263 pub fn destroy(self: *Bool, comp: *Compilation) void {
137 comp.a().destroy(self);
264 comp.gpa().destroy(self);
138265 }
139266
140267 pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef {
......@@ -156,12 +283,14 @@ pub const Value = struct {
156283 }
157284
158285 pub fn destroy(self: *NoReturn, comp: *Compilation) void {
159 comp.a().destroy(self);
286 comp.gpa().destroy(self);
160287 }
161288 };
162289
163290 pub const Ptr = struct {
164291 base: Value,
292 special: Special,
293 mut: Mut,
165294
166295 pub const Mut = enum {
167296 CompTimeConst,
......@@ -169,8 +298,268 @@ pub const Value = struct {
169298 RunTime,
170299 };
171300
301 pub const Special = union(enum) {
302 Scalar: *Value,
303 BaseArray: BaseArray,
304 BaseStruct: BaseStruct,
305 HardCodedAddr: u64,
306 Discard,
307 };
308
309 pub const BaseArray = struct {
310 val: *Value,
311 elem_index: usize,
312 };
313
314 pub const BaseStruct = struct {
315 val: *Value,
316 field_index: usize,
317 };
318
319 pub async fn createArrayElemPtr(
320 comp: *Compilation,
321 array_val: *Array,
322 mut: Type.Pointer.Mut,
323 size: Type.Pointer.Size,
324 elem_index: usize,
325 ) !*Ptr {
326 array_val.base.ref();
327 errdefer array_val.base.deref(comp);
328
329 const elem_type = array_val.base.typ.cast(Type.Array).?.key.elem_type;
330 const ptr_type = try await (async Type.Pointer.get(comp, Type.Pointer.Key{
331 .child_type = elem_type,
332 .mut = mut,
333 .vol = Type.Pointer.Vol.Non,
334 .size = size,
335 .alignment = Type.Pointer.Align.Abi,
336 }) catch unreachable);
337 var ptr_type_consumed = false;
338 errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp);
339
340 const self = try comp.gpa().create(Value.Ptr{
341 .base = Value{
342 .id = Value.Id.Ptr,
343 .typ = &ptr_type.base,
344 .ref_count = std.atomic.Int(usize).init(1),
345 },
346 .special = Special{
347 .BaseArray = BaseArray{
348 .val = &array_val.base,
349 .elem_index = 0,
350 },
351 },
352 .mut = Mut.CompTimeConst,
353 });
354 ptr_type_consumed = true;
355 errdefer comp.gpa().destroy(self);
356
357 return self;
358 }
359
172360 pub fn destroy(self: *Ptr, comp: *Compilation) void {
173 comp.a().destroy(self);
361 comp.gpa().destroy(self);
362 }
363
364 pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?llvm.ValueRef {
365 const llvm_type = self.base.typ.getLlvmType(ofile.arena, ofile.context);
366 // TODO carefully port the logic from codegen.cpp:gen_const_val_ptr
367 switch (self.special) {
368 Special.Scalar => |scalar| @panic("TODO"),
369 Special.BaseArray => |base_array| {
370 // TODO put this in one .o file only, and after that, generate extern references to it
371 const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?;
372 const ptr_bit_count = ofile.comp.target_ptr_bits;
373 const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory;
374 const indices = []llvm.ValueRef{
375 llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory,
376 llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory,
377 };
378 return llvm.ConstInBoundsGEP(
379 array_llvm_value,
380 &indices,
381 @intCast(c_uint, indices.len),
382 ) orelse return error.OutOfMemory;
383 },
384 Special.BaseStruct => |base_struct| @panic("TODO"),
385 Special.HardCodedAddr => |addr| @panic("TODO"),
386 Special.Discard => unreachable,
387 }
388 }
389 };
390
391 pub const Array = struct {
392 base: Value,
393 special: Special,
394
395 pub const Special = union(enum) {
396 Undefined,
397 OwnedBuffer: []u8,
398 Explicit: Data,
399 };
400
401 pub const Data = struct {
402 parent: Parent,
403 elements: []*Value,
404 };
405
406 /// Takes ownership of buffer
407 pub async fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array {
408 const u8_type = Type.Int.get_u8(comp);
409 defer u8_type.base.base.deref(comp);
410
411 const array_type = try await (async Type.Array.get(comp, Type.Array.Key{
412 .elem_type = &u8_type.base,
413 .len = buffer.len,
414 }) catch unreachable);
415 errdefer array_type.base.base.deref(comp);
416
417 const self = try comp.gpa().create(Value.Array{
418 .base = Value{
419 .id = Value.Id.Array,
420 .typ = &array_type.base,
421 .ref_count = std.atomic.Int(usize).init(1),
422 },
423 .special = Special{ .OwnedBuffer = buffer },
424 });
425 errdefer comp.gpa().destroy(self);
426
427 return self;
428 }
429
430 pub fn destroy(self: *Array, comp: *Compilation) void {
431 switch (self.special) {
432 Special.Undefined => {},
433 Special.OwnedBuffer => |buf| {
434 comp.gpa().free(buf);
435 },
436 Special.Explicit => {},
437 }
438 comp.gpa().destroy(self);
439 }
440
441 pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?llvm.ValueRef {
442 switch (self.special) {
443 Special.Undefined => {
444 const llvm_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
445 return llvm.GetUndef(llvm_type);
446 },
447 Special.OwnedBuffer => |buf| {
448 const dont_null_terminate = 1;
449 const llvm_str_init = llvm.ConstStringInContext(
450 ofile.context,
451 buf.ptr,
452 @intCast(c_uint, buf.len),
453 dont_null_terminate,
454 ) orelse return error.OutOfMemory;
455 const str_init_type = llvm.TypeOf(llvm_str_init);
456 const global = llvm.AddGlobal(ofile.module, str_init_type, c"") orelse return error.OutOfMemory;
457 llvm.SetInitializer(global, llvm_str_init);
458 llvm.SetLinkage(global, llvm.PrivateLinkage);
459 llvm.SetGlobalConstant(global, 1);
460 llvm.SetUnnamedAddr(global, 1);
461 llvm.SetAlignment(global, llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, str_init_type));
462 return global;
463 },
464 Special.Explicit => @panic("TODO"),
465 }
466
467 //{
468 // uint64_t len = type_entry->data.array.len;
469 // if (const_val->data.x_array.special == ConstArraySpecialUndef) {
470 // return LLVMGetUndef(type_entry->type_ref);
471 // }
472
473 // LLVMValueRef *values = allocate<LLVMValueRef>(len);
474 // LLVMTypeRef element_type_ref = type_entry->data.array.child_type->type_ref;
475 // bool make_unnamed_struct = false;
476 // for (uint64_t i = 0; i < len; i += 1) {
477 // ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i];
478 // LLVMValueRef val = gen_const_val(g, elem_value, "");
479 // values[i] = val;
480 // make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val);
481 // }
482 // if (make_unnamed_struct) {
483 // return LLVMConstStruct(values, len, true);
484 // } else {
485 // return LLVMConstArray(element_type_ref, values, (unsigned)len);
486 // }
487 //}
488 }
489 };
490
491 pub const Int = struct {
492 base: Value,
493 big_int: std.math.big.Int,
494
495 pub fn createFromString(comp: *Compilation, typ: *Type, base: u8, value: []const u8) !*Int {
496 const self = try comp.gpa().create(Value.Int{
497 .base = Value{
498 .id = Value.Id.Int,
499 .typ = typ,
500 .ref_count = std.atomic.Int(usize).init(1),
501 },
502 .big_int = undefined,
503 });
504 typ.base.ref();
505 errdefer comp.gpa().destroy(self);
506
507 self.big_int = try std.math.big.Int.init(comp.gpa());
508 errdefer self.big_int.deinit();
509
510 try self.big_int.setString(base, value);
511
512 return self;
513 }
514
515 pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?llvm.ValueRef {
516 switch (self.base.typ.id) {
517 Type.Id.Int => {
518 const type_ref = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
519 if (self.big_int.len == 0) {
520 return llvm.ConstNull(type_ref);
521 }
522 const unsigned_val = if (self.big_int.len == 1) blk: {
523 break :blk llvm.ConstInt(type_ref, self.big_int.limbs[0], @boolToInt(false));
524 } else if (@sizeOf(std.math.big.Limb) == @sizeOf(u64)) blk: {
525 break :blk llvm.ConstIntOfArbitraryPrecision(
526 type_ref,
527 @intCast(c_uint, self.big_int.len),
528 @ptrCast([*]u64, self.big_int.limbs.ptr),
529 );
530 } else {
531 @compileError("std.math.Big.Int.Limb size does not match LLVM");
532 };
533 return if (self.big_int.positive) unsigned_val else llvm.ConstNeg(unsigned_val);
534 },
535 Type.Id.ComptimeInt => unreachable,
536 else => unreachable,
537 }
538 }
539
540 pub fn copy(old: *Int, comp: *Compilation) !*Int {
541 old.base.typ.base.ref();
542 errdefer old.base.typ.base.deref(comp);
543
544 const new = try comp.gpa().create(Value.Int{
545 .base = Value{
546 .id = Value.Id.Int,
547 .typ = old.base.typ,
548 .ref_count = std.atomic.Int(usize).init(1),
549 },
550 .big_int = undefined,
551 });
552 errdefer comp.gpa().destroy(new);
553
554 new.big_int = try old.big_int.clone();
555 errdefer new.big_int.deinit();
556
557 return new;
558 }
559
560 pub fn destroy(self: *Int, comp: *Compilation) void {
561 self.big_int.deinit();
562 comp.gpa().destroy(self);
174563 }
175564 };
176565};
src/analyze.cpp+6-5
......@@ -1454,7 +1454,9 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {
14541454 case TypeTableEntryIdFn:
14551455 return type_entry->data.fn.fn_type_id.cc == CallingConventionC;
14561456 case TypeTableEntryIdPointer:
1457 return type_allowed_in_extern(g, type_entry->data.pointer.child_type);
1457 if (type_size(g, type_entry) == 0)
1458 return false;
1459 return true;
14581460 case TypeTableEntryIdStruct:
14591461 return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked;
14601462 case TypeTableEntryIdOptional:
......@@ -4377,7 +4379,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
43774379
43784380static ZigWindowsSDK *get_windows_sdk(CodeGen *g) {
43794381 if (g->win_sdk == nullptr) {
4380 if (os_find_windows_sdk(&g->win_sdk)) {
4382 if (zig_find_windows_sdk(&g->win_sdk)) {
43814383 fprintf(stderr, "unable to determine windows sdk path\n");
43824384 exit(1);
43834385 }
......@@ -4497,12 +4499,11 @@ void find_libc_lib_path(CodeGen *g) {
44974499 ZigWindowsSDK *sdk = get_windows_sdk(g);
44984500
44994501 if (g->msvc_lib_dir == nullptr) {
4500 Buf* vc_lib_dir = buf_alloc();
4501 if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) {
4502 if (sdk->msvc_lib_dir_ptr == nullptr) {
45024503 fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir");
45034504 exit(1);
45044505 }
4505 g->msvc_lib_dir = vc_lib_dir;
4506 g->msvc_lib_dir = buf_create_from_mem(sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len);
45064507 }
45074508
45084509 if (g->libc_lib_dir == nullptr) {
src/codegen.cpp+76-36
......@@ -60,6 +60,33 @@ PackageTableEntry *new_anonymous_package(void) {
6060 return new_package("", "");
6161}
6262
63static const char *symbols_that_llvm_depends_on[] = {
64 "memcpy",
65 "memset",
66 "sqrt",
67 "powi",
68 "sin",
69 "cos",
70 "pow",
71 "exp",
72 "exp2",
73 "log",
74 "log10",
75 "log2",
76 "fma",
77 "fabs",
78 "minnum",
79 "maxnum",
80 "copysign",
81 "floor",
82 "ceil",
83 "trunc",
84 "rint",
85 "nearbyint",
86 "round",
87 // TODO probably all of compiler-rt needs to go here
88};
89
6390CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode,
6491 Buf *zig_lib_dir)
6592{
......@@ -94,6 +121,10 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
94121 g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib);
95122 buf_resize(&g->global_asm, 0);
96123
124 for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) {
125 g->external_prototypes.put(buf_create_from_str(symbols_that_llvm_depends_on[i]), nullptr);
126 }
127
97128 if (root_src_path) {
98129 Buf *src_basename = buf_alloc();
99130 Buf *src_dir = buf_alloc();
......@@ -7419,51 +7450,60 @@ static void gen_h_file(CodeGen *g) {
74197450 case TypeTableEntryIdPromise:
74207451 zig_unreachable();
74217452 case TypeTableEntryIdEnum:
7422 assert(type_entry->data.enumeration.layout == ContainerLayoutExtern);
7423 fprintf(out_h, "enum %s {\n", buf_ptr(&type_entry->name));
7424 for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) {
7425 TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i];
7426 Buf *value_buf = buf_alloc();
7427 bigint_append_buf(value_buf, &enum_field->value, 10);
7428 fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf));
7429 if (field_i != type_entry->data.enumeration.src_field_count - 1) {
7430 fprintf(out_h, ",");
7453 if (type_entry->data.enumeration.layout == ContainerLayoutExtern) {
7454 fprintf(out_h, "enum %s {\n", buf_ptr(&type_entry->name));
7455 for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) {
7456 TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i];
7457 Buf *value_buf = buf_alloc();
7458 bigint_append_buf(value_buf, &enum_field->value, 10);
7459 fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf));
7460 if (field_i != type_entry->data.enumeration.src_field_count - 1) {
7461 fprintf(out_h, ",");
7462 }
7463 fprintf(out_h, "\n");
74317464 }
7432 fprintf(out_h, "\n");
7465 fprintf(out_h, "};\n\n");
7466 } else {
7467 fprintf(out_h, "enum %s;\n", buf_ptr(&type_entry->name));
74337468 }
7434 fprintf(out_h, "};\n\n");
74357469 break;
74367470 case TypeTableEntryIdStruct:
7437 assert(type_entry->data.structure.layout == ContainerLayoutExtern);
7438 fprintf(out_h, "struct %s {\n", buf_ptr(&type_entry->name));
7439 for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) {
7440 TypeStructField *struct_field = &type_entry->data.structure.fields[field_i];
7441
7442 Buf *type_name_buf = buf_alloc();
7443 get_c_type(g, gen_h, struct_field->type_entry, type_name_buf);
7444
7445 if (struct_field->type_entry->id == TypeTableEntryIdArray) {
7446 fprintf(out_h, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf),
7447 buf_ptr(struct_field->name),
7448 struct_field->type_entry->data.array.len);
7449 } else {
7450 fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name));
7451 }
7471 if (type_entry->data.structure.layout == ContainerLayoutExtern) {
7472 fprintf(out_h, "struct %s {\n", buf_ptr(&type_entry->name));
7473 for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) {
7474 TypeStructField *struct_field = &type_entry->data.structure.fields[field_i];
7475
7476 Buf *type_name_buf = buf_alloc();
7477 get_c_type(g, gen_h, struct_field->type_entry, type_name_buf);
7478
7479 if (struct_field->type_entry->id == TypeTableEntryIdArray) {
7480 fprintf(out_h, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf),
7481 buf_ptr(struct_field->name),
7482 struct_field->type_entry->data.array.len);
7483 } else {
7484 fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name));
7485 }
74527486
7487 }
7488 fprintf(out_h, "};\n\n");
7489 } else {
7490 fprintf(out_h, "struct %s;\n", buf_ptr(&type_entry->name));
74537491 }
7454 fprintf(out_h, "};\n\n");
74557492 break;
74567493 case TypeTableEntryIdUnion:
7457 assert(type_entry->data.unionation.layout == ContainerLayoutExtern);
7458 fprintf(out_h, "union %s {\n", buf_ptr(&type_entry->name));
7459 for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) {
7460 TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i];
7461
7462 Buf *type_name_buf = buf_alloc();
7463 get_c_type(g, gen_h, union_field->type_entry, type_name_buf);
7464 fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name));
7494 if (type_entry->data.unionation.layout == ContainerLayoutExtern) {
7495 fprintf(out_h, "union %s {\n", buf_ptr(&type_entry->name));
7496 for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) {
7497 TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i];
7498
7499 Buf *type_name_buf = buf_alloc();
7500 get_c_type(g, gen_h, union_field->type_entry, type_name_buf);
7501 fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name));
7502 }
7503 fprintf(out_h, "};\n\n");
7504 } else {
7505 fprintf(out_h, "union %s;\n", buf_ptr(&type_entry->name));
74657506 }
7466 fprintf(out_h, "};\n\n");
74677507 break;
74687508 case TypeTableEntryIdOpaque:
74697509 fprintf(out_h, "struct %s;\n\n", buf_ptr(&type_entry->name));
src/ir.cpp+63-26
......@@ -246,6 +246,8 @@ static void ir_ref_bb(IrBasicBlock *bb) {
246246static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) {
247247 assert(instruction->id != IrInstructionIdInvalid);
248248 instruction->ref_count += 1;
249 if (instruction->owner_bb != cur_bb && !instr_is_comptime(instruction))
250 ir_ref_bb(instruction->owner_bb);
249251}
250252
251253static void ir_ref_var(VariableTableEntry *var) {
......@@ -2959,16 +2961,34 @@ static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_sco
29592961 results[ReturnKindUnconditional] = 0;
29602962 results[ReturnKindError] = 0;
29612963
2962 while (inner_scope != outer_scope) {
2963 assert(inner_scope);
2964 if (inner_scope->id == ScopeIdDefer) {
2965 AstNode *defer_node = inner_scope->source_node;
2966 assert(defer_node->type == NodeTypeDefer);
2967 ReturnKind defer_kind = defer_node->data.defer.kind;
2968 results[defer_kind] += 1;
2964 Scope *scope = inner_scope;
29692965
2966 while (scope != outer_scope) {
2967 assert(scope);
2968 switch (scope->id) {
2969 case ScopeIdDefer: {
2970 AstNode *defer_node = scope->source_node;
2971 assert(defer_node->type == NodeTypeDefer);
2972 ReturnKind defer_kind = defer_node->data.defer.kind;
2973 results[defer_kind] += 1;
2974 scope = scope->parent;
2975 continue;
2976 }
2977 case ScopeIdDecls:
2978 case ScopeIdFnDef:
2979 return;
2980 case ScopeIdBlock:
2981 case ScopeIdVarDecl:
2982 case ScopeIdLoop:
2983 case ScopeIdSuspend:
2984 case ScopeIdCompTime:
2985 scope = scope->parent;
2986 continue;
2987 case ScopeIdDeferExpr:
2988 case ScopeIdCImport:
2989 case ScopeIdCoroPrelude:
2990 zig_unreachable();
29702991 }
2971 inner_scope = inner_scope->parent;
29722992 }
29732993}
29742994
......@@ -2984,27 +3004,43 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o
29843004 if (!scope)
29853005 return is_noreturn;
29863006
2987 if (scope->id == ScopeIdDefer) {
2988 AstNode *defer_node = scope->source_node;
2989 assert(defer_node->type == NodeTypeDefer);
2990 ReturnKind defer_kind = defer_node->data.defer.kind;
2991 if (defer_kind == ReturnKindUnconditional ||
2992 (gen_error_defers && defer_kind == ReturnKindError))
2993 {
2994 AstNode *defer_expr_node = defer_node->data.defer.expr;
2995 Scope *defer_expr_scope = defer_node->data.defer.expr_scope;
2996 IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);
2997 if (defer_expr_value != irb->codegen->invalid_instruction) {
2998 if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) {
2999 is_noreturn = true;
3000 } else {
3001 ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value));
3007 switch (scope->id) {
3008 case ScopeIdDefer: {
3009 AstNode *defer_node = scope->source_node;
3010 assert(defer_node->type == NodeTypeDefer);
3011 ReturnKind defer_kind = defer_node->data.defer.kind;
3012 if (defer_kind == ReturnKindUnconditional ||
3013 (gen_error_defers && defer_kind == ReturnKindError))
3014 {
3015 AstNode *defer_expr_node = defer_node->data.defer.expr;
3016 Scope *defer_expr_scope = defer_node->data.defer.expr_scope;
3017 IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);
3018 if (defer_expr_value != irb->codegen->invalid_instruction) {
3019 if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) {
3020 is_noreturn = true;
3021 } else {
3022 ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value));
3023 }
30023024 }
30033025 }
3026 scope = scope->parent;
3027 continue;
30043028 }
3005
3029 case ScopeIdDecls:
3030 case ScopeIdFnDef:
3031 return is_noreturn;
3032 case ScopeIdBlock:
3033 case ScopeIdVarDecl:
3034 case ScopeIdLoop:
3035 case ScopeIdSuspend:
3036 case ScopeIdCompTime:
3037 scope = scope->parent;
3038 continue;
3039 case ScopeIdDeferExpr:
3040 case ScopeIdCImport:
3041 case ScopeIdCoroPrelude:
3042 zig_unreachable();
30063043 }
3007 scope = scope->parent;
30083044 }
30093045 return is_noreturn;
30103046}
......@@ -9408,7 +9444,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc
94089444 if (type_is_invalid(casted_payload->value.type))
94099445 return ira->codegen->invalid_instruction;
94109446
9411 ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefBad);
9447 ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefOk);
94129448 if (!val)
94139449 return ira->codegen->invalid_instruction;
94149450
......@@ -13090,6 +13126,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1309013126 impl_fn->ir_executable.parent_exec = ira->new_irb.exec;
1309113127 impl_fn->analyzed_executable.source_node = call_instruction->base.source_node;
1309213128 impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec;
13129 impl_fn->analyzed_executable.backward_branch_quota = ira->new_irb.exec->backward_branch_quota;
1309313130 impl_fn->analyzed_executable.is_generic_instantiation = true;
1309413131
1309513132 ira->codegen->fn_defs.append(impl_fn);
src/link.cpp+1-1
......@@ -901,7 +901,7 @@ static void construct_linker_job_macho(LinkJob *lj) {
901901 if (strchr(buf_ptr(link_lib->name), '/') == nullptr) {
902902 Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
903903 lj->args.append(buf_ptr(arg));
904 } else {
904 } else {
905905 lj->args.append(buf_ptr(link_lib->name));
906906 }
907907 }
src/os.cpp+4-244
......@@ -26,7 +26,6 @@
2626#include <windows.h>
2727#include <io.h>
2828#include <fcntl.h>
29#include "windows_com.hpp"
3029
3130typedef SSIZE_T ssize_t;
3231#else
......@@ -1115,249 +1114,10 @@ void os_stderr_set_color(TermColor color) {
11151114#endif
11161115}
11171116
1118int os_find_windows_sdk(ZigWindowsSDK **out_sdk) {
1119#if defined(ZIG_OS_WINDOWS)
1120 ZigWindowsSDK *result_sdk = allocate<ZigWindowsSDK>(1);
1121 buf_resize(&result_sdk->path10, 0);
1122 buf_resize(&result_sdk->path81, 0);
1123
1124 HKEY key;
1125 HRESULT rc;
1126 rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key);
1127 if (rc != ERROR_SUCCESS) {
1128 return ErrorFileNotFound;
1129 }
1130
1131 {
1132 DWORD tmp_buf_len = MAX_PATH;
1133 buf_resize(&result_sdk->path10, tmp_buf_len);
1134 rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path10), &tmp_buf_len);
1135 if (rc == ERROR_FILE_NOT_FOUND) {
1136 buf_resize(&result_sdk->path10, 0);
1137 } else {
1138 buf_resize(&result_sdk->path10, tmp_buf_len);
1139 }
1140 }
1141 {
1142 DWORD tmp_buf_len = MAX_PATH;
1143 buf_resize(&result_sdk->path81, tmp_buf_len);
1144 rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path81), &tmp_buf_len);
1145 if (rc == ERROR_FILE_NOT_FOUND) {
1146 buf_resize(&result_sdk->path81, 0);
1147 } else {
1148 buf_resize(&result_sdk->path81, tmp_buf_len);
1149 }
1150 }
1151
1152 if (buf_len(&result_sdk->path10) != 0) {
1153 Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\*", buf_ptr(&result_sdk->path10));
1154
1155 // enumerate files in sdk path looking for latest version
1156 WIN32_FIND_DATA ffd;
1157 HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd);
1158 if (hFind == INVALID_HANDLE_VALUE) {
1159 return ErrorFileNotFound;
1160 }
1161 int v0 = 0, v1 = 0, v2 = 0, v3 = 0;
1162 bool found_version_dir = false;
1163 for (;;) {
1164 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1165 int c0 = 0, c1 = 0, c2 = 0, c3 = 0;
1166 sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3);
1167 if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) {
1168 // Microsoft released 26624 as 10240 accidentally.
1169 // https://developer.microsoft.com/en-us/windows/downloads/sdk-archive
1170 c2 = 26624;
1171 }
1172 if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) {
1173 v0 = c0, v1 = c1, v2 = c2, v3 = c3;
1174 buf_init_from_str(&result_sdk->version10, ffd.cFileName);
1175 found_version_dir = true;
1176 }
1177 }
1178 if (FindNextFile(hFind, &ffd) == 0) {
1179 FindClose(hFind);
1180 break;
1181 }
1182 }
1183 if (!found_version_dir) {
1184 buf_resize(&result_sdk->path10, 0);
1185 }
1186 }
1187
1188 if (buf_len(&result_sdk->path81) != 0) {
1189 Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\winv*", buf_ptr(&result_sdk->path81));
1190
1191 // enumerate files in sdk path looking for latest version
1192 WIN32_FIND_DATA ffd;
1193 HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd);
1194 if (hFind == INVALID_HANDLE_VALUE) {
1195 return ErrorFileNotFound;
1196 }
1197 int v0 = 0, v1 = 0;
1198 bool found_version_dir = false;
1199 for (;;) {
1200 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1201 int c0 = 0, c1 = 0;
1202 sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1);
1203 if ((c0 > v0) || (c1 > v1)) {
1204 v0 = c0, v1 = c1;
1205 buf_init_from_str(&result_sdk->version81, ffd.cFileName);
1206 found_version_dir = true;
1207 }
1208 }
1209 if (FindNextFile(hFind, &ffd) == 0) {
1210 FindClose(hFind);
1211 break;
1212 }
1213 }
1214 if (!found_version_dir) {
1215 buf_resize(&result_sdk->path81, 0);
1216 }
1217 }
1218
1219 *out_sdk = result_sdk;
1220 return 0;
1221#else
1222 return ErrorFileNotFound;
1223#endif
1224}
1225
1226int os_get_win32_vcruntime_path(Buf* output_buf, ZigLLVM_ArchType platform_type) {
1227#if defined(ZIG_OS_WINDOWS)
1228 buf_resize(output_buf, 0);
1229 //COM Smart Pointerse requires explicit scope
1230 {
1231 HRESULT rc;
1232 rc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
1233 if (rc != S_OK) {
1234 goto com_done;
1235 }
1236
1237 //This COM class is installed when a VS2017
1238 ISetupConfigurationPtr setup_config;
1239 rc = setup_config.CreateInstance(__uuidof(SetupConfiguration));
1240 if (rc != S_OK) {
1241 goto com_done;
1242 }
1243
1244 IEnumSetupInstancesPtr all_instances;
1245 rc = setup_config->EnumInstances(&all_instances);
1246 if (rc != S_OK) {
1247 goto com_done;
1248 }
1249
1250 ISetupInstance* curr_instance;
1251 ULONG found_inst;
1252 while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) {
1253 BSTR bstr_inst_path;
1254 rc = curr_instance->GetInstallationPath(&bstr_inst_path);
1255 if (rc != S_OK) {
1256 goto com_done;
1257 }
1258 //BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length
1259 UINT bstr_path_len = *((UINT*)bstr_inst_path - 1);
1260 ULONG tmp_path_len = bstr_path_len / 2 + 1;
1261 char* conv_path = (char*)bstr_inst_path;
1262 char *tmp_path = (char*)alloca(tmp_path_len);
1263 memset(tmp_path, 0, tmp_path_len);
1264 uint32_t c = 0;
1265 for (uint32_t i = 0; i < bstr_path_len; i += 2) {
1266 tmp_path[c] = conv_path[i];
1267 ++c;
1268 assert(c != tmp_path_len);
1269 }
1270
1271 buf_append_str(output_buf, tmp_path);
1272 buf_append_char(output_buf, '\\');
1273
1274 Buf* tmp_buf = buf_alloc();
1275 buf_append_buf(tmp_buf, output_buf);
1276 buf_append_str(tmp_buf, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
1277 FILE* tools_file = fopen(buf_ptr(tmp_buf), "r");
1278 if (!tools_file) {
1279 goto com_done;
1280 }
1281 memset(tmp_path, 0, tmp_path_len);
1282 fgets(tmp_path, tmp_path_len, tools_file);
1283 strtok(tmp_path, " \r\n");
1284 fclose(tools_file);
1285 buf_appendf(output_buf, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path);
1286 switch (platform_type) {
1287 case ZigLLVM_x86:
1288 buf_append_str(output_buf, "x86\\");
1289 break;
1290 case ZigLLVM_x86_64:
1291 buf_append_str(output_buf, "x64\\");
1292 break;
1293 case ZigLLVM_arm:
1294 buf_append_str(output_buf, "arm\\");
1295 break;
1296 default:
1297 zig_panic("Attemped to use vcruntime for non-supported platform.");
1298 }
1299 buf_resize(tmp_buf, 0);
1300 buf_append_buf(tmp_buf, output_buf);
1301 buf_append_str(tmp_buf, "vcruntime.lib");
1302
1303 if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
1304 return 0;
1305 }
1306 }
1307 }
1308
1309com_done:;
1310 HKEY key;
1311 HRESULT rc;
1312 rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key);
1313 if (rc != ERROR_SUCCESS) {
1314 return ErrorFileNotFound;
1315 }
1316
1317 DWORD dw_type = 0;
1318 DWORD cb_data = 0;
1319 rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data);
1320 if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) {
1321 return ErrorFileNotFound;
1322 }
1323
1324 Buf* tmp_buf = buf_alloc_fixed(cb_data);
1325 RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)buf_ptr(tmp_buf), &cb_data);
1326 //RegQueryValueExA returns the length of the string INCLUDING the null terminator
1327 buf_resize(tmp_buf, cb_data-1);
1328 buf_append_str(tmp_buf, "VC\\Lib\\");
1329 switch (platform_type) {
1330 case ZigLLVM_x86:
1331 //x86 is in the root of the Lib folder
1332 break;
1333 case ZigLLVM_x86_64:
1334 buf_append_str(tmp_buf, "amd64\\");
1335 break;
1336 case ZigLLVM_arm:
1337 buf_append_str(tmp_buf, "arm\\");
1338 break;
1339 default:
1340 zig_panic("Attemped to use vcruntime for non-supported platform.");
1341 }
1342
1343 buf_append_buf(output_buf, tmp_buf);
1344 buf_append_str(tmp_buf, "vcruntime.lib");
1345
1346 if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
1347 return 0;
1348 } else {
1349 buf_resize(output_buf, 0);
1350 return ErrorFileNotFound;
1351 }
1352#else
1353 return ErrorFileNotFound;
1354#endif
1355}
1356
13571117int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) {
13581118#if defined(ZIG_OS_WINDOWS)
13591119 buf_resize(output_buf, 0);
1360 buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10));
1120 buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr);
13611121 switch (platform_type) {
13621122 case ZigLLVM_x86:
13631123 buf_append_str(output_buf, "x86\\");
......@@ -1389,7 +1149,7 @@ int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch
13891149int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) {
13901150#if defined(ZIG_OS_WINDOWS)
13911151 buf_resize(output_buf, 0);
1392 buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10));
1152 buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr);
13931153 if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) {
13941154 return 0;
13951155 }
......@@ -1406,7 +1166,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy
14061166#if defined(ZIG_OS_WINDOWS)
14071167 {
14081168 buf_resize(output_buf, 0);
1409 buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10));
1169 buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr);
14101170 switch (platform_type) {
14111171 case ZigLLVM_x86:
14121172 buf_append_str(output_buf, "x86\\");
......@@ -1429,7 +1189,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy
14291189 }
14301190 {
14311191 buf_resize(output_buf, 0);
1432 buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path81), buf_ptr(&sdk->version81));
1192 buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr);
14331193 switch (platform_type) {
14341194 case ZigLLVM_x86:
14351195 buf_append_str(output_buf, "x86\\");
src/os.hpp+1-9
......@@ -12,6 +12,7 @@
1212#include "buffer.hpp"
1313#include "error.hpp"
1414#include "zig_llvm.h"
15#include "windows_sdk.h"
1516
1617#include <stdio.h>
1718#include <inttypes.h>
......@@ -79,15 +80,6 @@ bool os_is_sep(uint8_t c);
7980
8081int os_self_exe_path(Buf *out_path);
8182
82struct ZigWindowsSDK {
83 Buf path10;
84 Buf version10;
85 Buf path81;
86 Buf version81;
87};
88
89int os_find_windows_sdk(ZigWindowsSDK **out_sdk);
90int os_get_win32_vcruntime_path(Buf *output_buf, ZigLLVM_ArchType platform_type);
9183int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf);
9284int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);
9385int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);
src/tokenizer.cpp+13-8
......@@ -460,16 +460,21 @@ static const char* get_escape_shorthand(uint8_t c) {
460460static void invalid_char_error(Tokenize *t, uint8_t c) {
461461 if (c == '\r') {
462462 tokenize_error(t, "invalid carriage return, only '\\n' line endings are supported");
463 } else if (isprint(c)) {
463 return;
464 }
465
466 const char *sh = get_escape_shorthand(c);
467 if (sh) {
468 tokenize_error(t, "invalid character: '%s'", sh);
469 return;
470 }
471
472 if (isprint(c)) {
464473 tokenize_error(t, "invalid character: '%c'", c);
465 } else {
466 const char *sh = get_escape_shorthand(c);
467 if (sh) {
468 tokenize_error(t, "invalid character: '%s'", sh);
469 } else {
470 tokenize_error(t, "invalid character: '\\x%x'", c);
471 }
474 return;
472475 }
476
477 tokenize_error(t, "invalid character: '\\x%02x'", c);
473478}
474479
475480void tokenize(Buf *buf, Tokenization *out) {
src/windows_sdk.cpp created+352
......@@ -0,0 +1,352 @@
1/*
2 * Copyright (c) 2018 Andrew Kelley
3 *
4 * This file is part of zig, which is MIT licensed.
5 * See http://opensource.org/licenses/MIT
6 */
7
8#include "windows_sdk.h"
9
10#if defined(_WIN32)
11
12#include "windows_com.hpp"
13#include <inttypes.h>
14#include <assert.h>
15
16struct ZigWindowsSDKPrivate {
17 ZigWindowsSDK base;
18};
19
20enum NativeArch {
21 NativeArchArm,
22 NativeArchi386,
23 NativeArchx86_64,
24};
25
26#if defined(_M_ARM) || defined(__arm_)
27static const NativeArch native_arch = NativeArchArm;
28#endif
29#if defined(_M_IX86) || defined(__i386__)
30static const NativeArch native_arch = NativeArchi386;
31#endif
32#if defined(_M_X64) || defined(__x86_64__)
33static const NativeArch native_arch = NativeArchx86_64;
34#endif
35
36void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {
37 if (sdk == nullptr) {
38 return;
39 }
40 free((void*)sdk->path10_ptr);
41 free((void*)sdk->version10_ptr);
42 free((void*)sdk->path81_ptr);
43 free((void*)sdk->version81_ptr);
44 free((void*)sdk->msvc_lib_dir_ptr);
45}
46
47static ZigFindWindowsSdkError find_msvc_lib_dir(ZigWindowsSDKPrivate *priv) {
48 //COM Smart Pointers requires explicit scope
49 {
50 HRESULT rc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
51 if (rc != S_OK && rc != S_FALSE) {
52 goto com_done;
53 }
54
55 //This COM class is installed when a VS2017
56 ISetupConfigurationPtr setup_config;
57 rc = setup_config.CreateInstance(__uuidof(SetupConfiguration));
58 if (rc != S_OK) {
59 goto com_done;
60 }
61
62 IEnumSetupInstancesPtr all_instances;
63 rc = setup_config->EnumInstances(&all_instances);
64 if (rc != S_OK) {
65 goto com_done;
66 }
67
68 ISetupInstance* curr_instance;
69 ULONG found_inst;
70 while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) {
71 BSTR bstr_inst_path;
72 rc = curr_instance->GetInstallationPath(&bstr_inst_path);
73 if (rc != S_OK) {
74 goto com_done;
75 }
76 //BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length
77 //TODO call an actual function to do this
78 UINT bstr_path_len = *((UINT*)bstr_inst_path - 1);
79 ULONG tmp_path_len = bstr_path_len / 2 + 1;
80 char* conv_path = (char*)bstr_inst_path;
81 // TODO don't use alloca
82 char *tmp_path = (char*)alloca(tmp_path_len);
83 memset(tmp_path, 0, tmp_path_len);
84 uint32_t c = 0;
85 for (uint32_t i = 0; i < bstr_path_len; i += 2) {
86 tmp_path[c] = conv_path[i];
87 ++c;
88 assert(c != tmp_path_len);
89 }
90 char output_path[4096];
91 output_path[0] = 0;
92 char *out_append_ptr = output_path;
93
94 out_append_ptr += sprintf(out_append_ptr, "%s\\", tmp_path);
95
96 char tmp_buf[4096];
97 sprintf(tmp_buf, "%s%s", output_path, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
98 FILE* tools_file = fopen(tmp_buf, "rb");
99 if (!tools_file) {
100 goto com_done;
101 }
102 memset(tmp_path, 0, tmp_path_len);
103 fgets(tmp_path, tmp_path_len, tools_file);
104 strtok(tmp_path, " \r\n");
105 fclose(tools_file);
106 out_append_ptr += sprintf(out_append_ptr, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path);
107 switch (native_arch) {
108 case NativeArchi386:
109 out_append_ptr += sprintf(out_append_ptr, "x86\\");
110 break;
111 case NativeArchx86_64:
112 out_append_ptr += sprintf(out_append_ptr, "x64\\");
113 break;
114 case NativeArchArm:
115 out_append_ptr += sprintf(out_append_ptr, "arm\\");
116 break;
117 }
118 sprintf(tmp_buf, "%s%s", output_path, "vcruntime.lib");
119
120 if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) {
121 priv->base.msvc_lib_dir_ptr = strdup(output_path);
122 if (priv->base.msvc_lib_dir_ptr == nullptr) {
123 return ZigFindWindowsSdkErrorOutOfMemory;
124 }
125 priv->base.msvc_lib_dir_len = strlen(priv->base.msvc_lib_dir_ptr);
126 return ZigFindWindowsSdkErrorNone;
127 }
128 }
129 }
130
131com_done:;
132 HKEY key;
133 HRESULT rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0,
134 KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key);
135 if (rc != ERROR_SUCCESS) {
136 return ZigFindWindowsSdkErrorNotFound;
137 }
138
139 DWORD dw_type = 0;
140 DWORD cb_data = 0;
141 rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data);
142 if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) {
143 return ZigFindWindowsSdkErrorNotFound;
144 }
145
146 char tmp_buf[4096];
147
148 RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)tmp_buf, &cb_data);
149 // RegQueryValueExA returns the length of the string INCLUDING the null terminator
150 char *tmp_buf_append_ptr = tmp_buf + (cb_data - 1);
151 tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "VC\\Lib\\");
152 switch (native_arch) {
153 case NativeArchi386:
154 //x86 is in the root of the Lib folder
155 break;
156 case NativeArchx86_64:
157 tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "amd64\\");
158 break;
159 case NativeArchArm:
160 tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "arm\\");
161 break;
162 }
163
164 char *output_path = strdup(tmp_buf);
165 if (output_path == nullptr) {
166 return ZigFindWindowsSdkErrorOutOfMemory;
167 }
168
169 tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "vcruntime.lib");
170
171 if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) {
172 priv->base.msvc_lib_dir_ptr = output_path;
173 priv->base.msvc_lib_dir_len = strlen(output_path);
174 return ZigFindWindowsSdkErrorNone;
175 } else {
176 free(output_path);
177 return ZigFindWindowsSdkErrorNotFound;
178 }
179}
180
181static ZigFindWindowsSdkError find_10_version(ZigWindowsSDKPrivate *priv) {
182 if (priv->base.path10_ptr == nullptr)
183 return ZigFindWindowsSdkErrorNone;
184
185 char sdk_lib_dir[4096];
186 int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\*", priv->base.path10_ptr);
187 if (n < 0 || n >= 4096) {
188 return ZigFindWindowsSdkErrorPathTooLong;
189 }
190
191 // enumerate files in sdk path looking for latest version
192 WIN32_FIND_DATA ffd;
193 HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd);
194 if (hFind == INVALID_HANDLE_VALUE) {
195 return ZigFindWindowsSdkErrorNotFound;
196 }
197 int v0 = 0, v1 = 0, v2 = 0, v3 = 0;
198 for (;;) {
199 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
200 int c0 = 0, c1 = 0, c2 = 0, c3 = 0;
201 sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3);
202 if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) {
203 // Microsoft released 26624 as 10240 accidentally.
204 // https://developer.microsoft.com/en-us/windows/downloads/sdk-archive
205 c2 = 26624;
206 }
207 if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) {
208 v0 = c0, v1 = c1, v2 = c2, v3 = c3;
209 free((void*)priv->base.version10_ptr);
210 priv->base.version10_ptr = strdup(ffd.cFileName);
211 if (priv->base.version10_ptr == nullptr) {
212 FindClose(hFind);
213 return ZigFindWindowsSdkErrorOutOfMemory;
214 }
215 }
216 }
217 if (FindNextFile(hFind, &ffd) == 0) {
218 FindClose(hFind);
219 break;
220 }
221 }
222 priv->base.version10_len = strlen(priv->base.version10_ptr);
223 return ZigFindWindowsSdkErrorNone;
224}
225
226static ZigFindWindowsSdkError find_81_version(ZigWindowsSDKPrivate *priv) {
227 if (priv->base.path81_ptr == nullptr)
228 return ZigFindWindowsSdkErrorNone;
229
230 char sdk_lib_dir[4096];
231 int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\winv*", priv->base.path81_ptr);
232 if (n < 0 || n >= 4096) {
233 return ZigFindWindowsSdkErrorPathTooLong;
234 }
235
236 // enumerate files in sdk path looking for latest version
237 WIN32_FIND_DATA ffd;
238 HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd);
239 if (hFind == INVALID_HANDLE_VALUE) {
240 return ZigFindWindowsSdkErrorNotFound;
241 }
242 int v0 = 0, v1 = 0;
243 for (;;) {
244 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
245 int c0 = 0, c1 = 0;
246 sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1);
247 if ((c0 > v0) || (c1 > v1)) {
248 v0 = c0, v1 = c1;
249 free((void*)priv->base.version81_ptr);
250 priv->base.version81_ptr = strdup(ffd.cFileName);
251 if (priv->base.version81_ptr == nullptr) {
252 FindClose(hFind);
253 return ZigFindWindowsSdkErrorOutOfMemory;
254 }
255 }
256 }
257 if (FindNextFile(hFind, &ffd) == 0) {
258 FindClose(hFind);
259 break;
260 }
261 }
262 priv->base.version81_len = strlen(priv->base.version81_ptr);
263 return ZigFindWindowsSdkErrorNone;
264}
265
266ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) {
267 ZigWindowsSDKPrivate *priv = (ZigWindowsSDKPrivate*)calloc(1, sizeof(ZigWindowsSDKPrivate));
268 if (priv == nullptr) {
269 return ZigFindWindowsSdkErrorOutOfMemory;
270 }
271
272 HKEY key;
273 HRESULT rc;
274 rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0,
275 KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key);
276 if (rc != ERROR_SUCCESS) {
277 zig_free_windows_sdk(&priv->base);
278 return ZigFindWindowsSdkErrorNotFound;
279 }
280
281 {
282 DWORD tmp_buf_len = MAX_PATH;
283 priv->base.path10_ptr = (const char *)calloc(tmp_buf_len, 1);
284 if (priv->base.path10_ptr == nullptr) {
285 zig_free_windows_sdk(&priv->base);
286 return ZigFindWindowsSdkErrorOutOfMemory;
287 }
288 rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)priv->base.path10_ptr, &tmp_buf_len);
289 if (rc == ERROR_SUCCESS) {
290 priv->base.path10_len = tmp_buf_len - 1;
291 if (priv->base.path10_ptr[priv->base.path10_len - 1] == '\\') {
292 priv->base.path10_len -= 1;
293 }
294 } else {
295 free((void*)priv->base.path10_ptr);
296 priv->base.path10_ptr = nullptr;
297 }
298 }
299 {
300 DWORD tmp_buf_len = MAX_PATH;
301 priv->base.path81_ptr = (const char *)calloc(tmp_buf_len, 1);
302 if (priv->base.path81_ptr == nullptr) {
303 zig_free_windows_sdk(&priv->base);
304 return ZigFindWindowsSdkErrorOutOfMemory;
305 }
306 rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)priv->base.path81_ptr, &tmp_buf_len);
307 if (rc == ERROR_SUCCESS) {
308 priv->base.path81_len = tmp_buf_len - 1;
309 if (priv->base.path81_ptr[priv->base.path81_len - 1] == '\\') {
310 priv->base.path81_len -= 1;
311 }
312 } else {
313 free((void*)priv->base.path81_ptr);
314 priv->base.path81_ptr = nullptr;
315 }
316 }
317
318 {
319 ZigFindWindowsSdkError err = find_10_version(priv);
320 if (err == ZigFindWindowsSdkErrorOutOfMemory) {
321 zig_free_windows_sdk(&priv->base);
322 return err;
323 }
324 }
325 {
326 ZigFindWindowsSdkError err = find_81_version(priv);
327 if (err == ZigFindWindowsSdkErrorOutOfMemory) {
328 zig_free_windows_sdk(&priv->base);
329 return err;
330 }
331 }
332
333 {
334 ZigFindWindowsSdkError err = find_msvc_lib_dir(priv);
335 if (err == ZigFindWindowsSdkErrorOutOfMemory) {
336 zig_free_windows_sdk(&priv->base);
337 return err;
338 }
339 }
340
341 *out_sdk = &priv->base;
342 return ZigFindWindowsSdkErrorNone;
343}
344
345#else
346
347void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {}
348ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) {
349 return ZigFindWindowsSdkErrorNotFound;
350}
351
352#endif
src/windows_sdk.h created+47
......@@ -0,0 +1,47 @@
1/*
2 * Copyright (c) 2018 Andrew Kelley
3 *
4 * This file is part of zig, which is MIT licensed.
5 * See http://opensource.org/licenses/MIT
6 */
7
8#ifndef ZIG_WINDOWS_SDK_H
9#define ZIG_WINDOWS_SDK_H
10
11#ifdef __cplusplus
12#define ZIG_EXTERN_C extern "C"
13#else
14#define ZIG_EXTERN_C
15#endif
16
17#include <stddef.h>
18
19struct ZigWindowsSDK {
20 const char *path10_ptr;
21 size_t path10_len;
22
23 const char *version10_ptr;
24 size_t version10_len;
25
26 const char *path81_ptr;
27 size_t path81_len;
28
29 const char *version81_ptr;
30 size_t version81_len;
31
32 const char *msvc_lib_dir_ptr;
33 size_t msvc_lib_dir_len;
34};
35
36enum ZigFindWindowsSdkError {
37 ZigFindWindowsSdkErrorNone,
38 ZigFindWindowsSdkErrorOutOfMemory,
39 ZigFindWindowsSdkErrorNotFound,
40 ZigFindWindowsSdkErrorPathTooLong,
41};
42
43ZIG_EXTERN_C enum ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk);
44
45ZIG_EXTERN_C void zig_free_windows_sdk(struct ZigWindowsSDK *sdk);
46
47#endif
src/zig_llvm.cpp+5
......@@ -455,6 +455,11 @@ ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unreso
455455 return reinterpret_cast<ZigLLVMDIBuilder *>(di_builder);
456456}
457457
458void ZigLLVMDisposeDIBuilder(ZigLLVMDIBuilder *dbuilder) {
459 DIBuilder *di_builder = reinterpret_cast<DIBuilder *>(dbuilder);
460 delete di_builder;
461}
462
458463void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder, int line, int column, ZigLLVMDIScope *scope) {
459464 unwrap(builder)->SetCurrentDebugLocation(DebugLoc::get(
460465 line, column, reinterpret_cast<DIScope*>(scope)));
src/zig_llvm.h+5-1
......@@ -22,6 +22,9 @@
2222#define ZIG_EXTERN_C
2323#endif
2424
25// ATTENTION: If you modify this file, be sure to update the corresponding
26// extern function declarations in the self-hosted compiler.
27
2528struct ZigLLVMDIType;
2629struct ZigLLVMDIBuilder;
2730struct ZigLLVMDICompileUnit;
......@@ -39,7 +42,7 @@ struct ZigLLVMInsertionPoint;
3942ZIG_EXTERN_C void ZigLLVMInitializeLoopStrengthReducePass(LLVMPassRegistryRef R);
4043ZIG_EXTERN_C void ZigLLVMInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R);
4144
42/// Caller must free memory.
45/// Caller must free memory with LLVMDisposeMessage
4346ZIG_EXTERN_C char *ZigLLVMGetHostCPUName(void);
4447ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);
4548
......@@ -145,6 +148,7 @@ ZIG_EXTERN_C unsigned ZigLLVMTag_DW_enumeration_type(void);
145148ZIG_EXTERN_C unsigned ZigLLVMTag_DW_union_type(void);
146149
147150ZIG_EXTERN_C struct ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved);
151ZIG_EXTERN_C void ZigLLVMDisposeDIBuilder(struct ZigLLVMDIBuilder *dbuilder);
148152ZIG_EXTERN_C void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module);
149153ZIG_EXTERN_C void ZigLLVMAddModuleCodeViewFlag(LLVMModuleRef module);
150154
std/array_list.zig+38-11
......@@ -113,13 +113,12 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
113113 return old_item;
114114 }
115115
116 pub fn removeOrError(self: *Self, n: usize) !T {
117 if (n >= self.len) return error.OutOfBounds;
118 if (self.len - 1 == n) return self.pop();
119
120 var old_item = self.at(n);
121 try self.setOrError(n, self.pop());
122 return old_item;
116 /// Removes the element at the specified index and returns it
117 /// or an error.OutOfBounds is returned. If no error then
118 /// the empty slot is filled from the end of the list.
119 pub fn swapRemoveOrError(self: *Self, i: usize) !T {
120 if (i >= self.len) return error.OutOfBounds;
121 return self.swapRemove(i);
123122 }
124123
125124 pub fn appendSlice(self: *Self, items: []align(A) const T) !void {
......@@ -192,7 +191,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
192191 };
193192}
194193
195test "basic ArrayList test" {
194test "std.ArrayList.basic" {
196195 var bytes: [1024]u8 = undefined;
197196 const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
198197
......@@ -279,7 +278,35 @@ test "std.ArrayList.swapRemove" {
279278 assert(list.len == 4);
280279}
281280
282test "iterator ArrayList test" {
281test "std.ArrayList.swapRemoveOrError" {
282 var list = ArrayList(i32).init(debug.global_allocator);
283 defer list.deinit();
284
285 // Test just after initialization
286 assertError(list.swapRemoveOrError(0), error.OutOfBounds);
287
288 // Test after adding one item and remote it
289 try list.append(1);
290 assert((try list.swapRemoveOrError(0)) == 1);
291 assertError(list.swapRemoveOrError(0), error.OutOfBounds);
292
293 // Test after adding two items and remote both
294 try list.append(1);
295 try list.append(2);
296 assert((try list.swapRemoveOrError(1)) == 2);
297 assert((try list.swapRemoveOrError(0)) == 1);
298 assertError(list.swapRemoveOrError(0), error.OutOfBounds);
299
300 // Test out of bounds with one item
301 try list.append(1);
302 assertError(list.swapRemoveOrError(1), error.OutOfBounds);
303
304 // Test out of bounds with two items
305 try list.append(2);
306 assertError(list.swapRemoveOrError(2), error.OutOfBounds);
307}
308
309test "std.ArrayList.iterator" {
283310 var list = ArrayList(i32).init(debug.global_allocator);
284311 defer list.deinit();
285312
......@@ -308,7 +335,7 @@ test "iterator ArrayList test" {
308335 assert(it.next().? == 1);
309336}
310337
311test "insert ArrayList test" {
338test "std.ArrayList.insert" {
312339 var list = ArrayList(i32).init(debug.global_allocator);
313340 defer list.deinit();
314341
......@@ -322,7 +349,7 @@ test "insert ArrayList test" {
322349 assert(list.items[3] == 3);
323350}
324351
325test "insertSlice ArrayList test" {
352test "std.ArrayList.insertSlice" {
326353 var list = ArrayList(i32).init(debug.global_allocator);
327354 defer list.deinit();
328355
std/atomic/int.zig+4
......@@ -25,5 +25,9 @@ pub fn Int(comptime T: type) type {
2525 pub fn get(self: *Self) T {
2626 return @atomicLoad(T, &self.unprotected_value, AtomicOrder.SeqCst);
2727 }
28
29 pub fn xchg(self: *Self, new_value: T) T {
30 return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Xchg, new_value, AtomicOrder.SeqCst);
31 }
2832 };
2933}
std/atomic/queue.zig+14
......@@ -51,6 +51,20 @@ pub fn Queue(comptime T: type) type {
5151 return head;
5252 }
5353
54 pub fn unget(self: *Self, node: *Node) void {
55 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
56 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
57
58 const opt_head = self.head;
59 self.head = node;
60 if (opt_head) |head| {
61 head.next = node;
62 } else {
63 assert(self.tail == null);
64 self.tail = node;
65 }
66 }
67
5468 pub fn isEmpty(self: *Self) bool {
5569 return @atomicLoad(?*Node, &self.head, builtin.AtomicOrder.SeqCst) != null;
5670 }
std/buffer.zig+13
......@@ -54,6 +54,19 @@ pub const Buffer = struct {
5454 return result;
5555 }
5656
57 pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: ...) !Buffer {
58 const countSize = struct {
59 fn countSize(size: *usize, bytes: []const u8) (error{}!void) {
60 size.* += bytes.len;
61 }
62 }.countSize;
63 var size: usize = 0;
64 std.fmt.format(&size, error{}, countSize, format, args) catch |err| switch (err) {};
65 var self = try Buffer.initSize(allocator, size);
66 assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size);
67 return self;
68 }
69
5770 pub fn deinit(self: *Buffer) void {
5871 self.list.deinit();
5972 }
std/c/darwin.zig+1-1
......@@ -30,7 +30,7 @@ pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlen
3030pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;
3131pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int;
3232
33pub use @import("../os/darwin_errno.zig");
33pub use @import("../os/darwin/errno.zig");
3434
3535pub const _errno = __error;
3636
std/dwarf.zig+37
......@@ -639,3 +639,40 @@ pub const LNE_define_file = 0x03;
639639pub const LNE_set_discriminator = 0x04;
640640pub const LNE_lo_user = 0x80;
641641pub const LNE_hi_user = 0xff;
642
643pub const LANG_C89 = 0x0001;
644pub const LANG_C = 0x0002;
645pub const LANG_Ada83 = 0x0003;
646pub const LANG_C_plus_plus = 0x0004;
647pub const LANG_Cobol74 = 0x0005;
648pub const LANG_Cobol85 = 0x0006;
649pub const LANG_Fortran77 = 0x0007;
650pub const LANG_Fortran90 = 0x0008;
651pub const LANG_Pascal83 = 0x0009;
652pub const LANG_Modula2 = 0x000a;
653pub const LANG_Java = 0x000b;
654pub const LANG_C99 = 0x000c;
655pub const LANG_Ada95 = 0x000d;
656pub const LANG_Fortran95 = 0x000e;
657pub const LANG_PLI = 0x000f;
658pub const LANG_ObjC = 0x0010;
659pub const LANG_ObjC_plus_plus = 0x0011;
660pub const LANG_UPC = 0x0012;
661pub const LANG_D = 0x0013;
662pub const LANG_Python = 0x0014;
663pub const LANG_Go = 0x0016;
664pub const LANG_C_plus_plus_11 = 0x001a;
665pub const LANG_Rust = 0x001c;
666pub const LANG_C11 = 0x001d;
667pub const LANG_C_plus_plus_14 = 0x0021;
668pub const LANG_Fortran03 = 0x0022;
669pub const LANG_Fortran08 = 0x0023;
670pub const LANG_lo_user = 0x8000;
671pub const LANG_hi_user = 0xffff;
672pub const LANG_Mips_Assembler = 0x8001;
673pub const LANG_Upc = 0x8765;
674pub const LANG_HP_Bliss = 0x8003;
675pub const LANG_HP_Basic91 = 0x8004;
676pub const LANG_HP_Pascal91 = 0x8005;
677pub const LANG_HP_IMacro = 0x8006;
678pub const LANG_HP_Assembler = 0x8007;
std/event/future.zig+31-8
......@@ -6,15 +6,20 @@ const AtomicOrder = builtin.AtomicOrder;
66const Lock = std.event.Lock;
77const Loop = std.event.Loop;
88
9/// This is a value that starts out unavailable, until a value is put().
9/// This is a value that starts out unavailable, until resolve() is called
1010/// While it is unavailable, coroutines suspend when they try to get() it,
11/// and then are resumed when the value is put().
12/// At this point the value remains forever available, and another put() is not allowed.
11/// and then are resumed when resolve() is called.
12/// At this point the value remains forever available, and another resolve() is not allowed.
1313pub fn Future(comptime T: type) type {
1414 return struct {
1515 lock: Lock,
1616 data: T,
17 available: u8, // TODO make this a bool
17
18 /// TODO make this an enum
19 /// 0 - not started
20 /// 1 - started
21 /// 2 - finished
22 available: u8,
1823
1924 const Self = this;
2025 const Queue = std.atomic.Queue(promise);
......@@ -31,7 +36,7 @@ pub fn Future(comptime T: type) type {
3136 /// available.
3237 /// Thread-safe.
3338 pub async fn get(self: *Self) *T {
34 if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) {
39 if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) {
3540 return &self.data;
3641 }
3742 const held = await (async self.lock.acquire() catch unreachable);
......@@ -43,18 +48,36 @@ pub fn Future(comptime T: type) type {
4348 /// Gets the data without waiting for it. If it's available, a pointer is
4449 /// returned. Otherwise, null is returned.
4550 pub fn getOrNull(self: *Self) ?*T {
46 if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) {
51 if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) {
4752 return &self.data;
4853 } else {
4954 return null;
5055 }
5156 }
5257
58 /// If someone else has started working on the data, wait for them to complete
59 /// and return a pointer to the data. Otherwise, return null, and the caller
60 /// should start working on the data.
61 /// It's not required to call start() before resolve() but it can be useful since
62 /// this method is thread-safe.
63 pub async fn start(self: *Self) ?*T {
64 const state = @cmpxchgStrong(u8, &self.available, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null;
65 switch (state) {
66 1 => {
67 const held = await (async self.lock.acquire() catch unreachable);
68 held.release();
69 return &self.data;
70 },
71 2 => return &self.data,
72 else => unreachable,
73 }
74 }
75
5376 /// Make the data become available. May be called only once.
5477 /// Before calling this, modify the `data` property.
5578 pub fn resolve(self: *Self) void {
56 const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
57 assert(prev == 0); // put() called twice
79 const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst);
80 assert(prev == 0 or prev == 1); // resolve() called twice
5881 Lock.Held.release(Lock.Held{ .lock = &self.lock });
5982 }
6083 };
std/event/group.zig+14-2
......@@ -6,7 +6,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
66const AtomicOrder = builtin.AtomicOrder;
77const assert = std.debug.assert;
88
9/// ReturnType should be `void` or `E!void`
9/// ReturnType must be `void` or `E!void`
1010pub fn Group(comptime ReturnType: type) type {
1111 return struct {
1212 coro_stack: Stack,
......@@ -38,8 +38,17 @@ pub fn Group(comptime ReturnType: type) type {
3838 self.alloc_stack.push(node);
3939 }
4040
41 /// Add a node to the group. Thread-safe. Cannot fail.
42 /// `node.data` should be the promise handle to add to the group.
43 /// The node's memory should be in the coroutine frame of
44 /// the handle that is in the node, or somewhere guaranteed to live
45 /// at least as long.
46 pub fn addNode(self: *Self, node: *Stack.Node) void {
47 self.coro_stack.push(node);
48 }
49
4150 /// This is equivalent to an async call, but the async function is added to the group, instead
42 /// of returning a promise. func must be async and have return type void.
51 /// of returning a promise. func must be async and have return type ReturnType.
4352 /// Thread-safe.
4453 pub fn call(self: *Self, comptime func: var, args: ...) (error{OutOfMemory}!void) {
4554 const S = struct {
......@@ -67,6 +76,7 @@ pub fn Group(comptime ReturnType: type) type {
6776
6877 /// Wait for all the calls and promises of the group to complete.
6978 /// Thread-safe.
79 /// Safe to call any number of times.
7080 pub async fn wait(self: *Self) ReturnType {
7181 // TODO catch unreachable because the allocation can be grouped with
7282 // the coro frame allocation
......@@ -98,6 +108,8 @@ pub fn Group(comptime ReturnType: type) type {
98108 }
99109
100110 /// Cancel all the outstanding promises. May only be called if wait was never called.
111 /// TODO These should be `cancelasync` not `cancel`.
112 /// See https://github.com/ziglang/zig/issues/1261
101113 pub fn cancelAll(self: *Self) void {
102114 while (self.coro_stack.pop()) |node| {
103115 cancel node.data;
std/event/loop.zig+100-109
......@@ -12,7 +12,6 @@ pub const Loop = struct {
1212 next_tick_queue: std.atomic.Queue(promise),
1313 os_data: OsData,
1414 final_resume_node: ResumeNode,
15 dispatch_lock: u8, // TODO make this a bool
1615 pending_event_count: usize,
1716 extra_threads: []*std.os.Thread,
1817
......@@ -74,11 +73,10 @@ pub const Loop = struct {
7473 /// max(thread_count - 1, 0)
7574 fn initInternal(self: *Loop, allocator: *mem.Allocator, thread_count: usize) !void {
7675 self.* = Loop{
77 .pending_event_count = 0,
76 .pending_event_count = 1,
7877 .allocator = allocator,
7978 .os_data = undefined,
8079 .next_tick_queue = std.atomic.Queue(promise).init(),
81 .dispatch_lock = 1, // start locked so threads go directly into epoll wait
8280 .extra_threads = undefined,
8381 .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(),
8482 .eventfd_resume_nodes = undefined,
......@@ -235,8 +233,6 @@ pub const Loop = struct {
235233 }
236234 },
237235 builtin.Os.windows => {
238 self.os_data.extra_thread_count = extra_thread_count;
239
240236 self.os_data.io_port = try std.os.windowsCreateIoCompletionPort(
241237 windows.INVALID_HANDLE_VALUE,
242238 null,
......@@ -306,7 +302,7 @@ pub const Loop = struct {
306302 pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void {
307303 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
308304 errdefer {
309 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
305 self.finishOneEvent();
310306 }
311307 try self.modFd(
312308 fd,
......@@ -326,7 +322,7 @@ pub const Loop = struct {
326322
327323 pub fn removeFd(self: *Loop, fd: i32) void {
328324 self.removeFdNoCounter(fd);
329 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
325 self.finishOneEvent();
330326 }
331327
332328 fn removeFdNoCounter(self: *Loop, fd: i32) void {
......@@ -345,14 +341,70 @@ pub const Loop = struct {
345341 }
346342 }
347343
344 fn dispatch(self: *Loop) void {
345 while (self.available_eventfd_resume_nodes.pop()) |resume_stack_node| {
346 const next_tick_node = self.next_tick_queue.get() orelse {
347 self.available_eventfd_resume_nodes.push(resume_stack_node);
348 return;
349 };
350 const eventfd_node = &resume_stack_node.data;
351 eventfd_node.base.handle = next_tick_node.data;
352 switch (builtin.os) {
353 builtin.Os.macosx => {
354 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent);
355 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
356 _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch {
357 self.next_tick_queue.unget(next_tick_node);
358 self.available_eventfd_resume_nodes.push(resume_stack_node);
359 return;
360 };
361 },
362 builtin.Os.linux => {
363 // the pending count is already accounted for
364 const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT |
365 std.os.linux.EPOLLET;
366 self.modFd(
367 eventfd_node.eventfd,
368 eventfd_node.epoll_op,
369 epoll_events,
370 &eventfd_node.base,
371 ) catch {
372 self.next_tick_queue.unget(next_tick_node);
373 self.available_eventfd_resume_nodes.push(resume_stack_node);
374 return;
375 };
376 },
377 builtin.Os.windows => {
378 // this value is never dereferenced but we need it to be non-null so that
379 // the consumer code can decide whether to read the completion key.
380 // it has to do this for normal I/O, so we match that behavior here.
381 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
382 std.os.windowsPostQueuedCompletionStatus(
383 self.os_data.io_port,
384 undefined,
385 eventfd_node.completion_key,
386 overlapped,
387 ) catch {
388 self.next_tick_queue.unget(next_tick_node);
389 self.available_eventfd_resume_nodes.push(resume_stack_node);
390 return;
391 };
392 },
393 else => @compileError("unsupported OS"),
394 }
395 }
396 }
397
348398 /// Bring your own linked list node. This means it can't fail.
349399 pub fn onNextTick(self: *Loop, node: *NextTickNode) void {
350400 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
351401 self.next_tick_queue.put(node);
402 self.dispatch();
352403 }
353404
354405 pub fn run(self: *Loop) void {
355 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
406 self.finishOneEvent(); // the reference we start with
407
356408 self.workerRun();
357409 for (self.extra_threads) |extra_thread| {
358410 extra_thread.wait();
......@@ -392,110 +444,49 @@ pub const Loop = struct {
392444 .next = undefined,
393445 .data = p,
394446 };
395 loop.onNextTick(&my_tick_node);
447 self.onNextTick(&my_tick_node);
396448 }
397449 }
398450
399 fn workerRun(self: *Loop) void {
400 start_over: while (true) {
401 if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {
402 while (self.next_tick_queue.get()) |next_tick_node| {
403 const handle = next_tick_node.data;
404 if (self.next_tick_queue.isEmpty()) {
405 // last node, just resume it
406 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
407 resume handle;
408 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
409 continue :start_over;
410 }
411
412 // non-last node, stick it in the epoll/kqueue set so that
413 // other threads can get to it
414 if (self.available_eventfd_resume_nodes.pop()) |resume_stack_node| {
415 const eventfd_node = &resume_stack_node.data;
416 eventfd_node.base.handle = handle;
417 switch (builtin.os) {
418 builtin.Os.macosx => {
419 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent);
420 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
421 _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch {
422 // fine, we didn't need it anyway
423 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
424 self.available_eventfd_resume_nodes.push(resume_stack_node);
425 resume handle;
426 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
427 continue :start_over;
428 };
429 },
430 builtin.Os.linux => {
431 // the pending count is already accounted for
432 const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET;
433 self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch {
434 // fine, we didn't need it anyway
435 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
436 self.available_eventfd_resume_nodes.push(resume_stack_node);
437 resume handle;
438 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
439 continue :start_over;
440 };
441 },
442 builtin.Os.windows => {
443 // this value is never dereferenced but we need it to be non-null so that
444 // the consumer code can decide whether to read the completion key.
445 // it has to do this for normal I/O, so we match that behavior here.
446 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
447 std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, eventfd_node.completion_key, overlapped) catch {
448 // fine, we didn't need it anyway
449 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
450 self.available_eventfd_resume_nodes.push(resume_stack_node);
451 resume handle;
452 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
453 continue :start_over;
454 };
455 },
456 else => @compileError("unsupported OS"),
451 fn finishOneEvent(self: *Loop) void {
452 if (@atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst) == 1) {
453 // cause all the threads to stop
454 switch (builtin.os) {
455 builtin.Os.linux => {
456 // writing 8 bytes to an eventfd cannot fail
457 std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
458 return;
459 },
460 builtin.Os.macosx => {
461 const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent);
462 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
463 // cannot fail because we already added it and this just enables it
464 _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable;
465 return;
466 },
467 builtin.Os.windows => {
468 var i: usize = 0;
469 while (i < self.extra_threads.len + 1) : (i += 1) {
470 while (true) {
471 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
472 std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
473 break;
457474 }
458 } else {
459 // threads are too busy, can't add another eventfd to wake one up
460 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
461 resume handle;
462 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
463 continue :start_over;
464475 }
465 }
466
467 const pending_event_count = @atomicLoad(usize, &self.pending_event_count, AtomicOrder.SeqCst);
468 if (pending_event_count == 0) {
469 // cause all the threads to stop
470 switch (builtin.os) {
471 builtin.Os.linux => {
472 // writing 8 bytes to an eventfd cannot fail
473 std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
474 return;
475 },
476 builtin.Os.macosx => {
477 const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent);
478 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
479 // cannot fail because we already added it and this just enables it
480 _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable;
481 return;
482 },
483 builtin.Os.windows => {
484 var i: usize = 0;
485 while (i < self.os_data.extra_thread_count) : (i += 1) {
486 while (true) {
487 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
488 std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
489 break;
490 }
491 }
492 return;
493 },
494 else => @compileError("unsupported OS"),
495 }
496 }
476 return;
477 },
478 else => @compileError("unsupported OS"),
479 }
480 }
481 }
497482
498 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
483 fn workerRun(self: *Loop) void {
484 while (true) {
485 while (true) {
486 const next_tick_node = self.next_tick_queue.get() orelse break;
487 self.dispatch();
488 resume next_tick_node.data;
489 self.finishOneEvent();
499490 }
500491
501492 switch (builtin.os) {
......@@ -519,7 +510,7 @@ pub const Loop = struct {
519510 }
520511 resume handle;
521512 if (resume_node_id == ResumeNode.Id.EventFd) {
522 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
513 self.finishOneEvent();
523514 }
524515 }
525516 },
......@@ -541,7 +532,7 @@ pub const Loop = struct {
541532 }
542533 resume handle;
543534 if (resume_node_id == ResumeNode.Id.EventFd) {
544 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
535 self.finishOneEvent();
545536 }
546537 }
547538 },
......@@ -570,7 +561,7 @@ pub const Loop = struct {
570561 }
571562 resume handle;
572563 if (resume_node_id == ResumeNode.Id.EventFd) {
573 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
564 self.finishOneEvent();
574565 }
575566 },
576567 else => @compileError("unsupported OS"),
std/event/tcp.zig+2-1
......@@ -125,8 +125,9 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File
125125test "listen on a port, send bytes, receive bytes" {
126126 if (builtin.os != builtin.Os.linux) {
127127 // TODO build abstractions for other operating systems
128 return;
128 return error.SkipZigTest;
129129 }
130
130131 const MyServer = struct {
131132 tcp_server: Server,
132133
std/fmt/index.zig+6-2
......@@ -785,11 +785,15 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 {
785785 return buf[0 .. buf.len - context.remaining.len];
786786}
787787
788pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 {
788pub const AllocPrintError = error{OutOfMemory};
789
790pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) AllocPrintError![]u8 {
789791 var size: usize = 0;
790792 format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {};
791793 const buf = try allocator.alloc(u8, size);
792 return bufPrint(buf, fmt, args);
794 return bufPrint(buf, fmt, args) catch |err| switch (err) {
795 error.BufferTooSmall => unreachable, // we just counted the size above
796 };
793797}
794798
795799fn countSize(size: *usize, bytes: []const u8) (error{}!void) {
std/index.zig+3
......@@ -36,6 +36,8 @@ pub const sort = @import("sort.zig");
3636pub const unicode = @import("unicode.zig");
3737pub const zig = @import("zig/index.zig");
3838
39pub const lazyInit = @import("lazy_init.zig").lazyInit;
40
3941test "std" {
4042 // run tests from these
4143 _ = @import("atomic/index.zig");
......@@ -71,4 +73,5 @@ test "std" {
7173 _ = @import("sort.zig");
7274 _ = @import("unicode.zig");
7375 _ = @import("zig/index.zig");
76 _ = @import("lazy_init.zig");
7477}
std/lazy_init.zig created+85
......@@ -0,0 +1,85 @@
1const std = @import("index.zig");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4const AtomicRmwOp = builtin.AtomicRmwOp;
5const AtomicOrder = builtin.AtomicOrder;
6
7/// Thread-safe initialization of global data.
8/// TODO use a mutex instead of a spinlock
9pub fn lazyInit(comptime T: type) LazyInit(T) {
10 return LazyInit(T){
11 .data = undefined,
12 .state = 0,
13 };
14}
15
16fn LazyInit(comptime T: type) type {
17 return struct {
18 state: u8, // TODO make this an enum
19 data: Data,
20
21 const Self = this;
22
23 // TODO this isn't working for void, investigate and then remove this special case
24 const Data = if (@sizeOf(T) == 0) u8 else T;
25 const Ptr = if (T == void) void else *T;
26
27 /// Returns a usable pointer to the initialized data,
28 /// or returns null, indicating that the caller should
29 /// perform the initialization and then call resolve().
30 pub fn get(self: *Self) ?Ptr {
31 while (true) {
32 var state = @cmpxchgWeak(u8, &self.state, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null;
33 switch (state) {
34 0 => continue,
35 1 => {
36 // TODO mutex instead of a spinlock
37 continue;
38 },
39 2 => {
40 if (@sizeOf(T) == 0) {
41 return T(undefined);
42 } else {
43 return &self.data;
44 }
45 },
46 else => unreachable,
47 }
48 }
49 }
50
51 pub fn resolve(self: *Self) void {
52 const prev = @atomicRmw(u8, &self.state, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst);
53 assert(prev == 1); // resolve() called twice
54 }
55 };
56}
57
58var global_number = lazyInit(i32);
59
60test "std.lazyInit" {
61 if (global_number.get()) |_| @panic("bad") else {
62 global_number.data = 1234;
63 global_number.resolve();
64 }
65 if (global_number.get()) |x| {
66 assert(x.* == 1234);
67 } else {
68 @panic("bad");
69 }
70 if (global_number.get()) |x| {
71 assert(x.* == 1234);
72 } else {
73 @panic("bad");
74 }
75}
76
77var global_void = lazyInit(void);
78
79test "std.lazyInit(void)" {
80 if (global_void.get()) |_| @panic("bad") else {
81 global_void.resolve();
82 }
83 assert(global_void.get() != null);
84 assert(global_void.get() != null);
85}
std/macho.zig+1-1
......@@ -141,7 +141,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable
141141 }
142142
143143 // Effectively a no-op, lld emits symbols in ascending order.
144 std.sort.insertionSort(Symbol, symbols[0..nsyms], Symbol.addressLessThan);
144 std.sort.sort(Symbol, symbols[0..nsyms], Symbol.addressLessThan);
145145
146146 // Insert the sentinel. Since we don't know where the last function ends,
147147 // we arbitrarily limit it to the start address + 4 KB.
std/math/big/int.zig+152-13
......@@ -60,8 +60,9 @@ pub const Int = struct {
6060 self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity);
6161 }
6262
63 pub fn deinit(self: Int) void {
63 pub fn deinit(self: *Int) void {
6464 self.allocator.free(self.limbs);
65 self.* = undefined;
6566 }
6667
6768 pub fn clone(other: Int) !Int {
......@@ -115,13 +116,63 @@ pub const Int = struct {
115116 return !r.isOdd();
116117 }
117118
118 fn bitcount(self: Int) usize {
119 const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1]));
120 return usize(@boolToInt(!self.positive)) + u_bit_count;
119 // Returns the number of bits required to represent the absolute value of self.
120 fn bitCountAbs(self: Int) usize {
121 return (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1]));
121122 }
122123
124 // Returns the number of bits required to represent the integer in twos-complement form.
125 //
126 // If the integer is negative the value returned is the number of bits needed by a signed
127 // integer to represent the value. If positive the value is the number of bits for an
128 // unsigned integer. Any unsigned integer will fit in the signed integer with bitcount
129 // one greater than the returned value.
130 //
131 // e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7.
132 fn bitCountTwosComp(self: Int) usize {
133 var bits = self.bitCountAbs();
134
135 // If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos
136 // complement requires one less bit.
137 if (!self.positive) block: {
138 bits += 1;
139
140 if (@popCount(self.limbs[self.len - 1]) == 1) {
141 for (self.limbs[0 .. self.len - 1]) |limb| {
142 if (@popCount(limb) != 0) {
143 break :block;
144 }
145 }
146
147 bits -= 1;
148 }
149 }
150
151 return bits;
152 }
153
154 pub fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool {
155 if (self.eqZero()) {
156 return true;
157 }
158 if (!is_signed and !self.positive) {
159 return false;
160 }
161
162 const req_bits = self.bitCountTwosComp() + @boolToInt(self.positive and is_signed);
163 return bit_count >= req_bits;
164 }
165
166 pub fn fits(self: Int, comptime T: type) bool {
167 return self.fitsInTwosComp(T.is_signed, T.bit_count);
168 }
169
170 // Returns the approximate size of the integer in the given base. Negative values accomodate for
171 // the minus sign. This is used for determining the number of characters needed to print the
172 // value. It is inexact and will exceed the given value by 1-2 digits.
123173 pub fn sizeInBase(self: Int, base: usize) usize {
124 return (self.bitcount() / math.log2(base)) + 1;
174 const bit_count = usize(@boolToInt(!self.positive)) + self.bitCountAbs();
175 return (bit_count / math.log2(base)) + 1;
125176 }
126177
127178 pub fn set(self: *Int, value: var) Allocator.Error!void {
......@@ -189,9 +240,9 @@ pub const Int = struct {
189240 pub fn to(self: Int, comptime T: type) ConvertError!T {
190241 switch (@typeId(T)) {
191242 TypeId.Int => {
192 const UT = if (T.is_signed) @IntType(false, T.bit_count - 1) else T;
243 const UT = @IntType(false, T.bit_count);
193244
194 if (self.bitcount() > 8 * @sizeOf(UT)) {
245 if (self.bitCountTwosComp() > T.bit_count) {
195246 return error.TargetTooSmall;
196247 }
197248
......@@ -208,9 +259,17 @@ pub const Int = struct {
208259 }
209260
210261 if (!T.is_signed) {
211 return if (self.positive) r else error.NegativeIntoUnsigned;
262 return if (self.positive) @intCast(T, r) else error.NegativeIntoUnsigned;
212263 } else {
213 return if (self.positive) @intCast(T, r) else -@intCast(T, r);
264 if (self.positive) {
265 return @intCast(T, r);
266 } else {
267 if (math.cast(T, r)) |ok| {
268 return -ok;
269 } else |_| {
270 return @minValue(T);
271 }
272 }
214273 }
215274 },
216275 else => {
......@@ -274,6 +333,7 @@ pub const Int = struct {
274333 self.positive = positive;
275334 }
276335
336 /// TODO make this call format instead of the other way around
277337 pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 {
278338 if (base < 2 or base > 16) {
279339 return error.InvalidBase;
......@@ -356,6 +416,21 @@ pub const Int = struct {
356416 return s;
357417 }
358418
419 /// for the std lib format function
420 /// TODO make this non-allocating
421 pub fn format(
422 self: Int,
423 comptime fmt: []const u8,
424 context: var,
425 comptime FmtError: type,
426 output: fn (@typeOf(context), []const u8) FmtError!void,
427 ) FmtError!void {
428 // TODO look at fmt and support other bases
429 const str = self.toString(self.allocator, 10) catch @panic("TODO make this non allocating");
430 defer self.allocator.free(str);
431 return output(context, str);
432 }
433
359434 // returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.
360435 pub fn cmpAbs(a: Int, b: Int) i8 {
361436 if (a.len < b.len) {
......@@ -1120,24 +1195,88 @@ test "big.int bitcount + sizeInBase" {
11201195 var a = try Int.init(al);
11211196
11221197 try a.set(0b100);
1123 debug.assert(a.bitcount() == 3);
1198 debug.assert(a.bitCountAbs() == 3);
11241199 debug.assert(a.sizeInBase(2) >= 3);
11251200 debug.assert(a.sizeInBase(10) >= 1);
11261201
1202 a.negate();
1203 debug.assert(a.bitCountAbs() == 3);
1204 debug.assert(a.sizeInBase(2) >= 4);
1205 debug.assert(a.sizeInBase(10) >= 2);
1206
11271207 try a.set(0xffffffff);
1128 debug.assert(a.bitcount() == 32);
1208 debug.assert(a.bitCountAbs() == 32);
11291209 debug.assert(a.sizeInBase(2) >= 32);
11301210 debug.assert(a.sizeInBase(10) >= 10);
11311211
11321212 try a.shiftLeft(a, 5000);
1133 debug.assert(a.bitcount() == 5032);
1213 debug.assert(a.bitCountAbs() == 5032);
11341214 debug.assert(a.sizeInBase(2) >= 5032);
11351215 a.positive = false;
11361216
1137 debug.assert(a.bitcount() == 5033);
1217 debug.assert(a.bitCountAbs() == 5032);
11381218 debug.assert(a.sizeInBase(2) >= 5033);
11391219}
11401220
1221test "big.int bitcount/to" {
1222 var a = try Int.init(al);
1223
1224 try a.set(0);
1225 debug.assert(a.bitCountTwosComp() == 0);
1226
1227 // TODO: stack smashing
1228 // debug.assert((try a.to(u0)) == 0);
1229 // TODO: sigsegv
1230 // debug.assert((try a.to(i0)) == 0);
1231
1232 try a.set(-1);
1233 debug.assert(a.bitCountTwosComp() == 1);
1234 debug.assert((try a.to(i1)) == -1);
1235
1236 try a.set(-8);
1237 debug.assert(a.bitCountTwosComp() == 4);
1238 debug.assert((try a.to(i4)) == -8);
1239
1240 try a.set(127);
1241 debug.assert(a.bitCountTwosComp() == 7);
1242 debug.assert((try a.to(u7)) == 127);
1243
1244 try a.set(-128);
1245 debug.assert(a.bitCountTwosComp() == 8);
1246 debug.assert((try a.to(i8)) == -128);
1247
1248 try a.set(-129);
1249 debug.assert(a.bitCountTwosComp() == 9);
1250 debug.assert((try a.to(i9)) == -129);
1251}
1252
1253test "big.int fits" {
1254 var a = try Int.init(al);
1255
1256 try a.set(0);
1257 debug.assert(a.fits(u0));
1258 debug.assert(a.fits(i0));
1259
1260 try a.set(255);
1261 debug.assert(!a.fits(u0));
1262 debug.assert(!a.fits(u1));
1263 debug.assert(!a.fits(i8));
1264 debug.assert(a.fits(u8));
1265 debug.assert(a.fits(u9));
1266 debug.assert(a.fits(i9));
1267
1268 try a.set(-128);
1269 debug.assert(!a.fits(i7));
1270 debug.assert(a.fits(i8));
1271 debug.assert(a.fits(i9));
1272 debug.assert(!a.fits(u9));
1273
1274 try a.set(0x1ffffffffeeeeeeee);
1275 debug.assert(!a.fits(u32));
1276 debug.assert(!a.fits(u64));
1277 debug.assert(a.fits(u65));
1278}
1279
11411280test "big.int string set" {
11421281 var a = try Int.init(al);
11431282 try a.setString(10, "120317241209124781241290847124");
std/mem.zig+10-2
......@@ -35,6 +35,7 @@ pub const Allocator = struct {
3535 freeFn: fn (self: *Allocator, old_mem: []u8) void,
3636
3737 /// Call `destroy` with the result
38 /// TODO this is deprecated. use createOne instead
3839 pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) {
3940 const T = @typeOf(init);
4041 if (@sizeOf(T) == 0) return &(T{});
......@@ -44,6 +45,14 @@ pub const Allocator = struct {
4445 return ptr;
4546 }
4647
48 /// Call `destroy` with the result.
49 /// Returns undefined memory.
50 pub fn createOne(self: *Allocator, comptime T: type) Error!*T {
51 if (@sizeOf(T) == 0) return &(T{});
52 const slice = try self.alloc(T, 1);
53 return &slice[0];
54 }
55
4756 /// `ptr` should be the return value of `create`
4857 pub fn destroy(self: *Allocator, ptr: var) void {
4958 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));
......@@ -149,13 +158,12 @@ pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {
149158 @setRuntimeSafety(false);
150159 assert(dest.len >= source.len);
151160 var i = source.len;
152 while(i > 0){
161 while (i > 0) {
153162 i -= 1;
154163 dest[i] = source[i];
155164 }
156165}
157166
158
159167pub fn set(comptime T: type, dest: []T, value: T) void {
160168 for (dest) |*d|
161169 d.* = value;
std/os/darwin.zig+87-1
......@@ -2,7 +2,7 @@ const std = @import("../index.zig");
22const c = std.c;
33const assert = std.debug.assert;
44
5pub use @import("darwin_errno.zig");
5pub use @import("darwin/errno.zig");
66
77pub const PATH_MAX = 1024;
88
......@@ -482,6 +482,92 @@ pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080;
482482/// data is mach absolute time units
483483pub const NOTE_MACHTIME = 0x00000100;
484484
485pub const AF_UNSPEC: c_int = 0;
486pub const AF_LOCAL: c_int = 1;
487pub const AF_UNIX: c_int = AF_LOCAL;
488pub const AF_INET: c_int = 2;
489pub const AF_SYS_CONTROL: c_int = 2;
490pub const AF_IMPLINK: c_int = 3;
491pub const AF_PUP: c_int = 4;
492pub const AF_CHAOS: c_int = 5;
493pub const AF_NS: c_int = 6;
494pub const AF_ISO: c_int = 7;
495pub const AF_OSI: c_int = AF_ISO;
496pub const AF_ECMA: c_int = 8;
497pub const AF_DATAKIT: c_int = 9;
498pub const AF_CCITT: c_int = 10;
499pub const AF_SNA: c_int = 11;
500pub const AF_DECnet: c_int = 12;
501pub const AF_DLI: c_int = 13;
502pub const AF_LAT: c_int = 14;
503pub const AF_HYLINK: c_int = 15;
504pub const AF_APPLETALK: c_int = 16;
505pub const AF_ROUTE: c_int = 17;
506pub const AF_LINK: c_int = 18;
507pub const AF_XTP: c_int = 19;
508pub const AF_COIP: c_int = 20;
509pub const AF_CNT: c_int = 21;
510pub const AF_RTIP: c_int = 22;
511pub const AF_IPX: c_int = 23;
512pub const AF_SIP: c_int = 24;
513pub const AF_PIP: c_int = 25;
514pub const AF_ISDN: c_int = 28;
515pub const AF_E164: c_int = AF_ISDN;
516pub const AF_KEY: c_int = 29;
517pub const AF_INET6: c_int = 30;
518pub const AF_NATM: c_int = 31;
519pub const AF_SYSTEM: c_int = 32;
520pub const AF_NETBIOS: c_int = 33;
521pub const AF_PPP: c_int = 34;
522pub const AF_MAX: c_int = 40;
523
524pub const PF_UNSPEC: c_int = AF_UNSPEC;
525pub const PF_LOCAL: c_int = AF_LOCAL;
526pub const PF_UNIX: c_int = PF_LOCAL;
527pub const PF_INET: c_int = AF_INET;
528pub const PF_IMPLINK: c_int = AF_IMPLINK;
529pub const PF_PUP: c_int = AF_PUP;
530pub const PF_CHAOS: c_int = AF_CHAOS;
531pub const PF_NS: c_int = AF_NS;
532pub const PF_ISO: c_int = AF_ISO;
533pub const PF_OSI: c_int = AF_ISO;
534pub const PF_ECMA: c_int = AF_ECMA;
535pub const PF_DATAKIT: c_int = AF_DATAKIT;
536pub const PF_CCITT: c_int = AF_CCITT;
537pub const PF_SNA: c_int = AF_SNA;
538pub const PF_DECnet: c_int = AF_DECnet;
539pub const PF_DLI: c_int = AF_DLI;
540pub const PF_LAT: c_int = AF_LAT;
541pub const PF_HYLINK: c_int = AF_HYLINK;
542pub const PF_APPLETALK: c_int = AF_APPLETALK;
543pub const PF_ROUTE: c_int = AF_ROUTE;
544pub const PF_LINK: c_int = AF_LINK;
545pub const PF_XTP: c_int = AF_XTP;
546pub const PF_COIP: c_int = AF_COIP;
547pub const PF_CNT: c_int = AF_CNT;
548pub const PF_SIP: c_int = AF_SIP;
549pub const PF_IPX: c_int = AF_IPX;
550pub const PF_RTIP: c_int = AF_RTIP;
551pub const PF_PIP: c_int = AF_PIP;
552pub const PF_ISDN: c_int = AF_ISDN;
553pub const PF_KEY: c_int = AF_KEY;
554pub const PF_INET6: c_int = AF_INET6;
555pub const PF_NATM: c_int = AF_NATM;
556pub const PF_SYSTEM: c_int = AF_SYSTEM;
557pub const PF_NETBIOS: c_int = AF_NETBIOS;
558pub const PF_PPP: c_int = AF_PPP;
559pub const PF_MAX: c_int = AF_MAX;
560
561pub const SYSPROTO_EVENT: c_int = 1;
562pub const SYSPROTO_CONTROL: c_int = 2;
563
564pub const SOCK_STREAM: c_int = 1;
565pub const SOCK_DGRAM: c_int = 2;
566pub const SOCK_RAW: c_int = 3;
567pub const SOCK_RDM: c_int = 4;
568pub const SOCK_SEQPACKET: c_int = 5;
569pub const SOCK_MAXADDRLEN: c_int = 255;
570
485571fn wstatus(x: i32) i32 {
486572 return x & 0o177;
487573}
std/os/darwin/errno.zig created+328
......@@ -0,0 +1,328 @@
1/// Operation not permitted
2pub const EPERM = 1;
3
4/// No such file or directory
5pub const ENOENT = 2;
6
7/// No such process
8pub const ESRCH = 3;
9
10/// Interrupted system call
11pub const EINTR = 4;
12
13/// Input/output error
14pub const EIO = 5;
15
16/// Device not configured
17pub const ENXIO = 6;
18
19/// Argument list too long
20pub const E2BIG = 7;
21
22/// Exec format error
23pub const ENOEXEC = 8;
24
25/// Bad file descriptor
26pub const EBADF = 9;
27
28/// No child processes
29pub const ECHILD = 10;
30
31/// Resource deadlock avoided
32pub const EDEADLK = 11;
33
34/// Cannot allocate memory
35pub const ENOMEM = 12;
36
37/// Permission denied
38pub const EACCES = 13;
39
40/// Bad address
41pub const EFAULT = 14;
42
43/// Block device required
44pub const ENOTBLK = 15;
45
46/// Device / Resource busy
47pub const EBUSY = 16;
48
49/// File exists
50pub const EEXIST = 17;
51
52/// Cross-device link
53pub const EXDEV = 18;
54
55/// Operation not supported by device
56pub const ENODEV = 19;
57
58/// Not a directory
59pub const ENOTDIR = 20;
60
61/// Is a directory
62pub const EISDIR = 21;
63
64/// Invalid argument
65pub const EINVAL = 22;
66
67/// Too many open files in system
68pub const ENFILE = 23;
69
70/// Too many open files
71pub const EMFILE = 24;
72
73/// Inappropriate ioctl for device
74pub const ENOTTY = 25;
75
76/// Text file busy
77pub const ETXTBSY = 26;
78
79/// File too large
80pub const EFBIG = 27;
81
82/// No space left on device
83pub const ENOSPC = 28;
84
85/// Illegal seek
86pub const ESPIPE = 29;
87
88/// Read-only file system
89pub const EROFS = 30;
90
91/// Too many links
92pub const EMLINK = 31;
93/// Broken pipe
94
95// math software
96pub const EPIPE = 32;
97
98/// Numerical argument out of domain
99pub const EDOM = 33;
100/// Result too large
101
102// non-blocking and interrupt i/o
103pub const ERANGE = 34;
104
105/// Resource temporarily unavailable
106pub const EAGAIN = 35;
107
108/// Operation would block
109pub const EWOULDBLOCK = EAGAIN;
110
111/// Operation now in progress
112pub const EINPROGRESS = 36;
113/// Operation already in progress
114
115// ipc/network software -- argument errors
116pub const EALREADY = 37;
117
118/// Socket operation on non-socket
119pub const ENOTSOCK = 38;
120
121/// Destination address required
122pub const EDESTADDRREQ = 39;
123
124/// Message too long
125pub const EMSGSIZE = 40;
126
127/// Protocol wrong type for socket
128pub const EPROTOTYPE = 41;
129
130/// Protocol not available
131pub const ENOPROTOOPT = 42;
132
133/// Protocol not supported
134pub const EPROTONOSUPPORT = 43;
135
136/// Socket type not supported
137pub const ESOCKTNOSUPPORT = 44;
138
139/// Operation not supported
140pub const ENOTSUP = 45;
141
142/// Protocol family not supported
143pub const EPFNOSUPPORT = 46;
144
145/// Address family not supported by protocol family
146pub const EAFNOSUPPORT = 47;
147
148/// Address already in use
149pub const EADDRINUSE = 48;
150/// Can't assign requested address
151
152// ipc/network software -- operational errors
153pub const EADDRNOTAVAIL = 49;
154
155/// Network is down
156pub const ENETDOWN = 50;
157
158/// Network is unreachable
159pub const ENETUNREACH = 51;
160
161/// Network dropped connection on reset
162pub const ENETRESET = 52;
163
164/// Software caused connection abort
165pub const ECONNABORTED = 53;
166
167/// Connection reset by peer
168pub const ECONNRESET = 54;
169
170/// No buffer space available
171pub const ENOBUFS = 55;
172
173/// Socket is already connected
174pub const EISCONN = 56;
175
176/// Socket is not connected
177pub const ENOTCONN = 57;
178
179/// Can't send after socket shutdown
180pub const ESHUTDOWN = 58;
181
182/// Too many references: can't splice
183pub const ETOOMANYREFS = 59;
184
185/// Operation timed out
186pub const ETIMEDOUT = 60;
187
188/// Connection refused
189pub const ECONNREFUSED = 61;
190
191/// Too many levels of symbolic links
192pub const ELOOP = 62;
193
194/// File name too long
195pub const ENAMETOOLONG = 63;
196
197/// Host is down
198pub const EHOSTDOWN = 64;
199
200/// No route to host
201pub const EHOSTUNREACH = 65;
202/// Directory not empty
203
204// quotas & mush
205pub const ENOTEMPTY = 66;
206
207/// Too many processes
208pub const EPROCLIM = 67;
209
210/// Too many users
211pub const EUSERS = 68;
212/// Disc quota exceeded
213
214// Network File System
215pub const EDQUOT = 69;
216
217/// Stale NFS file handle
218pub const ESTALE = 70;
219
220/// Too many levels of remote in path
221pub const EREMOTE = 71;
222
223/// RPC struct is bad
224pub const EBADRPC = 72;
225
226/// RPC version wrong
227pub const ERPCMISMATCH = 73;
228
229/// RPC prog. not avail
230pub const EPROGUNAVAIL = 74;
231
232/// Program version wrong
233pub const EPROGMISMATCH = 75;
234
235/// Bad procedure for program
236pub const EPROCUNAVAIL = 76;
237
238/// No locks available
239pub const ENOLCK = 77;
240
241/// Function not implemented
242pub const ENOSYS = 78;
243
244/// Inappropriate file type or format
245pub const EFTYPE = 79;
246
247/// Authentication error
248pub const EAUTH = 80;
249/// Need authenticator
250
251// Intelligent device errors
252pub const ENEEDAUTH = 81;
253
254/// Device power is off
255pub const EPWROFF = 82;
256
257/// Device error, e.g. paper out
258pub const EDEVERR = 83;
259/// Value too large to be stored in data type
260
261// Program loading errors
262pub const EOVERFLOW = 84;
263
264/// Bad executable
265pub const EBADEXEC = 85;
266
267/// Bad CPU type in executable
268pub const EBADARCH = 86;
269
270/// Shared library version mismatch
271pub const ESHLIBVERS = 87;
272
273/// Malformed Macho file
274pub const EBADMACHO = 88;
275
276/// Operation canceled
277pub const ECANCELED = 89;
278
279/// Identifier removed
280pub const EIDRM = 90;
281
282/// No message of desired type
283pub const ENOMSG = 91;
284
285/// Illegal byte sequence
286pub const EILSEQ = 92;
287
288/// Attribute not found
289pub const ENOATTR = 93;
290
291/// Bad message
292pub const EBADMSG = 94;
293
294/// Reserved
295pub const EMULTIHOP = 95;
296
297/// No message available on STREAM
298pub const ENODATA = 96;
299
300/// Reserved
301pub const ENOLINK = 97;
302
303/// No STREAM resources
304pub const ENOSR = 98;
305
306/// Not a STREAM
307pub const ENOSTR = 99;
308
309/// Protocol error
310pub const EPROTO = 100;
311
312/// STREAM ioctl timeout
313pub const ETIME = 101;
314
315/// No such policy registered
316pub const ENOPOLICY = 103;
317
318/// State not recoverable
319pub const ENOTRECOVERABLE = 104;
320
321/// Previous owner died
322pub const EOWNERDEAD = 105;
323
324/// Interface output queue is full
325pub const EQFULL = 106;
326
327/// Must be equal largest errno
328pub const ELAST = 106;
std/os/darwin_errno.zig deleted-328
......@@ -1,328 +0,0 @@
1/// Operation not permitted
2pub const EPERM = 1;
3
4/// No such file or directory
5pub const ENOENT = 2;
6
7/// No such process
8pub const ESRCH = 3;
9
10/// Interrupted system call
11pub const EINTR = 4;
12
13/// Input/output error
14pub const EIO = 5;
15
16/// Device not configured
17pub const ENXIO = 6;
18
19/// Argument list too long
20pub const E2BIG = 7;
21
22/// Exec format error
23pub const ENOEXEC = 8;
24
25/// Bad file descriptor
26pub const EBADF = 9;
27
28/// No child processes
29pub const ECHILD = 10;
30
31/// Resource deadlock avoided
32pub const EDEADLK = 11;
33
34/// Cannot allocate memory
35pub const ENOMEM = 12;
36
37/// Permission denied
38pub const EACCES = 13;
39
40/// Bad address
41pub const EFAULT = 14;
42
43/// Block device required
44pub const ENOTBLK = 15;
45
46/// Device / Resource busy
47pub const EBUSY = 16;
48
49/// File exists
50pub const EEXIST = 17;
51
52/// Cross-device link
53pub const EXDEV = 18;
54
55/// Operation not supported by device
56pub const ENODEV = 19;
57
58/// Not a directory
59pub const ENOTDIR = 20;
60
61/// Is a directory
62pub const EISDIR = 21;
63
64/// Invalid argument
65pub const EINVAL = 22;
66
67/// Too many open files in system
68pub const ENFILE = 23;
69
70/// Too many open files
71pub const EMFILE = 24;
72
73/// Inappropriate ioctl for device
74pub const ENOTTY = 25;
75
76/// Text file busy
77pub const ETXTBSY = 26;
78
79/// File too large
80pub const EFBIG = 27;
81
82/// No space left on device
83pub const ENOSPC = 28;
84
85/// Illegal seek
86pub const ESPIPE = 29;
87
88/// Read-only file system
89pub const EROFS = 30;
90
91/// Too many links
92pub const EMLINK = 31;
93/// Broken pipe
94
95// math software
96pub const EPIPE = 32;
97
98/// Numerical argument out of domain
99pub const EDOM = 33;
100/// Result too large
101
102// non-blocking and interrupt i/o
103pub const ERANGE = 34;
104
105/// Resource temporarily unavailable
106pub const EAGAIN = 35;
107
108/// Operation would block
109pub const EWOULDBLOCK = EAGAIN;
110
111/// Operation now in progress
112pub const EINPROGRESS = 36;
113/// Operation already in progress
114
115// ipc/network software -- argument errors
116pub const EALREADY = 37;
117
118/// Socket operation on non-socket
119pub const ENOTSOCK = 38;
120
121/// Destination address required
122pub const EDESTADDRREQ = 39;
123
124/// Message too long
125pub const EMSGSIZE = 40;
126
127/// Protocol wrong type for socket
128pub const EPROTOTYPE = 41;
129
130/// Protocol not available
131pub const ENOPROTOOPT = 42;
132
133/// Protocol not supported
134pub const EPROTONOSUPPORT = 43;
135
136/// Socket type not supported
137pub const ESOCKTNOSUPPORT = 44;
138
139/// Operation not supported
140pub const ENOTSUP = 45;
141
142/// Protocol family not supported
143pub const EPFNOSUPPORT = 46;
144
145/// Address family not supported by protocol family
146pub const EAFNOSUPPORT = 47;
147
148/// Address already in use
149pub const EADDRINUSE = 48;
150/// Can't assign requested address
151
152// ipc/network software -- operational errors
153pub const EADDRNOTAVAIL = 49;
154
155/// Network is down
156pub const ENETDOWN = 50;
157
158/// Network is unreachable
159pub const ENETUNREACH = 51;
160
161/// Network dropped connection on reset
162pub const ENETRESET = 52;
163
164/// Software caused connection abort
165pub const ECONNABORTED = 53;
166
167/// Connection reset by peer
168pub const ECONNRESET = 54;
169
170/// No buffer space available
171pub const ENOBUFS = 55;
172
173/// Socket is already connected
174pub const EISCONN = 56;
175
176/// Socket is not connected
177pub const ENOTCONN = 57;
178
179/// Can't send after socket shutdown
180pub const ESHUTDOWN = 58;
181
182/// Too many references: can't splice
183pub const ETOOMANYREFS = 59;
184
185/// Operation timed out
186pub const ETIMEDOUT = 60;
187
188/// Connection refused
189pub const ECONNREFUSED = 61;
190
191/// Too many levels of symbolic links
192pub const ELOOP = 62;
193
194/// File name too long
195pub const ENAMETOOLONG = 63;
196
197/// Host is down
198pub const EHOSTDOWN = 64;
199
200/// No route to host
201pub const EHOSTUNREACH = 65;
202/// Directory not empty
203
204// quotas & mush
205pub const ENOTEMPTY = 66;
206
207/// Too many processes
208pub const EPROCLIM = 67;
209
210/// Too many users
211pub const EUSERS = 68;
212/// Disc quota exceeded
213
214// Network File System
215pub const EDQUOT = 69;
216
217/// Stale NFS file handle
218pub const ESTALE = 70;
219
220/// Too many levels of remote in path
221pub const EREMOTE = 71;
222
223/// RPC struct is bad
224pub const EBADRPC = 72;
225
226/// RPC version wrong
227pub const ERPCMISMATCH = 73;
228
229/// RPC prog. not avail
230pub const EPROGUNAVAIL = 74;
231
232/// Program version wrong
233pub const EPROGMISMATCH = 75;
234
235/// Bad procedure for program
236pub const EPROCUNAVAIL = 76;
237
238/// No locks available
239pub const ENOLCK = 77;
240
241/// Function not implemented
242pub const ENOSYS = 78;
243
244/// Inappropriate file type or format
245pub const EFTYPE = 79;
246
247/// Authentication error
248pub const EAUTH = 80;
249/// Need authenticator
250
251// Intelligent device errors
252pub const ENEEDAUTH = 81;
253
254/// Device power is off
255pub const EPWROFF = 82;
256
257/// Device error, e.g. paper out
258pub const EDEVERR = 83;
259/// Value too large to be stored in data type
260
261// Program loading errors
262pub const EOVERFLOW = 84;
263
264/// Bad executable
265pub const EBADEXEC = 85;
266
267/// Bad CPU type in executable
268pub const EBADARCH = 86;
269
270/// Shared library version mismatch
271pub const ESHLIBVERS = 87;
272
273/// Malformed Macho file
274pub const EBADMACHO = 88;
275
276/// Operation canceled
277pub const ECANCELED = 89;
278
279/// Identifier removed
280pub const EIDRM = 90;
281
282/// No message of desired type
283pub const ENOMSG = 91;
284
285/// Illegal byte sequence
286pub const EILSEQ = 92;
287
288/// Attribute not found
289pub const ENOATTR = 93;
290
291/// Bad message
292pub const EBADMSG = 94;
293
294/// Reserved
295pub const EMULTIHOP = 95;
296
297/// No message available on STREAM
298pub const ENODATA = 96;
299
300/// Reserved
301pub const ENOLINK = 97;
302
303/// No STREAM resources
304pub const ENOSR = 98;
305
306/// Not a STREAM
307pub const ENOSTR = 99;
308
309/// Protocol error
310pub const EPROTO = 100;
311
312/// STREAM ioctl timeout
313pub const ETIME = 101;
314
315/// No such policy registered
316pub const ENOPOLICY = 103;
317
318/// State not recoverable
319pub const ENOTRECOVERABLE = 104;
320
321/// Previous owner died
322pub const EOWNERDEAD = 105;
323
324/// Interface output queue is full
325pub const EQFULL = 106;
326
327/// Must be equal largest errno
328pub const ELAST = 106;
std/os/file.zig+28-35
......@@ -15,7 +15,7 @@ pub const File = struct {
1515 /// The OS-specific file descriptor or file handle.
1616 handle: os.FileHandle,
1717
18 const OpenError = os.WindowsOpenError || os.PosixOpenError;
18 pub const OpenError = os.WindowsOpenError || os.PosixOpenError;
1919
2020 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
2121 /// Call close to clean up.
......@@ -109,43 +109,42 @@ pub const File = struct {
109109 Unexpected,
110110 };
111111
112 pub fn access(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) AccessError!bool {
112 pub fn access(allocator: *mem.Allocator, path: []const u8) AccessError!void {
113113 const path_with_null = try std.cstr.addNullByte(allocator, path);
114114 defer allocator.free(path_with_null);
115115
116116 if (is_posix) {
117 // mode is ignored and is always F_OK for now
118117 const result = posix.access(path_with_null.ptr, posix.F_OK);
119118 const err = posix.getErrno(result);
120 if (err > 0) {
121 return switch (err) {
122 posix.EACCES => error.PermissionDenied,
123 posix.EROFS => error.PermissionDenied,
124 posix.ELOOP => error.PermissionDenied,
125 posix.ETXTBSY => error.PermissionDenied,
126 posix.ENOTDIR => error.NotFound,
127 posix.ENOENT => error.NotFound,
119 switch (err) {
120 0 => return,
121 posix.EACCES => return error.PermissionDenied,
122 posix.EROFS => return error.PermissionDenied,
123 posix.ELOOP => return error.PermissionDenied,
124 posix.ETXTBSY => return error.PermissionDenied,
125 posix.ENOTDIR => return error.NotFound,
126 posix.ENOENT => return error.NotFound,
128127
129 posix.ENAMETOOLONG => error.NameTooLong,
130 posix.EINVAL => error.BadMode,
131 posix.EFAULT => error.BadPathName,
132 posix.EIO => error.Io,
133 posix.ENOMEM => error.SystemResources,
134 else => os.unexpectedErrorPosix(err),
135 };
128 posix.ENAMETOOLONG => return error.NameTooLong,
129 posix.EINVAL => unreachable,
130 posix.EFAULT => return error.BadPathName,
131 posix.EIO => return error.Io,
132 posix.ENOMEM => return error.SystemResources,
133 else => return os.unexpectedErrorPosix(err),
136134 }
137 return true;
138135 } else if (is_windows) {
139136 if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {
140 return true;
137 return;
141138 }
142139
143140 const err = windows.GetLastError();
144 return switch (err) {
145 windows.ERROR.FILE_NOT_FOUND => error.NotFound,
146 windows.ERROR.ACCESS_DENIED => error.PermissionDenied,
147 else => os.unexpectedErrorWindows(err),
148 };
141 switch (err) {
142 windows.ERROR.FILE_NOT_FOUND,
143 windows.ERROR.PATH_NOT_FOUND,
144 => return error.NotFound,
145 windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
146 else => return os.unexpectedErrorWindows(err),
147 }
149148 } else {
150149 @compileError("TODO implement access for this OS");
151150 }
......@@ -242,7 +241,7 @@ pub const File = struct {
242241 },
243242 Os.windows => {
244243 var pos: windows.LARGE_INTEGER = undefined;
245 if (windows.SetFilePointerEx(self.handle, 0, *pos, windows.FILE_CURRENT) == 0) {
244 if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) {
246245 const err = windows.GetLastError();
247246 return switch (err) {
248247 windows.ERROR.INVALID_PARAMETER => error.BadFd,
......@@ -251,13 +250,7 @@ pub const File = struct {
251250 }
252251
253252 assert(pos >= 0);
254 if (@sizeOf(@typeOf(pos)) > @sizeOf(usize)) {
255 if (pos > @maxValue(usize)) {
256 return error.FilePosLargerThanPointerRange;
257 }
258 }
259
260 return usize(pos);
253 return math.cast(usize, pos) catch error.FilePosLargerThanPointerRange;
261254 },
262255 else => @compileError("unsupported OS"),
263256 }
......@@ -289,7 +282,7 @@ pub const File = struct {
289282 Unexpected,
290283 };
291284
292 fn mode(self: *File) ModeError!os.FileMode {
285 pub fn mode(self: *File) ModeError!os.FileMode {
293286 if (is_posix) {
294287 var stat: posix.Stat = undefined;
295288 const err = posix.getErrno(posix.fstat(self.handle, &stat));
......@@ -364,7 +357,7 @@ pub const File = struct {
364357
365358 pub const WriteError = os.WindowsWriteError || os.PosixWriteError;
366359
367 fn write(self: *File, bytes: []const u8) WriteError!void {
360 pub fn write(self: *File, bytes: []const u8) WriteError!void {
368361 if (is_posix) {
369362 try os.posixWrite(self.handle, bytes);
370363 } else if (is_windows) {
std/os/get_app_data_dir.zig created+69
......@@ -0,0 +1,69 @@
1const std = @import("../index.zig");
2const builtin = @import("builtin");
3const unicode = std.unicode;
4const mem = std.mem;
5const os = std.os;
6
7pub const GetAppDataDirError = error{
8 OutOfMemory,
9 AppDataDirUnavailable,
10};
11
12/// Caller owns returned memory.
13pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
14 switch (builtin.os) {
15 builtin.Os.windows => {
16 var dir_path_ptr: [*]u16 = undefined;
17 switch (os.windows.SHGetKnownFolderPath(
18 &os.windows.FOLDERID_LocalAppData,
19 os.windows.KF_FLAG_CREATE,
20 null,
21 &dir_path_ptr,
22 )) {
23 os.windows.S_OK => {
24 defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr));
25 const global_dir = unicode.utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr)) catch |err| switch (err) {
26 error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
27 error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
28 error.DanglingSurrogateHalf => return error.AppDataDirUnavailable,
29 error.OutOfMemory => return error.OutOfMemory,
30 };
31 defer allocator.free(global_dir);
32 return os.path.join(allocator, global_dir, appname);
33 },
34 os.windows.E_OUTOFMEMORY => return error.OutOfMemory,
35 else => return error.AppDataDirUnavailable,
36 }
37 },
38 builtin.Os.macosx => {
39 const home_dir = os.getEnvPosix("HOME") orelse {
40 // TODO look in /etc/passwd
41 return error.AppDataDirUnavailable;
42 };
43 return os.path.join(allocator, home_dir, "Library", "Application Support", appname);
44 },
45 builtin.Os.linux => {
46 const home_dir = os.getEnvPosix("HOME") orelse {
47 // TODO look in /etc/passwd
48 return error.AppDataDirUnavailable;
49 };
50 return os.path.join(allocator, home_dir, ".local", "share", appname);
51 },
52 else => @compileError("Unsupported OS"),
53 }
54}
55
56fn utf16lePtrSlice(ptr: [*]const u16) []const u16 {
57 var index: usize = 0;
58 while (ptr[index] != 0) : (index += 1) {}
59 return ptr[0..index];
60}
61
62test "std.os.getAppDataDir" {
63 var buf: [512]u8 = undefined;
64 const allocator = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
65
66 // We can't actually validate the result
67 _ = getAppDataDir(allocator, "zig") catch return;
68}
69
std/os/index.zig+8-1
......@@ -11,13 +11,14 @@ const os = this;
1111test "std.os" {
1212 _ = @import("child_process.zig");
1313 _ = @import("darwin.zig");
14 _ = @import("darwin_errno.zig");
14 _ = @import("darwin/errno.zig");
1515 _ = @import("get_user_id.zig");
1616 _ = @import("linux/index.zig");
1717 _ = @import("path.zig");
1818 _ = @import("test.zig");
1919 _ = @import("time.zig");
2020 _ = @import("windows/index.zig");
21 _ = @import("get_app_data_dir.zig");
2122}
2223
2324pub const windows = @import("windows/index.zig");
......@@ -76,6 +77,9 @@ pub const WindowsWriteError = windows_util.WriteError;
7677
7778pub const FileHandle = if (is_windows) windows.HANDLE else i32;
7879
80pub const getAppDataDir = @import("get_app_data_dir.zig").getAppDataDir;
81pub const GetAppDataDirError = @import("get_app_data_dir.zig").GetAppDataDirError;
82
7983const debug = std.debug;
8084const assert = debug.assert;
8185
......@@ -494,6 +498,7 @@ pub var linux_aux_raw = []usize{0} ** 38;
494498pub var posix_environ_raw: [][*]u8 = undefined;
495499
496500/// Caller must free result when done.
501/// TODO make this go through libc when we have it
497502pub fn getEnvMap(allocator: *Allocator) !BufMap {
498503 var result = BufMap.init(allocator);
499504 errdefer result.deinit();
......@@ -537,6 +542,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
537542 }
538543}
539544
545/// TODO make this go through libc when we have it
540546pub fn getEnvPosix(key: []const u8) ?[]const u8 {
541547 for (posix_environ_raw) |ptr| {
542548 var line_i: usize = 0;
......@@ -559,6 +565,7 @@ pub const GetEnvVarOwnedError = error{
559565};
560566
561567/// Caller must free returned memory.
568/// TODO make this go through libc when we have it
562569pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 {
563570 if (is_windows) {
564571 const key_with_null = try cstr.addNullByte(allocator, key);
std/os/test.zig+3-3
......@@ -23,14 +23,14 @@ test "makePath, put some files in it, deleteTree" {
2323
2424test "access file" {
2525 try os.makePath(a, "os_test_tmp");
26 if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {
27 unreachable;
26 if (os.File.access(a, "os_test_tmp/file.txt")) |ok| {
27 @panic("expected error");
2828 } else |err| {
2929 assert(err == error.NotFound);
3030 }
3131
3232 try io.writeFile(a, "os_test_tmp/file.txt", "");
33 assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true);
33 try os.File.access(a, "os_test_tmp/file.txt");
3434 try os.deleteTree(a, "os_test_tmp");
3535}
3636
std/os/windows/advapi32.zig created+30
......@@ -0,0 +1,30 @@
1use @import("index.zig");
2
3pub const PROV_RSA_FULL = 1;
4
5pub const REGSAM = ACCESS_MASK;
6pub const ACCESS_MASK = DWORD;
7pub const PHKEY = &HKEY;
8pub const HKEY = &HKEY__;
9pub const HKEY__ = extern struct {
10 unused: c_int,
11};
12pub const LSTATUS = LONG;
13
14pub extern "advapi32" stdcallcc fn CryptAcquireContextA(
15 phProv: *HCRYPTPROV,
16 pszContainer: ?LPCSTR,
17 pszProvider: ?LPCSTR,
18 dwProvType: DWORD,
19 dwFlags: DWORD,
20) BOOL;
21
22pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL;
23
24pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL;
25
26pub extern "advapi32" stdcallcc fn RegOpenKeyExW(hKey: HKEY, lpSubKey: LPCWSTR, ulOptions: DWORD, samDesired: REGSAM,
27 phkResult: &HKEY,) LSTATUS;
28
29pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD,
30 lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS;
std/os/windows/index.zig+90-179
......@@ -1,188 +1,19 @@
1const std = @import("../../index.zig");
2const assert = std.debug.assert;
3
4pub use @import("advapi32.zig");
5pub use @import("kernel32.zig");
6pub use @import("ole32.zig");
7pub use @import("shell32.zig");
8pub use @import("shlwapi.zig");
9pub use @import("user32.zig");
10
111test "import" {
212 _ = @import("util.zig");
313}
414
515pub const ERROR = @import("error.zig");
616
7pub extern "advapi32" stdcallcc fn CryptAcquireContextA(
8 phProv: *HCRYPTPROV,
9 pszContainer: ?LPCSTR,
10 pszProvider: ?LPCSTR,
11 dwProvType: DWORD,
12 dwFlags: DWORD,
13) BOOL;
14
15pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL;
16
17pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL;
18
19pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
20
21pub extern "kernel32" stdcallcc fn CreateDirectoryA(
22 lpPathName: LPCSTR,
23 lpSecurityAttributes: ?*SECURITY_ATTRIBUTES,
24) BOOL;
25
26pub extern "kernel32" stdcallcc fn CreateFileA(
27 lpFileName: LPCSTR,
28 dwDesiredAccess: DWORD,
29 dwShareMode: DWORD,
30 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
31 dwCreationDisposition: DWORD,
32 dwFlagsAndAttributes: DWORD,
33 hTemplateFile: ?HANDLE,
34) HANDLE;
35
36pub extern "kernel32" stdcallcc fn CreatePipe(
37 hReadPipe: *HANDLE,
38 hWritePipe: *HANDLE,
39 lpPipeAttributes: *const SECURITY_ATTRIBUTES,
40 nSize: DWORD,
41) BOOL;
42
43pub extern "kernel32" stdcallcc fn CreateProcessA(
44 lpApplicationName: ?LPCSTR,
45 lpCommandLine: LPSTR,
46 lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
47 lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
48 bInheritHandles: BOOL,
49 dwCreationFlags: DWORD,
50 lpEnvironment: ?*c_void,
51 lpCurrentDirectory: ?LPCSTR,
52 lpStartupInfo: *STARTUPINFOA,
53 lpProcessInformation: *PROCESS_INFORMATION,
54) BOOL;
55
56pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
57 lpSymlinkFileName: LPCSTR,
58 lpTargetFileName: LPCSTR,
59 dwFlags: DWORD,
60) BOOLEAN;
61
62pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
63
64pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
65
66pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
67
68pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
69
70pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
71pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
72pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
73
74pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
75
76pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
77
78pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
79
80pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD;
81
82pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8;
83
84pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD;
85
86pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL;
87
88pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
89
90pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
91
92pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
93
94pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
95
96pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
97 in_hFile: HANDLE,
98 in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
99 out_lpFileInformation: *c_void,
100 in_dwBufferSize: DWORD,
101) BOOL;
102
103pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
104 hFile: HANDLE,
105 lpszFilePath: LPSTR,
106 cchFilePath: DWORD,
107 dwFlags: DWORD,
108) DWORD;
109
110pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
111pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
112
113pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void;
114pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
115
116pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
117pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
118pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
119pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
120pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL;
121pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
122pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
123
124pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE;
125
126pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void;
127
128pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
129
130pub extern "kernel32" stdcallcc fn MoveFileExA(
131 lpExistingFileName: LPCSTR,
132 lpNewFileName: LPCSTR,
133 dwFlags: DWORD,
134) BOOL;
135
136pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;
137
138pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;
139
140pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
141
142pub extern "kernel32" stdcallcc fn ReadFile(
143 in_hFile: HANDLE,
144 out_lpBuffer: *c_void,
145 in_nNumberOfBytesToRead: DWORD,
146 out_lpNumberOfBytesRead: *DWORD,
147 in_out_lpOverlapped: ?*OVERLAPPED,
148) BOOL;
149
150pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
151
152pub extern "kernel32" stdcallcc fn SetFilePointerEx(
153 in_fFile: HANDLE,
154 in_liDistanceToMove: LARGE_INTEGER,
155 out_opt_ldNewFilePointer: ?*LARGE_INTEGER,
156 in_dwMoveMethod: DWORD,
157) BOOL;
158
159pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL;
160
161pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
162
163pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;
164
165pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
166
167pub extern "kernel32" stdcallcc fn WriteFile(
168 in_hFile: HANDLE,
169 in_lpBuffer: *const c_void,
170 in_nNumberOfBytesToWrite: DWORD,
171 out_lpNumberOfBytesWritten: ?*DWORD,
172 in_out_lpOverlapped: ?*OVERLAPPED,
173) BOOL;
174
175//TODO: call unicode versions instead of relying on ANSI code page
176pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
177
178pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
179
180pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
181
182pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
183
184pub const PROV_RSA_FULL = 1;
185
18617pub const BOOL = c_int;
18718pub const BOOLEAN = BYTE;
18819pub const BYTE = u8;
......@@ -204,6 +35,7 @@ pub const LPSTR = [*]CHAR;
20435pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR;
20536pub const LPVOID = *c_void;
20637pub const LPWSTR = [*]WCHAR;
38pub const LPCWSTR = [*]const WCHAR;
20739pub const PVOID = *c_void;
20840pub const PWSTR = [*]WCHAR;
20941pub const SIZE_T = usize;
......@@ -439,3 +271,82 @@ pub const SYSTEM_INFO = extern struct {
439271 wProcessorLevel: WORD,
440272 wProcessorRevision: WORD,
441273};
274
275pub const HRESULT = c_long;
276
277pub const KNOWNFOLDERID = GUID;
278pub const GUID = extern struct {
279 Data1: c_ulong,
280 Data2: c_ushort,
281 Data3: c_ushort,
282 Data4: [8]u8,
283
284 pub fn parse(str: []const u8) GUID {
285 var guid: GUID = undefined;
286 var index: usize = 0;
287 assert(str[index] == '{');
288 index += 1;
289
290 guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index..index + 8], 16) catch unreachable;
291 index += 8;
292
293 assert(str[index] == '-');
294 index += 1;
295
296 guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable;
297 index += 4;
298
299 assert(str[index] == '-');
300 index += 1;
301
302 guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable;
303 index += 4;
304
305 assert(str[index] == '-');
306 index += 1;
307
308 guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
309 index += 2;
310 guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
311 index += 2;
312
313 assert(str[index] == '-');
314 index += 1;
315
316 var i: usize = 2;
317 while (i < guid.Data4.len) : (i += 1) {
318 guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
319 index += 2;
320 }
321
322 assert(str[index] == '}');
323 index += 1;
324 return guid;
325 }
326};
327
328pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}");
329
330pub const KF_FLAG_DEFAULT = 0;
331pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536;
332pub const KF_FLAG_CREATE = 32768;
333pub const KF_FLAG_DONT_VERIFY = 16384;
334pub const KF_FLAG_DONT_UNEXPAND = 8192;
335pub const KF_FLAG_NO_ALIAS = 4096;
336pub const KF_FLAG_INIT = 2048;
337pub const KF_FLAG_DEFAULT_PATH = 1024;
338pub const KF_FLAG_NOT_PARENT_RELATIVE = 512;
339pub const KF_FLAG_SIMPLE_IDLIST = 256;
340pub const KF_FLAG_ALIAS_ONLY = -2147483648;
341
342pub const S_OK = 0;
343pub const E_NOTIMPL = @bitCast(c_long, c_ulong(0x80004001));
344pub const E_NOINTERFACE = @bitCast(c_long, c_ulong(0x80004002));
345pub const E_POINTER = @bitCast(c_long, c_ulong(0x80004003));
346pub const E_ABORT = @bitCast(c_long, c_ulong(0x80004004));
347pub const E_FAIL = @bitCast(c_long, c_ulong(0x80004005));
348pub const E_UNEXPECTED = @bitCast(c_long, c_ulong(0x8000FFFF));
349pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005));
350pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006));
351pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E));
352pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057));
std/os/windows/kernel32.zig created+162
......@@ -0,0 +1,162 @@
1use @import("index.zig");
2
3pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
4
5pub extern "kernel32" stdcallcc fn CreateDirectoryA(
6 lpPathName: LPCSTR,
7 lpSecurityAttributes: ?*SECURITY_ATTRIBUTES,
8) BOOL;
9
10pub extern "kernel32" stdcallcc fn CreateFileA(
11 lpFileName: LPCSTR,
12 dwDesiredAccess: DWORD,
13 dwShareMode: DWORD,
14 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
15 dwCreationDisposition: DWORD,
16 dwFlagsAndAttributes: DWORD,
17 hTemplateFile: ?HANDLE,
18) HANDLE;
19
20pub extern "kernel32" stdcallcc fn CreatePipe(
21 hReadPipe: *HANDLE,
22 hWritePipe: *HANDLE,
23 lpPipeAttributes: *const SECURITY_ATTRIBUTES,
24 nSize: DWORD,
25) BOOL;
26
27pub extern "kernel32" stdcallcc fn CreateProcessA(
28 lpApplicationName: ?LPCSTR,
29 lpCommandLine: LPSTR,
30 lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
31 lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
32 bInheritHandles: BOOL,
33 dwCreationFlags: DWORD,
34 lpEnvironment: ?*c_void,
35 lpCurrentDirectory: ?LPCSTR,
36 lpStartupInfo: *STARTUPINFOA,
37 lpProcessInformation: *PROCESS_INFORMATION,
38) BOOL;
39
40pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
41 lpSymlinkFileName: LPCSTR,
42 lpTargetFileName: LPCSTR,
43 dwFlags: DWORD,
44) BOOLEAN;
45
46pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
47
48pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
49
50pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
51
52pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
53
54pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
55pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
56pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
57
58pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
59
60pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
61
62pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
63
64pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD;
65
66pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8;
67
68pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD;
69
70pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL;
71
72pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
73
74pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
75
76pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
77
78pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
79
80pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
81 in_hFile: HANDLE,
82 in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
83 out_lpFileInformation: *c_void,
84 in_dwBufferSize: DWORD,
85) BOOL;
86
87pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
88 hFile: HANDLE,
89 lpszFilePath: LPSTR,
90 cchFilePath: DWORD,
91 dwFlags: DWORD,
92) DWORD;
93
94pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
95pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
96
97pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void;
98pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
99
100pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
101pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
102pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
103pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
104pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL;
105pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
106pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
107
108pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE;
109
110pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void;
111
112pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
113
114pub extern "kernel32" stdcallcc fn MoveFileExA(
115 lpExistingFileName: LPCSTR,
116 lpNewFileName: LPCSTR,
117 dwFlags: DWORD,
118) BOOL;
119
120pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;
121
122pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;
123
124pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
125
126pub extern "kernel32" stdcallcc fn ReadFile(
127 in_hFile: HANDLE,
128 out_lpBuffer: *c_void,
129 in_nNumberOfBytesToRead: DWORD,
130 out_lpNumberOfBytesRead: *DWORD,
131 in_out_lpOverlapped: ?*OVERLAPPED,
132) BOOL;
133
134pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
135
136pub extern "kernel32" stdcallcc fn SetFilePointerEx(
137 in_fFile: HANDLE,
138 in_liDistanceToMove: LARGE_INTEGER,
139 out_opt_ldNewFilePointer: ?*LARGE_INTEGER,
140 in_dwMoveMethod: DWORD,
141) BOOL;
142
143pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL;
144
145pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
146
147pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;
148
149pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
150
151pub extern "kernel32" stdcallcc fn WriteFile(
152 in_hFile: HANDLE,
153 in_lpBuffer: *const c_void,
154 in_nNumberOfBytesToWrite: DWORD,
155 out_lpNumberOfBytesWritten: ?*DWORD,
156 in_out_lpOverlapped: ?*OVERLAPPED,
157) BOOL;
158
159//TODO: call unicode versions instead of relying on ANSI code page
160pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
161
162pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
std/os/windows/ole32.zig created+18
......@@ -0,0 +1,18 @@
1use @import("index.zig");
2
3pub extern "ole32.dll" stdcallcc fn CoTaskMemFree(pv: LPVOID) void;
4pub extern "ole32.dll" stdcallcc fn CoUninitialize() void;
5pub extern "ole32.dll" stdcallcc fn CoGetCurrentProcess() DWORD;
6pub extern "ole32.dll" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT;
7
8
9pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED;
10pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED;
11pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE;
12pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY;
13pub const COINIT = extern enum {
14 COINIT_APARTMENTTHREADED = 2,
15 COINIT_MULTITHREADED = 0,
16 COINIT_DISABLE_OLE1DDE = 4,
17 COINIT_SPEED_OVER_MEMORY = 8,
18};
std/os/windows/shell32.zig created+4
......@@ -0,0 +1,4 @@
1use @import("index.zig");
2
3pub extern "shell32.dll" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT;
4
std/os/windows/shlwapi.zig created+4
......@@ -0,0 +1,4 @@
1use @import("index.zig");
2
3pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
4
std/os/windows/user32.zig created+4
......@@ -0,0 +1,4 @@
1use @import("index.zig");
2
3pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
4
std/special/test_runner.zig+17-3
......@@ -5,11 +5,25 @@ const test_fn_list = builtin.__zig_test_fn_slice;
55const warn = std.debug.warn;
66
77pub fn main() !void {
8 var ok_count: usize = 0;
9 var skip_count: usize = 0;
810 for (test_fn_list) |test_fn, i| {
911 warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
1012
11 try test_fn.func();
12
13 warn("OK\n");
13 if (test_fn.func()) |_| {
14 ok_count += 1;
15 warn("OK\n");
16 } else |err| switch (err) {
17 error.SkipZigTest => {
18 skip_count += 1;
19 warn("SKIP\n");
20 },
21 else => return err,
22 }
23 }
24 if (ok_count == test_fn_list.len) {
25 warn("All tests passed.\n");
26 } else {
27 warn("{} passed; {} skipped.\n", ok_count, skip_count);
1428 }
1529}
std/unicode.zig+89
......@@ -1,5 +1,8 @@
11const std = @import("./index.zig");
2const builtin = @import("builtin");
23const debug = std.debug;
4const assert = std.debug.assert;
5const mem = std.mem;
36
47/// Returns how many bytes the UTF-8 representation would require
58/// for the given codepoint.
......@@ -441,3 +444,89 @@ fn testDecode(bytes: []const u8) !u32 {
441444 debug.assert(bytes.len == length);
442445 return utf8Decode(bytes);
443446}
447
448// TODO: make this API on top of a non-allocating Utf16LeView
449pub fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {
450 var result = std.ArrayList(u8).init(allocator);
451 // optimistically guess that it will all be ascii.
452 try result.ensureCapacity(utf16le.len);
453
454 const utf16le_as_bytes = @sliceToBytes(utf16le);
455 var i: usize = 0;
456 var out_index: usize = 0;
457 while (i < utf16le_as_bytes.len) : (i += 2) {
458 // decode
459 const c0: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]);
460 var codepoint: u32 = undefined;
461 if (c0 & ~u32(0x03ff) == 0xd800) {
462 // surrogate pair
463 i += 2;
464 if (i >= utf16le_as_bytes.len) return error.DanglingSurrogateHalf;
465 const c1: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]);
466 if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf;
467 codepoint = 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff));
468 } else if (c0 & ~u32(0x03ff) == 0xdc00) {
469 return error.UnexpectedSecondSurrogateHalf;
470 } else {
471 codepoint = c0;
472 }
473
474 // encode
475 const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable;
476 try result.resize(result.len + utf8_len);
477 _ = utf8Encode(codepoint, result.items[out_index..]) catch unreachable;
478 out_index += utf8_len;
479 }
480
481 return result.toOwnedSlice();
482}
483
484test "utf16leToUtf8" {
485 var utf16le: [2]u16 = undefined;
486 const utf16le_as_bytes = @sliceToBytes(utf16le[0..]);
487
488 {
489 mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little);
490 mem.writeInt(utf16le_as_bytes[2..], u16('a'), builtin.Endian.Little);
491 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
492 assert(mem.eql(u8, utf8, "Aa"));
493 }
494
495 {
496 mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little);
497 mem.writeInt(utf16le_as_bytes[2..], u16(0xffff), builtin.Endian.Little);
498 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
499 assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf"));
500 }
501
502 {
503 // the values just outside the surrogate half range
504 mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little);
505 mem.writeInt(utf16le_as_bytes[2..], u16(0xe000), builtin.Endian.Little);
506 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
507 assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80"));
508 }
509
510 {
511 // smallest surrogate pair
512 mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little);
513 mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little);
514 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
515 assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80"));
516 }
517
518 {
519 // largest surrogate pair
520 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);
521 mem.writeInt(utf16le_as_bytes[2..], u16(0xdfff), builtin.Endian.Little);
522 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
523 assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf"));
524 }
525
526 {
527 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);
528 mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little);
529 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
530 assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80"));
531 }
532}
std/zig/index.zig+3
......@@ -2,6 +2,7 @@ const tokenizer = @import("tokenizer.zig");
22pub const Token = tokenizer.Token;
33pub const Tokenizer = tokenizer.Tokenizer;
44pub const parse = @import("parse.zig").parse;
5pub const parseStringLiteral = @import("parse_string_literal.zig").parseStringLiteral;
56pub const render = @import("render.zig").render;
67pub const ast = @import("ast.zig");
78
......@@ -10,4 +11,6 @@ test "std.zig tests" {
1011 _ = @import("parse.zig");
1112 _ = @import("render.zig");
1213 _ = @import("tokenizer.zig");
14 _ = @import("parse_string_literal.zig");
1315}
16
std/zig/parse.zig+1-1
......@@ -2356,7 +2356,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
23562356 const token = nextToken(&tok_it, &tree);
23572357 switch (token.ptr.id) {
23582358 Token.Id.IntegerLiteral => {
2359 _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token.index);
2359 _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.IntegerLiteral, token.index);
23602360 continue;
23612361 },
23622362 Token.Id.FloatLiteral => {
std/zig/parse_string_literal.zig created+76
......@@ -0,0 +1,76 @@
1const std = @import("../index.zig");
2const assert = std.debug.assert;
3
4const State = enum {
5 Start,
6 Backslash,
7};
8
9pub const ParseStringLiteralError = error{
10 OutOfMemory,
11
12 /// When this is returned, index will be the position of the character.
13 InvalidCharacter,
14};
15
16/// caller owns returned memory
17pub fn parseStringLiteral(
18 allocator: *std.mem.Allocator,
19 bytes: []const u8,
20 bad_index: *usize, // populated if error.InvalidCharacter is returned
21) ParseStringLiteralError![]u8 {
22 const first_index = if (bytes[0] == 'c') usize(2) else usize(1);
23 assert(bytes[bytes.len - 1] == '"');
24
25 var list = std.ArrayList(u8).init(allocator);
26 errdefer list.deinit();
27
28 const slice = bytes[first_index..];
29 try list.ensureCapacity(slice.len - 1);
30
31 var state = State.Start;
32 for (slice) |b, index| {
33 switch (state) {
34 State.Start => switch (b) {
35 '\\' => state = State.Backslash,
36 '\n' => {
37 bad_index.* = index;
38 return error.InvalidCharacter;
39 },
40 '"' => return list.toOwnedSlice(),
41 else => try list.append(b),
42 },
43 State.Backslash => switch (b) {
44 'x' => @panic("TODO"),
45 'u' => @panic("TODO"),
46 'U' => @panic("TODO"),
47 'n' => {
48 try list.append('\n');
49 state = State.Start;
50 },
51 'r' => {
52 try list.append('\r');
53 state = State.Start;
54 },
55 '\\' => {
56 try list.append('\\');
57 state = State.Start;
58 },
59 't' => {
60 try list.append('\t');
61 state = State.Start;
62 },
63 '"' => {
64 try list.append('"');
65 state = State.Start;
66 },
67 else => {
68 bad_index.* = index;
69 return error.InvalidCharacter;
70 },
71 },
72 else => unreachable,
73 }
74 }
75 unreachable;
76}
std/zig/tokenizer.zig+1
......@@ -73,6 +73,7 @@ pub const Token = struct {
7373 return null;
7474 }
7575
76 /// TODO remove this enum
7677 const StrLitKind = enum {
7778 Normal,
7879 C,
test/cases/cast.zig+17
......@@ -468,3 +468,20 @@ test "@intCast i32 to u7" {
468468 var z = x >> @intCast(u7, y);
469469 assert(z == 0xff);
470470}
471
472test "implicit cast undefined to optional" {
473 assert(MakeType(void).getNull() == null);
474 assert(MakeType(void).getNonNull() != null);
475}
476
477fn MakeType(comptime T: type) type {
478 return struct {
479 fn getNull() ?T {
480 return null;
481 }
482
483 fn getNonNull() ?T {
484 return T(undefined);
485 }
486 };
487}
test/cases/defer.zig+15
......@@ -61,3 +61,18 @@ test "defer and labeled break" {
6161
6262 assert(i == 1);
6363}
64
65test "errdefer does not apply to fn inside fn" {
66 if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assert(e == error.Bad);
67}
68
69fn testNestedFnErrDefer() error!void {
70 var a: i32 = 0;
71 errdefer a += 1;
72 const S = struct {
73 fn baz() error {
74 return error.Bad;
75 }
76 };
77 return S.baz();
78}
test/cases/eval.zig+10
......@@ -642,3 +642,13 @@ test "@tagName of @typeId" {
642642 const str = @tagName(@typeId(u8));
643643 assert(std.mem.eql(u8, str, "Int"));
644644}
645
646test "setting backward branch quota just before a generic fn call" {
647 @setEvalBranchQuota(1001);
648 loopNTimes(1001);
649}
650
651fn loopNTimes(comptime n: usize) void {
652 comptime var i = 0;
653 inline while (i < n) : (i += 1) {}
654}
test/gen_h.zig+47
......@@ -76,4 +76,51 @@ pub fn addCases(cases: *tests.GenHContext) void {
7676 \\TEST_EXPORT void entry(struct Foo foo, uint8_t bar[]);
7777 \\
7878 );
79
80 cases.add("ptr to zig struct",
81 \\const S = struct {
82 \\ a: u8,
83 \\};
84 \\
85 \\export fn a(s: *S) u8 {
86 \\ return s.a;
87 \\}
88
89 ,
90 \\struct S;
91 \\TEST_EXPORT uint8_t a(struct S * s);
92 \\
93 );
94
95 cases.add("ptr to zig union",
96 \\const U = union(enum) {
97 \\ A: u8,
98 \\ B: u16,
99 \\};
100 \\
101 \\export fn a(s: *U) u8 {
102 \\ return s.A;
103 \\}
104
105 ,
106 \\union U;
107 \\TEST_EXPORT uint8_t a(union U * s);
108 \\
109 );
110
111 cases.add("ptr to zig enum",
112 \\const E = enum(u8) {
113 \\ A,
114 \\ B,
115 \\};
116 \\
117 \\export fn a(s: *E) u8 {
118 \\ return @enumToInt(s.*);
119 \\}
120
121 ,
122 \\enum E;
123 \\TEST_EXPORT uint8_t a(enum E * s);
124 \\
125 );
79126}
test/stage2/compare_output.zig created+12
......@@ -0,0 +1,12 @@
1const std = @import("std");
2const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
3
4pub fn addCases(ctx: *TestContext) !void {
5 try ctx.testCompareOutputLibC(
6 \\extern fn puts([*]const u8) void;
7 \\export fn main() c_int {
8 \\ puts(c"Hello, world!");
9 \\ return 0;
10 \\}
11 , "Hello, world!" ++ std.cstr.line_sep);
12}
test/stage2/compile_errors.zig+18
......@@ -9,4 +9,22 @@ pub fn addCases(ctx: *TestContext) !void {
99 try ctx.testCompileError(
1010 \\fn() void {}
1111 , "1.zig", 1, 1, "missing function name");
12
13 try ctx.testCompileError(
14 \\comptime {
15 \\ return;
16 \\}
17 , "1.zig", 2, 5, "return expression outside function definition");
18
19 try ctx.testCompileError(
20 \\export fn entry() void {
21 \\ defer return;
22 \\}
23 , "1.zig", 2, 11, "cannot return from defer expression");
24
25 try ctx.testCompileError(
26 \\export fn entry() c_int {
27 \\ return 36893488147419103232;
28 \\}
29 , "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
1230}
test/tests.zig+4-7
......@@ -89,12 +89,13 @@ pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8, modes:
8989 return cases.step;
9090}
9191
92pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
92pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
9393 const cases = b.allocator.create(BuildExamplesContext{
9494 .b = b,
9595 .step = b.step("test-build-examples", "Build the examples"),
9696 .test_index = 0,
9797 .test_filter = test_filter,
98 .modes = modes,
9899 }) catch unreachable;
99100
100101 build_examples.addCases(cases);
......@@ -697,6 +698,7 @@ pub const BuildExamplesContext = struct {
697698 step: *build.Step,
698699 test_index: usize,
699700 test_filter: ?[]const u8,
701 modes: []const Mode,
700702
701703 pub fn addC(self: *BuildExamplesContext, root_src: []const u8) void {
702704 self.addAllArgs(root_src, true);
......@@ -739,12 +741,7 @@ pub const BuildExamplesContext = struct {
739741 pub fn addAllArgs(self: *BuildExamplesContext, root_src: []const u8, link_libc: bool) void {
740742 const b = self.b;
741743
742 for ([]Mode{
743 Mode.Debug,
744 Mode.ReleaseSafe,
745 Mode.ReleaseFast,
746 Mode.ReleaseSmall,
747 }) |mode| {
744 for (self.modes) |mode| {
748745 const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})", root_src, @tagName(mode)) catch unreachable;
749746 if (self.test_filter) |filter| {
750747 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;