2017-12-22 21:29:39 -08:00
|
|
|
const std = @import("std");
|
|
|
|
const os = std.os;
|
|
|
|
const io = std.io;
|
|
|
|
const mem = std.mem;
|
2018-07-05 12:09:02 -07:00
|
|
|
const Allocator = mem.Allocator;
|
2017-12-22 21:29:39 -08:00
|
|
|
const Buffer = std.Buffer;
|
|
|
|
const llvm = @import("llvm.zig");
|
|
|
|
const c = @import("c.zig");
|
|
|
|
const builtin = @import("builtin");
|
|
|
|
const Target = @import("target.zig").Target;
|
|
|
|
const warn = std.debug.warn;
|
2018-02-09 10:08:02 -08:00
|
|
|
const Token = std.zig.Token;
|
2017-12-22 21:29:39 -08:00
|
|
|
const ArrayList = std.ArrayList;
|
2018-05-30 15:26:09 -07:00
|
|
|
const errmsg = @import("errmsg.zig");
|
2018-06-29 12:39:55 -07:00
|
|
|
const ast = std.zig.ast;
|
|
|
|
const event = std.event;
|
2018-07-05 12:09:02 -07:00
|
|
|
const assert = std.debug.assert;
|
2018-07-12 12:08:40 -07:00
|
|
|
const AtomicRmwOp = builtin.AtomicRmwOp;
|
|
|
|
const AtomicOrder = builtin.AtomicOrder;
|
|
|
|
const Scope = @import("scope.zig").Scope;
|
|
|
|
const Decl = @import("decl.zig").Decl;
|
|
|
|
const ir = @import("ir.zig");
|
|
|
|
const Visib = @import("visib.zig").Visib;
|
|
|
|
const Value = @import("value.zig").Value;
|
|
|
|
const Type = Value.Type;
|
2018-07-13 18:56:38 -07:00
|
|
|
const Span = errmsg.Span;
|
2018-07-14 12:45:15 -07:00
|
|
|
const codegen = @import("codegen.zig");
|
2018-07-16 17:52:50 -07:00
|
|
|
const Package = @import("package.zig").Package;
|
2018-07-17 10:18:13 -07:00
|
|
|
const link = @import("link.zig").link;
|
2018-07-17 15:36:47 -07:00
|
|
|
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
|
2018-07-18 21:08:47 -07:00
|
|
|
const CInt = @import("c_int.zig").CInt;
|
2018-07-14 12:45:15 -07:00
|
|
|
|
|
|
|
/// Data that is local to the event loop.
|
|
|
|
pub const EventLoopLocal = struct {
|
|
|
|
loop: *event.Loop,
|
|
|
|
llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),
|
|
|
|
|
2018-07-16 17:52:50 -07:00
|
|
|
/// TODO pool these so that it doesn't have to lock
|
|
|
|
prng: event.Locked(std.rand.DefaultPrng),
|
|
|
|
|
2018-07-17 15:36:47 -07:00
|
|
|
native_libc: event.Future(LibCInstallation),
|
|
|
|
|
2018-07-19 20:52:44 -07:00
|
|
|
cwd: []const u8,
|
|
|
|
|
2018-07-16 17:52:50 -07:00
|
|
|
var lazy_init_targets = std.lazyInit(void);
|
|
|
|
|
|
|
|
fn init(loop: *event.Loop) !EventLoopLocal {
|
|
|
|
lazy_init_targets.get() orelse {
|
|
|
|
Target.initializeAll();
|
|
|
|
lazy_init_targets.resolve();
|
|
|
|
};
|
|
|
|
|
|
|
|
var seed_bytes: [@sizeOf(u64)]u8 = undefined;
|
|
|
|
try std.os.getRandomBytes(seed_bytes[0..]);
|
|
|
|
const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big);
|
2018-07-19 20:52:44 -07:00
|
|
|
|
|
|
|
const cwd = try os.getCwd(loop.allocator);
|
|
|
|
errdefer loop.allocator.free(cwd);
|
|
|
|
|
2018-07-14 12:45:15 -07:00
|
|
|
return EventLoopLocal{
|
|
|
|
.loop = loop,
|
|
|
|
.llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
|
2018-07-16 17:52:50 -07:00
|
|
|
.prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)),
|
2018-07-17 15:36:47 -07:00
|
|
|
.native_libc = event.Future(LibCInstallation).init(loop),
|
2018-07-19 20:52:44 -07:00
|
|
|
.cwd = cwd,
|
2018-07-14 12:45:15 -07:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-07-18 21:08:47 -07:00
|
|
|
/// Must be called only after EventLoop.run completes.
|
2018-07-14 12:45:15 -07:00
|
|
|
fn deinit(self: *EventLoopLocal) void {
|
2018-07-19 20:52:44 -07:00
|
|
|
self.loop.allocator.free(self.cwd);
|
2018-07-14 12:45:15 -07:00
|
|
|
while (self.llvm_handle_pool.pop()) |node| {
|
|
|
|
c.LLVMContextDispose(node.data);
|
|
|
|
self.loop.allocator.destroy(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets an exclusive handle on any LlvmContext.
|
|
|
|
/// Caller must release the handle when done.
|
|
|
|
pub fn getAnyLlvmContext(self: *EventLoopLocal) !LlvmHandle {
|
|
|
|
if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node };
|
|
|
|
|
|
|
|
const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory;
|
|
|
|
errdefer c.LLVMContextDispose(context_ref);
|
|
|
|
|
|
|
|
const node = try self.loop.allocator.create(std.atomic.Stack(llvm.ContextRef).Node{
|
|
|
|
.next = undefined,
|
|
|
|
.data = context_ref,
|
|
|
|
});
|
|
|
|
errdefer self.loop.allocator.destroy(node);
|
|
|
|
|
|
|
|
return LlvmHandle{ .node = node };
|
|
|
|
}
|
2018-07-17 15:36:47 -07:00
|
|
|
|
|
|
|
pub async fn getNativeLibC(self: *EventLoopLocal) !*LibCInstallation {
|
|
|
|
if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr;
|
|
|
|
try await (async self.native_libc.data.findNative(self.loop) catch unreachable);
|
|
|
|
self.native_libc.resolve();
|
|
|
|
return &self.native_libc.data;
|
|
|
|
}
|
2018-07-14 12:45:15 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
pub const LlvmHandle = struct {
|
|
|
|
node: *std.atomic.Stack(llvm.ContextRef).Node,
|
|
|
|
|
|
|
|
pub fn release(self: LlvmHandle, event_loop_local: *EventLoopLocal) void {
|
|
|
|
event_loop_local.llvm_handle_pool.push(self.node);
|
|
|
|
}
|
|
|
|
};
|
2017-12-22 21:29:39 -08:00
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
pub const Compilation = struct {
|
2018-07-14 12:45:15 -07:00
|
|
|
event_loop_local: *EventLoopLocal,
|
2018-06-29 12:39:55 -07:00
|
|
|
loop: *event.Loop,
|
2017-12-22 21:29:39 -08:00
|
|
|
name: Buffer,
|
2018-07-16 17:52:50 -07:00
|
|
|
llvm_triple: Buffer,
|
2017-12-22 21:29:39 -08:00
|
|
|
root_src_path: ?[]const u8,
|
|
|
|
target: Target,
|
2018-07-16 17:52:50 -07:00
|
|
|
llvm_target: llvm.TargetRef,
|
2017-12-22 21:29:39 -08:00
|
|
|
build_mode: builtin.Mode,
|
|
|
|
zig_lib_dir: []const u8,
|
2018-07-16 17:52:50 -07:00
|
|
|
zig_std_dir: []const u8,
|
|
|
|
|
|
|
|
/// lazily created when we need it
|
|
|
|
tmp_dir: event.Future(BuildError![]u8),
|
2017-12-22 21:29:39 -08:00
|
|
|
|
|
|
|
version_major: u32,
|
|
|
|
version_minor: u32,
|
|
|
|
version_patch: u32,
|
|
|
|
|
|
|
|
linker_script: ?[]const u8,
|
|
|
|
cache_dir: []const u8,
|
|
|
|
out_h_path: ?[]const u8,
|
|
|
|
|
|
|
|
is_test: bool,
|
|
|
|
each_lib_rpath: bool,
|
|
|
|
strip: bool,
|
|
|
|
is_static: bool,
|
|
|
|
linker_rdynamic: bool,
|
|
|
|
|
|
|
|
clang_argv: []const []const u8,
|
|
|
|
llvm_argv: []const []const u8,
|
|
|
|
lib_dirs: []const []const u8,
|
|
|
|
rpath_list: []const []const u8,
|
|
|
|
assembly_files: []const []const u8,
|
2018-07-16 17:52:50 -07:00
|
|
|
|
|
|
|
/// paths that are explicitly provided by the user to link against
|
2017-12-22 21:29:39 -08:00
|
|
|
link_objects: []const []const u8,
|
|
|
|
|
2018-07-16 17:52:50 -07:00
|
|
|
/// functions that have their own objects that we need to link
|
|
|
|
/// it uses an optional pointer so that tombstone removals are possible
|
|
|
|
fn_link_set: event.Locked(FnLinkSet),
|
|
|
|
|
|
|
|
pub const FnLinkSet = std.LinkedList(?*Value.Fn);
|
|
|
|
|
2017-12-22 21:29:39 -08:00
|
|
|
windows_subsystem_windows: bool,
|
|
|
|
windows_subsystem_console: bool,
|
|
|
|
|
2018-05-31 07:56:59 -07:00
|
|
|
link_libs_list: ArrayList(*LinkLib),
|
|
|
|
libc_link_lib: ?*LinkLib,
|
2017-12-22 21:29:39 -08:00
|
|
|
|
2018-05-30 15:26:09 -07:00
|
|
|
err_color: errmsg.Color,
|
2017-12-22 21:29:39 -08:00
|
|
|
|
|
|
|
verbose_tokenize: bool,
|
|
|
|
verbose_ast_tree: bool,
|
|
|
|
verbose_ast_fmt: bool,
|
|
|
|
verbose_cimport: bool,
|
|
|
|
verbose_ir: bool,
|
|
|
|
verbose_llvm_ir: bool,
|
|
|
|
verbose_link: bool,
|
|
|
|
|
|
|
|
darwin_frameworks: []const []const u8,
|
|
|
|
darwin_version_min: DarwinVersionMin,
|
|
|
|
|
|
|
|
test_filters: []const []const u8,
|
|
|
|
test_name_prefix: ?[]const u8,
|
|
|
|
|
|
|
|
emit_file_type: Emit,
|
|
|
|
|
|
|
|
kind: Kind,
|
|
|
|
|
2018-06-29 12:39:55 -07:00
|
|
|
link_out_file: ?[]const u8,
|
|
|
|
events: *event.Channel(Event),
|
|
|
|
|
2018-07-05 12:09:02 -07:00
|
|
|
exported_symbol_names: event.Locked(Decl.Table),
|
|
|
|
|
2018-07-10 12:17:01 -07:00
|
|
|
/// Before code generation starts, must wait on this group to make sure
|
|
|
|
/// the build is complete.
|
2018-07-16 17:52:50 -07:00
|
|
|
prelink_group: event.Group(BuildError!void),
|
2018-07-10 12:17:01 -07:00
|
|
|
|
2018-07-10 17:18:43 -07:00
|
|
|
compile_errors: event.Locked(CompileErrList),
|
2018-07-10 12:17:01 -07:00
|
|
|
|
2018-07-12 12:08:40 -07:00
|
|
|
meta_type: *Type.MetaType,
|
|
|
|
void_type: *Type.Void,
|
|
|
|
bool_type: *Type.Bool,
|
|
|
|
noreturn_type: *Type.NoReturn,
|
2018-07-18 21:08:47 -07:00
|
|
|
comptime_int_type: *Type.ComptimeInt,
|
2018-07-12 12:08:40 -07:00
|
|
|
|
|
|
|
void_value: *Value.Void,
|
|
|
|
true_value: *Value.Bool,
|
|
|
|
false_value: *Value.Bool,
|
|
|
|
noreturn_value: *Value.NoReturn,
|
|
|
|
|
2018-07-16 17:52:50 -07:00
|
|
|
target_machine: llvm.TargetMachineRef,
|
|
|
|
target_data_ref: llvm.TargetDataRef,
|
|
|
|
target_layout_str: [*]u8,
|
|
|
|
|
|
|
|
/// for allocating things which have the same lifetime as this Compilation
|
|
|
|
arena_allocator: std.heap.ArenaAllocator,
|
|
|
|
|
|
|
|
root_package: *Package,
|
|
|
|
std_package: *Package,
|
|
|
|
|
2018-07-17 21:34:42 -07:00
|
|
|
override_libc: ?*LibCInstallation,
|
|
|
|
|
|
|
|
/// need to wait on this group before deinitializing
|
|
|
|
deinit_group: event.Group(void),
|
|
|
|
|
|
|
|
destroy_handle: promise,
|
|
|
|
|
2018-07-18 14:40:59 -07:00
|
|
|
have_err_ret_tracing: bool,
|
|
|
|
|
2018-07-18 21:08:47 -07:00
|
|
|
/// not locked because it is read-only
|
|
|
|
primitive_type_table: TypeTable,
|
|
|
|
|
|
|
|
int_type_table: event.Locked(IntTypeTable),
|
|
|
|
|
|
|
|
c_int_types: [CInt.list.len]*Type.Int,
|
|
|
|
|
|
|
|
const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
|
|
|
|
const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8);
|
|
|
|
|
2018-07-10 17:18:43 -07:00
|
|
|
const CompileErrList = std.ArrayList(*errmsg.Msg);
|
2018-07-10 12:17:01 -07:00
|
|
|
|
2018-06-29 12:39:55 -07:00
|
|
|
// TODO handle some of these earlier and report them in a way other than error codes
|
|
|
|
pub const BuildError = error{
|
|
|
|
OutOfMemory,
|
|
|
|
EndOfStream,
|
|
|
|
BadFd,
|
|
|
|
Io,
|
|
|
|
IsDir,
|
|
|
|
Unexpected,
|
|
|
|
SystemResources,
|
|
|
|
SharingViolation,
|
|
|
|
PathAlreadyExists,
|
|
|
|
FileNotFound,
|
|
|
|
AccessDenied,
|
|
|
|
PipeBusy,
|
|
|
|
FileTooBig,
|
|
|
|
SymLinkLoop,
|
|
|
|
ProcessFdQuotaExceeded,
|
|
|
|
NameTooLong,
|
|
|
|
SystemFdQuotaExceeded,
|
|
|
|
NoDevice,
|
|
|
|
PathNotFound,
|
|
|
|
NoSpaceLeft,
|
|
|
|
NotDir,
|
|
|
|
FileSystem,
|
|
|
|
OperationAborted,
|
|
|
|
IoPending,
|
|
|
|
BrokenPipe,
|
|
|
|
WouldBlock,
|
|
|
|
FileClosed,
|
|
|
|
DestinationAddressRequired,
|
|
|
|
DiskQuota,
|
|
|
|
InputOutput,
|
|
|
|
NoStdHandles,
|
2018-07-02 12:25:23 -07:00
|
|
|
Overflow,
|
2018-07-02 14:55:32 -07:00
|
|
|
NotSupported,
|
2018-07-10 17:18:43 -07:00
|
|
|
BufferTooSmall,
|
2018-07-13 18:56:38 -07:00
|
|
|
Unimplemented, // TODO remove this one
|
|
|
|
SemanticAnalysisFailed, // TODO remove this one
|
2018-07-16 17:52:50 -07:00
|
|
|
ReadOnlyFileSystem,
|
|
|
|
LinkQuotaExceeded,
|
|
|
|
EnvironmentVariableNotFound,
|
2018-07-16 21:01:36 -07:00
|
|
|
AppDataDirUnavailable,
|
2018-07-17 10:18:13 -07:00
|
|
|
LinkFailed,
|
2018-07-17 21:34:42 -07:00
|
|
|
LibCRequiredButNotProvidedOrFound,
|
|
|
|
LibCMissingDynamicLinker,
|
2018-06-29 12:39:55 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
pub const Event = union(enum) {
|
|
|
|
Ok,
|
|
|
|
Error: BuildError,
|
2018-07-13 18:56:38 -07:00
|
|
|
Fail: []*errmsg.Msg,
|
2018-06-29 12:39:55 -07:00
|
|
|
};
|
|
|
|
|
2017-12-22 21:29:39 -08:00
|
|
|
pub const DarwinVersionMin = union(enum) {
|
|
|
|
None,
|
|
|
|
MacOS: []const u8,
|
|
|
|
Ios: []const u8,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub const Kind = enum {
|
|
|
|
Exe,
|
|
|
|
Lib,
|
|
|
|
Obj,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub const LinkLib = struct {
|
|
|
|
name: []const u8,
|
|
|
|
path: ?[]const u8,
|
2018-05-17 20:21:44 -07:00
|
|
|
|
2017-12-22 21:29:39 -08:00
|
|
|
/// the list of symbols we depend on from this lib
|
|
|
|
symbols: ArrayList([]u8),
|
|
|
|
provided_explicitly: bool,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub const Emit = enum {
|
|
|
|
Binary,
|
|
|
|
Assembly,
|
|
|
|
LlvmIr,
|
|
|
|
};
|
|
|
|
|
2018-06-20 14:33:29 -07:00
|
|
|
pub fn create(
|
2018-07-14 12:45:15 -07:00
|
|
|
event_loop_local: *EventLoopLocal,
|
2018-06-20 14:33:29 -07:00
|
|
|
name: []const u8,
|
|
|
|
root_src_path: ?[]const u8,
|
2018-07-16 17:52:50 -07:00
|
|
|
target: Target,
|
2018-06-20 14:33:29 -07:00
|
|
|
kind: Kind,
|
|
|
|
build_mode: builtin.Mode,
|
2018-07-16 17:52:50 -07:00
|
|
|
is_static: bool,
|
2018-06-20 14:33:29 -07:00
|
|
|
zig_lib_dir: []const u8,
|
|
|
|
cache_dir: []const u8,
|
2018-07-14 13:12:41 -07:00
|
|
|
) !*Compilation {
|
2018-07-14 12:45:15 -07:00
|
|
|
const loop = event_loop_local.loop;
|
2018-07-16 17:52:50 -07:00
|
|
|
const comp = try event_loop_local.loop.allocator.create(Compilation{
|
2018-06-29 12:39:55 -07:00
|
|
|
.loop = loop,
|
2018-07-16 17:52:50 -07:00
|
|
|
.arena_allocator = std.heap.ArenaAllocator.init(loop.allocator),
|
2018-07-14 12:45:15 -07:00
|
|
|
.event_loop_local = event_loop_local,
|
2018-07-16 17:52:50 -07:00
|
|
|
.events = undefined,
|
2017-12-22 21:29:39 -08:00
|
|
|
.root_src_path = root_src_path,
|
2018-07-16 17:52:50 -07:00
|
|
|
.target = target,
|
|
|
|
.llvm_target = undefined,
|
2017-12-22 21:29:39 -08:00
|
|
|
.kind = kind,
|
|
|
|
.build_mode = build_mode,
|
|
|
|
.zig_lib_dir = zig_lib_dir,
|
2018-07-16 17:52:50 -07:00
|
|
|
.zig_std_dir = undefined,
|
2017-12-22 21:29:39 -08:00
|
|
|
.cache_dir = cache_dir,
|
2018-07-16 17:52:50 -07:00
|
|
|
.tmp_dir = event.Future(BuildError![]u8).init(loop),
|
|
|
|
|
|
|
|
.name = undefined,
|
|
|
|
.llvm_triple = undefined,
|
2017-12-22 21:29:39 -08:00
|
|
|
|
|
|
|
.version_major = 0,
|
|
|
|
.version_minor = 0,
|
|
|
|
.version_patch = 0,
|
|
|
|
|
|
|
|
.verbose_tokenize = false,
|
|
|
|
.verbose_ast_tree = false,
|
|
|
|
.verbose_ast_fmt = false,
|
|
|
|
.verbose_cimport = false,
|
|
|
|
.verbose_ir = false,
|
|
|
|
.verbose_llvm_ir = false,
|
|
|
|
.verbose_link = false,
|
|
|
|
|
|
|
|
.linker_script = null,
|
|
|
|
.out_h_path = null,
|
|
|
|
.is_test = false,
|
|
|
|
.each_lib_rpath = false,
|
|
|
|
.strip = false,
|
2018-07-16 17:52:50 -07:00
|
|
|
.is_static = is_static,
|
2017-12-22 21:29:39 -08:00
|
|
|
.linker_rdynamic = false,
|
|
|
|
.clang_argv = [][]const u8{},
|
|
|
|
.llvm_argv = [][]const u8{},
|
|
|
|
.lib_dirs = [][]const u8{},
|
|
|
|
.rpath_list = [][]const u8{},
|
|
|
|
.assembly_files = [][]const u8{},
|
|
|
|
.link_objects = [][]const u8{},
|
2018-07-16 17:52:50 -07:00
|
|
|
.fn_link_set = event.Locked(FnLinkSet).init(loop, FnLinkSet.init()),
|
2017-12-22 21:29:39 -08:00
|
|
|
.windows_subsystem_windows = false,
|
|
|
|
.windows_subsystem_console = false,
|
2018-07-16 17:52:50 -07:00
|
|
|
.link_libs_list = undefined,
|
2017-12-22 21:29:39 -08:00
|
|
|
.libc_link_lib = null,
|
2018-05-30 15:26:09 -07:00
|
|
|
.err_color = errmsg.Color.Auto,
|
2017-12-22 21:29:39 -08:00
|
|
|
.darwin_frameworks = [][]const u8{},
|
|
|
|
.darwin_version_min = DarwinVersionMin.None,
|
|
|
|
.test_filters = [][]const u8{},
|
|
|
|
.test_name_prefix = null,
|
|
|
|
.emit_file_type = Emit.Binary,
|
2018-06-29 12:39:55 -07:00
|
|
|
.link_out_file = null,
|
2018-07-05 12:09:02 -07:00
|
|
|
.exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),
|
2018-07-16 17:52:50 -07:00
|
|
|
.prelink_group = event.Group(BuildError!void).init(loop),
|
2018-07-17 21:34:42 -07:00
|
|
|
.deinit_group = event.Group(void).init(loop),
|
2018-07-10 17:18:43 -07:00
|
|
|
.compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)),
|
2018-07-18 21:08:47 -07:00
|
|
|
.int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)),
|
|
|
|
.c_int_types = undefined,
|
2018-07-12 12:08:40 -07:00
|
|
|
|
|
|
|
.meta_type = undefined,
|
|
|
|
.void_type = undefined,
|
|
|
|
.void_value = undefined,
|
|
|
|
.bool_type = undefined,
|
|
|
|
.true_value = undefined,
|
|
|
|
.false_value = undefined,
|
|
|
|
.noreturn_type = undefined,
|
|
|
|
.noreturn_value = undefined,
|
2018-07-18 21:08:47 -07:00
|
|
|
.comptime_int_type = undefined,
|
2018-07-16 17:52:50 -07:00
|
|
|
|
|
|
|
.target_machine = undefined,
|
|
|
|
.target_data_ref = undefined,
|
|
|
|
.target_layout_str = undefined,
|
|
|
|
|
|
|
|
.root_package = undefined,
|
|
|
|
.std_package = undefined,
|
2018-07-17 21:34:42 -07:00
|
|
|
|
|
|
|
.override_libc = null,
|
|
|
|
.destroy_handle = undefined,
|
2018-07-18 14:40:59 -07:00
|
|
|
.have_err_ret_tracing = false,
|
2018-07-18 21:08:47 -07:00
|
|
|
.primitive_type_table = undefined,
|
2018-07-12 12:08:40 -07:00
|
|
|
});
|
2018-07-16 17:52:50 -07:00
|
|
|
errdefer {
|
2018-07-18 21:08:47 -07:00
|
|
|
comp.int_type_table.private_data.deinit();
|
2018-07-16 17:52:50 -07:00
|
|
|
comp.arena_allocator.deinit();
|
|
|
|
comp.loop.allocator.destroy(comp);
|
|
|
|
}
|
|
|
|
|
|
|
|
comp.name = try Buffer.init(comp.arena(), name);
|
|
|
|
comp.llvm_triple = try target.getTriple(comp.arena());
|
|
|
|
comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);
|
|
|
|
comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
|
|
|
|
comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std");
|
2018-07-18 21:08:47 -07:00
|
|
|
comp.primitive_type_table = TypeTable.init(comp.arena());
|
2018-07-16 17:52:50 -07:00
|
|
|
|
|
|
|
const opt_level = switch (build_mode) {
|
|
|
|
builtin.Mode.Debug => llvm.CodeGenLevelNone,
|
|
|
|
else => llvm.CodeGenLevelAggressive,
|
|
|
|
};
|
|
|
|
|
|
|
|
const reloc_mode = if (is_static) llvm.RelocStatic else llvm.RelocPIC;
|
|
|
|
|
|
|
|
// LLVM creates invalid binaries on Windows sometimes.
|
|
|
|
// See https://github.com/ziglang/zig/issues/508
|
|
|
|
// As a workaround we do not use target native features on Windows.
|
|
|
|
var target_specific_cpu_args: ?[*]u8 = null;
|
|
|
|
var target_specific_cpu_features: ?[*]u8 = null;
|
|
|
|
errdefer llvm.DisposeMessage(target_specific_cpu_args);
|
|
|
|
errdefer llvm.DisposeMessage(target_specific_cpu_features);
|
|
|
|
if (target == Target.Native and !target.isWindows()) {
|
|
|
|
target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory;
|
|
|
|
target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory;
|
|
|
|
}
|
|
|
|
|
|
|
|
comp.target_machine = llvm.CreateTargetMachine(
|
|
|
|
comp.llvm_target,
|
|
|
|
comp.llvm_triple.ptr(),
|
|
|
|
target_specific_cpu_args orelse c"",
|
|
|
|
target_specific_cpu_features orelse c"",
|
|
|
|
opt_level,
|
|
|
|
reloc_mode,
|
|
|
|
llvm.CodeModelDefault,
|
|
|
|
) orelse return error.OutOfMemory;
|
|
|
|
errdefer llvm.DisposeTargetMachine(comp.target_machine);
|
|
|
|
|
|
|
|
comp.target_data_ref = llvm.CreateTargetDataLayout(comp.target_machine) orelse return error.OutOfMemory;
|
|
|
|
errdefer llvm.DisposeTargetData(comp.target_data_ref);
|
|
|
|
|
|
|
|
comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory;
|
|
|
|
errdefer llvm.DisposeMessage(comp.target_layout_str);
|
|
|
|
|
|
|
|
comp.events = try event.Channel(Event).create(comp.loop, 0);
|
|
|
|
errdefer comp.events.destroy();
|
|
|
|
|
|
|
|
if (root_src_path) |root_src| {
|
|
|
|
const dirname = std.os.path.dirname(root_src) orelse ".";
|
|
|
|
const basename = std.os.path.basename(root_src);
|
|
|
|
|
|
|
|
comp.root_package = try Package.create(comp.arena(), dirname, basename);
|
|
|
|
comp.std_package = try Package.create(comp.arena(), comp.zig_std_dir, "index.zig");
|
|
|
|
try comp.root_package.add("std", comp.std_package);
|
|
|
|
} else {
|
|
|
|
comp.root_package = try Package.create(comp.arena(), ".", "");
|
|
|
|
}
|
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
try comp.initTypes();
|
2018-07-17 21:34:42 -07:00
|
|
|
|
|
|
|
comp.destroy_handle = try async<loop.allocator> comp.internalDeinit();
|
2018-07-16 17:52:50 -07:00
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
return comp;
|
2018-07-12 12:08:40 -07:00
|
|
|
}
|
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
/// it does ref the result because it could be an arbitrary integer size
|
|
|
|
pub fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type {
|
|
|
|
if (name.len >= 2) {
|
|
|
|
switch (name[0]) {
|
|
|
|
'i', 'u' => blk: {
|
|
|
|
for (name[1..]) |byte|
|
|
|
|
switch (byte) {
|
|
|
|
'0'...'9' => {},
|
|
|
|
else => break :blk,
|
|
|
|
};
|
|
|
|
const is_signed = name[0] == 'i';
|
|
|
|
const bit_count = std.fmt.parseUnsigned(u32, name[1..], 10) catch |err| switch (err) {
|
|
|
|
error.Overflow => return error.Overflow,
|
|
|
|
error.InvalidCharacter => unreachable, // we just checked the characters above
|
|
|
|
};
|
|
|
|
@panic("get int type - need to make everything async");
|
|
|
|
},
|
|
|
|
else => {},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (comp.primitive_type_table.get(name)) |entry| {
|
|
|
|
entry.value.base.ref();
|
|
|
|
return entry.value;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
fn initTypes(comp: *Compilation) !void {
|
2018-07-18 21:08:47 -07:00
|
|
|
comp.meta_type = try comp.arena().create(Type.MetaType{
|
2018-07-12 12:08:40 -07:00
|
|
|
.base = Type{
|
2018-07-18 21:08:47 -07:00
|
|
|
.name = "type",
|
2018-07-12 12:08:40 -07:00
|
|
|
.base = Value{
|
|
|
|
.id = Value.Id.Type,
|
|
|
|
.typeof = undefined,
|
2018-07-14 12:45:15 -07:00
|
|
|
.ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice
|
2018-07-12 12:08:40 -07:00
|
|
|
},
|
|
|
|
.id = builtin.TypeId.Type,
|
|
|
|
},
|
|
|
|
.value = undefined,
|
|
|
|
});
|
2018-07-14 13:12:41 -07:00
|
|
|
comp.meta_type.value = &comp.meta_type.base;
|
|
|
|
comp.meta_type.base.base.typeof = &comp.meta_type.base;
|
2018-07-18 21:08:47 -07:00
|
|
|
assert((try comp.primitive_type_table.put(comp.meta_type.base.name, &comp.meta_type.base)) == null);
|
2018-07-12 12:08:40 -07:00
|
|
|
|
2018-07-18 21:08:47 -07:00
|
|
|
comp.void_type = try comp.arena().create(Type.Void{
|
2018-07-12 12:08:40 -07:00
|
|
|
.base = Type{
|
2018-07-18 21:08:47 -07:00
|
|
|
.name = "void",
|
2018-07-12 12:08:40 -07:00
|
|
|
.base = Value{
|
|
|
|
.id = Value.Id.Type,
|
2018-07-14 13:12:41 -07:00
|
|
|
.typeof = &Type.MetaType.get(comp).base,
|
2018-07-14 12:45:15 -07:00
|
|
|
.ref_count = std.atomic.Int(usize).init(1),
|
2018-07-12 12:08:40 -07:00
|
|
|
},
|
|
|
|
.id = builtin.TypeId.Void,
|
|
|
|
},
|
|
|
|
});
|
2018-07-18 21:08:47 -07:00
|
|
|
assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null);
|
2018-07-12 12:08:40 -07:00
|
|
|
|
2018-07-18 21:08:47 -07:00
|
|
|
comp.noreturn_type = try comp.arena().create(Type.NoReturn{
|
2018-07-12 12:08:40 -07:00
|
|
|
.base = Type{
|
2018-07-18 21:08:47 -07:00
|
|
|
.name = "noreturn",
|
2018-07-12 12:08:40 -07:00
|
|
|
.base = Value{
|
|
|
|
.id = Value.Id.Type,
|
2018-07-14 13:12:41 -07:00
|
|
|
.typeof = &Type.MetaType.get(comp).base,
|
2018-07-14 12:45:15 -07:00
|
|
|
.ref_count = std.atomic.Int(usize).init(1),
|
2018-07-12 12:08:40 -07:00
|
|
|
},
|
|
|
|
.id = builtin.TypeId.NoReturn,
|
|
|
|
},
|
|
|
|
});
|
2018-07-18 21:08:47 -07:00
|
|
|
assert((try comp.primitive_type_table.put(comp.noreturn_type.base.name, &comp.noreturn_type.base)) == null);
|
2018-07-12 12:08:40 -07:00
|
|
|
|
2018-07-18 21:08:47 -07:00
|
|
|
comp.comptime_int_type = try comp.arena().create(Type.ComptimeInt{
|
2018-07-12 12:08:40 -07:00
|
|
|
.base = Type{
|
2018-07-18 21:08:47 -07:00
|
|
|
.name = "comptime_int",
|
|
|
|
.base = Value{
|
|
|
|
.id = Value.Id.Type,
|
|
|
|
.typeof = &Type.MetaType.get(comp).base,
|
|
|
|
.ref_count = std.atomic.Int(usize).init(1),
|
|
|
|
},
|
|
|
|
.id = builtin.TypeId.ComptimeInt,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
assert((try comp.primitive_type_table.put(comp.comptime_int_type.base.name, &comp.comptime_int_type.base)) == null);
|
|
|
|
|
|
|
|
comp.bool_type = try comp.arena().create(Type.Bool{
|
|
|
|
.base = Type{
|
|
|
|
.name = "bool",
|
2018-07-12 12:08:40 -07:00
|
|
|
.base = Value{
|
|
|
|
.id = Value.Id.Type,
|
2018-07-14 13:12:41 -07:00
|
|
|
.typeof = &Type.MetaType.get(comp).base,
|
2018-07-14 12:45:15 -07:00
|
|
|
.ref_count = std.atomic.Int(usize).init(1),
|
2018-07-12 12:08:40 -07:00
|
|
|
},
|
|
|
|
.id = builtin.TypeId.Bool,
|
|
|
|
},
|
|
|
|
});
|
2018-07-18 21:08:47 -07:00
|
|
|
assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null);
|
2018-07-12 12:08:40 -07:00
|
|
|
|
2018-07-18 21:08:47 -07:00
|
|
|
comp.void_value = try comp.arena().create(Value.Void{
|
2018-07-12 12:08:40 -07:00
|
|
|
.base = Value{
|
|
|
|
.id = Value.Id.Void,
|
2018-07-14 13:12:41 -07:00
|
|
|
.typeof = &Type.Void.get(comp).base,
|
2018-07-14 12:45:15 -07:00
|
|
|
.ref_count = std.atomic.Int(usize).init(1),
|
2018-07-12 12:08:40 -07:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2018-07-18 21:08:47 -07:00
|
|
|
comp.true_value = try comp.arena().create(Value.Bool{
|
2018-07-12 12:08:40 -07:00
|
|
|
.base = Value{
|
|
|
|
.id = Value.Id.Bool,
|
2018-07-14 13:12:41 -07:00
|
|
|
.typeof = &Type.Bool.get(comp).base,
|
2018-07-14 12:45:15 -07:00
|
|
|
.ref_count = std.atomic.Int(usize).init(1),
|
2018-07-12 12:08:40 -07:00
|
|
|
},
|
|
|
|
.x = true,
|
|
|
|
});
|
|
|
|
|
2018-07-18 21:08:47 -07:00
|
|
|
comp.false_value = try comp.arena().create(Value.Bool{
|
2018-07-12 12:08:40 -07:00
|
|
|
.base = Value{
|
|
|
|
.id = Value.Id.Bool,
|
2018-07-14 13:12:41 -07:00
|
|
|
.typeof = &Type.Bool.get(comp).base,
|
2018-07-14 12:45:15 -07:00
|
|
|
.ref_count = std.atomic.Int(usize).init(1),
|
2018-07-12 12:08:40 -07:00
|
|
|
},
|
|
|
|
.x = false,
|
2018-06-20 08:40:21 -07:00
|
|
|
});
|
2018-07-12 12:08:40 -07:00
|
|
|
|
2018-07-18 21:08:47 -07:00
|
|
|
comp.noreturn_value = try comp.arena().create(Value.NoReturn{
|
2018-07-12 12:08:40 -07:00
|
|
|
.base = Value{
|
|
|
|
.id = Value.Id.NoReturn,
|
2018-07-14 13:12:41 -07:00
|
|
|
.typeof = &Type.NoReturn.get(comp).base,
|
2018-07-14 12:45:15 -07:00
|
|
|
.ref_count = std.atomic.Int(usize).init(1),
|
2018-07-12 12:08:40 -07:00
|
|
|
},
|
|
|
|
});
|
2017-12-22 21:29:39 -08:00
|
|
|
|
2018-07-18 21:08:47 -07:00
|
|
|
for (CInt.list) |cint, i| {
|
|
|
|
const c_int_type = try comp.arena().create(Type.Int{
|
|
|
|
.base = Type{
|
|
|
|
.name = cint.zig_name,
|
|
|
|
.base = Value{
|
|
|
|
.id = Value.Id.Type,
|
|
|
|
.typeof = &Type.MetaType.get(comp).base,
|
|
|
|
.ref_count = std.atomic.Int(usize).init(1),
|
|
|
|
},
|
|
|
|
.id = builtin.TypeId.Int,
|
|
|
|
},
|
|
|
|
.key = Type.Int.Key{
|
|
|
|
.is_signed = cint.is_signed,
|
|
|
|
.bit_count = comp.target.cIntTypeSizeInBits(cint.id),
|
|
|
|
},
|
|
|
|
.garbage_node = undefined,
|
|
|
|
});
|
|
|
|
comp.c_int_types[i] = c_int_type;
|
|
|
|
assert((try comp.primitive_type_table.put(cint.zig_name, &c_int_type.base)) == null);
|
|
|
|
}
|
2018-07-17 21:34:42 -07:00
|
|
|
}
|
|
|
|
|
2018-07-18 21:08:47 -07:00
|
|
|
/// This function can safely use async/await, because it manages Compilation's lifetime,
|
|
|
|
/// and EventLoopLocal.deinit will not be called until the event.Loop.run() completes.
|
2018-07-17 21:34:42 -07:00
|
|
|
async fn internalDeinit(self: *Compilation) void {
|
|
|
|
suspend;
|
2018-07-18 21:08:47 -07:00
|
|
|
|
2018-07-17 21:34:42 -07:00
|
|
|
await (async self.deinit_group.wait() catch unreachable);
|
|
|
|
if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| {
|
|
|
|
// TODO evented I/O?
|
|
|
|
os.deleteTree(self.arena(), tmp_dir) catch {};
|
|
|
|
} else |_| {};
|
|
|
|
|
2018-06-29 12:39:55 -07:00
|
|
|
self.events.destroy();
|
2017-12-22 21:29:39 -08:00
|
|
|
|
2018-07-16 17:52:50 -07:00
|
|
|
llvm.DisposeMessage(self.target_layout_str);
|
|
|
|
llvm.DisposeTargetData(self.target_data_ref);
|
|
|
|
llvm.DisposeTargetMachine(self.target_machine);
|
|
|
|
|
2018-07-18 21:08:47 -07:00
|
|
|
self.primitive_type_table.deinit();
|
|
|
|
|
2018-07-16 17:52:50 -07:00
|
|
|
self.arena_allocator.deinit();
|
|
|
|
self.gpa().destroy(self);
|
2017-12-22 21:29:39 -08:00
|
|
|
}
|
|
|
|
|
2018-07-17 21:34:42 -07:00
|
|
|
pub fn destroy(self: *Compilation) void {
|
|
|
|
resume self.destroy_handle;
|
|
|
|
}
|
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
pub fn build(self: *Compilation) !void {
|
2017-12-26 16:44:08 -08:00
|
|
|
if (self.llvm_argv.len != 0) {
|
2018-07-16 17:52:50 -07:00
|
|
|
var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{
|
2018-05-17 20:21:44 -07:00
|
|
|
[][]const u8{"zig (LLVM option parsing)"},
|
|
|
|
self.llvm_argv,
|
|
|
|
});
|
2017-12-26 16:44:08 -08:00
|
|
|
defer c_compatible_args.deinit();
|
2018-06-29 12:39:55 -07:00
|
|
|
// TODO this sets global state
|
2017-12-26 16:44:08 -08:00
|
|
|
c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);
|
|
|
|
}
|
|
|
|
|
2018-07-16 17:52:50 -07:00
|
|
|
_ = try async<self.gpa()> self.buildAsync();
|
2018-06-29 12:39:55 -07:00
|
|
|
}
|
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
async fn buildAsync(self: *Compilation) void {
|
2018-06-29 12:39:55 -07:00
|
|
|
while (true) {
|
|
|
|
// TODO directly awaiting async should guarantee memory allocation elision
|
2018-07-17 10:18:13 -07:00
|
|
|
const build_result = await (async self.compileAndLink() catch unreachable);
|
2018-07-13 18:56:38 -07:00
|
|
|
|
|
|
|
// this makes a handy error return trace and stack trace in debug mode
|
|
|
|
if (std.debug.runtime_safety) {
|
|
|
|
build_result catch unreachable;
|
|
|
|
}
|
|
|
|
|
2018-07-10 17:18:43 -07:00
|
|
|
const compile_errors = blk: {
|
|
|
|
const held = await (async self.compile_errors.acquire() catch unreachable);
|
|
|
|
defer held.release();
|
|
|
|
break :blk held.value.toOwnedSlice();
|
|
|
|
};
|
|
|
|
|
2018-07-13 18:56:38 -07:00
|
|
|
if (build_result) |_| {
|
|
|
|
if (compile_errors.len == 0) {
|
|
|
|
await (async self.events.put(Event.Ok) catch unreachable);
|
|
|
|
} else {
|
|
|
|
await (async self.events.put(Event{ .Fail = compile_errors }) catch unreachable);
|
|
|
|
}
|
|
|
|
} else |err| {
|
|
|
|
// if there's an error then the compile errors have dangling references
|
2018-07-16 17:52:50 -07:00
|
|
|
self.gpa().free(compile_errors);
|
2018-07-13 18:56:38 -07:00
|
|
|
|
|
|
|
await (async self.events.put(Event{ .Error = err }) catch unreachable);
|
2018-07-10 17:18:43 -07:00
|
|
|
}
|
2018-07-13 18:56:38 -07:00
|
|
|
|
2018-07-05 12:09:02 -07:00
|
|
|
// for now we stop after 1
|
|
|
|
return;
|
2018-06-29 12:39:55 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-17 10:18:13 -07:00
|
|
|
async fn compileAndLink(self: *Compilation) !void {
|
2018-07-19 12:11:39 -07:00
|
|
|
if (self.root_src_path) |root_src_path| {
|
|
|
|
// TODO async/await os.path.real
|
|
|
|
const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
|
|
|
|
try printError("unable to get real path '{}': {}", root_src_path, err);
|
|
|
|
return err;
|
|
|
|
};
|
|
|
|
const root_scope = blk: {
|
|
|
|
errdefer self.gpa().free(root_src_real_path);
|
2017-12-22 21:29:39 -08:00
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
// TODO async/await readFileAlloc()
|
|
|
|
const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| {
|
|
|
|
try printError("unable to open '{}': {}", root_src_real_path, err);
|
|
|
|
return err;
|
|
|
|
};
|
|
|
|
errdefer self.gpa().free(source_code);
|
2018-07-10 17:18:43 -07:00
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
var tree = try std.zig.parse(self.gpa(), source_code);
|
|
|
|
errdefer tree.deinit();
|
2018-07-05 12:09:02 -07:00
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
break :blk try Scope.Root.create(self, tree, root_src_real_path);
|
|
|
|
};
|
|
|
|
defer root_scope.base.deref(self);
|
2018-07-05 12:09:02 -07:00
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
const tree = &root_scope.tree;
|
2018-07-05 12:09:02 -07:00
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
const decls = try Scope.Decls.create(self, &root_scope.base);
|
|
|
|
defer decls.base.deref(self);
|
2018-07-10 12:17:01 -07:00
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
var decl_group = event.Group(BuildError!void).init(self.loop);
|
|
|
|
errdefer decl_group.cancelAll();
|
2018-07-18 14:40:59 -07:00
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
var it = tree.root_node.decls.iterator(0);
|
|
|
|
while (it.next()) |decl_ptr| {
|
|
|
|
const decl = decl_ptr.*;
|
|
|
|
switch (decl.id) {
|
|
|
|
ast.Node.Id.Comptime => {
|
|
|
|
const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
|
2018-07-05 12:09:02 -07:00
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
try decl_group.call(addCompTimeBlock, self, &decls.base, comptime_node);
|
|
|
|
},
|
|
|
|
ast.Node.Id.VarDecl => @panic("TODO"),
|
|
|
|
ast.Node.Id.FnProto => {
|
|
|
|
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
|
|
|
|
|
|
|
|
const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
|
|
|
|
try self.addCompileError(root_scope, Span{
|
|
|
|
.first = fn_proto.fn_token,
|
|
|
|
.last = fn_proto.fn_token + 1,
|
|
|
|
}, "missing function name");
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
|
|
|
|
const fn_decl = try self.gpa().create(Decl.Fn{
|
|
|
|
.base = Decl{
|
|
|
|
.id = Decl.Id.Fn,
|
|
|
|
.name = name,
|
|
|
|
.visib = parseVisibToken(tree, fn_proto.visib_token),
|
|
|
|
.resolution = event.Future(BuildError!void).init(self.loop),
|
|
|
|
.resolution_in_progress = 0,
|
|
|
|
.parent_scope = &decls.base,
|
|
|
|
},
|
|
|
|
.value = Decl.Fn.Val{ .Unresolved = {} },
|
|
|
|
.fn_proto = fn_proto,
|
|
|
|
});
|
|
|
|
errdefer self.gpa().destroy(fn_decl);
|
|
|
|
|
|
|
|
try decl_group.call(addTopLevelDecl, self, &fn_decl.base);
|
|
|
|
},
|
|
|
|
ast.Node.Id.TestDecl => @panic("TODO"),
|
|
|
|
else => unreachable,
|
|
|
|
}
|
2018-07-05 12:09:02 -07:00
|
|
|
}
|
2018-07-19 12:11:39 -07:00
|
|
|
try await (async decl_group.wait() catch unreachable);
|
2018-07-05 12:09:02 -07:00
|
|
|
}
|
2018-07-19 12:11:39 -07:00
|
|
|
|
2018-07-16 17:52:50 -07:00
|
|
|
try await (async self.prelink_group.wait() catch unreachable);
|
2018-07-17 10:18:13 -07:00
|
|
|
|
|
|
|
const any_prelink_errors = blk: {
|
|
|
|
const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
|
|
|
|
defer compile_errors.release();
|
|
|
|
|
|
|
|
break :blk compile_errors.value.len != 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!any_prelink_errors) {
|
2018-07-17 21:34:42 -07:00
|
|
|
try await (async link(self) catch unreachable);
|
2018-07-17 10:18:13 -07:00
|
|
|
}
|
2018-07-05 12:09:02 -07:00
|
|
|
}
|
|
|
|
|
2018-07-18 14:40:59 -07:00
|
|
|
/// caller takes ownership of resulting Code
|
|
|
|
async fn genAndAnalyzeCode(
|
|
|
|
comp: *Compilation,
|
|
|
|
scope: *Scope,
|
|
|
|
node: *ast.Node,
|
|
|
|
expected_type: ?*Type,
|
2018-07-19 12:11:39 -07:00
|
|
|
) !*ir.Code {
|
|
|
|
const unanalyzed_code = try await (async ir.gen(
|
2018-07-18 14:40:59 -07:00
|
|
|
comp,
|
|
|
|
node,
|
|
|
|
scope,
|
2018-07-19 12:11:39 -07:00
|
|
|
) catch unreachable);
|
2018-07-18 14:40:59 -07:00
|
|
|
defer unanalyzed_code.destroy(comp.gpa());
|
|
|
|
|
|
|
|
if (comp.verbose_ir) {
|
|
|
|
std.debug.warn("unanalyzed:\n");
|
|
|
|
unanalyzed_code.dump();
|
|
|
|
}
|
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
const analyzed_code = try await (async ir.analyze(
|
2018-07-18 14:40:59 -07:00
|
|
|
comp,
|
|
|
|
unanalyzed_code,
|
|
|
|
expected_type,
|
2018-07-19 12:11:39 -07:00
|
|
|
) catch unreachable);
|
2018-07-18 14:40:59 -07:00
|
|
|
errdefer analyzed_code.destroy(comp.gpa());
|
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
if (comp.verbose_ir) {
|
|
|
|
std.debug.warn("analyzed:\n");
|
|
|
|
analyzed_code.dump();
|
|
|
|
}
|
|
|
|
|
2018-07-18 14:40:59 -07:00
|
|
|
return analyzed_code;
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn addCompTimeBlock(
|
|
|
|
comp: *Compilation,
|
|
|
|
scope: *Scope,
|
|
|
|
comptime_node: *ast.Node.Comptime,
|
|
|
|
) !void {
|
|
|
|
const void_type = Type.Void.get(comp);
|
|
|
|
defer void_type.base.base.deref(comp);
|
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
const analyzed_code = (await (async genAndAnalyzeCode(
|
2018-07-18 14:40:59 -07:00
|
|
|
comp,
|
|
|
|
scope,
|
|
|
|
comptime_node.expr,
|
|
|
|
&void_type.base,
|
2018-07-19 12:11:39 -07:00
|
|
|
) catch unreachable)) catch |err| switch (err) {
|
|
|
|
// This poison value should not cause the errdefers to run. It simply means
|
|
|
|
// that comp.compile_errors is populated.
|
|
|
|
error.SemanticAnalysisFailed => return {},
|
|
|
|
else => return err,
|
|
|
|
};
|
2018-07-18 14:40:59 -07:00
|
|
|
analyzed_code.destroy(comp.gpa());
|
|
|
|
}
|
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void {
|
2018-07-19 12:11:39 -07:00
|
|
|
const tree = &decl.findRootScope().tree;
|
|
|
|
const is_export = decl.isExported(tree);
|
2018-07-05 12:09:02 -07:00
|
|
|
|
2018-07-10 12:17:01 -07:00
|
|
|
if (is_export) {
|
2018-07-16 17:52:50 -07:00
|
|
|
try self.prelink_group.call(verifyUniqueSymbol, self, decl);
|
|
|
|
try self.prelink_group.call(resolveDecl, self, decl);
|
2018-07-10 12:17:01 -07:00
|
|
|
}
|
|
|
|
}
|
2018-07-05 12:09:02 -07:00
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
fn addCompileError(self: *Compilation, root: *Scope.Root, span: Span, comptime fmt: []const u8, args: ...) !void {
|
|
|
|
const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
|
|
|
|
errdefer self.gpa().free(text);
|
2018-07-10 17:18:43 -07:00
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
try self.prelink_group.call(addCompileErrorAsync, self, root, span, text);
|
2018-07-10 17:18:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async fn addCompileErrorAsync(
|
2018-07-14 13:12:41 -07:00
|
|
|
self: *Compilation,
|
2018-07-19 12:11:39 -07:00
|
|
|
root: *Scope.Root,
|
2018-07-13 18:56:38 -07:00
|
|
|
span: Span,
|
2018-07-10 17:18:43 -07:00
|
|
|
text: []u8,
|
|
|
|
) !void {
|
2018-07-19 20:52:44 -07:00
|
|
|
const relpath = try os.path.relative(self.gpa(), self.event_loop_local.cwd, root.realpath);
|
|
|
|
errdefer self.gpa().free(relpath);
|
|
|
|
|
|
|
|
const msg = try self.gpa().create(errmsg.Msg{
|
|
|
|
.path = if (relpath.len < root.realpath.len) relpath else root.realpath,
|
2018-07-10 17:18:43 -07:00
|
|
|
.text = text,
|
2018-07-13 18:56:38 -07:00
|
|
|
.span = span,
|
2018-07-19 12:11:39 -07:00
|
|
|
.tree = &root.tree,
|
2018-07-10 17:18:43 -07:00
|
|
|
});
|
2018-07-19 20:52:44 -07:00
|
|
|
errdefer self.gpa().destroy(msg);
|
2018-07-10 17:18:43 -07:00
|
|
|
|
|
|
|
const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
|
|
|
|
defer compile_errors.release();
|
|
|
|
|
|
|
|
try compile_errors.value.append(msg);
|
|
|
|
}
|
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
async fn verifyUniqueSymbol(self: *Compilation, decl: *Decl) !void {
|
2018-07-10 12:17:01 -07:00
|
|
|
const exported_symbol_names = await (async self.exported_symbol_names.acquire() catch unreachable);
|
|
|
|
defer exported_symbol_names.release();
|
|
|
|
|
|
|
|
if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
|
2018-07-10 17:18:43 -07:00
|
|
|
try self.addCompileError(
|
2018-07-19 12:11:39 -07:00
|
|
|
decl.findRootScope(),
|
2018-07-10 17:18:43 -07:00
|
|
|
decl.getSpan(),
|
|
|
|
"exported symbol collision: '{}'",
|
|
|
|
decl.name,
|
|
|
|
);
|
2018-07-12 12:08:40 -07:00
|
|
|
// TODO add error note showing location of other symbol
|
2018-07-05 12:09:02 -07:00
|
|
|
}
|
2017-12-22 21:29:39 -08:00
|
|
|
}
|
|
|
|
|
2018-07-14 21:04:12 -07:00
|
|
|
pub fn haveLibC(self: *Compilation) bool {
|
|
|
|
return self.libc_link_lib != null;
|
|
|
|
}
|
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
pub fn addLinkLib(self: *Compilation, name: []const u8, provided_explicitly: bool) !*LinkLib {
|
2017-12-22 21:29:39 -08:00
|
|
|
const is_libc = mem.eql(u8, name, "c");
|
|
|
|
|
|
|
|
if (is_libc) {
|
|
|
|
if (self.libc_link_lib) |libc_link_lib| {
|
|
|
|
return libc_link_lib;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (self.link_libs_list.toSliceConst()) |existing_lib| {
|
|
|
|
if (mem.eql(u8, name, existing_lib.name)) {
|
|
|
|
return existing_lib;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-16 17:52:50 -07:00
|
|
|
const link_lib = try self.gpa().create(LinkLib{
|
2017-12-22 21:29:39 -08:00
|
|
|
.name = name,
|
|
|
|
.path = null,
|
|
|
|
.provided_explicitly = provided_explicitly,
|
2018-07-16 17:52:50 -07:00
|
|
|
.symbols = ArrayList([]u8).init(self.gpa()),
|
2018-06-20 08:40:21 -07:00
|
|
|
});
|
2018-01-07 13:51:46 -08:00
|
|
|
try self.link_libs_list.append(link_lib);
|
2017-12-22 21:29:39 -08:00
|
|
|
if (is_libc) {
|
|
|
|
self.libc_link_lib = link_lib;
|
2018-07-17 15:36:47 -07:00
|
|
|
|
|
|
|
// get a head start on looking for the native libc
|
2018-07-17 21:34:42 -07:00
|
|
|
if (self.target == Target.Native and self.override_libc == null) {
|
|
|
|
try self.deinit_group.call(startFindingNativeLibC, self);
|
2018-07-17 15:36:47 -07:00
|
|
|
}
|
2017-12-22 21:29:39 -08:00
|
|
|
}
|
|
|
|
return link_lib;
|
|
|
|
}
|
2018-06-29 12:39:55 -07:00
|
|
|
|
2018-07-17 15:36:47 -07:00
|
|
|
/// cancels itself so no need to await or cancel the promise.
|
|
|
|
async fn startFindingNativeLibC(self: *Compilation) void {
|
2018-07-17 21:34:42 -07:00
|
|
|
await (async self.loop.yield() catch unreachable);
|
2018-07-17 15:36:47 -07:00
|
|
|
// we don't care if it fails, we're just trying to kick off the future resolution
|
2018-07-17 21:34:42 -07:00
|
|
|
_ = (await (async self.event_loop_local.getNativeLibC() catch unreachable)) catch return;
|
2018-07-17 15:36:47 -07:00
|
|
|
}
|
|
|
|
|
2018-07-16 17:52:50 -07:00
|
|
|
/// General Purpose Allocator. Must free when done.
|
|
|
|
fn gpa(self: Compilation) *mem.Allocator {
|
2018-06-29 12:39:55 -07:00
|
|
|
return self.loop.allocator;
|
|
|
|
}
|
2018-07-16 17:52:50 -07:00
|
|
|
|
|
|
|
/// Arena Allocator. Automatically freed when the Compilation is destroyed.
|
|
|
|
fn arena(self: *Compilation) *mem.Allocator {
|
|
|
|
return &self.arena_allocator.allocator;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// If the temporary directory for this compilation has not been created, it creates it.
|
|
|
|
/// Then it creates a random file name in that dir and returns it.
|
|
|
|
pub async fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer {
|
|
|
|
const tmp_dir = try await (async self.getTmpDir() catch unreachable);
|
|
|
|
const file_prefix = await (async self.getRandomFileName() catch unreachable);
|
|
|
|
|
|
|
|
const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", file_prefix[0..], suffix);
|
|
|
|
defer self.gpa().free(file_name);
|
|
|
|
|
|
|
|
const full_path = try os.path.join(self.gpa(), tmp_dir, file_name[0..]);
|
|
|
|
errdefer self.gpa().free(full_path);
|
|
|
|
|
|
|
|
return Buffer.fromOwnedSlice(self.gpa(), full_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// If the temporary directory for this Compilation has not been created, creates it.
|
|
|
|
/// Then returns it. The directory is unique to this Compilation and cleaned up when
|
|
|
|
/// the Compilation deinitializes.
|
|
|
|
async fn getTmpDir(self: *Compilation) ![]const u8 {
|
|
|
|
if (await (async self.tmp_dir.start() catch unreachable)) |ptr| return ptr.*;
|
|
|
|
self.tmp_dir.data = await (async self.getTmpDirImpl() catch unreachable);
|
|
|
|
self.tmp_dir.resolve();
|
|
|
|
return self.tmp_dir.data;
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn getTmpDirImpl(self: *Compilation) ![]u8 {
|
|
|
|
const comp_dir_name = await (async self.getRandomFileName() catch unreachable);
|
|
|
|
const zig_dir_path = try getZigDir(self.gpa());
|
|
|
|
defer self.gpa().free(zig_dir_path);
|
|
|
|
|
|
|
|
const tmp_dir = try os.path.join(self.arena(), zig_dir_path, comp_dir_name[0..]);
|
|
|
|
try os.makePath(self.gpa(), tmp_dir);
|
|
|
|
return tmp_dir;
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn getRandomFileName(self: *Compilation) [12]u8 {
|
|
|
|
// here we replace the standard +/ with -_ so that it can be used in a file name
|
|
|
|
const b64_fs_encoder = std.base64.Base64Encoder.init(
|
|
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
|
|
|
|
std.base64.standard_pad_char,
|
|
|
|
);
|
|
|
|
|
|
|
|
var rand_bytes: [9]u8 = undefined;
|
|
|
|
|
|
|
|
{
|
|
|
|
const held = await (async self.event_loop_local.prng.acquire() catch unreachable);
|
|
|
|
defer held.release();
|
|
|
|
|
|
|
|
held.value.random.bytes(rand_bytes[0..]);
|
|
|
|
}
|
|
|
|
|
|
|
|
var result: [12]u8 = undefined;
|
|
|
|
b64_fs_encoder.encode(result[0..], rand_bytes);
|
|
|
|
return result;
|
|
|
|
}
|
2018-07-18 21:08:47 -07:00
|
|
|
|
|
|
|
fn registerGarbage(comp: *Compilation, comptime T: type, node: *std.atomic.Stack(*T).Node) void {
|
|
|
|
// TODO put the garbage somewhere
|
|
|
|
}
|
2018-07-19 12:11:39 -07:00
|
|
|
|
|
|
|
/// Returns a value which has been ref()'d once
|
|
|
|
async fn analyzeConstValue(comp: *Compilation, scope: *Scope, node: *ast.Node, expected_type: *Type) !*Value {
|
|
|
|
const analyzed_code = try await (async comp.genAndAnalyzeCode(scope, node, expected_type) catch unreachable);
|
|
|
|
defer analyzed_code.destroy(comp.gpa());
|
|
|
|
|
|
|
|
return analyzed_code.getCompTimeResult(comp);
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn analyzeTypeExpr(comp: *Compilation, scope: *Scope, node: *ast.Node) !*Type {
|
|
|
|
const meta_type = &Type.MetaType.get(comp).base;
|
|
|
|
defer meta_type.base.deref(comp);
|
|
|
|
|
|
|
|
const result_val = try await (async comp.analyzeConstValue(scope, node, meta_type) catch unreachable);
|
|
|
|
errdefer result_val.base.deref(comp);
|
|
|
|
|
|
|
|
return result_val.cast(Type).?;
|
|
|
|
}
|
2017-12-22 21:29:39 -08:00
|
|
|
};
|
|
|
|
|
2018-01-31 19:48:40 -08:00
|
|
|
fn printError(comptime format: []const u8, args: ...) !void {
|
2018-01-07 13:51:46 -08:00
|
|
|
var stderr_file = try std.io.getStdErr();
|
2017-12-22 21:29:39 -08:00
|
|
|
var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
|
|
|
|
const out_stream = &stderr_file_out_stream.stream;
|
2018-01-07 13:51:46 -08:00
|
|
|
try out_stream.print(format, args);
|
2017-12-22 21:29:39 -08:00
|
|
|
}
|
2018-07-05 12:09:02 -07:00
|
|
|
|
|
|
|
fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {
|
|
|
|
if (optional_token_index) |token_index| {
|
|
|
|
const token = tree.tokens.at(token_index);
|
|
|
|
assert(token.id == Token.Id.Keyword_pub);
|
|
|
|
return Visib.Pub;
|
|
|
|
} else {
|
|
|
|
return Visib.Private;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-12 12:08:40 -07:00
|
|
|
/// This declaration has been blessed as going into the final code generation.
|
2018-07-14 13:12:41 -07:00
|
|
|
pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void {
|
2018-07-16 17:52:50 -07:00
|
|
|
if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*;
|
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
decl.resolution.data = (await (async generateDecl(comp, decl) catch unreachable)) catch |err| switch (err) {
|
|
|
|
// This poison value should not cause the errdefers to run. It simply means
|
|
|
|
// that comp.compile_errors is populated.
|
|
|
|
error.SemanticAnalysisFailed => {},
|
|
|
|
else => err,
|
|
|
|
};
|
2018-07-16 17:52:50 -07:00
|
|
|
decl.resolution.resolve();
|
|
|
|
return decl.resolution.data;
|
2018-07-12 12:08:40 -07:00
|
|
|
}
|
2018-07-05 12:09:02 -07:00
|
|
|
|
2018-07-12 12:08:40 -07:00
|
|
|
/// The function that actually does the generation.
|
2018-07-14 13:12:41 -07:00
|
|
|
async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
|
2018-07-12 12:08:40 -07:00
|
|
|
switch (decl.id) {
|
|
|
|
Decl.Id.Var => @panic("TODO"),
|
|
|
|
Decl.Id.Fn => {
|
|
|
|
const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl);
|
2018-07-14 13:12:41 -07:00
|
|
|
return await (async generateDeclFn(comp, fn_decl) catch unreachable);
|
2018-07-12 12:08:40 -07:00
|
|
|
},
|
|
|
|
Decl.Id.CompTime => @panic("TODO"),
|
2018-07-10 17:18:43 -07:00
|
|
|
}
|
2018-07-12 12:08:40 -07:00
|
|
|
}
|
2018-07-10 17:18:43 -07:00
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
2018-07-12 12:08:40 -07:00
|
|
|
const body_node = fn_decl.fn_proto.body_node orelse @panic("TODO extern fn proto decl");
|
2018-07-05 12:09:02 -07:00
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
|
|
|
|
defer fndef_scope.base.deref(comp);
|
2018-07-05 12:09:02 -07:00
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
const return_type_node = switch (fn_decl.fn_proto.return_type) {
|
|
|
|
ast.Node.FnProto.ReturnType.Explicit => |n| n,
|
|
|
|
ast.Node.FnProto.ReturnType.InferErrorSet => |n| n,
|
|
|
|
};
|
|
|
|
const return_type = try await (async comp.analyzeTypeExpr(&fndef_scope.base, return_type_node) catch unreachable);
|
|
|
|
return_type.base.deref(comp);
|
2018-07-14 12:45:15 -07:00
|
|
|
|
|
|
|
const is_var_args = false;
|
|
|
|
const params = ([*]Type.Fn.Param)(undefined)[0..0];
|
2018-07-14 13:12:41 -07:00
|
|
|
const fn_type = try Type.Fn.create(comp, return_type, params, is_var_args);
|
|
|
|
defer fn_type.base.base.deref(comp);
|
2018-07-05 12:09:02 -07:00
|
|
|
|
2018-07-16 17:52:50 -07:00
|
|
|
var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
|
2018-07-14 12:45:15 -07:00
|
|
|
errdefer symbol_name.deinit();
|
|
|
|
|
2018-07-17 10:18:13 -07:00
|
|
|
// The Decl.Fn owns the initial 1 reference count
|
2018-07-14 13:12:41 -07:00
|
|
|
const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
|
2018-07-12 12:08:40 -07:00
|
|
|
fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };
|
2018-07-05 12:09:02 -07:00
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
const analyzed_code = try await (async comp.genAndAnalyzeCode(
|
2018-07-18 14:40:59 -07:00
|
|
|
&fndef_scope.base,
|
|
|
|
body_node,
|
2018-07-18 21:08:47 -07:00
|
|
|
return_type,
|
2018-07-19 12:11:39 -07:00
|
|
|
) catch unreachable);
|
2018-07-16 17:52:50 -07:00
|
|
|
errdefer analyzed_code.destroy(comp.gpa());
|
2018-07-13 18:56:38 -07:00
|
|
|
|
2018-07-14 21:04:12 -07:00
|
|
|
// Kick off rendering to LLVM module, but it doesn't block the fn decl
|
2018-07-14 12:45:15 -07:00
|
|
|
// analysis from being complete.
|
2018-07-16 17:52:50 -07:00
|
|
|
try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code);
|
|
|
|
try comp.prelink_group.call(addFnToLinkSet, comp, fn_val);
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void {
|
|
|
|
fn_val.base.ref();
|
|
|
|
defer fn_val.base.deref(comp);
|
|
|
|
|
|
|
|
fn_val.link_set_node.data = fn_val;
|
|
|
|
|
|
|
|
const held = await (async comp.fn_link_set.acquire() catch unreachable);
|
|
|
|
defer held.release();
|
|
|
|
|
|
|
|
held.value.append(fn_val.link_set_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn getZigDir(allocator: *mem.Allocator) ![]u8 {
|
2018-07-18 07:07:22 -07:00
|
|
|
return os.getAppDataDir(allocator, "zig");
|
2018-07-12 12:08:40 -07:00
|
|
|
}
|