Merge pull request #1266 from ziglang/self-hosted-libc-hello-world
Self hosted libc hello worldmaster
commit
10bdf73a02
|
@ -426,6 +426,7 @@ set(ZIG_SOURCES
|
|||
)
|
||||
set(ZIG_CPP_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp"
|
||||
)
|
||||
|
||||
set(ZIG_STD_FILES
|
||||
|
@ -489,6 +490,7 @@ set(ZIG_STD_FILES
|
|||
"math/atan.zig"
|
||||
"math/atan2.zig"
|
||||
"math/atanh.zig"
|
||||
"math/big/index.zig"
|
||||
"math/big/int.zig"
|
||||
"math/cbrt.zig"
|
||||
"math/ceil.zig"
|
||||
|
@ -566,8 +568,14 @@ set(ZIG_STD_FILES
|
|||
"os/linux/x86_64.zig"
|
||||
"os/path.zig"
|
||||
"os/time.zig"
|
||||
"os/windows/advapi32.zig"
|
||||
"os/windows/error.zig"
|
||||
"os/windows/index.zig"
|
||||
"os/windows/kernel32.zig"
|
||||
"os/windows/ole32.zig"
|
||||
"os/windows/shell32.zig"
|
||||
"os/windows/shlwapi.zig"
|
||||
"os/windows/user32.zig"
|
||||
"os/windows/util.zig"
|
||||
"os/zen.zig"
|
||||
"rand/index.zig"
|
||||
|
@ -616,6 +624,7 @@ set(ZIG_STD_FILES
|
|||
"zig/ast.zig"
|
||||
"zig/index.zig"
|
||||
"zig/parse.zig"
|
||||
"zig/parse_string_literal.zig"
|
||||
"zig/render.zig"
|
||||
"zig/tokenizer.zig"
|
||||
)
|
||||
|
|
|
@ -21,19 +21,19 @@ clarity.
|
|||
* Compatible with C libraries with no wrapper necessary. Directly include
|
||||
C .h files and get access to the functions and symbols therein.
|
||||
* Provides standard library which competes with the C standard library and is
|
||||
always compiled against statically in source form. Compile units do not
|
||||
always compiled against statically in source form. Zig binaries do not
|
||||
depend on libc unless explicitly linked.
|
||||
* Nullable type instead of null pointers.
|
||||
* Optional type instead of null pointers.
|
||||
* Safe unions, tagged unions, and C ABI compatible unions.
|
||||
* Generics so that one can write efficient data structures that work for any
|
||||
data type.
|
||||
* No header files required. Top level declarations are entirely
|
||||
order-independent.
|
||||
* Compile-time code execution. Compile-time reflection.
|
||||
* Partial compile-time function evaluation with eliminates the need for
|
||||
* Partial compile-time function evaluation which eliminates the need for
|
||||
a preprocessor or macros.
|
||||
* The binaries produced by Zig have complete debugging information so you can,
|
||||
for example, use GDB or MSVC to debug your software.
|
||||
for example, use GDB, MSVC, or LLDB to debug your software.
|
||||
* Built-in unit tests with `zig test`.
|
||||
* Friendly toward package maintainers. Reproducible build, bootstrapping
|
||||
process carefully documented. Issues filed by package maintainers are
|
||||
|
|
|
@ -4,4 +4,5 @@ pub use @cImport({
|
|||
@cInclude("inttypes.h");
|
||||
@cInclude("config.h");
|
||||
@cInclude("zig_llvm.h");
|
||||
@cInclude("windows_sdk.h");
|
||||
});
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
pub const CInt = struct {
|
||||
id: Id,
|
||||
zig_name: []const u8,
|
||||
c_name: []const u8,
|
||||
is_signed: bool,
|
||||
|
||||
pub const Id = enum {
|
||||
Short,
|
||||
UShort,
|
||||
Int,
|
||||
UInt,
|
||||
Long,
|
||||
ULong,
|
||||
LongLong,
|
||||
ULongLong,
|
||||
};
|
||||
|
||||
pub const list = []CInt{
|
||||
CInt{
|
||||
.id = Id.Short,
|
||||
.zig_name = "c_short",
|
||||
.c_name = "short",
|
||||
.is_signed = true,
|
||||
},
|
||||
CInt{
|
||||
.id = Id.UShort,
|
||||
.zig_name = "c_ushort",
|
||||
.c_name = "unsigned short",
|
||||
.is_signed = false,
|
||||
},
|
||||
CInt{
|
||||
.id = Id.Int,
|
||||
.zig_name = "c_int",
|
||||
.c_name = "int",
|
||||
.is_signed = true,
|
||||
},
|
||||
CInt{
|
||||
.id = Id.UInt,
|
||||
.zig_name = "c_uint",
|
||||
.c_name = "unsigned int",
|
||||
.is_signed = false,
|
||||
},
|
||||
CInt{
|
||||
.id = Id.Long,
|
||||
.zig_name = "c_long",
|
||||
.c_name = "long",
|
||||
.is_signed = true,
|
||||
},
|
||||
CInt{
|
||||
.id = Id.ULong,
|
||||
.zig_name = "c_ulong",
|
||||
.c_name = "unsigned long",
|
||||
.is_signed = false,
|
||||
},
|
||||
CInt{
|
||||
.id = Id.LongLong,
|
||||
.zig_name = "c_longlong",
|
||||
.c_name = "long long",
|
||||
.is_signed = true,
|
||||
},
|
||||
CInt{
|
||||
.id = Id.ULongLong,
|
||||
.zig_name = "c_ulonglong",
|
||||
.c_name = "unsigned long long",
|
||||
.is_signed = false,
|
||||
},
|
||||
};
|
||||
};
|
|
@ -15,7 +15,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
|
|||
defer fn_val.base.deref(comp);
|
||||
defer code.destroy(comp.gpa());
|
||||
|
||||
var output_path = try await (async comp.createRandomOutputPath(comp.target.oFileExt()) catch unreachable);
|
||||
var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable);
|
||||
errdefer output_path.deinit();
|
||||
|
||||
const llvm_handle = try comp.event_loop_local.getAnyLlvmContext();
|
||||
|
@ -78,6 +78,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
|
|||
.dibuilder = dibuilder,
|
||||
.context = context,
|
||||
.lock = event.Lock.init(comp.loop),
|
||||
.arena = &code.arena.allocator,
|
||||
};
|
||||
|
||||
try renderToLlvmModule(&ofile, fn_val, code);
|
||||
|
@ -139,6 +140,7 @@ pub const ObjectFile = struct {
|
|||
dibuilder: *llvm.DIBuilder,
|
||||
context: llvm.ContextRef,
|
||||
lock: event.Lock,
|
||||
arena: *std.mem.Allocator,
|
||||
|
||||
fn gpa(self: *ObjectFile) *std.mem.Allocator {
|
||||
return self.comp.gpa();
|
||||
|
@ -147,7 +149,7 @@ pub const ObjectFile = struct {
|
|||
|
||||
pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void {
|
||||
// TODO audit more of codegen.cpp:fn_llvm_value and port more logic
|
||||
const llvm_fn_type = try fn_val.base.typeof.getLlvmType(ofile);
|
||||
const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context);
|
||||
const llvm_fn = llvm.AddFunction(
|
||||
ofile.module,
|
||||
fn_val.symbol_name.ptr(),
|
||||
|
@ -165,7 +167,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
|
|||
// try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack);
|
||||
//}
|
||||
|
||||
const fn_type = fn_val.base.typeof.cast(Type.Fn).?;
|
||||
const fn_type = fn_val.base.typ.cast(Type.Fn).?;
|
||||
|
||||
try addLLVMFnAttr(ofile, llvm_fn, "nounwind");
|
||||
//add_uwtable_attr(g, fn_table_entry->llvm_value);
|
||||
|
|
|
@ -21,13 +21,15 @@ const Scope = @import("scope.zig").Scope;
|
|||
const Decl = @import("decl.zig").Decl;
|
||||
const ir = @import("ir.zig");
|
||||
const Visib = @import("visib.zig").Visib;
|
||||
const ParsedFile = @import("parsed_file.zig").ParsedFile;
|
||||
const Value = @import("value.zig").Value;
|
||||
const Type = Value.Type;
|
||||
const Span = errmsg.Span;
|
||||
const Msg = errmsg.Msg;
|
||||
const codegen = @import("codegen.zig");
|
||||
const Package = @import("package.zig").Package;
|
||||
const link = @import("link.zig").link;
|
||||
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
|
||||
const CInt = @import("c_int.zig").CInt;
|
||||
|
||||
/// Data that is local to the event loop.
|
||||
pub const EventLoopLocal = struct {
|
||||
|
@ -37,6 +39,8 @@ pub const EventLoopLocal = struct {
|
|||
/// TODO pool these so that it doesn't have to lock
|
||||
prng: event.Locked(std.rand.DefaultPrng),
|
||||
|
||||
native_libc: event.Future(LibCInstallation),
|
||||
|
||||
var lazy_init_targets = std.lazyInit(void);
|
||||
|
||||
fn init(loop: *event.Loop) !EventLoopLocal {
|
||||
|
@ -48,13 +52,16 @@ pub const EventLoopLocal = struct {
|
|||
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);
|
||||
|
||||
return EventLoopLocal{
|
||||
.loop = loop,
|
||||
.llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
|
||||
.prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)),
|
||||
.native_libc = event.Future(LibCInstallation).init(loop),
|
||||
};
|
||||
}
|
||||
|
||||
/// Must be called only after EventLoop.run completes.
|
||||
fn deinit(self: *EventLoopLocal) void {
|
||||
while (self.llvm_handle_pool.pop()) |node| {
|
||||
c.LLVMContextDispose(node.data);
|
||||
|
@ -78,6 +85,13 @@ pub const EventLoopLocal = struct {
|
|||
|
||||
return LlvmHandle{ .node = node };
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
pub const LlvmHandle = struct {
|
||||
|
@ -108,13 +122,6 @@ pub const Compilation = struct {
|
|||
version_patch: u32,
|
||||
|
||||
linker_script: ?[]const u8,
|
||||
cache_dir: []const u8,
|
||||
libc_lib_dir: ?[]const u8,
|
||||
libc_static_lib_dir: ?[]const u8,
|
||||
libc_include_dir: ?[]const u8,
|
||||
msvc_lib_dir: ?[]const u8,
|
||||
kernel32_lib_dir: ?[]const u8,
|
||||
dynamic_linker: ?[]const u8,
|
||||
out_h_path: ?[]const u8,
|
||||
|
||||
is_test: bool,
|
||||
|
@ -179,6 +186,8 @@ pub const Compilation = struct {
|
|||
void_type: *Type.Void,
|
||||
bool_type: *Type.Bool,
|
||||
noreturn_type: *Type.NoReturn,
|
||||
comptime_int_type: *Type.ComptimeInt,
|
||||
u8_type: *Type.Int,
|
||||
|
||||
void_value: *Value.Void,
|
||||
true_value: *Value.Bool,
|
||||
|
@ -188,6 +197,7 @@ pub const Compilation = struct {
|
|||
target_machine: llvm.TargetMachineRef,
|
||||
target_data_ref: llvm.TargetDataRef,
|
||||
target_layout_str: [*]u8,
|
||||
target_ptr_bits: u32,
|
||||
|
||||
/// for allocating things which have the same lifetime as this Compilation
|
||||
arena_allocator: std.heap.ArenaAllocator,
|
||||
|
@ -195,7 +205,30 @@ pub const Compilation = struct {
|
|||
root_package: *Package,
|
||||
std_package: *Package,
|
||||
|
||||
const CompileErrList = std.ArrayList(*errmsg.Msg);
|
||||
override_libc: ?*LibCInstallation,
|
||||
|
||||
/// need to wait on this group before deinitializing
|
||||
deinit_group: event.Group(void),
|
||||
|
||||
destroy_handle: promise,
|
||||
|
||||
have_err_ret_tracing: bool,
|
||||
|
||||
/// not locked because it is read-only
|
||||
primitive_type_table: TypeTable,
|
||||
|
||||
int_type_table: event.Locked(IntTypeTable),
|
||||
array_type_table: event.Locked(ArrayTypeTable),
|
||||
ptr_type_table: event.Locked(PtrTypeTable),
|
||||
|
||||
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 ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
|
||||
const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
|
||||
const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8);
|
||||
|
||||
const CompileErrList = std.ArrayList(*Msg);
|
||||
|
||||
// TODO handle some of these earlier and report them in a way other than error codes
|
||||
pub const BuildError = error{
|
||||
|
@ -240,12 +273,16 @@ pub const Compilation = struct {
|
|||
EnvironmentVariableNotFound,
|
||||
AppDataDirUnavailable,
|
||||
LinkFailed,
|
||||
LibCRequiredButNotProvidedOrFound,
|
||||
LibCMissingDynamicLinker,
|
||||
InvalidDarwinVersionString,
|
||||
UnsupportedLinkArchitecture,
|
||||
};
|
||||
|
||||
pub const Event = union(enum) {
|
||||
Ok,
|
||||
Error: BuildError,
|
||||
Fail: []*errmsg.Msg,
|
||||
Fail: []*Msg,
|
||||
};
|
||||
|
||||
pub const DarwinVersionMin = union(enum) {
|
||||
|
@ -284,7 +321,6 @@ pub const Compilation = struct {
|
|||
build_mode: builtin.Mode,
|
||||
is_static: bool,
|
||||
zig_lib_dir: []const u8,
|
||||
cache_dir: []const u8,
|
||||
) !*Compilation {
|
||||
const loop = event_loop_local.loop;
|
||||
const comp = try event_loop_local.loop.allocator.create(Compilation{
|
||||
|
@ -299,7 +335,6 @@ pub const Compilation = struct {
|
|||
.build_mode = build_mode,
|
||||
.zig_lib_dir = zig_lib_dir,
|
||||
.zig_std_dir = undefined,
|
||||
.cache_dir = cache_dir,
|
||||
.tmp_dir = event.Future(BuildError![]u8).init(loop),
|
||||
|
||||
.name = undefined,
|
||||
|
@ -318,12 +353,6 @@ pub const Compilation = struct {
|
|||
.verbose_link = false,
|
||||
|
||||
.linker_script = null,
|
||||
.libc_lib_dir = null,
|
||||
.libc_static_lib_dir = null,
|
||||
.libc_include_dir = null,
|
||||
.msvc_lib_dir = null,
|
||||
.kernel32_lib_dir = null,
|
||||
.dynamic_linker = null,
|
||||
.out_h_path = null,
|
||||
.is_test = false,
|
||||
.each_lib_rpath = false,
|
||||
|
@ -350,7 +379,12 @@ pub const Compilation = struct {
|
|||
.link_out_file = null,
|
||||
.exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),
|
||||
.prelink_group = event.Group(BuildError!void).init(loop),
|
||||
.deinit_group = event.Group(void).init(loop),
|
||||
.compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)),
|
||||
.int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)),
|
||||
.array_type_table = event.Locked(ArrayTypeTable).init(loop, ArrayTypeTable.init(loop.allocator)),
|
||||
.ptr_type_table = event.Locked(PtrTypeTable).init(loop, PtrTypeTable.init(loop.allocator)),
|
||||
.c_int_types = undefined,
|
||||
|
||||
.meta_type = undefined,
|
||||
.void_type = undefined,
|
||||
|
@ -360,15 +394,26 @@ pub const Compilation = struct {
|
|||
.false_value = undefined,
|
||||
.noreturn_type = undefined,
|
||||
.noreturn_value = undefined,
|
||||
.comptime_int_type = undefined,
|
||||
.u8_type = undefined,
|
||||
|
||||
.target_machine = undefined,
|
||||
.target_data_ref = undefined,
|
||||
.target_layout_str = undefined,
|
||||
.target_ptr_bits = target.getArchPtrBitWidth(),
|
||||
|
||||
.root_package = undefined,
|
||||
.std_package = undefined,
|
||||
|
||||
.override_libc = null,
|
||||
.destroy_handle = undefined,
|
||||
.have_err_ret_tracing = false,
|
||||
.primitive_type_table = undefined,
|
||||
});
|
||||
errdefer {
|
||||
comp.int_type_table.private_data.deinit();
|
||||
comp.array_type_table.private_data.deinit();
|
||||
comp.ptr_type_table.private_data.deinit();
|
||||
comp.arena_allocator.deinit();
|
||||
comp.loop.allocator.destroy(comp);
|
||||
}
|
||||
|
@ -378,6 +423,7 @@ pub const Compilation = struct {
|
|||
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");
|
||||
comp.primitive_type_table = TypeTable.init(comp.arena());
|
||||
|
||||
const opt_level = switch (build_mode) {
|
||||
builtin.Mode.Debug => llvm.CodeGenLevelNone,
|
||||
|
@ -431,123 +477,221 @@ pub const Compilation = struct {
|
|||
|
||||
try comp.initTypes();
|
||||
|
||||
comp.destroy_handle = try async<loop.allocator> comp.internalDeinit();
|
||||
|
||||
return comp;
|
||||
}
|
||||
|
||||
/// it does ref the result because it could be an arbitrary integer size
|
||||
pub async 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
|
||||
};
|
||||
const int_type = try await (async Type.Int.get(comp, Type.Int.Key{
|
||||
.bit_count = bit_count,
|
||||
.is_signed = is_signed,
|
||||
}) catch unreachable);
|
||||
errdefer int_type.base.base.deref();
|
||||
return &int_type.base;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
if (comp.primitive_type_table.get(name)) |entry| {
|
||||
entry.value.base.ref();
|
||||
return entry.value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn initTypes(comp: *Compilation) !void {
|
||||
comp.meta_type = try comp.gpa().create(Type.MetaType{
|
||||
comp.meta_type = try comp.arena().create(Type.MetaType{
|
||||
.base = Type{
|
||||
.name = "type",
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
.typeof = undefined,
|
||||
.typ = undefined,
|
||||
.ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice
|
||||
},
|
||||
.id = builtin.TypeId.Type,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
.value = undefined,
|
||||
});
|
||||
comp.meta_type.value = &comp.meta_type.base;
|
||||
comp.meta_type.base.base.typeof = &comp.meta_type.base;
|
||||
errdefer comp.gpa().destroy(comp.meta_type);
|
||||
comp.meta_type.base.base.typ = &comp.meta_type.base;
|
||||
assert((try comp.primitive_type_table.put(comp.meta_type.base.name, &comp.meta_type.base)) == null);
|
||||
|
||||
comp.void_type = try comp.gpa().create(Type.Void{
|
||||
comp.void_type = try comp.arena().create(Type.Void{
|
||||
.base = Type{
|
||||
.name = "void",
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
.typeof = &Type.MetaType.get(comp).base,
|
||||
.typ = &Type.MetaType.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.id = builtin.TypeId.Void,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
});
|
||||
errdefer comp.gpa().destroy(comp.void_type);
|
||||
assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null);
|
||||
|
||||
comp.noreturn_type = try comp.gpa().create(Type.NoReturn{
|
||||
comp.noreturn_type = try comp.arena().create(Type.NoReturn{
|
||||
.base = Type{
|
||||
.name = "noreturn",
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
.typeof = &Type.MetaType.get(comp).base,
|
||||
.typ = &Type.MetaType.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.id = builtin.TypeId.NoReturn,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
});
|
||||
errdefer comp.gpa().destroy(comp.noreturn_type);
|
||||
assert((try comp.primitive_type_table.put(comp.noreturn_type.base.name, &comp.noreturn_type.base)) == null);
|
||||
|
||||
comp.bool_type = try comp.gpa().create(Type.Bool{
|
||||
comp.comptime_int_type = try comp.arena().create(Type.ComptimeInt{
|
||||
.base = Type{
|
||||
.name = "comptime_int",
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
.typeof = &Type.MetaType.get(comp).base,
|
||||
.typ = &Type.MetaType.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.id = builtin.TypeId.ComptimeInt,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
});
|
||||
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",
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
.typ = &Type.MetaType.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.id = builtin.TypeId.Bool,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
});
|
||||
errdefer comp.gpa().destroy(comp.bool_type);
|
||||
assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null);
|
||||
|
||||
comp.void_value = try comp.gpa().create(Value.Void{
|
||||
comp.void_value = try comp.arena().create(Value.Void{
|
||||
.base = Value{
|
||||
.id = Value.Id.Void,
|
||||
.typeof = &Type.Void.get(comp).base,
|
||||
.typ = &Type.Void.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
});
|
||||
errdefer comp.gpa().destroy(comp.void_value);
|
||||
|
||||
comp.true_value = try comp.gpa().create(Value.Bool{
|
||||
comp.true_value = try comp.arena().create(Value.Bool{
|
||||
.base = Value{
|
||||
.id = Value.Id.Bool,
|
||||
.typeof = &Type.Bool.get(comp).base,
|
||||
.typ = &Type.Bool.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.x = true,
|
||||
});
|
||||
errdefer comp.gpa().destroy(comp.true_value);
|
||||
|
||||
comp.false_value = try comp.gpa().create(Value.Bool{
|
||||
comp.false_value = try comp.arena().create(Value.Bool{
|
||||
.base = Value{
|
||||
.id = Value.Id.Bool,
|
||||
.typeof = &Type.Bool.get(comp).base,
|
||||
.typ = &Type.Bool.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.x = false,
|
||||
});
|
||||
errdefer comp.gpa().destroy(comp.false_value);
|
||||
|
||||
comp.noreturn_value = try comp.gpa().create(Value.NoReturn{
|
||||
comp.noreturn_value = try comp.arena().create(Value.NoReturn{
|
||||
.base = Value{
|
||||
.id = Value.Id.NoReturn,
|
||||
.typeof = &Type.NoReturn.get(comp).base,
|
||||
.typ = &Type.NoReturn.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
});
|
||||
errdefer comp.gpa().destroy(comp.noreturn_value);
|
||||
|
||||
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,
|
||||
.typ = &Type.MetaType.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.id = builtin.TypeId.Int,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
.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);
|
||||
}
|
||||
comp.u8_type = try comp.arena().create(Type.Int{
|
||||
.base = Type{
|
||||
.name = "u8",
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
.typ = &Type.MetaType.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.id = builtin.TypeId.Int,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
.key = Type.Int.Key{
|
||||
.is_signed = false,
|
||||
.bit_count = 8,
|
||||
},
|
||||
.garbage_node = undefined,
|
||||
});
|
||||
assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null);
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Compilation) void {
|
||||
/// 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.
|
||||
async fn internalDeinit(self: *Compilation) void {
|
||||
suspend;
|
||||
|
||||
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 |_| {};
|
||||
|
||||
self.noreturn_value.base.deref(self);
|
||||
self.void_value.base.deref(self);
|
||||
self.false_value.base.deref(self);
|
||||
self.true_value.base.deref(self);
|
||||
self.noreturn_type.base.base.deref(self);
|
||||
self.void_type.base.base.deref(self);
|
||||
self.meta_type.base.base.deref(self);
|
||||
|
||||
self.events.destroy();
|
||||
|
||||
llvm.DisposeMessage(self.target_layout_str);
|
||||
llvm.DisposeTargetData(self.target_data_ref);
|
||||
llvm.DisposeTargetMachine(self.target_machine);
|
||||
|
||||
self.primitive_type_table.deinit();
|
||||
|
||||
self.arena_allocator.deinit();
|
||||
self.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Compilation) void {
|
||||
resume self.destroy_handle;
|
||||
}
|
||||
|
||||
pub fn build(self: *Compilation) !void {
|
||||
if (self.llvm_argv.len != 0) {
|
||||
var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{
|
||||
|
@ -597,79 +741,103 @@ pub const Compilation = struct {
|
|||
}
|
||||
|
||||
async fn compileAndLink(self: *Compilation) !void {
|
||||
const root_src_path = self.root_src_path orelse @panic("TODO handle null 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;
|
||||
};
|
||||
errdefer self.gpa().free(root_src_real_path);
|
||||
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);
|
||||
|
||||
// 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);
|
||||
// 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);
|
||||
|
||||
const parsed_file = try self.gpa().create(ParsedFile{
|
||||
.tree = undefined,
|
||||
.realpath = root_src_real_path,
|
||||
});
|
||||
errdefer self.gpa().destroy(parsed_file);
|
||||
const tree = try self.gpa().createOne(ast.Tree);
|
||||
tree.* = try std.zig.parse(self.gpa(), source_code);
|
||||
errdefer {
|
||||
tree.deinit();
|
||||
self.gpa().destroy(tree);
|
||||
}
|
||||
|
||||
parsed_file.tree = try std.zig.parse(self.gpa(), source_code);
|
||||
errdefer parsed_file.tree.deinit();
|
||||
break :blk try Scope.Root.create(self, tree, root_src_real_path);
|
||||
};
|
||||
defer root_scope.base.deref(self);
|
||||
const tree = root_scope.tree;
|
||||
|
||||
const tree = &parsed_file.tree;
|
||||
var error_it = tree.errors.iterator(0);
|
||||
while (error_it.next()) |parse_error| {
|
||||
const msg = try Msg.createFromParseErrorAndScope(self, root_scope, parse_error);
|
||||
errdefer msg.destroy();
|
||||
|
||||
// create empty struct for it
|
||||
const decls = try Scope.Decls.create(self, null);
|
||||
defer decls.base.deref(self);
|
||||
|
||||
var decl_group = event.Group(BuildError!void).init(self.loop);
|
||||
errdefer decl_group.cancelAll();
|
||||
|
||||
var it = tree.root_node.decls.iterator(0);
|
||||
while (it.next()) |decl_ptr| {
|
||||
const decl = decl_ptr.*;
|
||||
switch (decl.id) {
|
||||
ast.Node.Id.Comptime => @panic("TODO"),
|
||||
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(parsed_file, 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,
|
||||
.parsed_file = parsed_file,
|
||||
.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,
|
||||
try await (async self.addCompileErrorAsync(msg) catch unreachable);
|
||||
}
|
||||
if (tree.errors.len != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const decls = try Scope.Decls.create(self, &root_scope.base);
|
||||
defer decls.base.deref(self);
|
||||
|
||||
var decl_group = event.Group(BuildError!void).init(self.loop);
|
||||
var decl_group_consumed = false;
|
||||
errdefer if (!decl_group_consumed) decl_group.cancelAll();
|
||||
|
||||
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);
|
||||
|
||||
try self.prelink_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),
|
||||
.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, decls, &fn_decl.base);
|
||||
},
|
||||
ast.Node.Id.TestDecl => @panic("TODO"),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
decl_group_consumed = true;
|
||||
try await (async decl_group.wait() catch unreachable);
|
||||
|
||||
// Now other code can rely on the decls scope having a complete list of names.
|
||||
decls.name_future.resolve();
|
||||
}
|
||||
try await (async decl_group.wait() catch unreachable);
|
||||
try await (async self.prelink_group.wait() catch unreachable);
|
||||
|
||||
(await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) {
|
||||
error.SemanticAnalysisFailed => {},
|
||||
else => return err,
|
||||
};
|
||||
|
||||
const any_prelink_errors = blk: {
|
||||
const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
|
||||
|
@ -679,39 +847,108 @@ pub const Compilation = struct {
|
|||
};
|
||||
|
||||
if (!any_prelink_errors) {
|
||||
try link(self);
|
||||
try await (async link(self) catch unreachable);
|
||||
}
|
||||
}
|
||||
|
||||
async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void {
|
||||
const is_export = decl.isExported(&decl.parsed_file.tree);
|
||||
/// caller takes ownership of resulting Code
|
||||
async fn genAndAnalyzeCode(
|
||||
comp: *Compilation,
|
||||
scope: *Scope,
|
||||
node: *ast.Node,
|
||||
expected_type: ?*Type,
|
||||
) !*ir.Code {
|
||||
const unanalyzed_code = try await (async ir.gen(
|
||||
comp,
|
||||
node,
|
||||
scope,
|
||||
) catch unreachable);
|
||||
defer unanalyzed_code.destroy(comp.gpa());
|
||||
|
||||
if (comp.verbose_ir) {
|
||||
std.debug.warn("unanalyzed:\n");
|
||||
unanalyzed_code.dump();
|
||||
}
|
||||
|
||||
const analyzed_code = try await (async ir.analyze(
|
||||
comp,
|
||||
unanalyzed_code,
|
||||
expected_type,
|
||||
) catch unreachable);
|
||||
errdefer analyzed_code.destroy(comp.gpa());
|
||||
|
||||
if (comp.verbose_ir) {
|
||||
std.debug.warn("analyzed:\n");
|
||||
analyzed_code.dump();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
const analyzed_code = (await (async genAndAnalyzeCode(
|
||||
comp,
|
||||
scope,
|
||||
comptime_node.expr,
|
||||
&void_type.base,
|
||||
) 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,
|
||||
};
|
||||
analyzed_code.destroy(comp.gpa());
|
||||
}
|
||||
|
||||
async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
|
||||
const tree = decl.findRootScope().tree;
|
||||
const is_export = decl.isExported(tree);
|
||||
|
||||
var add_to_table_resolved = false;
|
||||
const add_to_table = async self.addDeclToTable(decls, decl) catch unreachable;
|
||||
errdefer if (!add_to_table_resolved) cancel add_to_table; // TODO https://github.com/ziglang/zig/issues/1261
|
||||
|
||||
if (is_export) {
|
||||
try self.prelink_group.call(verifyUniqueSymbol, self, decl);
|
||||
try self.prelink_group.call(resolveDecl, self, decl);
|
||||
}
|
||||
|
||||
add_to_table_resolved = true;
|
||||
try await add_to_table;
|
||||
}
|
||||
|
||||
fn addCompileError(self: *Compilation, parsed_file: *ParsedFile, span: Span, comptime fmt: []const u8, args: ...) !void {
|
||||
const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args);
|
||||
errdefer self.loop.allocator.free(text);
|
||||
async fn addDeclToTable(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
|
||||
const held = await (async decls.table.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
try self.prelink_group.call(addCompileErrorAsync, self, parsed_file, span, text);
|
||||
if (try held.value.put(decl.name, decl)) |other_decl| {
|
||||
try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name);
|
||||
// TODO note: other definition here
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
const msg = try Msg.createFromScope(self, root, span, text);
|
||||
errdefer msg.destroy();
|
||||
|
||||
try self.prelink_group.call(addCompileErrorAsync, self, msg);
|
||||
}
|
||||
|
||||
async fn addCompileErrorAsync(
|
||||
self: *Compilation,
|
||||
parsed_file: *ParsedFile,
|
||||
span: Span,
|
||||
text: []u8,
|
||||
msg: *Msg,
|
||||
) !void {
|
||||
const msg = try self.loop.allocator.create(errmsg.Msg{
|
||||
.path = parsed_file.realpath,
|
||||
.text = text,
|
||||
.span = span,
|
||||
.tree = &parsed_file.tree,
|
||||
});
|
||||
errdefer self.loop.allocator.destroy(msg);
|
||||
errdefer msg.destroy();
|
||||
|
||||
const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
|
||||
defer compile_errors.release();
|
||||
|
@ -725,7 +962,7 @@ pub const Compilation = struct {
|
|||
|
||||
if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
|
||||
try self.addCompileError(
|
||||
decl.parsed_file,
|
||||
decl.findRootScope(),
|
||||
decl.getSpan(),
|
||||
"exported symbol collision: '{}'",
|
||||
decl.name,
|
||||
|
@ -762,10 +999,22 @@ pub const Compilation = struct {
|
|||
try self.link_libs_list.append(link_lib);
|
||||
if (is_libc) {
|
||||
self.libc_link_lib = link_lib;
|
||||
|
||||
// get a head start on looking for the native libc
|
||||
if (self.target == Target.Native and self.override_libc == null) {
|
||||
try self.deinit_group.call(startFindingNativeLibC, self);
|
||||
}
|
||||
}
|
||||
return link_lib;
|
||||
}
|
||||
|
||||
/// cancels itself so no need to await or cancel the promise.
|
||||
async fn startFindingNativeLibC(self: *Compilation) void {
|
||||
await (async self.loop.yield() catch unreachable);
|
||||
// we don't care if it fails, we're just trying to kick off the future resolution
|
||||
_ = (await (async self.event_loop_local.getNativeLibC() catch unreachable)) catch return;
|
||||
}
|
||||
|
||||
/// General Purpose Allocator. Must free when done.
|
||||
fn gpa(self: Compilation) *mem.Allocator {
|
||||
return self.loop.allocator;
|
||||
|
@ -831,6 +1080,37 @@ pub const Compilation = struct {
|
|||
b64_fs_encoder.encode(result[0..], rand_bytes);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn registerGarbage(comp: *Compilation, comptime T: type, node: *std.atomic.Stack(*T).Node) void {
|
||||
// TODO put the garbage somewhere
|
||||
}
|
||||
|
||||
/// 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).?;
|
||||
}
|
||||
|
||||
/// This declaration has been blessed as going into the final code generation.
|
||||
pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void {
|
||||
if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*;
|
||||
|
||||
decl.resolution.data = try await (async generateDecl(comp, decl) catch unreachable);
|
||||
decl.resolution.resolve();
|
||||
return decl.resolution.data;
|
||||
}
|
||||
};
|
||||
|
||||
fn printError(comptime format: []const u8, args: ...) !void {
|
||||
|
@ -850,15 +1130,6 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib
|
|||
}
|
||||
}
|
||||
|
||||
/// This declaration has been blessed as going into the final code generation.
|
||||
pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void {
|
||||
if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*;
|
||||
|
||||
decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable);
|
||||
decl.resolution.resolve();
|
||||
return decl.resolution.data;
|
||||
}
|
||||
|
||||
/// The function that actually does the generation.
|
||||
async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
|
||||
switch (decl.id) {
|
||||
|
@ -872,66 +1143,30 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
|
|||
}
|
||||
|
||||
async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
||||
const body_node = fn_decl.fn_proto.body_node orelse @panic("TODO extern fn proto decl");
|
||||
const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable);
|
||||
|
||||
const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
|
||||
defer fndef_scope.base.deref(comp);
|
||||
|
||||
// TODO actually look at the return type of the AST
|
||||
const return_type = &Type.Void.get(comp).base;
|
||||
defer return_type.base.deref(comp);
|
||||
|
||||
const is_var_args = false;
|
||||
const params = ([*]Type.Fn.Param)(undefined)[0..0];
|
||||
const fn_type = try Type.Fn.create(comp, return_type, params, is_var_args);
|
||||
const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
|
||||
defer fn_type.base.base.deref(comp);
|
||||
|
||||
var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
|
||||
errdefer symbol_name.deinit();
|
||||
var symbol_name_consumed = false;
|
||||
errdefer if (!symbol_name_consumed) symbol_name.deinit();
|
||||
|
||||
// The Decl.Fn owns the initial 1 reference count
|
||||
const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
|
||||
fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };
|
||||
fn_decl.value = Decl.Fn.Val{ .Fn = fn_val };
|
||||
symbol_name_consumed = true;
|
||||
|
||||
const unanalyzed_code = (await (async ir.gen(
|
||||
comp,
|
||||
body_node,
|
||||
const analyzed_code = try await (async comp.genAndAnalyzeCode(
|
||||
&fndef_scope.base,
|
||||
Span.token(body_node.lastToken()),
|
||||
fn_decl.base.parsed_file,
|
||||
) catch unreachable)) catch |err| switch (err) {
|
||||
// This poison value should not cause the errdefers to run. It simply means
|
||||
// that self.compile_errors is populated.
|
||||
// TODO https://github.com/ziglang/zig/issues/769
|
||||
error.SemanticAnalysisFailed => return {},
|
||||
else => return err,
|
||||
};
|
||||
defer unanalyzed_code.destroy(comp.gpa());
|
||||
|
||||
if (comp.verbose_ir) {
|
||||
std.debug.warn("unanalyzed:\n");
|
||||
unanalyzed_code.dump();
|
||||
}
|
||||
|
||||
const analyzed_code = (await (async ir.analyze(
|
||||
comp,
|
||||
fn_decl.base.parsed_file,
|
||||
unanalyzed_code,
|
||||
null,
|
||||
) catch unreachable)) catch |err| switch (err) {
|
||||
// This poison value should not cause the errdefers to run. It simply means
|
||||
// that self.compile_errors is populated.
|
||||
// TODO https://github.com/ziglang/zig/issues/769
|
||||
error.SemanticAnalysisFailed => return {},
|
||||
else => return err,
|
||||
};
|
||||
body_node,
|
||||
fn_type.return_type,
|
||||
) catch unreachable);
|
||||
errdefer analyzed_code.destroy(comp.gpa());
|
||||
|
||||
if (comp.verbose_ir) {
|
||||
std.debug.warn("analyzed:\n");
|
||||
analyzed_code.dump();
|
||||
}
|
||||
|
||||
// Kick off rendering to LLVM module, but it doesn't block the fn decl
|
||||
// analysis from being complete.
|
||||
try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code);
|
||||
|
@ -953,3 +1188,54 @@ async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void {
|
|||
fn getZigDir(allocator: *mem.Allocator) ![]u8 {
|
||||
return os.getAppDataDir(allocator, "zig");
|
||||
}
|
||||
|
||||
async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.FnProto) !*Type.Fn {
|
||||
const return_type_node = switch (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(scope, return_type_node) catch unreachable);
|
||||
return_type.base.deref(comp);
|
||||
|
||||
var params = ArrayList(Type.Fn.Param).init(comp.gpa());
|
||||
var params_consumed = false;
|
||||
defer if (params_consumed) {
|
||||
for (params.toSliceConst()) |param| {
|
||||
param.typ.base.deref(comp);
|
||||
}
|
||||
params.deinit();
|
||||
};
|
||||
|
||||
const is_var_args = false;
|
||||
{
|
||||
var it = fn_proto.params.iterator(0);
|
||||
while (it.next()) |param_node_ptr| {
|
||||
const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?;
|
||||
const param_type = try await (async comp.analyzeTypeExpr(scope, param_node.type_node) catch unreachable);
|
||||
errdefer param_type.base.deref(comp);
|
||||
try params.append(Type.Fn.Param{
|
||||
.typ = param_type,
|
||||
.is_noalias = param_node.noalias_token != null,
|
||||
});
|
||||
}
|
||||
}
|
||||
const fn_type = try Type.Fn.create(comp, return_type, params.toOwnedSlice(), is_var_args);
|
||||
params_consumed = true;
|
||||
errdefer fn_type.base.base.deref(comp);
|
||||
|
||||
return fn_type;
|
||||
}
|
||||
|
||||
async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
||||
const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
|
||||
defer fn_type.base.base.deref(comp);
|
||||
|
||||
var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
|
||||
var symbol_name_consumed = false;
|
||||
defer if (!symbol_name_consumed) symbol_name.deinit();
|
||||
|
||||
// The Decl.Fn owns the initial 1 reference count
|
||||
const fn_proto_val = try Value.FnProto.create(comp, fn_type, symbol_name);
|
||||
fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val };
|
||||
symbol_name_consumed = true;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ const Allocator = mem.Allocator;
|
|||
const mem = std.mem;
|
||||
const ast = std.zig.ast;
|
||||
const Visib = @import("visib.zig").Visib;
|
||||
const ParsedFile = @import("parsed_file.zig").ParsedFile;
|
||||
const event = std.event;
|
||||
const Value = @import("value.zig").Value;
|
||||
const Token = std.zig.Token;
|
||||
|
@ -16,8 +15,6 @@ pub const Decl = struct {
|
|||
name: []const u8,
|
||||
visib: Visib,
|
||||
resolution: event.Future(Compilation.BuildError!void),
|
||||
resolution_in_progress: u8,
|
||||
parsed_file: *ParsedFile,
|
||||
parent_scope: *Scope,
|
||||
|
||||
pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
|
||||
|
@ -48,6 +45,10 @@ pub const Decl = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn findRootScope(base: *const Decl) *Scope.Root {
|
||||
return base.parent_scope.findRoot();
|
||||
}
|
||||
|
||||
pub const Id = enum {
|
||||
Var,
|
||||
Fn,
|
||||
|
@ -61,12 +62,13 @@ pub const Decl = struct {
|
|||
pub const Fn = struct {
|
||||
base: Decl,
|
||||
value: Val,
|
||||
fn_proto: *const ast.Node.FnProto,
|
||||
fn_proto: *ast.Node.FnProto,
|
||||
|
||||
// TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
|
||||
pub const Val = union {
|
||||
pub const Val = union(enum) {
|
||||
Unresolved: void,
|
||||
Ok: *Value.Fn,
|
||||
Fn: *Value.Fn,
|
||||
FnProto: *Value.FnProto,
|
||||
};
|
||||
|
||||
pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
|
||||
|
|
|
@ -4,6 +4,8 @@ const os = std.os;
|
|||
const Token = std.zig.Token;
|
||||
const ast = std.zig.ast;
|
||||
const TokenIndex = std.zig.ast.TokenIndex;
|
||||
const Compilation = @import("compilation.zig").Compilation;
|
||||
const Scope = @import("scope.zig").Scope;
|
||||
|
||||
pub const Color = enum {
|
||||
Auto,
|
||||
|
@ -16,85 +18,220 @@ pub const Span = struct {
|
|||
last: ast.TokenIndex,
|
||||
|
||||
pub fn token(i: TokenIndex) Span {
|
||||
return Span {
|
||||
return Span{
|
||||
.first = i,
|
||||
.last = i,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn node(n: *ast.Node) Span {
|
||||
return Span{
|
||||
.first = n.firstToken(),
|
||||
.last = n.lastToken(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Msg = struct {
|
||||
path: []const u8,
|
||||
text: []u8,
|
||||
span: Span,
|
||||
tree: *ast.Tree,
|
||||
};
|
||||
text: []u8,
|
||||
data: Data,
|
||||
|
||||
/// `path` must outlive the returned Msg
|
||||
/// `tree` must outlive the returned Msg
|
||||
/// Caller owns returned Msg and must free with `allocator`
|
||||
pub fn createFromParseError(
|
||||
allocator: *mem.Allocator,
|
||||
parse_error: *const ast.Error,
|
||||
tree: *ast.Tree,
|
||||
path: []const u8,
|
||||
) !*Msg {
|
||||
const loc_token = parse_error.loc();
|
||||
var text_buf = try std.Buffer.initSize(allocator, 0);
|
||||
defer text_buf.deinit();
|
||||
const Data = union(enum) {
|
||||
PathAndTree: PathAndTree,
|
||||
ScopeAndComp: ScopeAndComp,
|
||||
};
|
||||
|
||||
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
|
||||
try parse_error.render(&tree.tokens, out_stream);
|
||||
const PathAndTree = struct {
|
||||
realpath: []const u8,
|
||||
tree: *ast.Tree,
|
||||
allocator: *mem.Allocator,
|
||||
};
|
||||
|
||||
const msg = try allocator.create(Msg{
|
||||
.tree = tree,
|
||||
.path = path,
|
||||
.text = text_buf.toOwnedSlice(),
|
||||
.span = Span{
|
||||
.first = loc_token,
|
||||
.last = loc_token,
|
||||
},
|
||||
});
|
||||
errdefer allocator.destroy(msg);
|
||||
const ScopeAndComp = struct {
|
||||
root_scope: *Scope.Root,
|
||||
compilation: *Compilation,
|
||||
};
|
||||
|
||||
return msg;
|
||||
}
|
||||
pub fn destroy(self: *Msg) void {
|
||||
switch (self.data) {
|
||||
Data.PathAndTree => |path_and_tree| {
|
||||
path_and_tree.allocator.free(self.text);
|
||||
path_and_tree.allocator.destroy(self);
|
||||
},
|
||||
Data.ScopeAndComp => |scope_and_comp| {
|
||||
scope_and_comp.root_scope.base.deref(scope_and_comp.compilation);
|
||||
scope_and_comp.compilation.gpa().free(self.text);
|
||||
scope_and_comp.compilation.gpa().destroy(self);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn getAllocator(self: *const Msg) *mem.Allocator {
|
||||
switch (self.data) {
|
||||
Data.PathAndTree => |path_and_tree| {
|
||||
return path_and_tree.allocator;
|
||||
},
|
||||
Data.ScopeAndComp => |scope_and_comp| {
|
||||
return scope_and_comp.compilation.gpa();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getRealPath(self: *const Msg) []const u8 {
|
||||
switch (self.data) {
|
||||
Data.PathAndTree => |path_and_tree| {
|
||||
return path_and_tree.realpath;
|
||||
},
|
||||
Data.ScopeAndComp => |scope_and_comp| {
|
||||
return scope_and_comp.root_scope.realpath;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getTree(self: *const Msg) *ast.Tree {
|
||||
switch (self.data) {
|
||||
Data.PathAndTree => |path_and_tree| {
|
||||
return path_and_tree.tree;
|
||||
},
|
||||
Data.ScopeAndComp => |scope_and_comp| {
|
||||
return scope_and_comp.root_scope.tree;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes ownership of text
|
||||
/// References root_scope, and derefs when the msg is freed
|
||||
pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg {
|
||||
const msg = try comp.gpa().create(Msg{
|
||||
.text = text,
|
||||
.span = span,
|
||||
.data = Data{
|
||||
.ScopeAndComp = ScopeAndComp{
|
||||
.root_scope = root_scope,
|
||||
.compilation = comp,
|
||||
},
|
||||
},
|
||||
});
|
||||
root_scope.base.ref();
|
||||
return msg;
|
||||
}
|
||||
|
||||
pub fn createFromParseErrorAndScope(
|
||||
comp: *Compilation,
|
||||
root_scope: *Scope.Root,
|
||||
parse_error: *const ast.Error,
|
||||
) !*Msg {
|
||||
const loc_token = parse_error.loc();
|
||||
var text_buf = try std.Buffer.initSize(comp.gpa(), 0);
|
||||
defer text_buf.deinit();
|
||||
|
||||
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
|
||||
try parse_error.render(&root_scope.tree.tokens, out_stream);
|
||||
|
||||
const msg = try comp.gpa().create(Msg{
|
||||
.text = undefined,
|
||||
.span = Span{
|
||||
.first = loc_token,
|
||||
.last = loc_token,
|
||||
},
|
||||
.data = Data{
|
||||
.ScopeAndComp = ScopeAndComp{
|
||||
.root_scope = root_scope,
|
||||
.compilation = comp,
|
||||
},
|
||||
},
|
||||
});
|
||||
root_scope.base.ref();
|
||||
msg.text = text_buf.toOwnedSlice();
|
||||
return msg;
|
||||
}
|
||||
|
||||
/// `realpath` must outlive the returned Msg
|
||||
/// `tree` must outlive the returned Msg
|
||||
/// Caller owns returned Msg and must free with `allocator`
|
||||
/// allocator will additionally be used for printing messages later.
|
||||
pub fn createFromParseError(
|
||||
allocator: *mem.Allocator,
|
||||
parse_error: *const ast.Error,
|
||||
tree: *ast.Tree,
|
||||
realpath: []const u8,
|
||||
) !*Msg {
|
||||
const loc_token = parse_error.loc();
|
||||
var text_buf = try std.Buffer.initSize(allocator, 0);
|
||||
defer text_buf.deinit();
|
||||
|
||||
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
|
||||
try parse_error.render(&tree.tokens, out_stream);
|
||||
|
||||
const msg = try allocator.create(Msg{
|
||||
.text = undefined,
|
||||
.data = Data{
|
||||
.PathAndTree = PathAndTree{
|
||||
.allocator = allocator,
|
||||
.realpath = realpath,
|
||||
.tree = tree,
|
||||
},
|
||||
},
|
||||
.span = Span{
|
||||
.first = loc_token,
|
||||
.last = loc_token,
|
||||
},
|
||||
});
|
||||
msg.text = text_buf.toOwnedSlice();
|
||||
errdefer allocator.destroy(msg);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {
|
||||
const allocator = msg.getAllocator();
|
||||
const realpath = msg.getRealPath();
|
||||
const tree = msg.getTree();
|
||||
|
||||
const cwd = try os.getCwd(allocator);
|
||||
defer allocator.free(cwd);
|
||||
|
||||
const relpath = try os.path.relative(allocator, cwd, realpath);
|
||||
defer allocator.free(relpath);
|
||||
|
||||
const path = if (relpath.len < realpath.len) relpath else realpath;
|
||||
|
||||
const first_token = tree.tokens.at(msg.span.first);
|
||||
const last_token = tree.tokens.at(msg.span.last);
|
||||
const start_loc = tree.tokenLocationPtr(0, first_token);
|
||||
const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
|
||||
if (!color_on) {
|
||||
try stream.print(
|
||||
"{}:{}:{}: error: {}\n",
|
||||
path,
|
||||
start_loc.line + 1,
|
||||
start_loc.column + 1,
|
||||
msg.text,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void {
|
||||
const first_token = msg.tree.tokens.at(msg.span.first);
|
||||
const last_token = msg.tree.tokens.at(msg.span.last);
|
||||
const start_loc = msg.tree.tokenLocationPtr(0, first_token);
|
||||
const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token);
|
||||
if (!color_on) {
|
||||
try stream.print(
|
||||
"{}:{}:{}: error: {}\n",
|
||||
msg.path,
|
||||
"{}:{}:{}: error: {}\n{}\n",
|
||||
path,
|
||||
start_loc.line + 1,
|
||||
start_loc.column + 1,
|
||||
msg.text,
|
||||
tree.source[start_loc.line_start..start_loc.line_end],
|
||||
);
|
||||
return;
|
||||
try stream.writeByteNTimes(' ', start_loc.column);
|
||||
try stream.writeByteNTimes('~', last_token.end - first_token.start);
|
||||
try stream.write("\n");
|
||||
}
|
||||
|
||||
try stream.print(
|
||||
"{}:{}:{}: error: {}\n{}\n",
|
||||
msg.path,
|
||||
start_loc.line + 1,
|
||||
start_loc.column + 1,
|
||||
msg.text,
|
||||
msg.tree.source[start_loc.line_start..start_loc.line_end],
|
||||
);
|
||||
try stream.writeByteNTimes(' ', start_loc.column);
|
||||
try stream.writeByteNTimes('~', last_token.end - first_token.start);
|
||||
try stream.write("\n");
|
||||
}
|
||||
|
||||
pub fn printToFile(file: *os.File, msg: *const Msg, color: Color) !void {
|
||||
const color_on = switch (color) {
|
||||
Color.Auto => file.isTty(),
|
||||
Color.On => true,
|
||||
Color.Off => false,
|
||||
};
|
||||
var stream = &std.io.FileOutStream.init(file).stream;
|
||||
return printToStream(stream, msg, color_on);
|
||||
}
|
||||
pub fn printToFile(msg: *const Msg, file: *os.File, color: Color) !void {
|
||||
const color_on = switch (color) {
|
||||
Color.Auto => file.isTty(),
|
||||
Color.On => true,
|
||||
Color.Off => false,
|
||||
};
|
||||
var stream = &std.io.FileOutStream.init(file).stream;
|
||||
return msg.printToStream(stream, color_on);
|
||||
}
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,462 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const event = std.event;
|
||||
const Target = @import("target.zig").Target;
|
||||
const c = @import("c.zig");
|
||||
|
||||
/// See the render function implementation for documentation of the fields.
|
||||
pub const LibCInstallation = struct {
|
||||
include_dir: []const u8,
|
||||
lib_dir: ?[]const u8,
|
||||
static_lib_dir: ?[]const u8,
|
||||
msvc_lib_dir: ?[]const u8,
|
||||
kernel32_lib_dir: ?[]const u8,
|
||||
dynamic_linker_path: ?[]const u8,
|
||||
|
||||
pub const FindError = error{
|
||||
OutOfMemory,
|
||||
FileSystem,
|
||||
UnableToSpawnCCompiler,
|
||||
CCompilerExitCode,
|
||||
CCompilerCrashed,
|
||||
CCompilerCannotFindHeaders,
|
||||
LibCRuntimeNotFound,
|
||||
LibCStdLibHeaderNotFound,
|
||||
LibCKernel32LibNotFound,
|
||||
UnsupportedArchitecture,
|
||||
};
|
||||
|
||||
pub fn parse(
|
||||
self: *LibCInstallation,
|
||||
allocator: *std.mem.Allocator,
|
||||
libc_file: []const u8,
|
||||
stderr: *std.io.OutStream(std.io.FileOutStream.Error),
|
||||
) !void {
|
||||
self.initEmpty();
|
||||
|
||||
const keys = []const []const u8{
|
||||
"include_dir",
|
||||
"lib_dir",
|
||||
"static_lib_dir",
|
||||
"msvc_lib_dir",
|
||||
"kernel32_lib_dir",
|
||||
"dynamic_linker_path",
|
||||
};
|
||||
const FoundKey = struct {
|
||||
found: bool,
|
||||
allocated: ?[]u8,
|
||||
};
|
||||
var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** keys.len;
|
||||
errdefer {
|
||||
self.initEmpty();
|
||||
for (found_keys) |found_key| {
|
||||
if (found_key.allocated) |s| allocator.free(s);
|
||||
}
|
||||
}
|
||||
|
||||
const contents = try std.io.readFileAlloc(allocator, libc_file);
|
||||
defer allocator.free(contents);
|
||||
|
||||
var it = std.mem.split(contents, "\n");
|
||||
while (it.next()) |line| {
|
||||
if (line.len == 0 or line[0] == '#') continue;
|
||||
var line_it = std.mem.split(line, "=");
|
||||
const name = line_it.next() orelse {
|
||||
try stderr.print("missing equal sign after field name\n");
|
||||
return error.ParseError;
|
||||
};
|
||||
const value = line_it.rest();
|
||||
inline for (keys) |key, i| {
|
||||
if (std.mem.eql(u8, name, key)) {
|
||||
found_keys[i].found = true;
|
||||
switch (@typeInfo(@typeOf(@field(self, key)))) {
|
||||
builtin.TypeId.Optional => {
|
||||
if (value.len == 0) {
|
||||
@field(self, key) = null;
|
||||
} else {
|
||||
found_keys[i].allocated = try std.mem.dupe(allocator, u8, value);
|
||||
@field(self, key) = found_keys[i].allocated;
|
||||
}
|
||||
},
|
||||
else => {
|
||||
if (value.len == 0) {
|
||||
try stderr.print("field cannot be empty: {}\n", key);
|
||||
return error.ParseError;
|
||||
}
|
||||
const dupe = try std.mem.dupe(allocator, u8, value);
|
||||
found_keys[i].allocated = dupe;
|
||||
@field(self, key) = dupe;
|
||||
},
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (found_keys) |found_key, i| {
|
||||
if (!found_key.found) {
|
||||
try stderr.print("missing field: {}\n", keys[i]);
|
||||
return error.ParseError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(self: *const LibCInstallation, out: *std.io.OutStream(std.io.FileOutStream.Error)) !void {
|
||||
@setEvalBranchQuota(4000);
|
||||
try out.print(
|
||||
\\# The directory that contains `stdlib.h`.
|
||||
\\# On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null`
|
||||
\\include_dir={}
|
||||
\\
|
||||
\\# The directory that contains `crt1.o`.
|
||||
\\# On Linux, can be found with `cc -print-file-name=crt1.o`.
|
||||
\\# Not needed when targeting MacOS.
|
||||
\\lib_dir={}
|
||||
\\
|
||||
\\# The directory that contains `crtbegin.o`.
|
||||
\\# On Linux, can be found with `cc -print-file-name=crtbegin.o`.
|
||||
\\# Not needed when targeting MacOS or Windows.
|
||||
\\static_lib_dir={}
|
||||
\\
|
||||
\\# The directory that contains `vcruntime.lib`.
|
||||
\\# Only needed when targeting Windows.
|
||||
\\msvc_lib_dir={}
|
||||
\\
|
||||
\\# The directory that contains `kernel32.lib`.
|
||||
\\# Only needed when targeting Windows.
|
||||
\\kernel32_lib_dir={}
|
||||
\\
|
||||
\\# The full path to the dynamic linker, on the target system.
|
||||
\\# Only needed when targeting Linux.
|
||||
\\dynamic_linker_path={}
|
||||
\\
|
||||
,
|
||||
self.include_dir,
|
||||
self.lib_dir orelse "",
|
||||
self.static_lib_dir orelse "",
|
||||
self.msvc_lib_dir orelse "",
|
||||
self.kernel32_lib_dir orelse "",
|
||||
self.dynamic_linker_path orelse Target(Target.Native).getDynamicLinkerPath(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Finds the default, native libc.
|
||||
pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void {
|
||||
self.initEmpty();
|
||||
var group = event.Group(FindError!void).init(loop);
|
||||
errdefer group.cancelAll();
|
||||
var windows_sdk: ?*c.ZigWindowsSDK = null;
|
||||
errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk));
|
||||
|
||||
switch (builtin.os) {
|
||||
builtin.Os.windows => {
|
||||
var sdk: *c.ZigWindowsSDK = undefined;
|
||||
switch (c.zig_find_windows_sdk(@ptrCast(?[*]?[*]c.ZigWindowsSDK, &sdk))) {
|
||||
c.ZigFindWindowsSdkError.None => {
|
||||
windows_sdk = sdk;
|
||||
|
||||
if (sdk.msvc_lib_dir_ptr) |ptr| {
|
||||
self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, ptr[0..sdk.msvc_lib_dir_len]);
|
||||
}
|
||||
try group.call(findNativeKernel32LibDir, self, loop, sdk);
|
||||
try group.call(findNativeIncludeDirWindows, self, loop, sdk);
|
||||
try group.call(findNativeLibDirWindows, self, loop, sdk);
|
||||
},
|
||||
c.ZigFindWindowsSdkError.OutOfMemory => return error.OutOfMemory,
|
||||
c.ZigFindWindowsSdkError.NotFound => return error.NotFound,
|
||||
c.ZigFindWindowsSdkError.PathTooLong => return error.NotFound,
|
||||
}
|
||||
},
|
||||
builtin.Os.linux => {
|
||||
try group.call(findNativeIncludeDirLinux, self, loop);
|
||||
try group.call(findNativeLibDirLinux, self, loop);
|
||||
try group.call(findNativeStaticLibDir, self, loop);
|
||||
try group.call(findNativeDynamicLinker, self, loop);
|
||||
},
|
||||
builtin.Os.macosx => {
|
||||
self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include");
|
||||
},
|
||||
else => @compileError("unimplemented: find libc for this OS"),
|
||||
}
|
||||
return await (async group.wait() catch unreachable);
|
||||
}
|
||||
|
||||
async fn findNativeIncludeDirLinux(self: *LibCInstallation, loop: *event.Loop) !void {
|
||||
const cc_exe = std.os.getEnvPosix("CC") orelse "cc";
|
||||
const argv = []const []const u8{
|
||||
cc_exe,
|
||||
"-E",
|
||||
"-Wp,-v",
|
||||
"-xc",
|
||||
"/dev/null",
|
||||
};
|
||||
// TODO make this use event loop
|
||||
const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024);
|
||||
const exec_result = if (std.debug.runtime_safety) blk: {
|
||||
break :blk errorable_result catch unreachable;
|
||||
} else blk: {
|
||||
break :blk errorable_result catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => return error.UnableToSpawnCCompiler,
|
||||
};
|
||||
};
|
||||
defer {
|
||||
loop.allocator.free(exec_result.stdout);
|
||||
loop.allocator.free(exec_result.stderr);
|
||||
}
|
||||
|
||||
switch (exec_result.term) {
|
||||
std.os.ChildProcess.Term.Exited => |code| {
|
||||
if (code != 0) return error.CCompilerExitCode;
|
||||
},
|
||||
else => {
|
||||
return error.CCompilerCrashed;
|
||||
},
|
||||
}
|
||||
|
||||
var it = std.mem.split(exec_result.stderr, "\n\r");
|
||||
var search_paths = std.ArrayList([]const u8).init(loop.allocator);
|
||||
defer search_paths.deinit();
|
||||
while (it.next()) |line| {
|
||||
if (line.len != 0 and line[0] == ' ') {
|
||||
try search_paths.append(line);
|
||||
}
|
||||
}
|
||||
if (search_paths.len == 0) {
|
||||
return error.CCompilerCannotFindHeaders;
|
||||
}
|
||||
|
||||
// search in reverse order
|
||||
var path_i: usize = 0;
|
||||
while (path_i < search_paths.len) : (path_i += 1) {
|
||||
const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1);
|
||||
const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
|
||||
const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h");
|
||||
defer loop.allocator.free(stdlib_path);
|
||||
|
||||
if (try fileExists(loop.allocator, stdlib_path)) {
|
||||
self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return error.LibCStdLibHeaderNotFound;
|
||||
}
|
||||
|
||||
async fn findNativeIncludeDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) !void {
|
||||
var search_buf: [2]Search = undefined;
|
||||
const searches = fillSearch(&search_buf, sdk);
|
||||
|
||||
var result_buf = try std.Buffer.initSize(loop.allocator, 0);
|
||||
defer result_buf.deinit();
|
||||
|
||||
for (searches) |search| {
|
||||
result_buf.shrink(0);
|
||||
const stream = &std.io.BufferOutStream.init(&result_buf).stream;
|
||||
try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version);
|
||||
|
||||
const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h");
|
||||
defer loop.allocator.free(stdlib_path);
|
||||
|
||||
if (try fileExists(loop.allocator, stdlib_path)) {
|
||||
self.include_dir = result_buf.toOwnedSlice();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return error.LibCStdLibHeaderNotFound;
|
||||
}
|
||||
|
||||
async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
|
||||
var search_buf: [2]Search = undefined;
|
||||
const searches = fillSearch(&search_buf, sdk);
|
||||
|
||||
var result_buf = try std.Buffer.initSize(loop.allocator, 0);
|
||||
defer result_buf.deinit();
|
||||
|
||||
for (searches) |search| {
|
||||
result_buf.shrink(0);
|
||||
const stream = &std.io.BufferOutStream.init(&result_buf).stream;
|
||||
try stream.print("{}\\Lib\\{}\\ucrt\\", search.path, search.version);
|
||||
switch (builtin.arch) {
|
||||
builtin.Arch.i386 => try stream.write("x86"),
|
||||
builtin.Arch.x86_64 => try stream.write("x64"),
|
||||
builtin.Arch.aarch64 => try stream.write("arm"),
|
||||
else => return error.UnsupportedArchitecture,
|
||||
}
|
||||
const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib");
|
||||
defer loop.allocator.free(ucrt_lib_path);
|
||||
if (try fileExists(loop.allocator, ucrt_lib_path)) {
|
||||
self.lib_dir = result_buf.toOwnedSlice();
|
||||
return;
|
||||
}
|
||||
}
|
||||
return error.LibCRuntimeNotFound;
|
||||
}
|
||||
|
||||
async fn findNativeLibDirLinux(self: *LibCInstallation, loop: *event.Loop) FindError!void {
|
||||
self.lib_dir = try await (async ccPrintFileName(loop, "crt1.o", true) catch unreachable);
|
||||
}
|
||||
|
||||
async fn findNativeStaticLibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void {
|
||||
self.static_lib_dir = try await (async ccPrintFileName(loop, "crtbegin.o", true) catch unreachable);
|
||||
}
|
||||
|
||||
async fn findNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop) FindError!void {
|
||||
var dyn_tests = []DynTest{
|
||||
DynTest{
|
||||
.name = "ld-linux-x86-64.so.2",
|
||||
.result = null,
|
||||
},
|
||||
DynTest{
|
||||
.name = "ld-musl-x86_64.so.1",
|
||||
.result = null,
|
||||
},
|
||||
};
|
||||
var group = event.Group(FindError!void).init(loop);
|
||||
errdefer group.cancelAll();
|
||||
for (dyn_tests) |*dyn_test| {
|
||||
try group.call(testNativeDynamicLinker, self, loop, dyn_test);
|
||||
}
|
||||
try await (async group.wait() catch unreachable);
|
||||
for (dyn_tests) |*dyn_test| {
|
||||
if (dyn_test.result) |result| {
|
||||
self.dynamic_linker_path = result;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const DynTest = struct {
|
||||
name: []const u8,
|
||||
result: ?[]const u8,
|
||||
};
|
||||
|
||||
async fn testNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop, dyn_test: *DynTest) FindError!void {
|
||||
if (await (async ccPrintFileName(loop, dyn_test.name, false) catch unreachable)) |result| {
|
||||
dyn_test.result = result;
|
||||
return;
|
||||
} else |err| switch (err) {
|
||||
error.LibCRuntimeNotFound => return,
|
||||
else => return err,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
|
||||
var search_buf: [2]Search = undefined;
|
||||
const searches = fillSearch(&search_buf, sdk);
|
||||
|
||||
var result_buf = try std.Buffer.initSize(loop.allocator, 0);
|
||||
defer result_buf.deinit();
|
||||
|
||||
for (searches) |search| {
|
||||
result_buf.shrink(0);
|
||||
const stream = &std.io.BufferOutStream.init(&result_buf).stream;
|
||||
try stream.print("{}\\Lib\\{}\\um\\", search.path, search.version);
|
||||
switch (builtin.arch) {
|
||||
builtin.Arch.i386 => try stream.write("x86\\"),
|
||||
builtin.Arch.x86_64 => try stream.write("x64\\"),
|
||||
builtin.Arch.aarch64 => try stream.write("arm\\"),
|
||||
else => return error.UnsupportedArchitecture,
|
||||
}
|
||||
const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib");
|
||||
defer loop.allocator.free(kernel32_path);
|
||||
if (try fileExists(loop.allocator, kernel32_path)) {
|
||||
self.kernel32_lib_dir = result_buf.toOwnedSlice();
|
||||
return;
|
||||
}
|
||||
}
|
||||
return error.LibCKernel32LibNotFound;
|
||||
}
|
||||
|
||||
fn initEmpty(self: *LibCInstallation) void {
|
||||
self.* = LibCInstallation{
|
||||
.include_dir = ([*]const u8)(undefined)[0..0],
|
||||
.lib_dir = null,
|
||||
.static_lib_dir = null,
|
||||
.msvc_lib_dir = null,
|
||||
.kernel32_lib_dir = null,
|
||||
.dynamic_linker_path = null,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// caller owns returned memory
|
||||
async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bool) ![]u8 {
|
||||
const cc_exe = std.os.getEnvPosix("CC") orelse "cc";
|
||||
const arg1 = try std.fmt.allocPrint(loop.allocator, "-print-file-name={}", o_file);
|
||||
defer loop.allocator.free(arg1);
|
||||
const argv = []const []const u8{ cc_exe, arg1 };
|
||||
|
||||
// TODO This simulates evented I/O for the child process exec
|
||||
await (async loop.yield() catch unreachable);
|
||||
const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024);
|
||||
const exec_result = if (std.debug.runtime_safety) blk: {
|
||||
break :blk errorable_result catch unreachable;
|
||||
} else blk: {
|
||||
break :blk errorable_result catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => return error.UnableToSpawnCCompiler,
|
||||
};
|
||||
};
|
||||
defer {
|
||||
loop.allocator.free(exec_result.stdout);
|
||||
loop.allocator.free(exec_result.stderr);
|
||||
}
|
||||
switch (exec_result.term) {
|
||||
std.os.ChildProcess.Term.Exited => |code| {
|
||||
if (code != 0) return error.CCompilerExitCode;
|
||||
},
|
||||
else => {
|
||||
return error.CCompilerCrashed;
|
||||
},
|
||||
}
|
||||
var it = std.mem.split(exec_result.stdout, "\n\r");
|
||||
const line = it.next() orelse return error.LibCRuntimeNotFound;
|
||||
const dirname = std.os.path.dirname(line) orelse return error.LibCRuntimeNotFound;
|
||||
|
||||
if (want_dirname) {
|
||||
return std.mem.dupe(loop.allocator, u8, dirname);
|
||||
} else {
|
||||
return std.mem.dupe(loop.allocator, u8, line);
|
||||
}
|
||||
}
|
||||
|
||||
const Search = struct {
|
||||
path: []const u8,
|
||||
version: []const u8,
|
||||
};
|
||||
|
||||
fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {
|
||||
var search_end: usize = 0;
|
||||
if (sdk.path10_ptr) |path10_ptr| {
|
||||
if (sdk.version10_ptr) |ver10_ptr| {
|
||||
search_buf[search_end] = Search{
|
||||
.path = path10_ptr[0..sdk.path10_len],
|
||||
.version = ver10_ptr[0..sdk.version10_len],
|
||||
};
|
||||
search_end += 1;
|
||||
}
|
||||
}
|
||||
if (sdk.path81_ptr) |path81_ptr| {
|
||||
if (sdk.version81_ptr) |ver81_ptr| {
|
||||
search_buf[search_end] = Search{
|
||||
.path = path81_ptr[0..sdk.path81_len],
|
||||
.version = ver81_ptr[0..sdk.version81_len],
|
||||
};
|
||||
search_end += 1;
|
||||
}
|
||||
}
|
||||
return search_buf[0..search_end];
|
||||
}
|
||||
|
||||
|
||||
fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool {
|
||||
if (std.os.File.access(allocator, path)) |_| {
|
||||
return true;
|
||||
} else |err| switch (err) {
|
||||
error.NotFound, error.PermissionDenied => return false,
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => return error.FileSystem,
|
||||
}
|
||||
}
|
|
@ -1,8 +1,12 @@
|
|||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const c = @import("c.zig");
|
||||
const builtin = @import("builtin");
|
||||
const ObjectFormat = builtin.ObjectFormat;
|
||||
const Compilation = @import("compilation.zig").Compilation;
|
||||
const Target = @import("target.zig").Target;
|
||||
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const Context = struct {
|
||||
comp: *Compilation,
|
||||
|
@ -12,9 +16,12 @@ const Context = struct {
|
|||
|
||||
link_err: error{OutOfMemory}!void,
|
||||
link_msg: std.Buffer,
|
||||
|
||||
libc: *LibCInstallation,
|
||||
out_file_path: std.Buffer,
|
||||
};
|
||||
|
||||
pub fn link(comp: *Compilation) !void {
|
||||
pub async fn link(comp: *Compilation) !void {
|
||||
var ctx = Context{
|
||||
.comp = comp,
|
||||
.arena = std.heap.ArenaAllocator.init(comp.gpa()),
|
||||
|
@ -22,15 +29,45 @@ pub fn link(comp: *Compilation) !void {
|
|||
.link_in_crt = comp.haveLibC() and comp.kind == Compilation.Kind.Exe,
|
||||
.link_err = {},
|
||||
.link_msg = undefined,
|
||||
.libc = undefined,
|
||||
.out_file_path = undefined,
|
||||
};
|
||||
defer ctx.arena.deinit();
|
||||
ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator);
|
||||
ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator);
|
||||
|
||||
if (comp.link_out_file) |out_file| {
|
||||
ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, out_file);
|
||||
} else {
|
||||
ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, comp.name.toSliceConst());
|
||||
switch (comp.kind) {
|
||||
Compilation.Kind.Exe => {
|
||||
try ctx.out_file_path.append(comp.target.exeFileExt());
|
||||
},
|
||||
Compilation.Kind.Lib => {
|
||||
try ctx.out_file_path.append(comp.target.libFileExt(comp.is_static));
|
||||
},
|
||||
Compilation.Kind.Obj => {
|
||||
try ctx.out_file_path.append(comp.target.objFileExt());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// even though we're calling LLD as a library it thinks the first
|
||||
// argument is its own exe name
|
||||
try ctx.args.append(c"lld");
|
||||
|
||||
if (comp.haveLibC()) {
|
||||
ctx.libc = ctx.comp.override_libc orelse blk: {
|
||||
switch (comp.target) {
|
||||
Target.Native => {
|
||||
break :blk (await (async comp.event_loop_local.getNativeLibC() catch unreachable)) catch return error.LibCRequiredButNotProvidedOrFound;
|
||||
},
|
||||
else => return error.LibCRequiredButNotProvidedOrFound,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
try constructLinkerArgs(&ctx);
|
||||
|
||||
if (comp.verbose_link) {
|
||||
|
@ -43,6 +80,7 @@ pub fn link(comp: *Compilation) !void {
|
|||
|
||||
const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat());
|
||||
const args_slice = ctx.args.toSlice();
|
||||
// Not evented I/O. LLD does its own multithreading internally.
|
||||
if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) {
|
||||
if (!ctx.link_msg.isNull()) {
|
||||
// TODO capture these messages and pass them through the system, reporting them through the
|
||||
|
@ -95,10 +133,7 @@ fn constructLinkerArgs(ctx: *Context) !void {
|
|||
}
|
||||
|
||||
fn constructLinkerArgsElf(ctx: *Context) !void {
|
||||
//if (g->libc_link_lib != nullptr) {
|
||||
// find_libc_lib_path(g);
|
||||
//}
|
||||
|
||||
// TODO commented out code in this function
|
||||
//if (g->linker_script) {
|
||||
// lj->args.append("-T");
|
||||
// lj->args.append(g->linker_script);
|
||||
|
@ -107,7 +142,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
|
|||
//if (g->no_rosegment_workaround) {
|
||||
// lj->args.append("--no-rosegment");
|
||||
//}
|
||||
//lj->args.append("--gc-sections");
|
||||
try ctx.args.append(c"--gc-sections");
|
||||
|
||||
//lj->args.append("-m");
|
||||
//lj->args.append(getLDMOption(&g->zig_target));
|
||||
|
@ -115,14 +150,13 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
|
|||
//bool is_lib = g->out_type == OutTypeLib;
|
||||
//bool shared = !g->is_static && is_lib;
|
||||
//Buf *soname = nullptr;
|
||||
//if (g->is_static) {
|
||||
// if (g->zig_target.arch.arch == ZigLLVM_arm || g->zig_target.arch.arch == ZigLLVM_armeb ||
|
||||
// g->zig_target.arch.arch == ZigLLVM_thumb || g->zig_target.arch.arch == ZigLLVM_thumbeb)
|
||||
// {
|
||||
// lj->args.append("-Bstatic");
|
||||
// } else {
|
||||
// lj->args.append("-static");
|
||||
// }
|
||||
if (ctx.comp.is_static) {
|
||||
if (ctx.comp.target.isArmOrThumb()) {
|
||||
try ctx.args.append(c"-Bstatic");
|
||||
} else {
|
||||
try ctx.args.append(c"-static");
|
||||
}
|
||||
}
|
||||
//} else if (shared) {
|
||||
// lj->args.append("-shared");
|
||||
|
||||
|
@ -133,23 +167,16 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
|
|||
// soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major);
|
||||
//}
|
||||
|
||||
//lj->args.append("-o");
|
||||
//lj->args.append(buf_ptr(&lj->out_file));
|
||||
try ctx.args.append(c"-o");
|
||||
try ctx.args.append(ctx.out_file_path.ptr());
|
||||
|
||||
//if (lj->link_in_crt) {
|
||||
// const char *crt1o;
|
||||
// const char *crtbegino;
|
||||
// if (g->is_static) {
|
||||
// crt1o = "crt1.o";
|
||||
// crtbegino = "crtbeginT.o";
|
||||
// } else {
|
||||
// crt1o = "Scrt1.o";
|
||||
// crtbegino = "crtbegin.o";
|
||||
// }
|
||||
// lj->args.append(get_libc_file(g, crt1o));
|
||||
// lj->args.append(get_libc_file(g, "crti.o"));
|
||||
// lj->args.append(get_libc_static_file(g, crtbegino));
|
||||
//}
|
||||
if (ctx.link_in_crt) {
|
||||
const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o";
|
||||
const crtbegino = if (ctx.comp.is_static) "crtbeginT.o" else "crtbegin.o";
|
||||
try addPathJoin(ctx, ctx.libc.lib_dir.?, crt1o);
|
||||
try addPathJoin(ctx, ctx.libc.lib_dir.?, "crti.o");
|
||||
try addPathJoin(ctx, ctx.libc.static_lib_dir.?, crtbegino);
|
||||
}
|
||||
|
||||
//for (size_t i = 0; i < g->rpath_list.length; i += 1) {
|
||||
// Buf *rpath = g->rpath_list.at(i);
|
||||
|
@ -182,25 +209,23 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
|
|||
// lj->args.append(lib_dir);
|
||||
//}
|
||||
|
||||
//if (g->libc_link_lib != nullptr) {
|
||||
// lj->args.append("-L");
|
||||
// lj->args.append(buf_ptr(g->libc_lib_dir));
|
||||
if (ctx.comp.haveLibC()) {
|
||||
try ctx.args.append(c"-L");
|
||||
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr);
|
||||
|
||||
// lj->args.append("-L");
|
||||
// lj->args.append(buf_ptr(g->libc_static_lib_dir));
|
||||
//}
|
||||
try ctx.args.append(c"-L");
|
||||
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr);
|
||||
|
||||
//if (!g->is_static) {
|
||||
// if (g->dynamic_linker != nullptr) {
|
||||
// assert(buf_len(g->dynamic_linker) != 0);
|
||||
// lj->args.append("-dynamic-linker");
|
||||
// lj->args.append(buf_ptr(g->dynamic_linker));
|
||||
// } else {
|
||||
// Buf *resolved_dynamic_linker = get_dynamic_linker_path(g);
|
||||
// lj->args.append("-dynamic-linker");
|
||||
// lj->args.append(buf_ptr(resolved_dynamic_linker));
|
||||
// }
|
||||
//}
|
||||
if (!ctx.comp.is_static) {
|
||||
const dl = blk: {
|
||||
if (ctx.libc.dynamic_linker_path) |dl| break :blk dl;
|
||||
if (ctx.comp.target.getDynamicLinkerPath()) |dl| break :blk dl;
|
||||
return error.LibCMissingDynamicLinker;
|
||||
};
|
||||
try ctx.args.append(c"-dynamic-linker");
|
||||
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr);
|
||||
}
|
||||
}
|
||||
|
||||
//if (shared) {
|
||||
// lj->args.append("-soname");
|
||||
|
@ -241,55 +266,358 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
|
|||
// lj->args.append(buf_ptr(arg));
|
||||
//}
|
||||
|
||||
//// libc dep
|
||||
//if (g->libc_link_lib != nullptr) {
|
||||
// if (g->is_static) {
|
||||
// lj->args.append("--start-group");
|
||||
// lj->args.append("-lgcc");
|
||||
// lj->args.append("-lgcc_eh");
|
||||
// lj->args.append("-lc");
|
||||
// lj->args.append("-lm");
|
||||
// lj->args.append("--end-group");
|
||||
// libc dep
|
||||
if (ctx.comp.haveLibC()) {
|
||||
if (ctx.comp.is_static) {
|
||||
try ctx.args.append(c"--start-group");
|
||||
try ctx.args.append(c"-lgcc");
|
||||
try ctx.args.append(c"-lgcc_eh");
|
||||
try ctx.args.append(c"-lc");
|
||||
try ctx.args.append(c"-lm");
|
||||
try ctx.args.append(c"--end-group");
|
||||
} else {
|
||||
try ctx.args.append(c"-lgcc");
|
||||
try ctx.args.append(c"--as-needed");
|
||||
try ctx.args.append(c"-lgcc_s");
|
||||
try ctx.args.append(c"--no-as-needed");
|
||||
try ctx.args.append(c"-lc");
|
||||
try ctx.args.append(c"-lm");
|
||||
try ctx.args.append(c"-lgcc");
|
||||
try ctx.args.append(c"--as-needed");
|
||||
try ctx.args.append(c"-lgcc_s");
|
||||
try ctx.args.append(c"--no-as-needed");
|
||||
}
|
||||
}
|
||||
|
||||
// crt end
|
||||
if (ctx.link_in_crt) {
|
||||
try addPathJoin(ctx, ctx.libc.static_lib_dir.?, "crtend.o");
|
||||
try addPathJoin(ctx, ctx.libc.lib_dir.?, "crtn.o");
|
||||
}
|
||||
|
||||
if (ctx.comp.target != Target.Native) {
|
||||
try ctx.args.append(c"--allow-shlib-undefined");
|
||||
}
|
||||
|
||||
if (ctx.comp.target.getOs() == builtin.Os.zen) {
|
||||
try ctx.args.append(c"-e");
|
||||
try ctx.args.append(c"_start");
|
||||
|
||||
try ctx.args.append(c"--image-base=0x10000000");
|
||||
}
|
||||
}
|
||||
|
||||
fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {
|
||||
const full_path = try std.os.path.join(&ctx.arena.allocator, dirname, basename);
|
||||
const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);
|
||||
try ctx.args.append(full_path_with_null.ptr);
|
||||
}
|
||||
|
||||
fn constructLinkerArgsCoff(ctx: *Context) !void {
|
||||
try ctx.args.append(c"-NOLOGO");
|
||||
|
||||
if (!ctx.comp.strip) {
|
||||
try ctx.args.append(c"-DEBUG");
|
||||
}
|
||||
|
||||
switch (ctx.comp.target.getArch()) {
|
||||
builtin.Arch.i386 => try ctx.args.append(c"-MACHINE:X86"),
|
||||
builtin.Arch.x86_64 => try ctx.args.append(c"-MACHINE:X64"),
|
||||
builtin.Arch.aarch64 => try ctx.args.append(c"-MACHINE:ARM"),
|
||||
else => return error.UnsupportedLinkArchitecture,
|
||||
}
|
||||
|
||||
if (ctx.comp.windows_subsystem_windows) {
|
||||
try ctx.args.append(c"/SUBSYSTEM:windows");
|
||||
} else if (ctx.comp.windows_subsystem_console) {
|
||||
try ctx.args.append(c"/SUBSYSTEM:console");
|
||||
}
|
||||
|
||||
const is_library = ctx.comp.kind == Compilation.Kind.Lib;
|
||||
|
||||
const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", ctx.out_file_path.toSliceConst());
|
||||
try ctx.args.append(out_arg.ptr);
|
||||
|
||||
if (ctx.comp.haveLibC()) {
|
||||
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr);
|
||||
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr);
|
||||
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr);
|
||||
}
|
||||
|
||||
if (ctx.link_in_crt) {
|
||||
const lib_str = if (ctx.comp.is_static) "lib" else "";
|
||||
const d_str = if (ctx.comp.build_mode == builtin.Mode.Debug) "d" else "";
|
||||
|
||||
if (ctx.comp.is_static) {
|
||||
const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", d_str);
|
||||
try ctx.args.append(cmt_lib_name.ptr);
|
||||
} else {
|
||||
const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", d_str);
|
||||
try ctx.args.append(msvcrt_lib_name.ptr);
|
||||
}
|
||||
|
||||
const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", lib_str, d_str);
|
||||
try ctx.args.append(vcruntime_lib_name.ptr);
|
||||
|
||||
const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", lib_str, d_str);
|
||||
try ctx.args.append(crt_lib_name.ptr);
|
||||
|
||||
// Visual C++ 2015 Conformance Changes
|
||||
// https://msdn.microsoft.com/en-us/library/bb531344.aspx
|
||||
try ctx.args.append(c"legacy_stdio_definitions.lib");
|
||||
|
||||
// msvcrt depends on kernel32
|
||||
try ctx.args.append(c"kernel32.lib");
|
||||
} else {
|
||||
try ctx.args.append(c"-NODEFAULTLIB");
|
||||
if (!is_library) {
|
||||
try ctx.args.append(c"-ENTRY:WinMainCRTStartup");
|
||||
// TODO
|
||||
//if (g->have_winmain) {
|
||||
// lj->args.append("-ENTRY:WinMain");
|
||||
//} else {
|
||||
// lj->args.append("-ENTRY:WinMainCRTStartup");
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_library and !ctx.comp.is_static) {
|
||||
try ctx.args.append(c"-DLL");
|
||||
}
|
||||
|
||||
//for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
|
||||
// const char *lib_dir = g->lib_dirs.at(i);
|
||||
// lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", lib_dir)));
|
||||
//}
|
||||
|
||||
for (ctx.comp.link_objects) |link_object| {
|
||||
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
|
||||
try ctx.args.append(link_obj_with_null.ptr);
|
||||
}
|
||||
try addFnObjects(ctx);
|
||||
|
||||
switch (ctx.comp.kind) {
|
||||
Compilation.Kind.Exe, Compilation.Kind.Lib => {
|
||||
if (!ctx.comp.haveLibC()) {
|
||||
@panic("TODO");
|
||||
//Buf *builtin_o_path = build_o(g, "builtin");
|
||||
//lj->args.append(buf_ptr(builtin_o_path));
|
||||
}
|
||||
|
||||
// msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage
|
||||
// TODO
|
||||
//Buf *compiler_rt_o_path = build_compiler_rt(g);
|
||||
//lj->args.append(buf_ptr(compiler_rt_o_path));
|
||||
},
|
||||
Compilation.Kind.Obj => {},
|
||||
}
|
||||
|
||||
//Buf *def_contents = buf_alloc();
|
||||
//ZigList<const char *> gen_lib_args = {0};
|
||||
//for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) {
|
||||
// LinkLib *link_lib = g->link_libs_list.at(lib_i);
|
||||
// if (buf_eql_str(link_lib->name, "c")) {
|
||||
// continue;
|
||||
// }
|
||||
// if (link_lib->provided_explicitly) {
|
||||
// if (lj->codegen->zig_target.env_type == ZigLLVM_GNU) {
|
||||
// Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
|
||||
// lj->args.append(buf_ptr(arg));
|
||||
// }
|
||||
// else {
|
||||
// lj->args.append(buf_ptr(link_lib->name));
|
||||
// }
|
||||
// } else {
|
||||
// lj->args.append("-lgcc");
|
||||
// lj->args.append("--as-needed");
|
||||
// lj->args.append("-lgcc_s");
|
||||
// lj->args.append("--no-as-needed");
|
||||
// lj->args.append("-lc");
|
||||
// lj->args.append("-lm");
|
||||
// lj->args.append("-lgcc");
|
||||
// lj->args.append("--as-needed");
|
||||
// lj->args.append("-lgcc_s");
|
||||
// lj->args.append("--no-as-needed");
|
||||
// buf_resize(def_contents, 0);
|
||||
// buf_appendf(def_contents, "LIBRARY %s\nEXPORTS\n", buf_ptr(link_lib->name));
|
||||
// for (size_t exp_i = 0; exp_i < link_lib->symbols.length; exp_i += 1) {
|
||||
// Buf *symbol_name = link_lib->symbols.at(exp_i);
|
||||
// buf_appendf(def_contents, "%s\n", buf_ptr(symbol_name));
|
||||
// }
|
||||
// buf_appendf(def_contents, "\n");
|
||||
|
||||
// Buf *def_path = buf_alloc();
|
||||
// os_path_join(g->cache_dir, buf_sprintf("%s.def", buf_ptr(link_lib->name)), def_path);
|
||||
// os_write_file(def_path, def_contents);
|
||||
|
||||
// Buf *generated_lib_path = buf_alloc();
|
||||
// os_path_join(g->cache_dir, buf_sprintf("%s.lib", buf_ptr(link_lib->name)), generated_lib_path);
|
||||
|
||||
// gen_lib_args.resize(0);
|
||||
// gen_lib_args.append("link");
|
||||
|
||||
// coff_append_machine_arg(g, &gen_lib_args);
|
||||
// gen_lib_args.append(buf_ptr(buf_sprintf("-DEF:%s", buf_ptr(def_path))));
|
||||
// gen_lib_args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(generated_lib_path))));
|
||||
// Buf diag = BUF_INIT;
|
||||
// if (!zig_lld_link(g->zig_target.oformat, gen_lib_args.items, gen_lib_args.length, &diag)) {
|
||||
// fprintf(stderr, "%s\n", buf_ptr(&diag));
|
||||
// exit(1);
|
||||
// }
|
||||
// lj->args.append(buf_ptr(generated_lib_path));
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
fn constructLinkerArgsMachO(ctx: *Context) !void {
|
||||
try ctx.args.append(c"-demangle");
|
||||
|
||||
if (ctx.comp.linker_rdynamic) {
|
||||
try ctx.args.append(c"-export_dynamic");
|
||||
}
|
||||
|
||||
const is_lib = ctx.comp.kind == Compilation.Kind.Lib;
|
||||
const shared = !ctx.comp.is_static and is_lib;
|
||||
if (ctx.comp.is_static) {
|
||||
try ctx.args.append(c"-static");
|
||||
} else {
|
||||
try ctx.args.append(c"-dynamic");
|
||||
}
|
||||
|
||||
//if (is_lib) {
|
||||
// if (!g->is_static) {
|
||||
// lj->args.append("-dylib");
|
||||
|
||||
// Buf *compat_vers = buf_sprintf("%" ZIG_PRI_usize ".0.0", g->version_major);
|
||||
// lj->args.append("-compatibility_version");
|
||||
// lj->args.append(buf_ptr(compat_vers));
|
||||
|
||||
// Buf *cur_vers = buf_sprintf("%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize,
|
||||
// g->version_major, g->version_minor, g->version_patch);
|
||||
// lj->args.append("-current_version");
|
||||
// lj->args.append(buf_ptr(cur_vers));
|
||||
|
||||
// // TODO getting an error when running an executable when doing this rpath thing
|
||||
// //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib",
|
||||
// // buf_ptr(g->root_out_name), g->version_major);
|
||||
// //lj->args.append("-install_name");
|
||||
// //lj->args.append(buf_ptr(dylib_install_name));
|
||||
|
||||
// if (buf_len(&lj->out_file) == 0) {
|
||||
// buf_appendf(&lj->out_file, "lib%s.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib",
|
||||
// buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//// crt end
|
||||
//if (lj->link_in_crt) {
|
||||
// lj->args.append(get_libc_static_file(g, "crtend.o"));
|
||||
// lj->args.append(get_libc_file(g, "crtn.o"));
|
||||
try ctx.args.append(c"-arch");
|
||||
const darwin_arch_str = try std.cstr.addNullByte(
|
||||
&ctx.arena.allocator,
|
||||
ctx.comp.target.getDarwinArchString(),
|
||||
);
|
||||
try ctx.args.append(darwin_arch_str.ptr);
|
||||
|
||||
const platform = try DarwinPlatform.get(ctx.comp);
|
||||
switch (platform.kind) {
|
||||
DarwinPlatform.Kind.MacOS => try ctx.args.append(c"-macosx_version_min"),
|
||||
DarwinPlatform.Kind.IPhoneOS => try ctx.args.append(c"-iphoneos_version_min"),
|
||||
DarwinPlatform.Kind.IPhoneOSSimulator => try ctx.args.append(c"-ios_simulator_version_min"),
|
||||
}
|
||||
const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", platform.major, platform.minor, platform.micro);
|
||||
try ctx.args.append(ver_str.ptr);
|
||||
|
||||
if (ctx.comp.kind == Compilation.Kind.Exe) {
|
||||
if (ctx.comp.is_static) {
|
||||
try ctx.args.append(c"-no_pie");
|
||||
} else {
|
||||
try ctx.args.append(c"-pie");
|
||||
}
|
||||
}
|
||||
|
||||
try ctx.args.append(c"-o");
|
||||
try ctx.args.append(ctx.out_file_path.ptr());
|
||||
|
||||
//for (size_t i = 0; i < g->rpath_list.length; i += 1) {
|
||||
// Buf *rpath = g->rpath_list.at(i);
|
||||
// add_rpath(lj, rpath);
|
||||
//}
|
||||
//add_rpath(lj, &lj->out_file);
|
||||
|
||||
if (shared) {
|
||||
try ctx.args.append(c"-headerpad_max_install_names");
|
||||
} else if (ctx.comp.is_static) {
|
||||
try ctx.args.append(c"-lcrt0.o");
|
||||
} else {
|
||||
switch (platform.kind) {
|
||||
DarwinPlatform.Kind.MacOS => {
|
||||
if (platform.versionLessThan(10, 5)) {
|
||||
try ctx.args.append(c"-lcrt1.o");
|
||||
} else if (platform.versionLessThan(10, 6)) {
|
||||
try ctx.args.append(c"-lcrt1.10.5.o");
|
||||
} else if (platform.versionLessThan(10, 8)) {
|
||||
try ctx.args.append(c"-lcrt1.10.6.o");
|
||||
}
|
||||
},
|
||||
DarwinPlatform.Kind.IPhoneOS => {
|
||||
if (ctx.comp.target.getArch() == builtin.Arch.aarch64) {
|
||||
// iOS does not need any crt1 files for arm64
|
||||
} else if (platform.versionLessThan(3, 1)) {
|
||||
try ctx.args.append(c"-lcrt1.o");
|
||||
} else if (platform.versionLessThan(6, 0)) {
|
||||
try ctx.args.append(c"-lcrt1.3.1.o");
|
||||
}
|
||||
},
|
||||
DarwinPlatform.Kind.IPhoneOSSimulator => {}, // no crt1.o needed
|
||||
}
|
||||
}
|
||||
|
||||
//for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
|
||||
// const char *lib_dir = g->lib_dirs.at(i);
|
||||
// lj->args.append("-L");
|
||||
// lj->args.append(lib_dir);
|
||||
//}
|
||||
|
||||
//if (!g->is_native_target) {
|
||||
// lj->args.append("--allow-shlib-undefined");
|
||||
for (ctx.comp.link_objects) |link_object| {
|
||||
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
|
||||
try ctx.args.append(link_obj_with_null.ptr);
|
||||
}
|
||||
try addFnObjects(ctx);
|
||||
|
||||
//// compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce
|
||||
//if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
|
||||
// Buf *compiler_rt_o_path = build_compiler_rt(g);
|
||||
// lj->args.append(buf_ptr(compiler_rt_o_path));
|
||||
//}
|
||||
|
||||
//if (g->zig_target.os == OsZen) {
|
||||
// lj->args.append("-e");
|
||||
// lj->args.append("_start");
|
||||
if (ctx.comp.target == Target.Native) {
|
||||
for (ctx.comp.link_libs_list.toSliceConst()) |lib| {
|
||||
if (mem.eql(u8, lib.name, "c")) {
|
||||
// on Darwin, libSystem has libc in it, but also you have to use it
|
||||
// to make syscalls because the syscall numbers are not documented
|
||||
// and change between versions.
|
||||
// so we always link against libSystem
|
||||
try ctx.args.append(c"-lSystem");
|
||||
} else {
|
||||
if (mem.indexOfScalar(u8, lib.name, '/') == null) {
|
||||
const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", lib.name);
|
||||
try ctx.args.append(arg.ptr);
|
||||
} else {
|
||||
const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name);
|
||||
try ctx.args.append(arg.ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try ctx.args.append(c"-undefined");
|
||||
try ctx.args.append(c"dynamic_lookup");
|
||||
}
|
||||
|
||||
// lj->args.append("--image-base=0x10000000");
|
||||
if (platform.kind == DarwinPlatform.Kind.MacOS) {
|
||||
if (platform.versionLessThan(10, 5)) {
|
||||
try ctx.args.append(c"-lgcc_s.10.4");
|
||||
} else if (platform.versionLessThan(10, 6)) {
|
||||
try ctx.args.append(c"-lgcc_s.10.5");
|
||||
}
|
||||
} else {
|
||||
@panic("TODO");
|
||||
}
|
||||
|
||||
//for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) {
|
||||
// lj->args.append("-framework");
|
||||
// lj->args.append(buf_ptr(g->darwin_frameworks.at(i)));
|
||||
//}
|
||||
}
|
||||
|
||||
fn constructLinkerArgsCoff(ctx: *Context) void {
|
||||
@panic("TODO");
|
||||
}
|
||||
|
||||
fn constructLinkerArgsMachO(ctx: *Context) void {
|
||||
@panic("TODO");
|
||||
}
|
||||
|
||||
fn constructLinkerArgsWasm(ctx: *Context) void {
|
||||
@panic("TODO");
|
||||
}
|
||||
|
@ -312,3 +640,85 @@ fn addFnObjects(ctx: *Context) !void {
|
|||
it = node.next;
|
||||
}
|
||||
}
|
||||
|
||||
const DarwinPlatform = struct {
|
||||
kind: Kind,
|
||||
major: u32,
|
||||
minor: u32,
|
||||
micro: u32,
|
||||
|
||||
const Kind = enum {
|
||||
MacOS,
|
||||
IPhoneOS,
|
||||
IPhoneOSSimulator,
|
||||
};
|
||||
|
||||
fn get(comp: *Compilation) !DarwinPlatform {
|
||||
var result: DarwinPlatform = undefined;
|
||||
const ver_str = switch (comp.darwin_version_min) {
|
||||
Compilation.DarwinVersionMin.MacOS => |ver| blk: {
|
||||
result.kind = Kind.MacOS;
|
||||
break :blk ver;
|
||||
},
|
||||
Compilation.DarwinVersionMin.Ios => |ver| blk: {
|
||||
result.kind = Kind.IPhoneOS;
|
||||
break :blk ver;
|
||||
},
|
||||
Compilation.DarwinVersionMin.None => blk: {
|
||||
assert(comp.target.getOs() == builtin.Os.macosx);
|
||||
result.kind = Kind.MacOS;
|
||||
break :blk "10.10";
|
||||
},
|
||||
};
|
||||
|
||||
var had_extra: bool = undefined;
|
||||
try darwinGetReleaseVersion(ver_str, &result.major, &result.minor, &result.micro, &had_extra,);
|
||||
if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) {
|
||||
return error.InvalidDarwinVersionString;
|
||||
}
|
||||
|
||||
if (result.kind == Kind.IPhoneOS) {
|
||||
switch (comp.target.getArch()) {
|
||||
builtin.Arch.i386,
|
||||
builtin.Arch.x86_64,
|
||||
=> result.kind = Kind.IPhoneOSSimulator,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn versionLessThan(self: DarwinPlatform, major: u32, minor: u32) bool {
|
||||
if (self.major < major)
|
||||
return true;
|
||||
if (self.major > major)
|
||||
return false;
|
||||
if (self.minor < minor)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the
|
||||
/// grouped values as integers. Numbers which are not provided are set to 0.
|
||||
/// return true if the entire string was parsed (9.2), or all groups were
|
||||
/// parsed (10.3.5extrastuff).
|
||||
fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u32, had_extra: *bool) !void {
|
||||
major.* = 0;
|
||||
minor.* = 0;
|
||||
micro.* = 0;
|
||||
had_extra.* = false;
|
||||
|
||||
if (str.len == 0)
|
||||
return error.InvalidDarwinVersionString;
|
||||
|
||||
var start_pos: usize = 0;
|
||||
for ([]*u32{major, minor, micro}) |v| {
|
||||
const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.');
|
||||
const end_pos = dot_pos orelse str.len;
|
||||
v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString;
|
||||
start_pos = (dot_pos orelse return) + 1;
|
||||
if (start_pos == str.len) return;
|
||||
}
|
||||
had_extra.* = true;
|
||||
}
|
||||
|
|
|
@ -23,13 +23,20 @@ pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef);
|
|||
pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef);
|
||||
pub const DIBuilder = c.ZigLLVMDIBuilder;
|
||||
|
||||
pub const ABIAlignmentOfType = c.LLVMABIAlignmentOfType;
|
||||
pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex;
|
||||
pub const AddFunction = c.LLVMAddFunction;
|
||||
pub const AddGlobal = c.LLVMAddGlobal;
|
||||
pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;
|
||||
pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;
|
||||
pub const ArrayType = c.LLVMArrayType;
|
||||
pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;
|
||||
pub const ConstAllOnes = c.LLVMConstAllOnes;
|
||||
pub const ConstArray = c.LLVMConstArray;
|
||||
pub const ConstBitCast = c.LLVMConstBitCast;
|
||||
pub const ConstInt = c.LLVMConstInt;
|
||||
pub const ConstIntOfArbitraryPrecision = c.LLVMConstIntOfArbitraryPrecision;
|
||||
pub const ConstNeg = c.LLVMConstNeg;
|
||||
pub const ConstNull = c.LLVMConstNull;
|
||||
pub const ConstStringInContext = c.LLVMConstStringInContext;
|
||||
pub const ConstStructInContext = c.LLVMConstStructInContext;
|
||||
|
@ -57,6 +64,7 @@ pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName;
|
|||
pub const GetHostCPUName = c.ZigLLVMGetHostCPUName;
|
||||
pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext;
|
||||
pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures;
|
||||
pub const GetUndef = c.LLVMGetUndef;
|
||||
pub const HalfTypeInContext = c.LLVMHalfTypeInContext;
|
||||
pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers;
|
||||
pub const InitializeAllAsmPrinters = c.LLVMInitializeAllAsmPrinters;
|
||||
|
@ -79,14 +87,24 @@ pub const MDStringInContext = c.LLVMMDStringInContext;
|
|||
pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext;
|
||||
pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext;
|
||||
pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext;
|
||||
pub const PointerType = c.LLVMPointerType;
|
||||
pub const SetAlignment = c.LLVMSetAlignment;
|
||||
pub const SetDataLayout = c.LLVMSetDataLayout;
|
||||
pub const SetGlobalConstant = c.LLVMSetGlobalConstant;
|
||||
pub const SetInitializer = c.LLVMSetInitializer;
|
||||
pub const SetLinkage = c.LLVMSetLinkage;
|
||||
pub const SetTarget = c.LLVMSetTarget;
|
||||
pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr;
|
||||
pub const StructTypeInContext = c.LLVMStructTypeInContext;
|
||||
pub const TokenTypeInContext = c.LLVMTokenTypeInContext;
|
||||
pub const TypeOf = c.LLVMTypeOf;
|
||||
pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
|
||||
pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;
|
||||
pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;
|
||||
|
||||
pub const ConstInBoundsGEP = LLVMConstInBoundsGEP;
|
||||
pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef;
|
||||
|
||||
pub const GetTargetFromTriple = LLVMGetTargetFromTriple;
|
||||
extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool;
|
||||
|
||||
|
@ -143,13 +161,28 @@ pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary;
|
|||
pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr;
|
||||
pub const EmitOutputType = c.ZigLLVM_EmitOutputType;
|
||||
|
||||
pub const CCallConv = c.LLVMCCallConv;
|
||||
pub const FastCallConv = c.LLVMFastCallConv;
|
||||
pub const ColdCallConv = c.LLVMColdCallConv;
|
||||
pub const WebKitJSCallConv = c.LLVMWebKitJSCallConv;
|
||||
pub const AnyRegCallConv = c.LLVMAnyRegCallConv;
|
||||
pub const X86StdcallCallConv = c.LLVMX86StdcallCallConv;
|
||||
pub const X86FastcallCallConv = c.LLVMX86FastcallCallConv;
|
||||
pub const CallConv = c.LLVMCallConv;
|
||||
|
||||
pub const FnInline = extern enum {
|
||||
Auto,
|
||||
Always,
|
||||
Never,
|
||||
};
|
||||
|
||||
fn removeNullability(comptime T: type) type {
|
||||
comptime assert(@typeId(T) == builtin.TypeId.Optional);
|
||||
return T.Child;
|
||||
}
|
||||
|
||||
pub const BuildRet = LLVMBuildRet;
|
||||
extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ValueRef;
|
||||
extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ?ValueRef;
|
||||
|
||||
pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile;
|
||||
extern fn ZigLLVMTargetMachineEmitToFile(
|
||||
|
@ -161,3 +194,8 @@ extern fn ZigLLVMTargetMachineEmitToFile(
|
|||
is_debug: bool,
|
||||
is_small: bool,
|
||||
) bool;
|
||||
|
||||
pub const BuildCall = ZigLLVMBuildCall;
|
||||
extern fn ZigLLVMBuildCall(B: BuilderRef, Fn: ValueRef, Args: [*]ValueRef, NumArgs: c_uint, CC: c_uint, fn_inline: FnInline, Name: [*]const u8) ?ValueRef;
|
||||
|
||||
pub const PrivateLinkage = c.LLVMLinkage.LLVMPrivateLinkage;
|
||||
|
|
|
@ -18,6 +18,7 @@ const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
|
|||
const Compilation = @import("compilation.zig").Compilation;
|
||||
const Target = @import("target.zig").Target;
|
||||
const errmsg = @import("errmsg.zig");
|
||||
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
|
||||
|
||||
var stderr_file: os.File = undefined;
|
||||
var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;
|
||||
|
@ -28,13 +29,14 @@ const usage =
|
|||
\\
|
||||
\\Commands:
|
||||
\\
|
||||
\\ build-exe [source] Create executable from source or object files
|
||||
\\ build-lib [source] Create library from source or object files
|
||||
\\ build-obj [source] Create object from source or assembly
|
||||
\\ fmt [source] Parse file and render in canonical zig format
|
||||
\\ targets List available compilation targets
|
||||
\\ version Print version number and exit
|
||||
\\ zen Print zen of zig and exit
|
||||
\\ build-exe [source] Create executable from source or object files
|
||||
\\ build-lib [source] Create library from source or object files
|
||||
\\ build-obj [source] Create object from source or assembly
|
||||
\\ fmt [source] Parse file and render in canonical zig format
|
||||
\\ libc [paths_file] Display native libc paths file or validate one
|
||||
\\ targets List available compilation targets
|
||||
\\ version Print version number and exit
|
||||
\\ zen Print zen of zig and exit
|
||||
\\
|
||||
\\
|
||||
;
|
||||
|
@ -85,6 +87,10 @@ pub fn main() !void {
|
|||
.name = "fmt",
|
||||
.exec = cmdFmt,
|
||||
},
|
||||
Command{
|
||||
.name = "libc",
|
||||
.exec = cmdLibC,
|
||||
},
|
||||
Command{
|
||||
.name = "targets",
|
||||
.exec = cmdTargets,
|
||||
|
@ -130,11 +136,10 @@ const usage_build_generic =
|
|||
\\ --color [auto|off|on] Enable or disable colored error messages
|
||||
\\
|
||||
\\Compile Options:
|
||||
\\ --libc [file] Provide a file which specifies libc paths
|
||||
\\ --assembly [source] Add assembly file to build
|
||||
\\ --cache-dir [path] Override the cache directory
|
||||
\\ --emit [filetype] Emit a specific file format as compilation output
|
||||
\\ --enable-timing-info Print timing diagnostics
|
||||
\\ --libc-include-dir [path] Directory where libc stdlib.h resides
|
||||
\\ --name [name] Override output name
|
||||
\\ --output [file] Override destination path
|
||||
\\ --output-h [file] Override generated header file path
|
||||
|
@ -163,12 +168,7 @@ const usage_build_generic =
|
|||
\\
|
||||
\\Link Options:
|
||||
\\ --ar-path [path] Set the path to ar
|
||||
\\ --dynamic-linker [path] Set the path to ld.so
|
||||
\\ --each-lib-rpath Add rpath for each used dynamic library
|
||||
\\ --libc-lib-dir [path] Directory where libc crt1.o resides
|
||||
\\ --libc-static-lib-dir [path] Directory where libc crtbegin.o resides
|
||||
\\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides
|
||||
\\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides
|
||||
\\ --library [lib] Link against lib
|
||||
\\ --forbid-library [lib] Make it an error to link against lib
|
||||
\\ --library-path [dir] Add a directory to the library search path
|
||||
|
@ -203,14 +203,13 @@ const args_build_generic = []Flag{
|
|||
}),
|
||||
|
||||
Flag.ArgMergeN("--assembly", 1),
|
||||
Flag.Arg1("--cache-dir"),
|
||||
Flag.Option("--emit", []const []const u8{
|
||||
"asm",
|
||||
"bin",
|
||||
"llvm-ir",
|
||||
}),
|
||||
Flag.Bool("--enable-timing-info"),
|
||||
Flag.Arg1("--libc-include-dir"),
|
||||
Flag.Arg1("--libc"),
|
||||
Flag.Arg1("--name"),
|
||||
Flag.Arg1("--output"),
|
||||
Flag.Arg1("--output-h"),
|
||||
|
@ -234,12 +233,7 @@ const args_build_generic = []Flag{
|
|||
Flag.Arg1("-mllvm"),
|
||||
|
||||
Flag.Arg1("--ar-path"),
|
||||
Flag.Arg1("--dynamic-linker"),
|
||||
Flag.Bool("--each-lib-rpath"),
|
||||
Flag.Arg1("--libc-lib-dir"),
|
||||
Flag.Arg1("--libc-static-lib-dir"),
|
||||
Flag.Arg1("--msvc-lib-dir"),
|
||||
Flag.Arg1("--kernel32-lib-dir"),
|
||||
Flag.ArgMergeN("--library", 1),
|
||||
Flag.ArgMergeN("--forbid-library", 1),
|
||||
Flag.ArgMergeN("--library-path", 1),
|
||||
|
@ -377,16 +371,11 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
|||
os.exit(1);
|
||||
}
|
||||
|
||||
const rel_cache_dir = flags.single("cache-dir") orelse "zig-cache"[0..];
|
||||
const full_cache_dir = os.path.resolve(allocator, ".", rel_cache_dir) catch {
|
||||
try stderr.print("invalid cache dir: {}\n", rel_cache_dir);
|
||||
os.exit(1);
|
||||
};
|
||||
defer allocator.free(full_cache_dir);
|
||||
|
||||
const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
|
||||
defer allocator.free(zig_lib_dir);
|
||||
|
||||
var override_libc: LibCInstallation = undefined;
|
||||
|
||||
var loop: event.Loop = undefined;
|
||||
try loop.initMultiThreaded(allocator);
|
||||
defer loop.deinit();
|
||||
|
@ -403,10 +392,18 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
|||
build_mode,
|
||||
is_static,
|
||||
zig_lib_dir,
|
||||
full_cache_dir,
|
||||
);
|
||||
defer comp.destroy();
|
||||
|
||||
if (flags.single("libc")) |libc_path| {
|
||||
parseLibcPaths(loop.allocator, &override_libc, libc_path);
|
||||
comp.override_libc = &override_libc;
|
||||
}
|
||||
|
||||
for (flags.many("library")) |lib| {
|
||||
_ = try comp.addLinkLib(lib, true);
|
||||
}
|
||||
|
||||
comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10);
|
||||
comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10);
|
||||
comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10);
|
||||
|
@ -430,25 +427,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
|||
|
||||
comp.strip = flags.present("strip");
|
||||
|
||||
if (flags.single("libc-lib-dir")) |libc_lib_dir| {
|
||||
comp.libc_lib_dir = libc_lib_dir;
|
||||
}
|
||||
if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| {
|
||||
comp.libc_static_lib_dir = libc_static_lib_dir;
|
||||
}
|
||||
if (flags.single("libc-include-dir")) |libc_include_dir| {
|
||||
comp.libc_include_dir = libc_include_dir;
|
||||
}
|
||||
if (flags.single("msvc-lib-dir")) |msvc_lib_dir| {
|
||||
comp.msvc_lib_dir = msvc_lib_dir;
|
||||
}
|
||||
if (flags.single("kernel32-lib-dir")) |kernel32_lib_dir| {
|
||||
comp.kernel32_lib_dir = kernel32_lib_dir;
|
||||
}
|
||||
if (flags.single("dynamic-linker")) |dynamic_linker| {
|
||||
comp.dynamic_linker = dynamic_linker;
|
||||
}
|
||||
|
||||
comp.verbose_tokenize = flags.present("verbose-tokenize");
|
||||
comp.verbose_ast_tree = flags.present("verbose-ast-tree");
|
||||
comp.verbose_ast_fmt = flags.present("verbose-ast-fmt");
|
||||
|
@ -484,7 +462,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
|||
|
||||
comp.emit_file_type = emit_type;
|
||||
comp.assembly_files = assembly_files;
|
||||
comp.link_out_file = flags.single("out-file");
|
||||
comp.link_out_file = flags.single("output");
|
||||
comp.link_objects = link_objects;
|
||||
|
||||
try comp.build();
|
||||
|
@ -499,7 +477,6 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
|
|||
|
||||
switch (build_event) {
|
||||
Compilation.Event.Ok => {
|
||||
std.debug.warn("Build succeeded\n");
|
||||
return;
|
||||
},
|
||||
Compilation.Event.Error => |err| {
|
||||
|
@ -508,7 +485,8 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
|
|||
},
|
||||
Compilation.Event.Fail => |msgs| {
|
||||
for (msgs) |msg| {
|
||||
errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1);
|
||||
defer msg.destroy();
|
||||
msg.printToFile(&stderr_file, color) catch os.exit(1);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -579,6 +557,53 @@ const Fmt = struct {
|
|||
}
|
||||
};
|
||||
|
||||
fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
|
||||
libc.parse(allocator, libc_paths_file, stderr) catch |err| {
|
||||
stderr.print(
|
||||
"Unable to parse libc path file '{}': {}.\n" ++
|
||||
"Try running `zig libc` to see an example for the native target.\n",
|
||||
libc_paths_file,
|
||||
@errorName(err),
|
||||
) catch os.exit(1);
|
||||
os.exit(1);
|
||||
};
|
||||
}
|
||||
|
||||
fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
|
||||
switch (args.len) {
|
||||
0 => {},
|
||||
1 => {
|
||||
var libc_installation: LibCInstallation = undefined;
|
||||
parseLibcPaths(allocator, &libc_installation, args[0]);
|
||||
return;
|
||||
},
|
||||
else => {
|
||||
try stderr.print("unexpected extra parameter: {}\n", args[1]);
|
||||
os.exit(1);
|
||||
},
|
||||
}
|
||||
|
||||
var loop: event.Loop = undefined;
|
||||
try loop.initMultiThreaded(allocator);
|
||||
defer loop.deinit();
|
||||
|
||||
var event_loop_local = try EventLoopLocal.init(&loop);
|
||||
defer event_loop_local.deinit();
|
||||
|
||||
const handle = try async<loop.allocator> findLibCAsync(&event_loop_local);
|
||||
defer cancel handle;
|
||||
|
||||
loop.run();
|
||||
}
|
||||
|
||||
async fn findLibCAsync(event_loop_local: *EventLoopLocal) void {
|
||||
const libc = (await (async event_loop_local.getNativeLibC() catch unreachable)) catch |err| {
|
||||
stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1);
|
||||
os.exit(1);
|
||||
};
|
||||
libc.render(stdout) catch os.exit(1);
|
||||
}
|
||||
|
||||
fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
|
||||
var flags = try Args.parse(allocator, args_fmt_spec, args);
|
||||
defer flags.deinit();
|
||||
|
@ -622,10 +647,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
|
|||
|
||||
var error_it = tree.errors.iterator(0);
|
||||
while (error_it.next()) |parse_error| {
|
||||
const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
|
||||
defer allocator.destroy(msg);
|
||||
const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
|
||||
defer msg.destroy();
|
||||
|
||||
try errmsg.printToFile(&stderr_file, msg, color);
|
||||
try msg.printToFile(&stderr_file, color);
|
||||
}
|
||||
if (tree.errors.len != 0) {
|
||||
os.exit(1);
|
||||
|
@ -678,10 +703,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
|
|||
|
||||
var error_it = tree.errors.iterator(0);
|
||||
while (error_it.next()) |parse_error| {
|
||||
const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, file_path);
|
||||
defer allocator.destroy(msg);
|
||||
const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, file_path);
|
||||
defer msg.destroy();
|
||||
|
||||
try errmsg.printToFile(&stderr_file, msg, color);
|
||||
try msg.printToFile(&stderr_file, color);
|
||||
}
|
||||
if (tree.errors.len != 0) {
|
||||
fmt.any_error = true;
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
const ast = @import("std").zig.ast;
|
||||
|
||||
pub const ParsedFile = struct {
|
||||
tree: ast.Tree,
|
||||
realpath: []const u8,
|
||||
};
|
|
@ -8,6 +8,8 @@ const ast = std.zig.ast;
|
|||
const Value = @import("value.zig").Value;
|
||||
const ir = @import("ir.zig");
|
||||
const Span = @import("errmsg.zig").Span;
|
||||
const assert = std.debug.assert;
|
||||
const event = std.event;
|
||||
|
||||
pub const Scope = struct {
|
||||
id: Id,
|
||||
|
@ -23,7 +25,8 @@ pub const Scope = struct {
|
|||
if (base.ref_count == 0) {
|
||||
if (base.parent) |parent| parent.deref(comp);
|
||||
switch (base.id) {
|
||||
Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(),
|
||||
Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp),
|
||||
Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(comp),
|
||||
Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp),
|
||||
Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp),
|
||||
Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp),
|
||||
|
@ -33,6 +36,15 @@ pub const Scope = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn findRoot(base: *Scope) *Root {
|
||||
var scope = base;
|
||||
while (scope.parent) |parent| {
|
||||
scope = parent;
|
||||
}
|
||||
assert(scope.id == Id.Root);
|
||||
return @fieldParentPtr(Root, "base", scope);
|
||||
}
|
||||
|
||||
pub fn findFnDef(base: *Scope) ?*FnDef {
|
||||
var scope = base;
|
||||
while (true) {
|
||||
|
@ -44,12 +56,33 @@ pub const Scope = struct {
|
|||
Id.Defer,
|
||||
Id.DeferExpr,
|
||||
Id.CompTime,
|
||||
Id.Root,
|
||||
=> scope = scope.parent orelse return null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn findDeferExpr(base: *Scope) ?*DeferExpr {
|
||||
var scope = base;
|
||||
while (true) {
|
||||
switch (scope.id) {
|
||||
Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", base),
|
||||
|
||||
Id.FnDef,
|
||||
Id.Decls,
|
||||
=> return null,
|
||||
|
||||
Id.Block,
|
||||
Id.Defer,
|
||||
Id.CompTime,
|
||||
Id.Root,
|
||||
=> scope = scope.parent orelse return null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const Id = enum {
|
||||
Root,
|
||||
Decls,
|
||||
Block,
|
||||
FnDef,
|
||||
|
@ -58,42 +91,82 @@ pub const Scope = struct {
|
|||
DeferExpr,
|
||||
};
|
||||
|
||||
pub const Root = struct {
|
||||
base: Scope,
|
||||
tree: *ast.Tree,
|
||||
realpath: []const u8,
|
||||
|
||||
/// Creates a Root scope with 1 reference
|
||||
/// Takes ownership of realpath
|
||||
/// Takes ownership of tree, will deinit and destroy when done.
|
||||
pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root {
|
||||
const self = try comp.gpa().create(Root{
|
||||
.base = Scope{
|
||||
.id = Id.Root,
|
||||
.parent = null,
|
||||
.ref_count = 1,
|
||||
},
|
||||
.tree = tree,
|
||||
.realpath = realpath,
|
||||
});
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Root, comp: *Compilation) void {
|
||||
comp.gpa().free(self.tree.source);
|
||||
self.tree.deinit();
|
||||
comp.gpa().destroy(self.tree);
|
||||
comp.gpa().free(self.realpath);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Decls = struct {
|
||||
base: Scope,
|
||||
table: Decl.Table,
|
||||
|
||||
/// The lock must be respected for writing. However once name_future resolves,
|
||||
/// readers can freely access it.
|
||||
table: event.Locked(Decl.Table),
|
||||
|
||||
/// Once this future is resolved, the table is complete and available for unlocked
|
||||
/// read-only access. It does not mean all the decls are resolved; it means only that
|
||||
/// the table has all the names. Each decl in the table has its own resolution state.
|
||||
name_future: event.Future(void),
|
||||
|
||||
/// Creates a Decls scope with 1 reference
|
||||
pub fn create(comp: *Compilation, parent: ?*Scope) !*Decls {
|
||||
pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
|
||||
const self = try comp.gpa().create(Decls{
|
||||
.base = Scope{
|
||||
.id = Id.Decls,
|
||||
.parent = parent,
|
||||
.ref_count = 1,
|
||||
},
|
||||
.table = undefined,
|
||||
.table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
|
||||
.name_future = event.Future(void).init(comp.loop),
|
||||
});
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
self.table = Decl.Table.init(comp.gpa());
|
||||
errdefer self.table.deinit();
|
||||
|
||||
if (parent) |p| p.ref();
|
||||
|
||||
parent.ref();
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Decls) void {
|
||||
pub fn destroy(self: *Decls, comp: *Compilation) void {
|
||||
self.table.deinit();
|
||||
self.table.allocator.destroy(self);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub async fn getTableReadOnly(self: *Decls) *Decl.Table {
|
||||
_ = await (async self.name_future.get() catch unreachable);
|
||||
return &self.table.private_data;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Block = struct {
|
||||
base: Scope,
|
||||
incoming_values: std.ArrayList(*ir.Instruction),
|
||||
incoming_values: std.ArrayList(*ir.Inst),
|
||||
incoming_blocks: std.ArrayList(*ir.BasicBlock),
|
||||
end_block: *ir.BasicBlock,
|
||||
is_comptime: *ir.Instruction,
|
||||
is_comptime: *ir.Inst,
|
||||
|
||||
safety: Safety,
|
||||
|
||||
|
@ -125,7 +198,7 @@ pub const Scope = struct {
|
|||
};
|
||||
|
||||
/// Creates a Block scope with 1 reference
|
||||
pub fn create(comp: *Compilation, parent: ?*Scope) !*Block {
|
||||
pub fn create(comp: *Compilation, parent: *Scope) !*Block {
|
||||
const self = try comp.gpa().create(Block{
|
||||
.base = Scope{
|
||||
.id = Id.Block,
|
||||
|
@ -140,7 +213,7 @@ pub const Scope = struct {
|
|||
});
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
if (parent) |p| p.ref();
|
||||
parent.ref();
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -157,7 +230,7 @@ pub const Scope = struct {
|
|||
|
||||
/// Creates a FnDef scope with 1 reference
|
||||
/// Must set the fn_val later
|
||||
pub fn create(comp: *Compilation, parent: ?*Scope) !*FnDef {
|
||||
pub fn create(comp: *Compilation, parent: *Scope) !*FnDef {
|
||||
const self = try comp.gpa().create(FnDef{
|
||||
.base = Scope{
|
||||
.id = Id.FnDef,
|
||||
|
@ -167,7 +240,7 @@ pub const Scope = struct {
|
|||
.fn_val = undefined,
|
||||
});
|
||||
|
||||
if (parent) |p| p.ref();
|
||||
parent.ref();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
@ -181,7 +254,7 @@ pub const Scope = struct {
|
|||
base: Scope,
|
||||
|
||||
/// Creates a CompTime scope with 1 reference
|
||||
pub fn create(comp: *Compilation, parent: ?*Scope) !*CompTime {
|
||||
pub fn create(comp: *Compilation, parent: *Scope) !*CompTime {
|
||||
const self = try comp.gpa().create(CompTime{
|
||||
.base = Scope{
|
||||
.id = Id.CompTime,
|
||||
|
@ -190,7 +263,7 @@ pub const Scope = struct {
|
|||
},
|
||||
});
|
||||
|
||||
if (parent) |p| p.ref();
|
||||
parent.ref();
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -212,7 +285,7 @@ pub const Scope = struct {
|
|||
/// Creates a Defer scope with 1 reference
|
||||
pub fn create(
|
||||
comp: *Compilation,
|
||||
parent: ?*Scope,
|
||||
parent: *Scope,
|
||||
kind: Kind,
|
||||
defer_expr_scope: *DeferExpr,
|
||||
) !*Defer {
|
||||
|
@ -229,7 +302,7 @@ pub const Scope = struct {
|
|||
|
||||
defer_expr_scope.base.ref();
|
||||
|
||||
if (parent) |p| p.ref();
|
||||
parent.ref();
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -242,9 +315,10 @@ pub const Scope = struct {
|
|||
pub const DeferExpr = struct {
|
||||
base: Scope,
|
||||
expr_node: *ast.Node,
|
||||
reported_err: bool,
|
||||
|
||||
/// Creates a DeferExpr scope with 1 reference
|
||||
pub fn create(comp: *Compilation, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr {
|
||||
pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr {
|
||||
const self = try comp.gpa().create(DeferExpr{
|
||||
.base = Scope{
|
||||
.id = Id.DeferExpr,
|
||||
|
@ -252,10 +326,11 @@ pub const Scope = struct {
|
|||
.ref_count = 1,
|
||||
},
|
||||
.expr_node = expr_node,
|
||||
.reported_err = false,
|
||||
});
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
if (parent) |p| p.ref();
|
||||
parent.ref();
|
||||
return self;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const llvm = @import("llvm.zig");
|
||||
const CInt = @import("c_int.zig").CInt;
|
||||
|
||||
pub const FloatAbi = enum {
|
||||
Hard,
|
||||
Soft,
|
||||
SoftFp,
|
||||
};
|
||||
|
||||
pub const Target = union(enum) {
|
||||
Native,
|
||||
|
@ -13,7 +20,7 @@ pub const Target = union(enum) {
|
|||
object_format: builtin.ObjectFormat,
|
||||
};
|
||||
|
||||
pub fn oFileExt(self: Target) []const u8 {
|
||||
pub fn objFileExt(self: Target) []const u8 {
|
||||
return switch (self.getObjectFormat()) {
|
||||
builtin.ObjectFormat.coff => ".obj",
|
||||
else => ".o",
|
||||
|
@ -27,6 +34,13 @@ pub const Target = union(enum) {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn libFileExt(self: Target, is_static: bool) []const u8 {
|
||||
return switch (self.getOs()) {
|
||||
builtin.Os.windows => if (is_static) ".lib" else ".dll",
|
||||
else => if (is_static) ".a" else ".so",
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getOs(self: Target) builtin.Os {
|
||||
return switch (self) {
|
||||
Target.Native => builtin.os,
|
||||
|
@ -76,6 +90,56 @@ pub const Target = union(enum) {
|
|||
};
|
||||
}
|
||||
|
||||
/// TODO expose the arch and subarch separately
|
||||
pub fn isArmOrThumb(self: Target) bool {
|
||||
return switch (self.getArch()) {
|
||||
builtin.Arch.armv8_3a,
|
||||
builtin.Arch.armv8_2a,
|
||||
builtin.Arch.armv8_1a,
|
||||
builtin.Arch.armv8,
|
||||
builtin.Arch.armv8r,
|
||||
builtin.Arch.armv8m_baseline,
|
||||
builtin.Arch.armv8m_mainline,
|
||||
builtin.Arch.armv7,
|
||||
builtin.Arch.armv7em,
|
||||
builtin.Arch.armv7m,
|
||||
builtin.Arch.armv7s,
|
||||
builtin.Arch.armv7k,
|
||||
builtin.Arch.armv7ve,
|
||||
builtin.Arch.armv6,
|
||||
builtin.Arch.armv6m,
|
||||
builtin.Arch.armv6k,
|
||||
builtin.Arch.armv6t2,
|
||||
builtin.Arch.armv5,
|
||||
builtin.Arch.armv5te,
|
||||
builtin.Arch.armv4t,
|
||||
builtin.Arch.armebv8_3a,
|
||||
builtin.Arch.armebv8_2a,
|
||||
builtin.Arch.armebv8_1a,
|
||||
builtin.Arch.armebv8,
|
||||
builtin.Arch.armebv8r,
|
||||
builtin.Arch.armebv8m_baseline,
|
||||
builtin.Arch.armebv8m_mainline,
|
||||
builtin.Arch.armebv7,
|
||||
builtin.Arch.armebv7em,
|
||||
builtin.Arch.armebv7m,
|
||||
builtin.Arch.armebv7s,
|
||||
builtin.Arch.armebv7k,
|
||||
builtin.Arch.armebv7ve,
|
||||
builtin.Arch.armebv6,
|
||||
builtin.Arch.armebv6m,
|
||||
builtin.Arch.armebv6k,
|
||||
builtin.Arch.armebv6t2,
|
||||
builtin.Arch.armebv5,
|
||||
builtin.Arch.armebv5te,
|
||||
builtin.Arch.armebv4t,
|
||||
builtin.Arch.thumb,
|
||||
builtin.Arch.thumbeb,
|
||||
=> true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn initializeAll() void {
|
||||
llvm.InitializeAllTargets();
|
||||
llvm.InitializeAllTargetInfos();
|
||||
|
@ -106,6 +170,257 @@ pub const Target = union(enum) {
|
|||
return result;
|
||||
}
|
||||
|
||||
pub fn is64bit(self: Target) bool {
|
||||
return self.getArchPtrBitWidth() == 64;
|
||||
}
|
||||
|
||||
pub fn getArchPtrBitWidth(self: Target) u32 {
|
||||
switch (self.getArch()) {
|
||||
builtin.Arch.avr,
|
||||
builtin.Arch.msp430,
|
||||
=> return 16,
|
||||
|
||||
builtin.Arch.arc,
|
||||
builtin.Arch.armv8_3a,
|
||||
builtin.Arch.armv8_2a,
|
||||
builtin.Arch.armv8_1a,
|
||||
builtin.Arch.armv8,
|
||||
builtin.Arch.armv8r,
|
||||
builtin.Arch.armv8m_baseline,
|
||||
builtin.Arch.armv8m_mainline,
|
||||
builtin.Arch.armv7,
|
||||
builtin.Arch.armv7em,
|
||||
builtin.Arch.armv7m,
|
||||
builtin.Arch.armv7s,
|
||||
builtin.Arch.armv7k,
|
||||
builtin.Arch.armv7ve,
|
||||
builtin.Arch.armv6,
|
||||
builtin.Arch.armv6m,
|
||||
builtin.Arch.armv6k,
|
||||
builtin.Arch.armv6t2,
|
||||
builtin.Arch.armv5,
|
||||
builtin.Arch.armv5te,
|
||||
builtin.Arch.armv4t,
|
||||
builtin.Arch.armebv8_3a,
|
||||
builtin.Arch.armebv8_2a,
|
||||
builtin.Arch.armebv8_1a,
|
||||
builtin.Arch.armebv8,
|
||||
builtin.Arch.armebv8r,
|
||||
builtin.Arch.armebv8m_baseline,
|
||||
builtin.Arch.armebv8m_mainline,
|
||||
builtin.Arch.armebv7,
|
||||
builtin.Arch.armebv7em,
|
||||
builtin.Arch.armebv7m,
|
||||
builtin.Arch.armebv7s,
|
||||
builtin.Arch.armebv7k,
|
||||
builtin.Arch.armebv7ve,
|
||||
builtin.Arch.armebv6,
|
||||
builtin.Arch.armebv6m,
|
||||
builtin.Arch.armebv6k,
|
||||
builtin.Arch.armebv6t2,
|
||||
builtin.Arch.armebv5,
|
||||
builtin.Arch.armebv5te,
|
||||
builtin.Arch.armebv4t,
|
||||
builtin.Arch.hexagon,
|
||||
builtin.Arch.le32,
|
||||
builtin.Arch.mips,
|
||||
builtin.Arch.mipsel,
|
||||
builtin.Arch.nios2,
|
||||
builtin.Arch.powerpc,
|
||||
builtin.Arch.r600,
|
||||
builtin.Arch.riscv32,
|
||||
builtin.Arch.sparc,
|
||||
builtin.Arch.sparcel,
|
||||
builtin.Arch.tce,
|
||||
builtin.Arch.tcele,
|
||||
builtin.Arch.thumb,
|
||||
builtin.Arch.thumbeb,
|
||||
builtin.Arch.i386,
|
||||
builtin.Arch.xcore,
|
||||
builtin.Arch.nvptx,
|
||||
builtin.Arch.amdil,
|
||||
builtin.Arch.hsail,
|
||||
builtin.Arch.spir,
|
||||
builtin.Arch.kalimbav3,
|
||||
builtin.Arch.kalimbav4,
|
||||
builtin.Arch.kalimbav5,
|
||||
builtin.Arch.shave,
|
||||
builtin.Arch.lanai,
|
||||
builtin.Arch.wasm32,
|
||||
builtin.Arch.renderscript32,
|
||||
=> return 32,
|
||||
|
||||
builtin.Arch.aarch64,
|
||||
builtin.Arch.aarch64_be,
|
||||
builtin.Arch.mips64,
|
||||
builtin.Arch.mips64el,
|
||||
builtin.Arch.powerpc64,
|
||||
builtin.Arch.powerpc64le,
|
||||
builtin.Arch.riscv64,
|
||||
builtin.Arch.x86_64,
|
||||
builtin.Arch.nvptx64,
|
||||
builtin.Arch.le64,
|
||||
builtin.Arch.amdil64,
|
||||
builtin.Arch.hsail64,
|
||||
builtin.Arch.spir64,
|
||||
builtin.Arch.wasm64,
|
||||
builtin.Arch.renderscript64,
|
||||
builtin.Arch.amdgcn,
|
||||
builtin.Arch.bpfel,
|
||||
builtin.Arch.bpfeb,
|
||||
builtin.Arch.sparcv9,
|
||||
builtin.Arch.s390x,
|
||||
=> return 64,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getFloatAbi(self: Target) FloatAbi {
|
||||
return switch (self.getEnviron()) {
|
||||
builtin.Environ.gnueabihf,
|
||||
builtin.Environ.eabihf,
|
||||
builtin.Environ.musleabihf,
|
||||
=> FloatAbi.Hard,
|
||||
else => FloatAbi.Soft,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getDynamicLinkerPath(self: Target) ?[]const u8 {
|
||||
const env = self.getEnviron();
|
||||
const arch = self.getArch();
|
||||
switch (env) {
|
||||
builtin.Environ.android => {
|
||||
if (self.is64bit()) {
|
||||
return "/system/bin/linker64";
|
||||
} else {
|
||||
return "/system/bin/linker";
|
||||
}
|
||||
},
|
||||
builtin.Environ.gnux32 => {
|
||||
if (arch == builtin.Arch.x86_64) {
|
||||
return "/libx32/ld-linux-x32.so.2";
|
||||
}
|
||||
},
|
||||
builtin.Environ.musl,
|
||||
builtin.Environ.musleabi,
|
||||
builtin.Environ.musleabihf,
|
||||
=> {
|
||||
if (arch == builtin.Arch.x86_64) {
|
||||
return "/lib/ld-musl-x86_64.so.1";
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
switch (arch) {
|
||||
builtin.Arch.i386,
|
||||
builtin.Arch.sparc,
|
||||
builtin.Arch.sparcel,
|
||||
=> return "/lib/ld-linux.so.2",
|
||||
|
||||
builtin.Arch.aarch64 => return "/lib/ld-linux-aarch64.so.1",
|
||||
builtin.Arch.aarch64_be => return "/lib/ld-linux-aarch64_be.so.1",
|
||||
|
||||
builtin.Arch.armv8_3a,
|
||||
builtin.Arch.armv8_2a,
|
||||
builtin.Arch.armv8_1a,
|
||||
builtin.Arch.armv8,
|
||||
builtin.Arch.armv8r,
|
||||
builtin.Arch.armv8m_baseline,
|
||||
builtin.Arch.armv8m_mainline,
|
||||
builtin.Arch.armv7,
|
||||
builtin.Arch.armv7em,
|
||||
builtin.Arch.armv7m,
|
||||
builtin.Arch.armv7s,
|
||||
builtin.Arch.armv7k,
|
||||
builtin.Arch.armv7ve,
|
||||
builtin.Arch.armv6,
|
||||
builtin.Arch.armv6m,
|
||||
builtin.Arch.armv6k,
|
||||
builtin.Arch.armv6t2,
|
||||
builtin.Arch.armv5,
|
||||
builtin.Arch.armv5te,
|
||||
builtin.Arch.armv4t,
|
||||
builtin.Arch.thumb,
|
||||
=> return switch (self.getFloatAbi()) {
|
||||
FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3",
|
||||
else => return "/lib/ld-linux.so.3",
|
||||
},
|
||||
|
||||
builtin.Arch.armebv8_3a,
|
||||
builtin.Arch.armebv8_2a,
|
||||
builtin.Arch.armebv8_1a,
|
||||
builtin.Arch.armebv8,
|
||||
builtin.Arch.armebv8r,
|
||||
builtin.Arch.armebv8m_baseline,
|
||||
builtin.Arch.armebv8m_mainline,
|
||||
builtin.Arch.armebv7,
|
||||
builtin.Arch.armebv7em,
|
||||
builtin.Arch.armebv7m,
|
||||
builtin.Arch.armebv7s,
|
||||
builtin.Arch.armebv7k,
|
||||
builtin.Arch.armebv7ve,
|
||||
builtin.Arch.armebv6,
|
||||
builtin.Arch.armebv6m,
|
||||
builtin.Arch.armebv6k,
|
||||
builtin.Arch.armebv6t2,
|
||||
builtin.Arch.armebv5,
|
||||
builtin.Arch.armebv5te,
|
||||
builtin.Arch.armebv4t,
|
||||
builtin.Arch.thumbeb,
|
||||
=> return switch (self.getFloatAbi()) {
|
||||
FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3",
|
||||
else => return "/lib/ld-linux.so.3",
|
||||
},
|
||||
|
||||
builtin.Arch.mips,
|
||||
builtin.Arch.mipsel,
|
||||
builtin.Arch.mips64,
|
||||
builtin.Arch.mips64el,
|
||||
=> return null,
|
||||
|
||||
builtin.Arch.powerpc => return "/lib/ld.so.1",
|
||||
builtin.Arch.powerpc64 => return "/lib64/ld64.so.2",
|
||||
builtin.Arch.powerpc64le => return "/lib64/ld64.so.2",
|
||||
builtin.Arch.s390x => return "/lib64/ld64.so.1",
|
||||
builtin.Arch.sparcv9 => return "/lib64/ld-linux.so.2",
|
||||
builtin.Arch.x86_64 => return "/lib64/ld-linux-x86-64.so.2",
|
||||
|
||||
builtin.Arch.arc,
|
||||
builtin.Arch.avr,
|
||||
builtin.Arch.bpfel,
|
||||
builtin.Arch.bpfeb,
|
||||
builtin.Arch.hexagon,
|
||||
builtin.Arch.msp430,
|
||||
builtin.Arch.nios2,
|
||||
builtin.Arch.r600,
|
||||
builtin.Arch.amdgcn,
|
||||
builtin.Arch.riscv32,
|
||||
builtin.Arch.riscv64,
|
||||
builtin.Arch.tce,
|
||||
builtin.Arch.tcele,
|
||||
builtin.Arch.xcore,
|
||||
builtin.Arch.nvptx,
|
||||
builtin.Arch.nvptx64,
|
||||
builtin.Arch.le32,
|
||||
builtin.Arch.le64,
|
||||
builtin.Arch.amdil,
|
||||
builtin.Arch.amdil64,
|
||||
builtin.Arch.hsail,
|
||||
builtin.Arch.hsail64,
|
||||
builtin.Arch.spir,
|
||||
builtin.Arch.spir64,
|
||||
builtin.Arch.kalimbav3,
|
||||
builtin.Arch.kalimbav4,
|
||||
builtin.Arch.kalimbav5,
|
||||
builtin.Arch.shave,
|
||||
builtin.Arch.lanai,
|
||||
builtin.Arch.wasm32,
|
||||
builtin.Arch.wasm64,
|
||||
builtin.Arch.renderscript32,
|
||||
builtin.Arch.renderscript64,
|
||||
=> return null,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef {
|
||||
var result: llvm.TargetRef = undefined;
|
||||
var err_msg: [*]u8 = undefined;
|
||||
|
@ -115,4 +430,133 @@ pub const Target = union(enum) {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn cIntTypeSizeInBits(self: Target, id: CInt.Id) u32 {
|
||||
const arch = self.getArch();
|
||||
switch (self.getOs()) {
|
||||
builtin.Os.freestanding => switch (self.getArch()) {
|
||||
builtin.Arch.msp430 => switch (id) {
|
||||
CInt.Id.Short,
|
||||
CInt.Id.UShort,
|
||||
CInt.Id.Int,
|
||||
CInt.Id.UInt,
|
||||
=> return 16,
|
||||
CInt.Id.Long,
|
||||
CInt.Id.ULong,
|
||||
=> return 32,
|
||||
CInt.Id.LongLong,
|
||||
CInt.Id.ULongLong,
|
||||
=> return 64,
|
||||
},
|
||||
else => switch (id) {
|
||||
CInt.Id.Short,
|
||||
CInt.Id.UShort,
|
||||
=> return 16,
|
||||
CInt.Id.Int,
|
||||
CInt.Id.UInt,
|
||||
=> return 32,
|
||||
CInt.Id.Long,
|
||||
CInt.Id.ULong,
|
||||
=> return self.getArchPtrBitWidth(),
|
||||
CInt.Id.LongLong,
|
||||
CInt.Id.ULongLong,
|
||||
=> return 64,
|
||||
},
|
||||
},
|
||||
|
||||
builtin.Os.linux,
|
||||
builtin.Os.macosx,
|
||||
builtin.Os.openbsd,
|
||||
builtin.Os.zen,
|
||||
=> switch (id) {
|
||||
CInt.Id.Short,
|
||||
CInt.Id.UShort,
|
||||
=> return 16,
|
||||
CInt.Id.Int,
|
||||
CInt.Id.UInt,
|
||||
=> return 32,
|
||||
CInt.Id.Long,
|
||||
CInt.Id.ULong,
|
||||
=> return self.getArchPtrBitWidth(),
|
||||
CInt.Id.LongLong,
|
||||
CInt.Id.ULongLong,
|
||||
=> return 64,
|
||||
},
|
||||
|
||||
builtin.Os.windows => switch (id) {
|
||||
CInt.Id.Short,
|
||||
CInt.Id.UShort,
|
||||
=> return 16,
|
||||
CInt.Id.Int,
|
||||
CInt.Id.UInt,
|
||||
=> return 32,
|
||||
CInt.Id.Long,
|
||||
CInt.Id.ULong,
|
||||
CInt.Id.LongLong,
|
||||
CInt.Id.ULongLong,
|
||||
=> return 64,
|
||||
},
|
||||
|
||||
builtin.Os.ananas,
|
||||
builtin.Os.cloudabi,
|
||||
builtin.Os.dragonfly,
|
||||
builtin.Os.freebsd,
|
||||
builtin.Os.fuchsia,
|
||||
builtin.Os.ios,
|
||||
builtin.Os.kfreebsd,
|
||||
builtin.Os.lv2,
|
||||
builtin.Os.netbsd,
|
||||
builtin.Os.solaris,
|
||||
builtin.Os.haiku,
|
||||
builtin.Os.minix,
|
||||
builtin.Os.rtems,
|
||||
builtin.Os.nacl,
|
||||
builtin.Os.cnk,
|
||||
builtin.Os.aix,
|
||||
builtin.Os.cuda,
|
||||
builtin.Os.nvcl,
|
||||
builtin.Os.amdhsa,
|
||||
builtin.Os.ps4,
|
||||
builtin.Os.elfiamcu,
|
||||
builtin.Os.tvos,
|
||||
builtin.Os.watchos,
|
||||
builtin.Os.mesa3d,
|
||||
builtin.Os.contiki,
|
||||
builtin.Os.amdpal,
|
||||
=> @panic("TODO specify the C integer type sizes for this OS"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getDarwinArchString(self: Target) []const u8 {
|
||||
const arch = self.getArch();
|
||||
switch (arch) {
|
||||
builtin.Arch.aarch64 => return "arm64",
|
||||
builtin.Arch.thumb,
|
||||
builtin.Arch.armv8_3a,
|
||||
builtin.Arch.armv8_2a,
|
||||
builtin.Arch.armv8_1a,
|
||||
builtin.Arch.armv8,
|
||||
builtin.Arch.armv8r,
|
||||
builtin.Arch.armv8m_baseline,
|
||||
builtin.Arch.armv8m_mainline,
|
||||
builtin.Arch.armv7,
|
||||
builtin.Arch.armv7em,
|
||||
builtin.Arch.armv7m,
|
||||
builtin.Arch.armv7s,
|
||||
builtin.Arch.armv7k,
|
||||
builtin.Arch.armv7ve,
|
||||
builtin.Arch.armv6,
|
||||
builtin.Arch.armv6m,
|
||||
builtin.Arch.armv6k,
|
||||
builtin.Arch.armv6t2,
|
||||
builtin.Arch.armv5,
|
||||
builtin.Arch.armv5te,
|
||||
builtin.Arch.armv4t,
|
||||
=> return "arm",
|
||||
builtin.Arch.powerpc => return "ppc",
|
||||
builtin.Arch.powerpc64 => return "ppc64",
|
||||
builtin.Arch.powerpc64le => return "ppc64le",
|
||||
else => return @tagName(arch),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -8,12 +8,14 @@ const assertOrPanic = std.debug.assertOrPanic;
|
|||
const errmsg = @import("errmsg.zig");
|
||||
const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
|
||||
|
||||
test "compile errors" {
|
||||
var ctx: TestContext = undefined;
|
||||
var ctx: TestContext = undefined;
|
||||
|
||||
test "stage2" {
|
||||
try ctx.init();
|
||||
defer ctx.deinit();
|
||||
|
||||
try @import("../test/stage2/compile_errors.zig").addCases(&ctx);
|
||||
try @import("../test/stage2/compare_output.zig").addCases(&ctx);
|
||||
|
||||
try ctx.run();
|
||||
}
|
||||
|
@ -25,7 +27,6 @@ pub const TestContext = struct {
|
|||
loop: std.event.Loop,
|
||||
event_loop_local: EventLoopLocal,
|
||||
zig_lib_dir: []u8,
|
||||
zig_cache_dir: []u8,
|
||||
file_index: std.atomic.Int(usize),
|
||||
group: std.event.Group(error!void),
|
||||
any_err: error!void,
|
||||
|
@ -38,7 +39,6 @@ pub const TestContext = struct {
|
|||
.loop = undefined,
|
||||
.event_loop_local = undefined,
|
||||
.zig_lib_dir = undefined,
|
||||
.zig_cache_dir = undefined,
|
||||
.group = undefined,
|
||||
.file_index = std.atomic.Int(usize).init(0),
|
||||
};
|
||||
|
@ -55,16 +55,12 @@ pub const TestContext = struct {
|
|||
self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
|
||||
errdefer allocator.free(self.zig_lib_dir);
|
||||
|
||||
self.zig_cache_dir = try introspect.resolveZigCacheDir(allocator);
|
||||
errdefer allocator.free(self.zig_cache_dir);
|
||||
|
||||
try std.os.makePath(allocator, tmp_dir_name);
|
||||
errdefer std.os.deleteTree(allocator, tmp_dir_name) catch {};
|
||||
}
|
||||
|
||||
fn deinit(self: *TestContext) void {
|
||||
std.os.deleteTree(allocator, tmp_dir_name) catch {};
|
||||
allocator.free(self.zig_cache_dir);
|
||||
allocator.free(self.zig_lib_dir);
|
||||
self.event_loop_local.deinit();
|
||||
self.loop.deinit();
|
||||
|
@ -109,7 +105,6 @@ pub const TestContext = struct {
|
|||
builtin.Mode.Debug,
|
||||
true, // is_static
|
||||
self.zig_lib_dir,
|
||||
self.zig_cache_dir,
|
||||
);
|
||||
errdefer comp.destroy();
|
||||
|
||||
|
@ -118,6 +113,84 @@ pub const TestContext = struct {
|
|||
try self.group.call(getModuleEvent, comp, source, path, line, column, msg);
|
||||
}
|
||||
|
||||
fn testCompareOutputLibC(
|
||||
self: *TestContext,
|
||||
source: []const u8,
|
||||
expected_output: []const u8,
|
||||
) !void {
|
||||
var file_index_buf: [20]u8 = undefined;
|
||||
const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());
|
||||
const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1);
|
||||
|
||||
const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, Target(Target.Native).exeFileExt());
|
||||
if (std.os.path.dirname(file1_path)) |dirname| {
|
||||
try std.os.makePath(allocator, dirname);
|
||||
}
|
||||
|
||||
// TODO async I/O
|
||||
try std.io.writeFile(allocator, file1_path, source);
|
||||
|
||||
var comp = try Compilation.create(
|
||||
&self.event_loop_local,
|
||||
"test",
|
||||
file1_path,
|
||||
Target.Native,
|
||||
Compilation.Kind.Exe,
|
||||
builtin.Mode.Debug,
|
||||
false,
|
||||
self.zig_lib_dir,
|
||||
);
|
||||
errdefer comp.destroy();
|
||||
|
||||
_ = try comp.addLinkLib("c", true);
|
||||
comp.link_out_file = output_file;
|
||||
try comp.build();
|
||||
|
||||
try self.group.call(getModuleEventSuccess, comp, output_file, expected_output);
|
||||
}
|
||||
|
||||
async fn getModuleEventSuccess(
|
||||
comp: *Compilation,
|
||||
exe_file: []const u8,
|
||||
expected_output: []const u8,
|
||||
) !void {
|
||||
// TODO this should not be necessary
|
||||
const exe_file_2 = try std.mem.dupe(allocator, u8, exe_file);
|
||||
|
||||
defer comp.destroy();
|
||||
const build_event = await (async comp.events.get() catch unreachable);
|
||||
|
||||
switch (build_event) {
|
||||
Compilation.Event.Ok => {
|
||||
const argv = []const []const u8{exe_file_2};
|
||||
// TODO use event loop
|
||||
const child = try std.os.ChildProcess.exec(allocator, argv, null, null, 1024 * 1024);
|
||||
switch (child.term) {
|
||||
std.os.ChildProcess.Term.Exited => |code| {
|
||||
if (code != 0) {
|
||||
return error.BadReturnCode;
|
||||
}
|
||||
},
|
||||
else => {
|
||||
return error.Crashed;
|
||||
},
|
||||
}
|
||||
if (!mem.eql(u8, child.stdout, expected_output)) {
|
||||
return error.OutputMismatch;
|
||||
}
|
||||
},
|
||||
Compilation.Event.Error => |err| return err,
|
||||
Compilation.Event.Fail => |msgs| {
|
||||
var stderr = try std.io.getStdErr();
|
||||
try stderr.write("build incorrectly failed:\n");
|
||||
for (msgs) |msg| {
|
||||
defer msg.destroy();
|
||||
try msg.printToFile(&stderr, errmsg.Color.Auto);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn getModuleEvent(
|
||||
comp: *Compilation,
|
||||
source: []const u8,
|
||||
|
@ -139,10 +212,10 @@ pub const TestContext = struct {
|
|||
Compilation.Event.Fail => |msgs| {
|
||||
assertOrPanic(msgs.len != 0);
|
||||
for (msgs) |msg| {
|
||||
if (mem.endsWith(u8, msg.path, path) and mem.eql(u8, msg.text, text)) {
|
||||
const first_token = msg.tree.tokens.at(msg.span.first);
|
||||
const last_token = msg.tree.tokens.at(msg.span.first);
|
||||
const start_loc = msg.tree.tokenLocationPtr(0, first_token);
|
||||
if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) {
|
||||
const first_token = msg.getTree().tokens.at(msg.span.first);
|
||||
const last_token = msg.getTree().tokens.at(msg.span.first);
|
||||
const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
|
||||
if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
|
||||
return;
|
||||
}
|
||||
|
@ -159,7 +232,8 @@ pub const TestContext = struct {
|
|||
std.debug.warn("\n====found:========\n");
|
||||
var stderr = try std.io.getStdErr();
|
||||
for (msgs) |msg| {
|
||||
try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto);
|
||||
defer msg.destroy();
|
||||
try msg.printToFile(&stderr, errmsg.Color.Auto);
|
||||
}
|
||||
std.debug.warn("============\n");
|
||||
return error.TestFailed;
|
||||
|
|
|
@ -4,11 +4,17 @@ const Scope = @import("scope.zig").Scope;
|
|||
const Compilation = @import("compilation.zig").Compilation;
|
||||
const Value = @import("value.zig").Value;
|
||||
const llvm = @import("llvm.zig");
|
||||
const ObjectFile = @import("codegen.zig").ObjectFile;
|
||||
const event = std.event;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
pub const Type = struct {
|
||||
base: Value,
|
||||
id: Id,
|
||||
name: []const u8,
|
||||
abi_alignment: AbiAlignment,
|
||||
|
||||
pub const AbiAlignment = event.Future(error{OutOfMemory}!u32);
|
||||
|
||||
pub const Id = builtin.TypeId;
|
||||
|
||||
|
@ -42,33 +48,37 @@ pub const Type = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn getLlvmType(base: *Type, ofile: *ObjectFile) (error{OutOfMemory}!llvm.TypeRef) {
|
||||
pub fn getLlvmType(
|
||||
base: *Type,
|
||||
allocator: *Allocator,
|
||||
llvm_context: llvm.ContextRef,
|
||||
) (error{OutOfMemory}!llvm.TypeRef) {
|
||||
switch (base.id) {
|
||||
Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(ofile),
|
||||
Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(ofile),
|
||||
Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Type => unreachable,
|
||||
Id.Void => unreachable,
|
||||
Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(ofile),
|
||||
Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.NoReturn => unreachable,
|
||||
Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(ofile),
|
||||
Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(ofile),
|
||||
Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(ofile),
|
||||
Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(ofile),
|
||||
Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.ComptimeFloat => unreachable,
|
||||
Id.ComptimeInt => unreachable,
|
||||
Id.Undefined => unreachable,
|
||||
Id.Null => unreachable,
|
||||
Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(ofile),
|
||||
Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(ofile),
|
||||
Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(ofile),
|
||||
Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(ofile),
|
||||
Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(ofile),
|
||||
Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Namespace => unreachable,
|
||||
Id.Block => unreachable,
|
||||
Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(ofile),
|
||||
Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.ArgTuple => unreachable,
|
||||
Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(ofile),
|
||||
Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(ofile),
|
||||
Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(allocator, llvm_context),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,8 +161,49 @@ pub const Type = struct {
|
|||
std.debug.warn("{}", @tagName(base.id));
|
||||
}
|
||||
|
||||
pub fn getAbiAlignment(base: *Type, comp: *Compilation) u32 {
|
||||
@panic("TODO getAbiAlignment");
|
||||
fn init(base: *Type, comp: *Compilation, id: Id, name: []const u8) void {
|
||||
base.* = Type{
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
.typ = &MetaType.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.id = id,
|
||||
.name = name,
|
||||
.abi_alignment = AbiAlignment.init(comp.loop),
|
||||
};
|
||||
}
|
||||
|
||||
/// If you happen to have an llvm context handy, use getAbiAlignmentInContext instead.
|
||||
/// Otherwise, this one will grab one from the pool and then release it.
|
||||
pub async fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 {
|
||||
if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
|
||||
|
||||
{
|
||||
const held = try comp.event_loop_local.getAnyLlvmContext();
|
||||
defer held.release(comp.event_loop_local);
|
||||
|
||||
const llvm_context = held.node.data;
|
||||
|
||||
base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable);
|
||||
}
|
||||
base.abi_alignment.resolve();
|
||||
return base.abi_alignment.data;
|
||||
}
|
||||
|
||||
/// If you have an llvm conext handy, you can use it here.
|
||||
pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 {
|
||||
if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
|
||||
|
||||
base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable);
|
||||
base.abi_alignment.resolve();
|
||||
return base.abi_alignment.data;
|
||||
}
|
||||
|
||||
/// Lower level function that does the work. See getAbiAlignment.
|
||||
async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 {
|
||||
const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context);
|
||||
return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type));
|
||||
}
|
||||
|
||||
pub const Struct = struct {
|
||||
|
@ -163,7 +214,7 @@ pub const Type = struct {
|
|||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Struct, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
|
@ -176,28 +227,23 @@ pub const Type = struct {
|
|||
|
||||
pub const Param = struct {
|
||||
is_noalias: bool,
|
||||
typeof: *Type,
|
||||
typ: *Type,
|
||||
};
|
||||
|
||||
pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn {
|
||||
const result = try comp.gpa().create(Fn{
|
||||
.base = Type{
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
.typeof = &MetaType.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.id = builtin.TypeId.Fn,
|
||||
},
|
||||
.base = undefined,
|
||||
.return_type = return_type,
|
||||
.params = params,
|
||||
.is_var_args = is_var_args,
|
||||
});
|
||||
errdefer comp.gpa().destroy(result);
|
||||
|
||||
result.base.init(comp, Id.Fn, "TODO fn type name");
|
||||
|
||||
result.return_type.base.ref();
|
||||
for (result.params) |param| {
|
||||
param.typeof.base.ref();
|
||||
param.typ.base.ref();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -205,20 +251,20 @@ pub const Type = struct {
|
|||
pub fn destroy(self: *Fn, comp: *Compilation) void {
|
||||
self.return_type.base.deref(comp);
|
||||
for (self.params) |param| {
|
||||
param.typeof.base.deref(comp);
|
||||
param.typ.base.deref(comp);
|
||||
}
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Fn, ofile: *ObjectFile) !llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
|
||||
const llvm_return_type = switch (self.return_type.id) {
|
||||
Type.Id.Void => llvm.VoidTypeInContext(ofile.context) orelse return error.OutOfMemory,
|
||||
else => try self.return_type.getLlvmType(ofile),
|
||||
Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory,
|
||||
else => try self.return_type.getLlvmType(allocator, llvm_context),
|
||||
};
|
||||
const llvm_param_types = try ofile.gpa().alloc(llvm.TypeRef, self.params.len);
|
||||
defer ofile.gpa().free(llvm_param_types);
|
||||
const llvm_param_types = try allocator.alloc(llvm.TypeRef, self.params.len);
|
||||
defer allocator.free(llvm_param_types);
|
||||
for (llvm_param_types) |*llvm_param_type, i| {
|
||||
llvm_param_type.* = try self.params[i].typeof.getLlvmType(ofile);
|
||||
llvm_param_type.* = try self.params[i].typ.getLlvmType(allocator, llvm_context);
|
||||
}
|
||||
|
||||
return llvm.FunctionType(
|
||||
|
@ -272,7 +318,7 @@ pub const Type = struct {
|
|||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Bool, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Bool, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
|
@ -293,13 +339,83 @@ pub const Type = struct {
|
|||
|
||||
pub const Int = struct {
|
||||
base: Type,
|
||||
key: Key,
|
||||
garbage_node: std.atomic.Stack(*Int).Node,
|
||||
|
||||
pub const Key = struct {
|
||||
bit_count: u32,
|
||||
is_signed: bool,
|
||||
|
||||
pub fn hash(self: *const Key) u32 {
|
||||
const rands = [2]u32{ 0xa4ba6498, 0x75fc5af7 };
|
||||
return rands[@boolToInt(self.is_signed)] *% self.bit_count;
|
||||
}
|
||||
|
||||
pub fn eql(self: *const Key, other: *const Key) bool {
|
||||
return self.bit_count == other.bit_count and self.is_signed == other.is_signed;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn get_u8(comp: *Compilation) *Int {
|
||||
comp.u8_type.base.base.ref();
|
||||
return comp.u8_type;
|
||||
}
|
||||
|
||||
pub async fn get(comp: *Compilation, key: Key) !*Int {
|
||||
{
|
||||
const held = await (async comp.int_type_table.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
if (held.value.get(&key)) |entry| {
|
||||
entry.value.base.base.ref();
|
||||
return entry.value;
|
||||
}
|
||||
}
|
||||
|
||||
const self = try comp.gpa().create(Int{
|
||||
.base = undefined,
|
||||
.key = key,
|
||||
.garbage_node = undefined,
|
||||
});
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
const u_or_i = "ui"[@boolToInt(key.is_signed)];
|
||||
const name = try std.fmt.allocPrint(comp.gpa(), "{c}{}", u_or_i, key.bit_count);
|
||||
errdefer comp.gpa().free(name);
|
||||
|
||||
self.base.init(comp, Id.Int, name);
|
||||
|
||||
{
|
||||
const held = await (async comp.int_type_table.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
_ = try held.value.put(&self.key, self);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Int, comp: *Compilation) void {
|
||||
self.garbage_node = std.atomic.Stack(*Int).Node{
|
||||
.data = self,
|
||||
.next = undefined,
|
||||
};
|
||||
comp.registerGarbage(Int, &self.garbage_node);
|
||||
}
|
||||
|
||||
pub async fn gcDestroy(self: *Int, comp: *Compilation) void {
|
||||
{
|
||||
const held = await (async comp.int_type_table.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
_ = held.value.remove(&self.key).?;
|
||||
}
|
||||
// we allocated the name
|
||||
comp.gpa().free(self.base.name);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Int, ofile: *ObjectFile) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
pub fn getLlvmType(self: *Int, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
|
||||
return llvm.IntTypeInContext(llvm_context, self.key.bit_count) orelse return error.OutOfMemory;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -310,56 +426,236 @@ pub const Type = struct {
|
|||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Float, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Float, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
pub const Pointer = struct {
|
||||
base: Type,
|
||||
mut: Mut,
|
||||
vol: Vol,
|
||||
size: Size,
|
||||
alignment: u32,
|
||||
key: Key,
|
||||
garbage_node: std.atomic.Stack(*Pointer).Node,
|
||||
|
||||
pub const Key = struct {
|
||||
child_type: *Type,
|
||||
mut: Mut,
|
||||
vol: Vol,
|
||||
size: Size,
|
||||
alignment: Align,
|
||||
|
||||
pub fn hash(self: *const Key) u32 {
|
||||
const align_hash = switch (self.alignment) {
|
||||
Align.Abi => 0xf201c090,
|
||||
Align.Override => |x| x,
|
||||
};
|
||||
return hash_usize(@ptrToInt(self.child_type)) *%
|
||||
hash_enum(self.mut) *%
|
||||
hash_enum(self.vol) *%
|
||||
hash_enum(self.size) *%
|
||||
align_hash;
|
||||
}
|
||||
|
||||
pub fn eql(self: *const Key, other: *const Key) bool {
|
||||
if (self.child_type != other.child_type or
|
||||
self.mut != other.mut or
|
||||
self.vol != other.vol or
|
||||
self.size != other.size or
|
||||
@TagType(Align)(self.alignment) != @TagType(Align)(other.alignment))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
switch (self.alignment) {
|
||||
Align.Abi => return true,
|
||||
Align.Override => |x| return x == other.alignment.Override,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Mut = enum {
|
||||
Mut,
|
||||
Const,
|
||||
};
|
||||
|
||||
pub const Vol = enum {
|
||||
Non,
|
||||
Volatile,
|
||||
};
|
||||
|
||||
pub const Align = union(enum) {
|
||||
Abi,
|
||||
Override: u32,
|
||||
};
|
||||
|
||||
pub const Size = builtin.TypeInfo.Pointer.Size;
|
||||
|
||||
pub fn destroy(self: *Pointer, comp: *Compilation) void {
|
||||
self.garbage_node = std.atomic.Stack(*Pointer).Node{
|
||||
.data = self,
|
||||
.next = undefined,
|
||||
};
|
||||
comp.registerGarbage(Pointer, &self.garbage_node);
|
||||
}
|
||||
|
||||
pub async fn gcDestroy(self: *Pointer, comp: *Compilation) void {
|
||||
{
|
||||
const held = await (async comp.ptr_type_table.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
_ = held.value.remove(&self.key).?;
|
||||
}
|
||||
self.key.child_type.base.deref(comp);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn get(
|
||||
comp: *Compilation,
|
||||
elem_type: *Type,
|
||||
mut: Mut,
|
||||
vol: Vol,
|
||||
size: Size,
|
||||
alignment: u32,
|
||||
) *Pointer {
|
||||
@panic("TODO get pointer");
|
||||
pub async fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 {
|
||||
switch (self.key.alignment) {
|
||||
Align.Abi => return await (async self.key.child_type.getAbiAlignment(comp) catch unreachable),
|
||||
Align.Override => |alignment| return alignment,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Pointer, ofile: *ObjectFile) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
pub async fn get(
|
||||
comp: *Compilation,
|
||||
key: Key,
|
||||
) !*Pointer {
|
||||
var normal_key = key;
|
||||
switch (key.alignment) {
|
||||
Align.Abi => {},
|
||||
Align.Override => |alignment| {
|
||||
const abi_align = try await (async key.child_type.getAbiAlignment(comp) catch unreachable);
|
||||
if (abi_align == alignment) {
|
||||
normal_key.alignment = Align.Abi;
|
||||
}
|
||||
},
|
||||
}
|
||||
{
|
||||
const held = await (async comp.ptr_type_table.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
if (held.value.get(&normal_key)) |entry| {
|
||||
entry.value.base.base.ref();
|
||||
return entry.value;
|
||||
}
|
||||
}
|
||||
|
||||
const self = try comp.gpa().create(Pointer{
|
||||
.base = undefined,
|
||||
.key = normal_key,
|
||||
.garbage_node = undefined,
|
||||
});
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
const size_str = switch (self.key.size) {
|
||||
Size.One => "*",
|
||||
Size.Many => "[*]",
|
||||
Size.Slice => "[]",
|
||||
};
|
||||
const mut_str = switch (self.key.mut) {
|
||||
Mut.Const => "const ",
|
||||
Mut.Mut => "",
|
||||
};
|
||||
const vol_str = switch (self.key.vol) {
|
||||
Vol.Volatile => "volatile ",
|
||||
Vol.Non => "",
|
||||
};
|
||||
const name = switch (self.key.alignment) {
|
||||
Align.Abi => try std.fmt.allocPrint(
|
||||
comp.gpa(),
|
||||
"{}{}{}{}",
|
||||
size_str,
|
||||
mut_str,
|
||||
vol_str,
|
||||
self.key.child_type.name,
|
||||
),
|
||||
Align.Override => |alignment| try std.fmt.allocPrint(
|
||||
comp.gpa(),
|
||||
"{}align<{}> {}{}{}",
|
||||
size_str,
|
||||
alignment,
|
||||
mut_str,
|
||||
vol_str,
|
||||
self.key.child_type.name,
|
||||
),
|
||||
};
|
||||
errdefer comp.gpa().free(name);
|
||||
|
||||
self.base.init(comp, Id.Pointer, name);
|
||||
|
||||
{
|
||||
const held = await (async comp.ptr_type_table.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
_ = try held.value.put(&self.key, self);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
|
||||
const elem_llvm_type = try self.key.child_type.getLlvmType(allocator, llvm_context);
|
||||
return llvm.PointerType(elem_llvm_type, 0) orelse return error.OutOfMemory;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Array = struct {
|
||||
base: Type,
|
||||
key: Key,
|
||||
garbage_node: std.atomic.Stack(*Array).Node,
|
||||
|
||||
pub const Key = struct {
|
||||
elem_type: *Type,
|
||||
len: usize,
|
||||
|
||||
pub fn hash(self: *const Key) u32 {
|
||||
return hash_usize(@ptrToInt(self.elem_type)) *% hash_usize(self.len);
|
||||
}
|
||||
|
||||
pub fn eql(self: *const Key, other: *const Key) bool {
|
||||
return self.elem_type == other.elem_type and self.len == other.len;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn destroy(self: *Array, comp: *Compilation) void {
|
||||
self.key.elem_type.base.deref(comp);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Array, ofile: *ObjectFile) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
pub async fn get(comp: *Compilation, key: Key) !*Array {
|
||||
key.elem_type.base.ref();
|
||||
errdefer key.elem_type.base.deref(comp);
|
||||
|
||||
{
|
||||
const held = await (async comp.array_type_table.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
if (held.value.get(&key)) |entry| {
|
||||
entry.value.base.base.ref();
|
||||
return entry.value;
|
||||
}
|
||||
}
|
||||
|
||||
const self = try comp.gpa().create(Array{
|
||||
.base = undefined,
|
||||
.key = key,
|
||||
.garbage_node = undefined,
|
||||
});
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
const name = try std.fmt.allocPrint(comp.gpa(), "[{}]{}", key.len, key.elem_type.name);
|
||||
errdefer comp.gpa().free(name);
|
||||
|
||||
self.base.init(comp, Id.Array, name);
|
||||
|
||||
{
|
||||
const held = await (async comp.array_type_table.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
_ = try held.value.put(&self.key, self);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Array, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
|
||||
const elem_llvm_type = try self.key.elem_type.getLlvmType(allocator, llvm_context);
|
||||
return llvm.ArrayType(elem_llvm_type, @intCast(c_uint, self.key.len)) orelse return error.OutOfMemory;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -374,6 +670,12 @@ pub const Type = struct {
|
|||
pub const ComptimeInt = struct {
|
||||
base: Type,
|
||||
|
||||
/// Adds 1 reference to the resulting type
|
||||
pub fn get(comp: *Compilation) *ComptimeInt {
|
||||
comp.comptime_int_type.base.base.ref();
|
||||
return comp.comptime_int_type;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *ComptimeInt, comp: *Compilation) void {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
@ -402,7 +704,7 @@ pub const Type = struct {
|
|||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Optional, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Optional, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
|
@ -414,7 +716,7 @@ pub const Type = struct {
|
|||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *ErrorUnion, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *ErrorUnion, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
|
@ -426,7 +728,7 @@ pub const Type = struct {
|
|||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *ErrorSet, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *ErrorSet, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
|
@ -438,7 +740,7 @@ pub const Type = struct {
|
|||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Enum, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Enum, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
|
@ -450,7 +752,7 @@ pub const Type = struct {
|
|||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Union, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Union, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
|
@ -478,7 +780,7 @@ pub const Type = struct {
|
|||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *BoundFn, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *BoundFn, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
|
@ -498,7 +800,7 @@ pub const Type = struct {
|
|||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Opaque, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
|
@ -510,8 +812,33 @@ pub const Type = struct {
|
|||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Promise, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Promise, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
fn hash_usize(x: usize) u32 {
|
||||
return switch (@sizeOf(usize)) {
|
||||
4 => x,
|
||||
8 => @truncate(u32, x *% 0xad44ee2d8e3fc13d),
|
||||
else => @compileError("implement this hash function"),
|
||||
};
|
||||
}
|
||||
|
||||
fn hash_enum(x: var) u32 {
|
||||
const rands = []u32{
|
||||
0x85ebf64f,
|
||||
0x3fcb3211,
|
||||
0x240a4e8e,
|
||||
0x40bb0e3c,
|
||||
0x78be45af,
|
||||
0x1ca98e37,
|
||||
0xec56053a,
|
||||
0x906adc48,
|
||||
0xd4fe9763,
|
||||
0x54c80dac,
|
||||
};
|
||||
comptime assert(@memberCount(@typeOf(x)) < rands.len);
|
||||
return rands[@enumToInt(x)];
|
||||
}
|
||||
|
|
|
@ -5,12 +5,13 @@ const Compilation = @import("compilation.zig").Compilation;
|
|||
const ObjectFile = @import("codegen.zig").ObjectFile;
|
||||
const llvm = @import("llvm.zig");
|
||||
const Buffer = std.Buffer;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
/// Values are ref-counted, heap-allocated, and copy-on-write
|
||||
/// If there is only 1 ref then write need not copy
|
||||
pub const Value = struct {
|
||||
id: Id,
|
||||
typeof: *Type,
|
||||
typ: *Type,
|
||||
ref_count: std.atomic.Int(usize),
|
||||
|
||||
/// Thread-safe
|
||||
|
@ -21,23 +22,37 @@ pub const Value = struct {
|
|||
/// Thread-safe
|
||||
pub fn deref(base: *Value, comp: *Compilation) void {
|
||||
if (base.ref_count.decr() == 1) {
|
||||
base.typeof.base.deref(comp);
|
||||
base.typ.base.deref(comp);
|
||||
switch (base.id) {
|
||||
Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp),
|
||||
Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp),
|
||||
Id.FnProto => @fieldParentPtr(FnProto, "base", base).destroy(comp),
|
||||
Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp),
|
||||
Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp),
|
||||
Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp),
|
||||
Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp),
|
||||
Id.Int => @fieldParentPtr(Int, "base", base).destroy(comp),
|
||||
Id.Array => @fieldParentPtr(Array, "base", base).destroy(comp),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setType(base: *Value, new_type: *Type, comp: *Compilation) void {
|
||||
base.typ.base.deref(comp);
|
||||
new_type.base.ref();
|
||||
base.typ = new_type;
|
||||
}
|
||||
|
||||
pub fn getRef(base: *Value) *Value {
|
||||
base.ref();
|
||||
return base;
|
||||
}
|
||||
|
||||
pub fn cast(base: *Value, comptime T: type) ?*T {
|
||||
if (base.id != @field(Id, @typeName(T))) return null;
|
||||
return @fieldParentPtr(T, "base", base);
|
||||
}
|
||||
|
||||
pub fn dump(base: *const Value) void {
|
||||
std.debug.warn("{}", @tagName(base.id));
|
||||
}
|
||||
|
@ -46,24 +61,111 @@ pub const Value = struct {
|
|||
switch (base.id) {
|
||||
Id.Type => unreachable,
|
||||
Id.Fn => @panic("TODO"),
|
||||
Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile),
|
||||
Id.Void => return null,
|
||||
Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),
|
||||
Id.NoReturn => unreachable,
|
||||
Id.Ptr => @panic("TODO"),
|
||||
Id.Ptr => return @fieldParentPtr(Ptr, "base", base).getLlvmConst(ofile),
|
||||
Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmConst(ofile),
|
||||
Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmConst(ofile),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn derefAndCopy(self: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
|
||||
if (self.ref_count.get() == 1) {
|
||||
// ( ͡° ͜ʖ ͡°)
|
||||
return self;
|
||||
}
|
||||
|
||||
assert(self.ref_count.decr() != 1);
|
||||
return self.copy(comp);
|
||||
}
|
||||
|
||||
pub fn copy(base: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
|
||||
switch (base.id) {
|
||||
Id.Type => unreachable,
|
||||
Id.Fn => unreachable,
|
||||
Id.FnProto => unreachable,
|
||||
Id.Void => unreachable,
|
||||
Id.Bool => unreachable,
|
||||
Id.NoReturn => unreachable,
|
||||
Id.Ptr => unreachable,
|
||||
Id.Array => unreachable,
|
||||
Id.Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base,
|
||||
}
|
||||
}
|
||||
|
||||
pub const Parent = union(enum) {
|
||||
None,
|
||||
BaseStruct: BaseStruct,
|
||||
BaseArray: BaseArray,
|
||||
BaseUnion: *Value,
|
||||
BaseScalar: *Value,
|
||||
|
||||
pub const BaseStruct = struct {
|
||||
val: *Value,
|
||||
field_index: usize,
|
||||
};
|
||||
|
||||
pub const BaseArray = struct {
|
||||
val: *Value,
|
||||
elem_index: usize,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Id = enum {
|
||||
Type,
|
||||
Fn,
|
||||
Void,
|
||||
Bool,
|
||||
NoReturn,
|
||||
Array,
|
||||
Ptr,
|
||||
Int,
|
||||
FnProto,
|
||||
};
|
||||
|
||||
pub const Type = @import("type.zig").Type;
|
||||
|
||||
pub const FnProto = struct {
|
||||
base: Value,
|
||||
|
||||
/// The main external name that is used in the .o file.
|
||||
/// TODO https://github.com/ziglang/zig/issues/265
|
||||
symbol_name: Buffer,
|
||||
|
||||
pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: Buffer) !*FnProto {
|
||||
const self = try comp.gpa().create(FnProto{
|
||||
.base = Value{
|
||||
.id = Value.Id.FnProto,
|
||||
.typ = &fn_type.base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.symbol_name = symbol_name,
|
||||
});
|
||||
fn_type.base.base.ref();
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *FnProto, comp: *Compilation) void {
|
||||
self.symbol_name.deinit();
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?llvm.ValueRef {
|
||||
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
|
||||
const llvm_fn = llvm.AddFunction(
|
||||
ofile.module,
|
||||
self.symbol_name.ptr(),
|
||||
llvm_fn_type,
|
||||
) orelse return error.OutOfMemory;
|
||||
|
||||
// TODO port more logic from codegen.cpp:fn_llvm_value
|
||||
|
||||
return llvm_fn;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Fn = struct {
|
||||
base: Value,
|
||||
|
||||
|
@ -98,7 +200,7 @@ pub const Value = struct {
|
|||
const self = try comp.gpa().create(Fn{
|
||||
.base = Value{
|
||||
.id = Value.Id.Fn,
|
||||
.typeof = &fn_type.base,
|
||||
.typ = &fn_type.base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.fndef_scope = fndef_scope,
|
||||
|
@ -187,6 +289,8 @@ pub const Value = struct {
|
|||
|
||||
pub const Ptr = struct {
|
||||
base: Value,
|
||||
special: Special,
|
||||
mut: Mut,
|
||||
|
||||
pub const Mut = enum {
|
||||
CompTimeConst,
|
||||
|
@ -194,8 +298,268 @@ pub const Value = struct {
|
|||
RunTime,
|
||||
};
|
||||
|
||||
pub const Special = union(enum) {
|
||||
Scalar: *Value,
|
||||
BaseArray: BaseArray,
|
||||
BaseStruct: BaseStruct,
|
||||
HardCodedAddr: u64,
|
||||
Discard,
|
||||
};
|
||||
|
||||
pub const BaseArray = struct {
|
||||
val: *Value,
|
||||
elem_index: usize,
|
||||
};
|
||||
|
||||
pub const BaseStruct = struct {
|
||||
val: *Value,
|
||||
field_index: usize,
|
||||
};
|
||||
|
||||
pub async fn createArrayElemPtr(
|
||||
comp: *Compilation,
|
||||
array_val: *Array,
|
||||
mut: Type.Pointer.Mut,
|
||||
size: Type.Pointer.Size,
|
||||
elem_index: usize,
|
||||
) !*Ptr {
|
||||
array_val.base.ref();
|
||||
errdefer array_val.base.deref(comp);
|
||||
|
||||
const elem_type = array_val.base.typ.cast(Type.Array).?.key.elem_type;
|
||||
const ptr_type = try await (async Type.Pointer.get(comp, Type.Pointer.Key{
|
||||
.child_type = elem_type,
|
||||
.mut = mut,
|
||||
.vol = Type.Pointer.Vol.Non,
|
||||
.size = size,
|
||||
.alignment = Type.Pointer.Align.Abi,
|
||||
}) catch unreachable);
|
||||
var ptr_type_consumed = false;
|
||||
errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp);
|
||||
|
||||
const self = try comp.gpa().create(Value.Ptr{
|
||||
.base = Value{
|
||||
.id = Value.Id.Ptr,
|
||||
.typ = &ptr_type.base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.special = Special{
|
||||
.BaseArray = BaseArray{
|
||||
.val = &array_val.base,
|
||||
.elem_index = 0,
|
||||
},
|
||||
},
|
||||
.mut = Mut.CompTimeConst,
|
||||
});
|
||||
ptr_type_consumed = true;
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Ptr, comp: *Compilation) void {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?llvm.ValueRef {
|
||||
const llvm_type = self.base.typ.getLlvmType(ofile.arena, ofile.context);
|
||||
// TODO carefully port the logic from codegen.cpp:gen_const_val_ptr
|
||||
switch (self.special) {
|
||||
Special.Scalar => |scalar| @panic("TODO"),
|
||||
Special.BaseArray => |base_array| {
|
||||
// TODO put this in one .o file only, and after that, generate extern references to it
|
||||
const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?;
|
||||
const ptr_bit_count = ofile.comp.target_ptr_bits;
|
||||
const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory;
|
||||
const indices = []llvm.ValueRef{
|
||||
llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory,
|
||||
llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory,
|
||||
};
|
||||
return llvm.ConstInBoundsGEP(
|
||||
array_llvm_value,
|
||||
&indices,
|
||||
@intCast(c_uint, indices.len),
|
||||
) orelse return error.OutOfMemory;
|
||||
},
|
||||
Special.BaseStruct => |base_struct| @panic("TODO"),
|
||||
Special.HardCodedAddr => |addr| @panic("TODO"),
|
||||
Special.Discard => unreachable,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Array = struct {
|
||||
base: Value,
|
||||
special: Special,
|
||||
|
||||
pub const Special = union(enum) {
|
||||
Undefined,
|
||||
OwnedBuffer: []u8,
|
||||
Explicit: Data,
|
||||
};
|
||||
|
||||
pub const Data = struct {
|
||||
parent: Parent,
|
||||
elements: []*Value,
|
||||
};
|
||||
|
||||
/// Takes ownership of buffer
|
||||
pub async fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array {
|
||||
const u8_type = Type.Int.get_u8(comp);
|
||||
defer u8_type.base.base.deref(comp);
|
||||
|
||||
const array_type = try await (async Type.Array.get(comp, Type.Array.Key{
|
||||
.elem_type = &u8_type.base,
|
||||
.len = buffer.len,
|
||||
}) catch unreachable);
|
||||
errdefer array_type.base.base.deref(comp);
|
||||
|
||||
const self = try comp.gpa().create(Value.Array{
|
||||
.base = Value{
|
||||
.id = Value.Id.Array,
|
||||
.typ = &array_type.base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.special = Special{ .OwnedBuffer = buffer },
|
||||
});
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Array, comp: *Compilation) void {
|
||||
switch (self.special) {
|
||||
Special.Undefined => {},
|
||||
Special.OwnedBuffer => |buf| {
|
||||
comp.gpa().free(buf);
|
||||
},
|
||||
Special.Explicit => {},
|
||||
}
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?llvm.ValueRef {
|
||||
switch (self.special) {
|
||||
Special.Undefined => {
|
||||
const llvm_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
|
||||
return llvm.GetUndef(llvm_type);
|
||||
},
|
||||
Special.OwnedBuffer => |buf| {
|
||||
const dont_null_terminate = 1;
|
||||
const llvm_str_init = llvm.ConstStringInContext(
|
||||
ofile.context,
|
||||
buf.ptr,
|
||||
@intCast(c_uint, buf.len),
|
||||
dont_null_terminate,
|
||||
) orelse return error.OutOfMemory;
|
||||
const str_init_type = llvm.TypeOf(llvm_str_init);
|
||||
const global = llvm.AddGlobal(ofile.module, str_init_type, c"") orelse return error.OutOfMemory;
|
||||
llvm.SetInitializer(global, llvm_str_init);
|
||||
llvm.SetLinkage(global, llvm.PrivateLinkage);
|
||||
llvm.SetGlobalConstant(global, 1);
|
||||
llvm.SetUnnamedAddr(global, 1);
|
||||
llvm.SetAlignment(global, llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, str_init_type));
|
||||
return global;
|
||||
},
|
||||
Special.Explicit => @panic("TODO"),
|
||||
}
|
||||
|
||||
//{
|
||||
// uint64_t len = type_entry->data.array.len;
|
||||
// if (const_val->data.x_array.special == ConstArraySpecialUndef) {
|
||||
// return LLVMGetUndef(type_entry->type_ref);
|
||||
// }
|
||||
|
||||
// LLVMValueRef *values = allocate<LLVMValueRef>(len);
|
||||
// LLVMTypeRef element_type_ref = type_entry->data.array.child_type->type_ref;
|
||||
// bool make_unnamed_struct = false;
|
||||
// for (uint64_t i = 0; i < len; i += 1) {
|
||||
// ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i];
|
||||
// LLVMValueRef val = gen_const_val(g, elem_value, "");
|
||||
// values[i] = val;
|
||||
// make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val);
|
||||
// }
|
||||
// if (make_unnamed_struct) {
|
||||
// return LLVMConstStruct(values, len, true);
|
||||
// } else {
|
||||
// return LLVMConstArray(element_type_ref, values, (unsigned)len);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Int = struct {
|
||||
base: Value,
|
||||
big_int: std.math.big.Int,
|
||||
|
||||
pub fn createFromString(comp: *Compilation, typ: *Type, base: u8, value: []const u8) !*Int {
|
||||
const self = try comp.gpa().create(Value.Int{
|
||||
.base = Value{
|
||||
.id = Value.Id.Int,
|
||||
.typ = typ,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.big_int = undefined,
|
||||
});
|
||||
typ.base.ref();
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
self.big_int = try std.math.big.Int.init(comp.gpa());
|
||||
errdefer self.big_int.deinit();
|
||||
|
||||
try self.big_int.setString(base, value);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?llvm.ValueRef {
|
||||
switch (self.base.typ.id) {
|
||||
Type.Id.Int => {
|
||||
const type_ref = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
|
||||
if (self.big_int.len == 0) {
|
||||
return llvm.ConstNull(type_ref);
|
||||
}
|
||||
const unsigned_val = if (self.big_int.len == 1) blk: {
|
||||
break :blk llvm.ConstInt(type_ref, self.big_int.limbs[0], @boolToInt(false));
|
||||
} else if (@sizeOf(std.math.big.Limb) == @sizeOf(u64)) blk: {
|
||||
break :blk llvm.ConstIntOfArbitraryPrecision(
|
||||
type_ref,
|
||||
@intCast(c_uint, self.big_int.len),
|
||||
@ptrCast([*]u64, self.big_int.limbs.ptr),
|
||||
);
|
||||
} else {
|
||||
@compileError("std.math.Big.Int.Limb size does not match LLVM");
|
||||
};
|
||||
return if (self.big_int.positive) unsigned_val else llvm.ConstNeg(unsigned_val);
|
||||
},
|
||||
Type.Id.ComptimeInt => unreachable,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy(old: *Int, comp: *Compilation) !*Int {
|
||||
old.base.typ.base.ref();
|
||||
errdefer old.base.typ.base.deref(comp);
|
||||
|
||||
const new = try comp.gpa().create(Value.Int{
|
||||
.base = Value{
|
||||
.id = Value.Id.Int,
|
||||
.typ = old.base.typ,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.big_int = undefined,
|
||||
});
|
||||
errdefer comp.gpa().destroy(new);
|
||||
|
||||
new.big_int = try old.big_int.clone();
|
||||
errdefer new.big_int.deinit();
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Int, comp: *Compilation) void {
|
||||
self.big_int.deinit();
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -4379,7 +4379,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
|
|||
|
||||
static ZigWindowsSDK *get_windows_sdk(CodeGen *g) {
|
||||
if (g->win_sdk == nullptr) {
|
||||
if (os_find_windows_sdk(&g->win_sdk)) {
|
||||
if (zig_find_windows_sdk(&g->win_sdk)) {
|
||||
fprintf(stderr, "unable to determine windows sdk path\n");
|
||||
exit(1);
|
||||
}
|
||||
|
@ -4499,12 +4499,11 @@ void find_libc_lib_path(CodeGen *g) {
|
|||
ZigWindowsSDK *sdk = get_windows_sdk(g);
|
||||
|
||||
if (g->msvc_lib_dir == nullptr) {
|
||||
Buf* vc_lib_dir = buf_alloc();
|
||||
if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) {
|
||||
if (sdk->msvc_lib_dir_ptr == nullptr) {
|
||||
fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir");
|
||||
exit(1);
|
||||
}
|
||||
g->msvc_lib_dir = vc_lib_dir;
|
||||
g->msvc_lib_dir = buf_create_from_mem(sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len);
|
||||
}
|
||||
|
||||
if (g->libc_lib_dir == nullptr) {
|
||||
|
|
|
@ -901,7 +901,7 @@ static void construct_linker_job_macho(LinkJob *lj) {
|
|||
if (strchr(buf_ptr(link_lib->name), '/') == nullptr) {
|
||||
Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
|
||||
lj->args.append(buf_ptr(arg));
|
||||
} else {
|
||||
} else {
|
||||
lj->args.append(buf_ptr(link_lib->name));
|
||||
}
|
||||
}
|
||||
|
|
248
src/os.cpp
248
src/os.cpp
|
@ -26,7 +26,6 @@
|
|||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include "windows_com.hpp"
|
||||
|
||||
typedef SSIZE_T ssize_t;
|
||||
#else
|
||||
|
@ -1115,249 +1114,10 @@ void os_stderr_set_color(TermColor color) {
|
|||
#endif
|
||||
}
|
||||
|
||||
int os_find_windows_sdk(ZigWindowsSDK **out_sdk) {
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
ZigWindowsSDK *result_sdk = allocate<ZigWindowsSDK>(1);
|
||||
buf_resize(&result_sdk->path10, 0);
|
||||
buf_resize(&result_sdk->path81, 0);
|
||||
|
||||
HKEY key;
|
||||
HRESULT rc;
|
||||
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key);
|
||||
if (rc != ERROR_SUCCESS) {
|
||||
return ErrorFileNotFound;
|
||||
}
|
||||
|
||||
{
|
||||
DWORD tmp_buf_len = MAX_PATH;
|
||||
buf_resize(&result_sdk->path10, tmp_buf_len);
|
||||
rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path10), &tmp_buf_len);
|
||||
if (rc == ERROR_FILE_NOT_FOUND) {
|
||||
buf_resize(&result_sdk->path10, 0);
|
||||
} else {
|
||||
buf_resize(&result_sdk->path10, tmp_buf_len);
|
||||
}
|
||||
}
|
||||
{
|
||||
DWORD tmp_buf_len = MAX_PATH;
|
||||
buf_resize(&result_sdk->path81, tmp_buf_len);
|
||||
rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path81), &tmp_buf_len);
|
||||
if (rc == ERROR_FILE_NOT_FOUND) {
|
||||
buf_resize(&result_sdk->path81, 0);
|
||||
} else {
|
||||
buf_resize(&result_sdk->path81, tmp_buf_len);
|
||||
}
|
||||
}
|
||||
|
||||
if (buf_len(&result_sdk->path10) != 0) {
|
||||
Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\*", buf_ptr(&result_sdk->path10));
|
||||
|
||||
// enumerate files in sdk path looking for latest version
|
||||
WIN32_FIND_DATA ffd;
|
||||
HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd);
|
||||
if (hFind == INVALID_HANDLE_VALUE) {
|
||||
return ErrorFileNotFound;
|
||||
}
|
||||
int v0 = 0, v1 = 0, v2 = 0, v3 = 0;
|
||||
bool found_version_dir = false;
|
||||
for (;;) {
|
||||
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
int c0 = 0, c1 = 0, c2 = 0, c3 = 0;
|
||||
sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3);
|
||||
if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) {
|
||||
// Microsoft released 26624 as 10240 accidentally.
|
||||
// https://developer.microsoft.com/en-us/windows/downloads/sdk-archive
|
||||
c2 = 26624;
|
||||
}
|
||||
if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) {
|
||||
v0 = c0, v1 = c1, v2 = c2, v3 = c3;
|
||||
buf_init_from_str(&result_sdk->version10, ffd.cFileName);
|
||||
found_version_dir = true;
|
||||
}
|
||||
}
|
||||
if (FindNextFile(hFind, &ffd) == 0) {
|
||||
FindClose(hFind);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_version_dir) {
|
||||
buf_resize(&result_sdk->path10, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (buf_len(&result_sdk->path81) != 0) {
|
||||
Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\winv*", buf_ptr(&result_sdk->path81));
|
||||
|
||||
// enumerate files in sdk path looking for latest version
|
||||
WIN32_FIND_DATA ffd;
|
||||
HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd);
|
||||
if (hFind == INVALID_HANDLE_VALUE) {
|
||||
return ErrorFileNotFound;
|
||||
}
|
||||
int v0 = 0, v1 = 0;
|
||||
bool found_version_dir = false;
|
||||
for (;;) {
|
||||
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
int c0 = 0, c1 = 0;
|
||||
sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1);
|
||||
if ((c0 > v0) || (c1 > v1)) {
|
||||
v0 = c0, v1 = c1;
|
||||
buf_init_from_str(&result_sdk->version81, ffd.cFileName);
|
||||
found_version_dir = true;
|
||||
}
|
||||
}
|
||||
if (FindNextFile(hFind, &ffd) == 0) {
|
||||
FindClose(hFind);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_version_dir) {
|
||||
buf_resize(&result_sdk->path81, 0);
|
||||
}
|
||||
}
|
||||
|
||||
*out_sdk = result_sdk;
|
||||
return 0;
|
||||
#else
|
||||
return ErrorFileNotFound;
|
||||
#endif
|
||||
}
|
||||
|
||||
int os_get_win32_vcruntime_path(Buf* output_buf, ZigLLVM_ArchType platform_type) {
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
buf_resize(output_buf, 0);
|
||||
//COM Smart Pointerse requires explicit scope
|
||||
{
|
||||
HRESULT rc;
|
||||
rc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if (rc != S_OK) {
|
||||
goto com_done;
|
||||
}
|
||||
|
||||
//This COM class is installed when a VS2017
|
||||
ISetupConfigurationPtr setup_config;
|
||||
rc = setup_config.CreateInstance(__uuidof(SetupConfiguration));
|
||||
if (rc != S_OK) {
|
||||
goto com_done;
|
||||
}
|
||||
|
||||
IEnumSetupInstancesPtr all_instances;
|
||||
rc = setup_config->EnumInstances(&all_instances);
|
||||
if (rc != S_OK) {
|
||||
goto com_done;
|
||||
}
|
||||
|
||||
ISetupInstance* curr_instance;
|
||||
ULONG found_inst;
|
||||
while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) {
|
||||
BSTR bstr_inst_path;
|
||||
rc = curr_instance->GetInstallationPath(&bstr_inst_path);
|
||||
if (rc != S_OK) {
|
||||
goto com_done;
|
||||
}
|
||||
//BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length
|
||||
UINT bstr_path_len = *((UINT*)bstr_inst_path - 1);
|
||||
ULONG tmp_path_len = bstr_path_len / 2 + 1;
|
||||
char* conv_path = (char*)bstr_inst_path;
|
||||
char *tmp_path = (char*)alloca(tmp_path_len);
|
||||
memset(tmp_path, 0, tmp_path_len);
|
||||
uint32_t c = 0;
|
||||
for (uint32_t i = 0; i < bstr_path_len; i += 2) {
|
||||
tmp_path[c] = conv_path[i];
|
||||
++c;
|
||||
assert(c != tmp_path_len);
|
||||
}
|
||||
|
||||
buf_append_str(output_buf, tmp_path);
|
||||
buf_append_char(output_buf, '\\');
|
||||
|
||||
Buf* tmp_buf = buf_alloc();
|
||||
buf_append_buf(tmp_buf, output_buf);
|
||||
buf_append_str(tmp_buf, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
|
||||
FILE* tools_file = fopen(buf_ptr(tmp_buf), "r");
|
||||
if (!tools_file) {
|
||||
goto com_done;
|
||||
}
|
||||
memset(tmp_path, 0, tmp_path_len);
|
||||
fgets(tmp_path, tmp_path_len, tools_file);
|
||||
strtok(tmp_path, " \r\n");
|
||||
fclose(tools_file);
|
||||
buf_appendf(output_buf, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path);
|
||||
switch (platform_type) {
|
||||
case ZigLLVM_x86:
|
||||
buf_append_str(output_buf, "x86\\");
|
||||
break;
|
||||
case ZigLLVM_x86_64:
|
||||
buf_append_str(output_buf, "x64\\");
|
||||
break;
|
||||
case ZigLLVM_arm:
|
||||
buf_append_str(output_buf, "arm\\");
|
||||
break;
|
||||
default:
|
||||
zig_panic("Attemped to use vcruntime for non-supported platform.");
|
||||
}
|
||||
buf_resize(tmp_buf, 0);
|
||||
buf_append_buf(tmp_buf, output_buf);
|
||||
buf_append_str(tmp_buf, "vcruntime.lib");
|
||||
|
||||
if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
com_done:;
|
||||
HKEY key;
|
||||
HRESULT rc;
|
||||
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key);
|
||||
if (rc != ERROR_SUCCESS) {
|
||||
return ErrorFileNotFound;
|
||||
}
|
||||
|
||||
DWORD dw_type = 0;
|
||||
DWORD cb_data = 0;
|
||||
rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data);
|
||||
if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) {
|
||||
return ErrorFileNotFound;
|
||||
}
|
||||
|
||||
Buf* tmp_buf = buf_alloc_fixed(cb_data);
|
||||
RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)buf_ptr(tmp_buf), &cb_data);
|
||||
//RegQueryValueExA returns the length of the string INCLUDING the null terminator
|
||||
buf_resize(tmp_buf, cb_data-1);
|
||||
buf_append_str(tmp_buf, "VC\\Lib\\");
|
||||
switch (platform_type) {
|
||||
case ZigLLVM_x86:
|
||||
//x86 is in the root of the Lib folder
|
||||
break;
|
||||
case ZigLLVM_x86_64:
|
||||
buf_append_str(tmp_buf, "amd64\\");
|
||||
break;
|
||||
case ZigLLVM_arm:
|
||||
buf_append_str(tmp_buf, "arm\\");
|
||||
break;
|
||||
default:
|
||||
zig_panic("Attemped to use vcruntime for non-supported platform.");
|
||||
}
|
||||
|
||||
buf_append_buf(output_buf, tmp_buf);
|
||||
buf_append_str(tmp_buf, "vcruntime.lib");
|
||||
|
||||
if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
|
||||
return 0;
|
||||
} else {
|
||||
buf_resize(output_buf, 0);
|
||||
return ErrorFileNotFound;
|
||||
}
|
||||
#else
|
||||
return ErrorFileNotFound;
|
||||
#endif
|
||||
}
|
||||
|
||||
int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) {
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
buf_resize(output_buf, 0);
|
||||
buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10));
|
||||
buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr);
|
||||
switch (platform_type) {
|
||||
case ZigLLVM_x86:
|
||||
buf_append_str(output_buf, "x86\\");
|
||||
|
@ -1389,7 +1149,7 @@ int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch
|
|||
int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) {
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
buf_resize(output_buf, 0);
|
||||
buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10));
|
||||
buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr);
|
||||
if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -1406,7 +1166,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy
|
|||
#if defined(ZIG_OS_WINDOWS)
|
||||
{
|
||||
buf_resize(output_buf, 0);
|
||||
buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10));
|
||||
buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr);
|
||||
switch (platform_type) {
|
||||
case ZigLLVM_x86:
|
||||
buf_append_str(output_buf, "x86\\");
|
||||
|
@ -1429,7 +1189,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy
|
|||
}
|
||||
{
|
||||
buf_resize(output_buf, 0);
|
||||
buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path81), buf_ptr(&sdk->version81));
|
||||
buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr);
|
||||
switch (platform_type) {
|
||||
case ZigLLVM_x86:
|
||||
buf_append_str(output_buf, "x86\\");
|
||||
|
|
10
src/os.hpp
10
src/os.hpp
|
@ -12,6 +12,7 @@
|
|||
#include "buffer.hpp"
|
||||
#include "error.hpp"
|
||||
#include "zig_llvm.h"
|
||||
#include "windows_sdk.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
|
@ -79,15 +80,6 @@ bool os_is_sep(uint8_t c);
|
|||
|
||||
int os_self_exe_path(Buf *out_path);
|
||||
|
||||
struct ZigWindowsSDK {
|
||||
Buf path10;
|
||||
Buf version10;
|
||||
Buf path81;
|
||||
Buf version81;
|
||||
};
|
||||
|
||||
int os_find_windows_sdk(ZigWindowsSDK **out_sdk);
|
||||
int os_get_win32_vcruntime_path(Buf *output_buf, ZigLLVM_ArchType platform_type);
|
||||
int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf);
|
||||
int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);
|
||||
int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);
|
||||
|
|
|
@ -0,0 +1,352 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Andrew Kelley
|
||||
*
|
||||
* This file is part of zig, which is MIT licensed.
|
||||
* See http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "windows_sdk.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include "windows_com.hpp"
|
||||
#include <inttypes.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct ZigWindowsSDKPrivate {
|
||||
ZigWindowsSDK base;
|
||||
};
|
||||
|
||||
enum NativeArch {
|
||||
NativeArchArm,
|
||||
NativeArchi386,
|
||||
NativeArchx86_64,
|
||||
};
|
||||
|
||||
#if defined(_M_ARM) || defined(__arm_)
|
||||
static const NativeArch native_arch = NativeArchArm;
|
||||
#endif
|
||||
#if defined(_M_IX86) || defined(__i386__)
|
||||
static const NativeArch native_arch = NativeArchi386;
|
||||
#endif
|
||||
#if defined(_M_X64) || defined(__x86_64__)
|
||||
static const NativeArch native_arch = NativeArchx86_64;
|
||||
#endif
|
||||
|
||||
void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {
|
||||
if (sdk == nullptr) {
|
||||
return;
|
||||
}
|
||||
free((void*)sdk->path10_ptr);
|
||||
free((void*)sdk->version10_ptr);
|
||||
free((void*)sdk->path81_ptr);
|
||||
free((void*)sdk->version81_ptr);
|
||||
free((void*)sdk->msvc_lib_dir_ptr);
|
||||
}
|
||||
|
||||
static ZigFindWindowsSdkError find_msvc_lib_dir(ZigWindowsSDKPrivate *priv) {
|
||||
//COM Smart Pointers requires explicit scope
|
||||
{
|
||||
HRESULT rc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if (rc != S_OK && rc != S_FALSE) {
|
||||
goto com_done;
|
||||
}
|
||||
|
||||
//This COM class is installed when a VS2017
|
||||
ISetupConfigurationPtr setup_config;
|
||||
rc = setup_config.CreateInstance(__uuidof(SetupConfiguration));
|
||||
if (rc != S_OK) {
|
||||
goto com_done;
|
||||
}
|
||||
|
||||
IEnumSetupInstancesPtr all_instances;
|
||||
rc = setup_config->EnumInstances(&all_instances);
|
||||
if (rc != S_OK) {
|
||||
goto com_done;
|
||||
}
|
||||
|
||||
ISetupInstance* curr_instance;
|
||||
ULONG found_inst;
|
||||
while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) {
|
||||
BSTR bstr_inst_path;
|
||||
rc = curr_instance->GetInstallationPath(&bstr_inst_path);
|
||||
if (rc != S_OK) {
|
||||
goto com_done;
|
||||
}
|
||||
//BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length
|
||||
//TODO call an actual function to do this
|
||||
UINT bstr_path_len = *((UINT*)bstr_inst_path - 1);
|
||||
ULONG tmp_path_len = bstr_path_len / 2 + 1;
|
||||
char* conv_path = (char*)bstr_inst_path;
|
||||
// TODO don't use alloca
|
||||
char *tmp_path = (char*)alloca(tmp_path_len);
|
||||
memset(tmp_path, 0, tmp_path_len);
|
||||
uint32_t c = 0;
|
||||
for (uint32_t i = 0; i < bstr_path_len; i += 2) {
|
||||
tmp_path[c] = conv_path[i];
|
||||
++c;
|
||||
assert(c != tmp_path_len);
|
||||
}
|
||||
char output_path[4096];
|
||||
output_path[0] = 0;
|
||||
char *out_append_ptr = output_path;
|
||||
|
||||
out_append_ptr += sprintf(out_append_ptr, "%s\\", tmp_path);
|
||||
|
||||
char tmp_buf[4096];
|
||||
sprintf(tmp_buf, "%s%s", output_path, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
|
||||
FILE* tools_file = fopen(tmp_buf, "rb");
|
||||
if (!tools_file) {
|
||||
goto com_done;
|
||||
}
|
||||
memset(tmp_path, 0, tmp_path_len);
|
||||
fgets(tmp_path, tmp_path_len, tools_file);
|
||||
strtok(tmp_path, " \r\n");
|
||||
fclose(tools_file);
|
||||
out_append_ptr += sprintf(out_append_ptr, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path);
|
||||
switch (native_arch) {
|
||||
case NativeArchi386:
|
||||
out_append_ptr += sprintf(out_append_ptr, "x86\\");
|
||||
break;
|
||||
case NativeArchx86_64:
|
||||
out_append_ptr += sprintf(out_append_ptr, "x64\\");
|
||||
break;
|
||||
case NativeArchArm:
|
||||
out_append_ptr += sprintf(out_append_ptr, "arm\\");
|
||||
break;
|
||||
}
|
||||
sprintf(tmp_buf, "%s%s", output_path, "vcruntime.lib");
|
||||
|
||||
if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) {
|
||||
priv->base.msvc_lib_dir_ptr = strdup(output_path);
|
||||
if (priv->base.msvc_lib_dir_ptr == nullptr) {
|
||||
return ZigFindWindowsSdkErrorOutOfMemory;
|
||||
}
|
||||
priv->base.msvc_lib_dir_len = strlen(priv->base.msvc_lib_dir_ptr);
|
||||
return ZigFindWindowsSdkErrorNone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
com_done:;
|
||||
HKEY key;
|
||||
HRESULT rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0,
|
||||
KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key);
|
||||
if (rc != ERROR_SUCCESS) {
|
||||
return ZigFindWindowsSdkErrorNotFound;
|
||||
}
|
||||
|
||||
DWORD dw_type = 0;
|
||||
DWORD cb_data = 0;
|
||||
rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data);
|
||||
if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) {
|
||||
return ZigFindWindowsSdkErrorNotFound;
|
||||
}
|
||||
|
||||
char tmp_buf[4096];
|
||||
|
||||
RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)tmp_buf, &cb_data);
|
||||
// RegQueryValueExA returns the length of the string INCLUDING the null terminator
|
||||
char *tmp_buf_append_ptr = tmp_buf + (cb_data - 1);
|
||||
tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "VC\\Lib\\");
|
||||
switch (native_arch) {
|
||||
case NativeArchi386:
|
||||
//x86 is in the root of the Lib folder
|
||||
break;
|
||||
case NativeArchx86_64:
|
||||
tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "amd64\\");
|
||||
break;
|
||||
case NativeArchArm:
|
||||
tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "arm\\");
|
||||
break;
|
||||
}
|
||||
|
||||
char *output_path = strdup(tmp_buf);
|
||||
if (output_path == nullptr) {
|
||||
return ZigFindWindowsSdkErrorOutOfMemory;
|
||||
}
|
||||
|
||||
tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "vcruntime.lib");
|
||||
|
||||
if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) {
|
||||
priv->base.msvc_lib_dir_ptr = output_path;
|
||||
priv->base.msvc_lib_dir_len = strlen(output_path);
|
||||
return ZigFindWindowsSdkErrorNone;
|
||||
} else {
|
||||
free(output_path);
|
||||
return ZigFindWindowsSdkErrorNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
static ZigFindWindowsSdkError find_10_version(ZigWindowsSDKPrivate *priv) {
|
||||
if (priv->base.path10_ptr == nullptr)
|
||||
return ZigFindWindowsSdkErrorNone;
|
||||
|
||||
char sdk_lib_dir[4096];
|
||||
int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\*", priv->base.path10_ptr);
|
||||
if (n < 0 || n >= 4096) {
|
||||
return ZigFindWindowsSdkErrorPathTooLong;
|
||||
}
|
||||
|
||||
// enumerate files in sdk path looking for latest version
|
||||
WIN32_FIND_DATA ffd;
|
||||
HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd);
|
||||
if (hFind == INVALID_HANDLE_VALUE) {
|
||||
return ZigFindWindowsSdkErrorNotFound;
|
||||
}
|
||||
int v0 = 0, v1 = 0, v2 = 0, v3 = 0;
|
||||
for (;;) {
|
||||
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
int c0 = 0, c1 = 0, c2 = 0, c3 = 0;
|
||||
sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3);
|
||||
if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) {
|
||||
// Microsoft released 26624 as 10240 accidentally.
|
||||
// https://developer.microsoft.com/en-us/windows/downloads/sdk-archive
|
||||
c2 = 26624;
|
||||
}
|
||||
if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) {
|
||||
v0 = c0, v1 = c1, v2 = c2, v3 = c3;
|
||||
free((void*)priv->base.version10_ptr);
|
||||
priv->base.version10_ptr = strdup(ffd.cFileName);
|
||||
if (priv->base.version10_ptr == nullptr) {
|
||||
FindClose(hFind);
|
||||
return ZigFindWindowsSdkErrorOutOfMemory;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (FindNextFile(hFind, &ffd) == 0) {
|
||||
FindClose(hFind);
|
||||
break;
|
||||
}
|
||||
}
|
||||
priv->base.version10_len = strlen(priv->base.version10_ptr);
|
||||
return ZigFindWindowsSdkErrorNone;
|
||||
}
|
||||
|
||||
static ZigFindWindowsSdkError find_81_version(ZigWindowsSDKPrivate *priv) {
|
||||
if (priv->base.path81_ptr == nullptr)
|
||||
return ZigFindWindowsSdkErrorNone;
|
||||
|
||||
char sdk_lib_dir[4096];
|
||||
int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\winv*", priv->base.path81_ptr);
|
||||
if (n < 0 || n >= 4096) {
|
||||
return ZigFindWindowsSdkErrorPathTooLong;
|
||||
}
|
||||
|
||||
// enumerate files in sdk path looking for latest version
|
||||
WIN32_FIND_DATA ffd;
|
||||
HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd);
|
||||
if (hFind == INVALID_HANDLE_VALUE) {
|
||||
return ZigFindWindowsSdkErrorNotFound;
|
||||
}
|
||||
int v0 = 0, v1 = 0;
|
||||
for (;;) {
|
||||
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
int c0 = 0, c1 = 0;
|
||||
sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1);
|
||||
if ((c0 > v0) || (c1 > v1)) {
|
||||
v0 = c0, v1 = c1;
|
||||
free((void*)priv->base.version81_ptr);
|
||||
priv->base.version81_ptr = strdup(ffd.cFileName);
|
||||
if (priv->base.version81_ptr == nullptr) {
|
||||
FindClose(hFind);
|
||||
return ZigFindWindowsSdkErrorOutOfMemory;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (FindNextFile(hFind, &ffd) == 0) {
|
||||
FindClose(hFind);
|
||||
break;
|
||||
}
|
||||
}
|
||||
priv->base.version81_len = strlen(priv->base.version81_ptr);
|
||||
return ZigFindWindowsSdkErrorNone;
|
||||
}
|
||||
|
||||
ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) {
|
||||
ZigWindowsSDKPrivate *priv = (ZigWindowsSDKPrivate*)calloc(1, sizeof(ZigWindowsSDKPrivate));
|
||||
if (priv == nullptr) {
|
||||
return ZigFindWindowsSdkErrorOutOfMemory;
|
||||
}
|
||||
|
||||
HKEY key;
|
||||
HRESULT rc;
|
||||
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0,
|
||||
KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key);
|
||||
if (rc != ERROR_SUCCESS) {
|
||||
zig_free_windows_sdk(&priv->base);
|
||||
return ZigFindWindowsSdkErrorNotFound;
|
||||
}
|
||||
|
||||
{
|
||||
DWORD tmp_buf_len = MAX_PATH;
|
||||
priv->base.path10_ptr = (const char *)calloc(tmp_buf_len, 1);
|
||||
if (priv->base.path10_ptr == nullptr) {
|
||||
zig_free_windows_sdk(&priv->base);
|
||||
return ZigFindWindowsSdkErrorOutOfMemory;
|
||||
}
|
||||
rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)priv->base.path10_ptr, &tmp_buf_len);
|
||||
if (rc == ERROR_SUCCESS) {
|
||||
priv->base.path10_len = tmp_buf_len - 1;
|
||||
if (priv->base.path10_ptr[priv->base.path10_len - 1] == '\\') {
|
||||
priv->base.path10_len -= 1;
|
||||
}
|
||||
} else {
|
||||
free((void*)priv->base.path10_ptr);
|
||||
priv->base.path10_ptr = nullptr;
|
||||
}
|
||||
}
|
||||
{
|
||||
DWORD tmp_buf_len = MAX_PATH;
|
||||
priv->base.path81_ptr = (const char *)calloc(tmp_buf_len, 1);
|
||||
if (priv->base.path81_ptr == nullptr) {
|
||||
zig_free_windows_sdk(&priv->base);
|
||||
return ZigFindWindowsSdkErrorOutOfMemory;
|
||||
}
|
||||
rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)priv->base.path81_ptr, &tmp_buf_len);
|
||||
if (rc == ERROR_SUCCESS) {
|
||||
priv->base.path81_len = tmp_buf_len - 1;
|
||||
if (priv->base.path81_ptr[priv->base.path81_len - 1] == '\\') {
|
||||
priv->base.path81_len -= 1;
|
||||
}
|
||||
} else {
|
||||
free((void*)priv->base.path81_ptr);
|
||||
priv->base.path81_ptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ZigFindWindowsSdkError err = find_10_version(priv);
|
||||
if (err == ZigFindWindowsSdkErrorOutOfMemory) {
|
||||
zig_free_windows_sdk(&priv->base);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
{
|
||||
ZigFindWindowsSdkError err = find_81_version(priv);
|
||||
if (err == ZigFindWindowsSdkErrorOutOfMemory) {
|
||||
zig_free_windows_sdk(&priv->base);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ZigFindWindowsSdkError err = find_msvc_lib_dir(priv);
|
||||
if (err == ZigFindWindowsSdkErrorOutOfMemory) {
|
||||
zig_free_windows_sdk(&priv->base);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
*out_sdk = &priv->base;
|
||||
return ZigFindWindowsSdkErrorNone;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {}
|
||||
ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) {
|
||||
return ZigFindWindowsSdkErrorNotFound;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Andrew Kelley
|
||||
*
|
||||
* This file is part of zig, which is MIT licensed.
|
||||
* See http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#ifndef ZIG_WINDOWS_SDK_H
|
||||
#define ZIG_WINDOWS_SDK_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define ZIG_EXTERN_C extern "C"
|
||||
#else
|
||||
#define ZIG_EXTERN_C
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct ZigWindowsSDK {
|
||||
const char *path10_ptr;
|
||||
size_t path10_len;
|
||||
|
||||
const char *version10_ptr;
|
||||
size_t version10_len;
|
||||
|
||||
const char *path81_ptr;
|
||||
size_t path81_len;
|
||||
|
||||
const char *version81_ptr;
|
||||
size_t version81_len;
|
||||
|
||||
const char *msvc_lib_dir_ptr;
|
||||
size_t msvc_lib_dir_len;
|
||||
};
|
||||
|
||||
enum ZigFindWindowsSdkError {
|
||||
ZigFindWindowsSdkErrorNone,
|
||||
ZigFindWindowsSdkErrorOutOfMemory,
|
||||
ZigFindWindowsSdkErrorNotFound,
|
||||
ZigFindWindowsSdkErrorPathTooLong,
|
||||
};
|
||||
|
||||
ZIG_EXTERN_C enum ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk);
|
||||
|
||||
ZIG_EXTERN_C void zig_free_windows_sdk(struct ZigWindowsSDK *sdk);
|
||||
|
||||
#endif
|
|
@ -6,7 +6,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
|
|||
const AtomicOrder = builtin.AtomicOrder;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
/// ReturnType should be `void` or `E!void`
|
||||
/// ReturnType must be `void` or `E!void`
|
||||
pub fn Group(comptime ReturnType: type) type {
|
||||
return struct {
|
||||
coro_stack: Stack,
|
||||
|
@ -38,8 +38,17 @@ pub fn Group(comptime ReturnType: type) type {
|
|||
self.alloc_stack.push(node);
|
||||
}
|
||||
|
||||
/// Add a node to the group. Thread-safe. Cannot fail.
|
||||
/// `node.data` should be the promise handle to add to the group.
|
||||
/// The node's memory should be in the coroutine frame of
|
||||
/// the handle that is in the node, or somewhere guaranteed to live
|
||||
/// at least as long.
|
||||
pub fn addNode(self: *Self, node: *Stack.Node) void {
|
||||
self.coro_stack.push(node);
|
||||
}
|
||||
|
||||
/// This is equivalent to an async call, but the async function is added to the group, instead
|
||||
/// of returning a promise. func must be async and have return type void.
|
||||
/// of returning a promise. func must be async and have return type ReturnType.
|
||||
/// Thread-safe.
|
||||
pub fn call(self: *Self, comptime func: var, args: ...) (error{OutOfMemory}!void) {
|
||||
const S = struct {
|
||||
|
@ -67,6 +76,7 @@ pub fn Group(comptime ReturnType: type) type {
|
|||
|
||||
/// Wait for all the calls and promises of the group to complete.
|
||||
/// Thread-safe.
|
||||
/// Safe to call any number of times.
|
||||
pub async fn wait(self: *Self) ReturnType {
|
||||
// TODO catch unreachable because the allocation can be grouped with
|
||||
// the coro frame allocation
|
||||
|
@ -98,6 +108,8 @@ pub fn Group(comptime ReturnType: type) type {
|
|||
}
|
||||
|
||||
/// Cancel all the outstanding promises. May only be called if wait was never called.
|
||||
/// TODO These should be `cancelasync` not `cancel`.
|
||||
/// See https://github.com/ziglang/zig/issues/1261
|
||||
pub fn cancelAll(self: *Self) void {
|
||||
while (self.coro_stack.pop()) |node| {
|
||||
cancel node.data;
|
||||
|
|
|
@ -444,7 +444,7 @@ pub const Loop = struct {
|
|||
.next = undefined,
|
||||
.data = p,
|
||||
};
|
||||
loop.onNextTick(&my_tick_node);
|
||||
self.onNextTick(&my_tick_node);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -785,11 +785,15 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 {
|
|||
return buf[0 .. buf.len - context.remaining.len];
|
||||
}
|
||||
|
||||
pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 {
|
||||
pub const AllocPrintError = error{OutOfMemory};
|
||||
|
||||
pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) AllocPrintError![]u8 {
|
||||
var size: usize = 0;
|
||||
format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {};
|
||||
const buf = try allocator.alloc(u8, size);
|
||||
return bufPrint(buf, fmt, args);
|
||||
return bufPrint(buf, fmt, args) catch |err| switch (err) {
|
||||
error.BufferTooSmall => unreachable, // we just counted the size above
|
||||
};
|
||||
}
|
||||
|
||||
fn countSize(size: *usize, bytes: []const u8) (error{}!void) {
|
||||
|
|
|
@ -141,7 +141,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable
|
|||
}
|
||||
|
||||
// Effectively a no-op, lld emits symbols in ascending order.
|
||||
std.sort.insertionSort(Symbol, symbols[0..nsyms], Symbol.addressLessThan);
|
||||
std.sort.sort(Symbol, symbols[0..nsyms], Symbol.addressLessThan);
|
||||
|
||||
// Insert the sentinel. Since we don't know where the last function ends,
|
||||
// we arbitrarily limit it to the start address + 4 KB.
|
||||
|
|
|
@ -60,8 +60,9 @@ pub const Int = struct {
|
|||
self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity);
|
||||
}
|
||||
|
||||
pub fn deinit(self: Int) void {
|
||||
pub fn deinit(self: *Int) void {
|
||||
self.allocator.free(self.limbs);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn clone(other: Int) !Int {
|
||||
|
@ -332,6 +333,7 @@ pub const Int = struct {
|
|||
self.positive = positive;
|
||||
}
|
||||
|
||||
/// TODO make this call format instead of the other way around
|
||||
pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 {
|
||||
if (base < 2 or base > 16) {
|
||||
return error.InvalidBase;
|
||||
|
@ -414,6 +416,21 @@ pub const Int = struct {
|
|||
return s;
|
||||
}
|
||||
|
||||
/// for the std lib format function
|
||||
/// TODO make this non-allocating
|
||||
pub fn format(
|
||||
self: Int,
|
||||
comptime fmt: []const u8,
|
||||
context: var,
|
||||
comptime FmtError: type,
|
||||
output: fn (@typeOf(context), []const u8) FmtError!void,
|
||||
) FmtError!void {
|
||||
// TODO look at fmt and support other bases
|
||||
const str = self.toString(self.allocator, 10) catch @panic("TODO make this non allocating");
|
||||
defer self.allocator.free(str);
|
||||
return output(context, str);
|
||||
}
|
||||
|
||||
// returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.
|
||||
pub fn cmpAbs(a: Int, b: Int) i8 {
|
||||
if (a.len < b.len) {
|
||||
|
|
12
std/mem.zig
12
std/mem.zig
|
@ -35,6 +35,7 @@ pub const Allocator = struct {
|
|||
freeFn: fn (self: *Allocator, old_mem: []u8) void,
|
||||
|
||||
/// Call `destroy` with the result
|
||||
/// TODO this is deprecated. use createOne instead
|
||||
pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) {
|
||||
const T = @typeOf(init);
|
||||
if (@sizeOf(T) == 0) return &(T{});
|
||||
|
@ -44,6 +45,14 @@ pub const Allocator = struct {
|
|||
return ptr;
|
||||
}
|
||||
|
||||
/// Call `destroy` with the result.
|
||||
/// Returns undefined memory.
|
||||
pub fn createOne(self: *Allocator, comptime T: type) Error!*T {
|
||||
if (@sizeOf(T) == 0) return &(T{});
|
||||
const slice = try self.alloc(T, 1);
|
||||
return &slice[0];
|
||||
}
|
||||
|
||||
/// `ptr` should be the return value of `create`
|
||||
pub fn destroy(self: *Allocator, ptr: var) void {
|
||||
const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));
|
||||
|
@ -149,13 +158,12 @@ pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {
|
|||
@setRuntimeSafety(false);
|
||||
assert(dest.len >= source.len);
|
||||
var i = source.len;
|
||||
while(i > 0){
|
||||
while (i > 0) {
|
||||
i -= 1;
|
||||
dest[i] = source[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn set(comptime T: type, dest: []T, value: T) void {
|
||||
for (dest) |*d|
|
||||
d.* = value;
|
||||
|
|
|
@ -109,43 +109,42 @@ pub const File = struct {
|
|||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn access(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) AccessError!bool {
|
||||
pub fn access(allocator: *mem.Allocator, path: []const u8) AccessError!void {
|
||||
const path_with_null = try std.cstr.addNullByte(allocator, path);
|
||||
defer allocator.free(path_with_null);
|
||||
|
||||
if (is_posix) {
|
||||
// mode is ignored and is always F_OK for now
|
||||
const result = posix.access(path_with_null.ptr, posix.F_OK);
|
||||
const err = posix.getErrno(result);
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
posix.EACCES => error.PermissionDenied,
|
||||
posix.EROFS => error.PermissionDenied,
|
||||
posix.ELOOP => error.PermissionDenied,
|
||||
posix.ETXTBSY => error.PermissionDenied,
|
||||
posix.ENOTDIR => error.NotFound,
|
||||
posix.ENOENT => error.NotFound,
|
||||
switch (err) {
|
||||
0 => return,
|
||||
posix.EACCES => return error.PermissionDenied,
|
||||
posix.EROFS => return error.PermissionDenied,
|
||||
posix.ELOOP => return error.PermissionDenied,
|
||||
posix.ETXTBSY => return error.PermissionDenied,
|
||||
posix.ENOTDIR => return error.NotFound,
|
||||
posix.ENOENT => return error.NotFound,
|
||||
|
||||
posix.ENAMETOOLONG => error.NameTooLong,
|
||||
posix.EINVAL => error.BadMode,
|
||||
posix.EFAULT => error.BadPathName,
|
||||
posix.EIO => error.Io,
|
||||
posix.ENOMEM => error.SystemResources,
|
||||
else => os.unexpectedErrorPosix(err),
|
||||
};
|
||||
posix.ENAMETOOLONG => return error.NameTooLong,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.EFAULT => return error.BadPathName,
|
||||
posix.EIO => return error.Io,
|
||||
posix.ENOMEM => return error.SystemResources,
|
||||
else => return os.unexpectedErrorPosix(err),
|
||||
}
|
||||
return true;
|
||||
} else if (is_windows) {
|
||||
if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
windows.ERROR.FILE_NOT_FOUND => error.NotFound,
|
||||
windows.ERROR.ACCESS_DENIED => error.PermissionDenied,
|
||||
else => os.unexpectedErrorWindows(err),
|
||||
};
|
||||
switch (err) {
|
||||
windows.ERROR.FILE_NOT_FOUND,
|
||||
windows.ERROR.PATH_NOT_FOUND,
|
||||
=> return error.NotFound,
|
||||
windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
|
||||
else => return os.unexpectedErrorWindows(err),
|
||||
}
|
||||
} else {
|
||||
@compileError("TODO implement access for this OS");
|
||||
}
|
||||
|
|
|
@ -23,14 +23,14 @@ test "makePath, put some files in it, deleteTree" {
|
|||
|
||||
test "access file" {
|
||||
try os.makePath(a, "os_test_tmp");
|
||||
if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {
|
||||
unreachable;
|
||||
if (os.File.access(a, "os_test_tmp/file.txt")) |ok| {
|
||||
@panic("expected error");
|
||||
} else |err| {
|
||||
assert(err == error.NotFound);
|
||||
}
|
||||
|
||||
try io.writeFile(a, "os_test_tmp/file.txt", "");
|
||||
assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true);
|
||||
try os.File.access(a, "os_test_tmp/file.txt");
|
||||
try os.deleteTree(a, "os_test_tmp");
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
use @import("index.zig");
|
||||
|
||||
pub const PROV_RSA_FULL = 1;
|
||||
|
||||
pub const REGSAM = ACCESS_MASK;
|
||||
pub const ACCESS_MASK = DWORD;
|
||||
pub const PHKEY = &HKEY;
|
||||
pub const HKEY = &HKEY__;
|
||||
pub const HKEY__ = extern struct {
|
||||
unused: c_int,
|
||||
};
|
||||
pub const LSTATUS = LONG;
|
||||
|
||||
pub extern "advapi32" stdcallcc fn CryptAcquireContextA(
|
||||
phProv: *HCRYPTPROV,
|
||||
pszContainer: ?LPCSTR,
|
||||
pszProvider: ?LPCSTR,
|
||||
dwProvType: DWORD,
|
||||
dwFlags: DWORD,
|
||||
) BOOL;
|
||||
|
||||
pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL;
|
||||
|
||||
pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL;
|
||||
|
||||
pub extern "advapi32" stdcallcc fn RegOpenKeyExW(hKey: HKEY, lpSubKey: LPCWSTR, ulOptions: DWORD, samDesired: REGSAM,
|
||||
phkResult: &HKEY,) LSTATUS;
|
||||
|
||||
pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD,
|
||||
lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS;
|
|
@ -1,190 +1,19 @@
|
|||
const std = @import("../../index.zig");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
pub use @import("advapi32.zig");
|
||||
pub use @import("kernel32.zig");
|
||||
pub use @import("ole32.zig");
|
||||
pub use @import("shell32.zig");
|
||||
pub use @import("shlwapi.zig");
|
||||
pub use @import("user32.zig");
|
||||
|
||||
test "import" {
|
||||
_ = @import("util.zig");
|
||||
}
|
||||
|
||||
pub const ERROR = @import("error.zig");
|
||||
|
||||
pub extern "advapi32" stdcallcc fn CryptAcquireContextA(
|
||||
phProv: *HCRYPTPROV,
|
||||
pszContainer: ?LPCSTR,
|
||||
pszProvider: ?LPCSTR,
|
||||
dwProvType: DWORD,
|
||||
dwFlags: DWORD,
|
||||
) BOOL;
|
||||
|
||||
pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL;
|
||||
|
||||
pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateDirectoryA(
|
||||
lpPathName: LPCSTR,
|
||||
lpSecurityAttributes: ?*SECURITY_ATTRIBUTES,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateFileA(
|
||||
lpFileName: LPCSTR,
|
||||
dwDesiredAccess: DWORD,
|
||||
dwShareMode: DWORD,
|
||||
lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
|
||||
dwCreationDisposition: DWORD,
|
||||
dwFlagsAndAttributes: DWORD,
|
||||
hTemplateFile: ?HANDLE,
|
||||
) HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreatePipe(
|
||||
hReadPipe: *HANDLE,
|
||||
hWritePipe: *HANDLE,
|
||||
lpPipeAttributes: *const SECURITY_ATTRIBUTES,
|
||||
nSize: DWORD,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateProcessA(
|
||||
lpApplicationName: ?LPCSTR,
|
||||
lpCommandLine: LPSTR,
|
||||
lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
|
||||
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
|
||||
bInheritHandles: BOOL,
|
||||
dwCreationFlags: DWORD,
|
||||
lpEnvironment: ?*c_void,
|
||||
lpCurrentDirectory: ?LPCSTR,
|
||||
lpStartupInfo: *STARTUPINFOA,
|
||||
lpProcessInformation: *PROCESS_INFORMATION,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
|
||||
lpSymlinkFileName: LPCSTR,
|
||||
lpTargetFileName: LPCSTR,
|
||||
dwFlags: DWORD,
|
||||
) BOOLEAN;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
|
||||
pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
|
||||
pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
|
||||
in_hFile: HANDLE,
|
||||
in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
|
||||
out_lpFileInformation: *c_void,
|
||||
in_dwBufferSize: DWORD,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
|
||||
hFile: HANDLE,
|
||||
lpszFilePath: LPSTR,
|
||||
cchFilePath: DWORD,
|
||||
dwFlags: DWORD,
|
||||
) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
|
||||
pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void;
|
||||
pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
|
||||
pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
|
||||
pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
|
||||
pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
|
||||
pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL;
|
||||
pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
|
||||
pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn MoveFileExA(
|
||||
lpExistingFileName: LPCSTR,
|
||||
lpNewFileName: LPCSTR,
|
||||
dwFlags: DWORD,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn ReadFile(
|
||||
in_hFile: HANDLE,
|
||||
out_lpBuffer: *c_void,
|
||||
in_nNumberOfBytesToRead: DWORD,
|
||||
out_lpNumberOfBytesRead: *DWORD,
|
||||
in_out_lpOverlapped: ?*OVERLAPPED,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn SetFilePointerEx(
|
||||
in_fFile: HANDLE,
|
||||
in_liDistanceToMove: LARGE_INTEGER,
|
||||
out_opt_ldNewFilePointer: ?*LARGE_INTEGER,
|
||||
in_dwMoveMethod: DWORD,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn WriteFile(
|
||||
in_hFile: HANDLE,
|
||||
in_lpBuffer: *const c_void,
|
||||
in_nNumberOfBytesToWrite: DWORD,
|
||||
out_lpNumberOfBytesWritten: ?*DWORD,
|
||||
in_out_lpOverlapped: ?*OVERLAPPED,
|
||||
) BOOL;
|
||||
|
||||
//TODO: call unicode versions instead of relying on ANSI code page
|
||||
pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
|
||||
|
||||
pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
|
||||
|
||||
pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
|
||||
|
||||
pub const PROV_RSA_FULL = 1;
|
||||
|
||||
pub const BOOL = c_int;
|
||||
pub const BOOLEAN = BYTE;
|
||||
pub const BYTE = u8;
|
||||
|
@ -206,6 +35,7 @@ pub const LPSTR = [*]CHAR;
|
|||
pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR;
|
||||
pub const LPVOID = *c_void;
|
||||
pub const LPWSTR = [*]WCHAR;
|
||||
pub const LPCWSTR = [*]const WCHAR;
|
||||
pub const PVOID = *c_void;
|
||||
pub const PWSTR = [*]WCHAR;
|
||||
pub const SIZE_T = usize;
|
||||
|
@ -442,10 +272,6 @@ pub const SYSTEM_INFO = extern struct {
|
|||
wProcessorRevision: WORD,
|
||||
};
|
||||
|
||||
pub extern "ole32.dll" stdcallcc fn CoTaskMemFree(pv: LPVOID) void;
|
||||
|
||||
pub extern "shell32.dll" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT;
|
||||
|
||||
pub const HRESULT = c_long;
|
||||
|
||||
pub const KNOWNFOLDERID = GUID;
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
use @import("index.zig");
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateDirectoryA(
|
||||
lpPathName: LPCSTR,
|
||||
lpSecurityAttributes: ?*SECURITY_ATTRIBUTES,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateFileA(
|
||||
lpFileName: LPCSTR,
|
||||
dwDesiredAccess: DWORD,
|
||||
dwShareMode: DWORD,
|
||||
lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
|
||||
dwCreationDisposition: DWORD,
|
||||
dwFlagsAndAttributes: DWORD,
|
||||
hTemplateFile: ?HANDLE,
|
||||
) HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreatePipe(
|
||||
hReadPipe: *HANDLE,
|
||||
hWritePipe: *HANDLE,
|
||||
lpPipeAttributes: *const SECURITY_ATTRIBUTES,
|
||||
nSize: DWORD,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateProcessA(
|
||||
lpApplicationName: ?LPCSTR,
|
||||
lpCommandLine: LPSTR,
|
||||
lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
|
||||
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
|
||||
bInheritHandles: BOOL,
|
||||
dwCreationFlags: DWORD,
|
||||
lpEnvironment: ?*c_void,
|
||||
lpCurrentDirectory: ?LPCSTR,
|
||||
lpStartupInfo: *STARTUPINFOA,
|
||||
lpProcessInformation: *PROCESS_INFORMATION,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
|
||||
lpSymlinkFileName: LPCSTR,
|
||||
lpTargetFileName: LPCSTR,
|
||||
dwFlags: DWORD,
|
||||
) BOOLEAN;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
|
||||
pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
|
||||
pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
|
||||
in_hFile: HANDLE,
|
||||
in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
|
||||
out_lpFileInformation: *c_void,
|
||||
in_dwBufferSize: DWORD,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
|
||||
hFile: HANDLE,
|
||||
lpszFilePath: LPSTR,
|
||||
cchFilePath: DWORD,
|
||||
dwFlags: DWORD,
|
||||
) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
|
||||
pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void;
|
||||
pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
|
||||
pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
|
||||
pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
|
||||
pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
|
||||
pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL;
|
||||
pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
|
||||
pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn MoveFileExA(
|
||||
lpExistingFileName: LPCSTR,
|
||||
lpNewFileName: LPCSTR,
|
||||
dwFlags: DWORD,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn ReadFile(
|
||||
in_hFile: HANDLE,
|
||||
out_lpBuffer: *c_void,
|
||||
in_nNumberOfBytesToRead: DWORD,
|
||||
out_lpNumberOfBytesRead: *DWORD,
|
||||
in_out_lpOverlapped: ?*OVERLAPPED,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn SetFilePointerEx(
|
||||
in_fFile: HANDLE,
|
||||
in_liDistanceToMove: LARGE_INTEGER,
|
||||
out_opt_ldNewFilePointer: ?*LARGE_INTEGER,
|
||||
in_dwMoveMethod: DWORD,
|
||||
) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn WriteFile(
|
||||
in_hFile: HANDLE,
|
||||
in_lpBuffer: *const c_void,
|
||||
in_nNumberOfBytesToWrite: DWORD,
|
||||
out_lpNumberOfBytesWritten: ?*DWORD,
|
||||
in_out_lpOverlapped: ?*OVERLAPPED,
|
||||
) BOOL;
|
||||
|
||||
//TODO: call unicode versions instead of relying on ANSI code page
|
||||
pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
|
||||
|
||||
pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
|
|
@ -0,0 +1,18 @@
|
|||
use @import("index.zig");
|
||||
|
||||
pub extern "ole32.dll" stdcallcc fn CoTaskMemFree(pv: LPVOID) void;
|
||||
pub extern "ole32.dll" stdcallcc fn CoUninitialize() void;
|
||||
pub extern "ole32.dll" stdcallcc fn CoGetCurrentProcess() DWORD;
|
||||
pub extern "ole32.dll" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT;
|
||||
|
||||
|
||||
pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED;
|
||||
pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED;
|
||||
pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE;
|
||||
pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY;
|
||||
pub const COINIT = extern enum {
|
||||
COINIT_APARTMENTTHREADED = 2,
|
||||
COINIT_MULTITHREADED = 0,
|
||||
COINIT_DISABLE_OLE1DDE = 4,
|
||||
COINIT_SPEED_OVER_MEMORY = 8,
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
use @import("index.zig");
|
||||
|
||||
pub extern "shell32.dll" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT;
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
use @import("index.zig");
|
||||
|
||||
pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
use @import("index.zig");
|
||||
|
||||
pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
|
||||
|
|
@ -2,6 +2,7 @@ const tokenizer = @import("tokenizer.zig");
|
|||
pub const Token = tokenizer.Token;
|
||||
pub const Tokenizer = tokenizer.Tokenizer;
|
||||
pub const parse = @import("parse.zig").parse;
|
||||
pub const parseStringLiteral = @import("parse_string_literal.zig").parseStringLiteral;
|
||||
pub const render = @import("render.zig").render;
|
||||
pub const ast = @import("ast.zig");
|
||||
|
||||
|
@ -10,4 +11,6 @@ test "std.zig tests" {
|
|||
_ = @import("parse.zig");
|
||||
_ = @import("render.zig");
|
||||
_ = @import("tokenizer.zig");
|
||||
_ = @import("parse_string_literal.zig");
|
||||
}
|
||||
|
||||
|
|
|
@ -2356,7 +2356,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
|
|||
const token = nextToken(&tok_it, &tree);
|
||||
switch (token.ptr.id) {
|
||||
Token.Id.IntegerLiteral => {
|
||||
_ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token.index);
|
||||
_ = try createToCtxLiteral(arena, opt_ctx, ast.Node.IntegerLiteral, token.index);
|
||||
continue;
|
||||
},
|
||||
Token.Id.FloatLiteral => {
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
const std = @import("../index.zig");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const State = enum {
|
||||
Start,
|
||||
Backslash,
|
||||
};
|
||||
|
||||
pub const ParseStringLiteralError = error{
|
||||
OutOfMemory,
|
||||
|
||||
/// When this is returned, index will be the position of the character.
|
||||
InvalidCharacter,
|
||||
};
|
||||
|
||||
/// caller owns returned memory
|
||||
pub fn parseStringLiteral(
|
||||
allocator: *std.mem.Allocator,
|
||||
bytes: []const u8,
|
||||
bad_index: *usize, // populated if error.InvalidCharacter is returned
|
||||
) ParseStringLiteralError![]u8 {
|
||||
const first_index = if (bytes[0] == 'c') usize(2) else usize(1);
|
||||
assert(bytes[bytes.len - 1] == '"');
|
||||
|
||||
var list = std.ArrayList(u8).init(allocator);
|
||||
errdefer list.deinit();
|
||||
|
||||
const slice = bytes[first_index..];
|
||||
try list.ensureCapacity(slice.len - 1);
|
||||
|
||||
var state = State.Start;
|
||||
for (slice) |b, index| {
|
||||
switch (state) {
|
||||
State.Start => switch (b) {
|
||||
'\\' => state = State.Backslash,
|
||||
'\n' => {
|
||||
bad_index.* = index;
|
||||
return error.InvalidCharacter;
|
||||
},
|
||||
'"' => return list.toOwnedSlice(),
|
||||
else => try list.append(b),
|
||||
},
|
||||
State.Backslash => switch (b) {
|
||||
'x' => @panic("TODO"),
|
||||
'u' => @panic("TODO"),
|
||||
'U' => @panic("TODO"),
|
||||
'n' => {
|
||||
try list.append('\n');
|
||||
state = State.Start;
|
||||
},
|
||||
'r' => {
|
||||
try list.append('\r');
|
||||
state = State.Start;
|
||||
},
|
||||
'\\' => {
|
||||
try list.append('\\');
|
||||
state = State.Start;
|
||||
},
|
||||
't' => {
|
||||
try list.append('\t');
|
||||
state = State.Start;
|
||||
},
|
||||
'"' => {
|
||||
try list.append('"');
|
||||
state = State.Start;
|
||||
},
|
||||
else => {
|
||||
bad_index.* = index;
|
||||
return error.InvalidCharacter;
|
||||
},
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
}
|
|
@ -73,6 +73,7 @@ pub const Token = struct {
|
|||
return null;
|
||||
}
|
||||
|
||||
/// TODO remove this enum
|
||||
const StrLitKind = enum {
|
||||
Normal,
|
||||
C,
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
const std = @import("std");
|
||||
const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
|
||||
|
||||
pub fn addCases(ctx: *TestContext) !void {
|
||||
try ctx.testCompareOutputLibC(
|
||||
\\extern fn puts([*]const u8) void;
|
||||
\\export fn main() c_int {
|
||||
\\ puts(c"Hello, world!");
|
||||
\\ return 0;
|
||||
\\}
|
||||
, "Hello, world!" ++ std.cstr.line_sep);
|
||||
}
|
|
@ -9,4 +9,22 @@ pub fn addCases(ctx: *TestContext) !void {
|
|||
try ctx.testCompileError(
|
||||
\\fn() void {}
|
||||
, "1.zig", 1, 1, "missing function name");
|
||||
|
||||
try ctx.testCompileError(
|
||||
\\comptime {
|
||||
\\ return;
|
||||
\\}
|
||||
, "1.zig", 2, 5, "return expression outside function definition");
|
||||
|
||||
try ctx.testCompileError(
|
||||
\\export fn entry() void {
|
||||
\\ defer return;
|
||||
\\}
|
||||
, "1.zig", 2, 11, "cannot return from defer expression");
|
||||
|
||||
try ctx.testCompileError(
|
||||
\\export fn entry() c_int {
|
||||
\\ return 36893488147419103232;
|
||||
\\}
|
||||
, "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue